/*
 * 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_hook_func.c
 */
#include <pbs_config.h>   /* the master config generated by configure */

#ifndef WIN32
#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>

#include <memory.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pbs_ifl.h"
#include "libpbs.h"
#include "list_link.h"
#include "work_task.h"
#include "hook.h"
#include "log.h"
#include "server_limits.h"
#include "attribute.h"
#include "credential.h"
#include "batch_request.h"
#include "job.h"
#include "svrfunc.h"
#include "pbs_python.h"  /* for python interpreter */
#include <signal.h>
#include "mom_func.h"
#include "placementsets.h"
#include "resmon.h"
#include "libutil.h"
#include "pbs_nodes.h"
#include "net_connect.h"
#include "mom_hook_func.h"
#include "hook.h"
#include "pbs_reliable.h"


#define	RESCASSN_NCPUS	"resources_assigned.ncpus"
#define	RESCASSN_MEM	"resources_assigned.mem"
#define	RESCASSN_HOST	"resources_assigned.host"
/* External functions */

/* Local Private Functions */

/* Global Data items */
static int	run_exit = 0;	/* run exit of child */

extern int       resc_access_perm;
extern	char		*path_hooks;
extern	char		*path_hooks_workdir;
extern	char		*path_log;
extern	char		*path_spool;
extern	char		*mom_home;
extern  char		mom_host[];
extern  char		mom_short_name[];
extern pbs_list_head	svr_execjob_begin_hooks;
extern pbs_list_head	svr_execjob_prologue_hooks;
extern pbs_list_head	svr_execjob_epilogue_hooks;
extern pbs_list_head	svr_execjob_preterm_hooks;
extern pbs_list_head	svr_execjob_launch_hooks;
extern pbs_list_head	svr_execjob_end_hooks;
extern pbs_list_head	svr_exechost_periodic_hooks;
extern pbs_list_head	svr_exechost_startup_hooks;
extern pbs_list_head	svr_execjob_attach_hooks;
extern pbs_list_head	svr_execjob_resize_hooks;
extern pbs_list_head	svr_hook_job_actions;
extern pbs_list_head	svr_hook_vnl_actions;

extern	pbs_list_head       task_list_immed;
extern	pbs_list_head       task_list_timed;
extern	pbs_list_head       task_list_event;
extern	pbs_list_head	svr_alljobs;
extern	int		svr_hook_resend_job_attrs;

extern	char		*msg_err_malloc;

extern	time_t		time_now;

extern	int		num_pcpus;
extern	int		num_acpus;
extern 	u_Long		av_phy_mem;
extern	char		pbs_version[];

extern int becomeuser(job *pjob);

extern int  send_sched_recycle(char *user);

static void post_periodic_hook(struct work_task *pwt);

extern vnl_t		*vnlp;
extern unsigned long	 hook_action_id;
extern	int		internal_state_update; /* flag for sending mom information update to the server */

/**
 * @brief
 * 	Print job data into stream pointed by 'fp'.
 *
 * @param[in]	fp - stream pointer where data is flushed
 * @param[in]	pjob - pointer to job whose data is being printed out
 *
 */
static void
fprintf_job_struct(FILE *fp, job *pjob)
{
	pbs_list_head	phead;
	attribute	*pattr;
	svrattrl	*psatl;
	svrattrl	*ps;
	int		i;

	if ((fp == NULL) || (pjob == NULL)) {
		return;
	}

	fprintf(fp, "%s.%s=%s\n", EVENT_JOB_OBJECT, "id", pjob->ji_qs.ji_jobid);

	/* Now print job attributes and resources */
	CLEAR_HEAD(phead);
	pattr = pjob->ji_wattr;
	for (i=0; i < (int)JOB_ATR_LAST; i++) {
		(void)(job_attr_def+i)->at_encode(pattr+i, &phead,
			(job_attr_def+i)->at_name, NULL,
			ATR_ENCODE_MOM, NULL);
	}
	attrl_fixlink(&phead);

	psatl = (svrattrl *)GET_NEXT(phead);
	for (ps = psatl; ps; ps = (svrattrl *)GET_NEXT(ps->al_link)) {
		if (ps->al_resc != NULL) {
			fprintf(fp, "%s.%s[%s]=%s\n", EVENT_JOB_OBJECT,
				ps->al_name, ps->al_resc, ps->al_value);
		} else {
			if (strcmp(ps->al_name, ATTR_v) == 0)
				fprintf(fp, "%s.%s=\"\"\"%s\"\"\"\n", EVENT_JOB_OBJECT, ps->al_name, ps->al_value);
#if MOM_ALPS
			else if (strcmp(ps->al_name, ATTR_tolerate_node_failures) == 0)
				fprintf(fp, "%s.%s=none\n", EVENT_JOB_OBJECT, ps->al_name);
#endif
			else
				fprintf(fp, "%s.%s=%s\n", EVENT_JOB_OBJECT, ps->al_name, ps->al_value);
		}
	}

	free_attrlist(&phead);
}

/**
 * @brief
 *	Alarm handling function to the set_alarm() call.
 *
 */
static void
run_hook_alarm(void)
{
	run_exit = -3;
}


/**
 * @brief
 *	Print to file pointed to by 'fp', the values in a vnl_t structure 'vp'.
 *
 * @param[in] 	fp - pointer to file to dump output into
 * @param[in]	head_str - some string to prefix outputted data
 * @param[in]	vp - pointer to a vnl_t structure containing data to print out.
 *
 * @note
 * 	vnl_t entry with attribute ATTR_NODE_TopologyInfo is ignored.
 *
 * @return none
 *
 */
void
fprint_vnl(FILE *fp, char *head_str, vnl_t *vp)
{
	char		*p;
	int		i, j;
	char		*attname = NULL;
	char		*attres = NULL;

	if ((fp == NULL) || (head_str == NULL) || (vp == NULL))
		return;

	for (i = 0; i < vp->vnl_used; i++) {
		vnal_t	*vnalp;

		vnalp = VNL_NODENUM(vp, i);

		for (j = 0; j < vnalp->vnal_used; j++) {
			vna_t		*vnap;

			vnap = VNAL_NODENUM(vnalp, j);
			attname = vnap->vna_name;
			if (strcmp(attname, ATTR_NODE_TopologyInfo) == 0) {
				/* ignoring this internal attribute  */
				/* since it tends to be quite a big data, */
				/* plus no support right now in hooks */
				/* to modify this data. */
				continue;
			}
			attres = NULL;
			p = strrchr(vnap->vna_name, '.');
			if (p != NULL) {
				*p = '\0';
				p++;
				attres = p;
			}

			if (attres != NULL) {
				fprintf(fp, "%s[\"%s\"].%s[%s]=%s\n",
					head_str,
					vnalp->vnal_id, attname, attres,
					vnap->vna_val);
			} else {
				fprintf(fp, "%s[\"%s\"].%s=%s\n",
					head_str,
					vnalp->vnal_id, attname,
					vnap->vna_val);
			}
			if (p != NULL)
				*p = '.';	/* restore value */
		}
	}
	fflush(fp);
}

/**
 * @brief
 *	Print to file pointed to by 'fp', the values of each job in the 'joblist'.
 *
 * @param[in] 	fp - pointer to file to dump output into
 * @param[in]	head_str - some string to prefix outputted data
 * @param[in]	joblist - pointer to a  list of jobs.
 *
 * @return none
 *
 */
void
fprint_joblist(FILE *fp, char *head_str, pbs_list_head *joblist)
{
	job		*pjob;
	int		i;
	pbs_list_head	phead;
	attribute	*pattr;
	svrattrl	*psatl;
	svrattrl	*ps;
	char		*jobid;
	int		keeping = 0;
	char		*std_file = NULL;

	if ((fp == NULL) || (head_str == NULL) || (joblist == NULL))
		return;

	for (pjob = (job *)GET_NEXT(*joblist); pjob;
		pjob = (job *)GET_NEXT(pjob->ji_alljobs)) {

		jobid = pjob->ji_qs.ji_jobid;
		/* Now print job attributes and resources */
		CLEAR_HEAD(phead);
		pattr = pjob->ji_wattr;
		for (i=0; i < (int)JOB_ATR_LAST; i++) {
			(void)(job_attr_def+i)->at_encode(pattr+i, &phead,
				(job_attr_def+i)->at_name, NULL,
				ATR_ENCODE_MOM, NULL);
		}
		attrl_fixlink(&phead);

		psatl = (svrattrl *)GET_NEXT(phead);
		for (ps = psatl; ps; ps = (svrattrl *)GET_NEXT(ps->al_link)) {
			if (ps->al_resc != NULL) {
				fprintf(fp, "%s[\"%s\"].%s[%s]=%s\n",
					head_str, jobid, ps->al_name, ps->al_resc,
					ps->al_value);
			} else {
				fprintf(fp, "%s[\"%s\"].%s=%s\n",
					head_str,
					jobid, ps->al_name, ps->al_value);
			}
		}
		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) != 0) {
			fprintf(fp, "%s[\"%s\"]._msmom=True\n",
					head_str, jobid);
		}

		std_file = std_file_name(pjob, StdOut, &keeping);
		fprintf(fp, "%s[\"%s\"]._stdout_file=%s\n",
				head_str, jobid, std_file?std_file:"");
		std_file = std_file_name(pjob, StdErr, &keeping);
		fprintf(fp, "%s[\"%s\"]._stderr_file=%s\n",
				head_str, jobid, std_file?std_file:"");
		free_attrlist(&phead);
	}

}

/**
 * @brief
 *	Add to a vnl_t structure 'p_vnlp' the natural vnode information.
 *
 * @Note
 *	This uses the mom_short_name as natural vnode name.
 *
 * @param[in/out] p_vnlp - the vnl_t structure to put natural vnode info.
 *			 - Be sure to free up the *p_vnlp structure when
 *			 - done as it is malloced.
 *
 * @return none
 *
 */
static void
add_natural_vnode_info(vnl_t **p_vnlp)
{
	char	bufs[BUFSIZ];
	char	*msgbuf;

	if (*p_vnlp == NULL) {
		if (vnl_alloc(p_vnlp) == NULL) {
			log_err(errno, __func__, "Failed to allocate vnlp");
			return;
		}
	}

	snprintf(bufs, sizeof(bufs)-1, "%d", num_pcpus);
	if (vn_addvnr(*p_vnlp, mom_short_name, ATTR_NODE_pcpus, bufs, 0, 0, NULL) == -1) {
		pbs_asprintf(&msgbuf,
			"Failed to add '%s %s=%s' to vnode list",
			mom_short_name, ATTR_NODE_pcpus, bufs);
		log_err(-1, __func__, msgbuf);
		free(msgbuf);
		return;

	}

	snprintf(bufs, sizeof(bufs)-1, "%d", num_acpus);
	if (vn_addvnr(*p_vnlp, mom_short_name, "resources_available.ncpus", bufs, 0, 0, NULL) == -1) {
		pbs_asprintf(&msgbuf,
			"Failed to add '%s %s=%s' to vnode list",
			mom_short_name, "resources_available.ncpus", bufs);
		log_err(-1, __func__, msgbuf);
		free(msgbuf);
		return;

	}

	snprintf(bufs, sizeof(bufs)-1, "%llukb", av_phy_mem);
	if (vn_addvnr(*p_vnlp, mom_short_name, "resources_available.mem", bufs, 0, 0, NULL) == -1) {
		pbs_asprintf(&msgbuf,
			"Failed to add '%s %s=%s' to vnode list",
			mom_short_name, "resources_available.mem", bufs);
		log_err(-1, __func__, msgbuf);
		free(msgbuf);
		return;

	}

	if (vn_addvnr(*p_vnlp, mom_short_name, "resources_available.arch",
		arch(NULL), 0, 0, NULL) == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed to add '%s %s=%s' to vnode list",
			mom_host, "arch", arch(NULL));
		log_err(-1, __func__, log_buffer);
		return;

	}

	if (vn_addvnr(*p_vnlp, mom_short_name, "pbs_version",
		pbs_version, 0, 0, NULL) == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed to add '%s %s=%s' to vnode list",
			mom_short_name, "pbs_version", pbs_version);
		log_err(-1, __func__, log_buffer);
		return;

	}
}

/**
 * @brief
 *	Free any vnl_t structures contained in a hook_vnl_action list and
 *	delete the individual list structure.
 *
 * @param[in]	listh - the head of the list of hook_vnl_action structures
 *
 * @return none
 *
 */
void
vna_list_free(pbs_list_head listh)
{
	struct hook_vnl_action *pvna;
	struct hook_vnl_action *nxt;

	pvna = (struct hook_vnl_action *)GET_NEXT(listh);

	while (pvna != NULL) {
		nxt = (struct hook_vnl_action *)GET_NEXT(pvna->hva_link);
		if (pvna->hva_vnl) {
			vnl_free((vnl_t *)pvna->hva_vnl);
		}
		delete_link(&pvna->hva_link);
		free(pvna);
		pvna = nxt;
	}
}


/**
 * @brief
 *	Copies 'src_file' to 'dest_file' and set 'dest_file' permission
 *	from 'pjob''s data.
 *
 * @param[in]	src_file - the source file to copy
 * @param[in]	dest_file - the destination file
 * @param[in]	pjob - job whose permissions will be used to the
 * 			'dest_file'.
 *
 * @return int
 * @retval 0  - for success
 * @retval -1 - for failure.
 *
 */
static int
copy_file_and_set_owner(char *src_file, char *dest_file, job *pjob) {
	int	st;

	if ((src_file == NULL) || (dest_file == NULL) || (pjob == NULL))
		return -1;

	st = copy_file_internal(src_file, dest_file);

	switch (st) {
		case 0:
			break;
		case COPY_FILE_BAD_INPUT:
			log_err(errno, __func__,
				"copy_file_internal: bad input parameter");
			return -1;
		case COPY_FILE_BAD_SOURCE:
			snprintf(log_buffer, sizeof(log_buffer),
				"Failed to open file %s",
				src_file);
			log_err(errno, __func__, log_buffer);
			return -1;
		case COPY_FILE_BAD_DEST:
			snprintf(log_buffer, sizeof(log_buffer),
				"Failed to open file copy %s",
				dest_file);
			log_err(errno, __func__, log_buffer);
			return -1;
		case COPY_FILE_BAD_WRITE:
			snprintf(log_buffer,
				sizeof(log_buffer),
				"Failed writing to file %s",
				dest_file);
			log_err(errno, __func__, log_buffer);
			return -1;
		default:
			snprintf(log_buffer,
				sizeof(log_buffer),
				"Unknown copy_file_internal return %d",
				st);
			log_err(errno, __func__, log_buffer);
			return -1;
	}
#ifndef WIN32
	if (chown(dest_file,
		pjob->ji_qs.ji_un.ji_momt.ji_exuid,
		pjob->ji_qs.ji_un.ji_momt.ji_exgid) == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			"chown: %s", dest_file);
		log_err(errno, __func__, log_buffer);
		(void)unlink(dest_file);
		return -1;
	}
