/*
 * Copyright (C) 1994-2018 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * PBS Pro 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.
 *
 * PBS Pro 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:
 *
 * For a copy of the commercial license terms and conditions,
 * go to: (http://www.pbspro.com/UserArea/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 PBS Pro 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™",
 * "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.
 */

#include <pbs_config.h>   /* the master config generated by configure */

#ifdef PYTHON
#include <Python.h>
#endif

#ifdef	WIN32
#include	<sys/timeb.h>
#else
#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>
#endif	/* WIN32 */

#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	"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	"rpp.h"
#include	"dis.h"
#include	"dis_init.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"
#if	defined(MOM_CPUSET)
#include	"mom_vnode.h"
#endif	/* MOM_CPUSET */
#include	"avltree.h"
#ifndef	WIN32
#ifndef NAS /* localmod 113 */
#include	"hwloc.h"
#endif /* localmod 113 */
#endif
#include	"hook.h"
#include	"mom_hook_func.h"
#include	"work_task.h"
#include	"pbs_share.h"
#include	"mom_server.h"
#if	MOM_CSA || MOM_ALPS
#include	"mom_mach.h"
#endif	/* MOM_CSA or MOM_ALPS */
#include	"pbs_reliable.h"

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


/*
 * 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.
 */

#if IRIX6_CPUSET == 1
#include "pbs_mutex.h"
#include "cpusets.h"

static pbs_mutex        pbs_commit_mtx;
volatile pbs_mutex      *pbs_commit_ptr = &pbs_commit_mtx;
#endif

/* Global Data Items */

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;
#else
char		*mom_domain;
#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 */
#if	defined(MOM_CPUSET) && (CPUSET_VERSION >= 4)
char		*cpuset_error_action = "offline";
#endif	/* MOM_CPUSET && CPUSET_VERSION >= 4 */
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 */

#ifdef WIN32
char            pbs_tmpdir[MAX_PATH] = TMP_DIR;
char            pbs_jobdir_root[MAX_PATH]= "";
#else
char            pbs_tmpdir[_POSIX_PATH_MAX] = TMP_DIR;
char            pbs_jobdir_root[_POSIX_PATH_MAX]= "";
#endif
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;
extern struct var_table vtable;	/* see start_exec.c */
#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		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 */

unsigned long	hook_action_id = 0;

pbs_list_head	svr_allhooks;
/* hooks below ignored */
pbs_list_head	svr_queuejob_hooks;
pbs_list_head	svr_modifyjob_hooks;
pbs_list_head	svr_resvsub_hooks;
pbs_list_head	svr_movejob_hooks;
pbs_list_head	svr_runjob_hooks;
pbs_list_head	svr_periodic_hooks;
pbs_list_head	svr_provision_hooks;
pbs_list_head	svr_resv_end_hooks;
pbs_list_head	svr_hook_job_actions;
pbs_list_head   svr_hook_vnl_actions;
int		svr_hook_resend_job_attrs = 0;
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;

/* the task lists */
pbs_list_head	task_list_immed;
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 */
static 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 */
static 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;
#ifdef	WIN32
static char     path_checkpoint_buf[MAX_PATH] = "\0";
#else
static char     path_checkpoint_buf[_POSIX_PATH_MAX] = "\0";
#endif
static time_t	maxtm;			/* see getkbdtime() */

#ifdef WIN32

#define IDLE_POLL_BUFSIZE 512

/**
 * This global variable is used to store name of pbs interative service
 * which is used to control the pbs interactive service (i.e. start/stop/check)
 */
const TCHAR *const g_PbsInteractiveName = __TEXT("PBS_INTERACTIVE");

/**
 * This global variable is used to store PBS_INTERACTIVE Service Control Handle
 * which is used to control the pbs interactive service (i.e. start/stop/check)
 */
SC_HANDLE schPbsInteractive = INVALID_HANDLE_VALUE;

/**
 * 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 *);
#if	defined(MOM_CPUSET) && (CPUSET_VERSION >= 4)
static handler_ret_t	set_cpuset_error_action(char *);
#endif	/* MOM_CPUSET && CPUSET_VERSION >= 4 */
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_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 *);
#if	MOM_BGL
static handler_ret_t	set_bgl_reserve_partitions(char *);
#endif	/* MOM_BGL */
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 */

#if defined(__sgi)
extern handler_ret_t	set_checkpoint_upgrade(char *);
#endif /* __sgi */

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 },
#if	MOM_BGL
	{ "bgl_reserve_partitions",	set_bgl_reserve_partitions },
#endif	/* MOM_BGL */
	{ "checkpoint_path",		set_checkpoint_path },
#if	defined(__sgi)
	{ "checkpoint_upgrade",		set_checkpoint_upgrade },
#endif	/* __sgi */
	{ "clienthost",			addclient },
	{ "configversion",		config_verscheck },
	{ "cputmult",			cputmult },
#if	defined(MOM_CPUSET) && (CPUSET_VERSION >= 4)
	{ "cpuset_error_action",	set_cpuset_error_action },
#endif	/* MOM_CPUSET && CPUSET_VERSION >= 4 */
	{ "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 },
#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 }
};


char	*log_file = NULL;
char	*path_log;


char			*ret_string;
int			ret_size;
struct	config		*config_array = NULL;
struct	config_list	*config_list = NULL;
#ifndef	WIN32
sigset_t		allsigs;
#endif
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;
#ifdef	WIN32
static	char		config_file[MAX_PATH] = "config";
#else
static	char		config_file[_POSIX_PATH_MAX] = "config";
#endif

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);
extern	void	scan_for_exiting(void);
#ifdef NAS /* localmod 015 */
extern	int	to_size(char *, struct size_value *);
#endif /* localmod 015 */

#ifndef	WIN32
extern  void    scan_for_terminated(void);

/* Local public functions */

void stop_me(int);
#endif

extern	void	cleanup_hooks_workdir(struct work_task *);

/* Local private functions */

static char *mk_dirs(char *);
static void check_busy(double);

/**
 * @brief
 *	'windows' only function to check for an existence
 *	of directory.
 *
 * @param[in] path - directory path
 * @param[in] id   - char pointer holding which directory
 *
 * @retval 0 success
 * @retval -1 failure
 *
 */
#ifdef WIN32
static int
check_directory(char *path, char *id)
{
	struct stat sb;

	/*
	 * Do some minimal sanity checking, like
	 * does the cleaned_value exist?
	 * and is it a directory?
	 */
	if (stat(path, &sb) == -1) {
		sprintf(log_buffer, "\"%s\" does not exist", path);
		log_err(-1, id, log_buffer);
		return -1;
	}
	if (!S_ISDIR(sb.st_mode)) {
		sprintf(log_buffer, "\"%s\" is not a directory", path);
		log_event(PBSEVENT_SYSTEM, 0, LOG_ERR, id, log_buffer);
		return -1;
	}
	return 0;

}
#endif

/**
 * @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
 *	returns username
 *
 * @return string
 * @retval user name
 *
 */
char *
getuname(void)
{
	static	char	*name = NULL;

#ifndef	WIN32
	struct	utsname	n;

	if (name == NULL) {
		if (uname(&n) == -1)
			return NULL;
		sprintf(ret_string, "%s %s %s %s %s", n.sysname,
			n.nodename, n.release, n.version, n.machine);
		name = strdup(ret_string);
	}
#else
	OSVERSIONINFO osvi;

	if (name == NULL) {
		char	hostname[PBS_MAXHOSTNAME+1];

		if (gethostname(hostname, (sizeof(hostname) - 1)) != 0)
			return NULL;

		ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

		GetVersionEx(&osvi);
		sprintf(ret_string, "WIN2000 %s %d.%d %d i386",
			hostname, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
		name = strdup(ret_string);
	}
#endif	/* WIN32 */

	return name;
}

/**
 * @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();
	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. cpusets initializations, 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;
	int		avl;
	AVL_IX_DESC	ix;
	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;

	/*
	 * Each node of the AVL tree has a key, the hostname in this
	 * case. The default length of the key is defined in avltree.h,
	 * but then overridden here by the union definition of xxrp. The
	 * rp variable then points to this structure. When memory beyond
	 * the default lenfth of the AVL_IX_REC is accessed, it must be
	 * through xxrp or the compiler will complain about accessing
	 * memory beyond the size of the structure.
	 * 
	 */
	union {
		AVL_IX_REC	xrp;
		char		buf[PBS_MAXHOSTNAME + sizeof(AVL_IX_REC) + 1];
	} xxrp;
	AVL_IX_REC *rp = &xxrp.xrp;
	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);

	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
#ifdef	_SX
			corelimit.rlim_cur =
				atol(pbs_conf.pbs_core_limit);
#else
			corelimit.rlim_cur =
				(rlim_t)atol(pbs_conf.pbs_core_limit);
#endif	/* _SX */
		/* 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.
	 */
	avl_create_index(&ix, AVL_NO_DUP_KEYS, 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;
		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 */
		snprintf(rp->key, PBS_MAXHOSTNAME, "%s", host);

		/* look to see if host has a sharing value saved */
		avl = avl_find_key(rp, &ix);
		if (avl != AVL_IX_OK) {
			/*
			 * Not found so save the one we got.
			 */
			rp->recptr = exclhost;
			if (avl_add_key(rp, &ix) != AVL_IX_OK) {
				log_err(errno, __func__, "avl_add_key");
				die(0);
			}
			continue;
		}

		/* the host exists, check if the saved value is the same */
		if (rp->recptr == (void *)exclhost)
			continue;

		/* they are different, now check if it is a bad combo */
		hostval = str_to_vnode_sharing(rp->recptr);
		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,
				(char *)rp->recptr, host);
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_NODE,
				LOG_NOTICE, __func__, log_buffer);
			die(0);
		}
	}

	avl_destroy_index(&ix);

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

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

/**
 * @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++) {
#ifdef	WIN32
		if (!isalnum(*str) && *str != ':' && *str != '_' && *str != '\\')
#else
		if (!isalnum(*str) && *str != ':' && *str != '_')
#endif
			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;
}

#ifdef	WIN32
/**
 * @brief
 *	Similar to tokcpy() except that we respect any double quoted strings and
 *	we only accept a whitespace character that is outside
 *	of any enclosing double quotes as being a valid terminator.
 *
 * @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 *
qwtokcpy(char *str, char *tok, int len)
{
	int  i;
	unsigned char  oq = 0x0;	    /* open quote mark toggle */

	for (i=0; *str && (i<len); str++, tok++, i++) {
		if (isspace((int)*str) && !oq)
			break;
		if (*str == '"')
			oq ^=  0x1;
		*tok = *str;
	}
	*tok = '\0';
	return str;
}
#endif	/* WIN32 */

