/*
 * Copyright (C) 1994-2021 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of both the OpenPBS software ("OpenPBS")
 * and the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * OpenPBS is free software. You can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * OpenPBS is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * PBS Pro is commercially licensed software that shares a common core with
 * the OpenPBS software.  For a copy of the commercial license terms and
 * conditions, go to: (http://www.pbspro.com/agreement.html) or contact the
 * Altair Legal Department.
 *
 * Altair's dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of OpenPBS and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair's trademarks, including but not limited to "PBS™",
 * "OpenPBS®", "PBS Professional®", and "PBS Pro™" and Altair's logos is
 * subject to Altair's trademark licensing policies.
 */

/**
 * @file	mom_main.c
 * @brief
 * The entry point function for MOM.
 */
#define _MOM_MAIN_C
#include <pbs_config.h> /* the master config generated by configure */

#ifdef PYTHON
#include <pbs_python_private.h>
#include <Python.h>
#include <pythonrun.h>
#include <wchar.h>
#endif

#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/times.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#ifdef _POSIX_MEMLOCK
#include <sys/mman.h>
#endif /* _POSIX_MEMLOCK */
#include <dirent.h>

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <libutil.h>

#include "auth.h"
#include "libpbs.h"
#include "pbs_ifl.h"
#include "server_limits.h"
#include "list_link.h"
#include "attribute.h"
#include "placementsets.h"
#include "resource.h"
#include "job.h"
#include "mom_func.h"
#include "pbs_nodes.h"
#include "svrfunc.h"
#include "pbs_error.h"
#include "log.h"
#include "net_connect.h"
#include "tpp.h"
#include "dis.h"
#include "resmon.h"
#include "batch_request.h"
#include "pbs_license.h"
#include "pbs_version.h"
#include "libsec.h"
#include "pbs_ecl.h"
#include "pbs_internal.h"
#include "pbs_idx.h"
#ifdef HWLOC
#include "hwloc.h"
#endif
#include "hook.h"
#include "mom_hook_func.h"
#include "work_task.h"
#include "pbs_share.h"
#include "mom_server.h"
#if MOM_ALPS
#include "mom_mach.h"
#endif /* MOM_ALPS */
#include "pbs_reliable.h"
#ifdef PMIX
#include "mom_pmix.h"
#endif /* PMIX */

#include "renew_creds.h"

#define STATE_UPDATE_TIME 10
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#ifndef PRIO_MIN
#define PRIO_MIN -20
#endif

/* Reducing tpp_request process for a minimum of 3 times to interleave other connections */
#define MAX_TPP_LOOPS 3

/*
 * Default "mutual exclusion" for job start/queue commit operations.  The
 * pointer is provided so multi-threaded mom implementations can replace it
 * with a pointer to a shared mutex.
 */

/* Global Data Items */
int mock_run = 0;
enum hup_action call_hup = HUP_CLEAR;
static int update_state_flag = 0;
double cputfactor = 1.00;
unsigned int default_server_port;
int exiting_tasks = 0;
float ideal_load_val = -1.0;
int idle_on_maxload = 0;
int internal_state = 0;
int internal_state_update = 0;
int termin_child = 0;
int do_debug_report = 0;
uid_t restrict_user_exempt_uids[NUM_RESTRICT_USER_EXEMPT_UIDS] = {0};
int svr_delay_entry = 0;
int mom_net_up = 0;
time_t mom_net_up_time = 0;
#ifdef WIN32
LASTINPUTINFO key_mouse_press = {sizeof(LASTINPUTINFO), 0};
int nrun_factor = 0;

void WINAPI PbsMomMain(DWORD dwArgc, LPTSTR *rgszArgv);
void WINAPI PbsMomHandler(DWORD dwControl);
DWORD WINAPI main_thread(void *pv);

/*
 * NOTE: Note the global state used by your service. Your service has a name,
 * state and a status handle used by SetServiceStatus.
 */
const TCHAR *const g_PbsMomName = __TEXT("PBS_MOM");
HANDLE g_hthreadMain = 0;
SERVICE_STATUS_HANDLE g_ssHandle = 0;
DWORD g_dwCurrentState = SERVICE_START_PENDING;
HANDLE hStop = NULL;
#endif /* WIN32 */
extern void mom_vnlp_report(vnl_t *vnl, char *header);

int alien_attach = 0; /* attach alien procs */
int alien_kill = 0;   /* kill alien procs */
int lockfds;
float max_load_val = -1.0;
int max_poll_downtime_val = PBS_MAX_POLL_DOWNTIME;
char *mom_domain;
char *mom_home;
char mom_host[PBS_MAXHOSTNAME + 1];
pid_t mom_pid;
int mom_run_state = 1;
char mom_short_name[PBS_MAXHOSTNAME + 1];
int next_sample_time = MAX_CHECK_POLL_TIME;
int max_check_poll = MAX_CHECK_POLL_TIME;
int min_check_poll = MIN_CHECK_POLL_TIME;
int inc_check_poll = 20;
int num_acpus = 1;
int num_pcpus = 1;
int num_oscpus = 1;
u_Long av_phy_mem = 0; /* physical memory in KB */
int num_var_env;
char *path_epilog;
char *path_jobs;
char *path_prolog;
char *path_spool;
char *path_undeliv;
char *path_addconfigs;
char path_addconfigs_reserved_prefix[] = "PBS";

char *path_hooks;
char *path_hooks_workdir;
char *path_rescdef;
hook *phook;
char *hook_suffix = HOOK_FILE_SUFFIX;
int hook_suf_len;
char hook_msg[HOOK_MSG_SIZE + 1];
int baselen;
char *psuffix;
struct dirent *pdirent;
DIR *dir;
/*char		pbs_current_user[PBS_MAXUSER] = "pbs_mom";*/ /* for libpbs.a */
/* above is TLS data now, strcpy the value "pbs_mom" into it in main */

char pbs_tmpdir[_POSIX_PATH_MAX] = TMP_DIR;
char pbs_jobdir_root[_POSIX_PATH_MAX] = "";
int pbs_jobdir_root_shared = FALSE;
vnl_t *vnlp = NULL; /* vnode list */
unsigned long hooks_rescdef_checksum = 0;

/* vnlp_from_hook: vnode list changes made by an exechost_startup hook, that */
/* sent to the server initially as part of the IS_HELLO/IS_CLUSTER_ADDR/ */
/* IS_CLUSTER_ADDR2 sequence . Then when successfully  sent to server,  */
/* entries matching HOOK_VNL_PERSISTENT_ATTRIBS will be merged with the */
/* main vnlp structure,  which gets  resent when server loses contact */
/* with mom, and server sends an IS_HELLO request */
vnl_t *vnlp_from_hook = NULL;

extern char *msg_startup1;
extern char *msg_init_chdir;
extern char *msg_corelimit;
int pbs_errno;
gid_t pbsgroup;
unsigned int pbs_mom_port;
unsigned int pbs_rm_port;
pbs_list_head mom_polljobs; /* jobs that must have resource limits polled */
pbs_list_head mom_deadjobs; /* jobs that need to purged, see chk_del_job */
int server_stream = -1;
pbs_list_head svr_newjobs; /* jobs being sent to MOM */
pbs_list_head svr_alljobs; /* all jobs under MOM's control */
time_t time_last_sample = 0;
extern time_t time_now;
time_t time_resc_updated = 0;
extern pbs_list_head svr_requests;
struct var_table vtable; /* see start_exec.c */

#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5)
extern pbs_list_head svr_allcreds;
#endif

#if MOM_ALPS
#define ALPS_REL_WAIT_TIME_DFLT 400000; /* 0.4 sec */
#define ALPS_REL_JITTER_DFLT 120000;	/* 0.12 sec */
#define ALPS_REL_TIMEOUT 600;		/* 10 min */
#define ALPS_CONF_EMPTY_TIMEOUT 10;	/* 10 sec */
#define ALPS_CONF_SWITCH_TIMEOUT 35;	/* 35 sec */
char *alps_client = NULL;
useconds_t alps_release_wait_time = ALPS_REL_WAIT_TIME_DFLT;
useconds_t alps_release_jitter = ALPS_REL_JITTER_DFLT;
int vnode_per_numa_node;
int alps_release_timeout;
int alps_confirm_empty_timeout;
int alps_confirm_switch_timeout;
#endif /* MOM_ALPS */
char *path_checkpoint = NULL;
static resource_def *rdcput;
static resource_def *rdwall;
int restart_background = FALSE;
int reject_root_scripts = FALSE;
int report_hook_checksums = TRUE;
int restart_transmogrify = FALSE;
int attach_allow = TRUE;
extern double wallfactor;
int suspend_signal;
int resume_signal;
int cycle_harvester = 0;	/* MOM configured for cycle harvesting */
int restrict_user = 0;		/* kill non PBS user procs */
int restrict_user_maxsys = 999; /* largest system user id */
int gen_nodefile_on_sister_mom = TRUE;
int vnode_additive = 1;
momvmap_t **mommap_array = NULL;
int mommap_array_size = 0;
unsigned long QA_testing = 0;

long joinjob_alarm_time = -1;
long job_launch_delay = -1; /* # of seconds to delay job launch due to pipe reads (pipe read timeout)  */
int update_joinjob_alarm_time = 0;
int update_job_launch_delay = 0;

#ifdef NAS		     /* localmod 015 */
unsigned long spoolsize = 0; /* default spoolsize = unlimited */
#endif			     /* localmod 015 */

#ifdef NAS /* localmod 153 */
static char quiesce_mom_flag_file[_POSIX_PATH_MAX] = "/PBS/flags/quiesce_mom";
int mom_should_quiesce = 0;
#endif /* localmod 153 */

#ifdef NAS_UNKILL	/* localmod 011 */
#define KP_WAIT_TIME 60 /* number of seconds to wait for kill
					   to do its deed before declaring the
					   process unkillable */

struct kp {
	pbs_list_link kp_link; /* linked list struct */
	pid_t pid;	       /* pid of process being killed */
	pid_t ppid;	       /* ppid of process being killed */
	u_Long start_time;     /* start_time of process being killed */
	time_t kill_time;      /* time() of first kill attempt */
};
typedef struct kp kp;

pbs_list_head killed_procs; /* procs killed by dorestrict_user() */
#endif			    /* localmod 011 */

void *jobs_idx = NULL;
pbs_list_head mom_pending_ruu;

unsigned long hook_action_id = 0;

pbs_list_head svr_allhooks;
/* hooks below ignored */
pbs_list_head svr_queuejob_hooks;
pbs_list_head svr_postqueuejob_hooks;
pbs_list_head svr_modifyjob_hooks;
pbs_list_head svr_resvsub_hooks;
pbs_list_head svr_modifyresv_hooks;
pbs_list_head svr_movejob_hooks;
pbs_list_head svr_runjob_hooks;
pbs_list_head svr_jobobit_hooks;
pbs_list_head svr_management_hooks;
pbs_list_head svr_modifyvnode_hooks;
pbs_list_head svr_periodic_hooks;
pbs_list_head svr_provision_hooks;
pbs_list_head svr_resv_confirm_hooks;
pbs_list_head svr_resv_begin_hooks;
pbs_list_head svr_resv_end_hooks;
pbs_list_head svr_hook_job_actions;
pbs_list_head svr_hook_vnl_actions;
int mom_recvd_ip_cluster_addrs = 0;

/* the mom hooks */
pbs_list_head svr_execjob_begin_hooks;
pbs_list_head svr_execjob_prologue_hooks;
pbs_list_head svr_execjob_epilogue_hooks;
pbs_list_head svr_execjob_preterm_hooks;
pbs_list_head svr_execjob_launch_hooks;
pbs_list_head svr_execjob_end_hooks;
pbs_list_head svr_exechost_periodic_hooks;
pbs_list_head svr_exechost_startup_hooks;
pbs_list_head svr_execjob_attach_hooks;
pbs_list_head svr_execjob_resize_hooks;
pbs_list_head svr_execjob_abort_hooks;
pbs_list_head svr_execjob_postsuspend_hooks;
pbs_list_head svr_execjob_preresume_hooks;

/* the task lists */
pbs_list_head task_list_immed;
pbs_list_head task_list_interleave;
pbs_list_head task_list_timed;
pbs_list_head task_list_event;

#ifdef WIN32
/* copy request list */
pbs_list_head mom_copyreqs_list;
#endif

#ifndef WIN32
#ifdef RLIM64_INFINITY
struct rlimit64 orig_stack_size;
struct rlimit64 orig_nproc_limit;
struct rlimit64 orig_core_limit;
#else
struct rlimit orig_stack_size;
struct rlimit orig_nproc_limit;
struct rlimit orig_core_limit;
#endif /* RLIM64... */
#endif /* WIN32 */

/* Local Data Items */

static int nconfig;	      /* items in conf file */
static time_t idle_avail = 0; /* seconds for keyboard to be idle */
static time_t idle_busy = 10; /* seconds for keyboard to remain */
static int idle_check = -1;   /* indicate if doing idle check */
time_t idle_poll = 1;	      /* rate to poll keyboard when ! busy */
static time_t went_busy = 0;  /* time keyboard went busy */
static time_t prior_key = 0;  /* time of prior keystroke/mouse */
static int restrictrm = 0;    /* restricted RM request */
int kill_jobs_on_exit = 0;    /* kill running jobs on Mom exit */
static char *path_checkpoint_from_getopt = NULL;
static char *path_checkpoint_from_getenv = NULL;
static char *path_checkpoint_default = NULL;
static char path_checkpoint_buf[_POSIX_PATH_MAX] = "\0";
static time_t maxtm; /* see getkbdtime() */

#ifdef WIN32

/**
 * This global variable is used to indicate whether PBS_INTERACTIVE service
 * has been registered with Service Control Manager
 *
 *	0  - Error
 *	1  - PBS_INTERACTIVE service is registered
 *	-1 - PBS_INTERACTIVE service is not registered
 */
int interactive_svc_avail = 0;

#endif

/**
 *	To handle new configuration file formats (beginning with the vnode-
 *	specific data needed for GRUNT), we introduce the notion that any
 *	new-style mom configuration file must declare its version number at
 *	the beginning of the file, via a "$configversion" directive.
 *
 *	At present, we handle only a single new version number, known internally
 *	as CONFIG_VNODEVERS.
 */
enum configvers {
	CONFIG_MINVERS = 2,
	CONFIG_VNODEVERS = 2,
	CONFIG_MAXVERS = 2
};

struct config_list {
	struct config c;
	struct config_list *c_link;
};
static handler_ret_t config_versionhandler(char *, const char *, FILE *);

static handler_ret_t addclient(char *);
static handler_ret_t add_mom_action(char *);
static handler_ret_t config_verscheck(char *);
static handler_ret_t cputmult(char *);
static handler_ret_t parse_config(char *);
static handler_ret_t prologalarm(char *);
static handler_ret_t set_joinjob_alarm(char *);
static handler_ret_t set_job_launch_delay(char *);
static handler_ret_t restricted(char *);
static handler_ret_t set_alien_attach(char *);
static handler_ret_t set_alien_kill(char *);
#if MOM_ALPS
static handler_ret_t set_alps_client(char *);
static handler_ret_t set_alps_release_wait_time(char *);
static handler_ret_t set_alps_release_jitter(char *);
static handler_ret_t set_alps_release_timeout(char *);
static handler_ret_t set_alps_confirm_empty_timeout(char *);
static handler_ret_t set_vnode_per_numa_node(char *);
static handler_ret_t set_alps_confirm_switch_timeout(char *);
#endif /* MOM_ALPS */
static handler_ret_t set_attach_allow(char *);
static handler_ret_t set_checkpoint_path(char *);
static handler_ret_t set_enforcement(char *);
static handler_ret_t set_jobdir_root(char *);
static handler_ret_t set_kbd_idle(char *);
static handler_ret_t set_max_check_poll(char *);
static handler_ret_t set_min_check_poll(char *);
static handler_ret_t set_momname(char *);
static handler_ret_t set_momport(char *);
#ifdef WIN32
static handler_ret_t set_nrun_factor(char *);
#endif
static handler_ret_t set_restart_background(char *);
static handler_ret_t set_restart_transmogrify(char *);
static handler_ret_t set_restrict_user(char *);
static handler_ret_t set_restrict_user_maxsys(char *);
static handler_ret_t set_restrict_user_exceptions(char *);
static handler_ret_t set_gen_nodefile_on_sister_mom(char *);
static handler_ret_t set_suspend_signal(char *);
static handler_ret_t set_tmpdir(char *);
static handler_ret_t set_vnode_additive(char *);
static handler_ret_t setidealload(char *);
static handler_ret_t setlogevent(char *);
static handler_ret_t set_reject_root_scripts(char *);
static handler_ret_t set_report_hook_checksums(char *);
static handler_ret_t setmaxload(char *);
static handler_ret_t set_max_poll_downtime(char *);
static handler_ret_t usecp(char *);
static handler_ret_t wallmult(char *);
#ifdef NAS /* localmod 015 */
static handler_ret_t set_spoolsize(char *);
#endif /* localmod 015 */

static struct specials {
	char *name;
	handler_ret_t (*handler)(char *);
} special[] = {
	/* alphabetized by name */
	{"action", add_mom_action},
	/*
	 ****************************************************
	 ** WARNING
	 ** These "alien" entries are undocumented and are for
	 ** prototype purposes only.  DO NOT USE.
	 ****************************************************
	 */
	{"alien_attach", set_alien_attach},
	{"alien_kill", set_alien_kill},
#if MOM_ALPS
	{"alps_client", set_alps_client},
	{"alps_confirm_empty_timeout", set_alps_confirm_empty_timeout},
	{"alps_release_wait_time", set_alps_release_wait_time},
	{"alps_release_jitter", set_alps_release_jitter},
	{"alps_release_timeout", set_alps_release_timeout},
	{"vnode_per_numa_node", set_vnode_per_numa_node},
	{"alps_confirm_switch_timeout", set_alps_confirm_switch_timeout},
#endif /* MOM_ALPS */
	{"attach_allow", set_attach_allow},
	{"checkpoint_path", set_checkpoint_path},
	{"clienthost", addclient},
	{"configversion", config_verscheck},
	{"cputmult", cputmult},
	{"enforce", set_enforcement},
	{"ideal_load", setidealload},
	{"jobdir_root", set_jobdir_root},
	{"kbd_idle", set_kbd_idle},
	{"logevent", setlogevent},
	{"max_check_poll", set_max_check_poll},
	{"max_load", setmaxload},
	{"max_poll_downtime", set_max_poll_downtime},
	{"min_check_poll", set_min_check_poll},
	{"momname", set_momname},
#ifdef WIN32
	{"nrun_factor", set_nrun_factor},
#endif
	{"port", set_momport},
	{"prologalarm", prologalarm},
	{"sister_join_job_alarm", set_joinjob_alarm},
	{"job_launch_delay", set_job_launch_delay},
	{"restart_background", set_restart_background},
	{"restart_transmogrify", set_restart_transmogrify},
	{"restrict_user", set_restrict_user},
	{"restrict_user_exceptions", set_restrict_user_exceptions},
	{"restrict_user_maxsysid", set_restrict_user_maxsys},
	{"restricted", restricted},
	{"gen_nodefile_on_sister_mom", set_gen_nodefile_on_sister_mom},
#ifdef NAS /* localmod 015 */
	/*
	 * spool size limit
	 */
	{"spool_size", set_spoolsize},
#endif /* localmod 015 */
	{"suspendsig", set_suspend_signal},
	{"tmpdir", set_tmpdir},
	{"vnodedef_additive", set_vnode_additive},
	{"usecp", usecp},
	{"wallmult", wallmult},
	{"reject_root_scripts", set_reject_root_scripts},
	{"report_hook_checksums", set_report_hook_checksums},
	{NULL, NULL}};

static struct specials addspecial[] = {
	{NULL, NULL}};

void *job_attr_idx = NULL;
char *log_file = NULL;
char *path_log;
#ifndef WIN32
sigset_t allsigs;
#endif
char *ret_string;
int ret_size;
struct config *config_array = NULL;
struct config_list *config_list = NULL;
int rm_errno;
unsigned int reqnum = 0; /* the packet number */
int port_care = 1;	 /* secure connecting ports */
uid_t uid = 0;		 /* uid we are running with */
int alarm_time = 10;	 /* time before alarm */
int nice_val = 0;	 /* nice daemon by this much */

char **maskclient = NULL; /* wildcard connections */
int mask_num = 0;
int mask_max = 0;
u_long localaddr = 0;

char extra_parm[] = "extra parameter(s)";
char no_parm[] = "required parameter not found";

int cphosts_num = 0;
struct cphosts *pcphosts = 0;
int enable_exechost2 = 0;
static int config_file_specified = 0;
static char config_file[_POSIX_PATH_MAX] = "config";

struct mom_action mom_action[(int) LastAction] = {
	{"terminate", 0, Default, NULL, NULL},
	{"checkpoint", 0, Default, NULL, NULL},
	{"checkpoint_abort", 0, Default, NULL, NULL},
	{"restart", 0, Default, NULL, NULL},
	{"multinodebusy", 0, Default, NULL, NULL}};

/*
 **	These routines are in the "dependent" code.
 */
extern void dep_initialize(void);
extern void dep_cleanup(void);

/* External Functions */
extern void catch_child(int);
extern void init_abort_jobs(int, pbs_list_head *);
extern void scan_for_exiting(void);
#ifdef NAS /* localmod 015 */
extern int to_size(char *, struct size_value *);
#endif /* localmod 015 */

extern void cleanup_hooks_workdir(struct work_task *);

extern char *getuname(void);
extern int get_permission(char *perm);
extern handler_ret_t check_interactive_service();
extern void finish_loop(time_t waittime);
extern void usage(char *prog);

#ifndef WIN32
extern void scan_for_terminated(void);

/* Local public functions */

extern void stop_me(int);
extern void catch_USR2(int);
extern void catch_hup(int);
extern void toolong(int);
#endif

extern eventent *event_dup(eventent *ep, job *pjob, hnodent *pnode);

/* Local private functions */
static char *mk_dirs(char *);
static void check_busy(double);

/**
 * @brief
 *	logs error message
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return NULL
 *
 */
char *
nullproc(struct rm_attribute *attrib)
{

	log_err(-1, __func__, "should not be called");
	return NULL;
}

char *pbs_mach = NULL;

/**
 * @brief
 *	gets machine architecture else logs error msg
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return string
 * @retval PBS_ARCH	Success
 * @retval NULL		Failure
 *
 */
char *
arch(struct rm_attribute *attrib)
{
	if (attrib) {
		log_err(-1, __func__, extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	if (pbs_mach != NULL)
		return pbs_mach;
	else
		return PBS_MACH;
}

/**
 * @brief
 *	requsts username else logs error msg on failure
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return string
 * @retval username	Success
 * @retval NULL		Failure
 *
 */
static char *
requname(struct rm_attribute *attrib)
{
	char *cp;

	if (attrib) {
		log_err(-1, __func__, extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}
	cp = getuname();
	return cp;
}

/**
 * @brief
 *	checks whether valid user
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return	string
 * @retval	yes	Success
 * @retval	no	Failure
 *
 */
static char *
validuser(struct rm_attribute *attrib)
{
	struct passwd *p;

	if (attrib == NULL || attrib->a_value == NULL) {
		log_err(-1, __func__, no_parm);
		rm_errno = RM_ERR_NOPARAM;
		return NULL;
	}

	p = getpwnam(attrib->a_value);
	if (p) {
		return "yes";
	} else {
		return "no";
	}
}

/**
 * @brief
 *	returns the current load average on node
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return   string
 * @retval   loadvalue   Success
 * @retval   NULL         Failure
 *
 */
char *
loadave(struct rm_attribute *attrib)
{
	static char ret_string[20];
	double la;

	if (attrib) {
		log_err(-1, __func__, extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}

	if (get_la(&la) != 0) {
		rm_errno = RM_ERR_SYSTEM;
		return NULL;
	}

	sprintf(ret_string, "%.2f", la);
	return ret_string;
}

/**
 * @brief
 *	Output the various resource lists.
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return  string
 * @retval  log_buffer  Success
 * @retval  NULL	Failure
 *
 */
char *
reslist(struct rm_attribute *attrib)
{
	struct config *cp;
	extern struct config common_config[];
	extern struct config standard_config[];
	extern struct config dependent_config[];
	size_t len;

	if (attrib) {
		log_err(-1, __func__, extra_parm);
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}

	log_buffer[0] = '\0';

	for (cp = common_config; cp->c_name; cp++) {
		strcat(log_buffer, cp->c_name);
		strcat(log_buffer, " ");
	}

	for (cp = standard_config; cp->c_name; cp++) {
		strcat(log_buffer, cp->c_name);
		strcat(log_buffer, " ");
	}

	for (cp = dependent_config; cp->c_name; cp++) {
		strcat(log_buffer, cp->c_name);
		strcat(log_buffer, " ");
	}

	if (config_array) {
		for (cp = config_array; cp->c_name; cp++) {
			strcat(log_buffer, cp->c_name);
			strcat(log_buffer, " ");
		}
	}

	len = strlen(log_buffer);
	if (len > 0) {
		log_buffer[len - 1] = '\0';
		return log_buffer;
	} else
		return NULL;
}

struct config common_config[] = {
	{"arch", {arch}},
	{"uname", {requname}},
	{"validuser", {validuser}},
	{"reslist", {reslist}},
	{NULL, {nullproc}}};

/**
 * @brief
 *	Search the array of resources read from the config files.
 *
 * @param[in] where - pointer to config structure
 * @param[in] what  - char pointer holding what to search
 *
 * @return	structure handle
 * @retval	pointer to config structure	Success
 * @retval      NULL				Failure
 *
 */
struct config *
rm_search(struct config *where, char *what)
{
	struct config *cp;

	if (where == NULL || what == NULL)
		return NULL;

	for (cp = where; cp->c_name; cp++) {
		if (strcmp(cp->c_name, what) == 0)
			break;
	}
	return (cp->c_name ? cp : NULL);
}

/**
 * @brief
 *	Search the various resource lists.
 *
 * @param[in] res - string holding resource
 * @param[in] attr - pointer to rm_attribute structure
 *
 * @return	string
 * @retval	structure handler to config	Success
 * @retval      NULL				Failure
 *
 */
char *
dependent(char *res, struct rm_attribute *attr)
{
	struct config *ap;
	extern struct config standard_config[];
	extern struct config dependent_config[];

	ap = rm_search(common_config, res);
	if (ap)
		return (ap->c_u.c_func(attr));

	ap = rm_search(standard_config, res);
	if (ap)
		return (ap->c_u.c_func(attr));

	ap = rm_search(dependent_config, res);
	if (ap)
		return (ap->c_u.c_func(attr));

	rm_errno = RM_ERR_UNKNOWN;
	return NULL;
}

/**
 * @brief
 *	wrapper function to dep_cleanup
 *
 */
void
cleanup(void)
{
	dep_cleanup();
}

/**
 * @brief
 *	Clean up after a signal.
 *
 * @param[in] sig - signal number
 *
 * @return Void
 *
 */
void
die(int sig)
{
	if (sig > 0) {
		sprintf(log_buffer, "caught signal %d", sig);
		log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, __func__, log_buffer);
	} else
		log_event(PBSEVENT_SYSTEM, 0, LOG_ALERT, __func__,
			  "abnormal termination");

	cleanup();
	pbs_idx_destroy(jobs_idx);
	unload_auths();
	log_close(1);
#ifdef WIN32
	ExitThread(1);
#else
	exit(1);
#endif
}

/**
 * @brief
 *	Performs initialization steps like loading pbs.conf values,
 *	setting core limit size, running platform-specific initializations
 *	(e.g. topology data gathering),
 *	running the exechost_startup hook, and
 *	checking that there are no bad combinations of sharing values
 *	across the vnodes.
 *
 * @return void
 *
 */
void
initialize(void)
{
	unsigned int i;
	void *temp_idx;
	char hook_msg[HOOK_MSG_SIZE + 1];
	char hook_buf[HOOK_BUF_SIZE + 1];
	mom_hook_input_t hook_input;
	mom_hook_output_t hook_output;
	int hook_errcode = 0;
	int hook_rc = 0;
	hook *last_phook = NULL;
	unsigned int hook_fail_action = 0;
	int ret;
	char none[] = "<unset>";
	enum vnode_sharing hostval;

	/* set limits that can be modified by the Admin */
#ifndef WIN32 /* ---- UNIX ------------------------------------------*/
#ifdef RLIMIT_CORE
	int char_in_cname = 0;

	(void) pbs_loadconf(0);
	set_log_conf(pbs_conf.pbs_leaf_name, pbs_conf.pbs_mom_node_name,
		     pbs_conf.locallog, pbs_conf.syslogfac,
		     pbs_conf.syslogsvr, pbs_conf.pbs_log_highres_timestamp);

	if (pbs_conf.pbs_core_limit) {
		char *pc = pbs_conf.pbs_core_limit;
		while (*pc != '\0') {
			if (!isdigit(*pc)) {
				/* there is a character in core limit */
				char_in_cname = 1;
				break;
			}
			pc++;
		}
	}

#if defined(RLIM64_INFINITY)
	if (pbs_conf.pbs_core_limit) {
		struct rlimit64 corelimit;
		corelimit.rlim_max = RLIM64_INFINITY;
		if (strcmp("unlimited", pbs_conf.pbs_core_limit) == 0)
			corelimit.rlim_cur = RLIM64_INFINITY;
		else if (char_in_cname == 1) {
			log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_NODE, LOG_WARNING,
				   __func__, msg_corelimit);
			corelimit.rlim_cur = RLIM64_INFINITY;
		} else
			corelimit.rlim_cur =
				(rlim64_t) atol(pbs_conf.pbs_core_limit);
		/* get system core limit */
		(void) getrlimit64(RLIMIT_CORE, &orig_core_limit);

		(void) setrlimit64(RLIMIT_CORE, &corelimit);
	}

#else  /* set rlimit 32 bit */

	if (pbs_conf.pbs_core_limit) {
		struct rlimit corelimit;
		corelimit.rlim_max = RLIM_INFINITY;
		if (strcmp("unlimited", pbs_conf.pbs_core_limit) == 0)
			corelimit.rlim_cur = RLIM_INFINITY;
		else if (char_in_cname == 1) {
			log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_NODE, LOG_WARNING,
				   __func__, msg_corelimit);
			corelimit.rlim_cur = RLIM_INFINITY;
		} else
			corelimit.rlim_cur =
				(rlim_t) atol(pbs_conf.pbs_core_limit);

		/* get system core limit */
		(void) getrlimit(RLIMIT_CORE, &orig_core_limit);

		(void) setrlimit(RLIMIT_CORE, &corelimit);
	}
#endif /* RLIM64_INFINITY */

#endif /* RLIMIT_CORE */
#endif /* !WIN32 ---------------------------------------------------------- */

	num_pcpus = num_acpus = num_oscpus = 0;
	dep_initialize();
	if (num_oscpus == 0)
		num_oscpus = num_pcpus;
	sprintf(log_buffer, "pcpus=%d, OS reports %d cpu(s)",
		num_pcpus, num_oscpus);
	log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, "initialize", log_buffer);

	if (vnlp_from_hook == NULL) {
		if (vnl_alloc(&vnlp_from_hook) == NULL) {
			log_err(PBSE_SYSTEM, __func__, "vnl_alloc failed");
			return;
		}
		vnlp_from_hook->vnl_modtime = time(NULL);
	}

	mom_hook_input_init(&hook_input);
	hook_input.vnl = (vnl_t *) vnlp;

	mom_hook_output_init(&hook_output);
	hook_output.reject_errcode = &hook_errcode;
	hook_output.last_phook = &last_phook;
	hook_output.fail_action = &hook_fail_action;
	hook_output.vnl = (vnl_t *) vnlp_from_hook;

	if (setup_resc(1) != 0) {
		/* log_buffer set in setup_resc */
		log_err(-1, "setup_resc", "warning: failed to setup resourcdef");
	}

	switch ((hook_rc = mom_process_hooks(HOOK_EVENT_EXECHOST_STARTUP,
					     PBS_MOM_SERVICE_NAME,
					     mom_host, &hook_input, &hook_output, hook_msg,
					     sizeof(hook_msg), 0))) {

		case 2: /* no hook script executed - go ahead and accept event */
			break;
		default:
			/* a value of '0' means explicit reject encountered, and '1' means explicit accept. */
			if ((hook_rc != 0) && (hook_rc != 1)) {
				/* we've hit an internal error (malloc error, full disk, etc...), so */
				/* treat this now like a  hook error so hook fail_action  */
				/* will be consulted.  */
				/* Before, behavior of an internal error was to ignore it! */
				hook_errcode = PBSE_HOOKERROR;
			}
			if (hook_errcode == PBSE_HOOKERROR) { /* error */
				if ((last_phook != NULL) &&
				    (last_phook->fail_action &
				     HOOK_FAIL_ACTION_OFFLINE_VNODES)) {
					snprintf(hook_buf,
						 HOOK_BUF_SIZE + 1,
						 "1,%s", last_phook->hook_name);

					ret = vn_addvnr(vnlp_from_hook,
							mom_short_name,
							VNATTR_HOOK_OFFLINE_VNODES,
							hook_buf, 0, 0, NULL);

					if (ret != 0) {
						snprintf(log_buffer,
							 sizeof(log_buffer),
							 "Failed to add to "
							 "vnlp_from_hook: %s=%s",
							 VNATTR_HOOK_OFFLINE_VNODES,
							 hook_buf);
						log_event(PBSEVENT_DEBUG2,
							  PBS_EVENTCLASS_HOOK, LOG_INFO,
							  last_phook->hook_name,
							  log_buffer);
					}
					vnlp_from_hook->vnl_modtime = time(NULL);
				}
				break;
			} else if (hook_fail_action & HOOK_FAIL_ACTION_CLEAR_VNODES) {
				/* no hook error */
				vnl_t *vnlp_tmp = NULL;

				/* of vnlp_from_hook */
				if (vnl_alloc(&vnlp_tmp) == NULL) {
					log_err(PBSE_SYSTEM, __func__,
						"vnl_alloc failed");
					return;
				}
				ret = vn_addvnr(vnlp_tmp, mom_short_name,
						VNATTR_HOOK_OFFLINE_VNODES, "0", 0,
						0, NULL);
				if (ret != 0) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "Failed to add to "
						 "vnlp_tmp: %s=%s",
						 VNATTR_HOOK_OFFLINE_VNODES,
						 hook_buf);
					log_event(PBSEVENT_DEBUG2,
						  PBS_EVENTCLASS_HOOK, LOG_INFO,
						  last_phook->hook_name,
						  log_buffer);
					vnl_free(vnlp_tmp);
					vnlp_tmp = NULL;
					return;
				}
				if (vnlp_from_hook->vnl_used > 0) {
					/* the clear offline_vnodes action ,*/
					/* as stored in 'vnlp_tmp' */
					/* must appear before other vn */
					/* actions (currently in
					 * vnlp_from_hook),  since it would be */
					/* clearing the states of all vnodes */
					/* and their comments. vnlp_from_hook */
					/* may contain vnode state and */
					/* comment changes, and we would not */
					/* want to override that. */
					vn_merge(vnlp_tmp, vnlp_from_hook,
						 NULL);
					vnl_free(vnlp_from_hook);
					vnlp_from_hook = vnlp_tmp;
				} else {
					vn_merge(vnlp_from_hook, vnlp_tmp,
						 NULL);
					vnl_free(vnlp_tmp);
				}
				vnlp_tmp = NULL;
			}
	}

	mom_vnlp_report(vnlp_from_hook, "vnlp_from_hook");

	if (vnlp_from_hook->vnl_used == 0) {
		vnl_free(vnlp_from_hook);
		vnlp_from_hook = NULL;
	}

	if (vnlp == NULL)
		return;

	/*
	 *	Check that there are no bad combinations of sharing values
	 *	across the vnodes.
	 */
	if ((temp_idx = pbs_idx_create(0, 0)) == NULL) {
		log_err(-1, __func__, "Failed to create index for checking sharing value on vnodes");
		die(0);
	}

	for (i = 0; i < vnlp->vnl_used; i++) {
		vnal_t *vnrlp = VNL_NODENUM(vnlp, i);
		char *host = attr_exist(vnrlp, "resources_available.host");
		char *share;
		char *exclhost = none;
		char *exclhost_frmidx = none;
		enum vnode_sharing shareval;

		if (host == NULL)
			/* mom_host and mom_short_name are different!! */
			/* use mom short name by default */
			host = mom_short_name;

		share = attr_exist(vnrlp, "sharing");
		shareval = str_to_vnode_sharing(share);
		if (shareval != VNS_UNSET)
			exclhost = vnode_sharing_to_str(shareval);

		/* search for host */
		if (pbs_idx_find(temp_idx, (void **) &host, (void **) &exclhost_frmidx, NULL) != PBS_IDX_RET_OK) {
			if (pbs_idx_insert(temp_idx, host, (void *) exclhost) != PBS_IDX_RET_OK) {
				log_errf(errno, __func__, "Failed to add exechost = %s for host %s in index", exclhost, host);
				die(0);
			}
			continue;
		}

		/* the host exists, check if the saved value is the same */
		if (exclhost_frmidx == exclhost)
			continue;

		/* they are different, now check if it is a bad combo */
		hostval = str_to_vnode_sharing(exclhost_frmidx);
		if (hostval == VNS_DFLT_EXCLHOST ||
		    hostval == VNS_FORCE_EXCLHOST ||
		    shareval == VNS_DFLT_EXCLHOST ||
		    shareval == VNS_FORCE_EXCLHOST) {
			sprintf(log_buffer,
				"It is erroneous to mix sharing=%s "
				"for vnode %s with sharing=%s which "
				"is set for other vnodes on host %s",
				exclhost, vnrlp->vnal_id,
				exclhost_frmidx, host);
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_NODE,
				  LOG_NOTICE, __func__, log_buffer);
			die(0);
		}
	}

	pbs_idx_destroy(temp_idx);

	if (joinjob_alarm_time == -1)
		joinjob_alarm_time = DEFAULT_JOINJOB_ALARM;

	if (job_launch_delay == -1)
		job_launch_delay = DEFAULT_JOB_LAUNCH_DELAY;

	time_delta_hellosvr(MOM_DELTA_RESET);
}