#else	/* Windows */

	if (secure_file2(dest_file,
		pjob->ji_user->pw_name,
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED,
		"Administrators",
	READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED) \
							      == 0 ) {
		log_err(errno, __func__, log_buffer);
		(void)unlink(dest_file);
		return -1;
	}
#endif

	return 0;
}

/**
 * @brief
 * 	Add to the vnodes list 'vnl' the data found in the vnode-to-mpi-process
 *	mapping array 'vnode_entry', whose number of entries is in 'num_vnodes'.
 *
 * @param[out]		vnl - pointer to the destination list of vnodes.
 * @param[in]		vnode_entry - array of vnode-to-mpi-process mappings.
 * @param[in]		num_vnodes - numbver of entries in 'vnode_entry'.
 * @param[out]		matched_vnode - set to 1 if one of the added vnodes
 * 					matches the natural vnode.
 * @return int
 * @retval 0  - for success
 * @retval -1 - for failure
 *
 */
static	int
vnl_add_vnode_entries(vnl_t *vnl, vmpiprocs *vnode_entry, int num_vnodes,
	int *matched_nvnode)
{
	int	i, rc;
	char	bufs[BUFSIZ];
	char	*msgbuf;
	char	*v_name = NULL;
	int	v_cpus = 0;
	Long	v_mem = 0;
	char    *v_cpus_o_str = NULL;
	char    *v_mem_o_str  = NULL;

	if ((vnl == NULL) || (vnode_entry == NULL) ||
		(matched_nvnode == NULL)) {
		return (0);	/* incomplete input, no need to proceed  */
	}

	for (i=0; i<num_vnodes; i++) {

		v_name = vnode_entry[i].vn_vname;

		/* matching vnode names is case-insensitive, */
		/* see find_nodebyname() where it does strcasecmp() */
		/* Also, we need to use the str case of mom_short_name */
		/* which is the natural vnode key returned by */
		/* pbs.get_local_nodename() */
		if (strcasecmp(v_name, mom_short_name) == 0)
			*matched_nvnode = 1;

		v_cpus = vnode_entry[i].vn_cpus;
		if ((v_cpus_o_str=vn_exist(vnl, v_name,
			RESCASSN_NCPUS)) != NULL) {
			v_cpus += atoi(v_cpus_o_str);
		}

		snprintf(bufs, sizeof(bufs), "%d", v_cpus);
		rc=vn_addvnr(vnl, v_name, RESCASSN_NCPUS, bufs, 0, 0, NULL);
		if (rc == -1) {
			pbs_asprintf(&msgbuf, "%s:failed to add '%s=%s'",
				v_name, RESCASSN_NCPUS, bufs);
			log_err(-1, __func__, msgbuf);
			free(msgbuf);
			return (-1);
		}

		if ((vnode_entry[i].vn_host != NULL) &&
			(vnode_entry[i].vn_host->hn_host != NULL)) {

			snprintf(bufs, sizeof(bufs), "%s", vnode_entry[i].vn_host->hn_host);
			rc = vn_addvnr(vnl, v_name, RESCASSN_HOST, bufs, 0, 0, NULL);
			if (rc == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					"%s:failed to add '%s=%s'",
					v_name, "host", bufs);
				log_err(-1, __func__, log_buffer);
				return (-1);
			}
		}

		v_mem = vnode_entry[i].vn_mem;
		if ((v_mem_o_str =vn_exist(vnl, v_name,
			RESCASSN_MEM)) != NULL) {
			v_mem += atoL(v_mem_o_str);
		}

		snprintf(bufs, sizeof(bufs), "%lldkb", v_mem);
		rc=vn_addvnr(vnl, v_name, RESCASSN_MEM, bufs, 0, 0, NULL);

		if (rc == -1) {
			pbs_asprintf(&msgbuf, "%s:failed add '%s=%s'",
				v_name, RESCASSN_MEM, bufs);
			log_err(-1, __func__, msgbuf);
			free(msgbuf);
			return (-1);
		}
	}
	return (0);
}

/**
 * @brief
 *	Runs the hook 'phook' in a child process in response to 'event_type'
 *	with input parameter 'hook_input'.
 *
 * @param[in]	phook	- pointer to hook being run.
 * @param[in]	event_type - the hook event type (e.g. HOOK_EVENT_EXECJOB_BEGIN)
 * @param[in]	hook_input - struct containing input parameters.
 * @param[in]	req_user - user executing the hook
 * @param[in]	req_host - where the hook is executing
 * @param[in]   parent_wait - if set to 1, parent process will wait for hook to
 *		finish; otherwise, it gets run in the background and when done,
 *		the 'post_func' below will execute.
 *		With parent_wait == 0, file_in, file_out are not filled in.
 * @param[in]   post_func - to function to execute when backgrounded hook
 *		finishes execution.
 *		, parent process will wait for job
 * @param[in/out] file_in 	- be filled in by the hook input file used
 * @param[in/out] file_out	- be filled in by the hook output file used
 * @param[in/out] file_data	- be filled in by the hook server data file used
 * @param[in]	  file_size	- max size of file_in and file_out string
 *				  buffers.
 *
 *
 * @return int		the exit value of the executing hook script
 * @return 0		for success
 * @return != 0		for error
 *
 */
static int
run_hook(hook *phook, unsigned int event_type, mom_hook_input_t *hook_input,
	char *req_user, char *req_host,
	int parent_wait,  void (*post_func)(struct work_task *),
	char *file_in,  char *file_out, char *file_data, size_t file_size)
{

	FILE		*fp = NULL;
	char		in_data[LOG_BUF_SIZE+1];
	char		hook_inputfile[MAXPATHLEN+1];
	char		hook_outputfile[MAXPATHLEN+1];
	char		hook_datafile[MAXPATHLEN+1];
	char		script_copy[MAXPATHLEN+1];
	char		hook_config_copy[MAXPATHLEN+1];
	char		rescdef_copy[MAXPATHLEN+1];
	char		log_file[MAXPATHLEN+1];
	char		*script_file = NULL;
	char		*rescdef_file = NULL;
	int		waitst = 0;
	char		pypath[MAXPATHLEN+1];
	pid_t		child;
	struct stat	sbuf;
	int		runas_jobuser = 0; /* if 1, run as job's euser */
	struct	work_task *ptask;
	vnl_t		*vnl = NULL;
	vnl_t		*vnl_fail = NULL;
	pbs_list_head	*failed_mom_list = NULL;
	pbs_list_head	*succeeded_mom_list = NULL;
	vnl_t		*nv = NULL;
	int		vnl_created = 0;
	job		*pjob = NULL;
	int		matched_nvnode = 0; /* match natural vnode */
	char		*arg[14];
	pid_t		myseq;	/* just some unique sequence number */
	char		logmask[BUFSIZ];
	char		path_hooks_rescdef[MAXPATHLEN+1];
	char		cmdline[2*BUFSIZ+1];
	struct passwd	*pwdp = NULL;
#ifdef WIN32
	FILE		*fp2 = NULL;
	pio_handles	pio;
#endif
	char		*progname = NULL;
	char		**argv = NULL;
	char		**env = NULL;
	char		*env_str = NULL;
	pid_t		pid = -1;
	int		k;
	char		script_path[MAXPATHLEN+1];
	char		hook_config_path[MAXPATHLEN+1];
	char		*p;
	pbs_list_head	*jobs_list = NULL;
	char		*pc;
	int		keeping = 0;
	char		*std_file = NULL;
	char		*msgbuf;
	reliable_job_node	*rjn;

	if ((phook == NULL) || (req_user == NULL) || (req_host == NULL)) {
		log_err(-1, __func__, "Bad input received!");
		return -1;
	}

	if (phook->hook_name == NULL) {
		log_err(-1, __func__, "hook has no name");
		return -1;
	}

	if (phook->script == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			"hook %s has no script value",
			phook->hook_name);
		log_err(-1, __func__, log_buffer);
		return -1;
	}
	if (hook_input == NULL) {
		log_err(-1, __func__, "missing input argument to event");
		return (-1);
	}

	/* Validate input parameters */

	switch (event_type) {
		case HOOK_EVENT_EXECJOB_LAUNCH:
		case HOOK_EVENT_EXECJOB_ATTACH:
			if (event_type == HOOK_EVENT_EXECJOB_LAUNCH) {
				progname = hook_input->progname;
				argv = hook_input->argv;
				env = hook_input->env;
			} else {
				pid = hook_input->pid;

			}
			/* falls through */
		case HOOK_EVENT_EXECJOB_BEGIN:
		case HOOK_EVENT_EXECJOB_RESIZE:
		case HOOK_EVENT_EXECJOB_PROLOGUE:
		case HOOK_EVENT_EXECJOB_PRETERM:
		case HOOK_EVENT_EXECJOB_EPILOGUE:
		case HOOK_EVENT_EXECJOB_END:
			pjob = hook_input->pjob;
			if ((event_type == HOOK_EVENT_EXECJOB_LAUNCH) ||
			    (event_type == HOOK_EVENT_EXECJOB_PROLOGUE)) {
				vnl = (vnl_t *)hook_input->vnl;
				vnl_fail = (vnl_t *)hook_input->vnl_fail;
				failed_mom_list = hook_input->failed_mom_list;
				succeeded_mom_list = hook_input->succeeded_mom_list;
			}
			break;
		case HOOK_EVENT_EXECHOST_PERIODIC:
			jobs_list = hook_input->jobs_list;
			/* falls through */
		case HOOK_EVENT_EXECHOST_STARTUP:
			vnl = hook_input->vnl;
			break;
		default:
			log_err(-1, __func__, "unknown hook event");
			return (-1);
	}

	snprintf(pypath, MAXPATHLEN, "%s/bin/pbs_python",
		pbs_conf.pbs_exec_path);
	run_exit = 0;

	if ((phook->user == HOOK_PBSUSER) && (event_type & USER_MOM_EVENTS))
		runas_jobuser = 1;