/**
 * @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
 *
 */
static 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
/**
 * @Brief
 *      Check whether PBS_INTERACTIVE service is registered or not into Service Control Manager?
 *		if PBS_INTERACTIVE service is registered then open Service Control Handle for PBS_INTERACTIVE service and
 *		store into global variable schPbsInteractive and set the value of interactive_svc_avail variable to 1 and
 *		return success.
 *		if PBS_INTERACTIVE service is not registered in to Service Control Manager the set the value
 *		of interactive_svc_avail variable to -1 and return success.
 *		on any error set value of interactive_svc_avail variable to 0 and return failure.
 *
 * @return	int
 * @retval	0  - On Success
 * @retval	1  - On Error
 *
 */
int
check_pbs_interactive()
{
	SC_HANDLE schPbsInteractiveMngr = INVALID_HANDLE_VALUE;

	/* Open Service Control Handle for Service Control Manager */
	schPbsInteractiveMngr = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if (schPbsInteractiveMngr == 0) {
		snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not open Service Control Manager");
		log_err(-1, __func__, log_buffer);
		return 1;
	}

	/* Trying to open Service Control Handle for PBS_INTERACTIVE service */
	schPbsInteractive = OpenService(schPbsInteractiveMngr, g_PbsInteractiveName, SERVICE_ALL_ACCESS);
	if (schPbsInteractive == 0) {
		/* Check why openning of Service Control Handle for PBS_INTERACTIVE service is failed? */
		if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
			/* Service does not exist */
			interactive_svc_avail = -1;
			CloseServiceHandle(schPbsInteractiveMngr);
			return 0;
		} else {
			snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not open %s service", g_PbsInteractiveName);
			log_err(-1, __func__, log_buffer);
			CloseServiceHandle(schPbsInteractiveMngr);
			return 1;
		}
	}

	/* Service exists and service control handle successfully created */
	interactive_svc_avail = 1;
	CloseServiceHandle(schPbsInteractiveMngr);
	return 0;
}

/**
 * @brief
 *      Start PBS_INTERACTIVE service with one argument as full path of pbs_idled binary
 *
 * @return	int
 * @retval	0  - On Success
 * @retval	1  - On Error
 *
 */
int
start_pbs_interactive()
{
	char *SvcArgv[1];
	char ExeFile_path[MAX_PATH];

	/* Check whether PBS_INTERACTIVE is registered of not? */
	if (interactive_svc_avail == -1) {
		/* PBS_INTERACTIVE service is not registered, log that and return success
		 * because here we assume that user want to use logon/logoff script
		 */
		snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not find %s service, Continuing Cycle Harvesting with Logon/Logoff Script", g_PbsInteractiveName);
		log_event(PBSEVENT_SYSTEM, 0, LOG_WARNING, g_PbsInteractiveName, log_buffer);
		return 0;
	} else if (interactive_svc_avail == 0) {
		/* Error occured */
		return 1;
	}

	/* Create full path of pbs_idled binary */
	snprintf(ExeFile_path, MAX_PATH, "%s/%s", pbs_conf.pbs_exec_path, "bin/pbs_idled");

	/* Initialize argument array for PBS_INTERACTIVE service */
	SvcArgv[0] = ExeFile_path;

	/* Start service, pass SvcArgv as arguments*/
	if (!StartService(schPbsInteractive, 1, SvcArgv)) {
		/* Failed to start PBS_INTERACTIVE service, return error */
		snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not start %s service", g_PbsInteractiveName);
		log_err(-1, __func__, log_buffer);
		return 1;
	}

	return 0;
}

/**
 * @Brief
 *      Stop PBS_INTERACTIVE service
 *
 * @return	void (Nothing)
 *
 */
void
stop_pbs_interactive()
{
	SERVICE_STATUS SvcSts;
	char temp_path[MAX_PATH];

	/* Check whether PBS_INTERACTIVE service is registered or not? */
	if (interactive_svc_avail == -1) {
		/* PBS_INTERACTIVE service is not registered, return success */
		return;
	} else if (interactive_svc_avail == 0) {
		/* Error occured */
		return;
	}

	/* PBS_INTERACTIVE service is registered */
	/* Get information about PBS_INTERACTIVE service */
	if (!QueryServiceStatus(schPbsInteractive, &SvcSts)) {
		snprintf(log_buffer, LOG_BUF_SIZE-1, "1st: Can not get information about %s service", g_PbsInteractiveName);
		log_err(-1, __func__, log_buffer);
		CloseServiceHandle(schPbsInteractive);
		return;
	}

	/* Check whether current status of service is RUNNING or not?
	 * if Yes, then stop service otherwise continue
	 */
	if (SvcSts.dwCurrentState == SERVICE_RUNNING) {
		/* Service is RUNNING, Now stop service */
		if (!ControlService(schPbsInteractive, SERVICE_CONTROL_STOP, &SvcSts)) {
			/* Stopping of service is failed, return Error */
			snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not stop %s service", g_PbsInteractiveName);
			log_err(-1, __func__, log_buffer);
			CloseServiceHandle(schPbsInteractive);
			return;
		}

		Sleep(SvcSts.dwWaitHint);

		/* Get information about PBS_INTERACTIVE service again */
		if (!QueryServiceStatus(schPbsInteractive, &SvcSts)) {
			snprintf(log_buffer, LOG_BUF_SIZE-1, "2nd: Can not get information about %s service", g_PbsInteractiveName);
			log_err(-1, __func__, log_buffer);
			CloseServiceHandle(schPbsInteractive);
			return;
		}

		/* Check whether stopping of service is success or not? */
		if (SvcSts.dwCurrentState != SERVICE_STOPPED) {
			/* Stopping of service is failed, return Error */
			snprintf(log_buffer, LOG_BUF_SIZE-1, "%s service did not respond in timely fashion", g_PbsInteractiveName);
			SetLastError(ERROR_SERVICE_REQUEST_TIMEOUT);
			log_err(-1, __func__, log_buffer);
			CloseServiceHandle(schPbsInteractive);
			return;
		}
	}

	/* Create full path of idle_poll_time file */
	snprintf(temp_path, MAX_PATH, "%s/%s", pbs_conf.pbs_home_path, "spool/idle_poll_time");

	/* Delete idle_poll_time file as it not needed any more */
	DeleteFile(temp_path);

	/* Create full path of idle_touch file */
	snprintf(temp_path, MAX_PATH, "%s/%s", pbs_conf.pbs_home_path, "spool/idle_touch");

	/* Delete idle_touch file as it not needed any more */
	DeleteFile(temp_path);

	/* Stopping of PBS_INTERACTIVE service is successful */
	CloseServiceHandle(schPbsInteractive);
	return;
}

/**
 * @brief
 *      Create idle_touch and idle_poll_time file in PBS_HOME/spool directory
 *	assign read/write permission to everyone to both file
 *	write idle_poll time to idle_poll_time file
 *
 * @return	int
 * @retval	0  - On Success
 * @retval	1  - On Error
 *
 */
int
create_idle_files()
{
	char idle_poll_buf[IDLE_POLL_BUFSIZE];
	char WrittenBuf[IDLE_POLL_BUFSIZE];
	HANDLE FileHandle = INVALID_HANDLE_VALUE;
	char idle_touchFile[MAX_PATH]; /* To store full path of idle_touch file */
	char idle_pollFile[MAX_PATH]; /* To store full path of idle_poll_time file */

	/* Create full path of idle_poll_time file */
	snprintf(idle_pollFile, MAX_PATH, "%s/%s", pbs_conf.pbs_home_path, "spool/idle_poll_time");

	/* Create full path of idle_touch file */
	snprintf(idle_touchFile, MAX_PATH, "%s/%s", pbs_conf.pbs_home_path, "spool/idle_touch");

	/* Convert idle_poll time value into string */
	_itoa_s(idle_poll, idle_poll_buf, BUF_SIZE, 10);

	/* Create idle_poll_time file in PBS_HOME/spool directory */
	FileHandle = CreateFile(idle_pollFile, GENERIC_ALL, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (FileHandle == INVALID_HANDLE_VALUE) {
		/* idle_poll_time file creation failed */
		snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not create file %s", idle_pollFile);
		log_err(-1, __func__, log_buffer);
		return 1;
	} else {
		/* idle_poll_time file created */
		/* secure idle_poll_time file for everyone with read/write permission */
		secure_file(idle_pollFile, "Everyone", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);

		/* idle_poll_time file is secured, write idle_poll time into idle_poll_time file */
		if (!WriteFile(FileHandle, idle_poll_buf, strlen(idle_poll_buf), (LPDWORD)&WrittenBuf, NULL)) {
			/* Writing idle_poll time into idle_poll_time file failed */
			snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not write idle_poll time into %s file", idle_pollFile);
			log_err(-1, __func__, log_buffer);
			CloseHandle(FileHandle);
			return 1;
		}
	}

	/* idle_poll_time file created, secured and idle_poll time written sucessfully */
	if (FileHandle)
		CloseHandle(FileHandle);

	/* Create idle_touch file in PBS_HOME/spool directory */
	FileHandle = CreateFile(idle_touchFile, GENERIC_ALL, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (FileHandle == INVALID_HANDLE_VALUE) {
		/* idle_touch file creation failed */
		snprintf(log_buffer, LOG_BUF_SIZE-1, "Can not create file %s", idle_touchFile);
		log_err(-1, __func__, log_buffer);
		return 1;
	} else {
		/* idle_touch file created */
		/* secure idle_touch file for everyone with read/write permission */
		secure_file(idle_touchFile, "Everyone", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
	}

	/* idle_touch file created and secured sucessfully */
	if (FileHandle)
		CloseHandle(FileHandle);

	return 0;
}
#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 */

#ifdef	WIN32
		str = qwtokcpy(++str, arg, _POSIX_PATH_MAX);
#else
		str = wtokcpy(++str, arg, _POSIX_PATH_MAX);
#endif
		str = skipwhite(str);

#ifdef	WIN32
		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);
		}
#else
		if (*arg != '/') {
			/* need to make relative path absolute to */
			/* 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);
		} else {
			scp = malloc(strlen(arg) + 1);
			if (scp == NULL) {
				return HANDLER_FAIL;
			}
			strcpy(scp, arg);
		}
#endif	/* WIN32 */

		/* 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) {
			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(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 */

	str = TOKCPY(str, arg);
	str = skipwhite(str);
	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;
}

#if	MOM_BGL
/**
 * @brief
 *	sets reserve partitions.
 *
 * @param[in] part_list - partition list
 *
 * @return      handler_ret_t (return value)
 * @retval      HANDLER_FAIL(0)                 Failure
 * @retval      HANDLER_SUCCESS(1)              Success
 *
 */

static handler_ret_t
set_bgl_reserve_partitions(char *part_list)
{
	reserve_bglpartitions = strdup(part_list);
	if (reserve_bglpartitions) {
		sprintf(log_buffer, "bgl_reserve_partitions %s", part_list);
		log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, __func__, log_buffer);
		return HANDLER_SUCCESS;
	} else {
		log_err(errno, __func__, "strdup failed");
		return HANDLER_FAIL;
	}

}
#endif	/* MOM_BGL */