/**
 * @brief
 *	Check for fatal memory allocation error.
 *
 * @param[in]  buf - reallocated memory
 *
 * @return Void
 *
 */
void
memcheck(char *buf)
{
	if (buf)
		return;
	log_err(-1, "memcheck", "memory allocation failed");
	die(0);
}

/**
 * @brief
 *	Check the ret_string buffer to make sure that there is
 *	enough room starting at *spot to hold len characters more.
 *	If not, realloc the buffer and make *spot point to
 *	the corresponding place that it used to point to in
 *	the old buffer.
 *
 * @param[in] spot - buffer
 * @param[in] len - buffer len
 *
 * @return Void
 *
 */
void
checkret(char **spot, int len)
{
	char *hold;

	if ((*spot - ret_string) < (ret_size - len))
		return;

	ret_size += len * 2; /* new buf size */
	sprintf(log_buffer, "size increased to %d", ret_size);
	log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__, log_buffer);
	hold = realloc(ret_string, ret_size); /* new buf */
	memcheck(hold);
	*spot = *spot - ret_string + hold; /* new spot in buf */
	ret_string = hold;
}

/**
 * @brief
 *	skipwhite - process the string to make it blank free
 *
 * @param[in] str - string to be processed
 *
 * @return	string
 * @retval	string with no blanks
 *
 */
char *
skipwhite(char *str)
{
	for (; *str; str++) {
		if (!isspace(*str))
			break;
	}
	return str;
}

/**
 * @brief
 *	copies string in str to string tok
 *
 * @param[in] str - string to be copied
 * @param[in] tok - destination string to be copied to
 * @param[in] len - size of string str
 *
 * @return	string
 * @retval	destination string "tok"
 *
 */
char *
tokcpy(char *str, char *tok, size_t len)
{
	size_t i;

	for (i = 0; *str && (i < len); str++, tok++, i++) {
		if (!isalnum(*str) && *str != ':' && *str != '_' && check_spl_ch(*str))
			break;
		*tok = *str;
	}
	*tok = '\0';
	return str;
}

#define TOKCPY(a, b) tokcpy(a, b, sizeof(b))

/**
 * @brief
 *	removes new line from str
 *
 * @param[in] str - string to be processed
 *
 * @return Void
 *
 */
void
rmnl(char *str)
{
	int i;

	i = strlen(str);
	while (--i) {
		if ((*(str + i) != '\n') && !isspace((int) *(str + i)))
			break;
		*(str + i) = '\0';
	}
}

/**
 * @brief
 *	Similar to tokcpy() with only whitespace as the delimiting characters
 *
 * @param[in] str - string to be copied
 * @param[in] tok - destination string to be copied to
 * @param[in] len - size of string str
 *
 * @return string
 * @retval processed destination string "tok"
 *
 */
char *
wtokcpy(char *str, char *tok, int len)
{
	int i;
	for (i = 0; *str && (i < len); str++, tok++, i++) {
		if (isspace((int) *str))
			break;
		*tok = *str;
	}
	*tok = '\0';
	return str;
}

/**
 * @brief
 *	malloc memory and make a copy of the path input string that does not
 *	contain any double quote marks
 *
 * @param[in] path - char pointer holding input path
 *
 * @return string
 * @retval processed path string
 *
 */
char *
remove_quotes(char *path)
{
	char *dp, *dup;

	if (!path || !(dup = strdup(path)))
		return NULL;
	else
		dp = dup;

	do {
		if (*path != '"')
			*dp++ = *path;
	} while (*path++);

	return dup;
}

#ifdef WIN32
extern void stop_pbs_interactive();
#endif

/**
 * @brief
 *	add_mom_action - Parse mom action command from mom config file and add
 *	into mom_action array
 *
 * @param[in]	str -	line from mom config file which contain action
 *			command for mom
 *
 * @return	handler_ret_t
 * @retval	HANDLER_FAIL	- on fail
 * @retval	HANDLER_SUCCESS	- on success
 *
 */
static handler_ret_t
add_mom_action(char *str)
{
	char arg[_POSIX_PATH_MAX + 1];
	int i;
	int count;
	char *pc;
	int na;
	char **pargs;
	char *scp;
	int tout;
	int white;

	if (*str == '\0')
		return HANDLER_FAIL;

	/* first token is name of event */
	str = TOKCPY(str, arg);
	str = skipwhite(str);
	if (*str == '\0')
		return HANDLER_FAIL;
	for (na = 0; na < (int) LastAction; na++) {
		if (strcmp(arg, mom_action[na].ma_name) == 0) {
			/* have a valid event name */
			break;
		}
	}
	if (na >= (int) LastAction)
		return HANDLER_FAIL;

	/* next should come the time out value */
	str = TOKCPY(str, arg);
	str = skipwhite(str);
	if (*str == '\0')
		return HANDLER_FAIL;
	if (!isdigit((int) *arg))
		return HANDLER_FAIL;
	tout = atoi(arg);

	/* next is the action verb: a script or some keyword */
	if (*str == '!') {

		/* script specified */
		str = process_string(++str, arg, _POSIX_PATH_MAX);
		str = skipwhite(str);

		if (is_full_path(arg)) {

			scp = malloc(strlen(arg) + 1);
			if (scp == NULL) {
				return HANDLER_FAIL;
			}
			strcpy(scp, arg);
		} else {
			/* convert relative path to an absolute */
			/* path based on PBS_HOME/mom_priv      */

			scp = malloc(strlen(arg) + strlen(mom_home) + 2);
			if (scp == NULL) {
				return HANDLER_FAIL;
			}
			strcpy(scp, mom_home);
			strcat(scp, "/");
			strcat(scp, arg);
		}

		/* now count up the number of args */

		white = -1;
		count = 0;
		pargs = 0;
		pc = str;
		while (*pc) {
			if (isspace((int) *pc)) {
				if (white != 1)
					white = 1;
			} else {
				if (white != 0) {
					white = 0;
					count++;
				}
			}
			pc++;
		}
		pargs = (char **) malloc((count + 1) * sizeof(char *));
		if (pargs == NULL) {
			free(scp);
			return HANDLER_FAIL;
		}

		/* now we know how many and have space, copy each arg */

		for (i = 0; i < count; i++) {
			str = wtokcpy(str, arg, _POSIX_PATH_MAX);
			str = skipwhite(str);
			if ((*(pargs + i) = strdup(arg)) == NULL) {
				for (; i >= 0; i--) {
					free(*(pargs + i));
				}
				free(scp);
				free(pargs);
				return HANDLER_FAIL;
			}
		}
		*(pargs + i) = NULL;

		/* now we can set the action array member */

		mom_action[na].ma_verb = Script;
		mom_action[na].ma_timeout = tout;
		mom_action[na].ma_script = scp;
		mom_action[na].ma_args = pargs;
		goto done;
	}

	/* not a script, must be a recognized verb */

	if (strcmp(arg, "requeue") == 0) {

		/* Requeue Verb */

		mom_action[na].ma_verb = Requeue;
		mom_action[na].ma_timeout = tout;
		mom_action[na].ma_script = NULL;
		mom_action[na].ma_args = NULL;

	} else
		return HANDLER_FAIL; /* error */

done:
	if (mom_action[na].ma_verb == Script)
		sprintf(log_buffer, "%s: %s", mom_action[na].ma_name,
			mom_action[na].ma_script);
	else
		sprintf(log_buffer, "%s: %s", mom_action[na].ma_name, arg);

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
		  "action", log_buffer);
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	adds client by name.
 *
 * @param[in] name - name of host
 *
 * @return	u_long
 * @retval	0		Failure
 * @retval	ipaddr of host	Success
 *
 */
static u_long
addclient_byname(char *name)
{
	struct hostent *host;
	struct in_addr saddr;
	u_long ipaddr = 0;
	int i;

	if ((host = gethostbyname(name)) == NULL) {
		sprintf(log_buffer, "host %s not found", name);
		log_err(-1, __func__, log_buffer);
		return 0;
	}

	for (i = 0; host->h_addr_list[i]; i++) {
		memcpy((char *) &saddr, host->h_addr_list[i], host->h_length);
		ipaddr = ntohl(saddr.s_addr);
		addrinsert(ipaddr);
	}
	return ipaddr;
}

/**
 * @brief
 *	wrapper func for addclient_byname.
 *
 * @param[in] name - name of host
 *
 * @return	handler_ret_t (return value)
 * @retval      HANDLER_FAIL(0)			Failure
 * @retval	HANDLER_SUCCESS(1)		Success
 *
 */

static handler_ret_t
addclient(char *name)
{
	if (addclient_byname(name) == 0)
		return HANDLER_FAIL;
	else
		return HANDLER_SUCCESS;
}

/**
 * @brief
 *	sets the log event
 *
 * @param[in] value - log value
 *
 * @return      handler_ret_t (return value)
 * @retval      HANDLER_FAIL(0)                 Failure
 * @retval      HANDLER_SUCCESS(1)              Success
 *
 */

static handler_ret_t
setlogevent(char *value)
{
	char *bad;

	*log_event_mask = strtol(value, &bad, 0);
	tpp_set_logmask(*log_event_mask);
	if ((*bad == '\0') || isspace((int) *bad))
		return HANDLER_SUCCESS;
	else
		return HANDLER_FAIL;
}

/**
 * @brief
 *	Set the configuration flag that defines whether the hook files/scripts
 *	or job scripts to be run under root are rejected by mom.
 *
 * @param[in] value - log value
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_reject_root_scripts(char *value)
{
	return (set_boolean(__func__, value, &reject_root_scripts));
}

/**
 * @brief
 *	Set the configuration flag that tells the mom to send the checksums
 *	of the hooks it knows about.
 *
 * @param[in] value - log value
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_report_hook_checksums(char *value)
{
	return (set_boolean(__func__, value, &report_hook_checksums));
}

/**
 * @brief
 *	sets log event if host is restricted.
 *
 * @param[in] name - name of host
 *
 * @return	handler_ret_t
 * @retval	HANDLER_FAIL(0)		Failure
 * @retval	HANDLER_SUCCESS		Success
 *
 */

static handler_ret_t
restricted(char *name)
{
	int i;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, name);
	if (mask_max == 0) {
		maskclient = (char **) calloc(4, sizeof(char *));
		if (maskclient == NULL)
			return HANDLER_FAIL; /* error */
		mask_max = 4;
	}
	if ((maskclient[mask_num] = strdup(name)) == NULL) {
		for (i = 0; i < mask_num; i++)
			free(maskclient[i]);
		mask_num = 0;
		return HANDLER_FAIL;
	}
	if (maskclient[mask_num++] == NULL) {
		for (i = 0; i < mask_num; i++)
			free(maskclient[i]);
		mask_num = 0;
		return HANDLER_FAIL; /* error */
	}

	if (mask_num == mask_max) {
		char **tmcl;
		tmcl = (char **) realloc(maskclient,
					 2 * mask_max * sizeof(char *));
		if (tmcl == NULL)
			return HANDLER_FAIL; /* error */
		maskclient = tmcl;
		mask_max *= 2;
	}
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	sets the cputfactor value
 *
 * @param[in] value - value for cputfactor
 *
 * @return      handler_ret_t (return value)
 * @retval      HANDLER_FAIL(0)                 Failure
 * @retval      HANDLER_SUCCESS(1)              Success
 *
 */

static handler_ret_t
cputmult(char *value)
{
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, value);
	if ((cputfactor = atof(value)) == 0.0)
		return HANDLER_FAIL; /* error */
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	sets wallfactor
 *
 * @param[in] value - value for wallfactor
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL(0)         Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
wallmult(char *value)
{
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, value);
	if ((wallfactor = atof(value)) == 0.0)
		return HANDLER_FAIL; /* error */
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets hosts
 *
 * @param[in] value - value for hosts
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL(0)         Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
usecp(char *value)
{
	char *pnxt;
	static int cphosts_max = 0;

	if (cphosts_max == 0) {
		pcphosts = malloc(2 * sizeof(struct cphosts));
		if (pcphosts == NULL) {
			return HANDLER_FAIL;
		}
		cphosts_max = 2;
	} else if (cphosts_max == cphosts_num) {

		struct cphosts *tmppcphosts;
		tmppcphosts = realloc(pcphosts,
				      (cphosts_max + 2) * sizeof(struct cphosts));
		if (tmppcphosts == NULL) {
			free(pcphosts);
			return HANDLER_FAIL;
		}
		pcphosts = tmppcphosts;
		cphosts_max += 2;
	}
	pnxt = strchr(value, (int) ':');
	if (pnxt == NULL) {
		sprintf(log_buffer, "invalid host specification: %s", value);
		log_err(-1, __func__, log_buffer);
		return HANDLER_FAIL;
	}
	*pnxt++ = '\0';
#ifdef NAS /* localmod 009 */
	/* support $usecp rules that exclude a pattern, look for hostname
	 * that starts with ! */
	if (value[0] == '!') {
		(pcphosts + cphosts_num)->cph_exclude = 1;
		value++;
	} else {
		(pcphosts + cphosts_num)->cph_exclude = 0;
	}
#endif /* localmod 009 */

	if (((pcphosts + cphosts_num)->cph_hosts = strdup(value)) == NULL)
		return HANDLER_FAIL;
	value = pnxt; /* now ptr to path */
	while (!isspace(*pnxt))
		pnxt++;
	*pnxt++ = '\0';
	if (((pcphosts + cphosts_num)->cph_from = strdup(value)) == NULL)
		return HANDLER_FAIL;

	if (((pcphosts + cphosts_num)->cph_to = strdup(skipwhite(pnxt))) == NULL)
		return HANDLER_FAIL;

	fix_path((pcphosts + cphosts_num)->cph_from, 1);
	fix_path((pcphosts + cphosts_num)->cph_to, 1);

	cphosts_num++;

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets prolog alarm
 *
 * @param[in] value - value for prolog alarm
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL(0)         Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
prologalarm(char *value)
{
	int i;
	extern unsigned int pe_alarm_time;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
		  "prolog alarm", value);
	i = (unsigned int) atoi(value);
	if (i <= 0)
		return HANDLER_FAIL; /* error */
	pe_alarm_time = (unsigned int) i;
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	Handler function for the $sister_join_job_alarm config option.
 *
 * @param[in]	value - the input given in config file.
 *
 * @return handler_ret_t
 * @retval HANNDLER_SUCCESS
 * @retval HANDLER_FAIL
 */
static handler_ret_t
set_joinjob_alarm(char *value)
{
	long i;
	char *endp;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
		  "sister_join_job_alarm", value);
	i = strtol(value, &endp, 10);
	if ((*endp != '\0') || (i <= 0) || (i == LONG_MIN) || (i == LONG_MAX))
		return HANDLER_FAIL; /* error */
	joinjob_alarm_time = i;
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	Handler function for the $job_launch_delay cconfig option.
 *
 * @param[in]	value - the input given in config file.
 *
 * @return handler_ret_t
 * @retval HANNDLER_SUCCESS
 * @retval HANDLER_FAIL
 */
static handler_ret_t
set_job_launch_delay(char *value)
{
	long i;
	char *endp;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
		  "job_launch_delay", value);
	i = strtol(value, &endp, 10);

	if ((*endp != '\0') || (i <= 0) || (i == LONG_MIN) || (i == LONG_MAX))
		return HANDLER_FAIL; /* error */
	job_launch_delay = i;
	return HANDLER_SUCCESS;
}

#ifdef WIN32

/**
 * @brief
 *      sets nrun_factor
 *
 * @param[in] value - value for nrun_factor
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL(0)         Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
set_nrun_factor(char *value)
{
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, value);
	if ((nrun_factor = atoi(value)) == 0)
		return HANDLER_FAIL; /* error */
	else
		return HANDLER_SUCCESS;
}

/**
 * @brief
 *      Performs shell_escape_timeout on process tree by using given parent process handle <hProcess> and close that handle.
 *
 * @return	Void
 *
 */

static HANDLE shell_escape_handle = INVALID_HANDLE_VALUE;
static void
shell_escape_timeout(void)
{
	int ret = 0;
	int err_no = 0;
	char log_buf[LOG_BUF_SIZE] = "";
	if (shell_escape_handle != INVALID_HANDLE_VALUE) {
		ret = processtree_op_by_handle(shell_escape_handle, TERMINATE, 13); /* Terminated process would have exit code 13 */
		if (ret == -1) {
			err_no = GetLastError();
			sprintf(log_buf, "could not terminate shell escape process tree, pid=%d", GetProcessId(shell_escape_handle));
			log_err(err_no, "shell_escape_timeout", log_buf);
		} else {
			SetLastError(0);
		}
		log_err(-1, "shell_escape_timeout", "terminate shell escape");
	}
}
#endif /* WIN32 */

/**
 * @brief
 * read and set values used in enforcement of cpupercent calculation and
 * other limit enforcement
 *
 * In the form of:
 * $enforce NAME VALUE
 *
 *	where "NAME is     default	   range of values	*/

int delta_percent_over = 50;	  /* 0   <= I <= 100	*/
double delta_cpufactor = 1.05;	  /* 1.0 <= D		*/
double delta_weightup = 0.4;	  /* 0.0 <= D <= 1.0	*/
double delta_weightdown = 0.1;	  /* 0.0 <= D <= 1.0	*/
int average_percent_over = 50;	  /* 0   <= I <= 100	*/
double average_cpufactor = 1.025; /* 1.0 <= D		*/
int average_trialperiod = 120;	  /* 0   <= I		*/
/*
 * or the form of:  $enforce [!]NAME
 * where NAME is:
 */
/* cpuburst */
int cpuburst = 0; /* 1 or 0		*/
/* cpuaverage */
int cpuaverage = 0; /* 1 or 0		*/
/* mem */
int enforce_mem = 0; /* on, value ignored	*/
/* complexmem	*/
int complex_mem_calc = 0; /* 1 or 0		*/

static handler_ret_t
set_enforcement(char *str)
{
	char arg[80];
	int on = 1;

	if (!str)
		return HANDLER_FAIL;

	/* if current token starts with !, then set value off and skip ! */
	if (*str == '!') {
		on = 0; /* set off */
		str++;
	}

	str = TOKCPY(str, arg);
	str = skipwhite(str);

	if (strcmp(arg, "delta_percent_over") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		delta_percent_over = atoi(str);
	} else if (strcmp(arg, "delta_cpufactor") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		delta_cpufactor = atof(str);
	} else if (strcmp(arg, "delta_weightup") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		delta_weightup = atof(str);
	} else if (strcmp(arg, "delta_weightdown") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		delta_weightdown = atof(str);
	} else if (strcmp(arg, "average_percent_over") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		average_percent_over = atoi(str);
	} else if (strcmp(arg, "average_cpufactor") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		average_cpufactor = atof(str);
	} else if (strcmp(arg, "average_trialperiod") == 0) {
		if (*str == '\0')
			return HANDLER_FAIL;
		average_trialperiod = atoi(str);
	} else if (strcmp(arg, "cpuburst") == 0) {
		cpuburst = on; /* may be off */
	} else if (strcmp(arg, "cpuaverage") == 0) {
		cpuaverage = on; /* may be off */
	} else if (strcmp(arg, "mem") == 0) {
		enforce_mem = on; /* may be off */
	} else if (strcmp(arg, "complexmem") == 0) {
		complex_mem_calc = on; /* may be off */
	} else {
		return HANDLER_FAIL;
	}
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	check for the type of action to be done on a certain event
 *
 * @param[in] ae - enum val for action_event
 *
 * @return	the Action_Verb enum value, see mom_func.h:
 * @retval	Default no directive to change the action for the event
 * @retval	Script defined in see mom_func.h
 * @retval	Requeue defined in see mom_func.h
 *
 */
enum Action_Verb
chk_mom_action(enum Action_Event ae)
{
	assert((0 <= ae) && (ae < (int) LastAction));

	return mom_action[ae].ma_verb;
}

/**
 * @brief
 *	if there is an external script defined for this
 *	action, do it and return values:
 *
 * @retval	1	script running in child process
 * @retval	0	script ran with no error
 * @retval	-1	error, script did not run correctly
 * @retval	-2	error, no script - do normal default action
 *
 *	The "post" function is called out of scan_for_terminated() when the
 *	child process (script) exits.  It is called with the pointer to the job
 *	and the script exit value.   If the script does not complete in the
 *	specified timeout value,  the "post" function will be called with the
 *	error value of -1.
 *
 *	The action taken by the "post" function on a error depends on the
 *	function itself.   Usually it should preform the "default" action
 *	for that action.
 *
 */
int
do_mom_action_script(int ae,	      /* index into action table */
		     job *pjob,	      /* ptr to job */
		     pbs_task *ptask, /* ptr to task */
		     char *path,
		     void (*post)(job *p, int e)) /* post action func */
{
	char **args = NULL;
	char buf[MAXPATHLEN + 1];
	int i;
	int nargs;
	char **pargs;
	struct stat sb;
	struct passwd *pwdp;
	int rc = -1;
	struct mom_action *ma;
	int transmog = 0;
	int j;
	int pipes[2], kid_read = -1, kid_write = -1;
	int parent_read = -1, parent_write = -1;
	struct startjob_rtn sjr;
	pid_t child;

	memset(&sjr, 0, sizeof(sjr));

	assert((0 <= ae) && (ae < (int) LastAction));

	ma = &mom_action[ae];
	if (ma == NULL || ma->ma_script == NULL)
		return -2;

	/* does script really exist? */
	if (stat(ma->ma_script, &sb) == -1) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s does not exist", ma->ma_name, ma->ma_script);
		return -1;
	} else if (sb.st_uid != 0 || sb.st_gid > 10 || (sb.st_mode & S_IXUSR) != S_IXUSR || (sb.st_mode & S_IWOTH) != 0) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s cannot be executed due to permissions", ma->ma_name, ma->ma_script);
		return -1;
	}

	if (ptask == NULL)
		ptask = (pbs_task *) GET_NEXT(pjob->ji_tasks);

	if (ptask == NULL) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s cannot run because job has no tasks", ma->ma_name, ma->ma_script);
		return -1;
	}

	/*
	 ** If we are going to leave the script running in the background,
	 ** the ji_momsubt field has to be free to track the pid.
	 */
	if (post != NULL && pjob->ji_momsubt) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s cannot be run due to existing subtask", ma->ma_name, ma->ma_script);
		return -1;
	}

	if ((pwdp = check_pwd(pjob)) == NULL) {
		log_event(PBSEVENT_JOB | PBSEVENT_SECURITY, PBS_EVENTCLASS_JOB, LOG_ERR, pjob->ji_qs.ji_jobid, log_buffer);
		return -1;
	}

	/* build up args to script */
	for (nargs = 0, pargs = ma->ma_args; pargs && *pargs; pargs++)
		nargs++;
	/* Add one for the command itself */
	nargs++;
	args = calloc((nargs + 1), sizeof(char *));
	if (args == NULL)
		return -1;

	/* set args[0] to script */
	args[0] = strdup(ma->ma_script);
	if (args[0] == NULL) {
		free(args);
		return -1;
	}

	pargs = ma->ma_args;
	for (i = 1; i < nargs; i++, pargs++) {
		if (**pargs == '%') {
			if (strcmp(*pargs + 1, "jobid") == 0)
				strcpy(buf, pjob->ji_qs.ji_jobid);
			else if (strcmp(*pargs + 1, "sid") == 0)
				sprintf(buf, "%d", ptask->ti_qs.ti_sid);
			else if (strcmp(*pargs + 1, "taskid") == 0)
				sprintf(buf, "%d", ptask->ti_qs.ti_task);
			else if (strcmp(*pargs + 1, "uid") == 0) {
				sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exuid);
			} else if (strcmp(*pargs + 1, "gid") == 0) {
				sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exgid);
			} else if (strcmp(*pargs + 1, "login") == 0)
				strcpy(buf, get_jattr_str(pjob, JOB_ATR_euser));
			else if (strcmp(*pargs + 1, "owner") == 0)
				strcpy(buf, get_jattr_str(pjob, JOB_ATR_job_owner));
			else if (strcmp(*pargs + 1, "globid") == 0)
				strcpy(buf, "NULL");
			else if (strcmp(*pargs + 1, "auxid") == 0) {
				char *value;
				if ((value = get_jattr_str(pjob, JOB_ATR_altid)) != NULL)
					pbs_strncpy(buf, value, sizeof(buf));
				else
					strcpy(buf, "NULL");
			} else if (strcmp(*pargs + 1, "path") == 0) {
				if (path != NULL)
					pbs_strncpy(buf, path, sizeof(buf));
				else
					strcpy(buf, "NULL");
			} else {
				log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
					   "action %s script %s cannot be run due to unknown parameter %s",
					   ma->ma_name, ma->ma_script, *pargs);
				goto done;
			}
		} else
			pbs_strncpy(buf, *pargs, sizeof(buf));

		*(args + i) = strdup(buf);
		if (*(args + i) == NULL)
			return -1;
	}

	/*
	 * Special case for restart_transmogrify.
	 * The script is going to morf into the task so we have to
	 * setup pipes just like in start_process()
	 */
	if ((transmog = (ae == RestartAction) && restart_transmogrify)) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s preparing to transmogrify task %8.8X",
			   ma->ma_name, ma->ma_script, ptask->ti_qs.ti_task);

		if (pipe(pipes) == -1)
			goto done;
		if (pipes[1] < 3) {
			kid_write = fcntl(pipes[1], F_DUPFD, 3);
			close(pipes[1]);
		} else
			kid_write = pipes[1];
		parent_read = pipes[0];

		if (pipe(pipes) == -1) {
			close(kid_write);
			close(parent_read);
			goto done;
		}
		if (pipes[0] < 3) {
			kid_read = fcntl(pipes[0], F_DUPFD, 3);
			close(pipes[0]);
		} else
			kid_read = pipes[0];
		parent_write = pipes[1];
	} else if (ae == RestartAction)
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid,
			   "action %s script %s preparing to restart task %8.8X",
			   ma->ma_name, ma->ma_script, ptask->ti_qs.ti_task);

	if ((child = fork_me(-1)) == 0) { /* child */
		extern char *variables_else[];
		char *shell;

		/* unprotect the child process which becomes the job */
		daemon_protect(0, PBS_DAEMON_PROTECT_OFF);

		shell = set_shell(pjob, pwdp); /* machine dependent */
		vtable.v_ensize = 30;
		vtable.v_used = 0;
		vtable.v_envp = (char **) calloc(vtable.v_ensize, sizeof(char *));
		if (vtable.v_envp == NULL) {
			free(args);
			log_err(errno, "setup environment", "out of memory");
			return -1;
		}
		/* setup environment */
		/* UID */
		sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exuid);
		bld_env_variables(&vtable, "UID", buf);
		/* GID */
		sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exgid);
		bld_env_variables(&vtable, "GID", buf);
		/* HOME */
		bld_env_variables(&vtable, variables_else[0], pwdp->pw_dir);
		/* LOGNAME */
		bld_env_variables(&vtable, variables_else[1], pwdp->pw_name);
		/* PBS_JOBNAME */
		bld_env_variables(&vtable, variables_else[2], get_jattr_str(pjob, JOB_ATR_jobname));
		/* PBS_JOBID */
		bld_env_variables(&vtable, variables_else[3], pjob->ji_qs.ji_jobid);
		/* PBS_QUEUE */
		bld_env_variables(&vtable, variables_else[4], get_jattr_str(pjob, JOB_ATR_in_queue));
		/* SHELL */
		bld_env_variables(&vtable, variables_else[5], shell);
		/* USER */
		bld_env_variables(&vtable, variables_else[6], pwdp->pw_name);
		/* PBS_JOBCOOKIE */
		bld_env_variables(&vtable, variables_else[7], get_jattr_str(pjob, JOB_ATR_Cookie));
		/* PBS_NODENUM */
		sprintf(buf, "%d", pjob->ji_nodeid);
		bld_env_variables(&vtable, variables_else[8], buf);
		/* PBS_TASKNUM */
		sprintf(buf, "%ld", (long) ptask->ti_qs.ti_task);
		bld_env_variables(&vtable, variables_else[9], buf);
		/* PBS_MOMPORT */
		sprintf(buf, "%d", pbs_rm_port);
		bld_env_variables(&vtable, variables_else[10], buf);
		/* PBS_NODEFILE */
		sprintf(buf, "%s/aux/%s", pbs_conf.pbs_home_path, pjob->ji_qs.ji_jobid);
		bld_env_variables(&vtable, variables_else[11], buf);
		/* PBS_SID */
		sprintf(buf, "%d", ptask->ti_qs.ti_sid);
		bld_env_variables(&vtable, "PBS_SID", buf);
		/* PBS_JOBDIR */
		if (is_jattr_set(pjob, JOB_ATR_sandbox) && strcasecmp(get_jattr_str(pjob, JOB_ATR_sandbox), "PRIVATE") == 0) {
			bld_env_variables(&vtable, "PBS_JOBDIR",
					  jobdirname(pjob->ji_qs.ji_jobid, pjob->ji_grpcache->gc_homedir));
		} else
			bld_env_variables(&vtable, "PBS_JOBDIR", pjob->ji_grpcache->gc_homedir);
		mom_unnice();

		/*
		 ** Do the same operations as start_process() but we don't
		 ** need to reset the global ID.
		 */
		if (transmog) {
			close(parent_read);
			close(parent_write);

#if MOM_ALPS
			/*
			 * ALPS jobs need a new PAGG when
			 * being restarted.
			 */
			memset(pjob->ji_extended.ji_ext.ji_jid, 0, sizeof(pjob->ji_extended.ji_ext.ji_jid));
#endif
			j = set_job(pjob, &sjr);
			if (j < 0) {
				if (j == -1)
					strcpy(log_buffer, "Unable to set task session");
				DBPRT(("%s: %s\n", __func__, log_buffer))
				log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_NOTICE, pjob->ji_qs.ji_jobid, log_buffer);
				if (j == -3)
					j = JOB_EXEC_FAIL2;
				else
					j = JOB_EXEC_RETRY;
				starter_return(kid_write, kid_read, j, &sjr);
			}
			ptask->ti_qs.ti_sid = sjr.sj_session;
			i = mom_set_limits(pjob, SET_LIMIT_SET);
			if (i != PBSE_NONE) {
				log_eventf(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB, LOG_WARNING, pjob->ji_qs.ji_jobid, "Unable to set limits, err=%d", i);
				if (i == PBSE_RESCUNAV)
					j = JOB_EXEC_RETRY;
				else
					j = JOB_EXEC_FAIL2;
				starter_return(kid_write, kid_read, j, &sjr);
			}
			log_close(0);
			starter_return(kid_write, kid_read, JOB_EXEC_OK, &sjr);
		} else { /* just close down anything hanging */
			close(0);
			close(1);
			close(2);
		}

		execve(ma->ma_script, args, vtable.v_envp);
		exit(254);
	}

	if (child == -1) {
		log_eventf(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "action script %s cannot be run due to fork failure %d", ma->ma_script, errno);
		goto done;
	}

	if (post != NULL) { /* post func means we do not wait */
		rc = 1;
		pjob->ji_momsubt = child;
		pjob->ji_mompost = post;
		if (ma->ma_timeout)
			pjob->ji_actalarm = time_now + ma->ma_timeout;
		else
			pjob->ji_actalarm = 0;
		goto done;
	}

	if (transmog) { /* setup new task */
		close(kid_read);
		close(kid_write);

		/* read sid */
		i = readpipe(parent_read, &sjr, sizeof(sjr));
		j = errno;
		close(parent_read);
		if (i != sizeof(sjr)) {
			log_errf(j, __func__, "read of pipe for pid job %s got %d not %d", pjob->ji_qs.ji_jobid, i, (int) sizeof(sjr));
			close(parent_write);
			goto done;
		}
		/* send info back as an acknowlegment */
		writepipe(parent_write, &sjr, sizeof(sjr));
		close(parent_write);
		DBPRT(("%s: read start return %d %d\n", __func__, sjr.sj_code, sjr.sj_session))
		/* update system specific ids and information from set_job() */
		set_globid(pjob, &sjr);
		if (sjr.sj_code < 0) {
			log_eventf(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB, LOG_NOTICE, pjob->ji_qs.ji_jobid,
				   "task %8.8X not started, %s %d", (unsigned int) ptask->ti_qs.ti_task, (sjr.sj_code == JOB_EXEC_RETRY) ? "Retry" : "Failure", sjr.sj_code);
			goto done;
		}
		ptask->ti_qs.ti_sid = sjr.sj_session;
		ptask->ti_qs.ti_status = TI_STATE_RUNNING;
		(void) task_save(ptask);
		/* update the job with the new session id */
		set_jattr_l_slim(pjob, JOB_ATR_session_id, sjr.sj_session, SET);
		if (!check_job_substate(pjob, JOB_SUBSTATE_RUNNING)) {
			set_job_state(pjob, JOB_STATE_LTR_RUNNING);
			set_job_substate(pjob, JOB_SUBSTATE_RUNNING);
			job_save(pjob);
		}

		rc = 0;
		log_eventf(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "task %8.8X transmogrified", (unsigned int) ptask->ti_qs.ti_task);
		enqueue_update_for_send(pjob, IS_RESCUSED);
	} else { /* wait for script */
		DBPRT(("action: setting alarm %d\n", ma->ma_timeout))
		alarm(ma->ma_timeout);
		rc = 0;
		if (waitpid(child, &rc, 0) == -1) {
			log_eventf(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "%s script %s: wait failed %d", ma->ma_name, ma->ma_script, errno);
			(void) kill(child, SIGKILL);
			(void) waitpid(child, &rc, 0);
		}
		alarm(0);
		if (WIFEXITED(rc)) {
			rc = WEXITSTATUS(rc);
			log_eventf(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "%s script %s: exit code %d", ma->ma_name, ma->ma_script, rc);
			if (rc != 0)
				rc = -1;
		} else if (WIFSIGNALED(rc)) {
			rc = WTERMSIG(rc);
			log_eventf(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "%s script %s: got signal %d", ma->ma_name, ma->ma_script, rc);
			rc = -1;
		} else {
			log_eventf(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, "%s script %s: exited abnormally", ma->ma_name, ma->ma_script);
			rc = -1;
		}
	}