#ifndef WIN32
	child = fork();
	if (child > 0) {	/* parent */

		if (!parent_wait) {
			ptask = set_task(WORK_Deferred_Child, child,
				post_func, phook);
			if (!ptask) {
				log_err(errno, __func__, msg_err_malloc);
				return (-1);
			}
			return (0);	/* no hook output file at this time */
		}


		set_alarm(phook->alarm, run_hook_alarm);
		while (waitpid(child, &waitst, 0) < 0) {	/* error on wait */
			if (errno != EINTR) {	/* continue loop on signal */
				run_exit = -5;
				break;
			}
			kill(-child, SIGKILL);
		}
		set_alarm(0, NULL);
		kill(-child, SIGKILL);
		if (run_exit == 0) {
			if (WIFEXITED(waitst)) {
				run_exit = WEXITSTATUS(waitst);
			} else if (WIFSIGNALED(waitst)) {
				run_exit = -4;
			}
		} else {
			snprintf(log_buffer, sizeof(log_buffer),
				"prematurely completed %s, exit=%d",
				((struct python_script *)(phook->script))->path,
				run_exit);
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK, LOG_INFO,
				phook->hook_name, log_buffer);
		}

	} else {		/* child */
		(void)setsid();

		myseq = getpid();
#else	/* Windows */
	myseq = rand();
	child = myseq;
#endif


	run_exit = 255;

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

	strncpy(hook_config_path, ((struct python_script *)phook->script)->path,
		sizeof(hook_config_path)-1);
	p = strstr(hook_config_path, HOOK_SCRIPT_SUFFIX);
	if (p != NULL) {
		/* replace <HOOK_SCRIPT_SUFFIX> with <HOOK_CONFIG_SUFFIX>: */
		/* must copy up to HOOK_SCRIPT_SUFFIX length so as to not */
		/* overflow */
		snprintf(p, sizeof(hook_config_path) - (p - hook_config_path),
			"%s", HOOK_CONFIG_SUFFIX);
		if (stat(hook_config_path, &sbuf) != 0) {
			hook_config_path[0] = '\0';
		}
	} else {
		hook_config_path[0] = '\0';
	}

	if (runas_jobuser) {

		/* use world writtable spool directory */

		snprintf(hook_inputfile, MAXPATHLEN,
			FMT_HOOK_INFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);
		snprintf(hook_outputfile, MAXPATHLEN,
			FMT_HOOK_OUTFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);
		snprintf(hook_datafile, MAXPATHLEN,
			FMT_HOOK_DATAFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);
		snprintf(script_copy, MAXPATHLEN,
			FMT_HOOK_SCRIPT, path_spool, myseq);
		snprintf(hook_config_copy, MAXPATHLEN,
			FMT_HOOK_CONFIG, path_spool, myseq);
		snprintf(rescdef_copy, MAXPATHLEN,
			FMT_HOOK_RESCDEF, path_spool, myseq);
		snprintf(log_file, MAXPATHLEN,
			FMT_HOOK_LOG, path_spool, myseq);

		/*
		 * get the password entry for the user under which the
		 * job is to be run we do this now to save a few things
		 * in the job structure
		 * All this info needed to pre-fetch for becomeuser()
		 * to work.
		 */

		if (pjob == NULL) {
			log_err(-1, __func__, "No job parameter passed!");
			goto run_hook_exit;
		}

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

		if (pjob->ji_grpcache == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				"job %s has no ji_grpcache value",
				pjob->ji_qs.ji_jobid);
			log_err(-1, __func__, log_buffer);
			goto run_hook_exit;
		}

		pjob->ji_qs.ji_un.ji_momt.ji_exuid = \
					pjob->ji_grpcache->gc_uid;
		pjob->ji_qs.ji_un.ji_momt.ji_exgid = \
					pjob->ji_grpcache->gc_gid;

		strncpy(script_path,
			((struct python_script *)phook->script)->path,
			MAXPATHLEN);

		/* copy hook_config_path to user-accessible */
		/* [PBS_HOME]/path_spool. */
		if (hook_config_path[0] != '\0') {
			if (copy_file_and_set_owner(hook_config_path,
				hook_config_copy, pjob) == -1) {
				goto run_hook_exit;
			}
			/* set hook_config_path to hook_config_copy */
			/* if the copying was successful */
			snprintf(hook_config_path, sizeof(hook_config_path),
				"%s", hook_config_copy);
		}
		/* copy script_path to user-accessible */
		/* [PBS_HOME]/path_spool */

		if (copy_file_and_set_owner(script_path, script_copy,
			pjob) == -1) {
			goto run_hook_exit;
		}
		if (stat(path_hooks_rescdef, &sbuf) == 0) {
			if (copy_file_and_set_owner(path_hooks_rescdef,
				rescdef_copy, pjob) == -1) {
				goto run_hook_exit;
			}
			rescdef_file = (char *)rescdef_copy;
		}
		script_file = (char *)script_copy;

#ifndef WIN32
		if (chown(script_file,
			pjob->ji_qs.ji_un.ji_momt.ji_exuid,
			pjob->ji_qs.ji_un.ji_momt.ji_exgid) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				"chown: %s", script_file);
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}
#else	/* Windows */

		if (secure_file2(script_file,
			pjob->ji_user->pw_name,
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED,
			"Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED) \
								      == 0 ) {
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}
#endif

		if ((fp=fopen(hook_inputfile, "w")) == NULL) {
			sprintf(log_buffer,
				"open of input file %s failed!",
				hook_inputfile);
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}

#ifndef WIN32
		if (chown(hook_inputfile,
			pjob->ji_qs.ji_un.ji_momt.ji_exuid,
			pjob->ji_qs.ji_un.ji_momt.ji_exgid) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				"chown: %s", hook_inputfile);
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}

		/* NOTE: "launch" hook is already running as the user */
		if (becomeuser(pjob) != 0) {
			char *jobuser;

			jobuser = pjob->ji_wattr[(int)JOB_ATR_euser].\
								  at_val.at_str;
			snprintf(log_buffer, sizeof(log_buffer),
				"Unable to become user %s!",
				(jobuser?jobuser:"<job euser unset>"));
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}
#else
		if (secure_file2(hook_inputfile,
			pjob->ji_user->pw_name,
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED,
			"Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED) \
								      == 0 ) {
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;

		}

		/* Force create the log file, to secure afterwards */
		if ((fp2=fopen(log_file, "w")) == NULL) {
			sprintf(log_buffer,
				"open of log file %s failed!",
				log_file);
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}
		fclose(fp2);
		fp2 = NULL;

		if (secure_file2(log_file,
			pjob->ji_user->pw_name,
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED,
			"Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED) \
								      == 0 ) {
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}
#endif

		/* chdir to path_spool like a job */
		/* NOTE: For launch hooks, it is already in the */
		/* environment of the job, so we don't want to */
		/* disturb its current working directory. */
		if (chdir(path_spool) != 0) {
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK, LOG_WARNING,
				phook->hook_name,
				"unable to go to spool directory");
		}
	} else { /* run as root */
		snprintf(hook_inputfile, MAXPATHLEN,
			FMT_HOOK_INFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);
		snprintf(hook_outputfile, MAXPATHLEN,
			FMT_HOOK_OUTFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);
		snprintf(hook_datafile, MAXPATHLEN,
			FMT_HOOK_DATAFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, myseq);

		script_file = ((struct python_script *)phook->script)->path;

		if (script_file == NULL) {
			log_event(PBSEVENT_DEBUG,
				PBS_EVENTCLASS_HOOK, LOG_ERR,
				phook->hook_name,
				"No script file found!");
			goto run_hook_exit;
		}

		if (stat(path_hooks_rescdef, &sbuf) == 0)
			rescdef_file = path_hooks_rescdef;

		log_file[0] = '\0';

		if ((fp=fopen(hook_inputfile, "w")) == NULL) {
			sprintf(log_buffer,
				"open of input file %s failed!",
				hook_inputfile);
			log_err(errno, __func__, log_buffer);
			goto run_hook_exit;
		}

#ifdef	WIN32
		secure_file(hook_inputfile, "Administrators",
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif
		/* Still need to chdir() here. A periodic hook may be */
		/* running the hook periodically and may no longer in the */
		/* original working directory */
		if (chdir(path_hooks_workdir) != 0) {
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK, LOG_WARNING, phook->hook_name,
				"unable to go to hooks tmp directory");
		}

	}

	switch (event_type) {
		case HOOK_EVENT_EXECJOB_LAUNCH:
		case HOOK_EVENT_EXECJOB_ATTACH:
			if (event_type == HOOK_EVENT_EXECJOB_LAUNCH) {

				if ((progname != NULL) && (progname[0] != '\0')) {
					fprintf(fp, "%s.%s=%s\n",
						EVENT_OBJECT, PY_EVENT_PARAM_PROGNAME,
						progname);
				}

				if (argv != NULL) {
					k=0;
					while (argv[k]) {
						fprintf(fp, "%s.%s[%d]=%s\n",
							EVENT_OBJECT,
							PY_EVENT_PARAM_ARGLIST, k,
							argv[k]);
						k++;
					}
				}

				env_str = env_array_to_str(env, ',');
				if (env_str != NULL) {
					if (env_str[0] != '\0') {
						fprintf(fp, "%s.%s=\"\"\"%s\"\"\"\n",
							EVENT_OBJECT,
							PY_EVENT_PARAM_ENV,
							env_str);
					}
					free(env_str);
				}
			} else {
				fprintf(fp, "%s.%s=%d\n",
					EVENT_OBJECT, PY_EVENT_PARAM_PID, pid);
			}
			/* fall through */
		case HOOK_EVENT_EXECJOB_BEGIN:
		case HOOK_EVENT_EXECJOB_RESIZE:
		case HOOK_EVENT_EXECJOB_PROLOGUE:
		case HOOK_EVENT_EXECJOB_EPILOGUE:
		case HOOK_EVENT_EXECJOB_END:
		case HOOK_EVENT_EXECJOB_PRETERM:
			if (pjob == NULL) {
				log_err(-1, __func__, "No job parameter passed!");
				goto run_hook_exit;
			}

			/* pass job parameter */
			fprintf_job_struct(fp, pjob);
			if (pjob->ji_qs.ji_svrflags & JOB_SVFLG_CHKPT) {
				fprintf(fp, "%s._checkpointed=True\n",
					EVENT_JOB_OBJECT);
			}
			if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) != 0) {
				fprintf(fp, "%s._msmom=True\n",
					EVENT_JOB_OBJECT);
			}
			std_file = std_file_name(pjob, StdOut, &keeping);
			fprintf(fp, "%s._stdout_file=%s\n",
				EVENT_JOB_OBJECT, std_file?std_file:"");
			std_file = std_file_name(pjob, StdErr, &keeping);
			fprintf(fp, "%s._stderr_file=%s\n",
				EVENT_JOB_OBJECT, std_file?std_file:"");

			/* pass vnode list parameter */
			if (vnl == NULL) {
				int	errcode;

				if (vnl_alloc(&vnl) == NULL) {
					log_err(errno, __func__,
						"Failed to allocate a vnlp structure");
					goto run_hook_exit;
				}
				vnl_created = 1;

				if (pjob->ji_numnodes == 0) {
					/* this executes in a child process */
					if ((errcode = job_nodes(pjob)) != 0) {
						snprintf(log_buffer,
							sizeof(log_buffer),
							"job_nodes failed with error %d",
							errcode);
						log_err(-1, __func__, log_buffer);
						goto run_hook_exit;
					}
				}

				/* This looks into pjob->ji_assn_vnodes[] whose     */
				/* entries map exec_vnode, the vnodes         */
				/* assigned to the job along with the         */
				/* allocated cpus and mem for each vnode.     */
				/* For example, given			      */
				/*   exec_vnode=(hostA[0]:ncpus=3:mem=100kb)+ */
				/*              (hostB[0]:mem=400kb:ncpus=1)+ */
				/*		(hostA[0]:ncpus=2:mem=200kb)  */
				/* vnl would end up getting accumulated       */
				/* entries:      			      */
				/*	(hostA[0],ncpus=5,mem=300kb)	      */
				/*	(hostB[0],ncpus=1,mem=400kb) 	      */

				if ((vnl_add_vnode_entries(vnl, pjob->ji_assn_vnodes,
					pjob->ji_num_assn_vnodes,
					&matched_nvnode) == -1))
					goto run_hook_exit;

				if (matched_nvnode) {
					add_natural_vnode_info(&vnl);
				}
			}
			fprint_vnl(fp, EVENT_VNODELIST_OBJECT, vnl);
			if (vnl_fail != NULL) { 
				fprint_vnl(fp,
				  EVENT_VNODELIST_FAIL_OBJECT, vnl_fail);
			}

			if (failed_mom_list != NULL) {
				rjn = (reliable_job_node *)GET_NEXT(*failed_mom_list);
				while (rjn) {
					fprintf(fp,  "%s=%s\n", JOB_FAILED_MOM_LIST_OBJECT, rjn->rjn_host);
					rjn = (reliable_job_node *)GET_NEXT(rjn->rjn_link);
				}
			}

			if (succeeded_mom_list != NULL) {
				rjn = (reliable_job_node *)GET_NEXT(*succeeded_mom_list);
				while (rjn) {
					fprintf(fp,  "%s=%s\n", JOB_SUCCEEDED_MOM_LIST_OBJECT, rjn->rjn_host);
					rjn = (reliable_job_node *)GET_NEXT(rjn->rjn_link);
				}
			}

			if (vnl_created) {
				vnl_free(vnl);
				vnl_created = 0;
			}
			break;

		case HOOK_EVENT_EXECHOST_PERIODIC:
			fprintf(fp, "%s.%s=%d\n", EVENT_OBJECT, "freq",
				phook->freq);
			add_natural_vnode_info(&nv);
			if (nv == NULL) {
				fprint_vnl(fp, EVENT_VNODELIST_OBJECT, vnl);
			} else {
				if (vnl != NULL) {
					vn_merge(nv, vnl, NULL);
				}
				fprint_vnl(fp, EVENT_VNODELIST_OBJECT, nv);
				vnl_free(nv);
			}

			fprint_joblist(fp, EVENT_JOBLIST_OBJECT, jobs_list);

			break;
		case HOOK_EVENT_EXECHOST_STARTUP:
			if (vnl == NULL) {
				/* create a default vnode_list containing */
				/* the natural vnode, to be used as hook */
				/* input */
				add_natural_vnode_info(&vnl);
				vnl_created = 1;
			}
			fprint_vnl(fp, EVENT_VNODELIST_OBJECT, vnl);
			if (vnl_created) {
				vnl_free(vnl);
				vnl_created = 0;
				vnl = NULL;
			}
			break;
		default:
			snprintf(log_buffer, sizeof(log_buffer),
				"Unknown event type %d", event_type);
			log_err(-1, __func__, log_buffer);
			goto run_hook_exit;
	}

	fprintf(fp, "%s.%s=%s\n", PBS_OBJ, GET_NODE_NAME_FUNC,
		(char *)mom_short_name);
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_TYPE,
		hook_event_as_string(event_type));
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_HOOK_NAME,
		phook->hook_name);
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_HOOK_TYPE,
		hook_type_as_string(phook->type));
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, "requestor",
		req_user);
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, "requestor_host",
		req_host);
	fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, "user", hook_user_as_string(phook->user));
	fprintf(fp, "%s.%s=%d\n", EVENT_OBJECT, "alarm", phook->alarm);

	if (phook->debug) {
		fprintf(fp, "%s.%s=%s\n", EVENT_OBJECT, "debug", hook_datafile);
	}

	fclose(fp);
	fp = NULL;

	arg[0] = (char *)pypath;
	arg[1] = "--hook";
	arg[2] = "-i";
	arg[3] = (char *)hook_inputfile;
	arg[4] = "-o";
	arg[5] = (char *)hook_outputfile;

	if (log_file[0] == '\0') {
		arg[6] = "-L";
		arg[7] = (char *)path_log;
	} else {
		arg[6] = "-l";
		arg[7] = (char *)log_file;
	}
	arg[8] = "-e";
	snprintf(logmask, sizeof(logmask)-1, "%ld", *log_event_mask);
	arg[9] = (char *)logmask;

	if (rescdef_file != NULL) {
		arg[10] = "-r";
		arg[11] = (char *)rescdef_file;
		arg[12] = script_file;
		snprintf(cmdline, sizeof(cmdline),
			"%s %s %s %s %s %s %s %s %s %s %s %s %s",
			arg[0], arg[1], arg[2], arg[3], arg[4], arg[5],
			arg[6], arg[7], arg[8], arg[9], arg[10], arg[11],
			arg[12]);
		arg[13] = NULL;
	} else {
		arg[10] = script_file;
		snprintf(cmdline, sizeof(cmdline),
			"%s %s %s %s %s %s %s %s %s %s %s",
			arg[0], arg[1], arg[2], arg[3], arg[4], arg[5],
			arg[6], arg[7], arg[8], arg[9], arg[10]);
		arg[11] = NULL;

	}
	pbs_asprintf(&msgbuf, "execve %s runas_jobuser=%d in child pid=%d",
		cmdline, runas_jobuser, myseq);
	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO,
		phook->hook_name, msgbuf);
	free(msgbuf);

	if (hook_config_path[0] == '\0') {
#ifdef WIN32
		/* since under Windows, this is still main mom (not forked), */
		/* need to unset the hook config environment variable. */
		setenv(PBS_HOOK_CONFIG_FILE, NULL, 1);
#endif /* WIN32 */
	} else if (setenv(PBS_HOOK_CONFIG_FILE, hook_config_path, 1) != 0) {
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name, "Failed to set PBS_HOOK_CONFIG_FILE");
		return (-1);
	}

#ifndef WIN32
	/* We're passing the calling process' (mom's) environment  */
	/* to python hook execution here, so as to match what's    */
	/* done on Windows. Also, this allows us to control 	   */
	/* pbs_python's execution environment via PBS'             */
	/* pbs_environment file (better security). 		   */
	/* We can provide a  workaround, in case pbs_python does   */
	/* not execute unless a particular variable is set,        */
	/* perhaps due to an incorrectly setup host.		   */

	if (pbs_conf.pbs_conf_file != NULL) {
		if (setenv("PBS_CONF_FILE", pbs_conf.pbs_conf_file, 1) != 0 ) {
			log_err(errno, __func__, "Failed to set PBS_CONF_FILE");
			goto run_hook_exit;
		}
	}

	execve(pypath, arg, environ);
run_hook_exit:
	if (fp != NULL) {
		fclose(fp);
		fp = NULL;
	}
	if (vnl_created) {
		vnl_free(vnl);
	}
	log_err(-1, __func__, "execv of hook");
	exit(run_exit);
}
#else
	if (!parent_wait) {
		if (win_popen(cmdline, "w", &pio, NULL) == 0) {
			errno = GetLastError();
			pbs_errno = errno;
			(void)sprintf(log_buffer,
				"executing %s failed errno=%d", cmdline, errno);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, __func__, log_buffer);

			win_pclose(&pio);
			return (-1);
		}
		ptask = set_task(WORK_Deferred_Child,
			(long)pio.pi.hProcess, post_func, phook);
		if (!ptask) {
			log_err(errno, __func__, msg_err_malloc);
			win_pclose(&pio);
			return (-1);
		}
		ptask->wt_aux2 = myseq;
		addpid(pio.pi.hProcess);
		win_pclose2(&pio); /* closes all handles except the process handle */
		return (0);	/* no hook output file at this time */
	} else if (runas_jobuser) {

		if (pwdp == NULL) {
			log_err(-1, __func__,
				"runas_jobuser does not have credential set!");
			run_exit = 255;
			goto run_hook_exit;
		}
		(void)win_alarm(phook->alarm, run_hook_alarm);
		run_exit = wsystem(cmdline, pwdp->pw_userlogin);
		(void)win_alarm(0, NULL);
	} else {
		/* The following blocks until after */
		(void)win_alarm(phook->alarm, run_hook_alarm);
		if (win_popen(cmdline, "r", &pio, NULL) == 0) {
			errno = GetLastError();
			pbs_errno = errno;
			(void)sprintf(log_buffer,
				"executing %s failed errno=%d", cmdline, errno);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, __func__, log_buffer);

		} else if ((GetExitCodeProcess(pio.pi.hProcess,
			&run_exit) == 0) ||
			(run_exit == STILL_ACTIVE)) {
			log_err(-1, __func__, "GetExitCodeProcess");
			run_exit = 255;
		}
		win_pclose(&pio);
		(void)win_alarm(0, NULL);
	}