/**
 * @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;
}

#if	defined(MOM_CPUSET) && (CPUSET_VERSION >= 4)
/**
 * @brief
 *	set the action to take when encountering
 *	CPU set errors.  value may be one of
 *
 *		"continue"	to log the errors and proceed normally
 *
 *		"offline"	in response to an error, the job's vnodes
 *				on this host will be marked offline;
 *				this is the default action
 *
 * @return      handler_ret_t
 * @retval      HANDLER_FAIL(0)         Failure
 * @retval      HANDLER_SUCCESS         Success
 *
 */

static handler_ret_t
set_cpuset_error_action(char *value)
{
	char		tok[80];
	char		*action;

	log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_DEBUG, __func__, value);

	if ((value == 0) || (*value == '\0') || (strlen(value) >= sizeof(tok)))
		return HANDLER_FAIL;

	(void) TOKCPY(value, tok);

	if (!strcmp(tok, "continue") || !strcmp(tok, "offline")) {
		action = strdup(tok);
		if (action != NULL) {
			cpuset_error_action = action;
			return HANDLER_SUCCESS;
		}
	}

	return HANDLER_FAIL;
}
#endif	/* MOM_CPUSET && CPUSET_VERSION >= 4 */

/**
 * @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;

#ifdef	WIN32
	back2forward_slash((pcphosts+cphosts_num)->cph_from);
	back2forward_slash((pcphosts+cphosts_num)->cph_to);
#endif

	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;
}

#define PBS_MAX_ACTION_ARGS 2000
/**
 * @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;
#ifdef	WIN32
	char		buf[MAX_PATH + 1];
#else
	char		buf[MAXPATHLEN + 1];
#endif
	int		i;
	int		nargs;
	char		**pargs;
	struct	stat	sb;
	struct	passwd	*pwdp;
	int		rc = -1;
	struct mom_action	*ma;
	int	transmog = 0;
#ifdef	WIN32
	char	*pnoq = 0;
	char	cmd_line[4096];
	int	flags = CREATE_DEFAULT_ERROR_MODE|CREATE_NEW_CONSOLE|
		CREATE_NEW_PROCESS_GROUP;
	STARTUPINFO             si = { 0 };
	PROCESS_INFORMATION     pi = { 0 };
	char	*env_block;
	char	*shell;
	extern	char	*variables_else[];
	DWORD	ret;
	HANDLE	hjob;
#else
	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));
#endif	/* WIN32 */

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

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

	/* does script really exist? */
#ifdef	WIN32
	pnoq = remove_quotes(ma->ma_script);
	if (stat(pnoq, &sb) == -1) {
		sprintf(log_buffer, "action %s script %s does not exist",
			ma->ma_name, ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		if (pnoq)
			free(pnoq);
		return -1;
	} else if (pnoq && chk_file_sec(pnoq, 0, 0, WRITES_MASK, 0)) {
		sprintf(log_buffer, "action %s script %s cannot be executed "
			"due to permissions", ma->ma_name, ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		if (pnoq)
			free(pnoq);
		return -1;
	}

	if (pnoq)
		free(pnoq);
#else
	if (stat(ma->ma_script, &sb) == -1) {
		sprintf(log_buffer, "action %s script %s does not exist",
			ma->ma_name, ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		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)) {
		sprintf(log_buffer, "action %s script %s cannot be executed "
			"due to permissions", ma->ma_name, ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		return -1;
	}
#endif	/* WIN32 */

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

	if (ptask == NULL) {
		sprintf(log_buffer, "action %s script %s cannot run because"
			" job has no tasks", ma->ma_name, ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		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) {
		sprintf(log_buffer, "action %s script %s cannot be run"
			" due to existing subtask", ma->ma_name,
			ma->ma_script);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		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;

#ifdef	WIN32
	strcpy(cmd_line, "cmd /q /c ");
	strcat(cmd_line, ma->ma_script);
#endif

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

	pargs = ma->ma_args;
	for (i = 1; i < nargs; i++, pargs++) {
		if (**pargs == '%') {
			if (strcmp(*pargs + 1, "jobid") == 0) {
				(void)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) {
#ifdef	WIN32
				sprintf(buf, "%ld", pjob->ji_qs.ji_un.ji_momt.ji_exuid);
#else
				sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exuid);
#endif
			} else if (strcmp(*pargs + 1, "gid") == 0) {
#ifdef	WIN32
				sprintf(buf, "%ld", pjob->ji_qs.ji_un.ji_momt.ji_exgid);
#else
				sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exgid);
#endif
			} else if (strcmp(*pargs + 1, "login") == 0) {
				strcpy(buf,
					pjob->ji_wattr[(int)JOB_ATR_euser].at_val.at_str);
			} else if (strcmp(*pargs + 1, "owner") == 0) {
				strcpy(buf,
					pjob->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str);
			} else if (strcmp(*pargs + 1, "globid") == 0) {
				strcpy(buf, "NULL");
			} else if (strcmp(*pargs + 1, "auxid") == 0) {
				if (pjob->ji_wattr[(int)JOB_ATR_altid].at_val.at_str) {
					strncpy(buf, pjob->ji_wattr[(int)JOB_ATR_altid].
						at_val.at_str, sizeof(buf)-1);
					buf[sizeof(buf)-1] = '\0';
				} else
					strcpy(buf, "NULL");
			} else if (strcmp(*pargs + 1, "path") == 0) {
				if (path != NULL) {
					strncpy(buf, path, sizeof(buf)-1);
					buf[sizeof(buf)-1] = '\0';
				} else
					strcpy(buf, "NULL");
			} else {
				sprintf(log_buffer, "action %s script %s cannot be run"
					" due to unknown parameter %s",
					ma->ma_name, ma->ma_script,
					*pargs);
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
					pjob->ji_qs.ji_jobid, log_buffer);
				goto done;
			}
		} else {
			(void)strcpy(buf, *pargs);
		}
		*(args + i) = strdup(buf);
		if (*(args + i) == NULL)
			return -1;
#ifdef	WIN32
		strcat(cmd_line, " ");
		strcat(cmd_line, buf);
#endif
	}

#ifdef	WIN32
	shell = set_shell(pjob, pwdp);	/* machine dependent */

	init_envp();

	/*
	 **	Setup environment
	 */
	/* UID */
	sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exuid);
	bld_wenv_variables("UID", buf);
	/* GID */
	sprintf(buf, "%d", pjob->ji_qs.ji_un.ji_momt.ji_exgid);
	bld_wenv_variables("GID", buf);
	/* HOME */
	bld_wenv_variables(variables_else[0], pwdp->pw_dir);
	/* LOGNAME */
	bld_wenv_variables(variables_else[1], pwdp->pw_name);
	/* PBS_JOBNAME */
	bld_wenv_variables(variables_else[2],
		pjob->ji_wattr[(int)JOB_ATR_jobname].at_val.at_str);
	/* PBS_JOBID */
	bld_wenv_variables(variables_else[3], pjob->ji_qs.ji_jobid);
	/* PBS_QUEUE */
	bld_wenv_variables(variables_else[4],
		pjob->ji_wattr[(int)JOB_ATR_in_queue].at_val.at_str);
	/* SHELL */
	bld_wenv_variables(variables_else[5], shell);
	/* USER */
	bld_wenv_variables(variables_else[6], pwdp->pw_name);
	/* PBS_JOBCOOKIE */
	bld_wenv_variables(variables_else[7],
		pjob->ji_wattr[(int)JOB_ATR_Cookie].at_val.at_str);
	/* PBS_NODENUM */
	sprintf(buf, "%d", pjob->ji_nodeid);
	bld_wenv_variables(variables_else[8], buf);
	/* PBS_TASKNUM */
	sprintf(buf, "%ld", (long)ptask->ti_qs.ti_task);
	bld_wenv_variables(variables_else[9], buf);
	/* PBS_MOMPORT */
	sprintf(buf, "%d", pbs_rm_port);
	bld_wenv_variables(variables_else[10], buf);
	/* PBS_NODEFILE */
	sprintf(buf, "%s/aux/%s", pbs_conf.pbs_home_path,
		pjob->ji_qs.ji_jobid);
	bld_wenv_variables(variables_else[11], buf);
	/* PBS_SID */
	sprintf(buf, "%ld", ptask->ti_qs.ti_sid);
	bld_wenv_variables("PBS_SID", buf);
	/* PBS_JOBDIR */
	if ((pjob->ji_wattr[(int)JOB_ATR_sandbox].at_flags & ATR_VFLAG_SET) &&
		(strcasecmp(pjob->ji_wattr[JOB_ATR_sandbox].at_val.at_str, "PRIVATE") == 0)) {
		bld_wenv_variables("PBS_JOBDIR", jobdirname(pjob->ji_qs.ji_jobid, pjob->ji_grpcache->gc_homedir));
	} else {
		bld_wenv_variables("PBS_JOBDIR", pjob->ji_grpcache->gc_homedir);
	}

	/* USERPROFILE */
	bld_wenv_variables(variables_else[16],
		default_local_homedir(pwdp->pw_name,
		pwdp->pw_userlogin, 1));

	/* USERNAME */
	bld_wenv_variables(variables_else[17], pwdp->pw_name);

	/*
	 ** Special case for restart_transmogrify.
	 */
	if ((transmog = (ae == RestartAction) && restart_transmogrify)) {
		if (pjob->ji_hJob == INVALID_HANDLE_VALUE) {
			hjob = CreateJobObject(NULL, pjob->ji_qs.ji_jobid);
			if (hjob == NULL) {
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
					LOG_WARNING, pjob->ji_qs.ji_jobid,
					"Unable to create job");
				goto done;
			}
			pjob->ji_hJob = hjob;
		}
		if ((i = mom_set_limits(pjob, SET_LIMIT_SET)) != PBSE_NONE) {
			sprintf(log_buffer, "Unable to set limits, err=%d", i);
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
				LOG_WARNING, pjob->ji_qs.ji_jobid, log_buffer);
			goto done;
		}
		flags |= CREATE_SUSPENDED;
	}

	env_block = make_envp();
	si.cb = sizeof(si);
	si.lpDesktop = "";

	ret = CreateProcess(NULL, cmd_line,
		NULL, NULL, TRUE, flags,
		env_block, NULL, &si, &pi);

	/* could be sitting on a user's network directory (checkpoint action) */
	if ((ret == 0) && (GetLastError() == ERROR_ACCESS_DENIED)) {
		char    current_dir[MAX_PATH+1];
		char    *temp_dir = NULL;

		current_dir[0] = '\0';
		_getcwd(current_dir, MAX_PATH+1);

		temp_dir = get_saved_env("SYSTEMROOT");
		chdir(temp_dir?temp_dir:"C:\\");

		ret = CreateProcess(NULL, cmd_line,
			NULL, NULL, TRUE, flags,
			env_block, NULL, &si, &pi);

		/* restore current working directory */
		chdir(current_dir);
	}
	if (env_block)
		free(env_block);

	if (ret == 0) {
		sprintf(log_buffer, "action script %s cannot be run due to"
			" CreateProcess failure %d", ma->ma_script,
			WSAGetLastError());
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		goto done;
	}

	if (post != NULL) {	/* post func means we do not wait */
		/* hook action process to pjob so that when pjob gets killed */
		/* all action processes will get cleared */
		if (pjob->ji_hJob != INVALID_HANDLE_VALUE)
			AssignProcessToJobObject(pjob->ji_hJob, pi.hProcess);

		rc = 1;
		pjob->ji_momsubt = pi.hProcess;
		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 */
		/*
		 * call to daemon_protect() to unprotect not needed
		 * for Windows as the created process does not inherit
		 * the protection value
		 */
		ret = AssignProcessToJobObject(pjob->ji_hJob, pi.hProcess);
		if (!ret) {
			log_err(-1, __func__, "AssignProcessToJobObject");
			CloseHandle(pi.hThread);
			if (processtree_op_by_handle(pi.hProcess, TERMINATE, 13) == -1)
				log_err(-1, __func__, "processtree_op_by_handle failed with TERMINATE");
			CloseHandle(pi.hProcess);
			goto done;
		}

		/*
		 **	After adding process to job
		 */
		ptask->ti_hProc = pi.hProcess;
		ptask->ti_qs.ti_sid = pi.dwProcessId;
		ptask->ti_qs.ti_status = TI_STATE_RUNNING;
		(void)task_save(ptask);
		/* update the job with the new session id */
		pjob->ji_wattr[(int)JOB_ATR_session_id].at_val.at_long
		= ptask->ti_qs.ti_sid;
		pjob->ji_wattr[(int)JOB_ATR_session_id].at_flags =
			ATR_VFLAG_SET | ATR_VFLAG_MODIFY;
		if (pjob->ji_qs.ji_substate != JOB_SUBSTATE_RUNNING) {
			pjob->ji_qs.ji_state = JOB_STATE_RUNNING;
			pjob->ji_qs.ji_substate = JOB_SUBSTATE_RUNNING;
			job_save(pjob, SAVEJOB_QUICK);
		}
		(void)sprintf(log_buffer, "task transmogrified, %s", cmd_line);
		log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);

		ret = ResumeThread(pi.hThread);
		if (ret == -1) {
			log_err(-1, __func__, "ResumeThread");
			CloseHandle(pi.hThread);
			if (processtree_op_by_handle(pi.hProcess, TERMINATE, 13) == -1)
				log_err(-1, __func__, "processtree_op_by_handle failed with TERMINATE");
			goto done;
		}
		CloseHandle(pi.hThread);
		/* send updated session id to server */
		update_ajob_status(pjob);
		rc = 0;
	}
	else {			/* wait for script */
		ret = WaitForSingleObject(pi.hProcess, ma->ma_timeout*1000);
		if (ret == WAIT_TIMEOUT) {
			sprintf(log_buffer, "%s script %s: timed out",
				ma->ma_name, ma->ma_script);
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB,
				LOG_INFO, pjob->ji_qs.ji_jobid,
				log_buffer);
			rc = -1;
		} else if (ret == WAIT_FAILED) { /* abnormal exit */
			sprintf(log_buffer, "%s script %s: wait failed %d",
				ma->ma_name, ma->ma_script, GetLastError());
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB,
				LOG_INFO, pjob->ji_qs.ji_jobid, log_buffer);
			rc = -1;
		} else {
			GetExitCodeProcess(pi.hProcess, (DWORD *)&rc);
			sprintf(log_buffer, "%s script %s: exit code %d",
				ma->ma_name, ma->ma_script, rc);
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_JOB,
				LOG_INFO, pjob->ji_qs.ji_jobid, log_buffer);
		}

		/* The following is iffy --
		 ** should we close the handle or do it outside?
		 */
		if (ret != WAIT_TIMEOUT) {
			CloseHandle(pi.hProcess);
		}
	}

	init_envp();