done:
	/* free args arrays */
	for (pargs = args; *pargs; pargs++)
		(void) free(*pargs);
	(void) free(args);

	return rc;
}

/**
 * @brief
 *	set the suspend (and resume) signal used
 *
 * @param[in] str - signal name
 *
 * @return	handler_ret_t
 * @retval	HANDLER_FAIL		Failure
 * @retval	HANDLER_SUCCESS		Success
 *
 */
static handler_ret_t
set_suspend_signal(char *str)
{
	char tok[80];

	if ((str == 0) || (*str == '\0'))
		return HANDLER_FAIL;

	str = TOKCPY(str, tok);
	str = skipwhite(str);

	suspend_signal = atoi(tok);

	if (*str != '\0')
		resume_signal = atoi(str);

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	Add static resource or shell escape line from config file.
 *	This is a support routine for read_config().
 *
 * @param[in] str - string holding resource name
 * @param[in] file - filename
 * @param[in] linenum - line number in file
 *
 * @return int
 *
 * @retval  1 - In case of error
 * @retval  0 - In case of success
 *
 */
static int
add_static(char *str, char *file, int linenum)
{
	int i;
	char name[256];
	struct config_list *cp;
	int perm;

	str = TOKCPY(str, name); /* resource name */
	str = skipwhite(str);	 /* resource value */
	if (*str == '!') {	 /* shell escape command */
		int err;
		char *filename;
		rmnl(str);
		filename = get_script_name(&str[1]);
		if (filename == NULL)
			return 1;
		perm = get_permission("write");
		err = tmp_file_sec(filename, 0, 1, perm, 1);

		if (err != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "error: %s file has a non-secure file access, errno: %d", filename, err);
			log_event(PBSEVENT_SECURITY, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
			free(filename);
			return 1;
		}
		free(filename);
	} else { /* get the value */
		i = strlen(str);
		while (--i) { /* strip trailing blanks */
			if (!isspace((int) *(str + i)))
				break;
			*(str + i) = '\0';
		}
	}

	cp = (struct config_list *) malloc(sizeof(struct config_list));
	memcheck((char *) cp);

	cp->c_link = config_list;
	cp->c.c_name = strdup(name);
	memcheck(cp->c.c_name);
	cp->c.c_u.c_value = strdup(str);
	memcheck(cp->c.c_u.c_value);

	snprintf(log_buffer, sizeof(log_buffer), "%s[%d] add name %s value %s",
		 file, linenum, name, str);
	log_event(PBSEVENT_DEBUG, 0, LOG_DEBUG, "add_static", log_buffer);

	config_list = cp;
	return 0;
}

/**
 * @brief
 *	sets ideal load
 *
 * @param[in] value - value for ideal load
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
setidealload(char *value)
{
	char newstr[50] = "ideal_load ";
	float val;

	val = (float) atof(value);
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  "ideal_load", value);
	if (val < 0.0)
		return HANDLER_FAIL; /* error */
	ideal_load_val = val;
	if (max_load_val < 0.0)
		max_load_val = val; /* set a default */
	(void) strcat(newstr, value);
	if (add_static(newstr, "config", 0))
		return HANDLER_FAIL;
	nconfig++;
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets maximum load
 *
 * @param[in] value - value for maximum load
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
setmaxload(char *value)
{
	char newstr[50] = "max_load ";
	char *endptr;
	float val;

	endptr = value;
	while ((!isspace((int) *endptr)) && *endptr)
		endptr++;
	val = (float) atof(value);
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  "max_load", value);
	if (val < 0.0)
		return HANDLER_FAIL; /* error */
	max_load_val = val;
	if (ideal_load_val < 0.0)
		ideal_load_val = val;
	(void) strncat(newstr, value, 40);
	if (add_static(newstr, "config", 0))
		return HANDLER_FAIL;
	nconfig++;

	if (*endptr != '\0') {
		if (strstr(endptr, "suspend"))
			idle_on_maxload = 1;
	}
	return HANDLER_SUCCESS;
}

/**
 * process $max_poll_downtime directive in config file:
 *	$max_poll_downtime 300
 */
static handler_ret_t
set_max_poll_downtime(char *value)
{
	char *sbuf;
	char *ebuf;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
		  LOG_INFO, "max_poll_downtime", value);
	sbuf = value;
	max_poll_downtime_val = (time_t) strtol(sbuf, &ebuf, 10);
	if (max_poll_downtime_val <= 0)
		return HANDLER_FAIL; /* error */

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	process $kbd_idle directive in config file:
 *	$kbidle avail [busy]
 *
 * @param[in] value - value for kb idle
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
set_kbd_idle(char *value)
{
	char *sbuf;
	char *ebuf;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
		  LOG_INFO, "idle_avail", value);
	sbuf = value;
	idle_avail = (time_t) strtol(sbuf, &ebuf, 10);
	if (idle_avail <= 0)
		return HANDLER_FAIL; /* error */

	idle_check = 1;
	cycle_harvester = 1;
	sbuf = ebuf;
	while (isspace((int) *sbuf))
		++sbuf;
	if (*sbuf == '\0')
		goto chk_for_interactive; /* no idle_busy, but that is ok */

	idle_busy = (time_t) strtol(sbuf, &ebuf, 10);
	if (idle_busy <= 0)
		return HANDLER_FAIL; /* error */

	sbuf = ebuf;
	while (isspace((int) *sbuf))
		++sbuf;
	if (*sbuf == '\0')
		goto chk_for_interactive; /* no idle_poll, but that is ok */

	idle_poll = (time_t) strtol(sbuf, &ebuf, 10);
	if (idle_poll <= 0)
		return HANDLER_FAIL; /* error */

	/* check whether PBS_INTERACTIVE service is registered or not? */
chk_for_interactive:
	return check_interactive_service();
}

/**
 * @brief
 *	sets temporary dirctory
 *
 * @param[in] value - value for temp directory
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
set_tmpdir(char *value)
{
	char *cleaned_value;
	int i;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
		  LOG_INFO, "tmpdir", value);
	cleaned_value = remove_quotes(value); /* remove quotes if any present */
	if (cleaned_value == NULL)
		return HANDLER_FAIL;

	/* Remove trailing separator */
	for (i = (strlen(cleaned_value) - 1); i >= 0; i--) {
		if (cleaned_value[i] != TRAILING_CHAR)
			break;
		cleaned_value[i] = '\0';
	}

	if (strlen(cleaned_value) > sizeof(pbs_tmpdir) - 1) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}

#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
	if (verify_dir(cleaned_value, 1, 1, 0, 1)) {
		free(cleaned_value);
		return HANDLER_FAIL; /* error */
	}
#endif /* NO_SECURITY_CHECK */

	strcpy(pbs_tmpdir, cleaned_value);
	free(cleaned_value);
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets job dirctory
 *
 * @param[in] value - value for job directory
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
set_jobdir_root(char *value)
{
	char *cleaned_value;
	char *savep = NULL;
	char *directive;
	char *p;

	p = value;
	value = strtok_r(p, " ", &savep);
	directive = strtok_r(NULL, " ", &savep);
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
		  LOG_INFO, __func__, value);
	cleaned_value = remove_quotes(value); /* remove quotes if any present */
	if (cleaned_value == NULL)
		return HANDLER_FAIL;

	if (strlen(cleaned_value) > sizeof(pbs_jobdir_root) - 1) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}

#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
	if ((strcmp(cleaned_value, JOBDIR_DEFAULT) != 0) && (verify_dir(cleaned_value, 1, 1, 0, 1))) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}
#endif /* NO_SECURITY_CHECK */

	strcpy(pbs_jobdir_root, cleaned_value);
	free(cleaned_value);

	if (directive != NULL) {
		if (strcmp(directive, "shared") == 0)
			pbs_jobdir_root_shared = TRUE;
	}
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	sets boolean value
 *
 * @param[in] id - function name
 * @param[in] value - value
 * @param[in] flag - configuration flag
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

handler_ret_t
set_boolean(const char *id, char *value, int *flag)
{
	if (value == NULL || *value == '\0') {
		sprintf(log_buffer, "No value specified, no action taken.");
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, log_buffer);
		return HANDLER_FAIL; /* error */
	}

	if ((strcasecmp(value, "no") == 0) ||
	    (strcasecmp(value, "false") == 0) ||
	    (strcasecmp(value, "off") == 0) ||
	    (strcmp(value, "0") == 0)) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, "false");
		*flag = FALSE;
	} else if ((strcasecmp(value, "yes") == 0) ||
		   (strcasecmp(value, "true") == 0) ||
		   (strcasecmp(value, "on") == 0) ||
		   (strcmp(value, "1") == 0)) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, "true");
		*flag = TRUE;
	} else {
		sprintf(log_buffer,
			"Illegal value \"%s\", no action taken.", value);
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, log_buffer);
		return HANDLER_FAIL; /* error */
	}
	return HANDLER_SUCCESS; /* success */
}

static handler_ret_t
set_int(const char *id, char *value, int *var)
{
	char *left;
	int val;

	if (value == NULL || *value == '\0') {
		sprintf(log_buffer, "No value specified, no action taken.");
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, log_buffer);
		return HANDLER_FAIL; /* error */
	}

	val = (int) strtol(value, &left, 0);
	if (*left != '\0' || val <= 0) {
		sprintf(log_buffer, "bad value \"%s\"", value);
		log_event(PBSEVENT_SYSTEM, 0, LOG_ERR, id, log_buffer);
		return HANDLER_FAIL; /* error */
	}
	*var = val;

	sprintf(log_buffer, "setting %d", val);
	log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, id, log_buffer);

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	set float value
 *
 * @param[in] id - function name
 * @param[in] value - value
 * @param[out] var - output float value
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL            Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */
handler_ret_t
set_float(const char *id, char *value, float *var)
{
	char *left;
	float val;

	if (value == NULL || *value == '\0') {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  id, "No value specified, no action taken.");
		return HANDLER_FAIL; /* error */
	}

	val = strtod(value, &left);
	if (left == value || val <= 0) {
		sprintf(log_buffer, "bad value \"%s\"", value);
		log_event(PBSEVENT_SYSTEM, 0, LOG_ERR, id, log_buffer);
		return HANDLER_FAIL; /* error */
	}
	*var = val;

	snprintf(log_buffer, sizeof(log_buffer), "setting %f", val);
	log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, id, log_buffer);

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	Set the configuration flag that defines whether the restart nunction
 *	occurs in the background.
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_restart_background(char *value)
{
	return (set_boolean(__func__, value, &restart_background));
}

/**
 * @brief
 *	 Set the configuration flag that defines whether the restart function
 *	transmogrifies into a task.
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_restart_transmogrify(char *value)
{
	return (set_boolean(__func__, value, &restart_transmogrify));
}

/**
 * @brief
 *	Set the configuration flag that defines whether a call to tm_attach
 *	is allowed.
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_attach_allow(char *value)
{
	return (set_boolean(__func__, value, &attach_allow));
}

#if MOM_ALPS
/**
 * @brief
 *	Set the path used for invoking the ALPS BASIL client.
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_alps_client(char *value)
{
	char *p;

	if (!value) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Unsetting alps_client.");
		if (alps_client) {
			free(alps_client);
			alps_client = NULL;
		}
		return HANDLER_SUCCESS;
	}
	if (alps_client && strcmp(value, alps_client) == 0) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "alps_client unchanged, values identical.");
		return HANDLER_SUCCESS;
	}
	if (strlen(value) > _POSIX_PATH_MAX) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "alps_client unchanged, new value too long.");
		return HANDLER_FAIL;
	}
	if (*value != '/') {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "alps_client unchanged, must be full path.");
		return HANDLER_FAIL; /* Must be full path. */
	}
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
	if (chk_file_sec(value, 0, 0, S_IWGRP | S_IWOTH, 0) != 0) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "alps_client unchanged, security check failed.");
		return HANDLER_FAIL;
	}
#else
	{
		struct stat sb;
		if (stat(value, &sb) == -1) {
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
				  LOG_NOTICE, __func__,
				  "alps_client unchanged, cannot stat file.");
			return HANDLER_FAIL;
		}
		if (!S_ISREG(sb.st_mode)) {
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
				  LOG_NOTICE, __func__,
				  "alps_client unchanged, not a regular file.");
			return HANDLER_FAIL;
		}
	}
#endif
	p = strdup(value);
	if (!p) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "alps_client unchanged, out of memory.");
		return HANDLER_FAIL;
	}
	if (alps_client)
		free(alps_client);
	alps_client = p;
	(void) sprintf(log_buffer, "%s %s",
		       "alps_client now set to", alps_client);
	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
		  __func__, log_buffer);
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	Set the timeout value in seconds when we will stop checking for
 *	ALPS SWITCH response to change from "EMPTY".
 *	In order to work around a situation where we must poll on "EMPTY" in
 *	case it changes.  After the timeout, we can proceed with the suspend.
 *
 * @par
 *	It is best if this value is not too large, since PBS will be
 *	blocked until the timeout is reached or the response changes from "EMPTY".
 *
 * @retval 0 failure
 * @retval 1 success
 */
static handler_ret_t
set_alps_confirm_empty_timeout(char *value)
{
	return (set_int(__func__, value, &alps_confirm_empty_timeout));
}

/**
 * @brief
 *	Set the time out value in seconds when we will stop checking for
 *	ALPS SWITCH to complete.  PBS will basically give up trying.
 *
 * @par
 *	It is best if this value is not too large, since PBS will be
 *	blocked until the timeout is reached or the SWITCH completes.
 *
 * @retval 0 failure
 * @retval 1 success
 */
static handler_ret_t
set_alps_confirm_switch_timeout(char *value)
{
	return (set_int(__func__, value, &alps_confirm_switch_timeout));
}

/**
 * @brief
 * Set the configuration flag that defines vnode creation behavior
 * on a Cray.
 *
 * @par
 * If vnode_per_numa_node is set to TRUE, then
 * PBS will create one vnode per NUMA node (i.e. per segment).
 * Thus there will be multiple vnodes per host.
 *
 * If vnode_per_numa_node is set to FALSE, then
 * PBS will create one vnode for the compute node (i.e. it will
 * cover all segment information in one vnode).
 *
 * @retval 0 failure
 * @retval 1 success
 */
static handler_ret_t
set_vnode_per_numa_node(char *value)
{

	return (set_boolean(__func__, value, &vnode_per_numa_node));
}

/**
 * @brief
 *	Set the alps_release_wait_time in micoseconds to wait between ALPS release
 *	reservation requests
 *
 * @return returns value of set_float()
 *
 */
static handler_ret_t
set_alps_release_wait_time(char *value)
{
	float tmp;
	handler_ret_t ret;
	double fract, integ;
	ret = set_float(__func__, value, &tmp);
	if (ret == HANDLER_SUCCESS) {
		fract = modf(tmp, &integ);
		alps_release_wait_time = (useconds_t) integ * 1000000 + (fract * 1000000);
	}
	return (ret);
}

/**
 * @brief
 *	Set the alps_release_jitter value in microseconds.
 *
 * @par
 *	PBS will randomly generate how much
 *	microseconds to add to the alps_release_wait_time value
 *
 * @return returns value of set_float()
 *
 */
static handler_ret_t
set_alps_release_jitter(char *value)
{
	float tmp;
	handler_ret_t ret;
	double fract, integ;
	ret = set_float(__func__, value, &tmp);
	if (ret == HANDLER_SUCCESS) {
		fract = modf(tmp, &integ);
		alps_release_jitter = (useconds_t) integ * 1000000 + (fract * 1000000);
	}
	return (ret);
}

/**
 * @brief
 *	Set the time out value in seconds when we will stop making ALPS release
 *	reservation requests.  PBS will basically give up trying.
 *
 * @par
 * It is best if this value is greater than the Cray node health
 * value for "suspectbegin" (configurable by admins)
 *
 * @retval 0 failure
 * @retval 1 success
 *
 */
static handler_ret_t
set_alps_release_timeout(char *value)
{
	return (set_int(__func__, value, &alps_release_timeout));
}
#endif /* MOM_ALPS */

/**
 * @brief
 *	Set the base directory used for checkpoint/restart functions.
 *
 * @retval 0 failure
 * @retval 1 success
 */
static handler_ret_t
set_checkpoint_path(char *value)
{
	int rc = 0;
	char newpath[_POSIX_PATH_MAX] = "\0";

	/*
	 * If value and path_checkpoint both contain the same address,
	 * then we have nothing to do.
	 */
	if (value == path_checkpoint && path_checkpoint)
		return HANDLER_SUCCESS;
	/*
	 * Try setting path_checkpoint in the following order:
	 * 1. command line argument
	 * 2. environment variable
	 * 3. mom config file value
	 * 4. PBS_HOME/checkpoint
	 *
	 * Only alter path_checkpoint if we succeed.
	 */
	if (path_checkpoint_from_getopt) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Using checkpoint path from command line.");
		pbs_strncpy(newpath, path_checkpoint_from_getopt, sizeof(newpath));
	} else if (path_checkpoint_from_getenv) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Using checkpoint path from environment.");
		pbs_strncpy(newpath, path_checkpoint_from_getenv, sizeof(newpath));
	} else if (value) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Using checkpoint path from config file.");
		pbs_strncpy(newpath, value, sizeof(newpath));
	} else {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Using default checkpoint path.");
		pbs_strncpy(newpath, path_checkpoint_default, sizeof(newpath));
	}
	if (strlen(newpath) == 0) {
		/* Bad mojo, fall back to existing or default. */
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, "Empty checkpoint path specified, ignoring.");
		if (*path_checkpoint == '\0')
			/* path_checkpoint is never allocated memory, it points to path_checkpoint_buf */
			pbs_strncpy(path_checkpoint, path_checkpoint_default, sizeof(path_checkpoint_buf));
		return HANDLER_FAIL; /* error */
	}
	if (*(newpath + strlen(newpath) - 1) != '/') {
		strcat(newpath, "/");
	}
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
	int perm;
	perm = get_permission("write");
	rc = chk_file_sec(newpath, 1, 0, perm, 0);
#else
	{
		struct stat sb;
		if (stat(newpath, &sb) == -1)
			rc = errno;
		else
			rc = 0;
	}
#endif /* !DEBUG && !NO_SECURITY_CHECK */
	if (rc == 0) {
		(void) sprintf(log_buffer, "%s %s",
			       "Setting checkpoint path to", newpath);
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			  __func__, log_buffer);
		strcpy(path_checkpoint_buf, newpath);
		if (!path_checkpoint)
			path_checkpoint = path_checkpoint_buf;
		return HANDLER_SUCCESS; /* success */
	}
	(void) sprintf(log_buffer, "%s %s",
		       "Error encountered setting checkpoint path to", newpath);
	log_err(rc, __func__, log_buffer);
	return HANDLER_FAIL; /* error */
}

/**
 * @brief
 *	sets mom name
 *
 * @param[in] value - value for mom name
 *
 * @return	handler_ret_t
 * @retval	HANDLER_SUCCESS		success
 * @retval	HANDLER_FAIL		Failure
 *
 */
static handler_ret_t
set_momname(char *value)
{
	if (strlen(value) > 63)
		return HANDLER_FAIL;
	(void) strcpy(mom_short_name, value);
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets mom port
 *
 * @param[in] value - value for mom port
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_momport(char *value)
{
	char *ebuf;
	char *sbuf;

	sbuf = value;
	pbs_mom_port = (unsigned int) strtol(sbuf, &ebuf, 10);
	if (pbs_mom_port == 0)
		return HANDLER_FAIL;
	pbs_rm_port = pbs_mom_port + 1; /* assume next port for RM */

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets maximum poll checks
 *
 * @param[in] value - max poll check
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_max_check_poll(char *value)
{
	static char id[] = "max_check_poll";

	return (set_int(id, value, &max_check_poll));
}

/**
 * @brief
 *      sets minimum poll checks
 *
 * @param[in] value - min poll checks
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_min_check_poll(char *value)
{
	static char id[] = "min_check_poll";

	return (set_int(id, value, &min_check_poll));
}

/**
 * @brief
 *      sets alien to be attached
 *
 * @param[in] value - value alien
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_alien_attach(char *value)
{
	return (set_boolean(__func__, value, &alien_attach));
}

/**
 * @brief
 *      sets alien kill value
 *
 * @param[in] value - value for alien kill
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_alien_kill(char *value)
{
	return (set_boolean(__func__, value, &alien_kill));
}

/**
 * @brief
 *      sets value for restrict user
 *
 * @param[in] value - value for restrict user
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_restrict_user(char *value)
{
	return (set_boolean(__func__, value, &restrict_user));
}

/**
 * @brief
 *      sets value for restrict maxsys user
 *
 * @param[in] value - value for restrict maxsys user
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_restrict_user_maxsys(char *value)
{
	return (set_int(__func__, value, &restrict_user_maxsys));
}

/**
 * @brief
 *	Exempt users from the restrict user feature.  The restrict_user_exempt_uids
 *	array holds the uids of the exempted users.
 *
 * @param[in]	user_list	comma separated string of usernames
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */
static handler_ret_t
set_restrict_user_exceptions(char *user_list)
{
	char *db_admins = NULL;
	char *p2 = NULL;
	struct passwd *pwent;
	int i;

	db_admins = strdup(user_list);
	if (db_admins == NULL) {
		log_err(errno, __func__, "strdup failed");
		return HANDLER_FAIL;
	}

	p2 = strtok(db_admins, ", ");

	i = 0;
	while (p2 != NULL) { /* each part */

		if (i == NUM_RESTRICT_USER_EXEMPT_UIDS) {
			sprintf(log_buffer, "Reached limit on # of uids exempted from dorestrict_user = %d", NUM_RESTRICT_USER_EXEMPT_UIDS);
			log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__,
				  log_buffer);

			break;
		}

		if (((pwent = getpwnam(p2)) == NULL)) {
			sprintf(log_buffer, "user %s doesn't exist", p2);
			log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__,
				  log_buffer);
		} else if (pwent->pw_uid == 0) {
			sprintf(log_buffer,
				"user %s ignored because uid=0", p2);
			log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__,
				  log_buffer);
		} else {
			restrict_user_exempt_uids[i] = pwent->pw_uid;
			sprintf(log_buffer,
				"restrict_user_exempt_uids[%d]=%d (user %s)",
				i, restrict_user_exempt_uids[i], p2);
			log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__,
				  log_buffer);
			i++;
		}
		p2 = strtok(NULL, ", ");
	} /* while */

	/* terminate the list of uids */
	if (i < NUM_RESTRICT_USER_EXEMPT_UIDS) {
		restrict_user_exempt_uids[i] = 0;
	}

	(void) free(db_admins);

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *      sets value for gen_nodefile_on_sister_mom
 *
 * @param[in] value - value for gen_nodefile_on_sister_mom
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
set_gen_nodefile_on_sister_mom(char *value)
{
	return (set_boolean(__func__, value, &gen_nodefile_on_sister_mom));
}

/**
 * @brief
 *	Set the configuration flag that defines whether to get rid of
 *	all vnode defs when reading the config files.
 *
 * @return	handler_ret_t.
 * @retval	0	Failure
 * @retval	1	Success
 *
 */
static handler_ret_t
set_vnode_additive(char *value)
{
	static char id[] = "set_vnodedef_additive";

	return (set_boolean(id, value, &vnode_additive));
}

/**
 * @brief
 *	parse the mom config file
 *
 * @param[in] file - filename
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
parse_config(char *file)
{
	FILE *conf;
	char line[512];
	char name[256], *str;
	int linenum, i;
	int err = 0;
	int num_newstaticdefs;
	handler_ret_t handler_ret = HANDLER_SUCCESS; /* init to success */

	if ((conf = fopen(file, "r")) == NULL) {
		sprintf(log_buffer, "fopen: %s", file);
		log_err(errno, __func__, log_buffer);
		return HANDLER_FAIL;
	} else {
		sprintf(log_buffer, "file %s", file);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
			  __func__, log_buffer);
	}

	num_newstaticdefs = 0;
	linenum = 0;
	while (fgets(line, sizeof(line), conf)) {
		linenum++;
		if (line[0] == '#') /* comment */
			continue;
		str = skipwhite(line); /* pass over initial whitespace */
		if (*str == '\0')
			continue;

		skip_trailing_spcl_char(line, 13);

		if (*str == '$') {		   /* special command */
			str = TOKCPY(++str, name); /* resource name */
			for (i = 0; special[i].name; i++) {
				if (strcmp(name, special[i].name) == 0)
					break;
			}
			if (special[i].name == NULL) { /* didn't find it */
				sprintf(log_buffer,
					"command name %s not found", name);
				log_err(-1, __func__, log_buffer);
				err = 1;
				continue;
			}
			str = skipwhite(str); /* command param */
			rmnl(str);

			if ((handler_ret = special[i].handler(str)) ==
			    HANDLER_FAIL) {
				sprintf(log_buffer,
					"%s[%d] command \"$%s %s\" failed, aborting",
					file, linenum, name, str);
				log_err(-1, __func__, log_buffer);
				err = 1;
			} else if (handler_ret == HANDLER_REPARSE) {
				/*
				 *	handler() asked us to pass parsing off
				 *	to the function that understands how to
				 *	read new-style configuration files.
				 *	As a result, this function will not
				 *	continue processing this file.  As an
				 *	additional check, we require that this
				 *	redirection occur at line 1 of the file
				 *	we're currently parsing.
				 */
				if (linenum != 1) {
					sprintf(log_buffer,
						"%s:  handler REPARSE at line %d",
						file, linenum);
					log_err(-1, __func__, log_buffer);
					handler_ret = HANDLER_FAIL;
					err = 1;
					break;
				}
				handler_ret = config_versionhandler(str, file,
								    conf);
				break;
			}
			continue;
		}

		if (add_static(str, file, linenum))
			continue;
		num_newstaticdefs++;
	}
	nconfig += num_newstaticdefs;

	(void) fclose(conf);
	if (err)
		return HANDLER_FAIL;
	return handler_ret;
}

/**
 * @brief
 *	Check the version number supplied to the $configversion directive to
 *	make sure it's within the range of versions we support.
 *
 * @param[in] value - value for config version check
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
config_verscheck(char *value)
{
	int vers;
	char *err = "$configversion value is out of range"
		    " (min %d, max %d) - will treat as old-style";

	vers = atoi(value);
	if ((vers < CONFIG_MINVERS) || (vers > CONFIG_MAXVERS)) {
		(void) sprintf(log_buffer, err, CONFIG_MINVERS, CONFIG_MAXVERS);
		log_err(-1, __func__, log_buffer);
		return HANDLER_FAIL;
	}

	return HANDLER_REPARSE;
}

/**
 * @brief
 *	version handler function for config file
 *
 * @param[in] value - value for config version check
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
config_versionhandler(char *value, const char *filename, FILE *fp)
{
	int vers;
	vnl_t *nv;
	extern callfunc_t vn_callback;

	switch (vers = atoi(value)) {
		case CONFIG_VNODEVERS:
			if ((nv = vn_parse_stream(fp, vn_callback)) == NULL) {
				(void) sprintf(log_buffer,
					       "error(s) parsing vnode definitions file %s",
					       filename);
				log_err(-1, __func__, log_buffer);
				return HANDLER_FAIL;
			}

			if (vnlp == NULL)
				vnlp = nv;
			else {
				vn_merge(vnlp, nv, vn_callback);
				vnl_free(nv);
			}
			break;

		default:
			(void) sprintf(log_buffer,
				       "unhandled config file version (%d) in file %s",
				       vers, filename);
			return HANDLER_FAIL;
	}

	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	compares two directories from directory list
 *
 * @param[in] s1 - directory 1
 * @param[in] s2 - directory 2
 *
 * @return int
 * @retval 0 Failure
 * @retval 1 Success
 *
 */

static int
dirsort(const void *s1, const void *s2)
{
	return (strcmp(*((const char **) s1), *((const char **) s2)));
}

/**
 * @brief
 *	frees the directory list
 *
 * @param[in] list - pointer to pointer directory list
 *
 * @return Void
 *
 */

static void
free_dirlist(char **list)
{
	char **p;

	if (list != NULL) {
		for (p = list; (p != NULL) && (*p != NULL); p++)
			free(*p);
		free(list);
	}
}

/**
 * @brief
 *	Given a directory name, read its contents.
 *
 * @param[in] dirname	directory name to read
 * @param[out] mod	modified time of the directory
 *
 * @return a sorted, NULL-terminated list of strings (excluding "." and "..")
 * @retval NULL					Failure
 * @retval NULL-terminated list of strings	Success
 *
 */
static char **
do_readdir(char *dirname, time_t *mod)
{
	DIR *dirp;
	struct dirent *dp;
	char **list = NULL;
	char **newlist;
	size_t nelem = 0;

	if ((dirp = opendir(dirname)) == NULL) {
		perror(dirname);
		return NULL;
	}
	/*
	 * Get mod time if requested.
	 */
	if (mod != NULL) {
		struct stat sb;

		if (stat(dirname, &sb) == -1) {
			perror(dirname);
			return NULL;
		}
		*mod = sb.st_mtime;
	}

	while (errno = 0, (dp = readdir(dirp)) != NULL) {
		char *s;

		if ((strcmp(dp->d_name, ".") == 0) ||
		    (strcmp(dp->d_name, "..") == 0))
			continue;
		newlist = realloc(list, (nelem + 2) * sizeof(char *));
		if (newlist == NULL) {
			log_err(errno, __func__, "realloc");
			free_dirlist(list);
			(void) closedir(dirp);
			return NULL;
		} else
			list = newlist;
		if ((s = strdup(dp->d_name)) == NULL) {
			log_err(errno, __func__, "strdup");
			free_dirlist(list);
			(void) closedir(dirp);
			return NULL;
		} else {
			/*
			 *	N.B.:  the free_dirlist() function above
			 *	depends on the list always being NULL-
			 *	terminated.
			 */
			list[nelem++] = s;
			list[nelem] = NULL;
		}
	}
	if (errno != 0 && errno != ENOENT) {
		free_dirlist(list);
		perror(dirname);
		(void) closedir(dirp);
		return NULL;
	}
	(void) closedir(dirp);

	if (list)
		qsort(list, nelem, sizeof(*list), dirsort);
	return (list);
}

/**
 * @brief
 *	We make two passes through the list of additional configuration
 *	files, first executing those whose names begin with a special
 *	prefix (see path_addconfigs_reserved_prefix above) which marks
 *	them as reserved for our use, then the rest (which customers
 *	may have added).  Files reserved for our use may have special
 *	handling.  See the addspecial[] list above.
 *
 *	Depending on the error encountered, we may either return
 *	immediately (return HANDLER_FAIL) or set a return value
 *	that will cause the overall config file reading process
 *	to fail, but allow us to continue parsing other config
 *	files (ret = HANDLER_FAIL).
 *
 * @return      handler_ret_t
 * @retval      HANDLER_SUCCESS         success
 * @retval      HANDLER_FAIL            Failure
 *
 */

static handler_ret_t
do_addconfigs(void)
{
	struct stat sb;
	long pathlen;
	char *namebuf = NULL;
	char **list; /* ... of config files */
	char **listhead;
	unsigned int i;
	time_t modtime;
	handler_ret_t ret = HANDLER_SUCCESS; /* accumulated return value */

	if (stat(path_addconfigs, &sb) == -1) /* no work to do */
		return HANDLER_SUCCESS;

	if ((pathlen = pathconf(mom_home, _PC_PATH_MAX)) == -1) {
		log_err(errno, __func__, "pathconf");
		return HANDLER_FAIL;
	} else if ((namebuf = malloc(pathlen)) == NULL) {
		log_err(errno, __func__, "malloc");
		return HANDLER_FAIL;
	}

	if ((listhead = do_readdir(path_addconfigs, &modtime)) == NULL) {
		free(namebuf);
		return HANDLER_SUCCESS; /* no work to do */
	}
	for (list = listhead; list != NULL && *list != NULL; list++) {
		if (strstr(*list, path_addconfigs_reserved_prefix) == *list) {
			if (snprintf(namebuf, pathlen, "%s/%s", path_addconfigs,
				     *list) >= pathlen) {
				sprintf(log_buffer, "%s/%s", path_addconfigs,
					*list);
				log_err(ENAMETOOLONG, __func__, log_buffer);
				free(namebuf);
				free_dirlist(listhead);
				return HANDLER_FAIL;
			}
			for (i = 0; addspecial[i].name; i++) {
				if (strcmp(*list, addspecial[i].name) == 0)
					break;
			}
			if (addspecial[i].name == NULL) {
				/* no special handling */
				if (parse_config(namebuf) == HANDLER_FAIL)
					ret = HANDLER_FAIL;
			} else {
				if (addspecial[i].handler(namebuf) ==
				    HANDLER_FAIL)
					ret = HANDLER_FAIL;
			}
		}
	}
	for (list = listhead; list != NULL && *list != NULL; list++) {
		if (strstr(*list, path_addconfigs_reserved_prefix) != *list) {
			if (snprintf(namebuf, pathlen, "%s/%s", path_addconfigs,
				     *list) >= pathlen) {
				sprintf(log_buffer, "%s/%s", path_addconfigs,
					*list);
				log_err(ENAMETOOLONG, __func__, log_buffer);
				free(namebuf);
				free_dirlist(listhead);
				return HANDLER_FAIL;
			}
			if (parse_config(namebuf) == HANDLER_FAIL)
				ret = HANDLER_FAIL;
		}
	}

	free(namebuf);
	free_dirlist(listhead);

	if (vnlp != NULL && modtime > vnlp->vnl_modtime)
		vnlp->vnl_modtime = modtime;

	return (ret);
}

/**
 * @brief
 *	Open and read the config file.  Save information in a linked
 *	list.  After reading the file, create an array, copy the list
 *	elements to the array and free the list.
 *
 * @param[in] file - filename
 *
 * @return int
 * @retval 0 success
 * @retval 1 error
 *
 */