run_hook_exit:
	if (fp != NULL)
		fclose(fp);

	if (vnl_created) {
		vnl_free(vnl);
	}

#endif

	if (run_exit != 0) {
		snprintf(log_buffer, sizeof(log_buffer), "execv of %s resulted in nonzero exit status=%d", pypath, run_exit);
		log_err(-1, __func__, log_buffer);

	}


	if (file_in != NULL)
		snprintf(file_in, file_size,
			FMT_HOOK_INFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, child);
	if (file_out != NULL)
		snprintf(file_out, file_size,
			FMT_HOOK_OUTFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, child);

	if (file_data != NULL)
		snprintf(file_data, file_size,
			FMT_HOOK_DATAFILE, path_hooks_workdir,
			hook_event_as_string(event_type),
			phook->hook_name, child);

	if (runas_jobuser) {

		/* move [PATH_SPOOL]/<hook input file> to <file_in> */
		/* where <file_in> is in [PBS_HOME]/mom_priv/hooks/tmp. */
		snprintf(hook_inputfile, MAXPATHLEN,
			FMT_HOOK_INFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, child);
		if ((stat(hook_inputfile, &sbuf) == 0) &&
			(file_in != NULL)) {
			(void)rename(hook_inputfile, file_in);
		}

		/* move [PATH_SPOOL]/<hook output file> to <file_out> */
		/* where <file_out> is in [PBS_HOME]/mom_priv/hooks/tmp. */
		snprintf(hook_outputfile, MAXPATHLEN,
			FMT_HOOK_OUTFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, child);
		if ((stat(hook_outputfile, &sbuf) == 0) &&
			(file_out != NULL)  ) {
			(void)rename(hook_outputfile, file_out);
		}

		/* move [PATH_SPOOL]/<hook data file> to <file_data> */
		/* where <file_data> is in [PBS_HOME]/mom_priv/hooks/tmp. */
		snprintf(hook_datafile, MAXPATHLEN,
			FMT_HOOK_DATAFILE, path_spool,
			hook_event_as_string(event_type),
			phook->hook_name, child);
		if ((stat(hook_datafile, &sbuf) == 0) &&
			(file_data != NULL)) {
			(void)rename(hook_datafile, file_data);
		}
		strncpy(script_path,
			((struct python_script *)phook->script)->path,
			MAXPATHLEN);
		pc = strrchr(script_path, '/');
		if (pc == NULL) {
			pc = (char *)script_path;
		} else {
			pc++;
		}

		/* delete [PATH_SPOOL]/<hook script file copy> */
		snprintf(script_copy, MAXPATHLEN,
				FMT_HOOK_SCRIPT, path_spool, child);

		if (stat(script_copy, &sbuf) == 0)
			(void)unlink(script_copy);

		/* delete [PATH_SPOOL]/<hook config file copy> */
		snprintf(hook_config_copy, MAXPATHLEN,
				FMT_HOOK_CONFIG, path_spool, child);

		if (stat(hook_config_copy, &sbuf) == 0) {
			(void)unlink(hook_config_copy);
		}

		/* delete [PATH_SPOOL]/<resourcedef copy> */
		snprintf(rescdef_copy, MAXPATHLEN,
				FMT_HOOK_RESCDEF, path_spool, child);

		if (stat(rescdef_copy, &sbuf) == 0)
			(void)unlink(rescdef_copy);

		snprintf(log_file, MAXPATHLEN,
			FMT_HOOK_LOG, path_spool, child);

		/* Log file generated in [PATH_SPOOL] should be appended to */
		/* main mom_logs. */
		if ((fp=fopen(log_file, "r")) != NULL) {
			size_t	ll;
			char	*p;
			char	*jobid = NULL;
			int	semicolons;

			jobid = pjob->ji_qs.ji_jobid;

			while( fgets(in_data, sizeof(in_data), fp) != \
								NULL ) {
				ll = strlen(in_data);
				if (in_data[ll-1] == '\n')
					/* remove newline */
					in_data[ll-1] = '\0';

				/* Format of pbs logfile is as follows: */
				/* <time>;<event>;<prog>;<class>;<obj>;<msg> */
				/* There are 5 semicolons before <msg>, */
				/* which is the one we need to get. */
				p = in_data;
				semicolons = 0;
				while( *p != '\0') {
					if (*p == ';') {
						semicolons++;
						if (semicolons == 5)
							break;
					}
					p++;
				}

				if (*p != '\0') {
					*p = '\0';
					p++;
					if (*p  != '\0') {

						if (strstr(in_data, ";Job;") != NULL)
							log_event(PBSEVENT_JOB,
								PBS_EVENTCLASS_JOB, LOG_DEBUG,
								jobid?jobid:"", p);
						else
						log_event(PBSEVENT_ADMIN|\
					   		PBSEVENT_SYSTEM,
								PBS_EVENTCLASS_HOOK,
								LOG_DEBUG,
								"pbs_python", p);
					}
				}
			}
			fclose(fp);
			if (unlink(log_file) == -1) {
				log_err(errno, __func__, log_file);
			}
		}
	}

#ifdef WIN32
	secure_file(file_out, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif


	return (run_exit);
}

/**
 * @brief
 *	Free the malloced "path" elemen of py_script.
 *
 * @param[in] py_script - the python_script structure containing the path element.
 *
 * @return Void
 *
 */
void
python_script_free(struct python_script *py_script)
{

	if (py_script != NULL) {
		if (py_script->path != NULL) {
			free(py_script->path);
		}
	}
}

/**
 * @brief
 *	Creates a path entry to *py_script using 'script_path' as value.
 *
 * @param[in]	script_path - the value to use
 * @param[in] 	py_script - the python_script being instantiated.
 *
 * @Note
 *	If *py_script is not NULL (previously allocated), then
 *	free up previously malloced entries, and reset *py_script to NULL.
 *
 * @return int
 * @retval	0	- for success
 * @retval	-1	- for failure
 *
 */

int
python_script_alloc(const char *script_path, struct python_script **py_script)
{

	struct python_script *tmp_py_script = NULL;
	size_t nbytes = sizeof(struct python_script);
	struct stat sbuf;

	if ((script_path == NULL) || (py_script == NULL)) {
		log_err(-1, __func__, "Bad input parameters");
		return (-1);
	}

	if ((stat(script_path, &sbuf) == -1)) {
		return (0);	/* could not stat script, so nothing to set */
	}

	if (*py_script != NULL) {
		if ((*py_script)->path != NULL) {
			free((*py_script)->path);
		}
		free(*py_script);
	}

	*py_script = NULL; /* init, to be safe */

	if (!(tmp_py_script = (struct python_script *) malloc(nbytes))) {
		log_err(errno, __func__, "failed to malloc struct python_script");
		goto python_script_alloc_exit;
	}
	(void) memset(tmp_py_script, 0, nbytes);

	tmp_py_script->path = strdup(script_path);
	if (tmp_py_script->path == NULL) {
		log_err(errno, __func__, "failed to malloc path");
		goto python_script_alloc_exit;
	}

	/* ok, we are set with py_script */
	*py_script = tmp_py_script;
	return 0;

python_script_alloc_exit:
	if (tmp_py_script != NULL) {
		if (tmp_py_script->path != NULL) {
			free(tmp_py_script->path);
		}
		free(tmp_py_script);
		tmp_py_script = NULL;
	}
	return -1;
}


/**
 * @brief
 *	Runs a periodic hook in the background.
 *
 * @param[in] 	phook - hook to run.
 *
 * @return none
 *
 */
void
run_periodic_hook_bg(hook *phook)
{
	int rc;
	mom_hook_input_t hook_input;

	if (phook == NULL) {
		log_err(-1, "run_periodic_hook_bg", "bad input parameter");
		return;
	}

	if (phook->enabled == FALSE) {
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name,
			"periodic hook has been disabled. Skipping hook");
		return;
	}

	if (phook->script == NULL) {
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name,
			"Hook has no script content. Skipping hook.");
		return;
	}


	/* Run the hook in a child process, and when done, */
	/* execute post_periodic_hook() */
	/* A copy of the system 'vnlp' is passed here */
	/* hook will execute in a child process, and even though */
	/* it may make changes to 'vnlp', it's only in the child */
	/* that will have this copy */

	/* this is the list of vnodes seen by a periodic hook including */
	/* those added by the exechost_startup hook */
	mom_hook_input_init(&hook_input);
	hook_input.vnl = (vnl_t *)vnlp;
	hook_input.jobs_list = &svr_alljobs;

	rc = run_hook(phook, HOOK_EVENT_EXECHOST_PERIODIC, &hook_input,
		PBS_MOM_SERVICE_NAME, mom_host, 0,
		post_periodic_hook,
		NULL, NULL, NULL, 0);
	if (rc != 0) {
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name,
			"Failed to deploy periodic hook");
	}
}

/**
 * @brief
 *	Runs the hook hanging off a work task.
 *
 * @param[in] 	work_task -  task to process.
 *
 * @return none
 *
 */
static void
run_periodic_hook_bg_task(struct work_task *ptask)
{
	hook	 *phook = (hook *)ptask->wt_parm1;


	if (phook == NULL) {
		log_err(-1, __func__, "A hook has disappeared.");
		return;	/* no hook to execute */
	}

	run_periodic_hook_bg(phook);

}

/**
 * @brief
 *	Get the results from 'input_file' of a previously run hook.
 *
 * @param[in] 		input_file -  file to process.
 * @param[in/out] 	accept_flag -  return 1 if event accept flag is true.
 * @param[in/out] 	reject_flag -  return 1 if event reject flag is true.
 * @param[in/out] 	reject_msg -  the reject message if reject_flag is 1.
 * @param[in]		reject_msg_size -  size of reject_msg buffer.
 * @param[out] 		reject_rerunjob -  return 1 if job is to be rerun.
 * @param[out] 		reject_deletejob -  return 1 if job is to be deleted.
 * @param[in/out] 	reboot_flag -  return 1 if pbs should reboot host
 * @param[in/out] 	reboot_cmd -  the command to use by pbs to reboot host.
 * @param[in]		reboot_cmd_size -  size of reboot_cmd buffer.
 * @param[in/out] 	pvnalist -  a linked list of any resultant vnodes list
 * @param[in/out] 	pjob -  job in question, where if present (not NULL),
 *			        it gets filled in with the
 *				"pbs.event().job" entries in 'input_file'.
 *				'pjob' can be NULL in periodic hooks, since
 *				 periodic hooks are not tied to jobs.
 *				 Note that pbs.event().job_list[<jobid>] entries
 *				 in 'input_file' fill in the individual
 *				 <jobid>'s job struct entry in the system, and
 *				 not the passed 'pjob' structure.
 * @param[in]		phook -  hook that executed of which we're getting the
 *				results. If non-NULL, then phook->user is
 *				used to validate 'pbs.event().job.euser' line
 *				in 'input_file'.
 *				If main Mom is reading a job related hook
 *				results file, phook will be null; an entry in
 *				the file should give us the hook name from which
 *				phook is found.
 * @param[in]		copy_file - copy the results file to one who name has
 *				the job id appended.  This is done when in a
 *				child process of Mom.
 * @param[out]		hook_output - struct of parameters to fill in output.
 *
 * @note
 *	If "copy_file" is true, then the processed 'input_file' lines are
 *	saved under the file name:
 *		hook_output<pjob's job-id>
 *	under the same directory where original 'input_file' is located.
 *	One and only one hook's output is being processed in this case.
 *	"Processed" here means the input line has passed some sanity checks, and
 *	deemed to be a valid input line.
 *	This new hook job output file can later be consulted by
 *	send_obit() and record_finish_exec() to retrieve job and vnl updates
 *	made by a hook in a child process.  It is in this case that a line
 *	containing the hook name will appear.  Multiple hooks of the same event
 *	type may be concatenated.
 *
 * @return int
 * @retval	0 for success
 * @retval	non-zero for failure;  the returned parameters (accept_flag,
 *		reject_flag, reject_rerunjob, reject_deletejob, reboot_flag,
 *		reboot_cmd, pvnalist and pjob) may be invalid and should be
 *		ignored.   The list pvnalisst could have mallocate space and
 *		should be freed by the calling program.
 *
 */