#else
	/*
	 ** 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)) {
		sprintf(log_buffer, "action %s script %s preparing "
			"to transmogrify task %8.8X",
			ma->ma_name, ma->ma_script,
			ptask->ti_qs.ti_task);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);

		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) {
		sprintf(log_buffer, "action %s script %s preparing "
			"to restart task %8.8X",
			ma->ma_name, ma->ma_script,
			ptask->ti_qs.ti_task);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
	}

	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) {
			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],
			pjob->ji_wattr[(int)JOB_ATR_jobname].at_val.at_str);
		/* PBS_JOBID */
		bld_env_variables(&vtable, variables_else[3],
			pjob->ji_qs.ji_jobid);
		/* PBS_QUEUE */
		bld_env_variables(&vtable, variables_else[4],
			pjob->ji_wattr[(int)JOB_ATR_in_queue].at_val.at_str);
		/* 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],
			pjob->ji_wattr[(int)JOB_ATR_Cookie].at_val.at_str);
		/* 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 ((pjob->ji_wattr[(int)JOB_ATR_sandbox].at_flags &
			ATR_VFLAG_SET) &&
			(strcasecmp(pjob->ji_wattr[JOB_ATR_sandbox].
			at_val.at_str, "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();

		if (set_mach_vars(pjob, &vtable) != 0) {
			log_event(PBSEVENT_JOB | PBSEVENT_SECURITY,
				PBS_EVENTCLASS_JOB,
				LOG_ERR, pjob->ji_qs.ji_jobid,
				"failed to setup dependent environment!");
			return -1;
		}

		/*
		 ** 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_4jid, 0,
				sizeof(pjob->ji_extended.ji_ext.ji_4jid));
#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) {
				sprintf(log_buffer,
					"Unable to set limits, err=%d", i);
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
					LOG_WARNING, pjob->ji_qs.ji_jobid,
					log_buffer);
				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) {		/* error */
		sprintf(log_buffer, "action script %s cannot be run due to"
			" fork failure %d", ma->ma_script, errno);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, log_buffer);
		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)) {
			sprintf(log_buffer,
				"read of pipe for pid job %s got %d not %d",
				pjob->ji_qs.ji_jobid, i, (int)sizeof(sjr));
			log_err(j, __func__, log_buffer);
			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) {
			sprintf(log_buffer, "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);
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_JOB,
				LOG_NOTICE, pjob->ji_qs.ji_jobid, log_buffer);
			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 */
		pjob->ji_wattr[(int)JOB_ATR_session_id].at_val.at_long
		= sjr.sj_session;
		pjob->ji_wattr[(int)JOB_ATR_session_id].at_flags =
			ATR_VFLAG_SET | ATR_VFLAG_MODIFY;
		if (pjob->ji_qs.ji_substate != JOB_SUBSTATE_RUNNING) {
			pjob->ji_qs.ji_state = JOB_STATE_RUNNING;
			pjob->ji_qs.ji_substate = JOB_SUBSTATE_RUNNING;
			job_save(pjob, SAVEJOB_QUICK);
		}

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

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 Void
 *
 */
static void
add_static(char *str, char *file, int linenum)
{
	int	 i;
	char	 name[256];
	struct	config_list	*cp;


	str = TOKCPY(str, name);/* resource name */
	str = skipwhite(str);	/* resource value */
	if (*str == '!')	/* shell escape command */
		rmnl(str);
	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);

	sprintf(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;
}

/**
 * @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);
	add_static(newstr, "config", 0);
	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);
	add_static(newstr, "config", 0);
	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:
#ifdef WIN32
	/* create idle_touch and idle_poll_time file in PBS_HOME/spool/
	 *    idle_touch file	  - To get kbd/mouse time information from pbs_idled process
	 *    idle_poll_time file - To inform about idle_poll time from MOM config to pbs_idled process
	 */
	if (create_idle_files())
		return HANDLER_FAIL;

	/* Check whether PBS_INTERACTIVE service is registered or not? */
	if (check_pbs_interactive()) {
		/* check for registration of PBS_INTERACTIVE service is failed, return failure */
		return HANDLER_FAIL;
	} else {
		/* check for registration of PBS_INTERACTIVE service is successed
		 * trying to start PBS_INTERACTIVE service */
		if (start_pbs_interactive()) {
			/* starting of PBS_INTERACTIVE service is failed, return failure */
			return HANDLER_FAIL;
		}
	}
#endif
	return HANDLER_SUCCESS;
}

/**
 * @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--) {
#ifdef	WIN32
		if (cleaned_value[i] != '\\')
#else
		if (cleaned_value[i] != '/')
#endif
			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)
#ifdef	WIN32
	if (check_directory(cleaned_value, "set_tmpdir") == -1) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}
#else	/* Unix Only */
	if (tmp_file_sec(cleaned_value, 1, 1, 0, 1)) {
		free(cleaned_value);
		return HANDLER_FAIL;		/* error */
	}