int
read_config(char *file)
{
	struct config_list *cp;
	struct config *ap;
	int i, j;
	int addconfig_ret;

	/*	initialize variable that can be set by config entries in case	*/
	/*	they are removed and we are HUPped				*/

	for (i = 0; i < mask_num; i++)
		free(maskclient[i]);
	mask_num = 0;

	average_percent_over = 50;
	average_cpufactor = 1.025;
	average_trialperiod = 120;
	complex_mem_calc = 0;
	cpuburst = 0;
	cpuaverage = 0;
	cputfactor = 1.0;
	delta_percent_over = 50;
	delta_cpufactor = 1.05;
	delta_weightup = 0.4;
	delta_weightdown = 0.1;
	enforce_mem = 0;
	ideal_load_val = -1.0;
	max_load_val = -1.0;
	idle_avail = 0;
	idle_busy = 10;
	idle_check = -1;
	idle_poll = 1;
	idle_on_maxload = 0;
	cycle_harvester = 0;
	wallfactor = 1.0;
	suspend_signal = SIGSTOP;
	resume_signal = SIGCONT;
	restart_background = FALSE;
	reject_root_scripts = FALSE;
	report_hook_checksums = TRUE;
	restart_transmogrify = FALSE;
	attach_allow = TRUE;
	max_check_poll = MAX_CHECK_POLL_TIME;
	min_check_poll = MIN_CHECK_POLL_TIME;
	vnode_additive = 1; /* keep vnodes on HUP */
	joinjob_alarm_time = -1;
	job_launch_delay = -1;
#ifdef NAS	       /* localmod 015 */
	spoolsize = 0; /* unlimited by default */
#endif		       /* localmod 015 */

#if MOM_ALPS
	alps_release_wait_time = ALPS_REL_WAIT_TIME_DFLT;
	alps_release_jitter = ALPS_REL_JITTER_DFLT;
	vnode_per_numa_node = FALSE;
	alps_release_timeout = ALPS_REL_TIMEOUT;
	alps_confirm_empty_timeout = ALPS_CONF_EMPTY_TIMEOUT;
	alps_confirm_switch_timeout = ALPS_CONF_SWITCH_TIMEOUT;
	set_alps_client(NULL);
#endif /* MOM_ALPS */

	strcpy(pbs_jobdir_root, "");
	restrict_user = 0;
	restrict_user_maxsys = 999;
	gen_nodefile_on_sister_mom = TRUE;
	for (j = 0; j < NUM_RESTRICT_USER_EXEMPT_UIDS; j++)
		if (restrict_user_exempt_uids[j] != 0)
			restrict_user_exempt_uids[j] = 0;

	for (i = 0; i < (int) LastAction; i++) {
		if (mom_action[i].ma_script) {
			free(mom_action[i].ma_script);
			mom_action[i].ma_script = NULL;
		}
		if (mom_action[i].ma_args) {
			for (j = 0; mom_action[i].ma_args[j]; j++)
				free(mom_action[i].ma_args[j]);
			free(mom_action[i].ma_args);
			mom_action[i].ma_args = NULL;
		}
		mom_action[i].ma_timeout = 0;
	}

	if (file == NULL)
		file = config_file;
	if (file[0] == '\0')
		return 0; /* no config file */

	if (access(file, F_OK) == -1) {
		sprintf(log_buffer, "access: %s", file);
		log_err(errno, __func__, log_buffer);
		if (config_file_specified)
			return 1; /* file given and not there = error */
		else
			return 0; /* ok for "config" not to be there  */
	}
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
	int perm;
	perm = get_permission("write");
	if (chk_file_sec(file, 0, 0, perm, FULLPATH)) {
		sprintf(log_buffer,
			"warning: %s file has a non-secure file access mask", file);
		log_err(errno, __func__, log_buffer);
		return 1;
	}
#endif /* NO_SECURITY_CHECK */

	nconfig = 0;
	if (parse_config(file) == HANDLER_FAIL)
		return 1;

	addconfig_ret = do_addconfigs();

	/* check for any bad combinations */
	if (min_check_poll > max_check_poll) {
		sprintf(log_buffer, "min_check_poll(%u) > max_check_poll(%u)",
			min_check_poll, max_check_poll);
		log_event(PBSEVENT_SYSTEM, 0, LOG_ERR, __func__, log_buffer);

		max_check_poll = MAX_CHECK_POLL_TIME;
		min_check_poll = MIN_CHECK_POLL_TIME;
	}
	sprintf(log_buffer, "max_check_poll = %u, min_check_poll = %u",
		max_check_poll, min_check_poll);
	log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__, log_buffer);

	inc_check_poll = (max_check_poll - min_check_poll + 19) / 20;

	if (restart_transmogrify) {
		if (mom_action[RestartAction].ma_script == NULL) {
			sprintf(log_buffer, "restart_transmogrify "
					    "value is TRUE but there is no restart script;"
					    " This is unsupported");
			log_err(-1, __func__, log_buffer);
			return 1;
		} else if (!restart_background) {
			sprintf(log_buffer, "WARNING: restart_background "
					    "value is FALSE but restart_transmogrify is "
					    "TRUE; this type of restart takes place in "
					    "the background regardless of the setting "
					    "of restart_background");
			log_err(-1, __func__, log_buffer);
		}
	} else if (!restart_background &&
		   mom_action[RestartAction].ma_script != NULL) {
		sprintf(log_buffer, "WARNING: restart_background value "
				    "is FALSE but restart is being done by a script; "
				    "restart_background forced TRUE");
		log_err(-1, __func__, log_buffer);
	}

	/* Create a new config_array[] */
	if (config_array) {
		for (ap = config_array; ap->c_name; ap++) {
			free(ap->c_name);
			free(ap->c_u.c_value);
		}
		free(config_array);
	}
	config_array = (struct config *) calloc(nconfig + 1,
						sizeof(struct config));
	memcheck((char *) config_array);

	/* copy information from config_list to config_array[] */
	for (i = 0, ap = config_array; i < nconfig; i++, ap++) {
		*ap = config_list->c;
		cp = config_list->c_link;
		free(config_list); /* don't free name and value strings */
		config_list = cp;  /* they carry over from the list */
	}
	ap->c_name = NULL; /* config_array[] is NULL-terminated */

	if (addconfig_ret == HANDLER_FAIL)
		return (1);
	else {
		if (joinjob_alarm_time == -1)
			update_joinjob_alarm_time = 1;
		else
			update_joinjob_alarm_time = 0;

		if (job_launch_delay == -1)
			update_job_launch_delay = 1;
		else
			update_job_launch_delay = 0;

		return (0);
	}
}

/**
 * @brief
 *	Inserts the config file
 *
 * @param[in] name - name of file
 * @param[in] input - input for file
 *
 * @return Void
 *
 */

void
doconfig_insert(char *name, char *input)
{
	struct stat sb;
	ssize_t nread;
	int fdin, fdout;
	long pathlen;
	char *namebuf;
	char iobuf[BUFSIZ];

	sprintf(log_buffer, "name %s, input %s", name, input);
	log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  __func__, log_buffer);

	if (stat(path_addconfigs, &sb) == -1) {
		/*
		 *	First time through, we will need to make the directory
		 *	that holds new config files.
		 */

		if (mkdir(path_addconfigs, S_IRWXU) == -1) {
			sprintf(log_buffer, "mkdir %s", path_addconfigs);
			log_err(errno, __func__, log_buffer);
			exit(1);
		}
	} else if (!S_ISDIR(sb.st_mode)) {
		sprintf(log_buffer, "%s is not a directory (0%o)",
			path_addconfigs, sb.st_mode);
		log_err(-1, __func__, log_buffer);
		exit(1);
	}

	if ((pathlen = pathconf(mom_home, _PC_PATH_MAX)) == -1) {
		log_err(errno, __func__, "pathconf");
		exit(1);
	} else if ((namebuf = malloc(pathlen)) == NULL) {
		log_err(errno, __func__, "malloc");
		exit(1);
	}

	if (snprintf(namebuf, pathlen, "%s/%s", path_addconfigs, name) >=
	    pathlen) {
		sprintf(log_buffer, "%s/%s", path_addconfigs, name);
		log_err(ENAMETOOLONG, __func__, log_buffer);
		exit(1);
	}
	if (stat(namebuf, &sb) == 0) {
		sprintf(log_buffer, "attempt to add existing config file %s",
			namebuf);
		log_err(EEXIST, __func__, log_buffer);
		exit(1);
	}
	if (strstr(name, path_addconfigs_reserved_prefix) == name) {
		sprintf(log_buffer, "config file may not start with \"%s\"",
			path_addconfigs_reserved_prefix);
		log_err(EPERM, __func__, log_buffer);
		exit(1);
	}

	fdin = fdout = -1; /* avoid accidentally close()ing an open fd */
	if (access(input, R_OK) == -1) {
		sprintf(log_buffer, "access R_OK %s", input);
		log_err(errno, __func__, log_buffer);
		exit(1);
	} else if ((fdin = open(input, O_RDONLY)) == -1) {
		sprintf(log_buffer, "open %s", input);
		log_err(errno, __func__, log_buffer);
		exit(1);
	} else if ((fdout = open(namebuf, O_WRONLY | O_CREAT, 0600)) == -1) {
		sprintf(log_buffer, "open %s", namebuf);
		log_err(errno, __func__, log_buffer);
		exit(1);
	} else {
		while ((nread = read(fdin, iobuf, sizeof(iobuf))) != 0) {
			if (nread == -1) {
				log_err(errno, __func__, "read");
				exit(1);
			} else if (write(fdout, iobuf, nread) != nread) {
				log_err(errno, __func__, "write");
				exit(1);
			}
		}
	}

	(void) close(fdout);
	(void) close(fdin);
	free(namebuf);
}

/**
 * @brief
 *	Removes config files
 *
 * @param[in] name - name of file
 *
 * @return Void
 *
 */

void
doconfig_remove(char *name)
{
	struct stat sb;
	long pathlen;
	char *namebuf;

	sprintf(log_buffer, "name %s", name);
	log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  __func__, log_buffer);

	if ((pathlen = pathconf(mom_home, _PC_PATH_MAX)) == -1) {
		log_err(errno, __func__, "pathconf");
		exit(1);
	} else if ((namebuf = malloc(pathlen)) == NULL) {
		log_err(errno, __func__, "malloc");
		exit(1);
	}

	if (strstr(name, path_addconfigs_reserved_prefix) == name) {
		sprintf(log_buffer, "file begins with reserved prefix \"%s\""
				    " and may not be removed",
			path_addconfigs_reserved_prefix);
		log_err(EPERM, __func__, log_buffer);
		exit(1);
	}

	if (snprintf(namebuf, pathlen, "%s/%s", path_addconfigs, name) >=
	    pathlen) {
		sprintf(log_buffer, "%s/%s", path_addconfigs, name);
		log_err(ENAMETOOLONG, __func__, log_buffer);
		exit(1);
	}
	if (stat(namebuf, &sb) == -1) {
		log_err(errno, __func__, namebuf);
		exit(1);
	}

	if (S_ISREG(sb.st_mode)) {
		if (unlink(namebuf) == -1) {
			log_err(errno, __func__, namebuf);
			exit(1);
		}
	} else {
		/*
		 *	We were asked to remove something that was not a regular
		 *	file, and refuse.  We use the same error (EPERM) that
		 *	unlink() returns if ``The file named by path is a
		 *	directory, and either the calling process does not
		 *	have appropriate privileges, or the implementation
		 *	prohibits using unlink() on directories.''
		 */
		sprintf(log_buffer, "%s/%s", path_addconfigs, name);
		log_err(EPERM, __func__, log_buffer);
		exit(1);
	}

	free(namebuf);
}

/**
 * @brief
 *	Lists the config files by reading directory
 *
 * @return Void
 *
 */

void
doconfig_list(void)
{
	struct stat sb;
	char **list;
	char **listhead;

	log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, "");

	if (stat(path_addconfigs, &sb) == -1)
		return;
	if ((listhead = do_readdir(path_addconfigs, NULL)) == NULL)
		return; /* no work to do */

	for (list = listhead; list != NULL && *list != NULL; list++)
		if (strstr(*list, path_addconfigs_reserved_prefix) == *list)
			printf("%s\n", *list);
	for (list = listhead; list != NULL && *list != NULL; list++)
		if (strstr(*list, path_addconfigs_reserved_prefix) != *list)
			printf("%s\n", *list);

	free_dirlist(listhead);
}

/**
 * @brief
 *
 */
void
doconfig_show(char *name)
{
	ssize_t nread;
	int fdin;
	long pathlen;
	char *namebuf;
	char iobuf[BUFSIZ];

	sprintf(log_buffer, "name %s", name);
	log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  __func__, log_buffer);

	if ((pathlen = pathconf(mom_home, _PC_PATH_MAX)) == -1) {
		log_err(errno, __func__, "pathconf");
		exit(1);
	} else if ((namebuf = malloc(pathlen)) == NULL) {
		log_err(errno, __func__, "malloc");
		exit(1);
	}

	if (snprintf(namebuf, pathlen, "%s/%s", path_addconfigs, name) >=
	    pathlen) {
		sprintf(log_buffer, "%s/%s", path_addconfigs, name);
		log_err(ENAMETOOLONG, __func__, log_buffer);
		exit(1);
	}
	fdin = -1; /* avoid accidentally close()ing an open fd */
	if (access(namebuf, R_OK) == -1) {
		sprintf(log_buffer, "access R_OK %s", namebuf);
		log_err(errno, __func__, log_buffer);
		exit(1);
	} else if ((fdin = open(namebuf, O_RDONLY)) == -1) {
		sprintf(log_buffer, "open %s", namebuf);
		log_err(errno, __func__, log_buffer);
		exit(1);
	} else {
		while ((nread = read(fdin, iobuf, sizeof(iobuf))) != 0) {
			if (nread == -1) {
				log_err(errno, __func__, "read");
				exit(1);
			} else if (write(1, iobuf, nread) != nread) {
				log_err(errno, __func__, "write");
				exit(1);
			}
		}
	}

	(void) close(fdin);
	free(namebuf);
}

/**
 * @brief
 *	wrapper function for different operations on config file
 *
 * @param[in] action - action name
 * @param[in] name - name of config file
 * @param[in] input - input for config file
 *
 * @return Void
 *
 */

void
do_configs(char *action, char *name, char *input)
{

	if (strcmp(action, "insert") == 0)
		doconfig_insert(name, input);
	else if (strcmp(action, "remove") == 0)
		doconfig_remove(name);
	else if (strcmp(action, "show") == 0)
		doconfig_show(name);
	else if (strcmp(action, "list") == 0)
		doconfig_list();
	else {
		sprintf(log_buffer, "internal error:  unexpected action %s",
			action);
		log_err(-1, __func__, log_buffer);
	}

	exit(0);
}

/**
 * @brief
 *	Get an rm_attribute structure from a string.  If a NULL is passed
 *	for the string, use the previously remembered string.
 *
 * @param[in] str - string holding info of attributes structure
 *
 * @return structure handle
 * @retval	pointer to rm_attribute structure	Success
 * @retval	NULL					Failure
 *
 */
struct rm_attribute *
momgetattr(char *str)
{
	static char cookie[] = "tag:"; /* rm_attribute to ignore */
	static char *hold = NULL;
	static char qual[256] = "";
	static char valu[4096] = "";
	static struct rm_attribute attr = {qual, valu};
	int level, i;

	if (str == NULL) /* if NULL is passed, used prev value */
		str = hold;

	do {
		str = skipwhite(str);
		if (*str++ != '[')
			return NULL;

		str = skipwhite(str); /* copy qualifier */
		str = TOKCPY(str, qual);
		str = skipwhite(str);

		if (*str++ != '=')
			return NULL;

		level = 0;
		for (i = 0; *str; str++, i++) {
			if (*str == '[')
				level++;
			else if (*str == ']') {
				if (level == 0)
					break;
				level--;
			}
			valu[i] = *str;
		}
		if (*str++ != ']')
			return NULL;

		valu[i] = '\0';
		DBPRT(("momgetattr: found %s = %s\n", qual, valu))
	} while (strncmp(qual, cookie, sizeof(cookie) - 1) == 0);
	hold = str;
	DBPRT(("momgetattr: passing back %s = %s\n", qual, valu))
	return &attr;
}

/**
 * @brief
 *	Check the request against the format of the line read from
 *	the config file.  If it is a static value, there should be
 *	no params.  If it is a shell escape, the parameters (if any)
 *	should match the command line for the system call.
 *
 *
 */
char *
conf_res(char *s, struct rm_attribute *attr)
{
	char *name[RM_NPARM];
	char *value[RM_NPARM];
	int used[RM_NPARM];
	char param[256], *d;
	int i, len;
	char *filename = NULL;
#ifdef WIN32
	pio_handles child;
#else
	FILE *child;
	int fd;
#endif
	char *child_spot;
	int child_len;
	int secondalarm = 0;
	int err;
	int perm;

	if (*s != '!') { /* static value */
		if (attr) {
			sprintf(ret_string, "? %d", RM_ERR_BADPARAM);
			return ret_string;
		} else
			return s;
	}

	if (restrictrm) /* no restricted shell escape */
		return "?";

	/*
	 **	From here on we are going to put together a shell command
	 **	to do the requestor's bidding.  Parameter substitution
	 **	is the first step.
	 */
	for (i = 0; i < RM_NPARM; i++) { /* remember params */
		if (attr == NULL)
			break;
		name[i] = strdup(attr->a_qualifier);
		memcheck(name[i]);
		value[i] = strdup(attr->a_value);
		memcheck(value[i]);
		used[i] = 0;
		attr = momgetattr(NULL);
	}
	if (attr) { /* too many params */
		log_err(-1, __func__, "too many parms");
		sprintf(ret_string, "? %d", RM_ERR_BADPARAM);
		goto done;
	}
	name[i] = NULL;

	for (d = ret_string, s++; *s;) { /* scan command */
		if (*s == '%') {	 /* possible token */
			char *hold;

			hold = TOKCPY(s + 1, param);
			for (i = 0; name[i]; i++) {
				if (strcmp(param, name[i]) == 0)
					break;
			}
			if (name[i]) { /* found a match */
				char *x = value[i];
				while (*x)
					*d++ = *x++;
				s = hold;
				used[i] = 1;
			} else
				*d++ = *s++;
		} else
			*d++ = *s++;
	}
	for (i = 0; name[i]; i++) {
		if (!used[i]) { /* parameter sent but not used */
			log_err(-1, __func__, "unused parameters");
			sprintf(ret_string, "? %d", RM_ERR_BADPARAM);
			goto done;
		}
	}

	*d = '\0';
	DBPRT(("command: %s\n", ret_string))

	filename = get_script_name(ret_string);
	if (filename == NULL)
		return NULL;
	/* Make sure file does not have open permissions */
	perm = get_permission("write");
	err = tmp_file_sec(filename, 0, 1, perm, 1);

	if (err != 0) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "error: %s file has a non-secure file access, errno: %d", filename, err);
		log_event(PBSEVENT_SECURITY, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
		goto done;
	}

#ifdef WIN32
	if (!win_popen(ret_string, "w", &child, NULL)) {
		errno = GetLastError();
		log_err(errno, __func__, "popen");
		sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
		goto done;
	}
#else
	if ((child = pbs_popen(ret_string, "r")) == NULL) {
		log_err(errno, __func__, "popen");
		sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
		goto done;
	}
#endif /* WIN32 */

#ifdef WIN32
	shell_escape_handle = child.pi.hProcess;
	(void) win_alarm(alarm_time, shell_escape_timeout);
#else
	fd = fileno(child);
#endif /* WIN32 */
	child_spot = ret_string;
	child_len = 0;
	child_spot[0] = '\0';
	while (child_len < ret_size) {

#ifdef WIN32
		if ((len = win_pread(&child, child_spot, ret_size - child_len)) > 0)
#else
		if ((len = read(fd, child_spot, ret_size - child_len)) > 0)
#endif
		{

			for (i = 0; i < len; i++) {
#ifdef WIN32
				/* match \r\n in windows */
				if ((child_spot[i] == '\n') || (child_spot[i] == '\r'))
#else
				if (child_spot[i] == '\n')
#endif
				{
					child_spot[i] = '\0';
					break;
				}
			}
			if (i < len) /* found newline */
				break;

			child_len += len;
			if (child_len >= ret_size) {
				log_err(-1, __func__, "line too long");
				sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
				break;
			}

			child_spot += len;
			checkret(&child_spot, len);
		} else if (len == 0) {
#ifdef WIN32
			if (GetLastError() == ERROR_BROKEN_PIPE) {
				log_err(errno, __func__, "resource read");
				sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
			}
#endif
			break;
		} else if ((len == -1) && (errno == EINTR)) {
			log_err(errno, __func__, "resource read alarm");
			if (secondalarm) {
#ifndef WIN32
				pbs_pkill(child, SIGKILL);
#endif
				sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
				break;
			} else {
#ifndef WIN32
				pbs_pkill(child, SIGINT);
				(void) alarm(alarm_time);
#endif
				secondalarm = 1;
			}
		} else {
			log_err(errno, __func__, "resource read");
			sprintf(ret_string, "? %d", RM_ERR_SYSTEM);
			break;
		}
	}

#ifdef WIN32

	if (shell_escape_handle != INVALID_HANDLE_VALUE) {
		close_valid_handle(&(shell_escape_handle));
		child.pi.hThread = INVALID_HANDLE_VALUE;
		child.pi.hProcess = INVALID_HANDLE_VALUE;
	}
	(void) win_alarm(0, NULL);
	win_pclose(&child);
#else
	pbs_pclose(child);
#endif /* WIN32 */

done:
	for (i = 0; name[i]; i++) { /* free up params */
		free(name[i]);
		free(value[i]);
	}
	free(filename);
	return ret_string;
}
#ifndef WIN32
extern void process_hup(void);
#endif

#ifdef DEBUG
/**
 * @brief
 *	creates logs event
 *
 * @param[in] id - function name
 * @param[in] buf - msg
 * @param[in] len - length of msg
 *
 * @return Void
 *
 */
void
log_verbose(char *id, char *buf, int len)
{
	int i;
	char *cp;

	len = MIN(len, 50);
	cp = log_buffer;
	for (i = 0; i < len; i++) {
		int c = buf[i];

		if (isprint(c))
			*cp++ = c;
		else {
			sprintf(cp, "(%d)", c);
			cp += strlen(cp);
		}
	}
	*cp = '\0';
	log_event(PBSEVENT_DEBUG, 0, LOG_DEBUG, id, log_buffer);
}
#else
#define log_verbose(a, b, c)
#endif

/**
 * @brief
 *	See if an IP address matches any names stored as "restricted"
 *	access hosts.  Return 0 if a name matches, 1 if not.
 *
 * @param[in] ipadd - ip address of host
 *
 * @return	int
 * @retval	0	Failure
 * @retval	1	Success
 *
 */
int
bad_restrict(u_long ipadd)
{
	struct hostent *host;
	struct in_addr in;
	int i, len1, len2;
	char *cp1, *cp2;

	in.s_addr = htonl(ipadd);
	if ((host = gethostbyaddr((void *) &in,
				  sizeof(struct in_addr), AF_INET)) == NULL)
		return 1;
	len1 = strlen(host->h_name) - 1;

	for (i = 0; i < mask_num; i++) {
		len2 = strlen(maskclient[i]) - 1;
		if (len1 < len2)
			continue;
		cp1 = &host->h_name[len1];
		cp2 = &maskclient[i][len2];
		while (len2 >= 0 && tolower(*cp1) == tolower(*cp2)) {
			cp1--;
			cp2--;
			len2--;
		}
		if ((len2 == 0 && *cp2 == '*') || len2 == -1)
			return 0;
	}
	return 1;
}

/**
 * @brief
 *	Process a request for the resource monitor. The i/o
 *	will take place using DIS over a tcp fd or an tpp stream.
 *
 * @param[in] iochan - i/o channel to indicate stream or fd
 * @param[in] version - protocol version
 * @param[in] prot - PROT_TCP or PROT_TPP
 *
 * @return int
 * @retval	0	Success
 * @retval	-1	Failure
 *
 */

int
rm_request(int iochan, int version, int prot)
{
	char name[256];
	static char *output = NULL;
	static int output_size = 0;
	int len;
	int command, ret;
	char *curr, *value, *cp, *body;
	struct config *ap;
	struct rm_attribute *attr;
	struct sockaddr_in *addr;
	u_long ipadd = 0;
	u_short port = 0;
	void (*close_io)(int) = NULL;

	errno = 0;
	if (!output) {
		output = (char *) malloc(BUFSIZ);
		if (!output) {
			log_err(errno, __func__, "malloc");
			goto bad;
		}
		output_size = BUFSIZ;
	}
	(void) memset(output, 0, output_size);
	if (prot == PROT_TCP) {
		conn_t *conn = get_conn(iochan);

		if (!conn) {
			log_err(errno, __func__,
				"not find iochan in connection table");
			closesocket(iochan);
			return -1;
		}
		ipadd = conn->cn_addr;
		port = conn->cn_port;
		close_io = close_conn;
	} else {
		addr = tpp_getaddr(iochan);
		if (addr == NULL) {
			sprintf(log_buffer, "Sender unknown");
			goto bad;
		}

		ipadd = ntohl(addr->sin_addr.s_addr);
		port = ntohs((unsigned short) addr->sin_port);
		close_io = (void (*)(int)) & tpp_close;
	}
	if (version != RM_PROTOCOL_VER) {
		sprintf(log_buffer, "protocol version %d unknown", version);
		goto bad;
	}

	restrictrm = 0;
	if ((prot == PROT_TCP && port_care && (port >= IPPORT_RESERVED)) || !addrfind(ipadd)) {
		if (bad_restrict(ipadd)) {
			sprintf(log_buffer, "bad attempt to connect");
			goto bad;
		}
		restrictrm = 1;
	}

	/* looks okay, find out what command it is */
	command = disrsi(iochan, &ret);
	if (ret != DIS_SUCCESS) {
		sprintf(log_buffer, "no command %s", dis_emsg[ret]);
		goto bad;
	}

	switch (command) {

		case RM_CMD_CLOSE: /* no response to this */
			close_io(iochan);
			return 1;

		case RM_CMD_REQUEST:
			reqnum++;
			ret = diswsi(iochan, RM_RSP_OK);
			if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"write request response failed: %s",
					dis_emsg[ret]);
				goto bad;
			}

			for (;;) {
				cp = disrst(iochan, &ret);
				if (ret == DIS_EOD)
					break;
				else if (ret != DIS_SUCCESS) {
					sprintf(log_buffer,
						"problem with request line: %s",
						dis_emsg[ret]);
					goto bad;
				}
				curr = skipwhite(cp);
				curr = TOKCPY(curr, name);
				if (strlen(name) == 0) { /* no name */
					sprintf(output, "%s=? %d",
						cp, RM_ERR_UNKNOWN);
				} else {
					ap = rm_search(config_array, name);
					attr = momgetattr(curr);

#ifndef WIN32
					(void) alarm(alarm_time);
#endif
					rm_errno = PBSE_NONE;
					if (ap) /* static */
						value = conf_res(ap->c_u.c_value, attr);
					else /* dynamic */
						value = dependent(name, attr);
#ifndef WIN32
					(void) alarm(0);
#endif
					if (value) {
#if MOM_ALPS
						int lim = 1 << 20;
#else
						int lim = 65536;
#endif

						len = strlen(cp) + strlen(value) + 2;
						if (len >= lim) {
							sprintf(output, "%s=? %d",
								cp, PBSE_BADATVAL);
						} else {
							char *hold;
							if (len > output_size) {
								hold = (char *) realloc(output, len);
								if (!hold) {
									log_err(errno, __func__, "realloc");
									goto bad;
								}
								output = hold;
								output_size = len;
							}
							sprintf(output, "%s=%s", cp, value);
						}
					} else { /* not found anywhere */
						sprintf(output, "%s=? %d", cp, rm_errno);
					}
				}
				free(cp);
				ret = diswst(iochan, output);
				if (ret != DIS_SUCCESS) {
					sprintf(log_buffer,
						"write string failed %s",
						dis_emsg[ret]);
					goto bad;
				}
			}
			break;

		case RM_CMD_CONFIG:
			if (restrictrm) {
				log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
					  LOG_NOTICE | LOG_AUTH, __func__,
					  "restricted configure attempt");
				goto bad;
			}

			log_event(PBSEVENT_SYSTEM, 0, LOG_INFO, __func__, "configure");
			body = disrst(iochan, &ret);
			if (ret == DIS_EOD)
				body = NULL;
			else if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"problem with config body %s",
					dis_emsg[ret]);
				goto bad;
			}
			len = read_config(body);

			ret = diswsi(iochan, len ? RM_RSP_ERROR : RM_RSP_OK);
			if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"write config response failed %s",
					dis_emsg[ret]);
				if (len == 0)	  /* config was okay but reply */
					goto bad; /* didn't work */
			}

			/* check if read_config failed */
			if (len != 0) {
				cleanup();
				log_close(1);
				tpp_shutdown();
#ifdef WIN32
				ExitThread(1);
#else
				exit(1);
#endif
			}
			break;

		case RM_CMD_SHUTDOWN:
			if (restrictrm) {
				log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
					  LOG_NOTICE | LOG_AUTH, __func__,
					  "restricted shutdown attempt");
				goto bad;
			}

			log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, __func__, "shutdown");
			ret = diswsi(iochan, RM_RSP_OK);
			if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"write shutdown response failed %s",
					dis_emsg[ret]);
				log_err(-1, __func__, log_buffer);
			}
			dis_flush(iochan);
			close_io(iochan);
			cleanup();
			log_close(1);
			tpp_shutdown();
#ifdef WIN32
			ExitThread(0);
#else
			exit(0);
#endif

		default:
			sprintf(log_buffer, "unknown command %d", command);
			log_err(-1, __func__, log_buffer);
			ret = diswsi(iochan, RM_RSP_ERROR);
			if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"write default response failed %s",
					dis_emsg[ret]);
				goto bad;
			}
			ret = diswst(iochan, log_buffer);
			if (ret != DIS_SUCCESS) {
				sprintf(log_buffer,
					"write string failed %s",
					dis_emsg[ret]);
				goto bad;
			}
			break;
	}
	if (dis_flush(iochan) == -1) {
		log_err(errno, __func__, "flush");
		goto bad;
	}
	return 0;

bad:
	sprintf(output,
		"\n\tmessage refused from port %d addr %ld.%ld.%ld.%ld", port,
		(ipadd & 0xff000000) >> 24,
		(ipadd & 0x00ff0000) >> 16,
		(ipadd & 0x0000ff00) >> 8,
		(ipadd & 0x000000ff));
	strcat(log_buffer, output);
	log_err(errno, __func__, log_buffer);

	/*
	 ** This is a special case, if the malloc fails for the 'output'
	 ** buffer, then 'close_io' function pointer won't get a chance
	 ** to be initialized. So, Initialize accordingly before use.
	 */
	if (close_io == NULL)
		close_io = (prot == PROT_TCP) ? close_conn : (void (*)(int)) & tpp_close;

	close_io(iochan);
	return -1;
}

/**
 * @brief
 *	Read a message from an TPP stream, figure out if it is a
 *	Resource Monitor request or an InterMom message.
 *
 * @param[in] stream - TPP stream
 *
 * @return Void
 *
 */
void
do_tpp(int stream)
{
	int ret, proto, version;
	void im_request(int stream, int version);
	void is_request(int stream, int version);
	void im_eof(int stream, int ret);

	DIS_tpp_funcs();
	proto = disrsi(stream, &ret);
	if (ret != DIS_SUCCESS) {
		im_eof(stream, ret);
		return;
	}
	version = disrsi(stream, &ret);
	if (ret != DIS_SUCCESS) {
		DBPRT(("%s: no protocol version number %s\n",
		       __func__, dis_emsg[ret]))
		im_eof(stream, ret);
		return;
	}

	switch (proto) {
		case RM_PROTOCOL:
			DBPRT(("%s: got a resource monitor request\n", __func__))
			if (rm_request(stream, version, PROT_TPP) == 0)
				tpp_eom(stream);
			break;

		case IM_PROTOCOL:
			DBPRT(("%s: got an internal task manager request\n", __func__))
			im_request(stream, version);
			break;

		case IS_PROTOCOL:
			DBPRT(("%s: got an inter-server request\n", __func__))
			is_request(stream, version);
			break;

		default:
			DBPRT(("%s: unknown request %d\n", __func__, proto))
			tpp_close(stream);
			break;
	}
}

/* ARGSUSED */

/**
 * @brief
 *	wrapper function for do_tpp
 *
 * @param[in] fd - file descriptor
 *
 * @return Void
 *
 */
void
tpp_request(int fd)
{
	int stream;
	int i;
	/* To reduce tpp process storm reducing max do_tpp processing to MAX_TPP_LOOPS times */
	for (i = 0; i < MAX_TPP_LOOPS; i++) {
		if ((stream = tpp_poll()) == -1) {
#ifdef WIN32
			if (errno != 10054)
#endif
				log_err(errno, __func__, "tpp_poll");
			break;
		}
		if (stream == -2)
			break;
		do_tpp(stream);
	}
}

/**
 * @brief
 *      Read a TCP message from fd, figure out if it is a
 *      Resource Monitor request or an InterMom message.
 *
 * @param[in] fd - tcp msg
 *
 * @return	int
 * @retval	0	Success
 * @retval	!0	Failure
 *
 */

int
do_tcp(int fd)
{
	int ret, proto, version;
	int tm_request(int stream, int version);

	pbs_tcp_timeout = 0;
	proto = disrsi(fd, &ret);
	pbs_tcp_timeout = PBS_DIS_TCP_TIMEOUT_SHORT;

	switch (ret) {
		case DIS_SUCCESS: /* worked */
			break;
		case DIS_EOF: /* closed */
			close_conn(fd);
		case DIS_EOD: /* still open */
			return 1;
		default:
			sprintf(log_buffer, "no protocol number: %s",
				dis_emsg[ret]);
			goto bad;
	}

	version = disrsi(fd, &ret);
	if (ret != DIS_SUCCESS) {
		DBPRT(("%s: no protocol version number %s\n",
		       __func__, dis_emsg[ret]))
		goto bad;
	}

	switch (proto) {
		case RM_PROTOCOL:
			DBPRT(("%s: got a resource monitor request\n", __func__))
			pbs_tcp_timeout = 0;
			ret = rm_request(fd, version, PROT_TCP);
			pbs_tcp_timeout = PBS_DIS_TCP_TIMEOUT_SHORT;
			break;

		case TM_PROTOCOL:
			DBPRT(("%s: got an internal task manager request\n", __func__))
			ret = tm_request(fd, version);
			break;

		default:
			DBPRT(("%s: unknown request %d\n", __func__, proto))
			goto bad;
	}
	return ret;

bad:
	close_conn(fd);
	return -1;
}

/**
 * @brief
 *      wrapper function for do_tcp which calls infinitely
 *
 * @param[in] fd - file descriptor
 *
 * @return Void
 *
 */

void
tcp_request(int fd)
{
	int c;
	long ipadd;
	char address[80];
	conn_t *conn = get_conn(fd);
	if (!conn) {
		sprintf(log_buffer, "could not find fd=%d in connection table",
			fd);
		log_err(-1, __func__, log_buffer);
		closesocket(fd);
		return;
	}

	ipadd = conn->cn_addr;

	sprintf(address, "%ld.%ld.%ld.%ld:%d",
		(ipadd & 0xff000000) >> 24,
		(ipadd & 0x00ff0000) >> 16,
		(ipadd & 0x0000ff00) >> 8,
		(ipadd & 0x000000ff),
		ntohs(conn->cn_port));
	DBPRT(("%s: fd %d addr %s\n", __func__, fd, address))
	DIS_tcp_funcs();
	if (!addrfind(ipadd)) {
		sprintf(log_buffer, "bad connect from %s", address);
		log_err(errno, __func__, log_buffer);
		close_conn(fd);
		return;
	}
	log_buffer[0] = '\0';
	for (c = 0;; c++) {
		DIS_tcp_funcs();

		if (do_tcp(fd))
			break;
	}
	DBPRT(("%s: processed %d\n", __func__, c))
}

/**
 * @brief
 *	Kill a job.
 *
 * @param[in]	pjob - pointer to job
 * @param[in]	sig - signal number
 *
 * @return      int
 * @retval      nonzero - success, on *NIX returns number of tasks killed
 * @retval      !1 - failure
 *
 */
int
kill_job(job *pjob, int sig)
{
	pbs_task *ptask = NULL;
	int ct = 0;
	int tsk_ct;

#ifdef WIN32
	log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_DEBUG,
		  pjob->ji_qs.ji_jobid, "kill_job");

	if (pjob->ji_hJob == NULL)
		return 0;

	/* Normal process termination, top command shell termination will result in exit codes < 256.     */
	/* To differentiate a process termination by signals, add BASE_SIGEXIT_CODE to sig, and the       */
	/* value (BASE_SIGEXIT_CODE + sig) will be assigned as the exit code for that terminated process. */
	if (TerminateJobObject(pjob->ji_hJob, BASE_SIGEXIT_CODE + sig) == 0) {
		log_err(-1, __func__, "TerminateJobObject");
		return 0;
	}
	/*
	 * for any external processes that got attached to the PBS job via pbs_attach
	 * but are not part of the job object, kill the individual tasks
	 */
	for (ptask = (pbs_task *) GET_NEXT(pjob->ji_tasks);
	     ptask;
	     ptask = (pbs_task *) GET_NEXT(ptask->ti_jobtask)) {
		DBPRT(("%s: task %8.8X status %d\n", __func__,
		       ptask->ti_qs.ti_task, ptask->ti_qs.ti_status))
		if (ptask->ti_qs.ti_status != TI_STATE_RUNNING)
			continue;
		ct += kill_task(ptask, sig, 0);
	}
	return 1;