int
get_hook_results(char *input_file, int *accept_flag, int *reject_flag,
	char *reject_msg, int reject_msg_size, int *reject_rerunjob,
	int *reject_deletejob, int *reboot_flag, char *reboot_cmd,
	int reboot_cmd_size, pbs_list_head *pvnalist, job *pjob,
	hook *phook, int copy_file, mom_hook_output_t *hook_output) {

	char *name_str;
	char *resc_str;
	char *obj_name;
	char *data_value;
	char *vname_str;
	char *jobid_str;
	int  rc = -1;
	char *pc, *pc1, *pc2, *pc3, *pc4, *pc5;
	char *in_data = NULL;
	size_t ll;
	FILE *fp;
	char *p;
	int   vn_obj_len = strlen(EVENT_VNODELIST_OBJECT);
	int   vn_fail_obj_len = strlen(EVENT_VNODELIST_FAIL_OBJECT);
	int   job_obj_len = strlen(EVENT_JOBLIST_OBJECT);
	int   index;
	attribute_def	*pdef;
	int	errcode;
	vnl_t	*hvnlp = NULL;
	vnl_t	*hvnlp_fail = NULL;
	char	hook_job_outfile[MAXPATHLEN+1];
	FILE	*fp2 = NULL;
	char	*line_data = NULL;
	int	line_data_sz;
	long int endpos;
	int	start_new_vnl = 1;
	int	start_new_vnl_fail = 1;
	struct hook_vnl_action *pvna;
	char hook_euser[PBS_MAXUSER+1] = {'\0'};
	char	*value_type = NULL;
	job	*pjob2 = NULL;
	job	*pjob2_prev = NULL;
	int	found_rerunjob_action = 0;
	int	found_deletejob_action = 0;
	int	found_joblist = 0;
	int	arg_list_entries = 0;
	int	b_triple_quotes = 0;
	int	e_triple_quotes = 0;
	char	buf_data[STRBUF];

	/* Preset hook_euser for later.  If we are reading a job related     */
	/* copy of hook results, there will be one or more (one per hook)    */
	/* pbs_event().hook_euser=<value> entries.  In that case, hook_euser */
	/* is reset to the <value>.  A null string <value> means PBSADMIN.   */
	if (phook && pjob &&  (phook->user == HOOK_PBSUSER)) {
		strncpy(hook_euser,
			pjob->ji_wattr[(int)JOB_ATR_euser].at_val.at_str,
			PBS_MAXUSER);
	}

	if ((input_file != NULL) && (*input_file != '\0')) {
		fp = fopen(input_file, "r");

		if (fp == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				"failed to open input file %s", input_file);
			log_err(errno, __func__, log_buffer);
			return (1);
		}
	} else {
		log_err(PBSE_INTERNAL, __func__, "bad input_file parameter");
		return (1);
	}

	/* if called by a child of Mom, then process the input file and    */
	/* copy it to a new file based on the job id.  Thus the main Mom   */
	/* process can read it and send any updates required to the Server */
	if (copy_file) {
		/* 'pjob' is used to name the file, and phook->user is used */
		/* to validate 'pbs.event().job.euser" line in  */
		/* 'input_file'. */
		char *p;
		char chr_save = '\0';
		char *p_dir = NULL;

		if ((pjob == NULL) || (phook == NULL)) {
			log_err(PBSE_INTERNAL, __func__, "bad copy_file parameter");
			return (1);
		}

		p = strrchr(input_file, '/');
		if (p != NULL) {
			p++;
			chr_save = *p;
			*p = '\0';
			p_dir = (char *)input_file;
		}

		snprintf(hook_job_outfile, MAXPATHLEN,
			FMT_HOOK_JOB_OUTFILE, p_dir?p_dir:"",
			pjob->ji_qs.ji_jobid);

		if (chr_save != '\0')
			*p = chr_save;  /* restore */

		fp2 = fopen(hook_job_outfile, "a");
		if (fp2 == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				"failed to open hook_job_outfile=%s",
				hook_job_outfile);
			log_err(errno, __func__, log_buffer);
			fclose(fp);
			(void)unlink(input_file);
			return (1);
		}
		/* first record for each hook is one holding the euser  */
		/* as which the hook was executed.  This also indicates */
		/* the start of a new hook stanza in the file.          */
		fprintf(fp2, "%s=%s\n", EVENT_HOOK_EUSER, hook_euser);
	}


	line_data_sz = STRBUF;
	line_data = (char *)malloc((size_t)line_data_sz);
	if (line_data == NULL) {
		log_err(errno, __func__, "malloc failed");
		rc = 1;
		goto get_hook_results_end;
	}
	line_data[0] = '\0';

	if (fseek(fp, 0, SEEK_END) != 0) {
		log_err(errno, __func__, "fseek to end failed");
		rc = 1;
		goto get_hook_results_end;
	}

	endpos = ftell(fp);
	if (fseek(fp, 0, SEEK_SET) != 0) {
		log_err(errno, __func__, "fseek to beginning failed");
		rc = 1;
		goto get_hook_results_end;
	}

	pjob2 = pjob;
	while (fgets(buf_data, STRBUF, fp) != NULL) {
		b_triple_quotes = 0;
		e_triple_quotes = 0;

		if (pbs_strcat(&line_data, &line_data_sz, buf_data) == NULL) {
			goto get_hook_results_end;
		}
		if (in_data != NULL) {
			free(in_data);
		}
		in_data = strdup(line_data); /* preserve line_data */
		if (in_data == NULL) {
			log_err(errno, __func__, "strdup failed");
			rc = 1;
			goto get_hook_results_end;
		}

		if ((p = strchr(in_data, '=')) != NULL) {
			b_triple_quotes = starts_with_triple_quotes(p+1);
		}

		ll = strlen(in_data);
		if (in_data[ll-1] == '\n') {
			e_triple_quotes = ends_with_triple_quotes(in_data, 0);

			if (b_triple_quotes && !e_triple_quotes) {
				int	jj;

				while (fgets(buf_data, STRBUF, fp) != NULL) {
					if (pbs_strcat(&line_data, &line_data_sz,
						buf_data) == NULL) {
						goto get_hook_results_end;
					}

					jj = strlen(line_data);
					if ((line_data[jj - 1] != '\n') &&
					    (ftell(fp) != endpos)) {
						/* get more input for
						 * current item.
						 */
						continue;
					}
					e_triple_quotes =
					  ends_with_triple_quotes(line_data, 0);

					if (e_triple_quotes) {
						break;
					}
				}

				if ((!b_triple_quotes && e_triple_quotes) ||
					(b_triple_quotes && !e_triple_quotes)) {
					snprintf(log_buffer, sizeof(log_buffer),
						"unmatched triple quotes! Skipping  line %s",
						in_data);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					/* process a new line */
					line_data[0] = '\0';
					continue;
				}
				free(in_data);
				in_data = strdup(line_data); /* preserve line_data */
				if (in_data == NULL) {
					log_err(errno, __func__, "strdup failed");
					rc = 1;
					goto get_hook_results_end;
				}
				/* remove newline */
				in_data[strlen(in_data)-1] = '\0';
			} else {
				/* remove newline */
				in_data[ll-1] = '\0';
			}

		} else if (ftell(fp) != endpos) { /* continued on next line */
			/* get more input for current item.  */
			continue;
		}

		data_value = NULL;
		if ((p=strchr(in_data, '=')) != NULL) {
			*p = '\0';
			p++;
			while (isspace(*p))
				p++;

			if (b_triple_quotes) {
				/* strip triple quotes */
				p += 3;
			}
			data_value = p;
			if (e_triple_quotes) {
				ends_with_triple_quotes(p, 1);
			}
		}

		obj_name = in_data;

		pc = strrchr(in_data, '.');
		if (pc) {
			*pc = '\0';
			pc++;
		} else {
			pc = in_data;
		}
		name_str = pc;

		pc1 = strchr(pc, '[');
		pc2 = strchr(pc, ']');
		resc_str = NULL;
		if (pc1 && pc2 && (pc2 > pc1)) {
			*pc1 = '\0';
			pc1++;
			*pc2 = '\0';
			pc2++;

			/* now let's if there's anything quoted inside */
			pc3 = strchr(pc1, '"');
			if (pc3 != NULL)
				pc4 = strchr(pc3+1, '"');
			else
				pc4 = NULL;

			if (pc3 && pc4 && (pc4 > pc3)) {
				pc3++;
				*pc4 = '\0';
				resc_str = pc3;
			} else {
				resc_str = pc1;
			}
		}

		/* at this point, we have */
		/* Given:  pbs.event().<attribute>=<value> */
		/* Given:  pbs.event().job.<attribute>=<value> */
		/* Given:  pbs.event().job.<attribute>[<resc>]=<value> */
		/* Given:  pbs.event().vnode_list[<vname>].<attribute>=<value> */
		/* Given:  pbs.event().vnode_list[<vname>].<attribute>[<resc>]=<value> */
		/* We get: */

		/* obj_name = pbs.event() or "pbs.event().job" or "pbs.event().vnode_list[<vname>]" */
		/* name_str = <attribute> */
		/* resc_str = <resc> */
		/* data_value = <value> */

		if (data_value == NULL) {

			snprintf(log_buffer, sizeof(log_buffer),
				"%s: no value given", in_data);
			log_err(errno, __func__, log_buffer);
			rc = 1;
			goto get_hook_results_end;
		}

		if (strcmp(obj_name, EVENT_OBJECT) == 0) {
			if (strcmp(name_str, "hook_euser") == 0) {
				strncpy(hook_euser, data_value, PBS_MAXUSER);
				start_new_vnl = 1;
				/* Need to also clear 'hvnlp' as previous  */
				/* one would have already been saved in */
				/* svr_hook_vnl_actions structure via */
				/* hook_requests_to_server(). Otherwise, */
				/* it would be bad if the same 'hvnlp' */
				/* gets duplicated in svr_hook_vnl_actions */
				/* which could cause a mom crash. */
				hvnlp = NULL;
				hvnlp_fail = NULL;
			} else if ((accept_flag != NULL) &&
				strcmp(name_str, "accept") == 0) {
				if (strcmp(data_value, "True") == 0)
					*accept_flag = 1;
				else
					*accept_flag = 0;
			} else if ((reject_flag != NULL) &&
				strcmp(name_str, "reject") == 0) {

				if (strcmp(data_value, "True") == 0)
					*reject_flag = 1;
				else
					*reject_flag = 0;
			} else if ((reject_msg != NULL) &&
				(strcmp(name_str, "reject_msg") == 0)) {
				strncpy(reject_msg, data_value,
					reject_msg_size-1);
			} else if (strcmp(name_str, PY_EVENT_PARAM_PROGNAME) == 0) {
				char	**prog;
				if (hook_output != NULL) {
					/* need to free up here previous value */
					/* in case of multiple hooks! */
					prog = hook_output->progname;
					if (*prog != NULL) {
						free(*prog);
					}
					*prog = strdup(data_value);
				}
			} else if (strcmp(name_str, PY_EVENT_PARAM_ARGLIST) == 0) {
				pbs_list_head *ar_list;

				arg_list_entries++;
				if (hook_output != NULL) {
					ar_list = hook_output->argv;
					/* free previous values at start of new list */
					if (arg_list_entries == 1) {
						free_attrlist(ar_list);
					}
					add_to_svrattrl_list(ar_list, name_str, resc_str,
									data_value, 0, NULL);
				}
			} else if (strcmp(name_str, PY_EVENT_PARAM_ENV) == 0) {
				if (hook_output != NULL) {
					if (hook_output->env != NULL) {
						free_str_array(hook_output->env);
					}
					hook_output->env = str_to_str_array(data_value, ',');
				}
			}
		} else if ((strcmp(obj_name, EVENT_JOB_OBJECT) == 0) || \
				(strncmp(obj_name, EVENT_JOBLIST_OBJECT, job_obj_len)) == 0) {

			if (strncmp(obj_name, EVENT_JOBLIST_OBJECT, job_obj_len) == 0) {
				/* NOTE: obj_name here is: pbs.event().job_list[<jobid>] */

				/* important here to look for the leftmost '[' (using strchr)
				 * and the rightmost ']' (using strrchr)
				 * as we can have:
				 *	pbs.event().job_list["23.fest"].<attr>=<val>
				 * 	and "23.fest" is a valid job id.
				 */

				found_joblist = 1;
				if (((pc1=strchr(obj_name, '[')) != NULL) &&
					((pc2=strrchr(obj_name, ']')) != NULL) &&
					(pc2 > pc1)) {
					pc1++; /*  pc1=<jobid>] */
					*pc2 = '\0'; /* pc1=<jobid>  */
					pc2++;

					/* now let's if there's anything quoted inside */
					pc3 = strchr(pc1, '"');
					if (pc3 != NULL)
						pc4 = strchr(pc3+1, '"');
					else
						pc4 = NULL;

					if (pc3 && pc4 && (pc4 > pc3)) {
						pc3++;
						*pc4 = '\0';
						jobid_str = pc3;
					} else {
						jobid_str = pc1;
					}
				} else {
					snprintf(log_buffer, sizeof(log_buffer),
						"object '%s' does not have a job id!",
						obj_name);
					log_err(-1, __func__, log_buffer);
					/* process a new line */
					in_data[0] = '\0';
					continue;
				}

				/* jobs list are stored sorted. If current */
				/* jobid does not match previous jobid, */
				/* this means we've now switched to the */
				/* new job's data. */
				if ((pjob2_prev != NULL) &&
					(strcmp(pjob2_prev->ji_qs.ji_jobid,
					jobid_str) == 0)) {
					pjob2 = pjob2_prev; /* optimize */
				} else {
					/* working on a new list of job data */
					if (pjob2_prev != NULL) {
						if (*reject_deletejob) {
							/* deletejob takes */
							/* precedence */
							new_job_action_req(pjob2,
							phook?phook->user:\
							   HOOK_PBSADMIN,
								JOB_ACT_REQ_DELETE);
						} else if (*reject_rerunjob) {
							new_job_action_req(pjob2,
							phook?phook->user:\
								HOOK_PBSADMIN,
								JOB_ACT_REQ_REQUEUE);
						}
						/* already sent the action */
						found_rerunjob_action = 0;
						found_deletejob_action = 0;
					}
					pjob2 = find_job(jobid_str);
					pjob2_prev = pjob2;
					if (reject_rerunjob != NULL)
						*reject_rerunjob = 0;
					if (reject_deletejob != NULL)
						*reject_deletejob = 0;
					found_rerunjob_action = 0;
					found_deletejob_action = 0;
				}
			} else {
				/* not a EVENT_JOBLIST object. Switch */
				/* back to passed job object */
				pjob2 = pjob;
			}
			/* Found: <resource_name>,<job_value_type> */
			if (resc_str != NULL) {
				if ((pc5=strchr(resc_str, ',')) != NULL) {
					*pc5 = '\0';
					pc5++;
					value_type = pc5;

				}
			}

			if (strcmp(name_str, "_rerun") == 0) {
				found_rerunjob_action = 1;
				if (reject_rerunjob == NULL) {
					/* process a new line */
					line_data[0] = '\0';
					continue;
				}
				if (strcmp(data_value, "True") == 0)
					*reject_rerunjob = 1;
				else
					*reject_rerunjob = 0;
			} else if (strcmp(name_str, "_delete") == 0) {
				found_deletejob_action = 1;
				if (reject_deletejob == NULL) {
					/* process a new line */
					line_data[0] = '\0';
					continue;
				}
				if (strcmp(data_value, "True") == 0)
					*reject_deletejob = 1;
				else
					*reject_deletejob = 0;
			} else if (pjob2 != NULL) {
				/* Is attribute not writeable by manager or */
				/* by a server? */
				/* Exempt attributes set by the hook script */
				resc_access_perm = ATR_DFLAG_USWR | \
					    		ATR_DFLAG_OPWR | \
					    		ATR_DFLAG_MGWR | \
				            		ATR_DFLAG_SvWR | \
					    		ATR_DFLAG_Creat;

				/* identify the attribute by name */
				index = find_attr(job_attr_def, name_str,
					JOB_ATR_LAST);
				if (index < 0) { /* didn`t recognize the name */
					snprintf(log_buffer, sizeof(log_buffer),
						"object '%s' unrecognized attrbute name %s!",
						obj_name, name_str);
					log_err(-1, __func__, log_buffer);
					/* process new line */
					line_data[0] = '\0';
					continue;
				}
				if ((index == JOB_ATR_runcount) && ((pjob2->ji_wattr[(int)JOB_ATR_run_version].at_flags & ATR_VFLAG_SET) == 0)) {
					snprintf(log_buffer, sizeof(log_buffer),
						"object '%s': ignoring setting attribute %s,"
						" talking to a server that does not allow %s modification, ",
						obj_name, name_str, name_str);
					log_err(-1, __func__, log_buffer);
					/* process new line */
					line_data[0] = '\0';
					continue;
				}

				/* security guard: hook that runs as    */
				/* PBSUSER should not be allowed  */
				/* to modify the euser value of the job */
				if ((phook != NULL) && \
				    (phook->user == HOOK_PBSUSER)) {
					long dval = atol(data_value);
					if (index == JOB_ATR_euser) {
						snprintf(log_buffer, sizeof(log_buffer),
							"object '%s': ignoring setting attribute %s,"
							" executing hook has user=pbsuser",
							obj_name, name_str);
						log_err(-1, __func__, log_buffer);
						/* process a new line */
						line_data[0] = '\0';
						continue;
					} else if ((index == JOB_ATR_runcount) && (pjob2->ji_wattr[index].at_flags & ATR_VFLAG_SET) && (dval < pjob2->ji_wattr[index].at_val.at_long)) {
						snprintf(log_buffer, sizeof(log_buffer),
							"object '%s': ignoring setting attribute %s,"
							" executing hook has user=pbsuser, "
							" cannot decrease value from %ld to %ld",
							obj_name, name_str,
							pjob2->ji_wattr[index].at_val.at_long,
							dval);
						log_err(-1, __func__, log_buffer);
						/* process a new line */
						line_data[0] = '\0';
						continue;
					}
				}

				pdef = &job_attr_def[index];

				/* decode attribute */
				errcode = pdef->at_decode(&pjob2->ji_wattr[index],
					name_str, resc_str, data_value);
				/* unknown resources still get decoded */
				/* using "unknown" placeholder resc def */
				if ((errcode != 0) &&
					(errcode != PBSE_UNKRESC)) {
					snprintf(log_buffer, sizeof(log_buffer),
						"object '%s' failed to decode (%s,%s,%s)! (errorcode %d)",
						obj_name, name_str, resc_str?resc_str:"",
						data_value, errcode);
					log_err(-1, __func__, log_buffer);
					/* process a new line */
					line_data[0] = '\0';
					continue;
				}
				if (errcode == PBSE_UNKRESC) {
					resource *prsc;
					resource_def *prdef;
					svrattrl *plist, *plist2, *plist_next;

					prdef = find_resc_def(svr_resc_def,
						"|unknown|", svr_resc_size);
					prsc = find_resc_entry(\
					    &pjob2->ji_wattr[index], prdef);

					if ((prdef == NULL) || (prsc == NULL)) {
						log_err(-1, __func__, "bad unknown resc");
						/* process a new line */
						line_data[0] = '\0';
						continue;
					}

					plist = (svrattrl *)GET_NEXT(\
				      	     prsc->rs_value.at_val.at_list);

					do {
						if (plist == NULL)
							break;

						plist_next = (svrattrl *)GET_NEXT(\
							   plist->al_link);

						/* check for duplicate resource */
						/* entry. The later ones take */
						/* precedence */
						plist2 = (svrattrl *)GET_NEXT(\
							plist->al_link);
						while (plist2 != NULL) {
							if (strcmp(plist->al_resc,
								plist2->al_resc) == 0) {
								delete_link(&plist->al_link);
								free(plist);
								break;
							}
							plist2 = (svrattrl *)GET_NEXT(\
							   plist2->al_link);
						}

						plist = plist_next;
					} while (plist != NULL);
				}
				/* resources set in a hook should be flagged */
				/* as such (see how mom_set_use treat */
				/* ATR_VFLAG_HOOK set resources) */
				if (resc_str != NULL) { /* a resource */
					resource_def 	*rd;
					resource	*pres;

					rd = find_resc_def(svr_resc_def,
						resc_str, svr_resc_size);
					if (rd != NULL) {
						pres = find_resc_entry(
							&pjob2->ji_wattr[index],
							rd);
						if (pres != NULL) {
							pres->rs_value.at_flags |=
								ATR_VFLAG_HOOK;
						}
					}
				}
				/* attributes in a hook should be flagged */
				/* with ATR_VFLAG_HOOK                    */
				pjob2->ji_wattr[index].at_flags |=
					ATR_VFLAG_HOOK;
				if (svr_hook_resend_job_attrs == 0) {
					svr_hook_resend_job_attrs = 1;
				}
			}

		} if ((strncmp(obj_name, EVENT_VNODELIST_FAIL_OBJECT,
					vn_fail_obj_len) == 0) ||
		      (strncmp(obj_name, EVENT_VNODELIST_OBJECT,
					vn_obj_len) == 0)) {

			int	*start_new_vnl_p;
			vnl_t	**hvnlp_p = NULL;
		
			if (strncmp(obj_name,
				EVENT_VNODELIST_FAIL_OBJECT,
					vn_fail_obj_len) == 0) {
				start_new_vnl_p = &start_new_vnl_fail;
				hvnlp_p = &hvnlp_fail;
			} else {
				start_new_vnl_p = &start_new_vnl;
				hvnlp_p = &hvnlp;
			}

			/* NOTE: obj_name here is: pbs.event().vnode_list[<vname>] */

			/* important here to look for the leftmost '[' (using strchr)
			 * and the rightmost ']' (using strrchr)
			 * as we can have:
			 *	pbs.event().vnode_list["altix[5]"].<attr>=<val>
			 * 	and "altix[5]" is a valid vnode id.
			 */
			if (((pc1=strchr(obj_name, '[')) != NULL) &&
				((pc2=strrchr(obj_name, ']')) != NULL) &&
				(pc2 > pc1)) {
				pc1++; /*  pc1=<vname>] */
				*pc2 = '\0'; /* pc1=<vname>  */
				pc2++;

				/* now let's if there's anything quoted inside */
				pc3 = strchr(pc1, '"');
				if (pc3 != NULL)
					pc4 = strchr(pc3+1, '"');
				else
					pc4 = NULL;

				if (pc3 && pc4 && (pc4 > pc3)) {
					pc3++;
					*pc4 = '\0';
					vname_str = pc3;
				} else {
					vname_str = pc1;
				}
			} else {
				snprintf(log_buffer, sizeof(log_buffer),
					"object '%s' does not have a vnode name!",
					obj_name);
				log_err(-1, __func__, log_buffer);
				/* process a new line */
				line_data[0] = '\0';
				continue;
			}

			if ((name_str != NULL) && (resc_str != NULL)) {
				/* format is in in_data: <attrib>\0<resc_str> */
				*(resc_str-1) = '.';/* so name_str=<attrib>.<resc_str> */
			}
			/* Found: resc_str = <resc_name>,<value_type> */
			if (resc_str != NULL) {
				if ((pc5=strchr(resc_str, ',')) != NULL) {
					*pc5 = '\0';
					pc5++;
					value_type = pc5;

				}
			}

			if (*start_new_vnl_p) {
				*start_new_vnl_p = 0;
				/* now add new hook_vnl_action structure to */
				/* the list of such to return for updating  */
				/* the Server                               */
				pvna = malloc(sizeof(struct hook_vnl_action));
				if ((pvna != NULL) && (pvnalist != NULL)) {
					CLEAR_LINK(pvna->hva_link);
					snprintf(pvna->hva_euser, sizeof(pvna->hva_euser),
						"%s", hook_euser);
					pvna->hva_actid = 0;
					pvna->hva_vnl   = NULL;
					pvna->hva_update_cmd = IS_UPDATE_FROM_HOOK;
					if (strncmp(obj_name, EVENT_VNODELIST_FAIL_OBJECT, vn_fail_obj_len) == 0) {
						pvna->hva_update_cmd = IS_UPDATE_FROM_HOOK2;
					}
					if (vnl_alloc(hvnlp_p) == NULL) {
						log_err(errno, __func__, "Failed to allocate hvnlp");
					} else {
						pvna->hva_vnl = *hvnlp_p;
						append_link(pvnalist,
							&pvna->hva_link, pvna);
					}
				} else {
					log_err(errno, __func__, "Failed to allocate hook action");
				}
				if ((copy_file == 0) && (*hvnlp_p != NULL)) {
					/* add a entry to the vnl for the user */
					rc=vn_addvnr(*hvnlp_p, mom_short_name,
						      VNATTR_HOOK_REQUESTOR,
						      hook_euser,
						      ATR_TYPE_STR, READ_ONLY,
						      NULL);
					if (rc == -1) {
						snprintf(log_buffer, sizeof(log_buffer),
							"Failed to add '%s %s=%s' vnode resource copy_file=%d",
							mom_short_name, VNATTR_HOOK_REQUESTOR,
							hook_euser, copy_file);
						log_err(-1, __func__, log_buffer);
					}
				}
			}
			rc = -1;
			if (*hvnlp_p) {
				char *p2;
				resource_def *prdef;
				unsigned int at_type;
				unsigned int rs_flag;

				/* We're now passing type and flag so that if 'name_str' resource */
				/* does not exist, then it will be dynamically allocated on the server */
				/* side, essentially allowing hook scripts to define custom resource! */
				rs_flag = READ_WRITE | ATR_DFLAG_CVTSLT;
				if ((p2=strrchr(name_str, '.')) != NULL) {
					p2++;
					prdef = find_resc_def(svr_resc_def,
						p2, svr_resc_size);
					if (prdef == NULL) { /* a custom resource */
						if (value_type != NULL) {
							if (strcmp(value_type, "boolean") == 0) {
								at_type =  ATR_TYPE_BOOL;
							} else if (strcmp(value_type, "long") == 0) {
								at_type =  ATR_TYPE_LONG;
							} else if (strcmp(value_type, "size") == 0) {
								at_type =  ATR_TYPE_SIZE;
							} else if (strcmp(value_type, "float") == 0) {
								at_type =  ATR_TYPE_FLOAT;
							} else if (strcmp(value_type, "string_array") == 0) {
								at_type =  ATR_TYPE_ARST;
							} else {
								at_type =  ATR_TYPE_STR;
							}
						} else {
							at_type =  ATR_TYPE_STR;
						}
					} else {
						at_type = prdef->rs_type;
						rs_flag = prdef->rs_flags;
					}
				} else {
					at_type =  ATR_TYPE_STR;
				}
				rc=vn_addvnr(*hvnlp_p, vname_str, name_str,
					data_value, at_type, rs_flag, NULL);
				if (rc == -1) {
					snprintf(log_buffer, sizeof(log_buffer),
						"Failed to add '%s %s=%s' at_type=%d rs_flag=%d",
						vname_str, name_str, data_value, at_type, rs_flag);
					log_err(-1, __func__, log_buffer);
				}
			}

		} else if ((strcmp(obj_name, SERVER_OBJECT) == 0) &&
			(strcmp(name_str, PY_SCHEDULER_RESTART_CYCLE_METHOD) == 0)) {

			if ((strcmp(data_value, "True") == 0) &&
				(copy_file == 0)) {
				/* ask Server to tell Scheduler to restart a */
				/* scheduling cycle; only done from main Mom */
				if (send_sched_recycle(hook_euser) != 0) {
					/* process a new line */
					line_data[0] = '\0';
					continue;
				}
			}

		} else if (strcmp(obj_name, PBS_OBJ) == 0) {

			if ((reboot_flag != NULL) &&
				(strcmp(name_str, PBS_REBOOT_OBJECT) == 0)) {
				if (strcmp(data_value, "True") == 0)
					*reboot_flag = 1;
				else
					*reboot_flag = 0;
			} else if ((reboot_cmd != NULL) &&
				(strcmp(name_str,
				PBS_REBOOT_CMD_OBJECT) == 0)) {
				strncpy(reboot_cmd, data_value, HOOK_BUF_SIZE-1);
			}
		}

		if ((fp2 != NULL) && (fputs(line_data, fp2) < 0)) {
			snprintf(log_buffer, sizeof(log_buffer),
				"Failed to save data in file %s",
				hook_job_outfile);
			log_err(errno, __func__, log_buffer);
			rc = 1;
			goto get_hook_results_end;
		}
		line_data[0] = '\0';
	}

	if (found_joblist && (found_rerunjob_action || found_deletejob_action)) {
		if ((reject_deletejob != NULL) && (*reject_deletejob)) {
			/* deletejob takes precedence */
			new_job_action_req(pjob2,
				phook?phook->user:HOOK_PBSADMIN, JOB_ACT_REQ_DELETE);
		} else if ((reject_rerunjob != NULL) && (*reject_rerunjob)) {
			new_job_action_req(pjob2,
				phook?phook->user:HOOK_PBSADMIN, JOB_ACT_REQ_REQUEUE);
		}
	}
	rc = 0;