#endif	/* WIN32 */
#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;

	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)
#ifdef	WIN32

	if (check_directory(cleaned_value, __func__)  == -1) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}

#else /* UNIX only */
	if (tmp_file_sec(cleaned_value, 1, 1, 0, 1)) {
		free(cleaned_value);
		return HANDLER_FAIL;
	}
#endif	/* WIN32 */
#endif	/* NO_SECURITY_CHECK */

	strcpy(pbs_jobdir_root, cleaned_value);
	free(cleaned_value);
	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;
#ifdef	WIN32
	char		newpath[MAX_PATH] = "\0";
#else
	char		newpath[_POSIX_PATH_MAX] = "\0";
#endif

	/*
	 * 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.");
		strcpy(newpath, path_checkpoint_from_getopt);
	} else if (path_checkpoint_from_getenv) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			__func__, "Using checkpoint path from environment.");
		strcpy(newpath, path_checkpoint_from_getenv);
	} else if (value) {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			__func__, "Using checkpoint path from config file.");
		strcpy(newpath, value);
	} else {
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			__func__, "Using default checkpoint path.");
		strcpy(newpath, path_checkpoint_default);
	}
	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')
			strcpy(path_checkpoint, path_checkpoint_default);
		return HANDLER_FAIL;	/* error */
	}
	if (*(newpath+strlen(newpath)-1) != '/') {
		strcat(newpath, "/");
	}
#if !defined(DEBUG) && !defined(NO_SECURITY_CHECK)
#ifdef	WIN32
	rc = chk_file_sec(newpath, 1, 0, WRITES_MASK, 0);
#else
	rc = chk_file_sec(newpath, 1, 0, S_IWGRP|S_IWOTH, 0);
#endif	/* WIN32 */
#else
	{
		struct stat	sb;
#ifdef	WIN32
		/* use windowed lstat for it works */
		if (lstat(newpath, &sb) == -1)
			rc = errno;
		else
			rc = 0;
#else
		if (stat(newpath, &sb) == -1)
			rc = errno;
		else
			rc = 0;
#endif	/* WIN32 */
	}
#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
 *	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 */
#ifdef	WIN32
	char		*p;
#endif

	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;

#ifdef	WIN32
		/* under Windows, skip troublesome trailing ^M chars */
		if (p = strrchr(line, 13))
			*p = ' ';
#endif

		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;
		}

		add_static(str, file, linenum);
		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) {
		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;

#if	MOM_BGL
	if (reserve_bglpartitions) {
		(void)free(reserve_bglpartitions);
		reserve_bglpartitions = NULL;
	}

#endif

	/*	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;
	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)
#ifdef	WIN32
	if (chk_file_sec(file, 0, 0, WRITES_MASK, 0)) {
		sprintf(log_buffer,
			"warning: %s file has a non-secure file access mask", file);
		log_err(errno, __func__, log_buffer);
		return 1;
	}
#else
	if (chk_file_sec(file, 0, 0, S_IWGRP|S_IWOTH, 1))
		return 1;
#endif	/* WIN32 */
#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	defined(MOM_CPUSET)
		/*
		 *	If this isn't the first time we've read the additional
		 *	configuration files, we may have undone any adjustments
		 *	to various vnodes' resources_available.ncpus attributes.
		 *	Now we resynch these attributes with the mom_vnodeinfo
		 *	mvi_cpulist array of CPUs.
		 */
		cpu_raresync();
#endif	/* MOM_CPUSET */

		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;
#ifdef	WIN32
	pio_handles     child;
#else
	FILE	*child;
	int	fd;
#endif
	char	*child_spot;
	int	child_len;
	int	secondalarm = 0;

	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))

#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]);
	}
	return ret_string;
}

#ifndef	WIN32
/**
 * @brief
 *	Function to catch HUP signal.
 *	Set call_hup = 1.
 * @param[in] sig - signal number
 *
 * @return Void
 *
 */
static void
catch_hup(int sig)
{
	sprintf(log_buffer, "caught signal %d", sig);
	log_event(PBSEVENT_SYSTEM, 0,  LOG_INFO, "catch_hup", log_buffer);
	call_hup = HUP_REAL;
	rpp_dbprt = 1 - rpp_dbprt;	/* toggle debug prints for RPP */
}

/**
 * @brief
 *	Do a restart of resmom.
 *	Read the last seen config file and
 *	Clean up and reinit the dependent code.
 *
 * @return Void
 *
 */
static void
process_hup(void)
{
	/**
	 * When call_hup == HUP_REAL, the catch_hup function has been called.
	 * When call_hup == HUP_INIT, we couldn't start a job so the ALPS
	 * inventory needs to be refreshed.
	 * When real_hup is false, some actions don't need to be done.
	 */
	int	real_hup = (call_hup == HUP_REAL);

	call_hup = HUP_CLEAR;

#if	MOM_BGL
	log_event(PBSEVENT_SYSTEM, 0, LOG_INFO, __func__,
		"HUP is a no-op under Blue Gene mom");
	return;
#else

	if (real_hup) {
		log_event(PBSEVENT_SYSTEM, 0, LOG_INFO, __func__, "reset");
		log_close(1);
		log_open(log_file, path_log);

		if ((num_var_env = setup_env(pbs_conf.pbs_environment)) == -1) {
			mom_run_state = 0;
			return;
		}
	}

	/*
	 ** See if we need to get rid of the previous vnode state.
	 */
	if (!vnode_additive) {
		if (vnlp != NULL) {
			vnl_free(vnlp);
			vnlp = NULL;
		}
		if (vnlp_from_hook != NULL) {
			vnl_free(vnlp_from_hook);
			vnlp_from_hook = NULL;
		}
	}

	if (read_config(NULL) != 0) {
		cleanup();
		log_close(1);
		rpp_shutdown();
#ifdef	WIN32
		ExitThread(1);
#else
		exit(1);
#endif
	}

	cleanup();
	initialize();

#if	MOM_CSA || MOM_ALPS /* ALPS needs libjob support */
	/*
	 * This needs to be called after the config file is read.
	 */
	ck_acct_facility_present();
#endif	/* MOM_CSA or MOM_ALPS */

	if (!real_hup)		/* no need to go on */
		return;

#if	MOM_CPUSET
#if	(CPUSET_VERSION >= 4)
	cpusets_initialize(2);		/* warm start recovery */
#else
	cpusets_initialize();
#endif	/* CPUSET_VERSION >= 4 */
#endif	/* MOM_CPUSET */

#endif	/* MOM_BGL */
}

/**
 * @brief
 *	signal handler for SIG_USR2
 *
 * @return Void
 *
 */

static void
catch_USR2(int sig)
{
	do_debug_report = 1;
}

/**
 * @brief
 *	Cause useful information to be logged.  This function is called from
 *	MoM's main loop after catching a SIGUSR2.
 *
 * @return Void
 *
 */

void
debug_report(void)
{
extern void	mom_CPUs_report(void);

	mom_CPUs_report();
	mom_vnlp_report(vnlp, NULL);
	do_debug_report = 0;
}

/**
 * @brief
 *	Got an alarm call.
 *
 * @param[in] sig - signal number
 *
 * @return Void
 *
 */
void
toolong(int sig)
{

	log_event(PBSEVENT_SYSTEM, 0, LOG_NOTICE, __func__, "alarm call");
	DBPRT(("alarm call\n"))
}
#endif	/* !WIN32 */

#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 rpp stream.
 *
 * @param[in] iochan - i/o channel to indicate stream or fd
 * @param[in] version - protocol version
 * @param[in] tcp - flag for tcp 0 or 1
 *
 * @return int
 * @retval	0	Success
 * @retval	-1	Failure
 *
 */

int
rm_request(int iochan, int version, int tcp)
{
	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;
	int			(*flush_io)	(int);

	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 (tcp) {
		conn_t *conn = get_conn(iochan);

		if (!conn) {
			log_err(errno, __func__,
				"not find iochan in connection table");
#ifdef	WIN32
			(void)closesocket(iochan);
#else
			(void)close(iochan);
#endif	/* WIN32 */
			return -1;
		}
		ipadd = conn->cn_addr;
		port = conn->cn_port;
		close_io = close_conn;
		flush_io = DIS_tcp_wflush;
	}
	else {
		addr = rpp_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))pfn_rpp_close;
		flush_io = pfn_rpp_flush;
	}
	if (version != RM_PROTOCOL_VER) {
		sprintf(log_buffer, "protocol version %d unknown", version);
		goto bad;
	}

	restrictrm = 0;
	if (pbs_conf.pbs_use_tcp == 0 && ((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);
				rpp_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);
			}
			flush_io(iochan);
			close_io(iochan);
			cleanup();
			log_close(1);
			rpp_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 (flush_io(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 = (tcp) ?(close_conn):((void (*)(int))pfn_rpp_close);
	}

	close_io(iochan);
	return -1;
}

/**
 * @brief
 *	Read a RPP message from a stream, figure out if it is a
 *	Resource Monitor request or an InterMom message.
 *
 * @param[in] stream - rpp msg
 *
 * @return Void
 *
 */
void
do_rpp(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_rpp_reset();
	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, 0) == 0)
				rpp_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))
			rpp_close(stream);
			break;
	}
}

/* ARGSUSED */

/**
 * @brief
 *	wrapper function for do_rpp
 *
 * @param[in] fd - file descriptor
 *
 * @return Void
 *
 */
#define MAX_RPP_LOOPS 3
/* Reducing rpp request process for a minimum of 3 times to interleave other connections */
void
rpp_request(int fd)
{
	int	stream;
	int	i;
	/* To reduce rpp process storm reducing max do_rpp processing to 3 times */
	for (i=0 ; i < MAX_RPP_LOOPS ; i++) {
		if ((stream = rpp_poll()) == -1) {
#ifdef	WIN32
			if (errno != 10054)
#endif
				log_err(errno, __func__, "rpp_poll");
			break;
		}
		if (stream == -2)
			break;
		do_rpp(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, 1);
			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);
#ifdef	WIN32
		(void)closesocket(fd);
#else
		(void)close(fd);
#endif	/* WIN32 */
		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_setup(fd);
	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 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;
		}
	}
	DBPRT(("%s: done %s killed %d\n", __func__, pjob->ji_qs.ji_jobid, ct))
	return ct;
#endif	/* WIN32 */
}

/**
 * @brief
 *	The finish of MOM's main loop
 *	Actually the heart of the loop
 *
 * @param[in] waittime - wait time
 *
 * @return Void
 *
 */