#else

	DBPRT(("%s: entered %s\n", __func__, pjob->ji_qs.ji_jobid))
	log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_DEBUG,
		  pjob->ji_qs.ji_jobid, __func__);

	for (ptask = (pbs_task *) GET_NEXT(pjob->ji_tasks);
	     ptask;
	     ptask = (pbs_task *) GET_NEXT(ptask->ti_jobtask)) {
		DBPRT(("%s: task %8.8X status %d\n", __func__,
		       ptask->ti_qs.ti_task, ptask->ti_qs.ti_status))
		if (ptask->ti_qs.ti_status != TI_STATE_RUNNING)
			continue;
		tsk_ct = kill_task(ptask, sig, 0);
		ct += tsk_ct;

#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5)
		if (sig == SIGKILL) { /* only stop afslog when the task is finally dying */
			AFSLOG_TERM(ptask);
		}
#endif

		/*
		 ** If this is an orphan task, force it to be EXITED
		 ** since it will not be seen by scan_for_terminated.
		 **
		 ** Also set the task status to EXITED if the count of
		 ** processes in this task == 0. This is to allow them
		 ** to properly transition to TI_DEAD in scan_for_exiting.
		 */
		if (((sig == SIGKILL) && (ptask->ti_flags & TI_FLAGS_ORPHAN)) || (tsk_ct == 0)) {
			ptask->ti_qs.ti_status = TI_STATE_EXITED;
			task_save(ptask);
			sprintf(log_buffer, "task %8.8X force exited",
				ptask->ti_qs.ti_task);
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB,
				  LOG_DEBUG, pjob->ji_qs.ji_jobid, log_buffer);
			/*
			 ** If it is the parent task who became orphan by
			 ** loosing the top shell, then set exiting_tasks.
			 */
			if (ptask->ti_qs.ti_parenttask == TM_NULL_TASK)
				exiting_tasks = 1;
#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5)
			AFSLOG_TERM(ptask);
#endif
		}
	}

#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5) && 0
	if (cred_by_job(pjob, CRED_DESTROY) != PBS_KRB5_OK) {
		log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB, LOG_DEBUG, pjob->ji_qs.ji_jobid,
			   "failed to destroy credentials");
	}
#endif
	DBPRT(("%s: done %s killed %d\n", __func__, pjob->ji_qs.ji_jobid, ct))
	return ct;
#endif /* WIN32 */
}

/**
 * @brief
 *	size decoding routine.
 *	Accepts a resource pointer and a pointer to the unsigned long integer
 *	to receive the decoded value.  It returns the decoded value in kb.
 *
 * @param[in] pres - pointer to resource
 *
 * @note  This will return only up to ULONG_MAX kb (i.e. return value is
 *	  unsigned long). Even though the new size data type has been expanded
 *	  to hold up to UlONG_MAX kb (i.e. unsigned long long max), this
 *	  function will still only return up to the ULONG_MAX kb. Extra care
 *	  has been taken to make sure bit shifts in this function don't go past
 *	  the # of bits of an unsigned long; otherwise, we might get some
 *	  unexpected result.
 *
 * @note  The size of a word is a constant shared between daemons to ensure
 *        consistency over correctness.
 *
 * @return	u_long
 * @retval	decoded value for size
 *
 */
u_long
getsize(resource *pres)
{
	u_Long value;
	u_long shift;

	if (pres->rs_value.at_type != ATR_TYPE_SIZE)
		return (0);
	value = pres->rs_value.at_val.at_size.atsv_num;
	shift = pres->rs_value.at_val.at_size.atsv_shift;

	if (pres->rs_value.at_val.at_size.atsv_units ==
	    ATR_SV_WORDSZ) {
		if (value > ULONG_MAX / SIZEOF_WORD)
			return (0);
		value *= SIZEOF_WORD;
	}
	if (shift > 10) {
		shift -= 10;
		if (shift >= (sizeof(ULONG_MAX) * CHAR_BIT)) {
			return (0);
		}
		return ((u_long) (value << shift));
	} else { /* in kb or < 1 kb */
		u_Long avalue;

		shift = 10 - shift;
		avalue = (value >> shift);
		/* if value is < 1kb but !0, then round up to 1kb */
		if ((value % (1 << shift)) > 0) { /* any remainder, round UP */
			avalue++;
		}
		return ((u_long) avalue);
	}
}

/**
 * @brief
 *	time decoding routine.
 *
 *	Accepts a resource pointer and a pointer to the unsigned long integer
 *	to receive the decoded value.  It returns the decoded value of time
 *	in seconds.
 *
 * @param[in] pres - pointer to resource structure
 *
 * @return	u_long
 * @retval	decoded value for time
 *
 */
u_long
gettime(resource *pres)
{

	if (pres->rs_value.at_type != ATR_TYPE_LONG)
		return (0);
	if (pres->rs_value.at_val.at_long < 0)
		return (0);
	return ((u_long) pres->rs_value.at_val.at_long);
}

/**
 * @brief
 *	Internal size decoding routine.
 *
 *	Accepts a resource pointer and a pointer to the unsigned long integer
 *	to receive the decoded value.  It returns a PBS error code, and the
 *	decoded value in the unsigned long integer.
 *
 * @param[in] pres - pointer to resource
 *
 * @note  In platforms other than sgi, the *ret value is only up to
 *	  ULONG_MAX bytes (i.e. return value is unsigned long). Even though the
 *	  new size data type has been expanded to hold up to UlONG_MAX bytes
 *	  (i.e. unsigned long long max), this function will still only return
 *	  up to the ULONG_MAX bytes. Extra care has been taken to make sure bit
 *	  shifts in this function don't go past the # of bits of an unsigned
 *	  long; otherwise, we might get unexpected result.
 *
 * @retval  ZERO(PBSE_NONE) SUCCESS
 * @retval  NON-ZERO PBS error code indicates failure
 *
 */
int
local_getsize(resource *pres, u_long *ret)
{
	u_Long value;
#define PBS_RLIM_MAX ULONG_MAX
#define PBS_RLIM_TYPE u_long

	/*
	 * If the resource pointer(pres) is NULL, then just
	 * return with error code PBSE_UNKRESC.
	 */
	if (pres == NULL)
		return (PBSE_UNKRESC);

	if (pres->rs_value.at_type != ATR_TYPE_SIZE)
		return (PBSE_ATTRTYPE);
	value = pres->rs_value.at_val.at_size.atsv_num;
	if (pres->rs_value.at_val.at_size.atsv_units == ATR_SV_WORDSZ) {
		if (value > (ULONG_MAX / SIZEOF_WORD))
			return (PBSE_BADATVAL);
		value *= SIZEOF_WORD;
	}
	if ((pres->rs_value.at_val.at_size.atsv_shift >=
	     (sizeof(PBS_RLIM_MAX) * CHAR_BIT)) ||
	    (value >
	     (PBS_RLIM_MAX >> pres->rs_value.at_val.at_size.atsv_shift))) {
		return (PBSE_BADATVAL);
	}
	*ret = (PBS_RLIM_TYPE) (value << pres->rs_value.at_val.at_size.atsv_shift);

	return (PBSE_NONE);
}

/**
 * @brief
 *	Internal time decoding routine.
 *
 *	Accepts a resource pointer and a pointer to the unsigned long integer
 *	to receive the decoded value.  It returns a PBS error code, and the
 *	decoded value of time in seconds in the unsigned long integer.
 *
 * @param[in] pres - pointer to resource
 * @param[out] ret - pointer to u_long to hold decoded value
 *
 * @return	error(numbers)
 * @retval	PBSE_NONE	no error
 * @retval	PBSE_UNKRESC	Unknown resource
 * @retval	PBSE_ATTRTYPE	incompatable queue attribute type
 * @retval	PBSE_BADATVAL	bad attribute value
 *
 *
 */
int
local_gettime(resource *pres, u_long *ret)
{
	/*
	 * If pres is NULL, then just return with PBSE_UNKRESC.
	 */
	if (pres == NULL)
		return (PBSE_UNKRESC);

	if (pres->rs_value.at_type != ATR_TYPE_LONG)
		return (PBSE_ATTRTYPE);
	if (pres->rs_value.at_val.at_long < 0)
		return (PBSE_BADATVAL);
	*ret = pres->rs_value.at_val.at_long;

	return (PBSE_NONE);
}

/**
 * @brief
 *	Internal long decoding routine.
 *
 *	Accepts a resource pointer and a pointer to the unsigned long integer
 *	to receive the decoded value.  It returns a PBS error code, and the
 *	decoded value in the unsigned long integer.
 *
 * @param[in] pres - pointer to resource
 * @param[out] ret - pointer to u_long to hold decoded value
 *
 * @return	error numbers
 *
 */
int
getlong(resource *pres, u_long *ret)
{
	return (local_gettime(pres, ret));
}

/**
 * @brief
 *	Calculate a moving weighted average percentage of cpus used by job.
 *	100% = 1 cpu full time.
 *
 * @param		pjob		pointer to job structure
 * @param		oldcput		cpu time from previous sample
 * @param		newcput		cpu time from current sample
 * @param		sampletime	time stamp of current sample
 *
 * @return		Nothing
 *
 */
void
calc_cpupercent(job *pjob, u_long oldcput, u_long newcput, time_t sampletime)
{
	attribute *at_used;
	u_long *lp;
	long ncpus_req;
	double new_sample_weight;
	u_long percent;
	resource *pres;
	resource *pres_req;
	resource *preswalltime;
	resource_def *rd;
	long dur;

	/* if job started after last sample skip calculation */
	if (pjob->ji_qs.ji_stime > sampletime)
		return;

	ncpus_req = 0;
	rd = &svr_resc_def[RESC_NCPUS];
	pres_req = find_resc_entry(get_jattr(pjob, JOB_ATR_resource), rd);
	if (pres_req != NULL)
		ncpus_req = MAX(0, pres_req->rs_value.at_val.at_long);

	rd = &svr_resc_def[RESC_CPUPERCENT];
	at_used = get_jattr(pjob, JOB_ATR_resc_used);
	pres = find_resc_entry(at_used, rd);
	if (pres == NULL)
		return;

	lp = (u_long *) &pres->rs_value.at_val.at_long;
	if (pjob->ji_sampletim == 0) {
		dur = MAX(1, sampletime - pjob->ji_qs.ji_stime);
	} else {
		dur = MAX(1, sampletime - pjob->ji_sampletim);
	}
	percent = ((newcput - oldcput) * 100) / dur;

	if ((*lp) == 0 && percent != 0 && (ncpus_req > 0))
		/* set old sample used in averaging to sane value at start */
		*lp = MIN(percent, ncpus_req * 100);

	if (percent >= (*lp)) { /* moving cpupercent up */
		new_sample_weight = delta_weightup *
				    MIN((double) 1.0, (double) dur / (double) max_check_poll);
	} else { /* moving cpupercent down */
		/*
		 * clamp sample that would push cpupercent down again
		 * so as to move back to the greater of
		 *   - average over entire run or
		 *   - ncpus*100
		 * rather than back to zero in the face of no cpu
		 * utilisation
		 *
		 * sample going down -
		 *   never allow percent to rise above (*lp)
		 */
		long wallt = -1;
		rd = &svr_resc_def[RESC_WALLTIME];
		preswalltime = find_resc_entry(at_used, rd);
		if ((preswalltime != NULL) &&
		    ((is_attr_set(&preswalltime->rs_value)) != 0)) {
			wallt = preswalltime->rs_value.at_val.at_long;
		}
		if (wallt <= 0)
			return;

		percent = MIN((double) (*lp),
			      MAX((double) percent,
				  MAX((double) ncpus_req,
				      (double) oldcput / (double) wallt) *
					  100.0));
		/* note wallt above corresponds to the old cput
		 * -- resource is only set to time_now further down
		 *    in the mom_set_use() routine
		 */

		new_sample_weight = delta_weightdown *
				    (MIN((double) 1.0, (double) dur / (double) max_check_poll));
	}

	*lp = (u_long) (percent * new_sample_weight + (*lp) * (1.0 - new_sample_weight));

	DBPRT(("cpu%% : ses %ld (new %lu - old %lu)/delta %ld = %lu%% or %ld%% weighted\n", get_jattr_long(pjob, JOB_ATR_session_id), newcput, oldcput, dur, percent, *lp))
}

#ifdef NAS /* localmod 015 */
/* functions for spool_usage limit */

/**
 * @brief
 *	sets spool size for job
 *
 * @param[in] value - spool size
 *
 * @return	handler_ret_t
 * @retval	HANDLER_FAIL	Failure
 * @retval	HANDLER_SUCCESS	Success
 *
 */

static handler_ret_t
set_spoolsize(char *value)

{
	char newstr[50] = "spool_size ";
	u_long val;
	struct size_value psize;

	psize.atsv_shift = 0;
	psize.atsv_num = 0;
	psize.atsv_units = ATR_SV_BYTESZ;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG,
		  "spool_size", value);
	if (to_size(value, &psize) != 0) {
		sprintf(log_buffer, "invalid spool size specification %s", value);
		log_err(-1, __func__, log_buffer);
		return HANDLER_FAIL;
	}

	/*
	 * Convert psize to kilobytes
	 */

	val = psize.atsv_num;

	/* Special-case 0 */

	if (val != 0) {
		if (psize.atsv_units == ATR_SV_WORDSZ)
			val *= sizeof(int);
		if (psize.atsv_shift == 0)
			val = (val + 1023) >> 10;
		else
			val = val << (psize.atsv_shift - 10);
	}

	spoolsize = val;
	(void) strncat(newstr, value, 39);
	if (add_static(newstr, "config", 0))
		return HANDLER_FAIL;
	nconfig++;
	return HANDLER_SUCCESS;
}

/**
 * @brief
 *	spool_usage - compute a job's spool usage (in KB)
 *
 * Returns the sum of the lengths of stdout and stderr in KB, iff they
 * are being written to the $PBS_HOME/spool directory. In all other cases
 * (e.g. interactive jobs, jobs running in a sandbox), returns zero.
 *
 * @param[in] pjob - pointer to job
 *
 * @return	long
 * @retval	length of output written by job into spool dir
 * @retval	0	not writing into spool dir
 *
 */

unsigned long
spool_usage(pjob)
job *pjob;

{
	unsigned long outsize = 0;
	unsigned long errsize = 0;
	char *outpath;
	char *errpath;
	struct stat buf; /* return buffer for stat(2) */
	size_t spool_path_len = strlen(path_spool);
	int keeping;

	/* Return 0 if job is interactive */

	if (is_jattr_set(pjob, JOB_ATR_interactive) && (get_jattr_long(pjob, JOB_ATR_interactive) > 0)) {
		DBPRT(("job is interactive\n"));
		return (0L);
	}

	/* Job is not interactive */

	/* Get full pathname of stdout file */

	outpath = std_file_name(pjob, StdOut, &keeping);
	DBPRT(("file = %s/n", outsize));
	if (strncmp(outpath, path_spool, spool_path_len) == 0) {

		/* stdout file resides under the spool directory (if it exists). */

		if (stat(outpath, &buf) == 0) { /* file exists */
			outsize = (unsigned long) buf.st_size;
			DBPRT(("size = %d\n", outsize));
		}
	}

	/* Get full pathname of stderr file */

	errpath = std_file_name(pjob, StdErr, &keeping);
	DBPRT(("file = %s\n", errsize));
	if (strncmp(errpath, path_spool, spool_path_len) == 0) {

		/* stderr file resides under the spool directory (if it exists). */

		if (stat(errpath, &buf) == 0) { /* file exists */
			errsize = (unsigned long) buf.st_size;
			DBPRT(("size = %d\n", outsize));
		}
	}

	return ((outsize + errsize) >> 10);
}

/**
 * @brief
 *	spool_over_limit - check job's spool usage against limit
 *
 * @param[in] pjob - pointer to job
 *
 * @return	int
 * @retval	1	iff job's spool usage exceeds spool limit in config file
 * @retval	0	otherwise.
 *
 * A spool limit <= 0 is considered to be "unlimited".
 *
 */

int
spool_over_limit(pjob)
job *pjob;
{
	if (spoolsize <= 0)
		return 0;

	return (spool_usage(pjob) > spoolsize);
}
#endif /* localmod 015 */

/**
 * @brief
 *	Measure job resource usage and compare with its limits.
 *
 *	ncpus, mem, vmem are checked against the node specific limit
 *	established by the job's select directive and passed via the
 *	exec_vnode string to job_nodes() into the ji_hosts array.
 *
 *	Job level resource limits, such as cput, walltime, ... are
 *	checked also as no single node can exceed the total
 *
 * @param[in] pjob - pointer to job
 *
 * @return Bool
 * @retval TRUE If any well-formed polled limit has been exceeded
 * @retval FALSE no polled limit has been exceeded
 *
 */
int
mom_over_limit(job *pjob)
{
	char *pname;
	int retval;
	u_long llvalue, llnum;
	u_long value, num;
	resource *pres;
	resource *used;
	attribute *uattr = get_jattr(pjob, JOB_ATR_resc_used);
	resource_def *rd;

	assert(pjob != NULL);
	assert((get_jattr(pjob, JOB_ATR_resource))->at_type == ATR_TYPE_RESC);
	pres = (resource *) GET_NEXT(get_jattr_list(pjob, JOB_ATR_resource));

	DBPRT(("%s: entered\n", __func__))

	/* check ncpus usage locally */

	value = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_ncpus;
	if (value != 0) { /* ignore cpuusage check when ncpus=0 */
		attribute *at;
		resource *prescpup;
		resource *prescput;
		resource *preswalltime;
		u_long cput_sum;
		u_long walltime_sum;

		at = get_jattr(pjob, JOB_ATR_resc_used);
		assert(at->at_type == ATR_TYPE_RESC);

		rd = &svr_resc_def[RESC_CPUPERCENT];
		prescpup = find_resc_entry(at, rd);
		if ((prescpup != NULL) &&
		    ((is_attr_set(&prescpup->rs_value)) != 0)) {
			num = prescpup->rs_value.at_val.at_long;
			if ((float) num >
			    (value * 100 * delta_cpufactor + delta_percent_over)) {
				sprintf(log_buffer,
					"ncpus %.1f exceeded limit %lu (burst)",
					(float) num / 100.0, value);
				if (cpuburst) { /* abort job */
					pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_NCPUS_BURST;
					return (TRUE);
				} else if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_cpuperc) == 0) {
					/* just log it */
					log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB,
						  LOG_INFO, pjob->ji_qs.ji_jobid,
						  log_buffer);
					pjob->ji_qs.ji_svrflags |= JOB_SVFLG_cpuperc;
				}
			}

			rd = &svr_resc_def[RESC_WALLTIME];
			preswalltime = find_resc_entry(at, rd);
			if ((preswalltime != NULL) &&
			    ((is_attr_set(&preswalltime->rs_value)) != 0)) {
				walltime_sum = preswalltime->rs_value.at_val.at_long;
				if (walltime_sum > average_trialperiod) {
					rd = &svr_resc_def[RESC_CPUT];
					prescput = find_resc_entry(at, rd);
					if ((prescput != NULL) &&
					    ((is_attr_set(&prescput->rs_value)) != 0)) {
						cput_sum = prescput->rs_value.at_val.at_long;
						/* "value" is from ncpus */
						if (((double) cput_sum / (double) walltime_sum) >
						    (value * average_cpufactor + average_percent_over / 100.0)) {
							sprintf(log_buffer,
								"ncpus %.2f exceeded limit %lu (sum)",
								(double) cput_sum / (double) walltime_sum,
								value);
							if (cpuaverage) { /* abort job */
								pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_NCPUS_SUM;
								return (TRUE);
							} else if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_cpuperc) == 0) {
								/* just log it */
								log_event(PBSEVENT_JOB,
									  PBS_EVENTCLASS_JOB, LOG_INFO,
									  pjob->ji_qs.ji_jobid,
									  log_buffer);
								pjob->ji_qs.ji_svrflags |= JOB_SVFLG_cpuperc;
							}
						}
					}
				}
			}
		}
	}

	/* check vmem useage locally */
	llvalue = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_vmem << 10;
	if (llvalue != 0) {
		rd = &svr_resc_def[RESC_VMEM];
		used = find_resc_entry(uattr, rd);
		retval = local_getsize(used, &llnum);
		if (retval == PBSE_NONE) {
			if (llnum > llvalue) {
				sprintf(log_buffer, "vmem %lukb exceeded limit %lukb", llnum / 1024, llvalue / 1024);
				pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_VMEM;
				return (TRUE);
			}
		}
	}

	/* check mem usage locally */
	llvalue = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_mem << 10;
	if (llvalue != 0) {
		rd = &svr_resc_def[RESC_MEM];
		used = find_resc_entry(uattr, rd);
		retval = local_getsize(used, &llnum);
		if (retval == PBSE_NONE) {
			if ((llnum > llvalue) && enforce_mem) {
				sprintf(log_buffer, "mem %lukb exceeded limit %lukb", llnum / 1024, llvalue / 1024);
				pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_MEM;
				return (TRUE);
			}
		}
	}

	pres = (resource *) GET_NEXT(get_jattr_list(pjob, JOB_ATR_resource));

	for (; pres != NULL; pres = (resource *) GET_NEXT(pres->rs_link)) {
		assert(pres->rs_defin != NULL);
		pname = pres->rs_defin->rs_name;
		used = find_resc_entry(uattr, pres->rs_defin);
		assert(pname != NULL);
		assert(*pname != '\0');

		/* The checks for cput and walltime (job wide limits) should
		 * only be done on the MS.  We are leaving the Cray specific
		 * mppe and mppsse to be checked on all nodes though
		 */

		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) != 0) {
			if (strcmp(pname, "cput") == 0) {
				retval = local_gettime(pres, &value);
				if (retval != PBSE_NONE)
					continue;
				retval = local_gettime(used, &num);
				if (retval != PBSE_NONE)
					continue;
				if (num > value) {
					sprintf(log_buffer,
						"cput %lu exceeded limit %lu",
						num, value);
					pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_CPUT;
					return (TRUE);
				}
			} else if (strcmp(pname, "walltime") == 0) {
				retval = local_gettime(pres, &value);
				if (retval != PBSE_NONE)
					continue;
				/* use the resources_used.walltime value */
				retval = local_gettime(used, &num);
				if (retval != PBSE_NONE)
					continue;
				/* add time that has not been accumulated */
				num += (time_now - pjob->ji_walltime_stamp) * wallfactor;
				if (num > value) {
					sprintf(log_buffer,
						"walltime %lu exceeded limit %lu",
						num, value);
					pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_KILL_WALLTIME;
					return (TRUE);
				}
			}
		}

		if (strcmp(pname, "mppe") == 0) {
			retval = getlong(pres, &value);
			if (retval != PBSE_NONE)
				continue;
			retval = getlong(used, &num);
			if (retval != PBSE_NONE)
				continue;
			if (num > value) {
				sprintf(log_buffer,
					"mppe %lu exceeded limit %lu",
					num, value);
				return (TRUE);
			}
		} else if (strcmp(pname, "mppssp") == 0) {
			retval = getlong(pres, &value);
			if (retval != PBSE_NONE)
				continue;
			retval = getlong(used, &num);
			if (retval != PBSE_NONE)
				continue;
			if (num > value) {
				sprintf(log_buffer,
					"mppssp %lu exceeded limit %lu",
					num, value);
				return (TRUE);
			}
		}
	}

	return (FALSE);
}

/**
 * @brief
 *	check attr value limits of job
 *
 * @param[in] pjob - pointer to job
 * @param[in] recover - recovering mode for MoM
 *
 * @return	int
 * @retval	0	Failure
 * @retval	1	Success
 *
 */

int
job_over_limit(job *pjob, int recover)
{
	attribute *used;
	resource *limresc;
	resource *useresc;
	struct resource_def *rd;
	u_long total_cpu, total_mem;
	u_long *total;
	int i;
	u_long limit;
	char *units;

	if (mom_over_limit(pjob)) {		     /* check my own limits */
		pjob->ji_nodekill = pjob->ji_nodeid; /* no more POLL's */
		return 1;
	}
	if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0) /* not MS */
		return 0;

	if (pjob->ji_nodekill >= pjob->ji_numnodes) {
		char *msgbuf = NULL;

		pbs_asprintf(&msgbuf,
			     "warning: job %s ji_nodekill=%d >= ji_numnodes=%d",
			     pjob->ji_qs.ji_jobid, pjob->ji_nodekill, pjob->ji_numnodes);
		log_err(-1, __func__, msgbuf);
		free(msgbuf);
	} else if (pjob->ji_nodekill != TM_ERROR_NODE) {
		hnodent *pnode = &pjob->ji_hosts[pjob->ji_nodekill];

		/* special case EOF */
		if (pnode->hn_sister == SISTER_EOF) {
			if ((reliable_job_node_find(&pjob->ji_failed_node_list, pnode->hn_host) != NULL) || (do_tolerate_node_failures(pjob)) || recover == 2) {
				snprintf(log_buffer, sizeof(log_buffer), "ignoring node EOF %d from failed mom %s as job is tolerant of node failures", pjob->ji_nodekill, pnode->hn_host ? pnode->hn_host : "");
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_JOB, LOG_DEBUG, pjob->ji_qs.ji_jobid, log_buffer);
				return 0;
			} else {
				sprintf(log_buffer, "node EOF %d (%s)",
					pjob->ji_nodekill,
					pnode->hn_host);
				log_event(PBSEVENT_JOB | PBSEVENT_FORCE,
					  PBS_EVENTCLASS_JOB, LOG_INFO,
					  pjob->ji_qs.ji_jobid, log_buffer);
				pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_RERUN_SIS_FAIL;
				(void) kill_job(pjob, SIGKILL);
				return 0;
			}
		}
		sprintf(log_buffer, "node %d (%s) requested job die, code %d",
			pjob->ji_nodekill, pnode->hn_host, pnode->hn_sister);
		return 1;
	}

#ifdef NAS /* localmod 015 */
	/* Check spool usage on the MS */
	if (spool_over_limit(pjob)) {
		sprintf(log_buffer, "spool usage job total %luKB exceeded limit %luKB",
			spool_usage(pjob), spoolsize);
		pjob->ji_nodekill = pjob->ji_nodeid;
		return 1;
	}
#endif /* localmod 015 */

	/* sum up cput and mem for all nodes */
	total_cpu = 0;
	total_mem = 0;
	for (i = 0; i < pjob->ji_numnodes - 1; i++) {
		noderes *nr = &pjob->ji_resources[i];

		total_cpu += nr->nr_cput;
		total_mem += nr->nr_mem;
	}

	used = get_jattr(pjob, JOB_ATR_resc_used);
	for (limresc = (resource *) GET_NEXT(get_jattr_list(pjob, JOB_ATR_resource));
	     limresc != NULL;
	     limresc = (resource *) GET_NEXT(limresc->rs_link)) {

		if (!is_attr_set(&limresc->rs_value))
			continue;

		rd = limresc->rs_defin;
		/* this is so we don't make extra calls to find_resc_entry */
		if (strcmp(rd->rs_name, "cput") != 0 &&
		    strcmp(rd->rs_name, "mem") != 0)
			continue;

		useresc = find_resc_entry(used, rd);
		if (useresc == NULL)
			continue;
		if (!is_attr_set(&useresc->rs_value))
			continue;

		if (strcmp(rd->rs_name, "cput") == 0) {
			total_cpu += gettime(useresc);
			limit = gettime(limresc);

			units = "secs";
			total = &total_cpu;
			if (limit < total_cpu)
				break;
		} else if (strcmp(rd->rs_name, "mem") == 0) {
			total_mem += getsize(useresc);
			limit = getsize(limresc);

			units = "kb";
			total = &total_mem;
			if (enforce_mem && (limit < total_mem))
				break;
		}
	}
	if (limresc == NULL)
		return 0;

	sprintf(log_buffer, "%s job total %lu %s exceeded limit %lu %s",
		rd->rs_name, *total, units, limit, units);
	pjob->ji_nodekill = pjob->ji_nodeid;
	return 1;
}

#ifdef NAS_UNKILL /* localmod 011 */

/**
 * @brief
 *	free_kp_list_entries(head) - delete_link() and free() entries of a linked
 *	list
 *
 * @param[in] head - pointer to pbs_list_head
 *
 * @return	Void
 *
 */
void
free_kp_list_entries(pbs_list_head *head)
{
	kp *entry;
	kp *next;

	entry = (kp *) GET_NEXT(*head);
	while (entry) {
		next = (kp *) GET_NEXT(entry->kp_link);
		delete_link(&entry->kp_link);
		free(entry);
		entry = next;
	}
	CLEAR_HEAD((*head));
}

/**
 * @brief
 *	kp_comment_node() - Set a node comment regarding the presence of unkillable
 *	processes. Based on Altair's offline_job_vnodes().
 *
 * @return	Void
 *
 */
void
kp_comment_node(void)
{
	static char id[] = "offline_node";
	static char *cmdbuf = NULL;
	static char *cmdprefix = "qmgr -c 'set node ";
	static char *cmdmidfix = "comment = \"";
	static char *cmdsuffix = "unkillable process\"'";
	char linebuf[_POSIX_ARG_MAX];
	long execmax = _POSIX_ARG_MAX;
	size_t linebufmax = sizeof(linebuf);
	time_t now;

	/*
	 ** Prepare cmdbuf with prefix up to before the node name
	 */
	if (cmdbuf == NULL) {
		cmdbuf = malloc(execmax);
		if (cmdbuf == NULL) {
			log_err(errno, id, "cmdbuf malloc");
			return;
		}
	}
	if (snprintf(cmdbuf, execmax, "%s/bin/%s",
		     pbs_conf.pbs_exec_path, cmdprefix) >= execmax) {
		log_err(-1, id, "cmdbuf overflow");
		return;
	}

	/*
	 ** Write out the rest of the command
	 */
	now = time(0);
	if (snprintf(linebuf, linebufmax, "%s %s%.24s: %s",
		     mom_short_name, cmdmidfix,
		     ctime(&now), cmdsuffix) >= linebufmax) {
		log_err(-1, id, "overflow of linebuf");
		return;
	}

	/* cmdbuf length + linebuf length + terminating character */
	if (strlen(cmdbuf) + strlen(linebuf) + 1 > execmax) {
		log_err(-1, id, "cmdbuf overflow");
		return;
	}
	(void) strcat(cmdbuf, linebuf);

	if (system(cmdbuf) == -1)
		log_err(errno, id, "attempt to set node comment failed");
}
#endif /* localmod 011 */

void
dorestrict_user(void)
{
	static char id[] = "restrict_user";
	pid_t *allpids(void);
	pid_t *pids = NULL;
	static pid_t mom_sid = -1;
	int i = 0;
	job *pjob = NULL, *hjob = NULL;
	pbs_task *ptask = NULL;
	static resource_def *prsdef = NULL;
	resource *pplace = NULL;
	int j = 0;
	int found_exempt = 0;
	struct passwd *pwent = NULL;
	static uid_t uid_dataservice = -1;
	char errmsg[PBS_MAX_DB_ERR];
	char *usr = NULL;

#ifdef NAS_UNKILL /* localmod 011 */
	pbs_list_head new_killed_procs;
	kp *current_kp, *prev_kp;

	CLEAR_HEAD(new_killed_procs);
#endif /* localmod 011 */

	if (!restrict_user)
		return;

	if (pbs_conf.start_server && uid_dataservice == -1) {
		/* setting uid_dataservice to 0 to prevent infinite logging of error message if the call to
		 *  pbs_get_dataservice_usr fails
		 */
		uid_dataservice = 0;

		/* Database user must be an exempted user
		 *
		 * errmsg is set to a default value and passed to the pbs_dataservice_usr api . It may be
		 * overwritten inside the pbs_get_dataservice_usr api by some other errorneous condition.
		 * The max possible length of the error message is set to PBS_MAX_DB_ERR
		 *
		 * On success the dataservice username is returned which should be freed by the caller to
		 * prevent a mem leak.
		 */
		if ((usr = pbs_get_dataservice_usr(errmsg, PBS_MAX_DB_ERR)) != NULL) {
			/* usr now contains the dataservice user name . Get the uid of the dataservice
			 * user name using the  getpwnam() api
			 */

			if (((pwent = getpwnam(usr)) == NULL)) {
				sprintf(log_buffer, "user %s doesn't exist", usr);
				log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, id,
					  log_buffer);
			} else {
				uid_dataservice = pwent->pw_uid;
				sprintf(log_buffer,
					"Dataservice user %s is an exmpted user ",
					usr);
				log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, id,
					  log_buffer);
			}
			free(usr);
		}
	}

	reqnum++;
	if ((pids = allpids()) == NULL)
		return;

	if (prsdef == NULL)
		prsdef = &svr_resc_def[RESC_PLACE];

#ifndef WIN32
	if (mom_sid == -1) {
		mom_sid = getsid(0);
		DBPRT(("%s: set mom sid %d\n", id, mom_sid))
	}