get_hook_results_end:

	if (fp != NULL)
		fclose(fp);

	if (fp2 != NULL) {
		if (fflush(fp2) != 0) {
			/* error in writting job related hook results file */
			snprintf(log_buffer, sizeof(log_buffer),
				"Failed to save data in file %s",
				hook_job_outfile);
			log_err(errno, __func__, log_buffer);
			rc = 1;
			fclose(fp2);
			unlink(hook_job_outfile);
		} else {
			fclose(fp2);
		}
	}
	if (phook && !phook->debug) {
		(void)unlink(input_file);
	}
	if (line_data != NULL) {
		free(line_data);
	}
	if (in_data != NULL) {
		free(in_data);
	}

	return (rc);
}

/**
 * @brief
 *	Make the actual call to reboot the current host.
 *	If 'reboot_cmd' is NULL, then use the default reboot
 *	cmd line - see REBOOT_CMD macro.
 *
 * @param[in] reboot_cmd char pointer which hold cmd to rebbot host
 *
 * @return Void
 *
 */
static void
do_reboot(char *reboot_cmd)
{
	char	bootcmd[HOOK_BUF_SIZE];
	int	rcode;

	if ((reboot_cmd == NULL) || (*reboot_cmd == '\0'))
		strncpy(bootcmd, REBOOT_CMD, HOOK_BUF_SIZE-1);
	else
		strncpy(bootcmd, reboot_cmd, HOOK_BUF_SIZE-1);

	snprintf(log_buffer, sizeof(log_buffer), "issuing cmd %s", bootcmd);
	log_event(PBSEVENT_DEBUG3, 0,
		LOG_INFO, "do_reboot", log_buffer);

#ifndef WIN32
	if ((rcode=system(bootcmd)) != -1)
#else
	if ((rcode=wsystem(bootcmd, INVALID_HANDLE_VALUE)) == 0)
#endif
	{
		log_event(PBSEVENT_DEBUG3, 0,
			LOG_INFO, "do_reboot", "mom exiting");
		exit(0);
	} else {
		snprintf(log_buffer, sizeof(log_buffer),
			"reboot failed exit code=%d", rcode);
		log_event(PBSEVENT_DEBUG2, 0,
			LOG_ERR, "do_reboot", log_buffer);
	}
}

/**
 * @brief
 *	Add a hook's delete or requeue action request to the list of such
 *	actions sent to server and call send_hook_job_action() to send it
 *	to the server.  It will be removed from the list when the Server
 *	replies or resent if the Server connection is reestablished.
 *
 * @param[in] pjob - pointer to job structure
 * @param[in] huser - hook ran as admin or user
 * @param[in] action - JOB_ACT_REQ_DELETE (1) or JOB_ACT_REQ_REQUEUE (0)
 *
 * @return none
 *
 */
void
new_job_action_req(job *pjob, enum hook_user huser, int action)
{
	struct hook_job_action *phja;

	if (pjob == NULL)
		return;
	phja = malloc(sizeof(struct hook_job_action));
	if (phja == NULL) {
		log_err(PBSE_SYSTEM, "new_job_action_req", msg_err_malloc);
		return;
	}
	CLEAR_LINK(phja->hja_link);
	snprintf(phja->hja_jid, sizeof(phja->hja_jid), "%s", pjob->ji_qs.ji_jobid);
	phja->hja_actid = ++hook_action_id;

	if (pjob->ji_wattr[(int)JOB_ATR_run_version].at_flags & ATR_VFLAG_SET) {
		phja->hja_runct = pjob->ji_wattr[(int)JOB_ATR_run_version].at_val.at_long;
	} else {
		phja->hja_runct = pjob->ji_wattr[(int)JOB_ATR_runcount].at_val.at_long;
	}
	phja->hja_huser = huser;
	phja->hja_action = action;
	append_link(&svr_hook_job_actions, &phja->hja_link, phja);
	send_hook_job_action(phja);
}