static void
finish_loop(time_t waittime)
{
#ifdef	WIN32
	time_t	i;
#endif

	if (pbs_conf.pbs_use_tcp == 0) {
		/* check for any extra rpp messages */
		rpp_request(42);
	}

#ifdef	WIN32
	/* wait for a request to process or exiting procs */
	for (i = 0; i < waittime; i++) {
		wait_action();

		if (exiting_tasks)
			scan_for_exiting();
		wait_request(1, NULL);
	}
#else
	if (do_debug_report)
		debug_report();
	if (termin_child) {
		scan_for_terminated();
		waittime = 1;	/* want faster time around to next loop */
	}
	if (exiting_tasks) {
		scan_for_exiting();
		waittime = 1;	/* want faster time around to next loop */
	}

	if (waittime > next_sample_time)
		waittime = next_sample_time;
	DBPRT(("%s: waittime %lu\n", __func__, (unsigned long) waittime))

	/* wait for a request to process */
	if (wait_request(waittime, NULL) != 0)
		log_err(-1, msg_daemonname, "wait_request failed");

#endif	/* WIN32 */

}


#ifdef	WIN32

/**
 * @brief
 *	locks mom and logs error msg
 *
 * @param[in] fds - file descriptor
 * @param[in] op - type of lock
 *
 * @return Void
 *
 */

static void
mom_lock(int fds,
	int op /* F_WRLCK  or  F_UNLCK */)
{
	struct stat  sbuf;

	if (fstat(fds, &sbuf) == -1) {
		log_err(errno, "mom_lock", "can't stat lock file");
		exit(1);
	}
	if (_locking(fds, op, (long)sbuf.st_size) == -1) {
		if (op == F_WRLCK) {
			(void)strcpy(log_buffer,
				"pbs_mom: another Mom running\n");
			log_err(errno, msg_daemonname, log_buffer);
			fprintf(stderr, log_buffer);
			exit(1);
		}
	}

}
#else

/**
 * @brief
 *	lock out other MOMs from this directory.
 *
 * @param[in] fds - file descriptor
 * @param[in] op - type of lock
 *
 * @return Void
 *
 */

static void
mom_lock(int fds,
	int op /* F_WRLCK  or  F_UNLCK */)
{
	struct flock flock;

	(void)lseek(fds, (off_t)0, SEEK_SET);
	flock.l_type   = op;
	flock.l_whence = SEEK_SET;
	flock.l_start  = 0;
	flock.l_len    = 0;	/* whole file */
	if (fcntl(fds, F_SETLK, &flock) < 0) {
		(void)strcpy(log_buffer, "pbs_mom: another mom running");
		log_err(errno, msg_daemonname, log_buffer);
		(void)strcat(log_buffer, "\n");
		(void)fprintf(stderr, "%s", log_buffer);
		exit(1);
	}
}
#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,
#if defined(__sgi)
	rlim64_t	*ret
#else
	u_long	*ret
#endif	/* __sgi */
	)
{
#if defined(__sgi)
	rlim64_t	value;
#define PBS_RLIM_MAX (~(rlim64_t)0)
#define PBS_RLIM_TYPE	rlim64_t
#else
	u_Long		value;
#define PBS_RLIM_MAX ULONG_MAX
#define PBS_RLIM_TYPE	u_long
#endif	/* __sgi */

	/*
	 * 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_req, *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 = find_resc_def(svr_resc_def, "ncpus", svr_resc_size);
	at_req = &pjob->ji_wattr[(int)JOB_ATR_resource];
	pres_req = find_resc_entry(at_req, rd);
	if (pres_req != NULL)
		ncpus_req = MAX(0, pres_req->rs_value.at_val.at_long);

	rd = find_resc_def(svr_resc_def, "cpupercent", svr_resc_size);
	assert(rd != NULL);
	at_used = &pjob->ji_wattr[(int)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 = find_resc_def(svr_resc_def, "Walltime", svr_resc_size);
		assert(rd != NULL);
		preswalltime = find_resc_entry(at_used, rd);
		if ((preswalltime != NULL) &&
			((preswalltime->rs_value.at_flags & ATR_VFLAG_SET) != 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", pjob->ji_wattr[(int)JOB_ATR_session_id].at_val.at_long, 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);
	add_static(newstr, "config", 0);
	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 ((pjob->ji_wattr[(int)JOB_ATR_interactive].at_flags&ATR_VFLAG_SET) &&
		(pjob->ji_wattr[(int)JOB_ATR_interactive].at_val.at_long > 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;
#if defined(__sgi)
	rlim64_t	llvalue, llnum;
#else
	u_long		llvalue, llnum;
#endif
	u_long		value, num;
	resource	*pres;
	resource	*used;
	attribute	*uattr = &pjob->ji_wattr[(int)JOB_ATR_resc_used];
	resource_def	*rd;

	assert(pjob != NULL);
	assert(pjob->ji_wattr[(int)JOB_ATR_resource].at_type == ATR_TYPE_RESC);
	pres = (resource *)
		GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

	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 = &pjob->ji_wattr[(int)JOB_ATR_resc_used];
		assert(at->at_type == ATR_TYPE_RESC);
		rd = find_resc_def(svr_resc_def, "cpupercent", svr_resc_size);
		assert(rd != NULL);
		prescpup = find_resc_entry(at, rd);
		if ((prescpup != NULL) &&
			((prescpup->rs_value.at_flags & ATR_VFLAG_SET) != 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 */
					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 = find_resc_def(svr_resc_def, "walltime",
				svr_resc_size);
			assert(rd != NULL);
			preswalltime = find_resc_entry(at, rd);
			if ((preswalltime != NULL) &&
				((preswalltime->rs_value.at_flags & ATR_VFLAG_SET) != 0)) {
				walltime_sum = preswalltime->rs_value.at_val.at_long;
				if (walltime_sum > average_trialperiod) {
					rd = find_resc_def(svr_resc_def, "cput",
						svr_resc_size);
					assert(rd != NULL);
					prescput = find_resc_entry(at, rd);
					if ((prescput != NULL) &&
						((prescput->rs_value.at_flags & ATR_VFLAG_SET) != 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 */
								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 = find_resc_def(svr_resc_def, "vmem", svr_resc_size);
		used = find_resc_entry(uattr, rd);
		retval = local_getsize(used, &llnum);
		if (retval == PBSE_NONE) {
			if (llnum > llvalue) {
#if defined(__sgi)
				sprintf(log_buffer,
					"vmem %llukb exceeded limit %llukb",
					llnum/1024, llvalue/1024);
#else
				sprintf(log_buffer,
					"vmem %lukb exceeded limit %lukb",
					llnum/1024, llvalue/1024);
#endif
				return (TRUE);
			}
		}
	}

	/* check mem usage locally */
	llvalue = pjob->ji_hosts[pjob->ji_nodeid].hn_nrlimit.rl_mem << 10;
	if (llvalue != 0) {
		rd = find_resc_def(svr_resc_def, "mem", svr_resc_size);
		used = find_resc_entry(uattr, rd);
		retval = local_getsize(used, &llnum);
		if (retval == PBSE_NONE) {
			if ((llnum > llvalue) && enforce_mem) {
#if defined(__sgi)
				sprintf(log_buffer,
					"mem %llukb exceeded limit %llukb",
					llnum/1024, llvalue/1024);
#else
				sprintf(log_buffer,
					"mem %lukb exceeded limit %lukb",
					llnum/1024, llvalue/1024);
#endif
				return (TRUE);
			}
		}
	}

	pres = (resource *)
		GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

	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);
					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);
					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
 *
 * @return	int
 * @retval	0	Failure
 * @retval	1	Success
 *
 */

int
job_over_limit(job *pjob)
{
	attribute		*attr;
	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))) {
			 	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;
	}

	attr = &pjob->ji_wattr[JOB_ATR_resource];
	used = &pjob->ji_wattr[JOB_ATR_resc_used];
	for (limresc = (resource *)GET_NEXT(attr->at_val.at_list);
		limresc != NULL;
		limresc = (resource *)GET_NEXT(limresc->rs_link)) {

		if ((limresc->rs_value.at_flags & ATR_VFLAG_SET) == 0)
			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 ((useresc->rs_value.at_flags & ATR_VFLAG_SET) == 0)
			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;
}

static char	configusage[] = "%s -s insert scriptname inputfile\n"
	"%s -s [ remove | show ] scriptname\n"
"%s -s list\n";

/**
 * @brief
 *	Prints usage for prog
 *
 * @param[in] prog - char pointer which holds program name
 *
 * @return Void
 *
 */

void
usage(char *prog)
{
#ifdef	WIN32
	fprintf(stderr,
		"Usage: %s [-C chkdirectory][-d dir][-c configfile][-r|-p][-R port][-M port][-S port][-L log][-a alarm][-n nice]\n", prog);
#else
	fprintf(stderr,
		"Usage: %s [-C chkdirectory][-d dir][-c configfile][-r|-p][-R port][-M port][-L log][-a alarm][-n nice]\n", prog);
#endif
	fprintf(stderr, "or\n");
	fprintf(stderr, configusage, prog, prog, prog);
	fprintf(stderr, "%s --version\n", prog);
	exit(1);
}

#ifdef	WIN32
/**
 * @brief
 *      Prints usage for prog
 *
 * @param[in] prog - char pointer which holds program name
 *
 * @return Void
 *
 */

void
usage2(prog)
char	*prog;
{
	printf("================================================================================\n");
	printf("Usage Info: %s [-R|-U|-N] <other options...>\n", prog);
	printf("\nTo run as a standalone server: %s -N <other options...>\n",
		prog);
	printf("where <other options...>  are: [-C chkdirectory][-d dir]"
		"[-c configfile][-r|-p][-R port][-M port][-S port]"
		"[-L log][-n nice]\n");
	printf("To register as a service: %s -R\n", prog);
	printf("To unregister the service: %s -U\n", prog);
	printf("To run as a service: %s <other options...>\n", prog);
	printf("To output PBSpro version and exit: %s --version\n", prog);
	printf("================================================================================\n");

}
#endif