#endif
	for (i = 0; pids[i] != -1; i++) {
		pid_t pid = pids[i];
		int ret;
		pid_t procsid;
		uid_t uid;
		char comm[30];
#ifdef WIN32
		char *uname = NULL; /* dummy */
		ret = dep_procinfo(pid, &procsid, &uid, uname, 0, comm, sizeof(comm));
#else
		ret = dep_procinfo(pid, &procsid, &uid, comm, sizeof(comm));
#endif
		if (ret != TM_OKAY) {
			DBPRT(("%s: no info pid %d\n", id, pid))
			continue;
		}

		/*
		 ** Ignore processes within MOM's session so we do not
		 ** kill stagein procs where a job does not yet exist.
		 */
		if (procsid == mom_sid) {
			DBPRT(("%s: MOM session pid %d uid %d comm %s\n",
			       id, pid, uid, comm))
			continue;
		}

		/* Ignore system processes. */
		if (uid <= (uid_t) restrict_user_maxsys)
			continue;
		/* Ignore the postgres process */
		if (uid == uid_dataservice)
			continue;

		found_exempt = 0;
		for (j = 0; j < NUM_RESTRICT_USER_EXEMPT_UIDS; j++) {
			if (restrict_user_exempt_uids[j] == 0)
				break;

			if (uid == restrict_user_exempt_uids[j]) {
				found_exempt = 1;
				break;
			}
		}
		if (found_exempt)
			continue;

		for (pjob = (job *) GET_NEXT(svr_alljobs);
		     pjob;
		     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {
			if (pjob->ji_qs.ji_un.ji_momt.ji_exuid == uid)
				break;
		}

		if (pjob == NULL) /* no job with same uid */
			goto badguy;

		/*
		 ****************************************************
		 ** WARNING
		 ** THIS IS PROTOTYPE CODE ... DO NOT USE
		 ****************************************************
		 ** Job with same uid exists but we are not doing any
		 ** special handling of aliens so it doesn't mater if
		 ** this is part of a job or not.
		 */
		if (alien_attach == 0 && alien_kill == 0)
			continue;

		hjob = pjob; /* save matching job */

		for (pjob = (job *) GET_NEXT(svr_alljobs);
		     pjob;
		     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {
			if (pjob->ji_qs.ji_un.ji_momt.ji_exuid != uid)
				continue;
			/* job should be running */
			if (!check_job_substate(pjob, JOB_SUBSTATE_RUNNING) && !check_job_substate(pjob, JOB_SUBSTATE_PRERUN))
				continue;

			for (ptask = (pbs_task *) GET_NEXT(pjob->ji_tasks);
			     ptask != NULL;
			     ptask = (pbs_task *)
				     GET_NEXT(ptask->ti_jobtask)) {
				pid_t tasksid;

				tasksid = ptask->ti_qs.ti_sid;
				/* DEAD task */
				if (tasksid <= 1)
					continue;

				if (procsid == tasksid)
					break;
			}

			if (ptask != NULL)
				break;
		}
		/*
		 ** If pjob is not NULL, the process is part of a job.
		 ** We do not want to touch it.
		 */
		if (pjob != NULL)
			continue;

		/*
		 ** We are not going to attach the alien, here we check
		 ** to see if the job has the node "excl".  If so, we
		 ** leave it alone.  If the job is not "excl", we kill
		 ** the alien.
		 */
		if (alien_kill) {
			pplace = find_resc_entry(get_jattr(hjob, JOB_ATR_resource), prsdef);
			if (pplace && pplace->rs_value.at_val.at_str) {
				if (strstr(pplace->rs_value.at_val.at_str,
					   "excl"))
					continue;
			}
			/* fall through to "badguy" */
		}

		/*
		 ** From here on, we are looking an 'alien' process i.e.
		 ** a process that is not part of a job.
		 */
		if (alien_attach && /* attach the alien */
		    procsid > 1) {  /* only if sid is good */
			/*
			 **	Create a new task for the session.
			 */
			ptask = momtask_create(hjob);
			if (ptask == NULL) {
				sprintf(log_buffer,
					"%s: task create failed", id);
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
					  LOG_NOTICE, hjob->ji_qs.ji_jobid,
					  log_buffer);
				continue;
			}

			strcpy(ptask->ti_qs.ti_parentjobid,
			       hjob->ji_qs.ji_jobid);
			/*
			 **	The parent self virtual nodes are not known.
			 */
			ptask->ti_qs.ti_parentnode = TM_ERROR_NODE;
			ptask->ti_qs.ti_myvnode = TM_ERROR_NODE;
			ptask->ti_qs.ti_parenttask = TM_INIT_TASK;
			ptask->ti_qs.ti_sid = procsid;
			ptask->ti_qs.ti_status = TI_STATE_RUNNING;
			ptask->ti_flags |= TI_FLAGS_ORPHAN;
			(void) task_save(ptask);

			if (!check_job_substate(hjob, JOB_SUBSTATE_RUNNING)) {
				set_job_state(hjob, JOB_STATE_LTR_RUNNING);
				set_job_substate(hjob, JOB_SUBSTATE_RUNNING);
				job_save(hjob);
			}

			/*
			 ** Add to list of polled jobs if it isn't
			 ** already there.
			 */
			if (is_linked(&mom_polljobs,
				      &hjob->ji_jobque) == 0) {
				append_link(&mom_polljobs,
					    &hjob->ji_jobque, hjob);
			}
			sprintf(log_buffer,
				"%s: pid %d sid %d cmd %s attached "
				"as task %8.8X",
				id,
				pid, procsid, comm, ptask->ti_qs.ti_task);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
				  hjob->ji_qs.ji_jobid, log_buffer);

			/*
			 ** Do any dependent attach operation.
			 */
			if (dep_attach(ptask) != TM_OKAY) {
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
					  LOG_NOTICE, hjob->ji_qs.ji_jobid,
					  log_buffer);
			}
			continue;
		}

		/*
		 ** We are not going to attach the alien, here we check
		 ** to see if the job has the node "excl".  If so, we
		 ** leave it alone.  If the job is not "excl", we kill
		 ** the alien.
		 */
		if (alien_kill) {
			pplace = find_resc_entry(get_jattr(hjob, JOB_ATR_resource), prsdef);
			if (pplace && pplace->rs_value.at_val.at_str) {
				if (strstr(pplace->rs_value.at_val.at_str,
					   "excl"))
					continue;
			}
			/* fall through to "badguy" */
		}

	badguy:

#ifdef WIN32
		log_err(-1, id, "not supported function");

#else
#ifdef NAS_UNKILL /* localmod 011 */
		if ((current_kp = (kp *) malloc(sizeof(kp))) == NULL) {
			log_err(errno, id, "malloc");
			exit(1);
		}

		/*
		 ** Gather indentifying information for the process that should
		 ** be killed
		 */
		current_kp->pid = pid;
		time(&current_kp->kill_time);
		CLEAR_LINK(current_kp->kp_link);
		ret = kill_procinfo(pid, &current_kp->ppid, &current_kp->start_time);
		if (ret != TM_OKAY) {
			/*
			 ** Give up on maintaining a kill history for this
			 ** process, but still continue with kill attempts
			 */
			sprintf(log_buffer, "unable to gather additional info for pid %d(%s) to track kill history",
				pid, comm);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER,
				  LOG_DEBUG, id, log_buffer);

			free(current_kp);
			current_kp = NULL;
		} else {
			/*
			 ** Search through the list of processes we have already
			 ** tried to kill.
			 */
			for (prev_kp = (kp *) GET_NEXT(killed_procs);
			     prev_kp;
			     prev_kp = (kp *) GET_NEXT(prev_kp->kp_link)) {
				if (current_kp->pid == prev_kp->pid &&
				    current_kp->ppid == prev_kp->ppid &&
				    current_kp->start_time == prev_kp->start_time) {
					if (time(0) - prev_kp->kill_time > KP_WAIT_TIME) {
						/*
						 ** We've determined we have an
						 ** unkillable process - set a
						 ** server comment for this
						 ** node, log a message, then
						 ** set flags to have the mom
						 ** quickly exit. For good
						 ** measure we cleanup the
						 ** memory we've malloc'd
						 */
						free(current_kp);
						free_kp_list_entries(&killed_procs);
						free_kp_list_entries(&new_killed_procs);
						kp_comment_node();

						sprintf(log_buffer, "SEC_EVENT |unkillable process|host %s", mom_short_name);
						log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE, id, log_buffer);

						mom_run_state = 0;
						next_sample_time = 1;

						return;
					} else {
						/*
						 ** Again attempt to kill the
						 ** process, keeping the
						 ** previous identifying info
						 */
						free(current_kp);
						current_kp = prev_kp;
						delete_link(&prev_kp->kp_link);
					}

					break;
				}
			}
		}

		/*
		 ** Build a new list of processes we've attempted to kill
		 */
		if (current_kp != NULL)
			append_link(&new_killed_procs, &current_kp->kp_link, current_kp);
#endif		  /* localmod 011 */
		DBPRT(("%s: KILL pid %d sid %d\n", id, pid, procsid))
		if (kill(pid, SIGKILL) == 0) {
			sprintf(log_buffer, "killed uid %d pid %d(%s)",
				uid, pid, comm);
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
				  LOG_NOTICE, id, log_buffer);
		} else {
			sprintf(log_buffer,
				"failed kill uid %d pid %d(%s)",
				uid, pid, comm);
			log_err(errno, id, log_buffer);
		}
#endif
	}
#ifdef NAS_UNKILL /* localmod 011 */
	/*
	 ** Our new list is now complete and includes processes we attempted to
	 ** kill previously but which are not dead yet. Discard the old list and
	 ** replace with the new
	 */
	free_kp_list_entries(&killed_procs);
	list_move(&new_killed_procs, &killed_procs);
#endif /* localmod 011 */
}

/*
 * @brief
 *	Function called by the Libtpp layer when the network connection to
 *	the pbs_comm router is restored. This is the implementation for mom.
 *
 * @param[in] data - currently unused
 *
 * @return	Void
 *
 */
void
net_restore_handler(void *data)
{
	mom_net_up = 1;
	mom_net_up_time = time(0);
	time_delta_hellosvr(MOM_DELTA_RESET);

	log_event(PBSEVENT_ERROR | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ALERT, __func__, "net restore handler called");
}

/*
 * @brief
 *	Function called by the Libtpp layer when the network connection to
 *	the pbs_comm router is down. In this implementation for the mom,
 *	the down handler closes the server stream. It then goes over the
 *	list of jobs and closes all streams to its sisterhood.
 *
 * @param[in] data - currently unused
 *
 * @return	Void
 *
 */
void
net_down_handler(void *data)
{
	int num;
	hnodent *np;
	job *pjob;

	log_event(PBSEVENT_ERROR | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ALERT, __func__, "net down handler called");
	if (server_stream >= 0) {
		log_eventf(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE, msg_daemonname,
			   "Closing existing server stream %d", server_stream);
		dis_flush(server_stream);
		tpp_close(server_stream);
		server_stream = -1;
	}

	/* close streams to sister-hood for all jobs */
	for (pjob = (job *) GET_NEXT(svr_alljobs);
	     pjob != NULL;
	     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {

		for (num = 0, np = pjob->ji_hosts;
		     num < pjob->ji_numnodes;
		     num++, np++) {
			if (np->hn_stream >= 0) {
				dis_flush(np->hn_stream);
				tpp_close(np->hn_stream);
				np->hn_stream = -1;
			}
		}
	}
	mom_net_up = 0;
	mom_net_up_time = 0;
}

/**
 * @brief
 *      This function returns the time delta
 * 	returns short bursts followed by longer intervals.
 *
 * 	This is used for how long mom should wait before sending next hello (in secs)
 * 	Can be used for any such scenario.
 *
 * @param[in] mode -	reset mode is to bring it back to bursting mode
 *
 * @return int
 * @retval >0 : time delta
 * @retval 0 : only in case of reset mode.
 */
int
time_delta_hellosvr(int mode)
{
	static int delta = 1;
	static int cnt = 1;
	int max_delta = 1 << 6; /* max interval will be 64s */

	DBPRT(("%s: mode= %d, delta= %d, cnt= %d", __func__, mode, delta, cnt))

	if (mode == MOM_DELTA_RESET) {
		delta = 1;
		cnt = 1;
		return 0;
	}

	if (cnt == 0) {
		if (delta == max_delta)
			return delta;

		delta <<= 1;
		cnt = delta << 1;
	} else
		cnt--;

	return delta;
}

/**
 * @brief
 * 	Resume multinode job after one or more sisters has been restarted
 *
 * @param[in] pjob - job pointer
 *
 * @return	Void
 *
 */

void
resume_multinode(job *pjob)
{
	if (pjob->ji_hosts == NULL)
		return;

	int com = IM_JOIN_RECOV_JOB;
	hnodent *np = NULL;
	eventent *ep = NULL;
	int i;
	for (i = 1; i < pjob->ji_numnodes; i++) {
		np = &pjob->ji_hosts[i];

		if (i == 1)
			ep = event_alloc(pjob, com, -1, np, TM_NULL_EVENT, TM_NULL_TASK);
		else
			ep = event_dup(ep, pjob, np);

		if (ep == NULL) {
			exec_bail(pjob, JOB_EXEC_FAIL1, NULL);
			return;
		}

		int stream = np->hn_stream;
		im_compose(stream, pjob->ji_qs.ji_jobid,
			   get_jattr_str(pjob, JOB_ATR_Cookie),
			   com, ep->ee_event, TM_NULL_TASK, IM_OLD_PROTOCOL_VER);
		(void) diswsi(stream, pjob->ji_numnodes);
		(void) diswsi(stream, pjob->ji_ports[0]);
		(void) diswsi(stream, pjob->ji_ports[1]);
		dis_flush(stream);
#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5)
		send_cred_sisters(pjob);
#endif
	}
}

#ifdef WIN32
/**
 * @brief
 *	main - the main program of MOM
 */
DWORD WINAPI
	main_thread(pv) void *pv;
#else
int
main(int argc, char *argv[])
#endif
{
	/* both Win32 and Unix */
	int rc;
	char *nodename;
	struct tpp_config tpp_conf;
	int errflg, c;
	int stalone = 0;
	int i;
	char *ptr;
	char *servername;
	unsigned int serverport;
	int recover = 0;
	time_t time_state_update = 0;
	int tppfd; /* fd for rm and im comm */
	double myla;
	time_t time_next_hello = 0;
	job *nxpjob;
	job *pjob;
	extern time_t wait_time;
	time_t getkbdtime();
	void activate_jobs();
	void idle_jobs();
	char *configscriptaction = NULL;
	char *inputfile = NULL;
	char *scriptname = NULL;
	resource *prscput;
	resource *prswall;
	char *getopt_str;
	int fd;
	u_long ipaddr;
	int optindinc = 0;
	mom_hook_input_t hook_input;
	char path_hooks_rescdef[MAXPATHLEN + 1];
	int sock_bind_rm;
	int sock_bind_mom;
#ifdef WIN32
	/* Win32 only */
	struct arg_param *p = (struct arg_param *) pv;
	int argc;
	char **argv;
	SERVICE_STATUS ss;
	int pmode = S_IREAD | S_IWRITE;
	struct _timeb tval;
	char *pwst = NULL;
	char winsta_name[MAXPATHLEN + 1];
	char desktop_name[MAXPATHLEN + 1];
	HWINSTA old_winsta = NULL;
	HWINSTA pbs_winsta = NULL;
	HDESK pbs_desktop = NULL;
	char *pch = NULL;
	extern char *pbs_conf_env;
#else
	/* Unix only */
	int pmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
	struct timeval tval;
	struct sigaction act;
	gid_t mygid;
	extern char *optarg;
	extern int optind;
#endif /* WIN32 */

#ifdef _POSIX_MEMLOCK
	int do_mlockall = 0;
#endif

#ifdef WIN32
	_fcloseall(); /* Close any inherited extra files, leaving stdin-err open */
#else
	/* Close any inherited extra files, leaving stdin-err open */
	c = sysconf(_SC_OPEN_MAX);
	while (--c > 2)
		(void) close(c); /* close any file desc left open by parent */

	/* the real deal or version and exit? */
	PRINT_VERSION_AND_EXIT(argc, argv);
#endif

	/* If we are not run with real and effective uid of 0, forget it */
#ifdef WIN32
	_set_fmode(_O_BINARY);
	argc = p->argc;
	argv = p->argv;

	ZeroMemory(&ss, sizeof(ss));
	ss.dwCheckPoint = 0;
	ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
	ss.dwCurrentState = g_dwCurrentState;
	ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
	ss.dwWaitHint = 3000;
	/*If this is a multi-instance Mom, it needs the corresponding PBS_CONF_FILE environment.*/
	if ((strlen(argv[0]) != strlen("PBS_MOM")) && (pch = strstr(argv[0], "PBS_MOM"))) {
		char *pbsconf_temp = "PBS_CONF_FILE";
		pch = pch + strlen("PBS_MOM");
		if ((pbs_conf_env = (char *) malloc(strlen(pbsconf_temp) + strlen(pch) + 1)) != NULL) {
			memset(pbs_conf_env, 0, strlen(pbsconf_temp) + strlen(pch) + 1);
			pbs_strncpy(pbs_conf_env, pbsconf_temp, strlen(pbsconf_temp) + strlen(pch) + 1);
			pbs_conf_env = strcat(pbs_conf_env, pch);
		} else {
			g_dwCurrentState = SERVICE_STOPPED;
			ss.dwCurrentState = g_dwCurrentState;
			ss.dwWin32ExitCode = ERROR_BAD_CONFIGURATION;
			if (g_ssHandle != 0)
				SetServiceStatus(g_ssHandle, &ss);
			return (1);
		}
	}
#endif

	/* set single threaded mode */
	pbs_client_thread_set_single_threaded_mode();
	/* disable attribute verification */
	set_no_attribute_verification();

#ifdef WIN32

	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);
	/* load the pbs conf file */
	if (pbs_loadconf(0) == 0) {
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_BAD_CONFIGURATION;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
		return (1);
	}

	set_log_conf(pbs_conf.pbs_leaf_name, pbs_conf.pbs_mom_node_name,
		     pbs_conf.locallog, pbs_conf.syslogfac,
		     pbs_conf.syslogsvr, pbs_conf.pbs_log_highres_timestamp);

	if (!isAdminPrivilege(getlogin())) {
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = CO_E_LAUNCH_PERMSSION_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
		fprintf(stderr, "%s: Must be run as root\n", argv[0]);
		return (1);
	}

	if (!has_privilege(SE_DEBUG_NAME))
		ena_privilege(SE_DEBUG_NAME);
#else
#ifndef DEBUG
	if ((getuid() != 0) || (geteuid() != 0)) {
		fprintf(stderr, "%s: Must be run as root\n", argv[0]);
		return (1);
	}
#endif
#endif /* WIN32 */

	/* initialize the thread context */
	if (pbs_client_thread_init_thread_context() != 0) {
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_OUTOFMEMORY;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#else
		fprintf(stderr, "%s: Unable to initialize thread context\n",
			argv[0]);
		return (1);
#endif /* WIN32 */
	}

	if (set_msgdaemonname("pbs_mom")) {
		fprintf(stderr, "Out of memory\n");
		return 1;
	}
#ifndef WIN32
	if (pbs_loadconf(0) == 0) {
		return (1);
	}
	set_log_conf(pbs_conf.pbs_leaf_name, pbs_conf.pbs_mom_node_name,
		     pbs_conf.locallog, pbs_conf.syslogfac,
		     pbs_conf.syslogsvr, pbs_conf.pbs_log_highres_timestamp);
#endif
	pbsgroup = getgid();

	/* Get our default service port */

	pbs_mom_port = pbs_conf.mom_service_port;
	default_server_port = pbs_conf.batch_service_port;
	pbs_rm_port = pbs_conf.manager_service_port;

	/* Is an alternate Mom Home path specified in pbs.conf ? */

	if (pbs_conf.pbs_mom_home) {
		if (pbs_conf.pbs_home_path != NULL)
			free(pbs_conf.pbs_home_path);
#ifdef WIN32
		pbs_conf.pbs_home_path =
			shorten_and_cleanup_path(pbs_conf.pbs_mom_home);
		if (pbs_conf.pbs_environment) {
			free(pbs_conf.pbs_environment);
			if ((pbs_conf.pbs_environment =
				     malloc(strlen(pbs_conf.pbs_home_path) + 17))) {
				sprintf(pbs_conf.pbs_environment, "%s/pbs_environment",
					pbs_conf.pbs_home_path);
				fix_path(pbs_conf.pbs_environment, 1);
			}
		}
#else
		if ((pbs_conf.pbs_home_path = strdup(pbs_conf.pbs_mom_home)) == NULL) {
			fprintf(stderr, "Unable to allocate Memory!\n");
			return (1);
		}
#endif
	}

	/*
	 * Set the default tmp directory, which may get overridden by $tmpdir in
	 * mom_priv/config later on.
	 */
	if (set_tmpdir(pbs_conf.pbs_tmpdir) != HANDLER_SUCCESS) {
		fprintf(stderr, "%s: Unable to configure temporary directory.\n", argv[0]);
		return (1);
	}

	errflg = 0;
	getopt_str = "d:c:M:mNS:R:lL:a:xC:prs:n:Q:-:";
	while ((c = getopt(argc, argv, getopt_str)) != -1) {
		switch (c) {
			case 'N': /* stand alone (win), no fork (others) */
				stalone = 1;
				break;
			case 'm':
#ifdef WIN32
				fprintf(stderr, "-m option not supported for Windows\n");
				g_dwCurrentState = SERVICE_STOPPED;
				ss.dwCurrentState = g_dwCurrentState;
				ss.dwWin32ExitCode = ERROR_INVALID_PARAMETER;
				if (g_ssHandle != 0)
					SetServiceStatus(g_ssHandle, &ss);
				return 1;
#endif
				mock_run = 1;
				break;
			case 'd': /* directory */
				if (pbs_conf.pbs_home_path != NULL)
					free(pbs_conf.pbs_home_path);
				pbs_conf.pbs_home_path = optarg;
				break;
			case 'c': /* config file */
				config_file_specified = 1;
				pbs_strncpy(config_file, optarg, sizeof(config_file)); /* remember name */
				break;
			case 'M':
				pbs_mom_port = (unsigned int) atoi(optarg);
				if (pbs_mom_port == 0) {
					fprintf(stderr, "Bad MOM port value %s\n",
						optarg);
#ifdef WIN32
					g_dwCurrentState = SERVICE_STOPPED;
					ss.dwCurrentState = g_dwCurrentState;
					ss.dwWin32ExitCode = ERROR_INVALID_PARAMETER;
					if (g_ssHandle != 0)
						SetServiceStatus(g_ssHandle, &ss);
#endif
					return (1);
				}
				break;
			case 'S':
				default_server_port = (unsigned int) atoi(optarg);
				if (default_server_port == 0) {
					fprintf(stderr, "Bad Server port value %s\n",
						optarg);
#ifdef WIN32
					g_dwCurrentState = SERVICE_STOPPED;
					ss.dwCurrentState = g_dwCurrentState;
					ss.dwWin32ExitCode = ERROR_INVALID_PARAMETER;
					if (g_ssHandle != 0)
						SetServiceStatus(g_ssHandle, &ss);
#endif
					return (1);
				}
				break;
			case 'R':
				pbs_rm_port = (unsigned int) atoi(optarg);
				if (pbs_rm_port == 0) {
					fprintf(stderr, "Bad RM port value %s\n",
						optarg);
#ifdef WIN32
					g_dwCurrentState = SERVICE_STOPPED;
					ss.dwCurrentState = g_dwCurrentState;
					ss.dwWin32ExitCode = ERROR_INVALID_PARAMETER;
					if (g_ssHandle != 0)
						SetServiceStatus(g_ssHandle, &ss);
#endif
					return (1);
				}
				break;
			case 'l':
#ifdef _POSIX_MEMLOCK
				do_mlockall = 1;
#else
				fprintf(stderr, "-l option - mlockall not supported\n");
#endif /* _POSIX_MEMLOCK */
				break;
			case 'L':
				log_file = optarg;
				break;
			case 'a':
				alarm_time = (int) strtol(optarg, &ptr, 10);
				if (alarm_time <= 0 || *ptr != '\0') {
					fprintf(stderr,
						"%s: bad alarm time\n", optarg);
					errflg = 1;
				}
				break;
			case 'x':
				port_care = 0;
				break;
			case 'C':
				path_checkpoint_from_getopt = optarg;
				break;
			case 'p':
				if (recover == 0)
					recover = 2;
				else
					errflg = 1;
				break;
			case 'r':
				if (recover == 0)
					recover = 1;
				else
					errflg = 1;
				break;
			case 's':
				configscriptaction = optarg;
				if (strcmp(optarg, "insert") == 0) {
					if (optind == argc - 2) {
						scriptname = argv[optind];
						inputfile = argv[optind + 1];
						optindinc += 2;
					} else
						errflg = 1;
				} else if ((strcmp(optarg, "remove") == 0) ||
					   (strcmp(optarg, "show") == 0)) {
					if (optind == argc - 1) {
						scriptname = argv[optind];
						optindinc++;
					} else
						errflg = 1;
				} else if (strcmp(optarg, "list") != 0)
					errflg = 1;
				break;
			case 'n':
				nice_val = (int) strtol(optarg, &ptr, 10);
				if ((nice_val < PRIO_MIN) ||
				    (nice_val > PRIO_MAX) ||
				    (*ptr != '\0')) {
					fprintf(stderr,
						"%s: bad nice value\n", optarg);
					errflg = 1;
				}
				break;
			case 'Q':
				fprintf(stderr, "Warning, this option is for QA testing only,  it should never be used by a production site\n");
				QA_testing = atol(optarg);
				break;
			case '?':
			default:
				errflg = 1;
		}
	}
	optind += optindinc;

	if (errflg || optind != argc) {
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_INVALID_PARAMETER;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
		usage2(argv[0]); /* exits */
		return (1);
#else
		usage(argv[0]); /* exits */
#endif /* WIN32 */
	}

	umask(022);

#ifdef WIN32
	save_env();
#endif
	/*
	 * The following is code to reduce security risks
	 * start out with standard umask, system resource limit infinite
	 */
	if ((num_var_env = setup_env(pbs_conf.pbs_environment)) == -1) {
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_INVALID_ENVIRONMENT;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
		return (1);
#else
		exit(1);
#endif /* WIN32 */
	}

#ifndef WIN32 /* ---- UNIX ------------------------------------------*/
	mygid = getgid();
	(void) setgroups(1, &mygid); /* secure suppl. groups */

#if defined(RLIM64_INFINITY)
	{
		struct rlimit64 rlimit;
		int curerror;

		rlimit.rlim_cur = RLIM64_INFINITY;
		rlimit.rlim_max = RLIM64_INFINITY;

		(void) setrlimit64(RLIMIT_CPU, &rlimit);
		(void) setrlimit64(RLIMIT_FSIZE, &rlimit);
		(void) setrlimit64(RLIMIT_DATA, &rlimit);
		if (getrlimit64(RLIMIT_STACK, &orig_stack_size) != -1) {
			if ((orig_stack_size.rlim_cur != RLIM64_INFINITY) && (orig_stack_size.rlim_cur < MIN_STACK_LIMIT)) {
				rlimit.rlim_cur = MIN_STACK_LIMIT;
				rlimit.rlim_max = MIN_STACK_LIMIT;
				if (setrlimit64(RLIMIT_STACK, &rlimit) == -1) {
					char msgbuf[] = "Stack limit setting failed";
					curerror = errno;
					log_err(curerror, __func__, msgbuf);
					sprintf(log_buffer, "%s errno=%d", msgbuf, curerror);
					log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER, LOG_ERR, (char *) __func__, log_buffer);
					exit(1);
				}
			}
		} else {
			char msgbuf[] = "Getting current Stack limit failed";

			curerror = errno;
			log_err(curerror, __func__, msgbuf);
			snprintf(log_buffer, sizeof(log_buffer), "%s errno=%d", msgbuf, curerror);
			log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER, LOG_ERR, (char *) __func__, log_buffer);
			exit(1);
		}

		rlimit.rlim_cur = RLIM64_INFINITY;
		rlimit.rlim_max = RLIM64_INFINITY;

#ifdef RLIMIT_NPROC
		(void) getrlimit64(RLIMIT_NPROC, &orig_nproc_limit); /* get for later */
		if (setrlimit64(RLIMIT_NPROC, &rlimit) == -1) {	     /* set unlimited */
			char msgbuf[] = "setrlimit NPROC setting failed";
			curerror = errno;
			log_err(curerror, __func__, msgbuf);
		}
#endif /* RLIMIT_NPROC */
#ifdef RLIMIT_RSS
		(void) setrlimit64(RLIMIT_RSS, &rlimit);
#endif /* RLIMIT_RSS */
#ifdef RLIMIT_VMEM
		(void) setrlimit64(RLIMIT_VMEM, &rlimit);
#endif /* RLIMIT_VMEM */
#ifdef RLIMIT_MEMLOCK
		(void) setrlimit64(RLIMIT_MEMLOCK, &rlimit);
#endif /* RLIMIT_MEMLOCK */
	}

#else /* set rlimit 32 bit */
	{
		struct rlimit rlimit;
		int curerror;

		rlimit.rlim_cur = RLIM_INFINITY;
		rlimit.rlim_max = RLIM_INFINITY;
		(void) setrlimit(RLIMIT_CPU, &rlimit);
#ifdef RLIMIT_NPROC
		(void) getrlimit(RLIMIT_NPROC, &orig_nproc_limit); /* get for later */
		if (setrlimit(RLIMIT_NPROC, &rlimit) == -1) {	   /* set unlimited */
			char msgbuf[] = "setrlimit NPROC setting failed";
			curerror = errno;
			log_err(curerror, __func__, msgbuf);
		}
#endif /* RLIMIT_NPROC */
#ifdef RLIMIT_RSS
		(void) setrlimit(RLIMIT_RSS, &rlimit);
#endif /* RLIMIT_RSS */
#ifdef RLIMIT_VMEM
		(void) setrlimit(RLIMIT_VMEM, &rlimit);
#endif /* RLIMIT_VMEM */
#ifdef RLIMIT_MEMLOCK
		(void) setrlimit(RLIMIT_MEMLOCK, &rlimit);
#endif /* RLIMIT_MEMLOCK */
#ifndef linux
		(void) setrlimit(RLIMIT_FSIZE, &rlimit);
		(void) setrlimit(RLIMIT_DATA, &rlimit);
		(void) getrlimit(RLIMIT_STACK, &orig_stack_size); /* get for later */
		(void) setrlimit(RLIMIT_STACK, &rlimit);
#else
		if (getrlimit(RLIMIT_STACK, &orig_stack_size) != -1) {
			if ((orig_stack_size.rlim_cur != RLIM_INFINITY) && (orig_stack_size.rlim_cur < MIN_STACK_LIMIT)) {
				rlimit.rlim_cur = MIN_STACK_LIMIT;
				rlimit.rlim_max = MIN_STACK_LIMIT;
				if (setrlimit(RLIMIT_STACK, &rlimit) == -1) {
					char msgbuf[] = "Stack limit setting failed";
					curerror = errno;
					log_err(curerror, __func__, msgbuf);
					sprintf(log_buffer, "%s errno=%d", msgbuf, curerror);
					log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER, LOG_ERR, (char *) __func__, log_buffer);
					exit(1);
				}
			}
		} else {
			char msgbuf[] = "Getting current Stack limit failed";
			curerror = errno;
			log_err(curerror, __func__, msgbuf);
			sprintf(log_buffer, "%s errno=%d", msgbuf, curerror);
			log_record(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER, LOG_ERR, (char *) __func__, log_buffer);
			exit(1);
		}
#endif /* not linux */
	}
#endif /* !RLIM64_INFINITY */

#endif /* !WIN32 */

	if ((job_attr_idx = cr_attrdef_idx(job_attr_def, JOB_ATR_LAST)) == NULL) {
		log_err(errno, __func__, "Failed creating job attribute search index");
		return (-1);
	}
	if (cr_rescdef_idx(svr_resc_def, svr_resc_size) != 0) {
		log_err(errno, __func__, "Failed creating resc definition search index");
		return (-1);
	}

	/* initialize pointers in resource_def array */
	for (i = 0; i < (svr_resc_size - 1); ++i)
		svr_resc_def[i].rs_next = &svr_resc_def[i + 1];
	/* last entry is left with null pointer */

	/* set up and validate home paths    */

	c = 0;
	mom_home = mk_dirs("mom_priv");
	path_jobs = mk_dirs("mom_priv/jobs/");
	path_hooks = mk_dirs("mom_priv/hooks/");
	path_hooks_workdir = mk_dirs("mom_priv/hooks/tmp/");
#ifdef WIN32
	path_epilog = mk_dirs("mom_priv/epilogue.bat");
	path_prolog = mk_dirs("mom_priv/prologue.bat");
#else
	path_epilog = mk_dirs("mom_priv/epilogue");
	path_prolog = mk_dirs("mom_priv/prologue");
#endif
	path_log = mk_dirs("mom_logs");
	path_spool = mk_dirs("spool/");
	path_undeliv = mk_dirs("undelivered/");
	path_addconfigs = mk_dirs("mom_priv/config.d");

	/* open log file while std in,out,err still open, forces to fd 4 */
#ifdef WIN32
	/* Don't worry about return value of log_open() like */
	/* in the server code.  A -1 is returned in log_open() for all */
	/* failure cases so it's hard to know if a corrupt log file is the */
	/* culprit, which happes occasionally under windows. */

	/*
	 * let SCM wait 10 seconds for log_open() to complete
	 * as it does network interface query which can take time
	 */

	ss.dwCheckPoint++;
	ss.dwWaitHint = 60000;
	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);
	log_open(log_file, path_log);

	/* moved the 2 functions here as they are now using the log functions,
	and these functions can be used after calling log_open() */

	/* let SCM wait 20 seconds for secure_misc_files() to complete */
	ss.dwCheckPoint++;
	ss.dwWaitHint = 20000;
	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);

	secure_misc_files();

	/* let SCM wait 30 seconds for secure_mom_files() to complete */
	ss.dwCheckPoint++;
	ss.dwWaitHint = 30000;
	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);

	secure_mom_files();

#else
	if ((c = log_open(log_file, path_log)) != 0) { /* use given name */
		fprintf(stderr, "pbs_mom: Unable to open logfile\n");
		return (1);
	}
#endif /* WIN32 */

	if (QA_testing != 0) {
		sprintf(log_buffer, "Warning QA_testing option set to %lu",
			QA_testing);
		log_event(PBSEVENT_ADMIN | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_WARNING, msg_daemonname, log_buffer);
	}

	if (configscriptaction != NULL) {
		/* must precede chdir(mom_home) */
		do_configs(configscriptaction, scriptname, inputfile);
		/* NOTREACHED */
	}

	/* change working directory to mom home (mom_priv) */

	if (chdir(mom_home) == -1) {
		perror("pbs_mom unable to change working directory to mom home");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_DIRECTORY;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (1);
	}

#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
#ifdef WIN32
	/* For windows, don't check full path. Let system put in default */
	/* permissions for top-level directories */
	c |= chk_file_sec(path_jobs, 1, 0, WRITES_MASK ^ FILE_WRITE_EA, 0);
	c |= chk_file_sec(path_hooks, 1, 0, WRITES_MASK ^ FILE_WRITE_EA, 0);
	c |= chk_file_sec(path_hooks_workdir, 1, 0, WRITES_MASK ^ FILE_WRITE_EA, 0);
	c |= chk_file_sec(path_spool, 1, 1, 0, 0);
	c |= chk_file_sec(pbs_conf.pbs_environment, 0, 0, WRITES_MASK ^ FILE_WRITE_EA, 0);
#else
	c |= chk_file_sec(path_jobs, 1, 0, S_IWGRP | S_IWOTH, 1);
	c |= chk_file_sec(path_hooks, 1, 0, S_IWGRP | S_IWOTH, 1);
	c |= chk_file_sec(path_hooks_workdir, 1, 0, S_IWGRP | S_IWOTH, 1);
	c |= chk_file_sec(path_spool, 1, 1, 0, 0);
	c |= chk_file_sec(pbs_conf.pbs_environment, 0, 0, S_IWGRP | S_IWOTH, 0);
#endif /* WIN32 */
	if (c) {
		sprintf(log_buffer,
			"Warning: one of chk_file_sec failed: %s, %s, %s, %s, %s",
			path_jobs, path_spool, pbs_conf.pbs_environment,
			path_hooks, path_hooks_workdir);
		log_err(0, "mom_main", log_buffer);

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (3);
	}

#endif /* not DEBUG and not NO_SECURITY_CHECK */

	if (initsocketlib())
		return 1;

#ifdef WIN32
	/* Under WIN32, create structure that will be used to track child processes. */
	if (initpids() == 0) {
		log_err(-1, "pbsd_init", "Creating pid handles table failed!");
		return (-1);
	}

	/* Let's do an extra validity check */

	if (check_executor() == 1) { /* failed on check for root */
		log_err(-1, msg_daemonname, winlog_buffer);
		return (3);
	}
	if (strlen(winlog_buffer) > 0) {

		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER,
			  LOG_WARNING, msg_daemonname, winlog_buffer);
	}