/**
 * @brief
 *	This function runs after the task that runs a single periodic hook
 *	in the background completes execution.  If there was an error (the
 *	hook process returned a non-zero exits status), the results file
 *	is just discarded (it may not even be there).
 *
 * @param[in] 	pwt - the work task.
 *
 * @return none
 *
 */
static void
post_periodic_hook(pwt)
struct work_task *pwt;
{
	int	 wstat = pwt->wt_aux;
	hook	 *phook = (hook *)pwt->wt_parm1;
#ifdef WIN32
	pid_t	 mypid = pwt->wt_aux2;
#else
	pid_t	 mypid = pwt->wt_event;
#endif
	char	hook_outfile[MAXPATHLEN+1];
	char	reject_msg[HOOK_MSG_SIZE+1];
	time_t	next_time;
	char	*next_time_str;
	int	accept_flag = 1;
	int	hook_error_flag = 0;
	int	reject_flag = 0;
	int	rerun_flag = 0;
	int	delete_flag = 0;
	int	reboot_flag = 0;
	char	reboot_cmd[HOOK_BUF_SIZE];
	pbs_list_head vnl_changes;

	CLEAR_HEAD(vnl_changes);
	reboot_cmd[0] = '\0';
	reject_msg[0] = '\0';

	/* Check hook exit status */
	if (wstat != 0) {
		snprintf(log_buffer, LOG_BUF_SIZE-1,
			"Non-zero exit status %d encountered for periodic hook",
			wstat);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name, log_buffer);
		hook_error_flag = 1;	/* hook results are invalid */
	}

	/* hook results path */
	snprintf(hook_outfile, MAXPATHLEN, FMT_HOOK_OUTFILE,
		path_hooks_workdir,
		HOOKSTR_EXECHOST_PERIODIC,
		phook->hook_name, mypid);

	if (hook_error_flag == 0) {

		/* hook exited normally, get results from file  */
		if (get_hook_results(hook_outfile, &accept_flag, &reject_flag,
			reject_msg, sizeof(reject_msg), &rerun_flag,
			&delete_flag, &reboot_flag, reboot_cmd, HOOK_BUF_SIZE,
			&vnl_changes, NULL, phook, 0, NULL) != 0) {
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Failed getting hook results");
			/* error getting results, do not accept results */
			hook_error_flag = 1;
		}
	}

	if ((hook_error_flag == 1) || (accept_flag == 0)) {

		snprintf(log_buffer, sizeof(log_buffer),
			"%s request rejected by '%s'",
			"exechost_periodic", phook->hook_name);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name, log_buffer);
		if ((reject_msg != NULL) && (reject_msg[0] != '\0')) {
			snprintf(log_buffer, sizeof(log_buffer), "%s",
				reject_msg);
			/* log also the custom reject message */
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name, log_buffer);
		}

	}

	if (hook_error_flag == 0) {
		/* No hook error means data is communicated to */
		/* the server and actions are done to jobs.    */
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_INFO, phook->hook_name, "periodic hook accepted");

		/* remove the processed results file, note that if  */
		/* there was an error, it is left for debugging use */
		if (phook && !phook->debug)
			(void)unlink(hook_outfile);	/* remove file */

		if ((struct hook_vnl_action *)GET_NEXT(vnl_changes) != NULL) {

			/* there are vnode hook updates */
			/* Push hook changes to server */

			hook_requests_to_server(&vnl_changes);
		}

		if (reboot_flag) {
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK,
				LOG_INFO, phook->hook_name,
				"requested for host to be rebooted");
			do_reboot(reboot_cmd);
		}
	}
	vna_list_free(vnl_changes);	/* free the list of changes */

	next_time = time_now+phook->freq;
	next_time_str = ctime(&next_time);
	if ((next_time_str != NULL) && (next_time_str[0] != '\0')) {
		next_time_str[strlen(next_time_str)-1] = '\0'; /* rem newline */
		snprintf(log_buffer, sizeof(log_buffer), "will run on %s",
			next_time_str);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name, log_buffer);
	}

	(void)set_task(WORK_Timed, time_now+phook->freq,
		run_periodic_hook_bg_task, phook);
}

/**
 *
 *	Based on 'hook_fail_action', send a request to server to
 *	perform a hook fail action for hook named 'hook_name'.
 *
 * @param[in]	hook_name - hook in question
 * @param[in]	hook_fail_action - the actual hook fail action.
 *
 * @return void
 */
void send_hook_fail_action(hook *phook)
{

	char	hook_buf[HOOK_BUF_SIZE];
	vnl_t *tvnl = NULL;
	int	vret = -1;

	if ((phook == NULL) || (phook->hook_name == NULL)) {
		return;
	}

	snprintf(hook_buf, sizeof(hook_buf)-1, "1,%s", phook->hook_name);

	if (vnl_alloc(&tvnl) == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "Failed to vnl_alloc vnlp for %s",
			 phook->hook_name);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			  LOG_INFO, "", log_buffer);
		goto send_hook_fail_action_error;
	}

	if (phook->fail_action & HOOK_FAIL_ACTION_OFFLINE_VNODES) {
		vret = vn_addvnr(tvnl, mom_short_name,
			 	 VNATTR_HOOK_OFFLINE_VNODES,
				 hook_buf, 0, 0, NULL);

		if (vret != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
					     "Failed to add to vnlp: %s=%s",
					     VNATTR_HOOK_OFFLINE_VNODES, hook_buf);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, phook->hook_name, log_buffer);
			goto send_hook_fail_action_error;
		}
	}

	if (phook->fail_action & HOOK_FAIL_ACTION_SCHEDULER_RESTART_CYCLE) {
		vret = vn_addvnr(tvnl, mom_short_name,
			 	VNATTR_HOOK_SCHEDULER_RESTART_CYCLE,
				hook_buf, 0, 0, NULL);
		if (vret != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
				"Failed to add to vnlp: %s=%s",
				VNATTR_HOOK_SCHEDULER_RESTART_CYCLE,
							hook_buf);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, phook->hook_name, log_buffer);
			goto send_hook_fail_action_error;
		}
	}

	if (vret == 0) {
		/* this saves 'tvnl' in svr_vnl_action, and later freed
		 * upon server acking action
		 */
		(void)send_hook_vnl(tvnl);
		tvnl = NULL;
	}

send_hook_fail_action_error:
	if (tvnl != NULL)
		vnl_free(tvnl);
}

/**
 *
 * @brief
 *	Record the name of the last hook that executed
 *	on behalf of 'pjob' into a well-known file
 *	location:
 *
 *	"<location_directory>/hook_<pjob's jobid>.out"
 *
 *
 * @param[in]	hook_event - calling event.
 * @param[in]	hook_name - name of hook that executed
 * @param[in] 	pjob - associated job executing hook
 * @param[in] 	filepath - name of a file whose
 *			directory location is used as
 *			<location_directory>.
 *
 * @note
 *	This will currently record only for
 *	HOOK_EVENT_EXECJOB_PROLOGUE hooks.
 * @return void
 */
static void
record_job_last_hook_executed(unsigned int hook_event,
	char *hook_name, job *pjob, char *filepath)
{
	char	hook_job_outfile[MAXPATHLEN+1];
	FILE	*fp = NULL;
	char *p;
	char chr_save = '\0';
	char *p_dir = NULL;

	if ((hook_name == NULL) || (pjob == NULL)) {
		return;
	}
	if (hook_event != HOOK_EVENT_EXECJOB_PROLOGUE) {
		return;
	}

	if (filepath != NULL) {
		p = strrchr(filepath, '/');
		if (p != NULL) {
			p++;
			chr_save = *p;
			*p = '\0';
			p_dir = filepath;
		}
	}

	snprintf(hook_job_outfile, MAXPATHLEN,
		FMT_HOOK_JOB_OUTFILE, p_dir?p_dir:"",
				pjob->ji_qs.ji_jobid);

	if (chr_save != '\0')
		*p = chr_save;  /* restore */

	fp = fopen(hook_job_outfile, "w");
	if (fp == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			"failed to open hook_job_outfile=%s",
				hook_job_outfile);
		log_err(errno, __func__, log_buffer);
		return;
	}
	fprintf(fp, "%s=%s\n", PY_EVENT_HOOK_NAME,
					hook_name);
	fclose(fp);
}

/**
 * @brief
 *	Process hook scripts based on request type.
 *	This loops through the matching list of
 *	hooks, and executes the corresponding hook scripts.
 *
 * @param[in] 	hook_event - the event of the hooks that need to process.
 * @param[in] 	req_user - who requested for the hook to execute
 * @param[in] 	req_host - where the hook to execute is located
 * @param[in]	hook_input - struct of input parameters
 * @param[out]	hook_output - struct of output parameters
 * @param[in] 	hook_msg  - upon failure, if this buffer is set, fill it with
 *		the actual error message.
 * @param[in]   msg_len  - the size of 'hook_msg' buffer.
 * @param[in]	update_server - if true, send vnode and job attributes updates
 *			and/or requeue/delete job actions to the Server;
 *			done when NOT a child of Mom, but Mom herself.
 *			For exechost_startup hook, this must not be done,
 *			as the event has no job actions associated with the
 *			hook, and the vnode changes are sent separately
 *			upon mom acknowledging the HELLO server message.
 * @return	int
 * @retval 	0 means at least one hook was encountered to have rejected the
 *		request.
 * @retval  	1 means all the executed hooks have agreed to accept the request
 * @retval	2 means no hook script executed (special case).
 * @retval      -1 an internal error occurred
 *
 */