#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;
	attribute		*patresc               = NULL;/* ptr to job/resv resource_list */
	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) {

			log_event(PBSEVENT_SYSTEM, 0, LOG_DEBUG, id,
				errmsg);
		}
		else {
			/* 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 = find_resc_def(svr_resc_def, "place", svr_resc_size);

#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 (pjob->ji_qs.ji_substate !=
				JOB_SUBSTATE_RUNNING &&
				pjob->ji_qs.ji_substate !=
				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) {
			patresc = &hjob->ji_wattr[(int)JOB_ATR_resource];
			pplace = find_resc_entry(patresc, 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 (hjob->ji_qs.ji_substate != JOB_SUBSTATE_RUNNING) {
				hjob->ji_qs.ji_state = JOB_STATE_RUNNING;
				hjob->ji_qs.ji_substate = JOB_SUBSTATE_RUNNING;
				job_save(hjob, SAVEJOB_QUICK);
			}

			/*
			 ** 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) {
			patresc = &hjob->ji_wattr[(int)JOB_ATR_resource];
			pplace = find_resc_entry(patresc, 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
 *	logs message when rpp fails
 *
 * @param[in] mess - msg to be logged
 *
 * @return	Void
 *
 */

void
log_rppfail(char *mess)
{
	log_event(PBSEVENT_DEBUG, LOG_DEBUG,
		PBS_EVENTCLASS_SERVER, "rpp", mess);
}

/**
 * @brief
 *	This is the log handler for tpp implemented in the daemon. The pointer to
 *	this function is used by the Libtpp layer when it needs to log something to
 *	the daemon logs
 *
 * @param[in] level   - Logging level
 * @param[in] objname - Name of the object about which logging is being done
 * @param[in] messa   - The log message
 *
 * @return	Void
 *
 */
static void
log_tppmsg(int level, const char *objname, char *mess)
{
	char id[2*PBS_MAXHOSTNAME];
	int thrd_index;
	int etype = log_level_2_etype(level);

	thrd_index = tpp_get_thrd_index();
	if (thrd_index == -1)
		snprintf(id, sizeof(id), "%s(Main Thread)", (objname != NULL) ? objname : msg_daemonname);
	else
		snprintf(id, sizeof(id), "%s(Thread %d)", (objname != NULL) ? objname : msg_daemonname, thrd_index);

	log_event(etype, PBS_EVENTCLASS_TPP, level, id, mess);
	DBPRT((mess));
	DBPRT(("\n"));
}

/*
 * @brief
 *	Function called by the Libtpp layer when the network connection to
 *	the pbs_comm router is restored. In this implementation for the mom,
 *	the restore handler sends a mom restart message to the server.
 *
 * @param[in] data - currently unused
 *
 * @return	Void
 *
 */
void
net_restore_handler(void *data)
{
	mom_net_up = 1;
	mom_net_up_time = time(0);
	log_tppmsg(LOG_INFO, msg_daemonname, "net restore handler called");
	send_restart();
}

/*
 * @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_tppmsg(LOG_INFO, msg_daemonname, "net down handler called");
	if (server_stream >= 0) {
		sprintf(log_buffer, "Closing existing server stream %d",
			server_stream);
		log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_NOTICE,
			msg_daemonname, log_buffer);
		rpp_flush(server_stream);
		rpp_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) {
				rpp_flush(np->hn_stream);
				rpp_close(np->hn_stream);
				np->hn_stream = -1;
			}
		}
	}
	mom_net_up = 0;
	mom_net_up_time = 0;
}

#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 */
	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					tryport;
	int					rppfd;				/* fd for rm and im comm */
	int					privfd = -1;		/* fd for sending job info */
	double				myla;
	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 */

#ifndef	DEBUG
	FILE				*dummyfile;
#endif

#ifdef _POSIX_MEMLOCK
	int					do_mlockall = 0;
#endif

#ifdef PYTHON
	PyObject			*path;
	char				buf[MAXPATHLEN];
#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? */
	execution_mode(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);
			strncpy(pbs_conf_env, pbsconf_temp, strlen(pbsconf_temp)+ strlen(pch));
			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);
		}
	}

	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);
	}

	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 */

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

	/* 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);
	}
#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);
				back2forward_slash(pbs_conf.pbs_environment);
			}
		}
#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:NS: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 '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;
				strcpy(config_file, optarg);	/* 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 */
			perror(" setrlimit NPROC");
			exit(1);
		}
#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 */
			perror(" setrlimit NPROC");
			exit(1);
		}
#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 */


	/* 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, 0);
	c |= chk_file_sec(path_hooks,   1, 0, WRITES_MASK, 0);
	c |= chk_file_sec(path_hooks_workdir,   1, 0, WRITES_MASK, 0);
	c |= chk_file_sec(path_spool,  1, 1, 0, 0);
	c |= chk_file_sec(pbs_conf.pbs_environment, 0, 0, WRITES_MASK, 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 */

#ifdef	WIN32
	winsock_init();

	/* 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 PBS_MOM_NODE_NAME if it is defined.
	 * Otherwise, set mom_host with a call to gethostname().
	 */
	c = 0;
	if (pbs_conf.pbs_mom_node_name) {
		/* Hostname was defined by PBS_MOM_NODE_NAME */
		(void)strncpy(mom_host, pbs_conf.pbs_mom_node_name, (sizeof(mom_host) - 1));
		mom_host[(sizeof(mom_host) - 1)] = '\0';
		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 {
		/* Query the hostname. */
		c = gethostname(mom_host, (sizeof(mom_host) - 1));
	}
	if (c != 0) {
		log_err(-1, msg_daemonname, "Unable to obtain my host name");
		return (-1);
	}
	(void)strncpy(mom_short_name, mom_host, (sizeof(mom_short_name) - 1));
	mom_short_name[(sizeof(mom_short_name) - 1)] = '\0';
	if ((ptr = strchr(mom_short_name, (int)'.')) != NULL)
		*ptr = '\0';  /* terminate shortname 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
	mom_lock(lockfds, F_WRLCK);	/* See if other MOMs are running */

	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_CSA || 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_CSA or 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*/

	{
		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, "pbsd_main", log_buffer);
			exit(3);
		}
	}

#ifdef	WIN32 /* ------------------------------------------------------------*/
#ifndef	DEBUG
	if (stalone != 1) {
		(void)fclose(stdin);
		(void)fclose(stdout);
		(void)fclose(stderr);
		dummyfile = fopen("NUL:", "r");
		assert((dummyfile != 0) && (fileno(dummyfile) == 0));
		dummyfile = fopen("NUL:", "w");
		assert((dummyfile != 0) && (fileno(dummyfile) == 1));
		dummyfile = fopen("NUL:", "w");
		assert((dummyfile != 0) && (fileno(dummyfile) == 2));
	}

#else	/* DEBUG */
	(void)setvbuf(stdout, NULL, _IONBF, 0);
	(void)setvbuf(stderr, NULL, _IONBF, 0);
#endif /* DEBUG */

	mom_pid = (pid_t)getpid();

#else /* ! WIN32 ------------------------------------------------------------*/

	/* go into the background and become own session/process group */

#ifndef	DEBUG
	if (stalone != 1) {
		mom_lock(lockfds, F_UNLCK);	/* unlock so child can relock */

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

		if (setsid() == -1) {
			log_err(errno, msg_daemonname, "setsid failed");
			return (2);
		}
		mom_lock(lockfds, F_WRLCK);	/* lock out other MOMs */
	}
	mom_pid = getpid();

	(void)fclose(stdin);
	(void)fclose(stdout);
	(void)fclose(stderr);
	dummyfile = fopen("/dev/null", "r");
	assert((dummyfile != 0) && (fileno(dummyfile) == 0));
	dummyfile = fopen("/dev/null", "w");
	assert((dummyfile != 0) && (fileno(dummyfile) == 1));
	dummyfile = fopen("/dev/null", "w");
	assert((dummyfile != 0) && (fileno(dummyfile) == 2));
#else	/* DEBUG */
	if (stalone != 1) {
		(void) sprintf(log_buffer, "Debug build does not fork.");
		log_record(PBSEVENT_SYSTEM, PBS_EVENTCLASS_SERVER, LOG_INFO,
				__func__, log_buffer);
	}
	mom_pid = getpid();
	(void)setvbuf(stdout, NULL, _IOLBF, 0);
	(void)setvbuf(stderr, NULL, _IOLBF, 0);
#endif	/* DEBUG */

	/* write MOM's pid into lockfile */
#ifdef	WIN32
	lseek(lockfds, (off_t)0, SEEK_SET);
#else
	(void)ftruncate(lockfds, (off_t)0);