#endif

	/*
	 * Set mom_host to gethostname(), if gethostname() fail, then use PBS_MOM_NODE_NAME
	 * if it is defined and complies to RFC 952/1123
	 */
	c = gethostname(mom_host, (sizeof(mom_host) - 1));
	if (c != 0) {
		/*
		 * backup plan
		 * use PBS_MOM_NODE_NAME as hostname if it is defined and complies to RFC 952/1123
		 */
		c = 0;
		if (pbs_conf.pbs_mom_node_name) {
			pbs_strncpy(mom_host, pbs_conf.pbs_mom_node_name, sizeof(mom_host));
			ptr = mom_host;
			/* First character must be alpha-numeric */
			if (isalnum((int) *ptr)) {
				/* Subsequent characters may also be dots or dashes */
				for (ptr++; (c == 0) && (*ptr != '\0'); ptr++) {
					if (*ptr == '.') {
						/* Disallow two dots in a row or a trailing dot */
						if (*(ptr + 1) == '.' || *(ptr + 1) == '\0')
							c = -1;
					} else if ((*ptr != '-') && !isalnum((int) *ptr)) {
						c = -1;
					}
				}
			} else {
				c = -1;
			}
		} else {
			c = -1;
		}
		if (c != 0) {
			log_err(-1, msg_daemonname, "Unable to obtain my host name");
			return (-1);
		}
	}

	/*
	 * Set mom_short_name to PBS_MOM_NODE_NAME if it is defined.
	 * Otherwise, set mom_short_name to the return value of
	 * gethostname(), truncated to first dot.
	 */
	if (pbs_conf.pbs_mom_node_name)
		/* mom_short_name was specified explicitly using PBS_MOM_NODE_NAME */
		pbs_strncpy(mom_short_name, pbs_conf.pbs_mom_node_name, sizeof(mom_short_name));
	else {
		/* use gethostname(), truncated to first dot */
		pbs_strncpy(mom_short_name, mom_host, sizeof(mom_short_name));
		if ((ptr = strchr(mom_short_name, (int) '.')) != NULL)
			*ptr = '\0'; /* terminate at first dot */
	}

	/*
	 * Now get mom_host, which determines resources_available.host
	 * and also the interface used to register to pbs_comm if
	 * PBS_LEAF_NAME is unset. Note that mom_host will be overridden
	 * by PBS_LEAF_NAME later on if TPP is enabled (search
	 * pbs_conf.pbs_leaf_name to find code section).
	 */
	c = get_fullhostname(mom_host, mom_host, (sizeof(mom_host) - 1));
	if (c == -1) {
		log_err(-1, msg_daemonname, "Unable to resolve my host name");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_INVALID_COMPUTERNAME;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (-1);
	}

	lockfds = open("mom.lock", O_CREAT | O_WRONLY, pmode);
	if (lockfds < 0) {
		(void) strcpy(log_buffer, "Unable to open lock file");
		log_err(-1, msg_daemonname, log_buffer);
		(void) strcat(log_buffer, "\n");
		(void) fprintf(stderr, "%s", log_buffer);
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_LOCK_FAILED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (1);
	}

#ifdef WIN32
	secure_file("mom.lock", "Administrators",
		    READS_MASK | WRITES_MASK | STANDARD_RIGHTS_REQUIRED);
#endif
	if (lock_file(lockfds, F_WRLCK, "mom.lock", 1, NULL, 0)) { /* See if other MOMs are running */
		log_errf(errno, msg_daemonname, "pbs_mom: another mom running");
		fprintf(stderr, "%s\n", "pbs_mom: another mom running");
		exit(1);
	}

	if (read_config(NULL)) {
		fprintf(stderr, "%s: config file(s) parsing failed\n", argv[0]);
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_FILE_INVALID;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (1);
	}
	if (pbs_rm_port != (pbs_mom_port + 1)) {
		fprintf(stderr, "Mom RM port must be one greater than the Mom Service port\n");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_FILE_INVALID;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (1);
	}

#if MOM_ALPS /* ALPS needs libjob support */
	/*
	 * This needs to be called after the config file is read and before MOM
	 * forks so the exit value can be seen if there is a bad flag combination.
	 */
	ck_acct_facility_present();
#endif /* MOM_ALPS */

	/* initialize the network interface */

	if ((sock_bind_mom = init_network(pbs_mom_port)) < 0) {
#ifdef WIN32
		errno = WSAGetLastError();
#endif
		c = errno;
		(void) sprintf(log_buffer,
			       "server port = %u, errno = %d",
			       pbs_mom_port, c);
#ifdef WIN32
		if (c == WSAEADDRINUSE)
#else
		if (c == EADDRINUSE)
#endif
			(void) strcat(log_buffer, ", already in use");
		log_err(-1, msg_daemonname, log_buffer);
		(void) strcat(log_buffer, "\n");
		(void) fprintf(stderr, "%s", log_buffer);
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_NETWORK_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (3);
	}

	if ((sock_bind_rm = init_network(pbs_rm_port)) < 0) {

#ifdef WIN32
		errno = WSAGetLastError();
#endif
		c = errno;
		(void) sprintf(log_buffer,
			       "resource (tcp) port = %u, errno = %d",
			       pbs_rm_port, c);
#ifdef WIN32
		if (c == WSAEADDRINUSE)
#else
		if (c == EADDRINUSE)
#endif
			(void) strcat(log_buffer, ", already in use");
		log_err(-1, msg_daemonname, log_buffer);
		(void) strcat(log_buffer, "\n");
		(void) fprintf(stderr, "%s", log_buffer);

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_NETWORK_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (3);
	}

	/*Initialize security library's internal data structures*/
	if (load_auths(AUTH_SERVER)) {
		log_err(-1, __func__, "Failed to load auth lib");
		exit(3);
	}

	{
		int csret;

		/* allow Libsec to log errors if part of PBS daemon code */
		p_cslog = log_err;

		if ((csret = CS_server_init()) != CS_SUCCESS) {
			sprintf(log_buffer,
				"Problem initializing security library (%d)", csret);
			log_err(-1, __func__, log_buffer);
			exit(3);
		}
	}

#ifndef DEBUG
	if (stalone != 1) {
		/* go into the background and become own session/process group */
		if (lock_file(lockfds, F_UNLCK, "mom.lock", 1, NULL, 0)) { /* unlock so child can relock */
			log_errf(errno, msg_daemonname, "failed to unlock mom.lock file");
			fprintf(stderr, "%s\n", "failed to unlock mom.lock file");
			exit(1);
		}

		if (fork() > 0)
			return (0); /* parent goes away */

		if ((setsid() == -1) && (errno != ENOSYS)) {
			log_err(errno, msg_daemonname, "setsid failed");
			return (2);
		}
		if (lock_file(lockfds, F_WRLCK, "mom.lock", 1, NULL, 0)) { /* lock out other MOMs */
			log_errf(errno, msg_daemonname, "pbs_mom: another mom running");
			fprintf(stderr, "%s\n", "pbs_mom: another mom running");
			exit(1);
		}
	}
	if (freopen(NULL_DEVICE, "r", stdin) == NULL) 
		log_errf(-1, __func__, "freopen failed. ERR : %s", strerror(errno));
	if (freopen(NULL_DEVICE, "w", stdout) == NULL) 
		log_errf(-1, __func__, "freopen failed. ERR : %s", strerror(errno));	
	if (freopen(NULL_DEVICE, "w", stderr) == NULL) 
		log_errf(-1, __func__, "freopen failed. ERR : %s", strerror(errno));		
#else  /* DEBUG */
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
	if (stalone != 1) {
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO,
			   __func__, "Debug build does not fork.");
	}
#endif /* DEBUG */

	mom_pid = getpid();
	sprintf(log_buffer, "%d\n", mom_pid);
	if (ftruncate(lockfds, 0) == -1) 
		log_errf(-1, __func__, "ftruncate failed. ERR : %s", strerror(errno));		
	if (write(lockfds, log_buffer, strlen(log_buffer)) == -1) 
		log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));		

#ifndef WIN32 /* ------------------------------------------------------------*/

	daemon_protect(0, PBS_DAEMON_PROTECT_ON);
#ifdef _POSIX_MEMLOCK
	if (do_mlockall == 1) {
		if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
			log_err(errno, __func__, "mlockall failed");
		}
	}
#endif /* _POSIX_MEMLOCK */

	sigemptyset(&allsigs);
	sigaddset(&allsigs, SIGHUP);  /* remember to block these */
	sigaddset(&allsigs, SIGINT);  /* during critical sections */
	sigaddset(&allsigs, SIGTERM); /* so we don't get confused */
	sigaddset(&allsigs, SIGCHLD);
	sigaddset(&allsigs, SIGUSR1);
	sigaddset(&allsigs, SIGUSR2);
	sigaddset(&allsigs, SIGPIPE);
#ifdef SIGINFO
	sigaddset(&allsigs, SIGINFO);
#endif

	act.sa_flags = 0;
	act.sa_mask = allsigs;

	/*
	 **	We want to abort system calls
	 **	and call a function.
	 */
#ifdef SA_INTERRUPT
	act.sa_flags |= SA_INTERRUPT; /* don't restart system calls */
#endif
	act.sa_handler = catch_USR2;
	sigaction(SIGUSR2, &act, NULL);

	act.sa_handler = catch_child; /* set up to catch Death of Child */
	sigaction(SIGCHLD, &act, NULL);
	act.sa_handler = catch_hup; /* do a restart on SIGHUP */
	sigaction(SIGHUP, &act, NULL);

	act.sa_handler = toolong; /* handle an alarm call */
	sigaction(SIGALRM, &act, NULL);

	act.sa_handler = stop_me; /* shutdown for these */
	sigaction(SIGINT, &act, NULL);
	sigaction(SIGTERM, &act, NULL);
#ifdef SIGXCPU
	sigaction(SIGXCPU, &act, NULL);
#endif
#ifdef SIGXFSZ
	sigaction(SIGXFSZ, &act, NULL);
#endif
#ifdef SIGCPULIM
	sigaction(SIGCPULIM, &act, NULL);
#endif
#ifdef SIGSHUTDN
	sigaction(SIGSHUTDN, &act, NULL);
#endif
	/*
	 **	These signals will be sent to 'stop_me' which will just
	 **	return so they will be ignored.  This is so any process
	 **	that is exec'ed will not have SIG_IGN set for anything.
	 */
	sigaction(SIGPIPE, &act, NULL);
	sigaction(SIGUSR1, &act, NULL);
#ifdef SIGINFO
	sigaction(SIGINFO, &act, NULL);
#endif
#endif /* ! WIN32 end -------------------------------------------------------*/

	/* initialize variables */

	if ((jobs_idx = pbs_idx_create(0, 0)) == NULL) {
		log_err(-1, __func__, "Creating jobs index failed!");
		fprintf(stderr, "Creating jobs index failed!\n");
		return (-1);
	}

	CLEAR_HEAD(mom_pending_ruu);

	CLEAR_HEAD(svr_newjobs);
	CLEAR_HEAD(svr_alljobs);
	CLEAR_HEAD(mom_polljobs);
	CLEAR_HEAD(svr_requests);
	CLEAR_HEAD(mom_deadjobs);

#ifdef NAS_UNKILL /* localmod 011 */
	CLEAR_HEAD(killed_procs);
#endif /* localmod 011 */

	CLEAR_HEAD(svr_allhooks);
	CLEAR_HEAD(svr_queuejob_hooks);
	CLEAR_HEAD(svr_postqueuejob_hooks);
	CLEAR_HEAD(svr_modifyjob_hooks);
	CLEAR_HEAD(svr_resvsub_hooks);
	CLEAR_HEAD(svr_modifyresv_hooks);
	CLEAR_HEAD(svr_movejob_hooks);
	CLEAR_HEAD(svr_runjob_hooks);
	CLEAR_HEAD(svr_jobobit_hooks);
	CLEAR_HEAD(svr_management_hooks);
	CLEAR_HEAD(svr_modifyvnode_hooks);
	CLEAR_HEAD(svr_periodic_hooks);
	CLEAR_HEAD(svr_provision_hooks);
	CLEAR_HEAD(svr_resv_confirm_hooks);
	CLEAR_HEAD(svr_resv_begin_hooks);
	CLEAR_HEAD(svr_resv_end_hooks);
	CLEAR_HEAD(svr_hook_job_actions);
	CLEAR_HEAD(svr_hook_vnl_actions);

	CLEAR_HEAD(svr_execjob_begin_hooks);
	CLEAR_HEAD(svr_execjob_prologue_hooks);
	CLEAR_HEAD(svr_execjob_epilogue_hooks);
	CLEAR_HEAD(svr_execjob_preterm_hooks);
	CLEAR_HEAD(svr_execjob_launch_hooks);
	CLEAR_HEAD(svr_execjob_end_hooks);
	CLEAR_HEAD(svr_exechost_periodic_hooks);
	CLEAR_HEAD(svr_exechost_startup_hooks);
	CLEAR_HEAD(svr_execjob_attach_hooks);
	CLEAR_HEAD(svr_execjob_resize_hooks);
	CLEAR_HEAD(svr_execjob_abort_hooks);
	CLEAR_HEAD(svr_execjob_postsuspend_hooks);
	CLEAR_HEAD(svr_execjob_preresume_hooks);

	CLEAR_HEAD(task_list_immed);
	CLEAR_HEAD(task_list_timed);
	CLEAR_HEAD(task_list_event);
	CLEAR_HEAD(task_list_interleave);

#if defined(PBS_SECURITY) && (PBS_SECURITY == KRB5)
	CLEAR_HEAD(svr_allcreds);
#endif

#ifdef WIN32
	CLEAR_HEAD(mom_copyreqs_list);

	_ftime_s(&tval);
	time_now = tval.time;
	srand(tval.millitm);
#else
	maxtm = time(0);
#endif

#ifndef WIN32
	/* block signals while we do things */
	if (sigprocmask(SIG_BLOCK, &allsigs, NULL) == -1)
		log_err(errno, __func__, "sigprocmask(BLOCK)");

	gettimeofday(&tval, NULL);
	time_now = tval.tv_sec;

	srandom(tval.tv_usec);
#endif /* !WIN32 */

	ret_size = 4096;
	if ((ret_string = malloc(ret_size)) == NULL) {
		perror("malloc");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_OUTOFMEMORY;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (1);
	}

	tpp_fd = -1;
	if (init_network_add(sock_bind_mom, NULL, process_request) != 0) {

		c = SOCK_ERRNO;
		(void) sprintf(log_buffer,
			       "server port = %u, errno = %d",
			       pbs_mom_port, c);

		if (c == EADDRINUSE)
			(void) strcat(log_buffer, ", already in use");
		log_err(-1, msg_daemonname, log_buffer);
		(void) strcat(log_buffer, "\n");
#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_NETWORK_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif
		return (3);
	}

	if (init_network_add(sock_bind_rm, NULL, tcp_request) != 0) {

		c = SOCK_ERRNO;
		(void) sprintf(log_buffer,
			       "resource (tcp) port = %u, errno = %d",
			       pbs_rm_port, c);

		if (c == EADDRINUSE)
			(void) strcat(log_buffer, ", already in use");
		log_err(-1, msg_daemonname, log_buffer);
		(void) strcat(log_buffer, "\n");

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_NETWORK_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (3);
	}

	sprintf(log_buffer, "Out of memory");
	if (pbs_conf.pbs_leaf_name) {
		char *p;
		nodename = strdup(pbs_conf.pbs_leaf_name);

		/* reset pbs_leaf_name to only the first leaf name with port */
		p = strchr(pbs_conf.pbs_leaf_name, ','); /* keep only the first leaf name */
		if (p)
			*p = '\0';
		p = strchr(pbs_conf.pbs_leaf_name, ':'); /* cut out the port */
		if (p)
			*p = '\0';
	} else {
		nodename = get_all_ips(mom_host, log_buffer, sizeof(log_buffer) - 1);
	}
	if (!nodename) {
		log_err(-1, __func__, log_buffer);
		fprintf(stderr, "%s\n", "Unable to determine TPP node name");
		return (1);
	}

	servername = get_servername(&serverport);
	localaddr = addclient_byname(LOCALHOST_SHORTNAME);
	(void) addclient_byname(mom_host);
	if (gethostname(ret_string, ret_size) == 0)
		(void) addclient_byname(ret_string);
	(void) addclient_byname(servername);
	if (pbs_conf.pbs_secondary) {
		servername = parse_servername(pbs_conf.pbs_secondary, &serverport);
		(void) addclient_byname(servername);
	}

	/* locate cput resource definition, needed for checking chkpt time */
	rdcput = &svr_resc_def[RESC_CPUT];
	rdwall = &svr_resc_def[RESC_WALLTIME];
	/* locate the checkpoint path */
	path_checkpoint_from_getenv = getenv("PBS_CHECKPOINT_PATH");
	path_checkpoint_default = mk_dirs("checkpoint/");
#define MAX_CHECKPOINT_DIR_RETRIES 5
	for (i = 0; i < MAX_CHECKPOINT_DIR_RETRIES; i++) {
		errno = 0;
		if ((c = set_checkpoint_path(path_checkpoint)) == 1)
			break;
		if (errno == ENOENT) {
			/* Skip the sleep() if this is the last try. */
			if (i >= (MAX_CHECKPOINT_DIR_RETRIES - 1))
				break;
			(void) sprintf(log_buffer, "%s %s %s %s",
				       "PBS checkpoint directory",
				       path_checkpoint ? path_checkpoint : "NULL",
				       "does not exist or is not NFS mounted.",
#ifdef WIN32
				       "Retrying in 2 secs."
#else
				       "Retrying in 1 minute."
#endif
			);
			log_err(errno, msg_daemonname, log_buffer);

#ifdef WIN32
			sleep(2);
#else
			sleep(60);
#endif
			continue;
		}
		break;
	}
	if (c == 0) {
		(void) sprintf(log_buffer, "%s %s %s %d %s",
			       "Error configuring PBS checkpoint directory",
			       path_checkpoint, "; Giving up after", i, "attempts.");
		log_err(errno, msg_daemonname, log_buffer);

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_FILE_CORRUPT;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (3);
	}

#ifndef WIN32
	if (!mock_run)
		mom_nice();
#endif
	/*
	 * Recover the hooks.
	 *
	 */
	if (chdir(path_hooks) != 0) {
		(void) snprintf(log_buffer, sizeof(log_buffer),
				msg_init_chdir, path_hooks);
		log_err(errno, __func__, log_buffer);
		return (-1);
	}
	hook_suf_len = strlen(hook_suffix);

	dir = opendir(".");
	if (dir == NULL) {
		log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER,
			  LOG_DEBUG, msg_daemonname,
			  "Could not open hooks dir");
	} else {
		/* Now, for each hook found ... */
		while (errno = 0, (pdirent = readdir(dir)) != NULL) {
			/* recover the hooks */
			baselen = strlen(pdirent->d_name) - hook_suf_len;
			if (baselen < 0)
				continue;
			psuffix = pdirent->d_name + baselen;
			if (strcmp(psuffix, hook_suffix)) {
				continue;
			}

			if ((phook = hook_recov(pdirent->d_name, NULL, hook_msg,
						sizeof(hook_msg), python_script_alloc,
						python_script_free)) == NULL) {
				sprintf(log_buffer,
					"hook_recov(%s): can't recover - %s",
					pdirent->d_name, hook_msg);
				log_event(PBSEVENT_SYSTEM,
					  PBS_EVENTCLASS_SERVER, LOG_NOTICE,
					  msg_daemonname, log_buffer);
			} else {
				sprintf(log_buffer, "Found hook %s type=%s",
					phook->hook_name,
					((phook->type == HOOK_SITE) ? "site" : "pbs"));
				log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN |
						  PBSEVENT_DEBUG,
					  PBS_EVENTCLASS_SERVER,
					  LOG_INFO, msg_daemonname, log_buffer);

				if (update_joinjob_alarm_time &&
				    (phook->enabled == TRUE) &&
				    ((phook->event & HOOK_EVENT_EXECJOB_BEGIN) != 0)) {
					if (joinjob_alarm_time == -1)
						joinjob_alarm_time = 0;
					joinjob_alarm_time += phook->alarm;
				}
				if (update_job_launch_delay &&
				    (phook->enabled == TRUE) &&
				    ((phook->event & HOOK_EVENT_EXECJOB_PROLOGUE) != 0)) {
					if (job_launch_delay == -1)
						job_launch_delay = 0;
					job_launch_delay += phook->alarm;
				}
			}
		}
		if (errno != 0 && errno != ENOENT)
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_SERVER,
				  LOG_DEBUG, msg_daemonname,
				  "Could not read hooks dir");

		(void) closedir(dir);
	}

	snprintf(path_hooks_rescdef, MAXPATHLEN, "%s%s", path_hooks,
		 PBS_RESCDEF);
	hooks_rescdef_checksum = crc_file(path_hooks_rescdef);

	snprintf(log_buffer, sizeof(log_buffer),
		 "hooks_rescdef_checksum(%s)=%lu",
		 path_hooks_rescdef, hooks_rescdef_checksum);
	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO,
		  PBS_RESCDEF, log_buffer);
	path_rescdef = (char *) path_hooks_rescdef;

	/* Need to go back to mom's working directory since when recovering */
	/* hooks, we temporarily chdir-ed to the hooks directory. */
	if (chdir(mom_home) == -1) {
		log_err(errno, __func__, "pbs_mom unable to change working directory to mom home");
		return (-1);
	}

	print_hooks(0);
	print_hooks(HOOK_EVENT_EXECJOB_BEGIN);
	print_hooks(HOOK_EVENT_EXECJOB_PROLOGUE);
	print_hooks(HOOK_EVENT_EXECJOB_LAUNCH);
	print_hooks(HOOK_EVENT_EXECJOB_EPILOGUE);
	print_hooks(HOOK_EVENT_EXECJOB_PRETERM);
	print_hooks(HOOK_EVENT_EXECJOB_END);
	print_hooks(HOOK_EVENT_EXECHOST_PERIODIC);
	print_hooks(HOOK_EVENT_EXECHOST_STARTUP);
	print_hooks(HOOK_EVENT_EXECJOB_ATTACH);
	print_hooks(HOOK_EVENT_EXECJOB_RESIZE);
	print_hooks(HOOK_EVENT_EXECJOB_ABORT);
	print_hooks(HOOK_EVENT_EXECJOB_POSTSUSPEND);
	print_hooks(HOOK_EVENT_EXECJOB_PRERESUME);

	/* cleanup the hooks work directory */
	cleanup_hooks_workdir(0);
	cleanup_hooks_in_path_spool(0);

#ifdef PYTHON
	set_py_progname();
	Py_NoSiteFlag = 1;
	Py_FrozenFlag = 1;
	Py_OptimizeFlag = 2;
	Py_IgnoreEnvironmentFlag = 1;
	Py_InitializeEx(0);
#endif

#ifndef WIN32
	initialize(); /* init RM code */
#endif

	rc = set_tpp_config(&pbs_conf, &tpp_conf, nodename, pbs_rm_port, pbs_conf.pbs_leaf_routers);
	free(nodename);

	if (rc == -1) {
		(void) sprintf(log_buffer, "Error setting TPP config");
		log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, msg_daemonname, log_buffer);
		fprintf(stderr, "%s", log_buffer);
		return (3);
	}

	tpp_set_app_net_handler(net_down_handler, net_restore_handler);

	if ((tppfd = tpp_init(&tpp_conf)) == -1) {
		(void) sprintf(log_buffer, "tpp_init failed");
		log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, msg_daemonname, log_buffer);
		fprintf(stderr, "%s", log_buffer);
		return (3);
	}
	(void) add_conn(tppfd, TppComm, (pbs_net_t) 0, 0, NULL, tpp_request);

	/* initialize machine dependent polling routines */
	if ((c = mom_open_poll()) != PBSE_NONE) {
		log_err(c, msg_daemonname, "pre_poll failed");

#ifdef WIN32
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_NETWORK_ACCESS_DENIED;
		if (g_ssHandle != 0)
			SetServiceStatus(g_ssHandle, &ss);
#endif /* WIN32 */
		return (3);
	}

	/* recover & abort Jobs which were under MOM's control */
// clang-format off
#ifdef	WIN32
	old_winsta = GetProcessWindowStation();

	strcpy(winsta_name, PBS_DESKTOP_NAME);
	strcpy(desktop_name, PBS_DESKTOP_NAME);

	if ((pwst = strrchr(winsta_name, '\\'))) {
		*pwst = '\0';
		strcpy(desktop_name, pwst+1);
	}
	/*
	 * Only members of the Administrators group are allowed
	 * to specify a name of windows station. If lpwinsta is
	 * NULL or an empty string the system forms a window station
	 * name using the logon session identifier for the calling process.
	 */

	pbs_winsta = CreateWindowStation(winsta_name, 0,
		WINSTA_ALL_ACCESS, NULL);

	if (pbs_winsta == NULL) {
		(void)sprintf(log_buffer, "CreateWindowStation failed! error=%d",
			GetLastError());
		log_err(errno, msg_daemonname, log_buffer);
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_INVALID_HANDLE;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

		return (3);
	}

	sprintf(log_buffer, "Created window station=%s", winsta_name);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_NOTICE, __func__, log_buffer);

	SetProcessWindowStation(pbs_winsta);

	pbs_desktop = CreateDesktop(desktop_name, NULL, NULL,
		DF_ALLOWOTHERACCOUNTHOOK,
		READ_CONTROL|
		WRITE_DAC|
		DESKTOP_READOBJECTS|
		DESKTOP_CREATEWINDOW|
		DESKTOP_CREATEMENU|
		DESKTOP_HOOKCONTROL|
		DESKTOP_JOURNALRECORD|
		DESKTOP_JOURNALPLAYBACK|
		DESKTOP_ENUMERATE|
		DESKTOP_WRITEOBJECTS|
		DESKTOP_SWITCHDESKTOP, NULL);

	if (pbs_desktop == NULL) {
		(void)sprintf(log_buffer, "CreateDesktop failed! error=%d",
			GetLastError());
		log_err(errno, msg_daemonname, log_buffer);
		g_dwCurrentState = SERVICE_STOPPED;
		ss.dwCurrentState = g_dwCurrentState;
		ss.dwWin32ExitCode = ERROR_INVALID_HANDLE;
		if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);

		return (3);
	}
	sprintf(log_buffer, "Created desktop %s in window station=%s",
		desktop_name, winsta_name);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_NOTICE, __func__, log_buffer);

	SetProcessWindowStation(old_winsta);

#endif	/* WIN32 */

	// clang-format on

	/* recover vnode to host map from file in case Server is not yet up */
	if ((c = recover_vmap()) != 0) {
		log_err(c, msg_daemonname, "unable to recover vnode to host mapping");
	}

	pbs_list_link multinode_jobs;

	/* recover & abort Jobs which were under MOM's control */
	init_abort_jobs(recover, &multinode_jobs);

	/* deploy periodic hooks */
	mom_hook_input_init(&hook_input);
	hook_input.vnl = (vnl_t *) vnlp;
	hook_input.jobs_list = &svr_alljobs;

	(void) mom_process_hooks(HOOK_EVENT_EXECHOST_PERIODIC,
				 PBS_MOM_SERVICE_NAME, mom_host, &hook_input,
				 NULL, NULL, 0, 0);

	/* record the fact that we are up and running */
	(void) sprintf(log_buffer, msg_startup1, PBS_VERSION, recover);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN | PBSEVENT_FORCE,
		  LOG_NOTICE, PBS_EVENTCLASS_SERVER, msg_daemonname, log_buffer);
	(void) sprintf(log_buffer,
		       "Mom pid = %d ready, using ports Server:%d MOM:%d RM:%d",
		       mom_pid, default_server_port, pbs_mom_port, pbs_rm_port);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
		  LOG_NOTICE, msg_daemonname, log_buffer);

	/* tell server we have restarted */
#ifdef WIN32
	ss.dwCheckPoint = 0;
	g_dwCurrentState = SERVICE_RUNNING;
	ss.dwCurrentState = g_dwCurrentState;
	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);

	/* put here to minimize chance of hanging up or delaying mom startup */
	initialize();
#endif /* WIN32 */

#ifdef PMIX
	pbs_pmix_server_init(msg_daemonname);
#endif

	/*
	 * Now at last, we are ready to do some work, the following section
	 * constitutes the "main" loop of MOM
	 */
	for (; mom_run_state; finish_loop(wait_time)) {

#ifndef WIN32
		if (call_hup != HUP_CLEAR) {
			process_hup();
			internal_state_update = UPDATE_MOM_STATE;
		}
#endif

		time_now = time(NULL);
		if (server_stream == -1) {
			if (time_now > time_next_hello) {
				send_hellosvr(server_stream);
				time_next_hello = time_now + time_delta_hellosvr(MOM_DELTA_NORMAL);
				if (server_stream != -1) {
					job *m_job;
					for (m_job = (job *) GET_NEXT(multinode_jobs); m_job;
					     m_job = (job *) GET_NEXT(m_job->ji_multinodejobs)) {
						if (m_job->ji_qs.ji_svrflags & JOB_SVFLG_HERE) {
							/* I am MS */
							resume_multinode(m_job);
						} else {
							/* I am sister */
							send_sisters(m_job, IM_RECONNECT_TO_MS, NULL);
						}
					}
					CLEAR_HEAD(multinode_jobs);
				}
			}
		} else
			send_pending_updates();

		wait_time = default_next_task();
#ifdef WIN32
		end_proc();
#endif

		dorestrict_user();

		/* check on User Activity */
		if (server_stream != -1) {
			if (idle_check > 0) {
				time_t lastkey, idletime;

				/*
				 * cycle stealing is turned on, monitor
				 * keyboard/mouse activity
				 *
				 * This impacts the "wait_time" above,  it must be
				 * about the same time of the next activity check
				 */
				lastkey = getkbdtime();
				if (lastkey > time_now)
					idletime = 0;
				else
					idletime = time_now - lastkey;

				if (internal_state & MOM_STATE_BUSYKB) {
					/* currently busy keyboard */
					if (idletime >= idle_avail) {
						/* no longer busy */
						internal_state &= ~MOM_STATE_BUSYKB;
						internal_state_update = UPDATE_MOM_STATE;
						went_busy = 0;
						wait_time = idle_poll;
						activate_jobs();
					}
				} else if (internal_state & MOM_STATE_INBYKB) {
					if (lastkey > (went_busy + idle_busy)) {
						internal_state &= ~MOM_STATE_INBYKB;
						internal_state |= MOM_STATE_BUSYKB;
						wait_time = 10;
					} else if (idletime > idle_busy) {
						/* can resume jobs */
						internal_state &= ~MOM_STATE_INBYKB;
						internal_state_update = UPDATE_MOM_STATE;
						went_busy = 0;
						wait_time = idle_poll;
						activate_jobs();
					}
					prior_key = lastkey;
				} else {
					/* not currently busy */
					if ((idletime < idle_avail) &&
					    (went_busy == 0) &&
					    (lastkey != prior_key)) {
						went_busy = lastkey;
						prior_key = lastkey;
						internal_state |= MOM_STATE_INBYKB;
						internal_state_update = UPDATE_MOM_STATE;
						wait_time = (idle_busy + 1) >> 1;
						idle_jobs();
						/* suspend jobs */
					}
				}
			} else if (idle_check == -1) {
				/*
				 * need to activate jobs that may have been idled before
				 * restart or SIGHUP
				 */
				activate_jobs();
				if (update_state_flag) {
					internal_state &= ~(MOM_STATE_INBYKB | MOM_STATE_BUSYKB | INUSE_BUSY);
					internal_state_update = UPDATE_MOM_STATE;
				}
				idle_check = 0;
			}
		}

		/*
		 * Is it time to update internal state?
		 * This is done at a more leisurely pace
		 */
		if (time_now > time_state_update) {
			time_state_update = time_now + STATE_UPDATE_TIME;

			/*
			 * If required, update node state info to Server
			 * check if loadave means we should be "busy"
			 */
			if (max_load_val > 0.0) {
				(void) get_la(&myla);
				/* check if need to update busy state */
				check_busy(myla);
			}
		}

		/*
		 * if needed, update server with my state change can be changed in
		 * check_busy() or query_adp()
		 */
		if (internal_state_update) {
			state_to_server(UPDATE_VNODES, 0);

			(void) send_hook_vnl(vnlp_from_hook);
			/*
			 * send_hook_vnl() saves 'vnlp_from_hook' internally, to be freed
			 * later when server acks the request.
			 */
			vnlp_from_hook = NULL;
		}

		/*
		 * Have we any jobs that can be purged now?
		 * They would be added to this list if the purge is ready to
		 * be done but the code is in the middle of a loop where
		 * purging the job would mess up the linked list over which
		 * the loop is running, or for some other reason it is not
		 * desirable to actually purge the job then.  For example,
		 * see chk_del_job() in mom_comm.c
		 * If any are found, then call dorestrict_user()
		 */
		i = 0;
		while ((pjob = (job *) GET_NEXT(mom_deadjobs)) != NULL) {
			/* sometimes this purge is happening earlier than
			* IS_DISCARD_JOB, which then does not get the pjob
			* pointer to call kill_job().
			*
			* Fixed by adding a kill_job here, which should be
			* no harm anyway.
			*/
			(void) kill_job(pjob, SIGKILL);
			job_purge_mom(pjob);
			++i;
		}
		if (i > 0)
			dorestrict_user();

		/*
		 * Have we any external (script) actions running for jobs
		 * that have gone longer than their alarm cut off time?
		 * If so, call the post action routine with error of -1
		 *
		 * Also, if this platform supports checkpoint/restart we
		 * want to minimize the wait time for qhold by using the
		 * minimum update time if a checkpoint is active.
		 */
		for (pjob = (job *) GET_NEXT(svr_alljobs);
		     pjob;
		     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {
			if ((pjob->ji_momsubt != 0) &&
			    (pjob->ji_actalarm != 0) &&
			    (pjob->ji_actalarm < time_now)) {
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB,
					  LOG_INFO, pjob->ji_qs.ji_jobid,
					  "Action alarm time exceeded");
				kill(pjob->ji_momsubt, SIGKILL);
				pjob->ji_momsubt = 0;
				pjob->ji_mompost(pjob, -1);
				pjob->ji_mompost = NULL;
			}

			if (do_tolerate_node_failures(pjob) &&
			    (check_job_substate(pjob, JOB_SUBSTATE_WAITING_JOIN_JOB)) &&
			    (pjob->ji_joinalarm != 0) &&
			    (pjob->ji_joinalarm < time_now)) {
				int rcode;

				snprintf(log_buffer, sizeof(log_buffer), "sister_join_job_alarm wait time %ld secs exceeded", joinjob_alarm_time);
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB,
					  LOG_INFO, pjob->ji_qs.ji_jobid, log_buffer);
				set_job_substate(pjob, JOB_SUBSTATE_PRERUN);

				rcode = pre_finish_exec(pjob, 1);
				if (rcode == PRE_FINISH_SUCCESS)
					finish_exec(pjob);
				else if (rcode != PRE_FINISH_SUCCESS_JOB_SETUP_SEND)
					exec_bail(pjob, JOB_EXEC_RETRY, "pre_finish_exec failure");
				pjob->ji_joinalarm = 0;
			}

			if (pjob->ji_flags & MOM_CHKPT_ACTIVE)
				next_sample_time = min_check_poll;
		}

		/*
		 * Is it time to update resources used for jobs?
		 * This is done fairly slowly.  This rate applies to
		 * everything from here on in the main loop.
		 */

		if (time_now < (time_resc_updated + next_sample_time))
			continue;

		/*
		 * time to next resources check is set to MIN when new job run
		 * increments upwards until MAX time
		 */

		if (server_stream == -1)
			next_sample_time = max_check_poll;
		else if ((next_sample_time += inc_check_poll) > max_check_poll)
			next_sample_time = max_check_poll;
		DBPRT(("next_sample_time = %d\n", next_sample_time))

		/* are there any jobs? No - then don't bother with Resources */

		if ((pjob = (job *) GET_NEXT(svr_alljobs)) == NULL)
			continue;

		/* there are jobs so update status	 */
		/* if we just got a sample, don't bother */
		if (time_now > time_last_sample) {
			if (mom_get_sample() != PBSE_NONE)
				continue;
		}

		time_resc_updated = time_now;
		for (pjob = (job *) GET_NEXT(svr_alljobs);
		     pjob != NULL;
		     pjob = nxpjob) {

			/* next job pointer in case job is purged */
			nxpjob = (job *) GET_NEXT(pjob->ji_alljobs);

			/* check for job stuck waiting for Svr to ack obit */
			if (!pjob->ji_hook_running_bg_on && check_job_substate(pjob, JOB_SUBSTATE_OBIT) &&
			    pjob->ji_sampletim < time_now - 45) {
				send_obit(pjob, 0); /* resend obit */
			}
			/* check for job stuck waiting for sister to deljob */
			if ((check_job_substate(pjob, JOB_SUBSTATE_DELJOB)) &&
			    (pjob->ji_sampletim < (time_now - 2 * MAX_CHECK_POLL_TIME))) {
				/* just delete the job and let server deal */
				if (pjob->ji_preq) {
					req_reject(PBSE_SISCOMM, 0, pjob->ji_preq);
					pjob->ji_preq = NULL;
				}
				job_purge_mom(pjob);
				dorestrict_user();
				continue;
			}

			c = pjob->ji_qs.ji_svrflags;
			if (c & (JOB_SVFLG_OVERLMT1 | JOB_SVFLG_OVERLMT2 |
				 JOB_SVFLG_TERMJOB)) {
				/* job is over a limit, if it is not already  */
				/* being terminated by action script, kill it */
				if ((!check_job_substate(pjob, JOB_SUBSTATE_TERM)) && (time_now >= pjob->ji_overlmt_timestamp)) {
					/* Unset the TERMJOB flag for KILL signal */
					pjob->ji_qs.ji_svrflags &= ~JOB_SVFLG_TERMJOB;
					(void) kill_job(pjob, SIGKILL);
					continue;
				}
			}

			if (!check_job_substate(pjob, JOB_SUBSTATE_RUNNING))
				continue;

			/* update information for my tasks */
			(void) mom_set_use(pjob);

			/* see if need to check point any job */
			if (pjob->ji_chkpttype == PBS_CHECKPOINT_CPUT) {
				/* checkpoint on cputime used */
				prscput = find_resc_entry(
					get_jattr(pjob, JOB_ATR_resc_used),
					rdcput);
				if (pjob->ji_chkptnext > prscput->rs_value.at_val.at_long)
					continue;

				pjob->ji_chkptnext = prscput->rs_value.at_val.at_long + pjob->ji_chkpttime;

			} else if (pjob->ji_chkpttype == PBS_CHECKPOINT_WALLT) {
				/*  checkpoint on walltime */
				prswall = find_resc_entry(
					get_jattr(pjob, JOB_ATR_resc_used),
					rdwall);
				if (pjob->ji_chkptnext > prswall->rs_value.at_val.at_long)
					continue;

				pjob->ji_chkptnext = prswall->rs_value.at_val.at_long + pjob->ji_chkpttime;

			} else {
				continue; /* no checkpoint to do */
			}
			/* now do the actual checkpoint */
			if ((c = start_checkpoint(pjob, 0, 0)) == PBSE_NONE)
				continue;
			if (c == PBSE_NOSUP)
				continue;

			/* getting here means something bad happened */
			(void) sprintf(log_buffer,
				       "Checkpoint failed, error %d", c);
			(void) message_job(pjob, StdErr, log_buffer);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_NOTICE,
				  pjob->ji_qs.ji_jobid, log_buffer);
		}

		/* dont try to send update to sevrer or send polls to sisters
		 * since server did not connect yet, and since that has not happened
		 * it means we do not have the IM_CLUSTERS information with us
		 */
		if (server_stream >= 0) {
			/* send updated resource usage info to server */
			update_jobs_status();
		}