int
mom_process_hooks(unsigned int hook_event, char *req_user, char *req_host,
	mom_hook_input_t *hook_input, mom_hook_output_t *hook_output, char *hook_msg,
	size_t msg_len, int update_server)
{
	hook			*phook;
	hook			*phook_next = NULL;
	int			rc;
	pbs_list_head		*head_ptr;
	int			num_run = 0;
	char			hook_infile[MAXPATHLEN+1];
	char			hook_outfile[MAXPATHLEN+1];
	char			hook_datafile[MAXPATHLEN+1];
	int			accept_flag = 1;
	int			reject_flag = 0;
	char			reject_msg[HOOK_MSG_SIZE+1];
	int			reject_rerunjob = 0;
	int			reject_deletejob = 0;
	int			reboot_flag = 0;
	char			reboot_cmd[HOOK_BUF_SIZE];
	int			wait_hook;
	pbs_list_head		vnl_changes;
	int			set_job_exit = 0;
	char			*log_id = NULL;
	int			log_type = 0;
	int			log_class = 0;
	job			*pjob = NULL;
	hook			**last_phook = NULL;
	/* last hook that executed */
	unsigned int		*pfail_action = NULL; 	/* holds summation of hook */
	/* fail_action values */
	/* of those hook that */
	/*  executed */
	int			*reject_errcode = NULL;

	if (hook_input == NULL) {
		log_err(-1, __func__, "missing input argument to event");
		return (-1);
	}

	pjob = hook_input->pjob;

	/* If output objects are given, must have at least 3 */
	/* otherwise, input array is ignored */
	if (hook_output != NULL) {
		reject_errcode = hook_output->reject_errcode;
		last_phook = hook_output->last_phook;
		pfail_action = hook_output->fail_action;
	}

	CLEAR_HEAD(vnl_changes);
	wait_hook = 1;
	switch (hook_event) {

		case HOOK_EVENT_EXECJOB_BEGIN:
			head_ptr = &svr_execjob_begin_hooks;
			break;
		case HOOK_EVENT_EXECJOB_PROLOGUE:
			head_ptr = &svr_execjob_prologue_hooks;
			break;
		case HOOK_EVENT_EXECJOB_EPILOGUE:
			head_ptr = &svr_execjob_epilogue_hooks;
			break;
		case HOOK_EVENT_EXECJOB_END:
			head_ptr = &svr_execjob_end_hooks;
			break;
		case HOOK_EVENT_EXECJOB_PRETERM:
			head_ptr = &svr_execjob_preterm_hooks;
			break;
		case HOOK_EVENT_EXECJOB_LAUNCH:
			head_ptr = &svr_execjob_launch_hooks;
			break;
		case HOOK_EVENT_EXECHOST_PERIODIC:
			head_ptr = &svr_exechost_periodic_hooks;
			wait_hook = 0;
			break;
		case HOOK_EVENT_EXECHOST_STARTUP:
			head_ptr = &svr_exechost_startup_hooks;
			break;
		case HOOK_EVENT_EXECJOB_ATTACH:
			head_ptr = &svr_execjob_attach_hooks;
			break;
		case HOOK_EVENT_EXECJOB_RESIZE:
			head_ptr = &svr_execjob_resize_hooks;
			break;
		default:
			return (-1); /* unexpected event encountered */
	}

	if (hook_msg != NULL)
		memset(hook_msg, '\0', msg_len);

	for (phook = (hook *)GET_NEXT(*head_ptr); phook; phook = phook_next) {
		switch (hook_event) {

			case HOOK_EVENT_EXECJOB_BEGIN:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_begin_hooks);
				break;
			case HOOK_EVENT_EXECJOB_PROLOGUE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_prologue_hooks);
				break;
			case HOOK_EVENT_EXECJOB_EPILOGUE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_epilogue_hooks);
				break;
			case HOOK_EVENT_EXECJOB_END:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_end_hooks);
				break;
			case HOOK_EVENT_EXECJOB_PRETERM:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_preterm_hooks);
				break;
			case HOOK_EVENT_EXECJOB_LAUNCH:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_launch_hooks);
				break;
			case HOOK_EVENT_EXECHOST_PERIODIC:
				phook_next = (hook *)GET_NEXT(phook->hi_exechost_periodic_hooks);
				break;
			case HOOK_EVENT_EXECHOST_STARTUP:
				phook_next = (hook *)GET_NEXT(phook->hi_exechost_startup_hooks);
				break;
			case HOOK_EVENT_EXECJOB_ATTACH:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_attach_hooks);
				break;
			case HOOK_EVENT_EXECJOB_RESIZE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_resize_hooks);
				break;
			default:
				return (-1); /*  should not get here */
		}

		if (phook->enabled == FALSE)
			continue;

		if (phook->script == NULL) {
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Hook has no script content. Skipping hook.");
			continue;
		}

		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_INFO, phook->hook_name, "started");

		hook_infile[0] = '\0';
		hook_outfile[0] = '\0';
		hook_datafile[0] = '\0';
		accept_flag = 1;
		reject_msg[0]  = '\0';

		/* on an execjob_end or execjob_epilogue hook, need to set  */
		/* the job's exit value. 				    */
		/* Set it only once, and only if there's a hook to execute  */
		/* since we're affecting the job directly.		    */
		if( ((hook_event == HOOK_EVENT_EXECJOB_END) || \
		     (hook_event == HOOK_EVENT_EXECJOB_EPILOGUE)) &&
			!set_job_exit) {

			pjob->ji_wattr[(int)JOB_ATR_exit_status].\
							at_val.at_long = \
				pjob->ji_qs.ji_un.ji_momt.ji_exitstat;
			pjob->ji_wattr[(int)JOB_ATR_exit_status].\
								at_flags |=
				(ATR_VFLAG_SET | ATR_VFLAG_MODCACHE);
			set_job_exit = 1;
		} else if ((hook_event == HOOK_EVENT_EXECJOB_LAUNCH) && (num_run >= 1)) {

			/*
			 * If there are multiple execjob_launch hooks,
			 * we need to cascade the execjob_launch specific
			 * parameters to the next execjob_launch hook,
			 * if any of the previous hooks has set these
			 * values.
			 */

			if (hook_output != NULL) {

				if (hook_output->progname != NULL) {
					hook_input->progname = *hook_output->progname;
				}

				if (hook_output->env != NULL) {
					hook_input->env = hook_output->env;
				}
				hook_input->argv = svrattrl_to_str_array(hook_output->argv);
			}

		}

		rc = run_hook(phook, hook_event, hook_input,
			req_user, req_host, wait_hook, post_periodic_hook,
			hook_infile, hook_outfile, hook_datafile, MAXPATHLEN+1  );

		if (last_phook != NULL) {
			*last_phook = phook;
		}
		if (pfail_action != NULL) {
			*pfail_action |= phook->fail_action;
		}

		/* go back to mom's private directory */
		if (chdir(mom_home) != 0) {
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK, LOG_WARNING, phook->hook_name,
				"unable to go back to mom_home");
		}

		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_INFO, phook->hook_name, "finished");

		if ((hook_event == HOOK_EVENT_EXECHOST_PERIODIC) ||
			(hook_event == HOOK_EVENT_EXECHOST_STARTUP)) {
			log_id = phook->hook_name;
			log_type = PBSEVENT_DEBUG2;
			log_class = PBS_EVENTCLASS_HOOK;
		} else {
			log_id = pjob->ji_qs.ji_jobid;
			log_type = PBSEVENT_JOB;
			log_class = PBS_EVENTCLASS_JOB;
		}

		switch (rc) {
			case 0:	/* success */
				break;
				/* -2 return from pbs_python on Windows == 2^8-2 on Linux*/
			case -2:	/* unhandled exception return on Windows */
			case 254:	/* unhandled exception return on Linux/Unix */
				snprintf(log_buffer, LOG_BUF_SIZE-1,
					"%s hook '%s' encountered an exception, "
					"request rejected",
					hook_event_as_string(hook_event), phook->hook_name);
				log_event(log_type, log_class,
					LOG_ERR, log_id, log_buffer);
				if (hook_msg != NULL) {
					snprintf(hook_msg,  msg_len-1,
						"request rejected as filter hook '%s' encountered an "
						"exception. Please inform Admin", phook->hook_name);
				}
				if (reject_errcode != NULL) {
					*reject_errcode = PBSE_HOOKERROR;
				}
				record_job_last_hook_executed(hook_event, phook->hook_name, pjob, hook_outfile);
				return (0);
				/* -3 return from pbs_python == 2^8-3, but run_hook() */
				/* itself could return "-3" if it catches the alarm()   */
				/* to the process first. Both run_hook() code here, and */
				/* pbs_python program set alarm signals. One or the   */
				/* other would catch it first */
			case -3:	/* somewhere in this file, we do return -3 */
			case 253:	/* alarm timeout */
				snprintf(log_buffer, LOG_BUF_SIZE-1,
					"alarm call while running %s hook '%s', "
					"request rejected",
					hook_event_as_string(hook_event), phook->hook_name);
				log_event(log_type, log_class,
					LOG_ERR, log_id, log_buffer);
				if (hook_msg != NULL) {
					snprintf(hook_msg,  msg_len-1,
						"request rejected as filter hook '%s' got an "
						"alarm call. Please inform Admin", phook->hook_name);
				}
				if (reject_errcode != NULL) {
					*reject_errcode = PBSE_HOOKERROR;
				}
				record_job_last_hook_executed(hook_event, phook->hook_name, pjob, hook_outfile);
				return (0);
			default:
				snprintf(log_buffer, sizeof(log_buffer),
					"Internal server error encountered. Skipping hook %s",
					phook->hook_name);
				log_event(log_type, log_class,
					LOG_ERR, log_id, log_buffer);
				return (-1); /* should not happen */
		}

		num_run++;

		if (hook_event == HOOK_EVENT_EXECHOST_PERIODIC)
			/* hook backgrounded */
			continue;

		/* We're in a non-periodic hook */
		accept_flag = 1;
		reject_flag = 0;
		reject_rerunjob = 0;
		reject_deletejob = 0;
		reboot_flag = 0;
		reject_msg[0] = '\0';
		reboot_cmd[0] = '\0';

		if (get_hook_results(hook_outfile, &accept_flag, &reject_flag,
			reject_msg, sizeof(reject_msg), &reject_rerunjob,
			&reject_deletejob, &reboot_flag, reboot_cmd,
			HOOK_BUF_SIZE, &vnl_changes, pjob, phook,
			(hook_event == HOOK_EVENT_EXECHOST_STARTUP)?0:!update_server,
								hook_output) != 0) {
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Failed to get hook results");
			vna_list_free(vnl_changes);
			return (-1);
		}

		if ((hook_output != NULL) && (hook_output->vnl != NULL)) {
			struct hook_vnl_action *phvna;
			/* save vnl changes into  results array */
			for (phvna = GET_NEXT(vnl_changes); phvna;
				phvna = GET_NEXT(phvna->hva_link)) {
				vn_merge(hook_output->vnl, phvna->hva_vnl, NULL);
			}
		}

		if (update_server == 1) {

			if (pjob != NULL) {
				/* Delete job or reject job actions */
				/* NOTE: Must appear here before vnode changes, */
				/* since this action will be sent whether or not */
				/* hook script executed by PBSADMIN or not. */
				if (reject_deletejob) {
					/* deletejob takes precedence */
					new_job_action_req(pjob, phook->user,
						JOB_ACT_REQ_DELETE);
				} else if (reject_rerunjob) {
					new_job_action_req(pjob, phook->user,
						JOB_ACT_REQ_REQUEUE);
				}

				/* Whether or not we accept or reject, we'll make */
				/* job changes, vnode changes, job actions */


				update_ajob_status_using_cmd(pjob,
					IS_RESCUSED_FROM_HOOK, 0);
			}

			/* Push vnl hook changes to server */
			hook_requests_to_server(&vnl_changes);
		} else {
			vna_list_free(vnl_changes);
		}

		/* reject if at least one hook script rejects */
		if (!accept_flag) {

			if (hook_msg != NULL) {
				snprintf(hook_msg, msg_len-1,
					"%s request rejected by '%s'",
					hook_event_as_string(hook_event),
					phook->hook_name);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
					LOG_ERR, phook->hook_name, hook_msg);
			} else {
				snprintf(log_buffer, sizeof(log_buffer),
					"%s request rejected by '%s'",
					hook_event_as_string(hook_event),
					phook->hook_name);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
					LOG_ERR, phook->hook_name, log_buffer);
			}
			if ((reject_msg != NULL) && (reject_msg[0] != '\0')) {
				if (hook_msg != NULL) {
					snprintf(hook_msg, msg_len-1, "%s",
						reject_msg);
					/* log also the custom reject message */
					log_event(log_type,
						log_class, LOG_ERR,
						log_id, hook_msg);
				} else {
					snprintf(log_buffer, sizeof(log_buffer), "%s",
						reject_msg);
					/* log also the custom reject message */
					log_event(log_type,
						log_class, LOG_ERR,
						log_id, log_buffer);
				}
			}

			if (reject_errcode != NULL) {
				*reject_errcode = PBSE_HOOK_REJECT;
				if (reject_rerunjob)
					*reject_errcode = PBSE_HOOK_REJECT_RERUNJOB;
				if (reject_deletejob)
					*reject_errcode = PBSE_HOOK_REJECT_DELETEJOB;
			}

			return (0); /* don't process anymore hooks on reject */

		}

		if (reboot_flag) {

			if (phook->user == HOOK_PBSUSER) {
				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK,
					LOG_INFO, phook->hook_name,
					"Not allowed to issue reboot if run as user");
			} else {

				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK,
					LOG_INFO, phook->hook_name,
					"requested for host to be rebooted");
				do_reboot(reboot_cmd);
			}
		}

	}
	if (num_run == 0)
		return (2);

	return (1);
}


/**
 * @brief
 *
 *	Returns the number of hook scripts that are eligible to
 *	be executed for the specified 'hook_event'.
 *	This means the hook is enabled and has a hook content.
 *
 * @param[in] 	hook_event - the event of the hooks to count
 * @return	int
 * @retval 	<n> number of hooks
 *
 */
int
num_eligible_hooks(unsigned int hook_event)
{
	hook			*phook;
	hook			*phook_next = NULL;
	pbs_list_head		*head_ptr;
	int			num_hooks = 0;

	switch (hook_event) {

		case HOOK_EVENT_EXECJOB_BEGIN:
			head_ptr = &svr_execjob_begin_hooks;
			break;
		case HOOK_EVENT_EXECJOB_PROLOGUE:
			head_ptr = &svr_execjob_prologue_hooks;
			break;
		case HOOK_EVENT_EXECJOB_EPILOGUE:
			head_ptr = &svr_execjob_epilogue_hooks;
			break;
		case HOOK_EVENT_EXECJOB_END:
			head_ptr = &svr_execjob_end_hooks;
			break;
		case HOOK_EVENT_EXECJOB_PRETERM:
			head_ptr = &svr_execjob_preterm_hooks;
			break;
		case HOOK_EVENT_EXECJOB_LAUNCH:
			head_ptr = &svr_execjob_launch_hooks;
			break;
		case HOOK_EVENT_EXECHOST_PERIODIC:
			head_ptr = &svr_exechost_periodic_hooks;
			break;
		case HOOK_EVENT_EXECHOST_STARTUP:
			head_ptr = &svr_exechost_startup_hooks;
			break;
		case HOOK_EVENT_EXECJOB_ATTACH:
			head_ptr = &svr_execjob_attach_hooks;
			break;
		case HOOK_EVENT_EXECJOB_RESIZE:
			head_ptr = &svr_execjob_resize_hooks;
			break;
		default:
			return (0); /* unexpected event encountered */
	}

	for (phook = (hook *)GET_NEXT(*head_ptr); phook; phook = phook_next) {
		switch (hook_event) {

			case HOOK_EVENT_EXECJOB_BEGIN:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_begin_hooks);
				break;
			case HOOK_EVENT_EXECJOB_PROLOGUE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_prologue_hooks);
				break;
			case HOOK_EVENT_EXECJOB_EPILOGUE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_epilogue_hooks);
				break;
			case HOOK_EVENT_EXECJOB_END:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_end_hooks);
				break;
			case HOOK_EVENT_EXECJOB_PRETERM:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_preterm_hooks);
				break;
			case HOOK_EVENT_EXECJOB_LAUNCH:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_launch_hooks);
				break;
			case HOOK_EVENT_EXECHOST_PERIODIC:
				phook_next = (hook *)GET_NEXT(phook->hi_exechost_periodic_hooks);
				break;
			case HOOK_EVENT_EXECHOST_STARTUP:
				phook_next = (hook *)GET_NEXT(phook->hi_exechost_startup_hooks);
				break;
			case HOOK_EVENT_EXECJOB_ATTACH:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_attach_hooks);
				break;
			case HOOK_EVENT_EXECJOB_RESIZE:
				phook_next = (hook *)GET_NEXT(phook->hi_execjob_resize_hooks);
				break;
			default:
				return (0); /*  should not get here */
		}

		if (phook->enabled == FALSE)
			continue;

		if (phook->script == NULL)
			continue;

		num_hooks++;
	}

	return (num_hooks);
}

/**
 * @brief
 * 	Cleans up files older than HOOKS_TMPFILE_MAX_AGE under
 *	<path_spool>.
 *
 * @param[in] 	ptask	- a task to queue up
 *
 * @return none
 *
 */
void
cleanup_hooks_in_path_spool(struct work_task *ptask)
{
	DIR	*dir;
	struct 	dirent *pdirent;
	struct 	stat sbuf;
	char	hook_file[MAXPATHLEN+1];

	memset(hook_file, '\0', MAXPATHLEN+1);
	dir = opendir(path_spool);
	if (dir == NULL) {
		sprintf(log_buffer, "could not opendir %s",
			path_hooks_workdir);
		log_err(errno, __func__, log_buffer);
		return;
	}
	while (errno = 0, (pdirent = readdir(dir)) != NULL) {

		if (pdirent->d_name[0] == '.') {
			if (pdirent->d_name[1] == '\0' ||
				(pdirent->d_name[1] == '.' &&
				pdirent->d_name[2] == '\0'))
				continue;
		}


		if (strncmp(pdirent->d_name, FMT_HOOK_PREFIX,
			sizeof(FMT_HOOK_PREFIX)-1) != 0)
			continue;

		snprintf(hook_file, MAXPATHLEN, "%s%s",
			path_spool, pdirent->d_name);
		if (stat(hook_file, &sbuf) == -1) {
			sprintf(log_buffer, "could not stat %s", hook_file);
			log_err(errno, __func__, log_buffer);
			continue;
		}

		/* remove files older than 'HOOKS_TMPFILE_MAX_AGE' */
		if ((time_now - sbuf.st_ctime) > HOOKS_TMPFILE_MAX_AGE) {
			if (unlink(hook_file) < 0) {
				if (errno != ENOENT) {
					sprintf(log_buffer, "could not cleanup %s",
						hook_file);
					log_err(errno, __func__, log_buffer);
				}
			}
		}

	}
	if (errno != 0 && errno != ENOENT) {
		log_err(errno, __func__, "readdir");
	}
	if (dir) {
		(void)closedir(dir);
	}

	/*  cleanup of hooks temp files happen in the next */
	/* 'HOOKS_TMPFILE_NEXT_CLEANUP_PERIOD' secs.	   */
	(void)set_task(WORK_Timed, time_now+HOOKS_TMPFILE_NEXT_CLEANUP_PERIOD,
		cleanup_hooks_in_path_spool, NULL);
}

/**
 *  @brief
 *  	Initializes all the elements of mom_hook_input_t structure.
 *
 *  @param[in]	hook_input - the structure to initialize.
 *  @return void
 *
 */
void
mom_hook_input_init(mom_hook_input_t *hook_input)
{
	if (hook_input == NULL)
		return;

	hook_input->pjob = NULL;
	hook_input->progname = NULL;
	hook_input->argv = NULL;
	hook_input->env = NULL;
	hook_input->vnl = NULL;
	hook_input->vnl_fail = NULL;
	hook_input->failed_mom_list = NULL;
	hook_input->succeeded_mom_list = NULL;
	hook_input->jobs_list = NULL;
}

/**
 *  @brief
 *  	Initializes all the elements of mom_hook_output_t structure.
 *
 *  @param[in]	hook_output - the structure to initialize.
 *  @return void
 *
 */
void
mom_hook_output_init(mom_hook_output_t *hook_output)
{
	if (hook_output == NULL)
		return;

	hook_output->reject_errcode = NULL;
	hook_output->last_phook = NULL;
	hook_output->fail_action = NULL;
	hook_output->progname = NULL;
	hook_output->argv = NULL;
	hook_output->env = NULL;
	hook_output->vnl = NULL;
	hook_output->vnl_fail = NULL;
}