#endif
	(void)sprintf(log_buffer, "%d\n", mom_pid);
	(void)write(lockfds, log_buffer, strlen(log_buffer));

	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 */


	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_modifyjob_hooks);
	CLEAR_HEAD(svr_resvsub_hooks);
	CLEAR_HEAD(svr_movejob_hooks);
	CLEAR_HEAD(svr_runjob_hooks);
	CLEAR_HEAD(svr_periodic_hooks);
	CLEAR_HEAD(svr_provision_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(task_list_immed);
	CLEAR_HEAD(task_list_timed);
	CLEAR_HEAD(task_list_event);

#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;
#ifdef	_SX
	srand48(tval.tv_usec);
#else
	srandom(tval.tv_usec);
#endif	/* _SX */
#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);
	}

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

		c = ERRORNO;
		(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, tcp_request) != 0) {

		c = ERRORNO;
		(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);
	}

	/*Initialize security library's internal data structures*/

	{
		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, "pbsd_main", log_buffer);
                        exit(3);
                }
	}
	if (pbs_conf.pbs_use_tcp == 1) {
		int rc;
		char *nodename;

		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, "pbsd_main", log_buffer);
			fprintf(stderr, "%s\n", "Unable to determine TPP node name");
			return (1);
		}

	    /* set tcp function pointers */
		set_tpp_funcs(log_tppmsg);

		if (pbs_conf.auth_method == AUTH_RESV_PORT) {
				rc = set_tpp_config(&pbs_conf, &tpp_conf, nodename, pbs_rm_port, pbs_conf.pbs_leaf_routers,
														pbs_conf.pbs_use_compression, TPP_AUTH_RESV_PORT, NULL, NULL);
		} else {
				/* for all non-resv-port based authentication use a callback from TPP */
				rc = set_tpp_config(&pbs_conf, &tpp_conf, nodename, pbs_rm_port, pbs_conf.pbs_leaf_routers,
														pbs_conf.pbs_use_compression, TPP_AUTH_EXTERNAL, get_ext_auth_data, validate_ext_auth_data);
		}

		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 ((rppfd = tpp_init(&tpp_conf)) == -1) {
			(void) sprintf(log_buffer, "rpp_init failed");
			log_event(PBSEVENT_SYSTEM | PBSEVENT_ADMIN, PBS_EVENTCLASS_SERVER,
				LOG_ERR, msg_daemonname, log_buffer);
			fprintf(stderr, "%s", log_buffer);
			return (3);
		}
	} else {
		/* set rpp function pointers */
		set_rpp_funcs(log_rppfail);

		if ((rppfd = rpp_bind(pbs_rm_port)) == -1) {
			log_err(errno, __func__, "rpp_bind");
#ifdef	WIN32
			g_dwCurrentState = SERVICE_STOPPED;
			ss.dwCurrentState = g_dwCurrentState;
			ss.dwWin32ExitCode = ERROR_NETWORK_BUSY;
			if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif	/* WIN32 */
			return (1);
		}

		rpp_fd = -1;
		tryport = IPPORT_RESERVED;
		while (--tryport > 0) {
			if ((privfd = rpp_bind(tryport)) != -1)
				break;
#ifdef	WIN32
			errno = WSAGetLastError();
			if ((errno != WSAEADDRINUSE) && (errno != WSAEADDRNOTAVAIL))
#else
			if ((errno != EADDRINUSE) && (errno != EADDRNOTAVAIL))
#endif	/* WIN32 */
				break;
		}
		if (privfd == -1) {
			log_err(errno, __func__, "no privileged ports");
#ifdef	WIN32
			g_dwCurrentState = SERVICE_STOPPED;
			ss.dwCurrentState = g_dwCurrentState;
			ss.dwWin32ExitCode = ERROR_NETWORK_BUSY;
			if (g_ssHandle != 0) SetServiceStatus(g_ssHandle, &ss);
#endif	/* WIN32 */
			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 = find_resc_def(svr_resc_def, "cput", svr_resc_size);
	rdwall = find_resc_def(svr_resc_def, "walltime", svr_resc_size);
	/* 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
	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);

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

#ifdef PYTHON
	Py_NoSiteFlag = 1;
	Py_FrozenFlag = 1;
	Py_Initialize();

	path = PySys_GetObject("path");
#ifdef WIN32
	snprintf(buf, sizeof(buf), "%s/python/Lib", pbs_conf.pbs_exec_path);

	PyList_Append(path, PyString_FromString(buf));

#else
	/* list of possible paths to Python modules (mom imports json) */
	snprintf(buf, sizeof(buf), "%s/python/lib/python2.7", pbs_conf.pbs_exec_path);
	PyList_Append(path, PyString_FromString(buf));

	snprintf(buf, sizeof(buf), "%s/python/lib/python2.7/lib-dynload", pbs_conf.pbs_exec_path);
	PyList_Append(path, PyString_FromString(buf));

	snprintf(buf, sizeof(buf), "/usr/lib/python/python2.7");
	PyList_Append(path, PyString_FromString(buf));

	snprintf(buf, sizeof(buf), "/usr/lib/python/python2.7/lib-dynload");
	PyList_Append(path, PyString_FromString(buf));

	snprintf(buf, sizeof(buf), "/usr/lib64/python/python2.7");
	PyList_Append(path, PyString_FromString(buf));

	snprintf(buf, sizeof(buf), "/usr/lib64/python/python2.7/lib-dynload");
	PyList_Append(path, PyString_FromString(buf));
#endif
	PySys_SetObject("path", path);
#endif

#ifndef	WIN32
	initialize();		/* init RM code */
#endif
	(void)add_conn(rppfd, RppComm, (pbs_net_t)0, 0, rpp_request);
	if (pbs_conf.pbs_use_tcp == 0)
		(void)add_conn(privfd, RppComm, (pbs_net_t)0, 0, rpp_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 */
#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_err(0, "main", 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_err(0, "main", log_buffer);

	SetProcessWindowStation(old_winsta);

#endif	/* WIN32 */


	/* 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");
	}

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

	/* 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);


#if	MOM_CPUSET
#if	(CPUSET_VERSION >= 4)
	cpusets_initialize(recover);
#else
	cpusets_initialize();
#endif	/* CPUSET_VERSION >= 4 */
#endif	/* MOM_CPUSET */

	/* 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);
#endif	/* WIN32 */

	/*
	 * TPP mode: don't send a restart at startup
	 * we will send one when we connect to router
	 */
	if (pbs_conf.pbs_use_tcp == 0)
		send_restart();

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

	/*
	 * 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

		wait_time = default_next_task();
		end_proc();
		time_now = time(NULL);

		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);

			(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(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) &&
				(pjob->ji_qs.ji_substate == 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);
				pjob->ji_qs.ji_substate = 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;

		if (pbs_conf.pbs_use_tcp == 0)
			(void)rpp_io(); /* drive io for RPP */
#if 0
		while (pjob != NULL) {
			job	*njob = (job *)GET_NEXT(pjob->ji_alljobs);

			/* see if MS polling has stopped */
			DBPRT(("%s: polltime %ld\n", pjob->ji_qs.ji_jobid,
				pjob->ji_polltime))
			if (pjob->ji_polltime != 0 &&
				time_now >= (pjob->ji_polltime +
				MAX_CHECK_POLL_TIME + 20)) {
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_JOB,
					LOG_INFO, pjob->ji_qs.ji_jobid,
					"polling stopped");
				kill_job(pjob, SIGKILL);
				mom_deljob(pjob);
			}
			pjob = njob;
		}
#endif	/* polling stopped check */

		/* 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_qs.ji_substate == 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 ((pjob->ji_qs.ji_substate == 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(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 ((pjob->ji_qs.ji_substate != 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 (pbs_conf.pbs_use_tcp == 0)
				(void)rpp_io();

			if (pjob->ji_qs.ji_substate != 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(
					&pjob->ji_wattr[(int)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(
					&pjob->ji_wattr[(int)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 (pjob->ji_qs.ji_substate != 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)) {

					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 = rpp_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 {
									(void)write(fd, pjob->ji_wattr[(int)JOB_ATR_Cookie].at_val.at_str,
										strlen(pjob->ji_wattr[(int)JOB_ATR_Cookie].at_val.at_str));
									(void)write(fd, kill_msg, strlen(kill_msg));
									(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 (pjob->ji_qs.ji_substate == JOB_SUBSTATE_RUNNING
				|| pjob->ji_qs.ji_substate == JOB_SUBSTATE_SUSPEND
				|| pjob->ji_qs.ji_substate == 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();

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

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

	while ((pjob = (job *)GET_NEXT(mom_deadjobs)) != NULL) {
		job_purge(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, "pbsd_main", log_buffer);
		}
	}

	cleanup();
	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
		LOG_NOTICE, msg_daemonname, "Is down");
	log_close(1);
#ifdef	WIN32
	mom_lock(lockfds, F_UNLCK);     /* unlock  */
	(void)close(lockfds);
	(void)unlink("mom.lock");
	CloseDesktop(pbs_desktop);
	CloseWindowStation(pbs_winsta);
	destroy_env_avltree();
#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?*/
	execution_mode(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");
		}

		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");
			}

			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");
				}
			} else {
				ErrorMessage("OpenService failed");
			}
			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");

		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");
			}
			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");
		}
		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");
	}

	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");
		}
	}

	g_hthreadMain = (HANDLE) _beginthreadex(0, 0,  main_thread, pap, 0, &dwTID);
	if (g_hthreadMain == 0) {
		(void)free_arg_param(pap);
		ErrorMessage("CreateThread");
	}

	dwWait = WaitForSingleObject(g_hthreadMain, INFINITE);
	if (dwWait != WAIT_OBJECT_0) {
		(void)free_arg_param(pap);
		ErrorMessage("WaitForSingleObject");
	}

	// 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 */

/**
 * @brief
 *	signal handler for SIGTERM and SIGINT
 *	TERM kills running jobs
 *	INT  leaves them running
 *
 * @param[in] sig - signal number
 *
 * @return	Void
 *
 */

void
stop_me(int sig)
{
	sprintf(log_buffer, "caught signal %d", sig);
	log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
		LOG_NOTICE, msg_daemonname, log_buffer);

	switch (sig) {
		case SIGPIPE:
		case SIGUSR1:
#ifdef	SIGINFO
		case SIGINFO:
#endif
			return;

		default:
			break;
	}

	mom_run_state = 0;
	if (sig == SIGTERM)
		kill_jobs_on_exit = 1;
}
#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 */
		pjob->ji_qs.ji_substate =  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);
			pjob->ji_qs.ji_substate = JOB_SUBSTATE_RUNNING;
		}
		pjob->ji_qs.ji_svrflags &= ~JOB_SVFLG_Actsuspd;
		send_wk_job_idle(pjob->ji_qs.ji_jobid, which);
	}
	job_save(pjob, SAVEJOB_QUICK);
}

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,
				pjob->ji_wattr[(int)JOB_ATR_Cookie].at_val.at_str,
				IM_REQUEUE, TM_NULL_EVENT, TM_NULL_TASK, IM_OLD_PROTOCOL_VER);
			rpp_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 ((pjob->ji_qs.ji_state == JOB_STATE_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 ((pjob->ji_qs.ji_state == JOB_STATE_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)
{
#ifndef NAS /* localmod 113 */
	extern char mom_short_name[];
	extern callfunc_t vn_callback;
	int ret;
	char *xmlbuf = NULL;
	int xmllen = 0;
	vnl_t *vtp = NULL;
	char *topology_type;

#ifndef	WIN32
	hwloc_topology_t topology;
	ret = 0;
	if (hwloc_topology_init(&topology) == -1)
		ret = -1;
	else if ((hwloc_topology_set_flags(topology,
			HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM | HWLOC_TOPOLOGY_FLAG_IO_DEVICES)
			== -1) || (hwloc_topology_load(topology) == -1)
			|| (hwloc_topology_export_xmlbuffer(topology, &xmlbuf, &xmllen)
					== -1)) {
		hwloc_topology_destroy(topology);
		ret = -1;
	}
	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);

			if  (vnl_alloc(&vnlp) == NULL) {
				log_err(PBSE_SYSTEM, __func__, "vnl_alloc failed");
				vnl_free(vtp);
				free(lbuf);
				goto bad;
			}

			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
	hwloc_free_xmlbuffer(topology, xmlbuf);
	hwloc_topology_destroy(topology);
#else
	;
#endif
#endif /* localmod 113 */
}