#ifdef NAS /* localmod 153 */
		int rc_qflag = access(quiesce_mom_flag_file, F_OK);

		if (rc_qflag != 0 && mom_should_quiesce != 0) {
			log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, __func__, "mom is no longer quiesced");
			mom_should_quiesce = 0;
		} else if (rc_qflag == 0 && mom_should_quiesce == 0) {
			log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, __func__, "mom will now quiesce");
			mom_should_quiesce = 1;
		}
#endif /* localmod 153 */

		if (mom_recvd_ip_cluster_addrs) {
			int num;
			hnodent *np;

			/* check on over limit condition for polled jobs */
			for (pjob = (job *) GET_NEXT(mom_polljobs); pjob;
			     pjob = (job *) GET_NEXT(pjob->ji_jobque)) {
#ifdef NAS /* localmod 153 */
				if (mom_should_quiesce) {
					break;
				}
#endif /* localmod 153 */
				if (!check_job_substate(pjob, JOB_SUBSTATE_RUNNING))
					continue;
				/*
				 ** Send message to get info from other MOM's
				 ** if I am Mother Superior for the job and
				 ** it is not being killed.
				 */
				if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) &&
				    (pjob->ji_nodekill == TM_ERROR_NODE)) {
					int err_flag = 0;
					/*
					 ** If can't send poll to everybody, the
					 ** time has come to die.
					 */
					if (send_sisters(pjob, IM_POLL_JOB, NULL) !=
					    pjob->ji_numnodes - 1) {

						for (num = 0, np = pjob->ji_hosts; num < pjob->ji_numnodes; num++, np++) {
							if (reliable_job_node_find(&pjob->ji_failed_node_list, np->hn_host) != NULL) {
								sprintf(log_buffer, "ignoring lost communication with %s for reliable job startup", np->hn_host);
								log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_JOB, LOG_DEBUG, pjob->ji_qs.ji_jobid, log_buffer);
								err_flag = 1;
							} else if ((time_now - np->hn_eof_ts) <= max_poll_downtime_val) {
								pjob->ji_nodekill = TM_ERROR_NODE; /* send poll failed, but dont kill job */
								sprintf(log_buffer, "lost communication with %s, not killing job yet", np->hn_host);
								log_joberr(-1, __func__, log_buffer, pjob->ji_qs.ji_jobid);
								err_flag = 1;
							}
						}
						if (err_flag == 0) {
							log_event(PBSEVENT_JOB | PBSEVENT_FORCE,
								  PBS_EVENTCLASS_JOB, LOG_INFO,
								  pjob->ji_qs.ji_jobid,
								  "send POLL failed");
							if (!is_comm_up(COMM_MATURITY_TIME)) {
								pjob->ji_nodekill = TM_ERROR_NODE; /* send poll failed, but dont kill job */
								sprintf(log_buffer, "Connection to pbs_comm down/recently established, not killing job");
								log_joberr(-1, __func__, log_buffer, pjob->ji_qs.ji_jobid);
							}
						}
					}
				}

				log_buffer[0] = '\0';
				c = pjob->ji_qs.ji_svrflags;

				/*
				 * Do not update job's overlimit timestamp
				 * when following flags are set
				 */
				if (c & (JOB_SVFLG_OVERLMT1 | JOB_SVFLG_OVERLMT2 | JOB_SVFLG_TERMJOB))
					continue;

				if (job_over_limit(pjob, recover)) {

					char *kill_msg;
					log_event(PBSEVENT_JOB | PBSEVENT_FORCE,
						  PBS_EVENTCLASS_JOB, LOG_INFO,
						  pjob->ji_qs.ji_jobid, log_buffer);

					kill_msg = malloc(80 + strlen(log_buffer));
					if (kill_msg != NULL) {
						sprintf(kill_msg, "=>> PBS: job killed: %s\n", log_buffer);
						if (c & JOB_SVFLG_HERE) {
							message_job(pjob, StdErr, kill_msg);
						} else {
							/* Multi-mom scenario - adding a connection to demux for reporting error */

							struct sockaddr_in *ap;
							/* We always have a stream open to MS at node 0 */
							i = pjob->ji_hosts[0].hn_stream;
							if ((ap = tpp_getaddr(i)) == NULL) {
								log_joberr(-1, "over_limit_message",
									   "cannot write to job stderr because there is no stream to MS",
									   pjob->ji_qs.ji_jobid);
							} else {
								ipaddr = ap->sin_addr.s_addr;
								if ((fd = open_demux(ipaddr, pjob->ji_stderr)) == -1) {
									(void) sprintf(log_buffer,
										       "over_limit_message: cannot write to job stderr because open_demux failed");
									log_event(PBSEVENT_JOB | PBSEVENT_FORCE,
										  PBS_EVENTCLASS_JOB, LOG_INFO,
										  pjob->ji_qs.ji_jobid, log_buffer);
								} else {
									if (write(fd, get_jattr_str(pjob, JOB_ATR_Cookie),
									      strlen(get_jattr_str(pjob, JOB_ATR_Cookie))) == -1) 
											log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));					
									if (write(fd, kill_msg, strlen(kill_msg)) == -1) 
										log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));
									(void) close(fd);
								}
							}
						}
						free(kill_msg);
					}

					(void) terminate_job(pjob, 1);
				}
			} /* for pjob in mom_polljobs */
		}	  /* for pjob in svr_alljobs */
	}		  /* Mom main loop */

	/* if kill_jobs_on_exit set, kill any running/suspended jobs */

	if (kill_jobs_on_exit) {
		pjob = (job *) GET_NEXT(svr_alljobs);
		while (pjob) {
			if (check_job_substate(pjob, JOB_SUBSTATE_RUNNING) || check_job_substate(pjob, JOB_SUBSTATE_SUSPEND) || check_job_substate(pjob, JOB_SUBSTATE_SCHSUSP))
				(void) kill_job(pjob, SIGKILL);
			else
				term_job(pjob);

			pjob = (job *) GET_NEXT(pjob->ji_alljobs);
		}
	}

#ifndef WIN32
	if (termin_child)
		scan_for_terminated();
#endif

	if (exiting_tasks)
		scan_for_exiting();
	(void) mom_close_poll();
	send_pending_updates();

	net_close(-1); /* close all network connections */
	tpp_shutdown();

	/* Have we any jobs that can be purged before we go away? */

	while ((pjob = (job *) GET_NEXT(mom_deadjobs)) != NULL)
		job_purge_mom(pjob);

	{
		int csret;
		if ((csret = CS_close_app()) != CS_SUCCESS) {
			/*had some problem closing the security library*/

			sprintf(log_buffer, "problem closing security library (%d)", csret);
			log_err(-1, __func__, log_buffer);
		}
	}

	cleanup();

#ifdef PMIX
	PMIx_server_finalize();
#endif

	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
		  LOG_NOTICE, msg_daemonname, "Is down");
	pbs_idx_destroy(jobs_idx);
	unload_auths();
	if (lock_file(lockfds, F_UNLCK, "mom.lock", 1, NULL, 0))
		log_errf(errno, msg_daemonname, "failed to unlock mom.lock file");
	log_close(1);
	close(lockfds);
	unlink("mom.lock");
#ifdef WIN32
	CloseDesktop(pbs_desktop);
	CloseWindowStation(pbs_winsta);
#endif

#ifdef PYTHON
	Py_Finalize();
#endif
	return (0);
}

/**
 * @brief
 *	make the directory names used by MOM
 *
 * @param[in] base - base path
 *
 * @return	string
 * @retval	string holding directory path
 *
 */

static char *
mk_dirs(char *base)
{
	char *pn;
	int ltop = strlen(pbs_conf.pbs_home_path);

	pn = malloc(ltop + strlen(base) + 2);
	if (pn == NULL)
#ifdef WIN32
		ExitThread(2);
#else
		exit(2);
#endif

	(void) strcpy(pn, pbs_conf.pbs_home_path);
#ifdef WIN32
	if (strchr(pn, '\\')) {
		if (*(pbs_conf.pbs_home_path + ltop - 1) != '\\')
			(void) strcat(pn, "\\");
	} else {
		if (*(pbs_conf.pbs_home_path + ltop - 1) != '/')
			(void) strcat(pn, "/");
	}
#else
	if (*(pbs_conf.pbs_home_path + ltop - 1) != '/')
		(void) strcat(pn, "/");
#endif /* WIN32 */
	(void) strcat(pn, base);
	return (pn);
}

#ifdef WIN32
int
main(int argc, char *argv[])
{
	int reg = 0;
	int unreg = 0;
	int stalone = 0;
	SC_HANDLE schManager;
	SC_HANDLE schSelf;
	TCHAR szFileName[MAX_PATH];

	/*the real deal or version and exit?*/
	PRINT_VERSION_AND_EXIT(argc, argv);

	if (argc > 1) {
		if (strcmp(argv[1], "-R") == 0)
			reg = 1;
		else if (strcmp(argv[1], "-U") == 0)
			unreg = 1;
		else if (strcmp(argv[1], "-N") == 0)
			stalone = 1;
		else
			usage2(argv[0]);
	}

	if (reg || unreg) {

		schManager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
		if (schManager == 0) {
			ErrorMessage("OpenSCManager");
			return 1;
		}

		if (reg) {
			GetModuleFileName(0, szFileName,
					  sizeof(szFileName) / sizeof(*szFileName));

			printf("Installing service %s\n", g_PbsMomName);
			schSelf =
				CreateService(schManager, g_PbsMomName,
					      __TEXT("PBS_MOM"),
					      SERVICE_ALL_ACCESS,
					      SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
					      SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
					      replace_space(szFileName, ""), 0, 0, 0, 0, 0);

			if (schSelf) {
				printf("Service %s installed successfully!\n",
				       g_PbsMomName);

			} else {
				ErrorMessage("CreateService");
				return 1;
			}

			if (schSelf != 0)
				CloseServiceHandle(schSelf);

		} else if (unreg) {
			schSelf = OpenService(schManager, g_PbsMomName, DELETE);

			if (schSelf) {
				if (DeleteService(schSelf)) {
					printf("Service %s uninstalled successfully!\n", g_PbsMomName);
				} else {
					ErrorMessage("DeleteService");
					return 1;
				}
			} else {
				ErrorMessage("OpenService failed");
				return 1;
			}
			if (schSelf != 0)
				CloseServiceHandle(schSelf);
		}

		if (schManager != 0)
			CloseServiceHandle(schManager);

	} else if (stalone) {

		struct arg_param *pap;
		int i, j;
		pap = create_arg_param();
		if (pap == NULL) {
			ErrorMessage("create_arg_param");
			return 1;
		}
		pap->argc = argc - 1; /* don't pass the second argument */
		for (i = j = 0; i < argc; i++) {
			if (i == 1)
				continue;
			if ((pap->argv[j] = strdup(argv[i])) == NULL) {
				free_arg_param(pap);
				ErrorMessage("strdup");
				return 1;
			}
			j++;
		}
		main_thread((void *) pap);
		if (cycle_harvester && interactive_svc_avail) {
			stop_pbs_interactive();
		}
		free_arg_param(pap);

	} else { /* run as service */
		SERVICE_TABLE_ENTRY ServiceTable[] = {
			{(TCHAR *) g_PbsMomName, PbsMomMain},
			{0}};

		if (getenv("PBS_CONF_FILE") == NULL) {
			char conf_path[80];
			char conf_env[80];
			char *p;
			char psave;
			struct stat sbuf;

			if (p = strstr(argv[0], "exec")) {
				psave = *p;
				*p = '\0';
				_snprintf(conf_path, 79, "%spbs.conf", argv[0]);
				*p = psave;
				if (stat(conf_path, &sbuf) == 0) {
					setenv("PBS_CONF_FILE", conf_path, 1);
				}
			}
		}
		hStop = CreateMutex(NULL, TRUE, NULL);
		if (!StartServiceCtrlDispatcher(ServiceTable)) {
			log_err(-1, "main", "StartServiceCtrlDispatcher");
			ErrorMessage("StartServiceCntrlDispatcher");
			return 1;
		}
		CloseHandle(hStop);
	}
	return (0);
}

/**
 * @brief
 *	Entry point for the service
 *
 * @param[in] dwArgc - Number of arguments in the rgszArgv array
 * @param[in] rgszArgv - Array of strings. The first string is the name of
 *			 the service and subsequent strings are passed by the process
 *			 that called the StartService function to start the service.
 *
 * @return Void
 *
 */
void
	WINAPI
	PbsMomMain(DWORD dwArgc, LPTSTR *rgszArgv)
{
	DWORD dwTID;
	DWORD dwWait;
	SERVICE_STATUS ss;
	DWORD i;

	struct arg_param *pap;

	g_ssHandle = RegisterServiceCtrlHandler(g_PbsMomName, PbsMomHandler);
	if (g_ssHandle == 0) {
		ErrorMessage("RegisterServiceCtrlHandler");
		return 1;
	}

	pap = create_arg_param();
	if (pap == NULL)
		return;
	pap->argc = dwArgc;

	for (i = 0; i < dwArgc; i++) {
		if ((pap->argv[i] = strdup(rgszArgv[i])) == NULL) {
			free_arg_param(pap);
			ErrorMessage("strdup");
			return 1;
		}
	}

	g_hthreadMain = (HANDLE) _beginthreadex(0, 0, main_thread, pap, 0, &dwTID);
	if (g_hthreadMain == 0) {
		(void) free_arg_param(pap);
		ErrorMessage("CreateThread");
		return 1;
	}

	dwWait = WaitForSingleObject(g_hthreadMain, INFINITE);
	if (dwWait != WAIT_OBJECT_0) {
		(void) free_arg_param(pap);
		ErrorMessage("WaitForSingleObject");
		return 1;
	}

	// NOTE: Update the global service state variable to indicate
	//      that the server has STOPPED. Use this to ACK the SCM
	//      that the service has stopped using SetServiceStatus.
	ZeroMemory(&ss, sizeof(ss));
	ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
	ss.dwCurrentState = SERVICE_STOPPED;
	ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

	if (g_ssHandle != 0)
		SetServiceStatus(g_ssHandle, &ss);

	free_arg_param(pap);
}

/**
 * @brief
 *	Handler function accepts shutdown and releases mutex so the
 *	PbsMomMain() can know it's time to exit.
 *
 * @param[in] dwControl - control code
 *
 * @return Void
 *
 */
void
	WINAPI
	PbsMomHandler(DWORD dwControl)
{
	SERVICE_STATUS ss;

	ZeroMemory(&ss, sizeof(ss));
	ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
	ss.dwCurrentState = g_dwCurrentState;
	ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

	switch (dwControl) {
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			// DONE: When you receive a stop request, update the global state
			//      variable to indicate that a STOP is pending. You need
			//      to then ACK the SCM by calling SetServiceStatus. Set
			//      the check point to 1 and the wait hint to 1 second,
			//      since we are going to wait for the server to shutdown.

			g_dwCurrentState = SERVICE_STOP_PENDING;
			ss.dwCurrentState = g_dwCurrentState;
			ss.dwCheckPoint = 1;
			ss.dwWaitHint = 1000;
			if (g_ssHandle != 0)
				SetServiceStatus(g_ssHandle, &ss);

			/*
			 log_close(1);
			 TerminateThread(g_hthreadMain, 0);
			 */
			ReleaseMutex(hStop);
			kill_jobs_on_exit = 1;

			/* if cycle harvesting configured and PBS_INTERACTIVE service is registered and started, then stop PBS_INTERACTIVE service */
			if (cycle_harvester && interactive_svc_avail) {
				_beginthreadex(0, 0, (void *) stop_pbs_interactive, NULL, 0, 0);
			}

			CloseHandle(g_hthreadMain);
			break;

		default:
			if (g_ssHandle != 0)
				SetServiceStatus(g_ssHandle, &ss);
			break;
	}
}
#else /* WIN32 */

#endif /* WIN32 */

/* the following is used in support of the getkbdtime() function */

static int check_idle_daemon = 1;
struct input_dev_list {
	int idl_here;
	char *idl_name;
} input_dev_list[] = {
	{1, "/dev/mouse"},
	{1, "/dev/kbd"},
	{1, "/dev/keybd"},
	{1, "/dev/kbd0"},
	{1, "/dev/kbd1"},
	{1, "/dev/hid/mouse_000"},
	{1, "/dev/hid/kbd_000"},
	{0, NULL}};

/**
 * @brief
 *	set most recent access time found for dev/file
 *      maxtm set if the dev/file st_atime is more recent than current value
 *
 * @param[in] dev - dev/file
 *
 * @retval  -1 if dev/file does not exist
 * @retval   0 if dev/file st_atime not most recent
 * @retval  +1 if dev/file st_atime is most recent (so far)
 *
 */
static int
setmax(char *dev)
{
	struct stat sb;

	if (stat(dev, &sb) == -1)
		return -1;
	if (maxtm < sb.st_atime) {
		maxtm = sb.st_atime;
		return 1;
	}

	return 0;
}

/**
 * get the most recent access time for mouse/keyboard/...
 *	we assume that "time_now" is the current time
 */
#ifdef WIN32
/**
 * @brief
 *	gets most recent access time found for idle_touch file
 *
 * @return	time_t
 * @retval	-1		Failure
 * @retval	max time	Success
 *
 */

time_t
getkbdtime()
{
	char idle_touch[MAX_PATH];

	/* Create full path of idle_touch file */
	snprintf(idle_touch, MAX_PATH, "%s/spool/idle_touch", pbs_conf.pbs_home_path);

	/* set most recent access time found for idle_touch file */
	if (setmax(idle_touch) == -1) {
		/* idled_touch file not found, Log this event and disable cycle harvesting */
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "Cycle Harvesting Failed, Please contact Admin");
		log_event(PBSEVENT_SYSTEM, 0, LOG_WARNING, "Cycle_Harvesting", log_buffer);
		cycle_harvester = 0;
		idle_check = -1;
	}
	return (maxtm);
}
#else

time_t
getkbdtime(void)
{
	DIR *dp;
	struct dirent *de;
	static char idle_dir[MAXPATHLEN + 1] = {'\0'};
	char *idle_file = NULL;
	struct input_dev_list *pl = &input_dev_list[0];
	int i;
	int checked = 0;
	char *ptsname;

	/* since we call this function so often, only want to set this once */
	if (idle_dir[0] == '\0')
		snprintf(idle_dir, sizeof(idle_dir), "%s/spool/idledir", pbs_conf.pbs_home_path);

	if (check_idle_daemon) {
		if ((dp = opendir(idle_dir)) != NULL) {
			while ((de = readdir(dp)) != NULL) {
				ptsname = de->d_name;

				if (maxtm >= time_now)
					break;
				if (*ptsname == '.')
					continue;
				pbs_asprintf(&idle_file, "%s/%s", idle_dir, ptsname);
				if (setmax(idle_file) > 0)
					checked = 1;
				free(idle_file);
			}
			closedir(dp);
		} else
			check_idle_daemon = 0;
	}

	if (checked == 0) {
		/* look at list of know keyboard/mouse devices */
		for (i = 0; (pl + i)->idl_name; ++i) {
			if ((pl + i)->idl_here && (pl + i)->idl_name) {
				if (setmax((pl + i)->idl_name) == -1)
					(pl + i)->idl_here = 0; /* ignore this now on */
			}
		}
	}

	return (maxtm);
}
#endif /* WIN32 */

/**
 * @brief
 *	returns the idle time of keyboard
 *
 * @param[in] attrib - pointer to rm_attribute structure
 *
 * @return	string
 * @retval	NULL	Failure
 * @retval	string	Success
 *
 */
char *
idletime(struct rm_attribute *attrib)
{
	time_t idle;
	time_t lastkey;

	if (attrib) {
		rm_errno = RM_ERR_BADPARAM;
		return NULL;
	}

	time_now = time(0);

	lastkey = getkbdtime();
	if (lastkey > time_now)
		idle = 0;
	else
		idle = time_now - lastkey;
	sprintf(ret_string, "%lu", (u_long) idle);
	return ret_string;
}

/**
 * @brief
 *	suspend/resume jobs based on keyboard being active/idle
 *	This function is like susp_resum() in requests.except that a
 *	different flag is set
 *
 * @param[in] pjob - pointer to job
 * @param[in] which is 1 to suspend, 0 to resume
 *
 * @return Void
 *
 */
static void
active_idle(job *pjob, int which)
{
	DBPRT(("active_idle (keyboard): %s job %s\n",
	       which == 1 ? "suspending" : "resuming", pjob->ji_qs.ji_jobid))
	if (((which == 1) && ((pjob->ji_qs.ji_svrflags &
			       (JOB_SVFLG_Suspend | JOB_SVFLG_Actsuspd)) == 0)) ||
	    ((which == 0) && ((pjob->ji_qs.ji_svrflags &
			       (JOB_SVFLG_Suspend | JOB_SVFLG_Actsuspd)) ==
			      JOB_SVFLG_Actsuspd))) {
		if (do_susres(pjob, which) < 0)
			return;
	}

	time_now = time(0);
	if (which == 1) { /* suspend */
		set_job_substate(pjob, JOB_SUBSTATE_SUSPEND);
		pjob->ji_qs.ji_svrflags |= JOB_SVFLG_Actsuspd;
		send_wk_job_idle(pjob->ji_qs.ji_jobid, which);
		if ((pjob->ji_qs.ji_svrflags &
		     (JOB_SVFLG_Suspend | JOB_SVFLG_Actsuspd)) == 0) {
			stop_walltime(pjob);
		}
	} else { /* resume */
		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_Suspend) == 0) {
			start_walltime(pjob);
			set_job_substate(pjob, JOB_SUBSTATE_RUNNING);
		}
		pjob->ji_qs.ji_svrflags &= ~JOB_SVFLG_Actsuspd;
		send_wk_job_idle(pjob->ji_qs.ji_jobid, which);
	}
	job_save(pjob);
}

void
do_multinodebusy(job *pjob, int which)
{
	int stream;

	DBPRT(("multinodebusy: dealing with job %s\n", pjob->ji_qs.ji_jobid))

	if (chk_mom_action(MultiNodeBusy) == Requeue) {

		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) != 0) {

			/* this is Mother Superior, kill and requeue job */

			pjob->ji_qs.ji_un.ji_momt.ji_exitstat = JOB_EXEC_RERUN;
			(void) kill_job(pjob, SIGKILL);
			/* Server will decide if job is rerunnable or not */
		} else {

			/* I am a sister node, send requeue message to Mom Superior */

			stream = pjob->ji_hosts[0].hn_stream;
			im_compose(stream, pjob->ji_qs.ji_jobid,
				   get_jattr_str(pjob, JOB_ATR_Cookie),
				   IM_REQUEUE, TM_NULL_EVENT, TM_NULL_TASK, IM_OLD_PROTOCOL_VER);
			dis_flush(stream);
		}
	}
}

/**
 * @brief
 *	idle all running jobs on keyboard active
 *	Even jobs that have been suspended on "suspend" signal request are idled
 *	At the current time, only single node jobs are suspended,
 *	multinode jobs are ignored or requeued.
 *
 * @return	Void
 *
 */
void
idle_jobs(void)
{
	job *pjob;
	update_state_flag = 0;
	for (pjob = (job *) GET_NEXT(svr_alljobs);
	     pjob;
	     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {
		if (check_job_state(pjob, JOB_STATE_LTR_RUNNING) &&
		    ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_Actsuspd) == 0)) {
			/* right now we can only handle one node jobs */
			if (pjob->ji_numnodes == 1) {
				update_state_flag = 1;
				active_idle(pjob, 1);
			} else {
				do_multinodebusy(pjob, 1);
			}
		}
	}
}

/**
 * @brief
 *	activate all idle jobs on keyboard going idle
 *	Even jobs that have been suspended on "suspend" signal request are
 *	"activated", though they will remain in suspend state.
 *	At the current time, only single node jobs are handled.
 *
 * @return	Void
 *
 */

void
activate_jobs(void)
{
	job *pjob;
	update_state_flag = 0;
	for (pjob = (job *) GET_NEXT(svr_alljobs);
	     pjob;
	     pjob = (job *) GET_NEXT(pjob->ji_alljobs)) {
		if (check_job_state(pjob, JOB_STATE_LTR_RUNNING) &&
		    ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_Actsuspd) != 0)) {
			if (pjob->ji_numnodes == 1) {
				update_state_flag = 1;
				active_idle(pjob, 0);
			}
		}
	}
}

/**
 * @brief
 *	If current load average >= max_load_val and busy not already set
 *		set it
 *	If current load average lt ideal_load_val and busy currently set
 *		unset it
 */
static void
check_busy(double mla)
{
	extern int internal_state;
	extern float ideal_load_val;
	extern float max_load_val;

	if ((mla >= max_load_val) && ((internal_state & INUSE_BUSY) == 0)) {
		internal_state |= INUSE_BUSY;
		internal_state_update = UPDATE_MOM_STATE;
		if (idle_on_maxload)
			idle_jobs();
	} else if ((mla < ideal_load_val) && ((internal_state & INUSE_BUSY) != 0)) {
		internal_state = (internal_state & ~INUSE_BUSY);
		internal_state_update = UPDATE_MOM_STATE;
		if (idle_on_maxload)
			activate_jobs();
	}
}

/**
 * @fn mom_topology
 * @brief
 *	compute and export platform-dependent topology information
 *
 * @return	void
 *
 * @par MT-Safe:	no
 * @par Side Effects:
 *	None
 *
 * @par Note:	nominally, we use the Open-MPI hardware locality (a.k.a. hwloc)
 *		functions to export the topology information that it generates,
 *		but the case for the Cray is different.
 *
 *		Also note that whenever we want the topology node attribute to
 *		contain a different type of information, this function will need
 *		to change.
 *
 *		On Windows we use native Windows API's to discover the topology
 *
 * @see	dep_topology()
 *
 */
void
mom_topology(void)
{
	extern char mom_short_name[];
	extern callfunc_t vn_callback;
	int ret = -1;
	char *xmlbuf = NULL;
	int xmllen = 0;
	vnl_t *vtp = NULL;
	char *topology_type;
	int fd[2];
	int pid;

#ifndef WIN32
	if (pipe(fd) == -1) 
		log_errf(-1, __func__, "pipe API failed. ERR : %s", strerror(errno));

	if ((pid = fork()) == -1) {
		log_err(PBSE_SYSTEM, __func__, "fork failed");
		return;
	}

	if (pid == 0) {
		hwloc_topology_t topology;
		ret = 0;

		close(fd[0]);

		ret = hwloc_topology_init(&topology);
		if (ret == 0)
#if HWLOC_API_VERSION < 0x00020000
			ret = hwloc_topology_set_flags(topology,
						       HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM |
							       HWLOC_TOPOLOGY_FLAG_IO_DEVICES);
#else
			ret = hwloc_topology_set_io_types_filter(topology,
								 HWLOC_TYPE_FILTER_KEEP_ALL);
#endif
		if (ret == 0)
			ret = hwloc_topology_load(topology);
		if (ret == 0)
#if HWLOC_API_VERSION < 0x00020000
			ret = hwloc_topology_export_xmlbuffer(topology,
							      &xmlbuf, &xmllen);
#else
			ret = hwloc_topology_export_xmlbuffer(topology,
							      &xmlbuf, &xmllen,
							      HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1);
#endif
		if (ret != 0)
			ret = -1;

		if (write(fd[1], &ret, (sizeof(ret))) == -1) 
			log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));
		if (write(fd[1], &xmllen, (sizeof(xmllen))) == -1) 
			log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));			
		if (write(fd[1], xmlbuf, xmllen) == -1) 
			log_errf(-1, __func__, "write failed. ERR : %s", strerror(errno));			

		hwloc_free_xmlbuffer(topology, xmlbuf);
		hwloc_topology_destroy(topology);

		exit(0);
	} else {
		close(fd[1]);

		if (read(fd[0], &ret, sizeof(ret)) == -1) 
			log_errf(-1, __func__, "read failed. ERR : %s", strerror(errno));
		if (read(fd[0], &xmllen, sizeof(xmllen)) == -1) 
			log_errf(-1, __func__, "read failed. ERR : %s", strerror(errno));			
		if ((xmlbuf = malloc(xmllen + 1)) == NULL) {
			log_err(PBSE_SYSTEM, __func__, "malloc failed");
			return;
		}
		xmlbuf[xmllen] = '\0';
		if (read(fd[0], xmlbuf, xmllen) == -1) 
			log_errf(-1, __func__, "read failed. ERR : %s", strerror(errno));

		close(fd[0]);

		waitpid(pid, NULL, 0);
	}
	if (ret < 0) {
		/* on any failure above, issue log message */
		log_err(PBSE_SYSTEM, __func__, "topology init/load/export failed");
		return;
	} else
#endif
	{
		char *lbuf;
		int lbuflen = xmllen + 1024;
#ifdef WIN32
		int no_of_sockets = 0;
#endif

		/*
		 *	xmlbuf is almost certain to overflow log_buffer's size,
		 *	so for logging this information, we allocate one large
		 *	enough to hold it
		 */
		if ((lbuf = malloc(lbuflen)) == NULL) {
			sprintf(log_buffer, "malloc logbuf (%d) failed",
				lbuflen);
			goto bad;
		} else {
			sprintf(lbuf, "allocated log buffer, len %d", lbuflen);
			log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_NODE,
				  LOG_DEBUG, __func__, lbuf);
		}
		log_event(PBSEVENT_DEBUG4,
			  PBS_EVENTCLASS_NODE,
			  LOG_DEBUG, __func__, "topology exported");
		if (vnl_alloc(&vtp) == NULL) {
			log_err(PBSE_SYSTEM, __func__, "vnl_alloc failed");
			free(lbuf);
			goto bad;
		}
#ifndef WIN32
		topology_type = NODE_TOPOLOGY_TYPE_HWLOC;
		sprintf(lbuf, "%s%s", topology_type, xmlbuf);
#else
		topology_type = NODE_TOPOLOGY_TYPE_WIN;
		no_of_sockets = count_sockets();
		if (no_of_sockets == -1)
			goto bad;
		sprintf(lbuf, "%ssockets:%d,gpus:%d,mics:%d", topology_type,
			no_of_sockets, count_gpus(), count_mics());
#endif
		if ((ret = vn_addvnr(vtp, mom_short_name, ATTR_NODE_TopologyInfo,
				     lbuf, ATR_TYPE_STR, READ_ONLY,
				     NULL)) != 0) {
			log_err(PBSE_SYSTEM, __func__, "vnl_addvnr failed");
			vnl_free(vtp);
			free(lbuf);
			goto bad;
		} else {
#ifndef WIN32
			sprintf(lbuf, "attribute '%s = %s%s' added", ATTR_NODE_TopologyInfo,
				topology_type, xmlbuf);
			log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_NODE, LOG_DEBUG, __func__, lbuf);
#else
			sprintf(log_buffer, "attribute '%s = %s' added", ATTR_NODE_TopologyInfo,
				lbuf);
			log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_NODE, LOG_DEBUG, __func__,
				  log_buffer);
#endif
		}
		if (vnlp == NULL) {
			/*
			 *	We must create a natural vnode to hold the
			 *	ATTR_NODE_TopologyInfo attribute.  This natural
			 *	vnode must also include available memory and
			 *	ncpus information.
			 */

			char attrbuf[1024];
			char valbuf[1024];
			char *memstr = physmem(NULL);

			sprintf(attrbuf, "%s.%s", ATTR_rescavail, "mem");
			sprintf(valbuf, "%s", memstr != NULL ? memstr : "0");
			if ((ret = vn_addvnr(vtp, mom_short_name, attrbuf,
					     valbuf, 0, 0, NULL)) != 0) {
				log_err(PBSE_SYSTEM, __func__, "vnl_alloc failed");
				vnl_free(vtp);
				free(lbuf);
				goto bad;
			} else {
				sprintf(lbuf, "resource '%s = %s' added",
					attrbuf, valbuf);
				log_event(PBSEVENT_DEBUG4,
					  PBS_EVENTCLASS_NODE,
					  LOG_DEBUG, __func__, lbuf);
			}

			sprintf(attrbuf, "%s.%s", ATTR_rescavail, "ncpus");
			sprintf(valbuf, "%d", num_acpus);
			if ((ret = vn_addvnr(vtp, mom_short_name, attrbuf,
					     valbuf, 0, 0, NULL)) != 0) {
				log_err(PBSE_SYSTEM, __func__, "vnl_alloc failed");
				vnl_free(vtp);
				free(lbuf);
				goto bad;
			} else {
				sprintf(lbuf, "resource '%s = %s' added",
					attrbuf, valbuf);
				log_event(PBSEVENT_DEBUG4,
					  PBS_EVENTCLASS_NODE,
					  LOG_DEBUG, __func__, lbuf);
			}
			vtp->vnl_modtime = time(NULL);
			vnlp = vtp;

		} else {
			vn_merge(vnlp, vtp, vn_callback);
			vnl_free(vtp);
		}
		free(lbuf);
	}
bad:
#ifndef WIN32
	free(xmlbuf);
#else
    ;
#endif
}
