/*
 * 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    hook_func.c
 *
 * @brief
 * hook_func.c - contains functions to record accounting information
 *
 * Functions included are:
 *
 * hook_action_tid_set
 * hook_action_tid_get
 * hook_track_save
 * send_rescdef
 * hook_track_recov
 * mgr_hook_create
 * mgr_hook_delete
 * py_compile_and_run
 * mgr_hook_import
 * mgr_hook_export
 * copy_hook
 * mgr_hook_set
 * mgr_hook_unset
 * status_hook
 * req_stat_hook
 * set_exec_time
 * set_hold_types
 * set_attribute
 * set_job_varlist
 * set_job_reslist
 * attribute_jobmap_init
 * attribute_jobmap_clear
 * attribute_jobmap_restore
 * do_runjob_accept_actions
 * do_runjob_reject_actions
 * write_hook_reject_debug_output_and_close
 * write_hook_accept_debug_output_and_close
 * process_hooks
 * recreate_request
 * add_mom_hook_action
 * delete_mom_hook_action
 * find_mom_hook_action
 * add_pending_mom_hook_action
 * delete_pending_mom_hook_action
 * has_pending_mom_action_delete
 * sync_mom_hookfiles_count
 * collapse_hook_tr
 * sync_mom_hookfiles
 * mk_deferred_hook_info
 * post_sendhookRPP
 * check_add_hook_mcast_info
 * del_deferred_hook_cmds
 * sync_mom_hookfilesRPP
 * kill_sync_hook_process
 * post_sync_mom_hookfiles
 * bg_sync_mom_hookfiles
 * add_pending_mom_allhooks_action
 * next_sync_mom_hookfiles
 * mark_mom_hooks_seen
 * mom_hooks_seen_count
 * bg_delete_mom_hooks
 * get_hook_rescdef_checksum
 */

#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 "attribute.h"
#include "batch_request.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 "reservation.h"
#include "queue.h"
#include "pbs_nodes.h"
#include "svrfunc.h"
#include "placementsets.h"
#include <pbs_python.h>  /* for python interpreter */
#include <signal.h>
#include "hook_func.h"
#include "net_connect.h"
#include "sched_cmds.h"
#include "rpp.h"
#include "reservation.h"
#include "cmds.h"
#include "server.h"
#include "pbs_sched.h"
#include "dis.h"

/* External functions */
extern void disable_svr_prov();
extern void set_srv_prov_attributes();
extern int  should_retry_route(int);
extern	char server_host[PBS_MAXHOSTNAME+1];

/* Local Private Functions */

/* Global Data items */
int	do_sync_mom_hookfiles = 1;
int	sync_mom_hookfiles_proc_running = 0;
pbs_list_head vnode_attr_list;
pbs_list_head resv_attr_list;

/* Local Data */
static char merr[] = "malloc failed";
static	int	mom_hooks_seen = 0;	/* # of mom hooks seen */
static	long long int hook_action_tid = 0LL; /* transaction id of the next */
static  int g_hook_replies_expected = 0; /* used only in TPP mode */
static  int g_hook_replies_recvd = 0; /* used only in TPP mode */
static	time_t	g_sync_hook_time = 0; /* time when mom hook files were last sent */
static	long long int g_sync_hook_tid = 0LL; /* identifies the latest group of hook updates to send out */
static	pid_t	g_sync_hook_pid = -1;	/* pid of the child sync_mom_hookfiles() process (non-TPP only) */
					/* process sending out mom hook files */
static	unsigned long	hook_rescdef_checksum = 0;

/* mom hook action(s) to keep track */

#define GROW_MOMHOOK_ARRAY_AMT 10
#define	CONN_RETRY	3
#define IS_SPECIAL_CHAR(c)	((c == '"') || (c == '\'') || (c  == ',') || (c == '\\'))
#define VALID_HOOK_CONFIG_SUFFIX        ".json .py .txt .xml .ini"

#define	SYNC_MOM_HOOKFILES_TIMEOUT_TPP	120	/* 2 minutes */
#define	SYNC_MOM_HOOKFILES_TIMEOUT	900	/* 15 minutes */

extern char *msg_daemonname;
extern char *path_priv;
extern char *path_hooks;
extern char *path_hooks_workdir;
extern char *path_hooks_tracking;

extern char		path_log[];
extern char		*log_file;
extern pbs_net_t	pbs_server_addr;
extern unsigned int	pbs_server_port_dis;

extern char *msg_badexit;
extern char *msg_err_malloc;
extern char *msg_manager;
extern char *msg_man_cre;
extern char *msg_man_del;
extern char *msg_man_set;
extern char *msg_man_uns;
extern char *msg_noattr;
extern char *msg_internal;
extern char *msg_norelytomom;
extern pbs_list_head svr_allhooks;
extern pbs_list_head svr_queuejob_hooks;
extern pbs_list_head svr_modifyjob_hooks;
extern pbs_list_head svr_resvsub_hooks;
extern pbs_list_head svr_movejob_hooks;
extern pbs_list_head svr_runjob_hooks;
extern pbs_list_head svr_periodic_hooks;
extern pbs_list_head svr_provision_hooks;
extern pbs_list_head svr_resv_end_hooks;
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_end_hooks;
extern pbs_list_head svr_exechost_periodic_hooks;
extern pbs_list_head svr_exechost_startup_hooks;
extern pbs_list_head svr_execjob_launch_hooks;
extern pbs_list_head svr_execjob_attach_hooks;
extern pbs_list_head svr_execjob_resize_hooks;
extern	time_t	time_now;
extern 	struct python_interpreter_data  svr_interp_data;
extern	pbs_list_head task_list_event;
extern struct work_task *add_mom_deferred_list(int stream, mominfo_t *minfo, void (*func)(), char *msgid, void *parm1, void *parm2);

extern	char	*path_rescdef;
extern	char	*path_hooks_rescdef;

extern int   comp_resc_gt;
extern int   comp_resc_lt;
extern int   comp_resc_nc;


struct def_hk_cmd_info {
	int index;
	int event;
	long long int tid;	/* transaction id */
};

/* structures required for TPP mcast communication
 * of hooks to moms
 */
typedef struct {
	int  mconn;
	char *msgid;
	char *hookname;
	int  action;
} hook_mcast_info_t;

/* global array of mcast information structs */
hook_mcast_info_t *g_hook_mcast_array = NULL;
int g_hook_mcast_array_len = 0;
extern int get_msgid(char **id);

/**
 * @brief
 *		hook_action_tid_set	- Sets the value of the global 'hook_action_tid' variable to the given
 *		'newval'.
 * @see
 *		main, hook_track_recov and bg_sync_mom_hookfiles.
 *
 * @param[in]	newval - the new value.
 *
 * @return void
 */
void
hook_action_tid_set(long long int newval)
{
	hook_action_tid = newval;
	snprintf(log_buffer, sizeof(log_buffer),
		"hook_action_tid=%lld", hook_action_tid);
	log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
		LOG_INFO, "hook_action_tid_set", log_buffer);
}
/**
 * @brief
 *		Returns the value of the global 'hook_action_tid' variable.
 *
 * @see
 *		sync_mom_hookfilesRPP and bg_sync_mom_hookfiles.
 *
 * @return long long int	- the 'hook_action_tid' value.
 */
long long int
hook_action_tid_get(void)
{
	return (hook_action_tid);
}

/**
 * @brief
 * 		attrlist_add	- Add <name> = <val> attributes to list headed by 'atl'
 *
 * @param[in,out]	atl -  attribute list headed by atl.
 * @param[in]		name - name, in the name value pair.
 * @param[in]		val -  val, in the name value pair.
 *
 * @return	int
 * @retval	0 - for success
 * @retval	1 - otherwise.
 */
static int
attrlist_add(pbs_list_head *atl, char *name, char *val)
{
	svrattrl	*pal2;

	if ((name == NULL) || (val == NULL)) {
		log_err(-1, __func__, "name or val param is NULL");
		return (1);
	}

	if ((name[0] == '\0') || (val[0] == '\0')) {
		sprintf(log_buffer,
			"(%s,%s) - name or val parameter is empty", name, val);
		log_err(errno, __func__, log_buffer);
		return (1);
	}

	pal2 = attrlist_create(name, NULL, (int)strlen(val)+1);
	if (pal2 == NULL) {
		sprintf(log_buffer,
			"(%s,%s) - failed to create attribute list", name, val);
		log_err(errno, __func__, log_buffer);
		return (1);
	}
	strcpy(pal2->al_value, val);
	append_link(atl, &pal2->al_link, pal2);
	return (0);
}

/* Mom Hooks tracking functions */

/**
 * @brief
 *		hook_track_save	- Save the mom hooks pending actions data to a path_hooks_tracking file.
 *		File format is:
 *		<mom_hostname>:<mom_port> <hookname> <action> <tid>
 *
 * @par Note:
 *		If 'minfo' is not NULL and k != -1, then data from action array attached
 *		to 'minfo' at index 'k' is appended to path_hooks_tracking file.
 *
 *		If 'minfo' is not NULL and k == -1, then no data is saved
 *		as hook_track_save() returns immediately.
 *
 *		If 'minfo' is NULL and whether or not k != -1 or k == -1, then data
 *		from action array attached to each mom in the system, gets
 *		appended to path_hooks_tracking file.
 *
 * @param[in]	minfo - used in conjunction with 'k' below:
 *			if not NULL and
 *			k != -1, then append only the array
 *			data attached to 'minfo'indexed at 'k' to
 *			path_hooks_tracking file.
 *
 * @param[in]	k     - used in conjunction with 'minfo' above.
 *
 * @return	void
 *
 */

void
hook_track_save(void *minfo, int k)
{
	int	i, j;
	FILE	*fp = NULL;
	mominfo_t	**minfo_array = NULL;
	int		minfo_array_size;
	mominfo_t	*minfo_array_tmp[1];
	char		msg[HOOK_MSG_SIZE+1];

	if ((minfo != NULL) && (k == -1))
		return;

	if (minfo != NULL) {

		fp = fopen(path_hooks_tracking, "a");
		minfo_array_tmp[0] = minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;
	} else {
		fp = fopen(path_hooks_tracking, "w");
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	}


	if (fp == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed to open hook tracking file %s",
			path_hooks_tracking);
		log_err(errno, __func__, log_buffer);
		return;
	}

#ifdef WIN32
	secure_file2(path_hooks_tracking, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED,
		"Everyone", READS_MASK|READ_CONTROL);
#endif

	if (lock_file(fp, F_WRLCK, path_hooks_tracking, LOCK_RETRY_DEFAULT,
		msg, sizeof(msg)) != 0) {
		log_err(errno, __func__, msg);
		fclose(fp);
		return;	/* failed to lock */
	}

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

		if (minfo_array[i] == NULL)
			continue;

		for (j=0; j < minfo_array[i]->mi_num_action; j++) {

			if (((minfo == NULL) || (k == j)) &&
				(minfo_array[i]->mi_action[j] != NULL)) {
				fprintf(fp, "%s:%d %s %d %lld\n", minfo_array[i]->mi_host,
					minfo_array[i]->mi_port,
					minfo_array[i]->mi_action[j]->hookname,
					minfo_array[i]->mi_action[j]->action,
					minfo_array[i]->mi_action[j]->tid);
			}
		}
	}
	(void)fflush(fp);

	if (lock_file(fp, F_UNLCK, path_hooks_tracking, LOCK_RETRY_DEFAULT,
		msg, sizeof(msg)) != 0)
		log_err(errno, __func__, msg);

	fclose(fp);
}
/**
 *
 * @brief
 *		send_rescdef	- checks if server's resourcedef file has a newer timestamp than the
 *		resourcedef file currently known to hooks, and if so sets up the
 *		necessary mom hook actions to send the resourcedef file to each of the
 *		mom hosts on the next sync_mom_hookfiles() call.
 *
 * @param[in]	force - if set to '1', means to skip checking for timetamps
 * 			and just force setting up the mom hook actions to
 * 			send the resourcedef file to each of the mom hosts.
 *
 * @return void
 */

void
send_rescdef(int force)
{

	int	st;
	int	st2;
	struct stat sbuf;
	struct stat sbuf2;

	if (mom_hooks_seen <= 0) {
		if (mom_hooks_seen < 0) {	/* should not happen */
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, __func__,
				"mom_hooks_seen went negative, resetting to 0");
			mom_hooks_seen = 0;
		}
		return;
	}

	st=stat(path_rescdef, &sbuf);
	st2=stat(path_hooks_rescdef, &sbuf2);

	if ((st == 0) && (sbuf.st_size > 0) &&
		(force || ((st2 == -1) && (errno == ENOENT)) ||
		((st2 == 0) && (sbuf.st_mtime > sbuf2.st_mtime)))) {
		st = copy_file_internal(path_rescdef, path_hooks_rescdef);
		if (st != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
				"warning: Failed to copy %s %s (error %d)",
				path_rescdef, path_hooks_rescdef, st);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_SERVER,
				LOG_ERR, __func__, log_buffer);
		} else {
			add_pending_mom_hook_action(NULL, PBS_RESCDEF,
				MOM_HOOK_ACTION_SEND_RESCDEF);
			do_sync_mom_hookfiles = 1;
		}
	} else if (((st == -1) || (sbuf.st_size == 0)) && (st2 == 0)) {
		/* server_priv/resourcedef disappeared and yet */
		/* server_priv/hooks/resourcedef still exists */
		add_pending_mom_hook_action(NULL, PBS_RESCDEF,
			MOM_HOOK_ACTION_DELETE_RESCDEF);
		do_sync_mom_hookfiles = 1;
		(void)unlink(path_hooks_rescdef);
	}

	hook_rescdef_checksum = crc_file(path_hooks_rescdef);
}

/**
 * @brief
 *		hook_track_recov	- Recover the tracking data in path_hooks_tracking for mom hooks.
 *
 * @par Functionality
 *		The tracking data contains the pending mom hook actions, and in a file
 *		has the format:
 *		<mom_name> <port_number> <hook_name> <action> <tid>
 *
 * @note
 *		This is to recover any pending mom hook actions when the
 *		server restarts.
 *
 *		This will also look for the highest value <tid> it has recovered
 *		from the hooks tracking file, and uses that as the new value of
 *		the global variable hook_action_tid.
 *
 *		If a hook-related file (*.HK, *.PY, resourcedef) was sent to the
 *		remote mom, and the mom was able to get the file, and if the child
 *		server goes away before it can record the result in the hooks
 *		tracking file, then the main server would be left with still the old
 *		tracking entry for the hook action, and is retried again by the
 *		next spawned child server. If the main server dies in the middle
 *		of the transaction, when it restarts, it will be able to recover any
 *		hook action results by the child server. And if there are still
 *		pending actions, then they are retried by the next child server.
 *
 * @return      void
 */
void
hook_track_recov(void)
{
	int	i, j;
	FILE	*fp = NULL;
	char	linebuf[BUFSIZ];
	int	linenum;
	char	*mom_name;
	char	*hook_name;
	int	port_num;
	char	*port_num_str;
	int	action;
	char	*action_str;
	long long int hook_tid;
	char 	*p;
	char 	*p2;
	char	msg[HOOK_MSG_SIZE+1];
	int	msg_len = HOOK_MSG_SIZE;
	mominfo_t *minfo = NULL;
	struct stat sbuf;
	long long int	max_tid_recov = 0LL; /* holds maximum tid number seen */
	/* in mom hooks tracking file */

	if (stat(path_hooks_tracking, &sbuf) != 0) {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed to stat %s errno=%d", path_hooks_tracking,
			errno);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
			LOG_ERR, __func__, log_buffer);
		return;
	}

	fp = fopen(path_hooks_tracking, "r");
	if (fp == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed to open file %s errno=%d",
			path_hooks_tracking, errno);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
			LOG_ERR, __func__, log_buffer);
		return;
	}

	if (lock_file(fp, F_RDLCK, path_hooks_tracking, LOCK_RETRY_DEFAULT,
		msg, sizeof(msg)) != 0) {
		log_err(errno, __func__, msg);
		fclose(fp);
		return;	/* failed to lock */
	}

	/* we now have a lock on the file */

	/* Need to clear out old entries */
	for (i=0; i < mominfo_array_size; i++) {
		if (mominfo_array[i] == NULL)
			continue;
		for (j=0; j < mominfo_array[i]->mi_num_action; j++) {
			if (mominfo_array[i]->mi_action[j] != NULL) {
				free(mominfo_array[i]->mi_action[j]);
				mominfo_array[i]->mi_action[j] = NULL;
			}
		}
	}


	/* We'll recover what we can, and just ignore errors */
	/* encountered processing the hook tracking file entries */
	linenum = 0;
	while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {

		linenum++;
		if ((p = strrchr(linebuf, '\n')) != NULL) {
			*p = '\0';
		} else {
			snprintf(msg, msg_len-1,
				"warning: line %d is too long", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}
		/* ignore initial white space; skip blank lines */
		p = linebuf;

		/* skip whitespace */
		while ((*p != '\0') && isspace(*p))
			p++;

		if (*p == '\0')
			continue;	/* empty line */

		mom_name = p;
		if ((p2 = strchr(linebuf, ':')) == NULL) {
			snprintf(msg, msg_len-1,
				"warning: line %d:  missing ':'", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}

		if (*(p2+1) == '\0') {
			snprintf(msg, msg_len-1,
				"warning: line %d:  no <port num>",
				linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}
		*p2 = '\0';


		p2++;
		port_num_str = p2;
		/* skip non-white space */
		while ((*p2 != '\0') && !isspace(*p2))
			p2++;

		if (*p2 == '\0') {
			snprintf(msg, msg_len-1,
				"warning: line %d:  no <hook name>",
				linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}

		*p2 = '\0';

		port_num = atoi(port_num_str);

		/* skip whitespace */
		p2++;
		while ((*p2 != '\0') && isspace(*p2))
			p2++;

		if (*p2 == '\0') {
			snprintf(msg, msg_len-1,
				"warning: line %d:  no <hook name>",
				linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}

		hook_name = p2;
		while ((*p2 != '\0') && !isspace(*p2))
			p2++;

		if (*p2 == '\0') {
			snprintf(msg, msg_len-1,
				"warning: line %d:  no <hook action>", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}

		*p2++ = '\0';

		/* skip whitespace */
		while ((*p2 != '\0') && isspace(*p2))
			p2++;

		action_str = p2;

		/* get non-whitespace characters */
		while ((*p2 != '\0') && !isspace(*p2))
			p2++;

		if (*p2 == '\0') {
			snprintf(msg, msg_len-1,
				"warning: line %d:  no <tid>", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			continue;
		}

		*p2 = '\0';

		action = atoi(action_str);

		p2++;

		/* skip whitespace */
		while ((*p2 != '\0') && isspace(*p2))
			p2++;

		/* preserve the recovered tid value */
		/* and use it to store into mom hook */
		/* action data structure */
#if defined(WIN32)    /* Windows */
		hook_tid = _atoi64(p2);
#else
		hook_tid = atoll(p2);
#endif
		if (hook_tid > max_tid_recov)
			max_tid_recov = hook_tid;

		/* optimization: use previous result if match */
		if ((minfo == NULL) ||
			(strcasecmp(minfo->mi_host, mom_name) != 0) ||
			(minfo->mi_port != port_num)) {

			minfo=find_mom_entry(mom_name, port_num);
		}
		if (minfo != NULL) {
			(void)add_mom_hook_action(&minfo->mi_action,
				&minfo->mi_num_action, hook_name, action, 1,
				hook_tid);
#ifdef WIN32
			/* Below is only for pbs_send_hooks.exe on Windows and */
			/* it is not for the main server. So the main server */
			/* would not recreate a mom entry for a mom that got */
			/* deleted. pbs_send_hooks is a "pseudo-server" where */
			/* it doesn't have the mom entries initially */
		} else if ((msg_daemonname != NULL) &&
			((strcmp(msg_daemonname, "PBS_send_hooks") == 0) ||
			(strcmp(msg_daemonname, "PBS_start_provision") == 0))) {
			/* a pseudo server on Windows where the mom entry */
			/* needs to be recreated. */
			minfo = create_mom_entry(mom_name, port_num);
			if (minfo == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					"warning: failed to create mom entry to (%s,%s) errno %d",
					mom_name, port_num, errno);
				log_err(PBSE_SYSTEM, __func__, log_buffer);
				continue;
			}
			(void)add_mom_hook_action(&minfo->mi_action,
				&minfo->mi_num_action, hook_name, action, 1,
				hook_tid);
#endif
		}

	}

	hook_action_tid_set(max_tid_recov);

	if (fp != NULL) {
		if (lock_file(fp, F_UNLCK, path_hooks_tracking,
			LOCK_RETRY_DEFAULT, msg, sizeof(msg)) != 0) {
			log_err(errno, __func__, msg);
		}
		fclose(fp);
	}
}

/*
 ************************************************************************
 *   Hook-related Qmgr operations.
 ************************************************************************
 */

/**
 * @brief
 *		mgr_hook_create	- Processes a "create hook" qmgr request.
 *
 * @see
 *		req_manager
 *
 * @param[in]	preq	- the requestor info via batch request structure
 *
 * @note
 *		Returns a reply to the sender of the batch_request.
 */
void
mgr_hook_create(struct batch_request *preq)
{
	svrattrl	*plist, *plx;
	hook		*phook = NULL;
	hook		*phook2 = NULL;
	char		hook_msg[HOOK_MSG_SIZE] = {'\0'};
	char		*hook_user_val = NULL;
	char		*hook_fail_action_val = NULL;
	char		*hook_freq_val = NULL;

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	if ((phook2=find_hook(preq->rq_ind.rq_manager.rq_objname)) != NULL) {
		if (phook2->pending_delete) {
			snprintf(hook_msg, sizeof(hook_msg),
				"hook name \'%s\' is pending delete, try another name",
				preq->rq_ind.rq_manager.rq_objname);
		} else {
			snprintf(hook_msg, sizeof(hook_msg),
				"hook name \'%s\' already registered, try another name",
				preq->rq_ind.rq_manager.rq_objname);
		}
		goto mgr_hook_create_error;
	}

	phook = hook_alloc();
	if (phook == NULL) {
		req_reject(PBSE_INTERNAL, 0, preq);
		return;
	}

	if (set_hook_name(phook, preq->rq_ind.rq_manager.rq_objname,
		hook_msg, sizeof(hook_msg)) != 0) {
		goto mgr_hook_create_error;
	}
	plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);

	plx = plist;
	while (plx) {
		if (strcasecmp(plx->al_name, HOOKATT_TYPE) == 0) {
			if (set_hook_type(phook, plx->al_value,
				hook_msg, sizeof(hook_msg), 0) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_ENABLED) == 0) {
			if (set_hook_enabled(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_DEBUG) == 0) {
			if (set_hook_debug(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_USER) == 0) {
			/* setting hook user value must be a deferred action, */
			/* as it is dependent on event having */
			/* execjob_prologue, execjob_epilogue, or */
			/* execjob_preterm being set. The event set action */
			/* could appear after this set user action. */
			if (hook_user_val != NULL)
				free(hook_user_val);
			hook_user_val = strdup(plx->al_value);
			if (hook_user_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_create_error;
			}
		} else if (strcasecmp(plx->al_name, HOOKATT_FAIL_ACTION) == 0) {
			/* setting hook fail_action value must be a deferred action, */
			/* as it is dependent on event mom hook event */
			/* being set. The event set action */
			/* could appear after this set fail_action. */
			if (hook_fail_action_val != NULL)
				free(hook_fail_action_val);
			hook_fail_action_val = strdup(plx->al_value);
			if (hook_fail_action_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_create_error;
			}
		} else if (strcasecmp(plx->al_name, HOOKATT_EVENT) == 0) {
			if (set_hook_event(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_ORDER) == 0) {
			if (set_hook_order(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_ALARM) == 0) {
			if (set_hook_alarm(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_create_error;
		} else if (strcasecmp(plx->al_name, HOOKATT_FREQ) == 0) {
			/* setting hook freq value must be a deferred action, */
			/* as it is dependent on event having */
			/* exechost_periodic being set. The event set action */
			/* could appear after this set freq action. */
			if (hook_freq_val != NULL)
				free(hook_freq_val);
			hook_freq_val = strdup(plx->al_value);
			if (hook_freq_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_create_error;
			}
		} else {
			snprintf(hook_msg, sizeof(hook_msg)-1, "%s - %s",
				msg_noattr, plx->al_name);
			goto mgr_hook_create_error;
		}

		plx = (svrattrl *)GET_NEXT(plx->al_link);
	}

	/* Now do the deferred set actions */
	if (hook_user_val != NULL) {
		if (set_hook_user(phook, hook_user_val,
			hook_msg, sizeof(hook_msg), 1) != 0)
			goto mgr_hook_create_error;
		free(hook_user_val);
		hook_user_val = NULL;
	}
	/* Now do the deferred set fail_action */
	if (hook_fail_action_val != NULL) {
		if (set_hook_fail_action(phook, hook_fail_action_val,
			hook_msg, sizeof(hook_msg), 1) != 0)
			goto mgr_hook_create_error;
		free(hook_fail_action_val);
		hook_fail_action_val = NULL;
	}
	if (hook_freq_val != NULL) {
		if (set_hook_freq(phook, hook_freq_val,
			hook_msg, sizeof(hook_msg)) != 0)
			goto mgr_hook_create_error;
		free(hook_freq_val);
		hook_freq_val = NULL;
	}

	sprintf(log_buffer, msg_manager, msg_man_cre,
		preq->rq_user, preq->rq_host);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		preq->rq_ind.rq_manager.rq_objname, log_buffer);

	if (hook_save(phook) != 0) {
		snprintf(hook_msg, sizeof(hook_msg),
			"Failed to store '%s' permanently.",
			preq->rq_ind.rq_manager.rq_objname);
		goto mgr_hook_create_error;
	}

	if (phook->event & MOM_EVENTS) {
		add_pending_mom_hook_action(NULL, phook->hook_name,
			MOM_HOOK_ACTION_SEND_ATTRS);
		mom_hooks_seen++;
		if (mom_hooks_seen == 1) {
			/* used to be no mom hooks in the system, but now */
			/* one is introduced. So see if resourcedef file */
			/* changed and need to be flagged to be sent to */
			/* the moms */
			send_rescdef(0);
		}
	}
	reply_ack(preq);	/*create completely successful*/
	return;

mgr_hook_create_error:
	if (hook_user_val != NULL)
		free(hook_user_val);
	if (hook_fail_action_val != NULL)
		free(hook_fail_action_val);
	if (hook_freq_val != NULL)
		free(hook_freq_val);

	if (phook)
		hook_purge(phook, pbs_python_ext_free_python_script);
	reply_text(preq, PBSE_HOOKERROR, hook_msg);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, hook_msg);
}

/**
 * @brief
 *		mgr_hook_delete	- Processes a "delete hook" qmgr request.
 *
 * @see
 * 		req_manager
 *
 * @param[in]	preq	- a batch_request structure representing the request.
 */
void
mgr_hook_delete(struct batch_request *preq)
{


	hook *phook = NULL;
	char hookname[PBS_MAXSVRJOBID + 1] = {'\0'};
	char hook_msg[HOOK_MSG_SIZE] = {'\0'};

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	snprintf(hookname, sizeof(hookname), "%s", preq->rq_ind.rq_manager.rq_objname);

	phook = find_hook(hookname);

	if ((phook == NULL) || phook->pending_delete) {
		snprintf(hook_msg, sizeof(hook_msg), "%s does not exist!",
			hookname);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
			hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	if (phook->type == HOOK_PBS) {
		sprintf(log_buffer, "cannot delete a PBS hook named %s",
			phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
			hookname, log_buffer);
		reply_text(preq, PBSE_HOOKERROR, "cannot delete a PBS hook");
		return;
	}

	if (phook->event & HOOK_EVENT_PROVISION)
		disable_svr_prov();

	if (phook->event & HOOK_EVENT_PERIODIC)
		(void)delete_task_by_parm1(phook, DELETE_ALL);

	if (phook->event & MOM_EVENTS) {

		phook->pending_delete = 1;
		add_pending_mom_hook_action(NULL, phook->hook_name,
			MOM_HOOK_ACTION_DELETE);
		mom_hooks_seen--;
		(void)snprintf(hook_msg, sizeof(hook_msg),
			"hook %s queued for deletion", phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
			hookname, hook_msg);
		if (hook_save(phook) != 0) {
			snprintf(hook_msg, sizeof(hook_msg),
				"Warning: failed to store '%s' permanently.",
				preq->rq_ind.rq_manager.rq_objname);
			log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
				hookname, hook_msg);
		}
		reply_ack(preq);	/*request completely successful*/
	} else {
		hook_purge(phook, pbs_python_ext_free_python_script);

		(void)sprintf(log_buffer, msg_manager, msg_man_del,
			preq->rq_user, preq->rq_host);

		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
			hookname, log_buffer);
		reply_ack(preq);	/*request completely successful*/
	}
}

/**
 *  @brief
 *  	py_compile_and_run	- Compiles and/or runs the input_file_path based on file's suffix.
 *
 * @see
 * 		mgr_hook_import
 *
 *  @param[in]	input_file_path - file to compile and/or run.
 *  @param[out]	hook_msg - any error message resulting from compilation/run
 *				filled in here.
 *  @param[in]	msg_len - size of the hook_msg
 *  @param[in]	hook_name - name of the associated hook.
 *  @param[in]	compile_only - if set to 1, means to compile only
 *  				'input_file_path'
 *
 * @return int
 * @retval	0	- for success
 * @retval	!= 0	- for any failure encountered
 */
static int
py_compile_and_run(char *input_file_path, char *hook_msg, size_t msg_len,
	char *hookname, int compile_only)
{
	struct	python_script *py_test_script = NULL;
	int	rc;

	if ((input_file_path == NULL) || (hook_msg == NULL) || (hookname == NULL)) {
		return -1;
	}

	memset(hook_msg, '\0', msg_len);

	if (pbs_python_ext_alloc_python_script(input_file_path,
		(struct python_script **) &py_test_script) == -1) {
		snprintf(hook_msg, msg_len,
			"failed to allocate storage for python script %s",
			input_file_path);
		return -1;
	}

	if (compile_only) {
		rc = pbs_python_check_and_compile_script(&svr_interp_data,
			py_test_script);
	} else {
		rc = pbs_python_run_code_in_namespace(&svr_interp_data,
			py_test_script, 0);
	}
	/* free py_script immediately, as it was used only for */
	/* test compile */
	pbs_python_ext_free_python_script(py_test_script);
	free(py_test_script);
	py_test_script = NULL;

	if (rc != 0) {
		snprintf(hook_msg, msg_len,
			"Failed to validate config file, "
			"hook '%s' config file not overwritten", hookname);
		return (rc);
	}

	return 0;
}

/**
 * @brief
 *		mgr_hook_import	- Processes an "import hook" qmgr request.
 * @note
 *		Returns a reply to the sender of the batch_request.
 * @see
 * 		req_manager
 *
 * @param[in]	preq	- batch_request structure representing request.
 */
void
mgr_hook_import(struct batch_request *preq)
{
	svrattrl *plist, *plx;
	char hookname[PBS_MAXSVRJOBID + 1] = {'\0'};
	hook *phook;
	char content_type[BUFSIZ];
	char content_encoding[BUFSIZ];
	char input_file[MAXPATHLEN + 1];
	char input_file_path[MAXPATHLEN + 1];
	char input_path[MAXPATHLEN + 1] = {'\0'};
	char temp_path[MAXPATHLEN + 1];
	char output_path[MAXPATHLEN + 1] = {'\0'};
	char msgbuf[HOOK_MSG_SIZE];
	char *hook_msg = NULL;
	int overwrite;
	struct python_script *py_test_script = NULL;
	int rc;
	int hook_obj;

	hook_obj = preq->rq_ind.rq_manager.rq_objtype;

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	snprintf(hookname, sizeof(hookname), "%s", preq->rq_ind.rq_manager.rq_objname);

	phook = find_hook(hookname);

	if ((phook == NULL) || phook->pending_delete) {
		pbs_asprintf(&hook_msg, "%s does not exist!", hookname);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		free(hook_msg);
		return;
	}

	/* Normally, only HOOK_SITE hooks can be shown/operated on in qmgr. */
	/* But HOOK_PBS hooks can also be shown if the qmgr request is */
	/* specifically operating on the "pbshook" keyword. */
	if ((phook->type != HOOK_SITE) && (hook_obj != MGR_OBJ_PBS_HOOK)) {
		pbs_asprintf(&hook_msg, "%s not of '%s' type", hookname, HOOKSTR_SITE);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		free(hook_msg);
		return;
	}

	/* Cannot show a HOOK_SITE hook if the qmgr request keyword is */
	/* "pbshook" */
	if ((phook->type == HOOK_SITE) && (hook_obj == MGR_OBJ_PBS_HOOK)) {
		pbs_asprintf(&hook_msg, "%s not of '%s' type", hookname, HOOKSTR_PBS);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		free(hook_msg);
		return;
	}

	sprintf(log_buffer, msg_manager, __func__, preq->rq_user, preq->rq_host);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		hookname, log_buffer);

	plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);
	plx = plist;
	content_type[0] = '\0';
	content_encoding[0] = '\0';
	input_file[0] = '\0';
	while (plx) {

		if (strcasecmp(plx->al_name, CONTENT_TYPE_PARAM) == 0) {
			if (plx->al_value == NULL) {
				pbs_asprintf(&hook_msg, "<%s> is NULL",
					CONTENT_TYPE_PARAM);
				goto mgr_hook_import_error;
			}
			if (hook_obj == MGR_OBJ_PBS_HOOK) {
				if (strcmp(plx->al_value, HOOKSTR_CONFIG) != 0) {
					pbs_asprintf(&hook_msg, "<%s> must be %s",
						CONTENT_TYPE_PARAM, HOOKSTR_CONFIG);
					goto mgr_hook_import_error;
				}
			} else if ((strcmp(plx->al_value, HOOKSTR_CONTENT) != 0) &&
					(strcmp(plx->al_value, HOOKSTR_CONFIG) != 0)) {
				pbs_asprintf(&hook_msg, "<%s> must be %s or %s",
					CONTENT_TYPE_PARAM, HOOKSTR_CONTENT, HOOKSTR_CONFIG);
				goto mgr_hook_import_error;
			}
			strcpy(content_type, plx->al_value);
		} else if (strcasecmp(plx->al_name,
			CONTENT_ENCODING_PARAM) == 0) {
			if (plx->al_value == NULL) {
				pbs_asprintf(&hook_msg, "<%s> is NULL",
					CONTENT_ENCODING_PARAM);
				goto mgr_hook_import_error;
			}
			if ((strcmp(plx->al_value, HOOKSTR_DEFAULT) != 0) &&
					(strcmp(plx->al_value, HOOKSTR_BASE64) != 0)) {
				pbs_asprintf(&hook_msg, "<%s> must be '%s' or '%s'",
					CONTENT_ENCODING_PARAM,
					HOOKSTR_DEFAULT, HOOKSTR_BASE64);
				goto mgr_hook_import_error;
			}
			strcpy(content_encoding, plx->al_value);
		} else if (strcasecmp(plx->al_name, INPUT_FILE_PARAM) == 0) {
			if (plx->al_value == NULL) {
				pbs_asprintf(&hook_msg, "input-file is NULL");
				goto mgr_hook_import_error;
			}

			if (is_full_path(plx->al_value)) {
				pbs_asprintf(&hook_msg, "<%s> path must be relative to %s",
					INPUT_FILE_PARAM, path_hooks_workdir);
				goto mgr_hook_import_error;
			}
			strcpy(input_file, plx->al_value);
		} else {
			pbs_asprintf(&hook_msg, "unrecognized parameter - %s",
				plx->al_name);
			goto mgr_hook_import_error;
		}

		plx = (struct svrattrl *)GET_NEXT(plx->al_link);
	}


	if (strcmp(content_type, HOOKSTR_CONFIG) == 0) {
		char *p;
		FILE	*temp_fp;
		char	*p2;
		char	tempfile_path[MAXPATHLEN + 1];

		p = strrchr(input_file, '.');
		if (p != NULL) {
			if (!in_string_list(p, ' ', VALID_HOOK_CONFIG_SUFFIX)) {
				pbs_asprintf(&hook_msg,
					"<%s> contains an invalid suffix, "
					"should be one of: %s",
					INPUT_FILE_PARAM,
					VALID_HOOK_CONFIG_SUFFIX);
				goto mgr_hook_import_error;
			}

			if (strcmp(p, ".py") == 0) {
				snprintf(input_file_path, sizeof(input_file_path),
					"%s%s", path_hooks_workdir, input_file);
				rc = py_compile_and_run(input_file_path, msgbuf,
					sizeof(msgbuf) - 1, hookname, 1);
				if (rc != 0) {
					hook_msg = strdup(msgbuf);
					goto mgr_hook_import_error;
				}
			} else if (strcmp(p, ".ini") == 0) {
				snprintf(input_file_path, sizeof(input_file_path),
					"%s%s", path_hooks_workdir, input_file);
				snprintf(tempfile_path, sizeof(tempfile_path),
					"%s", input_file_path);
				p2 = strrchr(tempfile_path, '.');
				*p2 = '\0';
				temp_fp = fopen(tempfile_path, "w");
				if (temp_fp != NULL) {
					fprintf(temp_fp, "import ConfigParser\n");
					fprintf(temp_fp,
						"Config  = ConfigParser.RawConfigParser()\n");
					fprintf(temp_fp,
						"Config.read(\"%s\")\n", input_file_path);
					fclose(temp_fp);
					rc = py_compile_and_run(tempfile_path,
						msgbuf, sizeof(msgbuf) - 1,
						hookname, 0);
					if (rc != 0) {
						hook_msg = strdup(msgbuf);
						goto mgr_hook_import_error;
					}
				}
			} else if (strcmp(p, ".json") == 0) {
				snprintf(input_file_path, sizeof(input_file_path),
					"%s%s", path_hooks_workdir, input_file);
				snprintf(tempfile_path, sizeof(tempfile_path),
					"%s", input_file_path);
				p2 = strrchr(tempfile_path, '.');
				*p2 = '\0';
				temp_fp = fopen(tempfile_path, "w");
				if (temp_fp != NULL) {
					fprintf(temp_fp,
						"import json\n");
					fprintf(temp_fp,
						"fd = open(\"%s\")\n",
						input_file_path);
					fprintf(temp_fp,
						"json.load(fd)\n");
					fprintf(temp_fp,
						"fd.close()\n");
					fclose(temp_fp);
					rc = py_compile_and_run(tempfile_path,
						msgbuf, sizeof(msgbuf) - 1,
						hookname, 0);
					if (rc != 0) {
						hook_msg = strdup(msgbuf);
						goto mgr_hook_import_error;
					}
				}
			}
		}
	}

	snprintf(input_path, sizeof(input_path),
		"%s%s", path_hooks_workdir, input_file);
	snprintf(temp_path, sizeof(temp_path), "%s%.*s.tmp", path_hooks_workdir,
		(int)(sizeof(temp_path) - strlen(input_file) - 5), input_file);

	if (strcmp(content_type, HOOKSTR_CONTENT) == 0) {
		snprintf(output_path, sizeof(output_path),
			"%s%s%s", path_hooks, hookname,
			HOOK_SCRIPT_SUFFIX);
	} else if (strcmp(content_type, HOOKSTR_CONFIG) == 0) {
		snprintf(output_path, sizeof(output_path),
			"%s%s%s", path_hooks, hookname,
			HOOK_CONFIG_SUFFIX);
	} else {
		pbs_asprintf(&hook_msg, "<%s> is unknown", CONTENT_TYPE_PARAM);
		goto mgr_hook_import_error;
	}

	overwrite = 0;
	if (file_exists(output_path))
		overwrite = 1;

	if (strcmp(content_type, HOOKSTR_CONFIG) == 0) {
		if (decode_hook_content(input_path, output_path, content_encoding,
				msgbuf, sizeof(msgbuf)) != 0) {
			hook_msg = strdup(msgbuf);
			goto mgr_hook_import_error;
		}
		if (overwrite) {
			pbs_asprintf(&hook_msg,
				"hook '%s' contents overwritten", hookname);
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, __func__, hook_msg);
			reply_text(preq, 0, hook_msg);
			free(hook_msg);
		} else {
			reply_ack(preq);
		}
		phook->hook_config_checksum = crc_file(output_path);

		if (phook->event & MOM_EVENTS)
			add_pending_mom_hook_action(NULL, phook->hook_name,
				MOM_HOOK_ACTION_SEND_CONFIG);

		return;

	} else {
		if (decode_hook_content(input_path, temp_path, content_encoding,
				msgbuf, sizeof(msgbuf)) != 0) {
			hook_msg = strdup(msgbuf);
			goto mgr_hook_import_error;
		}
	}

	/* create a py_script */
	if (pbs_python_ext_alloc_python_script(temp_path,
		(struct python_script **) &py_test_script) == -1) {
		pbs_asprintf(&hook_msg,
			"failed to allocate storage for python script %s",
			temp_path);
		unlink(temp_path);
		goto mgr_hook_import_error;
	}
	/* try compiling the now decoded file */
	rc=pbs_python_check_and_compile_script(&svr_interp_data,
		py_test_script);
	/* free py_script immediately, as it was used only for test compile */
	pbs_python_ext_free_python_script(py_test_script);
	free(py_test_script);

	if (rc != 0) {
		if (overwrite)
			pbs_asprintf(&hook_msg,
				"Failed to compile script, "
				"hook '%s' contents not overwritten",
				hookname);
		else
			pbs_asprintf(&hook_msg, "Failed to compile script");

		unlink(temp_path);
		goto mgr_hook_import_error;
	}

	/* now actually overwrite the old file, no decoding this time */
	if (decode_hook_content(temp_path, output_path, HOOKSTR_DEFAULT,
			msgbuf, sizeof(msgbuf)) != 0) {
		hook_msg = strdup(msgbuf);
		unlink(temp_path);
		goto mgr_hook_import_error;
	}

	unlink(temp_path);

	if (overwrite) {
		pbs_asprintf(&hook_msg, "hook '%s' contents overwritten", hookname);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_INFO, __func__, hook_msg);
		reply_text(preq, 0, hook_msg);
		free(hook_msg);
	} else {
		reply_ack(preq);
	}

	if (phook->script) {
		pbs_python_ext_free_python_script(phook->script);
		free(phook->script);
		phook->script = NULL;
	}

	if (pbs_python_ext_alloc_python_script(output_path,
		(struct python_script **)&phook->script) == -1) {
		pbs_asprintf(&hook_msg,
			"failed to allocate storage for python script %s",
			output_path);
		goto mgr_hook_import_error;
	}

	phook->hook_script_checksum = crc_file(output_path);

	if (phook->event & HOOK_EVENT_PROVISION)
		set_srv_prov_attributes(); /* check and set prov attributes */

	if (phook->event & MOM_EVENTS)
		add_pending_mom_hook_action(NULL, phook->hook_name,
			MOM_HOOK_ACTION_SEND_SCRIPT);

	if (phook->event & HOOK_EVENT_PERIODIC)
	{
		set_srv_pwr_prov_attribute(); /* check and set power attributes */
		if ((phook->enabled == TRUE) && (phook->freq > 0)) {
			/* Search and delete all already existing periodic hook task */
			delete_task_by_parm1 (phook, DELETE_ALL);
			(void)set_task(WORK_Timed, time_now+phook->freq, run_periodic_hook, phook);
		}
	}
	return;

mgr_hook_import_error:
	unlink(temp_path);
	reply_text(preq, PBSE_HOOKERROR, hook_msg);
	log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, hook_msg);
	free(hook_msg);
}


/**
 * @brief
 *		mgr_hook_export	- Processes an "export hook" qmgr request.
 * @note
 *		Returns a reply to the sender of the batch_request.
 *
 * @see
 * 		req_manager
 *
 * @param[in]	preq	- batch_request structure representing the request.
 */
void
mgr_hook_export(struct batch_request *preq)
{
	svrattrl *plist, *plx;
	char hookname[PBS_MAXSVRJOBID + 1] = {'\0'};
	hook *phook;
	char content_type[BUFSIZ];
	char content_encoding[BUFSIZ];
	char output_file[MAXPATHLEN + 1];
	char input_path[MAXPATHLEN + 1] = {'\0'};
	char output_path[MAXPATHLEN + 1] = {'\0'};
	char hook_msg[HOOK_MSG_SIZE] = {'\0'};
	int hook_obj;

	hook_obj = preq->rq_ind.rq_manager.rq_objtype;

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	snprintf(hookname, sizeof(hookname), "%.*s", PBS_MAXSVRJOBID,
		preq->rq_ind.rq_manager.rq_objname);

	/* Else one and only one vhook */
	phook = find_hook(hookname);

	if ((phook == NULL) || phook->pending_delete) {
		snprintf(hook_msg, sizeof(hook_msg), "%s does not exist!", hookname);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	/* Normally, only HOOK_SITE hooks can be shown/operated on in qmgr. */
	/* But HOOK_PBS hooks can also be shown if the qmgr request is */
	/* specifically operating on the "pbshook" keyword. */
	if ((phook->type != HOOK_SITE) && (hook_obj != MGR_OBJ_PBS_HOOK)) {
		snprintf(hook_msg, sizeof(hook_msg), "%s not of '%s' type", hookname, HOOKSTR_SITE);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	/* Cannot show a HOOK_SITE hook if the qmgr request keyword is */
	/* "pbshook" */
	if ((phook->type == HOOK_SITE) && (hook_obj == MGR_OBJ_PBS_HOOK)) {
		snprintf(hook_msg, sizeof(hook_msg), "%s not of '%s' type", hookname, HOOKSTR_PBS);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	sprintf(log_buffer, msg_manager, __func__, preq->rq_user, preq->rq_host);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		hookname, log_buffer);

	plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);
	plx = plist;
	content_type[0] = '\0';
	content_encoding[0] = '\0';
	output_file[0] = '\0';
	while (plx) {

		if (strcasecmp(plx->al_name, CONTENT_TYPE_PARAM) == 0) {
			if (plx->al_value == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> is NULL", CONTENT_TYPE_PARAM);
				goto mgr_hook_export_error;
			}
			if (hook_obj == MGR_OBJ_PBS_HOOK) {
				if (strcmp(plx->al_value, HOOKSTR_CONFIG) != 0) {
					snprintf(hook_msg, sizeof(hook_msg),
						"<%s> must be %s",
						CONTENT_TYPE_PARAM, HOOKSTR_CONFIG);
					goto mgr_hook_export_error;
				}
			} else if ((strcmp(plx->al_value,
				HOOKSTR_CONTENT) != 0) &&
				(strcmp(plx->al_value, HOOKSTR_CONFIG) != 0)) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> must be %s",
					CONTENT_TYPE_PARAM, HOOKSTR_CONTENT);
				goto mgr_hook_export_error;
			}
			strcpy(content_type, plx->al_value);
		} else if (strcasecmp(plx->al_name,
			CONTENT_ENCODING_PARAM) == 0) {
			if (plx->al_value == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> is NULL", CONTENT_ENCODING_PARAM);
				goto mgr_hook_export_error;
			}
			if ((strcmp(plx->al_value, HOOKSTR_DEFAULT) != 0) &&
				(strcmp(plx->al_value, HOOKSTR_BASE64) != 0)) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> must be '%s' or '%s'",
					CONTENT_ENCODING_PARAM,
					HOOKSTR_DEFAULT, HOOKSTR_BASE64);
				goto mgr_hook_export_error;
			}
			strcpy(content_encoding, plx->al_value);
		} else if (strcasecmp(plx->al_name, OUTPUT_FILE_PARAM) == 0) {
			if (plx->al_value == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> is NULL", OUTPUT_FILE_PARAM);
				goto mgr_hook_export_error;
			}

			if (is_full_path(plx->al_value)) {
				snprintf(hook_msg, sizeof(hook_msg),
					"<%s> path must be relative to %s",
					OUTPUT_FILE_PARAM, path_hooks_workdir);
				goto mgr_hook_export_error;
			}
			strcpy(output_file, plx->al_value);
		} else {
			snprintf(hook_msg, sizeof(hook_msg),
				"unrecognized parameter - %s",
				plx->al_name);
			goto mgr_hook_export_error;
		}

		plx = (struct svrattrl *)GET_NEXT(plx->al_link);
	}


	if (strcmp(content_type, HOOKSTR_CONTENT) == 0) {
		snprintf(input_path, MAXPATHLEN, "%s%s%s",
			path_hooks, hookname, HOOK_SCRIPT_SUFFIX);
	} else if (strcmp(content_type, HOOKSTR_CONFIG) == 0) {
		snprintf(input_path, MAXPATHLEN, "%s%s%s",
			path_hooks, hookname, HOOK_CONFIG_SUFFIX);
	} else {
		snprintf(hook_msg, sizeof(hook_msg),
			"<%s> is unknown", CONTENT_TYPE_PARAM);
		goto mgr_hook_export_error;
	}

	snprintf(output_path, sizeof(output_path), "%s%.*s", path_hooks_workdir,
		(int)(sizeof(output_path) - strlen(output_file)), output_file);
	if (encode_hook_content(input_path, output_path, content_encoding,
		hook_msg, sizeof(hook_msg)) != 0)
		goto mgr_hook_export_error;
	reply_ack(preq);
	return;

mgr_hook_export_error:
	reply_text(preq, PBSE_HOOKERROR, hook_msg);
	log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK, LOG_INFO,
		__func__, hook_msg);
}

#define COPY_HOOK_SAVE		0
#define COPY_HOOK_RESTORE	1
/**
 * @brief
 * 		copy_hook	- Copies some of the attribute values
 * 		(order, type, enabled, user, event, alarm) of 'src_hook' into 'dst_hook'.
 * 		If mode is COPY_HOOK_SAVE, then just do a straight copy of the values.
 * 		If mode is COPY_HOOK_RESTORE, then populate 'dst_hook' by
 * 		invoking the native set_hook* functions, in order to update any
 * 		dependencies. For example, if order is updated, then various system hooks
 * 		lists would automatically be updated to reflect the new ordering.
 *
 *	@param[in]	src_hook	- source hook from which values needs to be copied.
 *	@param[in]	dst_hook	- destination hook to which values needs to be copied.
 *	@param[in]	mode		- mode - whether direct copying or dependencies needs to be updated.
 */
static void
copy_hook(hook *src_hook, hook *dst_hook, int mode)
{
	char	hook_msg[HOOK_MSG_SIZE];

	if ((src_hook == NULL) || (dst_hook == NULL)) {
		return;
	}

	switch (mode) {
		case COPY_HOOK_SAVE:
			dst_hook->order = src_hook->order;
			dst_hook->type = src_hook->type;
			dst_hook->enabled = src_hook->enabled;
			dst_hook->user = src_hook->user;
			dst_hook->fail_action = src_hook->fail_action;
			dst_hook->debug = src_hook->debug;
			dst_hook->event = src_hook->event;
			dst_hook->alarm = src_hook->alarm;
			dst_hook->freq = src_hook->freq;
			break;
		case COPY_HOOK_RESTORE:
			(void)set_hook_order(dst_hook,
				hook_order_as_string(src_hook->order), hook_msg,
				sizeof(hook_msg));
			(void)set_hook_type(dst_hook,
				hook_type_as_string(src_hook->type), hook_msg,
				sizeof(hook_msg), 1);
			(void)set_hook_enabled(dst_hook,
				hook_enabled_as_string(src_hook->enabled), hook_msg,
				sizeof(hook_msg));
			(void)set_hook_user(dst_hook,
				hook_user_as_string(src_hook->user), hook_msg,
				sizeof(hook_msg), 0);
			(void)set_hook_fail_action(dst_hook,
				hook_fail_action_as_string(src_hook->fail_action), hook_msg,
				sizeof(hook_msg), 0);
			(void)set_hook_debug(dst_hook,
				hook_debug_as_string(src_hook->debug), hook_msg,
				sizeof(hook_msg));
			(void)set_hook_event(dst_hook,
				hook_event_as_string(src_hook->event), hook_msg,
				sizeof(hook_msg));
			(void)set_hook_alarm(dst_hook,
				hook_alarm_as_string(src_hook->alarm), hook_msg,
				sizeof(hook_msg));
			(void)set_hook_freq(dst_hook,
				hook_freq_as_string(src_hook->freq), hook_msg,
				sizeof(hook_msg));
			break;
	}
}

/**
 * @brief
 * 		mgr_hook_set	- Sets hook attributes
 *
 *		Finds the set of hooks, either one specified, or all for a host.
 *		Sets the request attributes on that set.
 *		returns a reply to the sender of the batch_request
 * @note
 * 		This is an atomic operation - either all the listed attributes
 * 		are set or none at all - uses copy_hook() to save/restore values.
 *
 * @see
 * 		req_manager
 *
 * @param[in]	preq	- batch_request structure representing the request.
 */
void
mgr_hook_set(struct batch_request *preq)

{
	svrattrl *plist, *plx;
	char hookname[PBS_MAXSVRJOBID + 1] = {'\0'};
	hook *phook;
	int num_set = 0;
	int got_event = 0;	/* event attribute operated on */
	char hook_msg[HOOK_MSG_SIZE] = {'\0'};
	hook shook;
	unsigned int prev_phook_event = 0;
	char *hook_user_val = NULL;
	char *hook_fail_action_val = NULL;
	enum batch_op hook_fail_action_op = DFLT;
	char *hook_freq_val = NULL;
	int hook_obj;

	hook_obj = preq->rq_ind.rq_manager.rq_objtype;

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	snprintf(hookname, sizeof(hookname), "%s", preq->rq_ind.rq_manager.rq_objname);

	phook = find_hook(hookname);

	if ((phook == NULL) || phook->pending_delete) {
		snprintf(hook_msg, sizeof(hook_msg), "%s does not exist!", hookname);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	prev_phook_event = phook->event;

	if ((phook->type == HOOK_PBS) && (hook_obj != MGR_OBJ_PBS_HOOK)) {
		snprintf(log_buffer, sizeof(log_buffer),
				"cannot set attributes of a '%s' hook named %s", HOOKSTR_PBS, phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, log_buffer);
		snprintf(log_buffer, sizeof(log_buffer), "cannot set attributes of a '%s' hook", HOOKSTR_PBS);
		reply_text(preq, PBSE_HOOKERROR, log_buffer);
		return;
	}

	if ((phook->type == HOOK_SITE) && (hook_obj == MGR_OBJ_PBS_HOOK)) {
		snprintf(log_buffer, sizeof(log_buffer),
				"cannot set attributes of a '%s' hook named %s", HOOKSTR_SITE, phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, log_buffer);
		snprintf(log_buffer, sizeof(log_buffer), "cannot set attributes of a '%s' hook", HOOKSTR_SITE);
		reply_text(preq, PBSE_HOOKERROR, log_buffer);
		return;
	}

	sprintf(log_buffer, msg_manager, __func__, preq->rq_user, preq->rq_host);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		hookname, log_buffer);

	copy_hook(phook, &shook, COPY_HOOK_SAVE);

	plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);
	plx = plist;
	while (plx) {

		if (strcasecmp(plx->al_name, HOOKATT_TYPE) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			if (set_hook_type(phook, plx->al_value,
				hook_msg, sizeof(hook_msg), 0) != 0)
				goto mgr_hook_set_error;
			num_set++;
		} else if (strcasecmp(plx->al_name, HOOKATT_ENABLED) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			if (set_hook_enabled(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_set_error;
			if (phook->event & HOOK_EVENT_PERIODIC) {
				if ((phook->enabled == TRUE) && (phook->freq > 0)) {
					/* Delete all existing work tasks
					 * There might be two of them:
					 *  1 - related to running the next occurance
					 *  2 - related to running the post processing function
					 */
					delete_task_by_parm1(phook, DELETE_ALL);
					if ((phook->freq > 0) && (phook->script != NULL))
						(void)set_task(WORK_Timed, time_now + phook->freq,
								run_periodic_hook, phook);
					else {
						sprintf(log_buffer, "periodic hook is missing information, check hook frequency and script");
						log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
							hookname, log_buffer);
						snprintf(hook_msg, sizeof(hook_msg),
							"periodic hook is missing information, check hook frequency and script");
						goto mgr_hook_set_error;
					}
				}
				else
					/* Delete any existing work task */
					delete_task_by_parm1(phook, DELETE_ALL);
			}
			num_set++;
		} else if (strcasecmp(plx->al_name, HOOKATT_DEBUG) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			if (set_hook_debug(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_set_error;
			num_set++;
		} else if (strcasecmp(plx->al_name, HOOKATT_USER) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;

			/* setting hook user value must be a deferred action, */
			/* as it is dependent on event having */
			/* execjob_prologue, execjob_epilogue, or */
			/* execjob_preterm being set. The event set action */
			/* could appear after this set user action. */
			if (hook_user_val != NULL)
				free(hook_user_val);
			hook_user_val = strdup(plx->al_value);
			if (hook_user_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_set_error;
			}
		} else if (strcasecmp(plx->al_name, HOOKATT_FAIL_ACTION) == 0) {
			/* setting hook fail_action value must be a deferred action, */
			/* as it is dependent on event having */
			/* mom hook event being set. The event set action */
			/* could appear after this set fail_action. */
			if (hook_fail_action_val != NULL)
				free(hook_fail_action_val);
			hook_fail_action_val = strdup(plx->al_value);
			if (hook_fail_action_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_set_error;
			}
			hook_fail_action_op = plx->al_op;

		} else if (strcasecmp(plx->al_name, HOOKATT_EVENT) == 0) {

			got_event = 1;

			switch (plx->al_op) {
				case SET:
					if (set_hook_event(phook, plx->al_value,
						hook_msg, sizeof(hook_msg)) != 0)
						goto mgr_hook_set_error;

					/* if exechost_periodic disappears, then */
					/* unset hook freq value */
					if((phook->event & HOOK_EVENT_EXECHOST_PERIODIC) == 0) {
						phook->freq = HOOK_FREQ_DEFAULT;
					}

					if ((phook->event & USER_MOM_EVENTS) == 0) {
						phook->user = HOOK_PBSADMIN;
					}
					if ((phook->event & FAIL_ACTION_EVENTS) == 0) {
						phook->fail_action = HOOK_FAIL_ACTION_NONE;
					}
					num_set++;
					break;
				case INCR:
					if (add_hook_event(phook, plx->al_value,
						hook_msg, sizeof(hook_msg)) != 0)
						goto mgr_hook_set_error;
					num_set++;
					break;
				case DECR:
					if (del_hook_event(phook, plx->al_value,
						hook_msg, sizeof(hook_msg)) != 0)
						goto mgr_hook_set_error;

					/* if exechost_periodic disappears, then */
					/* unset hook freq value */
					if((phook->event & HOOK_EVENT_EXECHOST_PERIODIC) == 0) {
						phook->freq = HOOK_FREQ_DEFAULT;
					}

					if ((phook->event & USER_MOM_EVENTS) == 0) {
						phook->user = HOOK_PBSADMIN;
					}
					if ((phook->event & FAIL_ACTION_EVENTS) == 0) {
						phook->fail_action = HOOK_FAIL_ACTION_NONE;
					}
					if ((phook->event & HOOK_EVENT_EXECJOB_BEGIN) == 0) {
						phook->fail_action &= ~HOOK_FAIL_ACTION_SCHEDULER_RESTART_CYCLE;
					}
					if ((phook->event & HOOK_EVENT_EXECHOST_STARTUP) == 0) {
						phook->fail_action &= ~HOOK_FAIL_ACTION_CLEAR_VNODES;
					}
					num_set++;
					break;
				default:
					snprintf(hook_msg, sizeof(hook_msg),
						"%s - %s:%d", msg_internal,
						plx->al_name, plx->al_op);
					goto mgr_hook_set_error;
			}

		} else if (strcasecmp(plx->al_name, HOOKATT_ORDER) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			if (phook->event & HOOK_EVENT_PERIODIC) {
				sprintf(log_buffer, "Setting order for a periodic hook has no effect");
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
					hookname, log_buffer);
			} else if (set_hook_order(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_set_error;
			num_set++;
		} else if (strcasecmp(plx->al_name, HOOKATT_ALARM) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			if (set_hook_alarm(phook, plx->al_value,
				hook_msg, sizeof(hook_msg)) != 0)
				goto mgr_hook_set_error;
			num_set++;
		} else if (strcasecmp(plx->al_name, HOOKATT_FREQ) == 0) {
			if (plx->al_op != SET)
				goto opnotequal;
			/* setting hook freq value must be a deferred action, */
			/* as it is dependent on event having */
			/* exechost_periodic being set. The event set action */
			/* could appear after this set freq action. */
			if (hook_freq_val != NULL)
				free(hook_freq_val);
			hook_freq_val = strdup(plx->al_value);
			if (hook_freq_val == NULL) {
				snprintf(hook_msg, sizeof(hook_msg),
					"strdup(%s) failed: errno %d",
					plx->al_value, errno);
				goto mgr_hook_set_error;
			}
		} else {
			snprintf(hook_msg, sizeof(hook_msg)-1, "%s - %s",
				msg_noattr, plx->al_name);
			goto mgr_hook_set_error;
		}

		plx = (struct svrattrl *)GET_NEXT(plx->al_link);
	}

	/* Now do the deferred set actions */
	if (hook_user_val != NULL) {
		if (set_hook_user(phook, hook_user_val,
			hook_msg, sizeof(hook_msg), 1) != 0)
			goto mgr_hook_set_error;
		else
			num_set++;
		free(hook_user_val);
		hook_user_val = NULL;
	}
	if (hook_fail_action_val != NULL) {
		switch (hook_fail_action_op) {
			case SET:
				if (set_hook_fail_action(phook,
					hook_fail_action_val,
					hook_msg, sizeof(hook_msg), 1) != 0)
					goto mgr_hook_set_error;
				else

					num_set++;
				break;
			case INCR:
				if (add_hook_fail_action(phook,
					hook_fail_action_val,
					hook_msg, sizeof(hook_msg), 1) != 0)
					goto mgr_hook_set_error;
				else
					num_set++;
				break;
			case DECR:
				if (del_hook_fail_action(phook,
					hook_fail_action_val,
					hook_msg, sizeof(hook_msg)) != 0)
					goto mgr_hook_set_error;
				else
					num_set++;
				break;
			default:
				snprintf(hook_msg, sizeof(hook_msg),
					"%s - %s:%d", msg_internal,
					plx->al_name, plx->al_op);
				goto mgr_hook_set_error;
		}
		free(hook_fail_action_val);
		hook_fail_action_val = NULL;
	}
	if (hook_freq_val != NULL) {
		if (set_hook_freq(phook, hook_freq_val,
			hook_msg, sizeof(hook_msg)) != 0)
			goto mgr_hook_set_error;
		else
			num_set++;
		free(hook_freq_val);
		hook_freq_val = NULL;
	}

	if (num_set > 0) {
		if (hook_save(phook) != 0) {
			snprintf(hook_msg, sizeof(hook_msg),
				"Failed to store '%s' permanently.",
				preq->rq_ind.rq_manager.rq_objname);
			goto mgr_hook_set_error;
		}
		if (phook->event & MOM_EVENTS) {
			add_pending_mom_hook_action(NULL, phook->hook_name,
				MOM_HOOK_ACTION_SEND_ATTRS);
			if ((prev_phook_event & MOM_EVENTS) == 0) {
				/* previous hook's event did not include a */
				/* mom hook-related event, but current */
				/* one does due to a set operation */
				mom_hooks_seen++;
				if (mom_hooks_seen == 1) {
					/* used to be no mom hooks in the system, */
					/* but now one is introduced. So see if */
					/* resourcedef file changed and need to */
					/* be flagged to be sent to the moms */
					send_rescdef(0);
				}
				add_pending_mom_hook_action(NULL,
					phook->hook_name,
					MOM_HOOK_ACTION_SEND_SCRIPT);
				add_pending_mom_hook_action(NULL,
					phook->hook_name,
					MOM_HOOK_ACTION_SEND_CONFIG);
			}
		} else {
			if (prev_phook_event & MOM_EVENTS) {
				/* previous hook's event include a */
				/* mom hook-related event, but current */
				/* one doesn't due to the set operation */
				add_pending_mom_hook_action(NULL,
					phook->hook_name,
					MOM_HOOK_ACTION_DELETE);
				mom_hooks_seen--;
			}
		}
	}

	if (phook->event & HOOK_EVENT_PROVISION)
		set_srv_prov_attributes(); /* check and set prov attributes */

	if (phook->event & HOOK_EVENT_PERIODIC)
		set_srv_pwr_prov_attribute(); /* check and set power attributes */

	reply_ack(preq);	/*create completely successful*/
	return;

opnotequal:
	snprintf(hook_msg, sizeof(hook_msg), "'%s' operator not =",
		plx->al_name);

mgr_hook_set_error:
	if (hook_user_val != NULL)
		free(hook_user_val);
	if (hook_fail_action_val != NULL)
		free(hook_fail_action_val);
	if (hook_freq_val != NULL)
		free(hook_freq_val);

	if ((num_set > 0) || got_event) {
		/*
		 * got_event of 1 means set_hook_event() was called which
		 * would have automatically initialized phook->event to 0
		 * so we'll need to restore previous value
		 */
		copy_hook(&shook, phook, COPY_HOOK_RESTORE);
	}

	reply_text(preq, PBSE_HOOKERROR, hook_msg);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		__func__, hook_msg);
	return;
}

/**
 * @brief
 * 		mgr_hook_unset	- Unsets hook attributes
 *
 *		Finds the set of hooks, either one specified, or all for a host.
 *		Unsets the request attributes on that set.
 *		returns a reply to the sender of the batch_request
 *
 * @note
 * 		This is an atomic operation - either all the listed attributes
 * 		are unset or none at all - uses copy_hook() to save/restore
 *								values.
 *
 * @param[in]	preq	- batch_request structure representing the request.
 */

void
mgr_hook_unset(struct batch_request *preq)

{
	svrattrl *plist, *plx;
	char hookname[PBS_MAXSVRJOBID + 1] = {'\0'};
	hook *phook;
	int num_unset = 0;
	char hook_msg[HOOK_MSG_SIZE] = {'\0'};
	hook shook;
	unsigned int prev_phook_event;
	int hook_obj;

	hook_obj = preq->rq_ind.rq_manager.rq_objtype;

	if (strlen(preq->rq_ind.rq_manager.rq_objname) == 0) {
		reply_text(preq, PBSE_HOOKERROR, "no hook name specified");
		return;
	}

	snprintf(hookname, sizeof(hookname), "%s", preq->rq_ind.rq_manager.rq_objname);

	/* Else one and only one vhook */
	phook = find_hook(hookname);

	if ((phook == NULL) || phook->pending_delete) {
		snprintf(hook_msg, sizeof(hook_msg), "%s does not exist!", hookname);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, hook_msg);
		reply_text(preq, PBSE_HOOKERROR, hook_msg);
		return;
	}

	prev_phook_event = phook->event;

	if ((phook->type == HOOK_PBS) && (hook_obj != MGR_OBJ_PBS_HOOK)) {
		snprintf(log_buffer, sizeof(log_buffer),
				"cannot unset attributes of a '%s' hook named %s", HOOKSTR_PBS, phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, log_buffer);
		snprintf(log_buffer, sizeof(log_buffer), "cannot unset attributes of a '%s' hook", HOOKSTR_PBS);
		reply_text(preq, PBSE_HOOKERROR, log_buffer);
		return;
	}

	if ((phook->type == HOOK_SITE) && (hook_obj == MGR_OBJ_PBS_HOOK)) {
		snprintf(log_buffer, sizeof(log_buffer),
				"cannot unset attributes of a '%s' hook named %s", HOOKSTR_SITE, phook->hook_name);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, hookname, log_buffer);
		snprintf(log_buffer, sizeof(log_buffer), "cannot unset attributes of a '%s' hook", HOOKSTR_SITE);
		reply_text(preq, PBSE_HOOKERROR, log_buffer);
		return;
	}

	sprintf(log_buffer, msg_manager, __func__, preq->rq_user, preq->rq_host);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO,
		hookname, log_buffer);

	copy_hook(phook, &shook, COPY_HOOK_SAVE);
	plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr);
	plx = plist;
	while (plx) {

		if (strcasecmp(plx->al_name, HOOKATT_TYPE) == 0) {
			if (unset_hook_type(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_ENABLED) == 0) {
			if (unset_hook_enabled(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_DEBUG) == 0) {
			if (unset_hook_debug(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_USER) == 0) {
			if (unset_hook_user(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_FAIL_ACTION) == 0) {
			if (unset_hook_fail_action(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_EVENT) == 0) {
			if (unset_hook_event(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			/* Given that we've set hook's event list to empty, */
			/* then we need to reset the freq, user, fail_action values, */
			/* which are dependent on certain events being */
			/* present. */
			phook->freq = HOOK_FREQ_DEFAULT;
			phook->user = HOOK_USER_DEFAULT;
			phook->fail_action = HOOK_FAIL_ACTION_DEFAULT;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_ORDER) == 0) {
			if (unset_hook_order(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_ALARM) == 0) {
			if (unset_hook_alarm(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else if (strcasecmp(plx->al_name, HOOKATT_FREQ) == 0) {
			if (unset_hook_freq(phook, hook_msg,
				sizeof(hook_msg)) != 0)
				goto mgr_hook_unset_error;
			num_unset++;
		} else {
			snprintf(hook_msg, sizeof(hook_msg)-1, "%s - %s",
				msg_noattr, plx->al_name);
			goto mgr_hook_unset_error;
		}

		plx = (struct svrattrl *)GET_NEXT(plx->al_link);
	}

	if (num_unset > 0) {
		if (hook_save(phook) != 0) {
			snprintf(hook_msg, sizeof(hook_msg),
				"Failed to store '%s' permanently.",
				preq->rq_ind.rq_manager.rq_objname);
			goto mgr_hook_unset_error;
		}
		if (phook->event & MOM_EVENTS) {
			add_pending_mom_hook_action(NULL, phook->hook_name,
				MOM_HOOK_ACTION_SEND_ATTRS);
		} else {
			if (prev_phook_event & MOM_EVENTS) {
				/* previous hook's event included a */
				/* mom hook-related event, but current */
				/* one doesn't due to the unset operation */
				add_pending_mom_hook_action(NULL,
					phook->hook_name,
					MOM_HOOK_ACTION_DELETE);
				mom_hooks_seen--;
			}
		}
	}

	if (phook->event & HOOK_EVENT_PROVISION)
		set_srv_prov_attributes(); /* check and set prov attributes */

	if (phook->event & HOOK_EVENT_PERIODIC)
		set_srv_pwr_prov_attribute(); /* check and set power attributes */

	reply_ack(preq);	/*unset completely successful*/
	return;

mgr_hook_unset_error:

	if (num_unset > 0)
		copy_hook(&shook, phook, COPY_HOOK_RESTORE);

	reply_text(preq, PBSE_HOOKERROR, hook_msg);
	log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, hook_msg);
}
/*
 ************************************************************************
 *   Hook-related req_stat operations.
 ************************************************************************
 */

/**
 * @brief
 * 		status_hook - Build the status reply for a single hook.
 *
 * @see
 * 		req_stat_hook
 *
 * @param[in]	phook		- ptr to hook to status
 * @param[in]	preq		- batch_request structure representing the request.
 * @param[in,out]	pstathd	- head of list to append status to
 * @param[out]	hook_msg	- output message
 * @param[out]	msg_len		- required length of output message
 *
 * @return      Error code
 * @retval	0  - Success
 * @retval	nonzero  - Failure
 */

int
status_hook(hook *phook, struct batch_request *preq, pbs_list_head *pstathd, char *hook_msg, size_t msg_len)
{
	struct brp_status *pstat;
	svrattrl	  *pal;
	char		  val_str[HOOK_BUF_SIZE];
	char		   *hookname;
	int		  hook_obj;

	/* status_hook() request will not have the object type directly. The extend */
	/* field will determine the object type */
	if (preq->rq_extend != NULL) {
		if (strcmp(preq->rq_extend, PBS_HOOK) == 0) {
			hook_obj = MGR_OBJ_PBS_HOOK;
		} else if (strcmp(preq->rq_extend, SITE_HOOK) == 0) {
			hook_obj = MGR_OBJ_SITE_HOOK;
		} else {
			return (PBSE_HOOKERROR); /* bad hook object type */
		}
	} else {
		hook_obj = MGR_OBJ_SITE_HOOK;
	}
	memset(hook_msg, '\0', msg_len);

	pstat = (struct brp_status *)malloc(sizeof(struct brp_status));
	if (pstat == NULL)
		return (PBSE_SYSTEM);


	hookname = phook->hook_name;
	pstat->brp_objtype = hook_obj;

	(void)strcpy(pstat->brp_objname, hookname);
	CLEAR_LINK(pstat->brp_stlink);
	CLEAR_HEAD(pstat->brp_attr);
	append_link(pstathd, &pstat->brp_stlink, pstat);

	/* add attributes to the status reply */

	pal = (svrattrl *)GET_NEXT(preq->rq_ind.rq_status.rq_attr);

	if (pal) {

		while (pal) {
			val_str[0] = '\0';
			if (strcmp(pal->al_name, HOOKATT_TYPE) == 0) {
				strcpy(val_str, hook_type_as_string(phook->type));
			} else if (strcmp(pal->al_name, HOOKATT_ENABLED) == 0) {
				strcpy(val_str, hook_enabled_as_string(phook->enabled));
			} else if (strcmp(pal->al_name, HOOKATT_USER) == 0) {
				strcpy(val_str, hook_user_as_string(phook->user));
			} else if (strcmp(pal->al_name, HOOKATT_EVENT) == 0) {
				strcpy(val_str, hook_event_as_string(phook->event));
			} else if (strcmp(pal->al_name, HOOKATT_ORDER) == 0) {
				strcpy(val_str, hook_order_as_string(phook->order));
			} else if (strcmp(pal->al_name, HOOKATT_ALARM) == 0) {
				strcpy(val_str, hook_alarm_as_string(phook->alarm));
			} else if ((strcmp(pal->al_name, HOOKATT_FREQ) == 0) &&
				(((phook->event & HOOK_EVENT_EXECHOST_PERIODIC) != 0) ||
				 ((phook->event & HOOK_EVENT_PERIODIC) != 0))) {
				strcpy(val_str, hook_freq_as_string(phook->freq));
			} else if (strcmp(pal->al_name, HOOKATT_DEBUG) == 0) {
				strcpy(val_str, hook_debug_as_string(phook->debug));
			} else if (strcmp(pal->al_name, HOOKATT_FAIL_ACTION) == 0) {
				strcpy(val_str, hook_fail_action_as_string(phook->fail_action));
			} else {
				snprintf(hook_msg, msg_len-1,
					"unknown hook attribute %s", pal->al_name);
				log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK,
					LOG_INFO, hookname, hook_msg);
				return (PBSE_HOOKERROR);
			}

			if (attrlist_add(&pstat->brp_attr, pal->al_name,
				val_str) != 0)
				return (PBSE_INTERNAL);
			pal = (svrattrl *)GET_NEXT(pal->al_link);
		}
	} else { /* return all attribute values */

		if ((attrlist_add(&pstat->brp_attr, HOOKATT_TYPE,
			hook_type_as_string(phook->type)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_ENABLED,
			hook_enabled_as_string(phook->enabled)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_EVENT,
			hook_event_as_string(phook->event)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_USER,
			hook_user_as_string(phook->user)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_ALARM,
			hook_alarm_as_string(phook->alarm)) != 0) ||
			((((phook->event & HOOK_EVENT_EXECHOST_PERIODIC) != 0) ||
			  ((phook->event & HOOK_EVENT_PERIODIC) != 0))&&
			(attrlist_add(&pstat->brp_attr, HOOKATT_FREQ,
			hook_freq_as_string(phook->freq)) != 0)) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_ORDER,
			hook_order_as_string(phook->order)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_DEBUG,
			hook_debug_as_string(phook->debug)) != 0) ||
			(attrlist_add(&pstat->brp_attr, HOOKATT_FAIL_ACTION,
			hook_fail_action_as_string(phook->fail_action)) != 0))
			return (PBSE_INTERNAL);
	}

	return (0);
}

/**
 * @brief
 * 		req_stat_hook - service the Status Hook Request
 *
 *		This request processes the request for status of a single SITE hook or
 *		the set of SITE hooks at the local server..
 *
 *	@see
 *		dispatch_request
 *
 * @param[in]	preq		- ptr to the decoded request
 *
 * @return	void
 */

void
req_stat_hook(struct batch_request *preq)
{
	char		   *name;
	hook		   *phook = NULL;
	struct batch_reply *preply;
	int		    rc   = 0;
	int		    type = 0;
	char		    hook_msg[HOOK_MSG_SIZE];
	int		    hook_obj;

	/*
	 * first, validate the name of the requested object, either
	 * a hook, or null for all hooks
	 */

	name = preq->rq_ind.rq_status.rq_id;

	/* req_stat_hook() will not have the object type directly, but it can */
	/* be determined via the extend field of the request. */
	if (preq->rq_extend != NULL) {
		if (strcmp(preq->rq_extend, PBS_HOOK) == 0) {
			hook_obj = MGR_OBJ_PBS_HOOK;
		} else if (strcmp(preq->rq_extend, SITE_HOOK) == 0) {
			hook_obj = MGR_OBJ_SITE_HOOK;
		} else {
			reply_text(preq, PBSE_HOOKERROR, "baad hook object type");
			return;
		}
	} else {
		hook_obj = MGR_OBJ_SITE_HOOK;
	}

	if (*name == '\0') {	/* match all hooks */
		type = 1;
	} else {
		phook = find_hook(name);
		if ((phook == NULL) || phook->pending_delete) {
			reply_text(preq, PBSE_HOOKERROR, "hook not found");
			return;
		}
	}

	preply = &preq->rq_reply;
	preply->brp_choice = BATCH_REPLY_CHOICE_Status;
	CLEAR_HEAD(preply->brp_un.brp_status);

	if (type == 0) {	/* get status of the one named hook */
		/* can only stat HOOK_SITE hooks */
		if (((hook_obj == MGR_OBJ_PBS_HOOK) &&
		     (phook->type == HOOK_PBS)) ||
		    ((hook_obj == MGR_OBJ_SITE_HOOK) &&
		     (phook->type == HOOK_SITE))) {
			rc = status_hook(phook, preq,
				&preply->brp_un.brp_status,
				hook_msg, sizeof(hook_msg));
		}

	} else {	/* get status of SITE or PBS hooks */

		phook = (hook *)GET_NEXT(svr_allhooks);
		while (phook) {
			if (!phook->pending_delete &&
			    (((hook_obj == MGR_OBJ_PBS_HOOK) &&
			      (phook->type == HOOK_PBS)) ||
			     ((hook_obj == MGR_OBJ_SITE_HOOK) &&
			      (phook->type == HOOK_SITE)))) {
				rc = status_hook(phook, preq,
					&preply->brp_un.brp_status, hook_msg,
					sizeof(hook_msg));
				if (rc != 0)
					break;
			}
			phook = (hook *)GET_NEXT(phook->hi_allhooks);
		}
	}
	if (rc) {
		if (hook_msg[0] == '\0')
			req_reject(rc, 0, preq);
		else
			reply_text(preq, PBSE_HOOKERROR, hook_msg);
	} else {
		(void)reply_send(preq);
	}
}

/**
 * @brief
 * 		This function will set pjobs Execution_Time attribute value to the value
 * 		corresponding to new_exec_time_str.
 *
 * @see
 * 		do_runjob_reject_actions
 *
 * @param[in,out]	pjob - job structure
 * @param[in]		new_exec_time_str - new execution time which needs to be placed in job structure
 * @param[out]		msg - filled in with actual error message of this function fails
 * @param[in]		msg_len - size of 'msg' buffer.
 * @param[in]		hook_name - the name of the hook where the set attribute
 *					function is called.
 *
 * @return int
 * @retval 	0	for success.
 * @retval	!= 0	otherwise.
 */
static int
set_exec_time(job *pjob, char *new_exec_time_str, char *msg,
	int msg_len, char *hook_name)
{

	int	rc = 1;
	long	new_exec_time;
	char	*exec_time_ctime;

	if ((msg == NULL) || (msg_len <= 0)) {
		log_err(PBSE_INTERNAL, __func__, "Bad msg buffer parameter!");
		return (2);
	}

	if ((pjob == NULL) || (new_exec_time_str == NULL) ||
		(hook_name == NULL)) {
		snprintf(msg, msg_len -1,
			"%s: bad pjob, new_exec_time_str, or hook_name!", __func__);
		return (2);
	}

	new_exec_time = atol(new_exec_time_str);

	if (new_exec_time == 0) {
		snprintf(msg, msg_len -1,
			"%s: Failed to convert %s into long", __func__, new_exec_time_str);
		return (2);
	}

	exec_time_ctime = ctime(&new_exec_time);
	if (exec_time_ctime == NULL) {
		snprintf(msg, msg_len -1,
			"%s: Failed to decode new_exec_time into ctime str", __func__);
		return (2);
	}
	exec_time_ctime[strlen(exec_time_ctime)-1] = '\0';

	job_attr_def[(int)JOB_ATR_exectime].at_free(
		&pjob->ji_wattr[(int)JOB_ATR_exectime]);

	rc = job_attr_def[(int)JOB_ATR_exectime].at_decode(
		&pjob->ji_wattr[(int)JOB_ATR_exectime],
		NULL,
		NULL,
		new_exec_time_str);

	if (rc == 0) {
		if (job_attr_def[(int)JOB_ATR_exectime].at_action) {
			rc = job_attr_def[(int)JOB_ATR_exectime].at_action(
				&pjob->ji_wattr[(int)JOB_ATR_exectime],
				pjob, ATR_ACTION_ALTER);
			if (rc != 0) {
				log_err(PBSE_INTERNAL, __func__,
					"Failed executing JOB_ATR_exectime action function.");
			}
		}
	} else {
		log_err(PBSE_INTERNAL, __func__,
			"Failed decoding a value for JOB_ATR_exectime.");
	}

	if (rc != 0) {
		snprintf(msg, msg_len-1,
			"'%s' hook failed to set job's %s = %s",
			hook_name,
			ATTR_a,
			exec_time_ctime);
		job_attr_def[(int)JOB_ATR_exectime].at_free(
			&pjob->ji_wattr[(int)JOB_ATR_exectime]);
	} else {
		int newstate, newsub;
		FILE	*fp_debug_out = NULL;

		snprintf(msg, msg_len-1,
			"'%s' hook set job's %s = %s",
			hook_name,
			ATTR_a,
			exec_time_ctime);
		pjob->ji_modified = 1;
		svr_evaljobstate(pjob, &newstate, &newsub, 0);
		(void)svr_setjobstate(pjob, newstate, newsub);

		fp_debug_out = pbs_python_get_hook_debug_output_fp();
		if (fp_debug_out != NULL) {
			fprintf(fp_debug_out, "%s.%s=%ld\n", EVENT_JOB_OBJECT, ATTR_a, new_exec_time);
		}
	}

	return (rc);

}

/**
 * @brief
 * 		This takes care of setting pjobs Hold_Types attribute to the
 * 		new_hold_types_str.
 *
 * @see
 * 		do_runjob_reject_actions
 *
 * @param[in,out]	pjob - job structure
 * @param[in]		new_hold_types_str - new hold type which needs to be placed in job structure
 * @param[out]		msg - Any messages resulting from the action is logged in 'msg'
 * @param[in]		opval - set or unset will do on pjob based on the value of opval
 * @param[in]		delval - if unset is chosen delval will be assigned instead of 'new_hold_types_str'
 * @param[in]		msg_len - 'msg' is up to 'msg_len' size.
 * @param[in]		hook_name - the name of the hook where the set attribute
 *					function is called.
 *
 * @return int
 * @retval 	0	for success.
 * @retval	!= 0	otherwise.
 */
static int
set_hold_types(job *pjob, char *new_hold_types_str,
	char *opval, char *delval, char *msg, int msg_len, char *hook_name)
{
	long	  old_hold;
	int	  do_release;
	int	  rc;
	int	  newstate;
	int	  newsub;

	if ((msg == NULL) || (msg_len <= 0)) {
		log_err(PBSE_INTERNAL, __func__, "Bad msg buffer parameter");
		return (2);
	}

	if ((pjob == NULL) || (new_hold_types_str == NULL) ||
		(opval == NULL) || (delval == NULL) || (hook_name == NULL)) {
		snprintf(msg, msg_len -1,
			"%s: bad pjob, new_hold_types_str, or hook_name", __func__);
		return (2);
	}

	if (strcmp(opval, PY_OPVAL_SUB) == 0)
		do_release = 1;
	else
		do_release = 0;

	old_hold = pjob->ji_wattr[(int)JOB_ATR_hold].at_val.at_long;

	rc = job_attr_def[(int)JOB_ATR_hold].at_decode(
		&pjob->ji_wattr[(int)JOB_ATR_hold],
		ATTR_h,
		NULL,
		new_hold_types_str);

	if (rc != 0) {
		log_err(PBSE_INTERNAL, __func__,
			"Failed decoding a value for JOB_ATR_hold.");
		snprintf(msg, msg_len -1,
			"'%s' hook failed to %s job's %s = %s",
			hook_name,
			(do_release?"unset":"set"),
			ATTR_h,
			(do_release?delval:new_hold_types_str));
		job_attr_def[(int)JOB_ATR_hold].at_free(
			&pjob->ji_wattr[(int)JOB_ATR_hold]);
		return (rc);
	}

	snprintf(msg, msg_len-1,
		"'%s' hook %s job's %s = %s",
		hook_name,
		(do_release?"unset":"set"),
		ATTR_h,
		(do_release?delval:new_hold_types_str));

	if (!do_release &&
		(pjob->ji_wattr[(int)JOB_ATR_hold].at_val.at_long != 0)) {
		time_t	now;
		char	date[32];
		char	buf[HOOK_BUF_SIZE];
		/* Note the hold time in the job comment. */
		now = time(NULL);
		snprintf(date, sizeof(date), "%s", (const char *)ctime(&now));
		(void)sprintf(buf, "Job held by '%s' hook on %s",
			hook_name, date);
		job_attr_def[(int)JOB_ATR_Comment].at_decode(
			&pjob->ji_wattr[(int)JOB_ATR_Comment],
			NULL, NULL, buf);
	}

	if (old_hold != pjob->ji_wattr[(int)JOB_ATR_hold].at_val.at_long) {
		FILE	*fp_debug_out = NULL;
		/* indicate attributes changed */
		pjob->ji_modified = 1;
		svr_evaljobstate(pjob, &newstate, &newsub, 0);
		(void)svr_setjobstate(pjob, newstate, newsub);

		fp_debug_out = pbs_python_get_hook_debug_output_fp();
		if (fp_debug_out != NULL) {
			fprintf(fp_debug_out, "%s.%s=%s\n", EVENT_JOB_OBJECT, ATTR_h, new_hold_types_str);
		}

	}

	return (rc);
}


/**
 * @brief
 *    	set_attribute	- This function will set pjob's attribute internally indexed
 *     						at 'attr_index' to 'new_str'.
 *
 * @see
 * 		set_job_varlist, do_runjob_accept_actions and do_runjob_reject_actions
 *
 * @param[in]	pjob - job in question
 * @param[in]	attr_index - index to internal job table holding attribute info
 * @param[in]	msg - filled in with actual error message of this function fails
 * @param[in]	msg_len - size of 'msg' buffer.
 * @param[in]	hook_name - the name of the hook where the set attribute
 *				function is called.
 *
 * @return int
 * @retval 	0	for success.
 * @retval	!= 0	otherwise.
 */
static int
set_attribute(job *pjob, int attr_index,
	char *new_str, char *msg, int msg_len, char *hook_name)
{

	int	rc = 1;
	char	*attr_name = NULL;
	char	*new_attrval_str = NULL;

	if ((msg == NULL) || (msg_len <= 0)) {
		log_err(PBSE_INTERNAL, __func__, "Bad msg buffer parameter!");
		return (2);
	}

	if ((pjob == NULL) || (new_str == NULL) || (hook_name == NULL)) {
		snprintf(msg, msg_len -1,
			"%s: bad pjob, new_attrval_str, or hook_name!", __func__);
		return (2);
	}

	attr_name = job_attr_def[attr_index].at_name;
	if (attr_name == NULL) {
		snprintf(msg, msg_len -1,
			"%s: bad job attribute name indexed at '%d'!", __func__,
			attr_index);
		return (2);
	}

	/*
	 * Need to dup 'new_str' for if fed to job attribute's decode function,
	 * cannot guarantee that the value will not get "munged".
	 */
	new_attrval_str = strdup(new_str);
	if (new_attrval_str == NULL) {
		log_err(errno, __func__, "strdup");
		snprintf(msg, msg_len -1,
			"%s: strdup failed (errno=%d)", __func__, errno);
		return (2);
	}

	if (strcmp(attr_name, ATTR_depend) == 0) {
		char	*pdepend;

		pdepend = malloc(PBS_DEPEND_LEN);
		if (pdepend == NULL) {
			log_err(errno, __func__, "malloc");
			snprintf(msg, msg_len -1,
				"%s: malloc failed (errno=%d)", __func__, errno);
			return (2);
		}

		/* below replaces short jobid with full jobid */
		if (parse_depend_list(new_attrval_str, &pdepend,
			PBS_DEPEND_LEN) != 0) {
			free(pdepend);
			log_err(errno, __func__, "parse_depend_list");
			snprintf(msg, msg_len -1,
				"%s: failed to parse_depend_list(%s) (errno=%d)",
				__func__, new_attrval_str, errno);
			return (2);
		}

		/* replace the value with the expanded value */
		free(new_attrval_str);
		new_attrval_str = pdepend;
	}

	if (job_attr_def[attr_index].at_free) {
		job_attr_def[attr_index].at_free(
			&pjob->ji_wattr[attr_index]);
	}

	rc = job_attr_def[attr_index].at_decode(
		&pjob->ji_wattr[attr_index],
		NULL,
		NULL,
		new_attrval_str);
	if (rc == 0) {
		if (job_attr_def[attr_index].at_action) {
			rc = job_attr_def[attr_index].at_action(
				&pjob->ji_wattr[attr_index],
				pjob, ATR_ACTION_ALTER);
			if (rc != 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"Failed executing attribute '%s' action function.",
					attr_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
			}
		}
	} else {
		snprintf(log_buffer, sizeof(log_buffer),
			"Failed decoding a value for '%s'", attr_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
	}

	if (rc != 0) {
		snprintf(msg, msg_len-1,
			"'%s' hook failed to set job's %s = %s",
			hook_name,
			attr_name,
			new_str);
		if (job_attr_def[attr_index].at_free) {
			job_attr_def[attr_index].at_free(
				&pjob->ji_wattr[attr_index]);
		}
	} else {
		FILE	*fp_debug_out = NULL;

		snprintf(msg, msg_len-1,
			"'%s' hook set job's %s = %s",
			hook_name,
			attr_name,
			new_str);

		fp_debug_out = pbs_python_get_hook_debug_output_fp();
		if (fp_debug_out != NULL) {
			fprintf(fp_debug_out, "%s.%s=%s\n", EVENT_JOB_OBJECT, attr_name, new_str);
		}
	}

	free(new_attrval_str);
	return (rc);

}

/**
 * @brief
 *		Sets the job's Variable_List value to the one set in the hook
 *		script, if they differ.
 *
 * @see
 * 		do_runjob_accept_actions and do_runjob_reject_actions
 *
 * @param[in]	pjob	- job to set
 * @param[in]	hook_name - name of the hook that is setting the Variable_List.
 * @param[in]	msg	- message buffer to be filled if error occurred
 * @param[in]	msg_len	- size of 'msg' buffer.
 *
 * @return int
 * @retval	0	- for success (including if nothing got set)
 * @retval	1	- for error occurred setting job's Variable_List
 */
static int
set_job_varlist(job *pjob, char *hook_name, char *msg, int msg_len)
{
	char	*orig_env_str = NULL;
	int	i;
	size_t	elen;
	struct array_strings *astr;
	char	*new_attrval_str = NULL;
	char	*pfrom, *end, *pc = NULL;

	if ((pjob == NULL) || (msg == NULL) || (msg_len <= 0)) {
		log_err(-1, __func__, "pjob, msg,or msg_len parameter is bad");
		return (1);
	}

	if (pjob->ji_wattr[(int)JOB_ATR_variables].at_flags & ATR_VFLAG_SET) {

		/* transform raw Variable_List data into a string */
		/* of the form "<var1>=<val1>,<var2>=<val2>,..." with */
		/* special characters escaped with a backslash. */
		astr = pjob->ji_wattr[(int)JOB_ATR_variables].at_val.at_arst;
		elen = 0;
		for (i=0; i<astr->as_usedptr; ++i) {
			pfrom = astr->as_string[i];
			end = pfrom + strlen(pfrom);
			while (pfrom < end) {
				/* account for back-slashes required */
				/* to escape special characters */
				if (IS_SPECIAL_CHAR(*pfrom))
					elen++;
				elen++;
				pfrom++;
			}

			elen ++; /* add 1 for separator comma or ending NULL */
		}

		if (elen > 0) {
			orig_env_str = (char *)malloc(elen);
			if (orig_env_str == NULL) {
				snprintf(msg, msg_len - 1,
					"malloc failure setting Variable_List for job %s",
					pjob->ji_qs.ji_jobid);
				log_err(errno, __func__, msg);
				return (1);
			}
			memset(orig_env_str, '\0', elen);
			for (i=0; i<astr->as_usedptr; ++i) {
				pfrom = astr->as_string[i];
				end = pfrom + strlen(pfrom);

				/* set destination string */
				if (i == 0) {
					pc = orig_env_str;
				} else {
					*pc++ = ',';
				}

				while (pfrom < end) {
					if (IS_SPECIAL_CHAR(*pfrom))
						*pc++ = '\\';
					*pc++ = *pfrom++;
				}
			}
		}
	}

	new_attrval_str = pbs_python_event_job_getval(ATTR_v);

	if ((orig_env_str != NULL) && (new_attrval_str != NULL)) {
		if (varlist_same(orig_env_str, new_attrval_str) == 1) {
			/* nothing to reset */
			new_attrval_str = NULL;
		}
	}

	if (orig_env_str != NULL) {
		free(orig_env_str);
	}

	if (new_attrval_str == NULL)
		return (0);	/* nothing to set */

	if (set_attribute(pjob, JOB_ATR_variables, new_attrval_str, msg,
		msg_len-1, hook_name) != 0) {
		log_event(PBSEVENT_ERROR|PBSEVENT_FORCE, PBS_EVENTCLASS_JOB,
			LOG_ERR, pjob->ji_qs.ji_jobid, msg);
		return (1);
	} else {
		if (msg[0] != '\0')
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
				pjob->ji_qs.ji_jobid, msg);
	}
	return (0);
}


enum hook_result {
	ACCEPT_HOOK_EVENT,
	REJECT_HOOK_EVENT
};

/**
 * @brief
 *		Sets the job's Resource_List.<resc>* values to the ones set in the hook
 *		script. 'hook_mode' determines which set of resources (<resc>) is NOT
 *		allowed to be modified.
 *
 * @see
 * 		do_runjob_accept_actions and do_runjob_reject_actions
 *
 * @param[in]	pjob	- job to set
 * @param[in]	hook_name - name of the hook that is setting the Resource_List.
 * @param[in]	msg	- message buffer to be filled if error occurred
 * @param[in]	msg_len	- size of 'msg' buffer.
 * @param[in]	hook_mode - if ACCEPT_HOOK_EVENT the list of resources
 *			     that are not modifiable are: "nodect", "select",
 *			     "place", and any resource with the
 *			     ATR_DFLAG_CVTSLT flag set. (i.e. those that
 *			     convert to a select spec). If REJECT_HOOK_EVENT,
 *			     only "nodect" is currently not modifiable.
 *
 * @return int
 * @retval	0	- for success (including if nothing got set)
 * @retval	1	- for error occurred setting job's Variable_List
 */
static int
set_job_reslist(job *pjob, char *hook_name, char *msg, int msg_len,
	enum hook_result hook_mode)
{
	char	*val_str_dup = NULL;
	char	*np = NULL;
	char	*np1 = NULL;
	char	*resc = NULL;
	char	*new_rescval_str = NULL;
	resource_def	*rescdef;
	resource	*prescjb;
	resource	*presc;
	resource_def *pseldef = NULL;
	attribute	*jb;
	int		 rc = 0;
	char	*new_attrval_str = NULL;
	FILE	*fp_debug_out = NULL;

	if ((pjob == NULL) || (hook_name == NULL) || (msg == NULL) ||
		(msg_len <= 0)) {
		log_err(-1, __func__,
			"pjob, hook_name, msg, or msg_len parameter is bad");
		return (1);
	}

	new_attrval_str = pbs_python_event_job_getval(ATTR_l);

	if (new_attrval_str == NULL)
		return (0); /* nothing to set */

	val_str_dup = strdup(new_attrval_str);
	if (val_str_dup == NULL) {
		log_err(errno, __func__, "strdup failed");
		return (1);
	}

	fp_debug_out = pbs_python_get_hook_debug_output_fp();

	jb = &pjob->ji_wattr[(int)JOB_ATR_resource];
	np = strtok(val_str_dup, ",");
	while (np != NULL) {
		resc = np;
		np1 = strstr(np, "=");
		if (np1 != NULL)
			*np1 = '\0';

		new_rescval_str = pbs_python_event_jobresc_getval_hookset(ATTR_l, resc);

		if (new_rescval_str == NULL) {
			np = strtok(NULL, ",");
			continue;
		}
		rescdef = find_resc_def(svr_resc_def, resc, svr_resc_size);
		if (rescdef == NULL) {
			snprintf(msg, msg_len-1,
				"Setting job '%s' attribute %s.%s failed: unknown resource", pjob->ji_qs.ji_jobid, ATTR_l, resc);

			log_event(PBSEVENT_ERROR|PBSEVENT_FORCE, PBS_EVENTCLASS_JOB, LOG_ERR, pjob->ji_qs.ji_jobid, msg);

			free(val_str_dup);
			return (1);
		}

		if (((hook_mode == ACCEPT_HOOK_EVENT) &&
		     ((strcmp(resc, "nodect") == 0) ||
		      (strcmp(resc, "select") == 0) ||
		      (strcmp(resc, "place") == 0) ||
		      ((rescdef->rs_flags & ATR_DFLAG_CVTSLT) != 0))) ||
		    ((hook_mode == REJECT_HOOK_EVENT) &&
		     (strcmp(resc, "nodect") == 0))) {

			snprintf(msg, msg_len-1,
				"'%s' hook failed to set job's %s.%s = %s (not allowed)",
				hook_name, ATTR_l, resc, new_rescval_str);

			log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
				PBS_EVENTCLASS_JOB, LOG_ERR,
				pjob->ji_qs.ji_jobid, msg);
			free(val_str_dup);
			return (1);
		}

		prescjb = find_resc_entry(jb, rescdef);

		if (prescjb == NULL) {
			prescjb = add_resource_entry(jb, rescdef);
		}

		if (prescjb == NULL) {
			snprintf(msg, msg_len-1, "'%s' hook failed to add job's %s.%s = %s",
				hook_name,
				ATTR_l,
				resc,
				new_rescval_str);
			log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
				PBS_EVENTCLASS_JOB, LOG_ERR, pjob->ji_qs.ji_jobid,
				msg);
			free(val_str_dup);
			return (1);
		}

		if ((rc = rescdef->rs_decode(&prescjb->rs_value,
			ATTR_l, rescdef->rs_name, new_rescval_str)) != 0) {
			snprintf(msg, msg_len-1,
				"'%s' hook failed to set job's %s.%s = %s",
				hook_name, ATTR_l,
				resc, new_rescval_str);
			log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
				PBS_EVENTCLASS_JOB, LOG_ERR,
				pjob->ji_qs.ji_jobid, msg);
			free(val_str_dup);
			return (1);
		}

		snprintf(msg, msg_len-1, "'%s' hook set job's %s.%s = %s",
			hook_name,
			ATTR_l,
			resc,
			new_rescval_str);
		log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
			pjob->ji_qs.ji_jobid, msg);

		if (fp_debug_out != NULL) {
			fprintf(fp_debug_out, "%s.%s[%s]=%s\n", EVENT_JOB_OBJECT, ATTR_l, resc, new_rescval_str);
		}
		np = strtok(NULL, ",");
	}

	/* The following forces new job's Resource_List values to be seen */
	/* in qstat -f */
	jb->at_flags |= (ATR_VFLAG_MODIFY|ATR_VFLAG_MODCACHE);

	pseldef = find_resc_def(svr_resc_def, "select", svr_resc_size);
	if (pseldef != NULL) {
		presc = find_resc_entry(jb, pseldef);
		if (presc && (presc->rs_value.at_flags & ATR_VFLAG_DEFLT)) {
			/* changing Resource_List and select is a default */
			/* clear "select" so it is rebuilt in set_resc_deflt */
			pseldef->rs_free(&presc->rs_value);
		}
		(void)set_resc_deflt((void *)pjob, JOB_OBJECT, NULL);
	}
	free(val_str_dup);
	return (0);
}

/* Associates a job attribute value to a slot in an attributes table. */
struct attribute_jobmap {
	enum job_atr	attr_i;		/* index to some table */
	attribute	attr_val;	/* job attribute value */
};

/**
 * @brief
 *		Initializes each entry of the attribute_jobmap table (a_map) to
 *		the value corresponding to the attribute entry in pjob->ji_wattr[]
 *		table.
 *
 * @see
 * 		do_runjob_accept_actions and do_runjob_reject_actions
 *
 * @param[in]	pjob - contains the original attribute values
 * @param[in]	a_map - holds the saved attribute values.
 *
 * @return void
 */
static void
attribute_jobmap_init(job *pjob, struct attribute_jobmap *a_map)
{
	int index, a_index;

	if ((pjob == NULL) || (a_map == NULL)) {
		log_err(-1, __func__, "bad pjob or a_map param");
		return;
	}

	for (index = 0; (a_index=(int)a_map[index].attr_i) >= 0; ++index) {
		if (a_map[index].attr_val.at_flags & ATR_VFLAG_SET) {
			if (job_attr_def[a_index].at_free) {
				job_attr_def[a_index].at_free(&a_map[index].attr_val);
			}
		}

		clear_attr(&a_map[index].attr_val, &job_attr_def[a_index]);
		if (pjob->ji_wattr[a_index].at_flags & ATR_VFLAG_SET) {
			(void)job_attr_def[a_index].at_set(
				&a_map[index].attr_val,
				&pjob->ji_wattr[a_index], SET);
		}
	}
}

/**
 * @brief
 *		Clear each entry of the attribute_jobmap table (a_map) and
 *		zero out the memory.
 *
 * @see
 * 		do_runjob_accept_actions and do_runjob_reject_actions
 *
 * @param[in]	a_map - holds the saved attribute values.
 *
 * @return void
 */
static void
attribute_jobmap_clear(struct attribute_jobmap *a_map)
{
	int index, a_index;

	if (a_map == NULL) {
		log_err(-1, __func__, "bad a_map param");
		return;
	}

	for (index = 0; (a_index=(int)a_map[index].attr_i) >= 0; ++index) {
		if (a_map[index].attr_val.at_flags & ATR_VFLAG_SET) {
			if (job_attr_def[a_index].at_free) {
				job_attr_def[a_index].at_free(&a_map[index].attr_val);
			}
		}
		clear_attr(&a_map[index].attr_val, &job_attr_def[a_index]);
	}
}

/**
 * @brief
 * 		Restores pjob's attribute values saved in 'a_map'.
 *
 * @see
 *		process_hooks
 *
 * @param[in]	pjob - contains the attribute values to be filled in
 * @param[in]	a_map - holds the saved attribute values.
 *
 * @return void
 */
static void
attribute_jobmap_restore(job *pjob, struct attribute_jobmap *a_map)
{
	int 		index, a_index;
	char		*attr_name = NULL;
	attribute	*pattr, *pattr_o;
	attribute_def	*pdef;
	int		newstate;
	int		newsub;

	if ((pjob == NULL) || (a_map == NULL)) {
		log_err(-1, __func__, "bad pjob or a_map param");
		return;
	}

	for (index = 0; (a_index=(int)a_map[index].attr_i) >= 0; ++index) {
		attr_name = job_attr_def[a_index].at_name;
		if (attr_name == NULL)
			continue;

		pattr = &pjob->ji_wattr[a_index];	/* current value */
		pattr_o = &a_map[index].attr_val;	/* original value */
		pdef = &job_attr_def[a_index];

		/* if there's a saved value, then use it */
		if (pattr_o->at_flags & ATR_VFLAG_SET) {

			if (pdef->at_comp != NULL) {
				if (pdef->at_type == ATR_TYPE_RESC) {
					if ((pdef->at_comp(pattr_o, pattr) == 0) && (comp_resc_gt == 0) && (comp_resc_lt == 0) && (comp_resc_nc == 0)) {
						continue;
					}
				} else if (pdef->at_type == ATR_TYPE_ARST) {
					/* compare if both are substrings of each other */
					if ((pdef->at_comp(pattr, pattr_o) == 0) && (pdef->at_comp(pattr_o, pattr) == 0)) {
						continue;
					}
				} else if (pdef->at_comp(pattr, pattr_o) == 0) {
					continue;
				}
			}
			if (pdef->at_free) {
				pdef->at_free(pattr);
			}
			if (pdef->at_set(pattr, pattr_o, SET) == 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"restored job %s's previous value",
					attr_name);
				log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB,
					LOG_INFO, pjob->ji_qs.ji_jobid, log_buffer);
				pjob->ji_modified = 1;
			}
		} else if (pattr->at_flags & ATR_VFLAG_SET) {
			/* original/saved value is unset, and yet current */
			/* value is set, need to revert to unset state */
			if (pdef->at_free) {
				pdef->at_free(pattr);
				snprintf(log_buffer, sizeof(log_buffer),
					"restored job %s's previous unset value",
					attr_name);
				log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB,
					LOG_INFO, pjob->ji_qs.ji_jobid, log_buffer);
				pjob->ji_modified = 1;
			}
		}
	}

	if (pjob->ji_modified) {
		svr_evaljobstate(pjob, &newstate, &newsub, 0);
		(void)svr_setjobstate(pjob, newstate, newsub);
	}
}

/*
 * the following is a list of attributes modifiable in a HOOK_EVENT_RUNJOB
 * hook that ends in a pbs.event().accept().
 * If this list is updated, be sure to update the 'runjob_modifiable_jobattrs'
 * macro in src/lib/Libpython/pbs_python_svr_internal.c.
 */
struct attribute_jobmap runjob_accept_attrlist[] = {
	{	JOB_ATR_outpath,	{0} },
	{	JOB_ATR_errpath,	{0} },
	{	JOB_ATR_resource,	{0} },
	{	JOB_ATR_variables,	{0} },
	{	(enum job_atr) -1,	{0} }
};

/**
 *
 * @brief
 *		Perform the job updates to 'pjob' as a result of a RUNJOB hook
 *		name 'hook_name' execution ending in a pbs.event().accept() call.
 *
 * @see
 * 		process_hooks
 *
 * @param[in]	pjob	- job to modify.
 * @param[in]	hook_name - hook doing the accept action.
 * @param[in]	msg	- gets filled in with the error message in case of
 *			  	an error.
 * @param[in]	msg_len	- size of the 'msg' buffer
 *
 * @return	int
 * @retval	0	- for success
 * @retval	1	- for error, with 'msg' buffer filled.
 */
static int
do_runjob_accept_actions(job *pjob, char *hook_name, char *msg, int msg_len)
{
	int	index, aindex;
	char	*attr_name = NULL;
	char	*new_attrval_str = NULL;

	if ((pjob == NULL) || (msg == NULL) || (msg_len <= 0) ||
		(hook_name == NULL)) {
		log_err(-1, __func__, "bad pjob, msg, or hook_name param");
		return (1);
	}

	msg[0] = '\0';

	attribute_jobmap_init(pjob, runjob_accept_attrlist);

	for (index = 0; (aindex=(int)runjob_accept_attrlist[index].attr_i) >= 0;
		++index) {

		attr_name = job_attr_def[aindex].at_name;
		if (attr_name == NULL) {
			log_err(-1, __func__,
				"encountered an unexpected NULL attr_name");
			continue;
		}
		if (strcmp(attr_name, ATTR_v) == 0) {
			if (set_job_varlist(pjob, hook_name, msg,
				msg_len-1) != 0) {
				return (1);
			}
		} else if (strcmp(attr_name, ATTR_l) == 0) {
			if (set_job_reslist(pjob, hook_name, msg,
				msg_len-1, ACCEPT_HOOK_EVENT) != 0) {
				return (1);
			}
		} else if ((strcmp(attr_name, ATTR_o) == 0) ||
			(strcmp(attr_name, ATTR_e) == 0)) {
			new_attrval_str =
				pbs_python_event_job_getval_hookset(attr_name,
				NULL, 0, NULL, 0);
			if (new_attrval_str == NULL)
				continue;

			if (set_attribute(pjob, aindex, new_attrval_str, msg, msg_len-1, hook_name) != 0) {
				log_event(PBSEVENT_ERROR|PBSEVENT_FORCE, PBS_EVENTCLASS_JOB, LOG_ERR, pjob->ji_qs.ji_jobid, msg);
				return (1);
			} else {
				if (msg[0] != '\0')
					log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO, pjob->ji_qs.ji_jobid, msg);
			}
		}

	}

	/*
	 * Don't let the values in runjob_accept_attrlist (a_map) linger.
	 * When resources get deleted and recreated between job runs, the
	 * pointer to rs_defin can get out of sync causing a crash when
	 * the data is reinitialized.
	 */
	attribute_jobmap_clear(runjob_accept_attrlist);

	return (0);
}

/*
 * the following is a list of attributes modifiable in a HOOK_EVENT_RUNJOB
 * hook that ends in a pbs.event().reject().
 * If this list is updated, be sure to update the 'runjob_modifiable_jobattrs'
 * macro in src/lib/Libpython/pbs_python_svr_internal.c.
 */
struct attribute_jobmap runjob_reject_attrlist[] = {
	{	JOB_ATR_exectime,	{0} },
	{	JOB_ATR_hold,		{0} },
	{	JOB_ATR_project,	{0} },
	{	JOB_ATR_depend,		{0} },
	{	JOB_ATR_variables,	{0} },
	{	JOB_ATR_resource,	{0} },
	{	(enum job_atr) -1,	{0} }
};

/**
 * @brief
 *		Perform the job updates to 'pjob' as a result of a RUNJOB hook
 *		execution ending in a pbs.event().reject() call.
 *
 * @see
 * 		process_hooks
 * @param[in]	pjob	- job to modify.
 * @param[in]	hook_name - name of hook doing the reject action.
 *
 * @return int
 * @retval 0	- success
 * @retval 1	- fail
 */
static int
do_runjob_reject_actions(job *pjob, char *hook_name)
{
	int	index, aindex;
	int	rc = 0;
	char	*attr_name = NULL;
	char	*new_attrval_str = NULL;

	if ((pjob == NULL) || (hook_name == NULL)) {
		log_err(-1, __func__, "bad pjob or hook_name param");
		return (1);
	}

	attribute_jobmap_init(pjob, runjob_reject_attrlist);

	for (index = 0; (aindex=(int)runjob_reject_attrlist[index].attr_i) >= 0;
		++index) {

		attr_name = job_attr_def[aindex].at_name;
		if (attr_name == NULL) {
			log_err(-1, __func__,
				"encountered an unexpected NULL attr_name");
			continue;
		}

		log_buffer[0] = '\0';
		if (strcmp(attr_name, ATTR_a) == 0) {

			new_attrval_str =
				pbs_python_event_job_getval_hookset(attr_name,
				NULL, 0, NULL, 0);

			if (new_attrval_str == NULL)
				continue;

			if (set_exec_time(pjob, new_attrval_str, log_buffer,
				LOG_BUF_SIZE, hook_name) != 0) {

				log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
					PBS_EVENTCLASS_JOB, LOG_ERR,
					pjob->ji_qs.ji_jobid, log_buffer);
				rc = 1;
				break;
			} else {
				if (log_buffer[0] != '\0')
					log_event(PBSEVENT_JOB,
						PBS_EVENTCLASS_JOB, LOG_INFO,
						pjob->ji_qs.ji_jobid,
						log_buffer);
			}

		} else if (strcmp(attr_name, ATTR_h) == 0) {
			char	hold_opval[HOOK_BUF_SIZE];
			char	hold_delval[HOOK_BUF_SIZE];

			new_attrval_str =
				pbs_python_event_job_getval_hookset(ATTR_h,
				hold_opval, HOOK_BUF_SIZE, hold_delval,
				HOOK_BUF_SIZE);
			if (new_attrval_str == NULL)
				continue;

			if (set_hold_types(pjob, new_attrval_str,
				hold_opval, hold_delval, log_buffer, LOG_BUF_SIZE,
				hook_name) != 0) {
				log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
					PBS_EVENTCLASS_JOB, LOG_ERR,
					pjob->ji_qs.ji_jobid, log_buffer);
			} else {

				if (log_buffer[0] != '\0')
					log_event(PBSEVENT_JOB,
						PBS_EVENTCLASS_JOB, LOG_INFO,
						pjob->ji_qs.ji_jobid,
						log_buffer);
			}

		} else if (strcmp(attr_name, ATTR_v) == 0) {
			if (set_job_varlist(pjob, hook_name, log_buffer,
					    LOG_BUF_SIZE-1) != 0) {
				rc = 1;
				break;
			}
		} else if (strcmp(attr_name, ATTR_l) == 0) {
			if (set_job_reslist(pjob, hook_name, log_buffer,
					    LOG_BUF_SIZE-1, REJECT_HOOK_EVENT) != 0) {
				rc = 1;
				break;
			}
		} else {
			new_attrval_str =
				pbs_python_event_job_getval_hookset(attr_name,
				NULL, 0, NULL, 0);
			if (new_attrval_str == NULL)
				continue;

			if (set_attribute(pjob, aindex,
				new_attrval_str, log_buffer, LOG_BUF_SIZE,
				hook_name) != 0) {

				log_event(PBSEVENT_ERROR|PBSEVENT_FORCE,
					PBS_EVENTCLASS_JOB, LOG_ERR,
					pjob->ji_qs.ji_jobid, log_buffer);
				rc = 1;
				break;
			} else {
				if (log_buffer[0] != '\0')
					log_event(PBSEVENT_JOB,
						PBS_EVENTCLASS_JOB, LOG_INFO,
						pjob->ji_qs.ji_jobid,
						log_buffer);
			}
		}
	}

	/*
	 * Don't let the values in runjob_reject_attrlist (a_map) linger.
	 * When resources get deleted and recreated between job runs, the
	 * pointer to rs_defin can get out of sync causing a crash when
	 * the data is reinitialized.
	 */
	attribute_jobmap_clear(runjob_reject_attrlist);

	return(rc);
}

/**
 * @brief
 * 		Write into the hook debug output file, information about
 * 		hook reject action, and close the hook debug output file stream.
 *
 * @param[in]	reject_msg	- the hook reject message.
 *
 * @return void
 */
void
write_hook_reject_debug_output_and_close(char *reject_msg)
{
	char	*hook_outfile;
	FILE	*fp_debug_out = NULL;

	fp_debug_out = pbs_python_get_hook_debug_output_fp();

	if ( fp_debug_out == NULL) {
		/* prepare to open file if output file pointer not stored */
		hook_outfile = pbs_python_get_hook_debug_output_file();
		if ((hook_outfile != NULL) && (hook_outfile[0] != '\0')) {
			/* need to open in append mode, as */
			/* process_hooks() may have */
			/* already written into this file. */
			fp_debug_out = fopen(hook_outfile, "a");
			if (fp_debug_out == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
				"warning: open of hook debug output file %s failed!",
							hook_outfile);
				log_err(-1, __func__, log_buffer);
			} else {
				pbs_python_set_hook_debug_output_fp(fp_debug_out);
			}
		}
	}

	if (fp_debug_out != NULL) {
		fprintf(fp_debug_out, "%s=True\n",
						EVENT_REJECT_OBJECT);
		fprintf(fp_debug_out, "%s=False\n",
						EVENT_ACCEPT_OBJECT);
		if (reject_msg != NULL)
			fprintf(fp_debug_out, "%s=%s\n",
				EVENT_REJECT_MSG_OBJECT, reject_msg);
		fclose(fp_debug_out);
		pbs_python_set_hook_debug_output_fp(NULL);
	}
}

/**
 * @brief
 * 		Write into the hook debug output file, information about
 * 		hook accept action, and close out the hook debug output file
 * 		stream.
 *
 * @see
 * 		process_hooks
 *
 * @return void
 */
void
write_hook_accept_debug_output_and_close(void)
{
	char	*hook_outfile;
	FILE	*fp_debug_out = NULL;

	fp_debug_out = pbs_python_get_hook_debug_output_fp();

	if ( fp_debug_out == NULL) {
		/* prepare to open file if output file pointer not stored */
		hook_outfile = pbs_python_get_hook_debug_output_file();
		if ((hook_outfile != NULL) && (hook_outfile[0] != '\0')) {
			/* need to open in append mode, as */
			/* process_hooks() may have */
			/* already written into this file. */
			fp_debug_out = fopen(hook_outfile, "a");
			if (fp_debug_out == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
				"warning: open of hook debug output file %s failed!",
							hook_outfile);
				log_err(-1, __func__, log_buffer);
			} else {
				pbs_python_set_hook_debug_output_fp(fp_debug_out);
			}
		}
	}

	if (fp_debug_out != NULL) {
		fprintf(fp_debug_out, "%s=True\n",
						EVENT_ACCEPT_OBJECT);
		fprintf(fp_debug_out, "%s=False\n",
						EVENT_REJECT_OBJECT);
		fclose(fp_debug_out);
		pbs_python_set_hook_debug_output_fp(NULL);
	}
}

/**
 * @brief
 * 		getting the vnode attributes and resource list
 *			each node attribute will be of the format:
 *			<node_name>.<attr_name>
 *
 * @see
 * 		run_periodic_hook
 *
 * @return void
 */
pbs_list_head *
get_vnode_list(void){
	int i;
	int index;
	char name_str_buf[STRBUF + 1] = {'\0'};
	struct pbsnode	*pnode = NULL;
	attribute_def *padef = node_attr_def;

	CLEAR_HEAD(vnode_attr_list);
	for (i = 0; i < svr_totnodes; i++) {
		pnode = pbsndlist[i];
		for (index = 0; index < ND_ATR_LAST; index++) {
			if ((padef+index)->at_flags & ATR_VFLAG_SET) {
				strncpy(name_str_buf, pnode->nd_name, STRBUF);
				strcat(name_str_buf, ".");
				strncat(name_str_buf, (padef+index)->at_name, (STRBUF - strlen(name_str_buf)));
				if ((padef+index)->at_encode(
						&pnode->nd_attr[index],
						&vnode_attr_list, name_str_buf,
						NULL, ATR_ENCODE_HOOK, NULL) < 0) {
					char *msgbuf;

					pbs_asprintf(&msgbuf,
						"error on encoding node attributes: %s",
						name_str_buf);
					log_event(PBSEVENT_DEBUG2,
						PBS_EVENTCLASS_HOOK, LOG_ERR,
						__func__, msgbuf);
					free(msgbuf);
					break;
				}
			}
		}
	}
	return &vnode_attr_list;
}

/**
 * @brief
 * 		getting the reservation attribute and resource list
 *
 * @see
 * 		run_periodic_hook
 *
 * @return void
 */
pbs_list_head *
get_resv_list(void) {
	int 	index;
	char	name_str_buf[STRBUF + 1] = {'\0'};
	resc_resv  *presv;
	attribute_def *padef= resv_attr_def;

	CLEAR_HEAD(resv_attr_list);
	presv = (resc_resv *)GET_NEXT(svr_allresvs);

	while (presv != NULL) {
		for (index = 0; index < RESV_ATR_LAST; index++) {
			if ((padef+index)->at_flags & ATR_VFLAG_SET) {
				strncpy(name_str_buf, presv->ri_qs.ri_resvID, STRBUF);
				strcat(name_str_buf, ".");
				strncat(name_str_buf, (padef+index)->at_name, (STRBUF - strlen(name_str_buf)));
				if ((padef+index)->at_encode(
						&presv->ri_wattr[index],
						&resv_attr_list, name_str_buf,
						NULL, ATR_ENCODE_HOOK, NULL) < 0) {
					char *msgbuf;

					pbs_asprintf(&msgbuf,
						"error on encoding reservation attributes: %s",
						name_str_buf);
					log_event(PBSEVENT_DEBUG2,
						PBS_EVENTCLASS_HOOK, LOG_ERR,
						__func__, msgbuf);
					free(msgbuf);
					break;
				}
			}
		}
		presv = (resc_resv *)GET_NEXT(presv->ri_allresvs);
	}
	return &resv_attr_list;
}

/**
 * @brief
 *
 *		Process hook scripts based on request type.
 *		This loops through the matching list of
 *		hooks, and executes the corresponding hook scripts.
 *
 * @see
 * 		req_modifyjob, req_movejob, req_quejob, req_resvSub, req_delete and req_runjob
 *
 * @param[in] 	preq	- the batch request
 * @param[in] 	hook_msg  - upon failure, fill this buffer with the actual error
 *			    message.
 * @param[in]   msg_len  - the size of 'hook_msg' buffer.
 * @param[in]   pyinter_func - the interrupt function used when hook has reached
 *			its execution time limit (alarm). This function raises
 *			some signal to the calling process.
 *		      Ex. pbs_python_set_interrupt() which sends an
 *			  an INT signal (ctrl-C)
 * @return	int
 * @retval	1 means all the executed hooks have agreed to accept the request
 * @retval 	0 means at least one hook was encountered to have rejected the
 request.
 * @retval	2 means no hook script executed (special case).
 * @retval	-1 an internal error occurred
 *
 * @par MT-safe: No
 */
int
process_hooks(struct batch_request *preq, char *hook_msg, size_t msg_len,
	void	(*pyinter_func)(void))
{
	hook			*phook;
	hook			*phook_next = NULL;
	unsigned int		hook_event;
	hook_input_param_t	req_ptr;
	pbs_list_head		*head_ptr;
	job			*pjob = NULL;
	int			t;
	char			*jobid = NULL;
	int			num_run = 0;
	int			rc = 1;
	int			event_initialized = 0;

	if (!svr_interp_data.interp_started) {
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, __func__, "Python interpreter not started, skipping hooks");
		return (2);
	}

	hook_input_param_init(&req_ptr);
	if (preq->rq_type == PBS_BATCH_QueueJob) {
		hook_event = HOOK_EVENT_QUEUEJOB;
		req_ptr.rq_job = (struct rq_quejob *)&preq->rq_ind.rq_queuejob;
		head_ptr = &svr_queuejob_hooks;
	} else if (preq->rq_type == PBS_BATCH_SubmitResv) {
		hook_event = HOOK_EVENT_RESVSUB;
		req_ptr.rq_job = (struct rq_quejob *)&preq->rq_ind.rq_queuejob;
		head_ptr = &svr_resvsub_hooks;
	} else if (preq->rq_type == PBS_BATCH_ModifyJob) {
		hook_event = HOOK_EVENT_MODIFYJOB;
		req_ptr.rq_manage = (struct rq_manage *)&preq->rq_ind.rq_modify;
		head_ptr = &svr_modifyjob_hooks;
		/* Modifyjob hooks not run if requester is the scheduler */
		if ((preq->rq_user != NULL) && (strcmp(preq->rq_user, PBS_SCHED_DAEMON_NAME) == 0) && (pbs_conf.sched_modify_event == 0)) {
			return(2);
		}
	} else if (preq->rq_type == PBS_BATCH_MoveJob) {
		hook_event = HOOK_EVENT_MOVEJOB;
		req_ptr.rq_move = (struct rq_move *)&preq->rq_ind.rq_move;
		head_ptr = &svr_movejob_hooks;
	} else if (preq->rq_type == PBS_BATCH_RunJob || preq->rq_type == PBS_BATCH_AsyrunJob) {
		hook_event = HOOK_EVENT_RUNJOB;
		req_ptr.rq_run = (struct rq_runjob *)&preq->rq_ind.rq_run;
		head_ptr = &svr_runjob_hooks;

		jobid = ((struct rq_runjob *)(req_ptr.rq_run))->rq_jid;
		t = is_job_array(jobid);
		if (t == IS_ARRAY_NO) {
			pjob = find_job(jobid); /* regular job */
		} else if ((t == IS_ARRAY_Single) || (t == IS_ARRAY_Range)) {
			pjob = find_arrayparent(jobid); /* subjob(s) */
		}

		/* an array job will fall through with pjob set to NULL */

		if (pjob == NULL) {
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK, LOG_ERR, __func__,
				"Did not find a job tied to runjob request!");
			return (-1);
		}
	} else if (preq->rq_type == PBS_BATCH_HookPeriodic) {
		hook_event = HOOK_EVENT_PERIODIC;
		head_ptr = &svr_periodic_hooks;
	} else if (preq->rq_type == PBS_BATCH_DeleteResv || preq->rq_type == PBS_BATCH_ResvOccurEnd) {
		hook_event = HOOK_EVENT_RESV_END;
		req_ptr.rq_manage = (struct rq_manage *)&preq->rq_ind.rq_delete;
		head_ptr = &svr_resv_end_hooks;
	} else {
		return (-1); /* unexpected event encountered */
	}

	memset(hook_msg, '\0', msg_len);

	/* initialize global flags */
	pbs_python_event_accept();


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

		if (preq->rq_type == PBS_BATCH_QueueJob) {
			phook_next = (hook *)GET_NEXT(phook->hi_queuejob_hooks);
		} else if (preq->rq_type == PBS_BATCH_SubmitResv) {
			phook_next = (hook *)GET_NEXT(phook->hi_resvsub_hooks);
		} else if (preq->rq_type == PBS_BATCH_ModifyJob) {
			phook_next = (hook *)GET_NEXT(phook->hi_modifyjob_hooks);
		} else if (preq->rq_type == PBS_BATCH_MoveJob) {
			phook_next = (hook *)GET_NEXT(phook->hi_movejob_hooks);
		} else if (preq->rq_type == PBS_BATCH_RunJob || preq->rq_type == PBS_BATCH_AsyrunJob) {
			phook_next = (hook *)GET_NEXT(phook->hi_runjob_hooks);
		} else if (preq->rq_type == PBS_BATCH_HookPeriodic) {
			phook_next = (hook *)GET_NEXT(phook->hi_periodic_hooks);
		} else if (preq->rq_type == PBS_BATCH_DeleteResv || preq->rq_type == PBS_BATCH_ResvOccurEnd) {
			phook_next = (hook *)GET_NEXT(phook->hi_resv_end_hooks);
		} else {
			return (-1); /* should not get here */
		}


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

		if (phook->user != HOOK_PBSADMIN)
			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;
		}

		if (hook_event & HOOK_EVENT_PERIODIC) {
			(void)set_task(WORK_Timed, time_now+phook->freq, run_periodic_hook, phook);
			num_run++;
			continue;
		}
		rc = server_process_hooks(preq->rq_type, preq->rq_user, preq->rq_host, phook,
				hook_event, pjob, &req_ptr, hook_msg, msg_len, pyinter_func,
				&num_run, &event_initialized);
		if ((rc == 0) || (rc == -1))
			return (rc);
	}

	if (num_run == 0)
		return (2);
	return 1;
}
/**
 * @brief
 *
 *		This function executes the hook script passed to it in
 *		hook structure.
 *
 * @param[in] 	rq_type	    - batch request type
 * @param[in] 	rq_user	    - batch request user
 * @param[in] 	rq_host	    - request host
 * @param[in]	phook	    - structure of the hook that needs to execute
 * @param[in]	pjob	    - structure of job corresponding to which hook needs to run
 *			      It is null when used with periodic hook.
 * @param[in]	req_ptr	    - Input parameters to be passed to the hook.
 * @param[in] 	hook_msg  - upon failure, fill this buffer with the actual error
 *			    message.
 * @param[in]   msg_len  - the size of 'hook_msg' buffer.
 * @param[in]   pyinter_func - the interrupt function used when hook has reached
 *			its execution time limit (alarm). This function raises
 *			some signal to the calling process.
 *		      Ex. pbs_python_set_interrupt() which sends an
 *			  an INT signal (ctrl-C)
 * @param[out]	num_run	    - reference of an integer which is incremented when
 *			      hook runs successfully.
 * @return	int
 * @retval	1 means the executed hook has agreed to accept the request
 * @retval 	0 means at least one hook was encountered to have rejected the
 request.
 * @retval	2 means no hook script executed (special case).
 * @retval	-1 an internal error occurred
 *
 * @par MT-safe: No
 */
int server_process_hooks(int rq_type, char *rq_user, char *rq_host, hook *phook,
				int hook_event, job *pjob, hook_input_param_t *req_ptr,
				char *hook_msg, int msg_len, void (*pyinter_func)(void),
				int *num_run, int *event_initialized)
{

	char			hook_inputfile[MAXPATHLEN+1];
	char			hook_datafile[MAXPATHLEN+1];
	char			hook_outfile[MAXPATHLEN+1];
	FILE			*fp_debug = NULL;
	FILE			*fp2_debug = NULL;
	FILE			*fp_debug_out = NULL;
	FILE			*fp_debug_out_save = NULL;
	static char		env_pbs_hook_config[2*MAXPATHLEN+1];
	char			hook_config_path[MAXPATHLEN+1];
	struct	python_script	*py_script = NULL;
	struct	stat		sbuf;
	int			rc;
	char			*p;
	static size_t		suffix_sz;
	hook_output_param_t 	req_params_out;
	pid_t 			mypid;
	pbs_list_head 		event_vnode;
	pbs_list_head 		event_resv;

	if (req_ptr == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			"warning: empty hook input param!");
		log_event(PBSEVENT_DEBUG3,
			PBS_EVENTCLASS_HOOK, LOG_ERR,
			phook->hook_name, log_buffer);
		return -1;
	}

	mypid = getpid();

	if (suffix_sz == 0)
		suffix_sz = strlen(HOOK_SCRIPT_SUFFIX);

	/* initialize various hook_debug_* instance */
	pbs_python_set_hook_debug_output_fp(NULL);
	pbs_python_set_hook_debug_output_file("");

	if (phook->debug) {
		if (rq_type == PBS_BATCH_HookPeriodic)
			snprintf(hook_inputfile, MAXPATHLEN, FMT_HOOK_INFILE, path_hooks_workdir,
				hook_event_as_string(hook_event), phook->hook_name, mypid);
		else
			snprintf(hook_inputfile, MAXPATHLEN, FMT_HOOK_INFILE, path_hooks_workdir,
				hook_event_as_string(hook_event), phook->hook_name, (int)time(0));

		fp_debug = fopen(hook_inputfile, "w");
		if (fp_debug == NULL) {
			sprintf(log_buffer,
				"warning: open of debug input file %s failed!",
				hook_inputfile);
			log_event(PBSEVENT_DEBUG3,
				PBS_EVENTCLASS_HOOK, LOG_ERR,
				phook->hook_name, log_buffer);
		} else {
			pbs_python_set_hook_debug_input_fp(fp_debug);
			pbs_python_set_hook_debug_input_file(hook_inputfile);
		}

		if (rq_type == PBS_BATCH_HookPeriodic)
			snprintf(hook_datafile, MAXPATHLEN, FMT_HOOK_DATAFILE,
				path_hooks_workdir, hook_event_as_string(hook_event),
				phook->hook_name, mypid);
		else
			snprintf(hook_datafile, MAXPATHLEN, FMT_HOOK_DATAFILE,
				path_hooks_workdir, hook_event_as_string(hook_event),
				phook->hook_name, (int)time(0));

		fp2_debug = fopen(hook_datafile, "w");
		if (fp2_debug == NULL) {
			sprintf(log_buffer,
				"warning: open of debug data file %s failed!",
				hook_datafile);
			log_event(PBSEVENT_DEBUG3,
				PBS_EVENTCLASS_HOOK, LOG_ERR,
				phook->hook_name, log_buffer);
		} else {
			pbs_python_set_hook_debug_data_fp(fp2_debug);
			pbs_python_set_hook_debug_data_file(hook_datafile);
		}
	}

	/* optimization here - create an event object only if there's */
	/* at least one enabled hook */
	if (!(*event_initialized)) { /* only once for all hooks */
		rc = pbs_python_event_set(hook_event, rq_user,
			rq_host, req_ptr);

		if (rc == -1) { /* internal server code failure */
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK, LOG_ERR,
				phook->hook_name,
				"Encountered an error while setting event");
		}
		*event_initialized = 1;

	} else if (phook->debug && (fp_debug != NULL)) {
		/* If we have several hooks attached to the same*/
		/* hook event, the first hook that runs */
		/* will call pbs_python_event_set() (above if case), */
		/* which will generate the hook input */
		/* debug file. On the next hook and succeeding hooks */
		/* that execute, we'll need to generate the */
		/* intermediate hook input debug file (based on */
		/* changes made by the previous hooks), by calling */
		/* recreate_request() on a 'temp_req' structure */
		/* that will be discarded (not acted upon). */
		struct	batch_request	*temp_req;
		int			do_recreate = 0;

		temp_req = (struct batch_request *)malloc(
				sizeof(struct batch_request));
		if (temp_req != NULL) {
			memset((void *)temp_req, (int)0,
					sizeof(struct batch_request));
			temp_req->rq_type = rq_type;

			switch (rq_type) {
				case PBS_BATCH_QueueJob:
				case PBS_BATCH_SubmitResv:
					CLEAR_HEAD(temp_req->rq_ind.rq_queuejob.rq_attr);
					do_recreate = 1;
					break;
				case PBS_BATCH_ModifyJob:
					CLEAR_HEAD(temp_req->rq_ind.rq_modify.rq_attr);
					do_recreate = 1;
					break;
				default:
					do_recreate = 0;
			}
			if (do_recreate) {
				fp_debug_out_save = pbs_python_get_hook_debug_output_fp();
				pbs_python_set_hook_debug_output_fp(fp_debug);
				/* recreate_request() appends */
				/* pbs.event().job or */
				/* pbs.event().resv values from */
				/* previous hooks execution into */
				/* 'temp_req' structure, which */
				/* results also in the values being */
				/* written into the file represented */
				/* by 'fp_debug'. */
				(void)recreate_request(temp_req);
				pbs_python_set_hook_debug_output_fp(fp_debug_out_save);
			}
			free_br(temp_req);
		} else {
			log_event(PBSEVENT_DEBUG3,
			PBS_EVENTCLASS_HOOK, LOG_WARNING,
				phook->hook_name,
				"warning: can't generate complete hook input file due to malloc failure.");
		}
	}
	/* hook_name changes for each hook */
	/* This sets Python event object's hook_name value */
	rc = pbs_python_event_set_attrval(PY_EVENT_HOOK_NAME,
		phook->hook_name);

	if (rc == -1) {
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name,
			"Failed to set event 'hook_name'.");
		if (fp_debug != NULL) {
			fclose(fp_debug);
			fp_debug = NULL;
			pbs_python_set_hook_debug_input_fp(NULL);
			pbs_python_set_hook_debug_input_file("");
		}
		if (fp2_debug != NULL) {
			fclose(fp2_debug);
			fp2_debug = NULL;
			pbs_python_set_hook_debug_data_fp(NULL);
			pbs_python_set_hook_debug_data_file("");
		}
		return (-1);
	}

	/*
	 * hook_type needed for internal processing;
	 * hook_type changes for each hook.
	 * This sets Python event object's hook_type value
	 */
	rc = pbs_python_event_set_attrval(PY_EVENT_HOOK_TYPE,
		hook_type_as_string(phook->type));

	if (rc == -1) {
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name,
			"Failed to set event 'hook_type'.");
		if (fp_debug != NULL) {
			fclose(fp_debug);
			fp_debug = NULL;
			pbs_python_set_hook_debug_input_fp(NULL);
			pbs_python_set_hook_debug_input_file("");
		}
		if (fp2_debug != NULL) {
			fclose(fp2_debug);
			fp2_debug = NULL;
			pbs_python_set_hook_debug_data_fp(NULL);
			pbs_python_set_hook_debug_data_file("");
		}
		if (fp_debug_out != NULL) {
			fclose(fp_debug_out);
			fp_debug_out = NULL;
			pbs_python_set_hook_debug_output_fp(NULL);
			pbs_python_set_hook_debug_output_file("");
		}
		return (-1);
	}

	if (rq_type == PBS_BATCH_HookPeriodic) {
		char freq_str[5];
		sprintf(freq_str, "%d", phook->freq);
		rc = pbs_python_event_set_attrval(PY_EVENT_FREQ, freq_str);

		if (rc == -1) {
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Failed to set event 'freq'.");
			if (fp_debug != NULL) {
				fclose(fp_debug);
				fp_debug = NULL;
				pbs_python_set_hook_debug_input_fp(NULL);
				pbs_python_set_hook_debug_input_file("");
			}
			if (fp2_debug != NULL) {
				fclose(fp2_debug);
				fp2_debug = NULL;
				pbs_python_set_hook_debug_data_fp(NULL);
				pbs_python_set_hook_debug_data_file("");
			}
			if (fp_debug_out != NULL) {
				fclose(fp_debug_out);
				fp_debug_out = NULL;
				pbs_python_set_hook_debug_output_fp(NULL);
				pbs_python_set_hook_debug_output_file("");
			}
			return (-1);
		}
	}	
	
	set_alarm(phook->alarm, pyinter_func);

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

	pbs_python_set_mode(PY_MODE); /* hook script mode */

	/* hook script may create files, and we don't want it to */
	/* be littering server's private directory. */
	/* NOTE: path_hooks_workdir is periodically cleaned up */
	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");
	}
	pbs_python_set_os_environ(PBS_HOOK_CONFIG_FILE, NULL);
	(void)pbs_python_set_pbs_hook_config_filename(NULL);

	strncpy(env_pbs_hook_config, PBS_HOOK_CONFIG_FILE,
		sizeof(env_pbs_hook_config)-1);
	py_script = phook->script;
	if (py_script->path != NULL) {
		strncpy(hook_config_path, py_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>. suffix_sz is */
			/* length of <HOOK_SCRIPT_SUFFIX> so as */
			/* to not overflow. */
			strncpy(p, HOOK_CONFIG_SUFFIX, suffix_sz);

			if (stat(hook_config_path, &sbuf) == 0) {
				pbs_python_set_os_environ(
					PBS_HOOK_CONFIG_FILE,
					hook_config_path);
				(void)pbs_python_set_pbs_hook_config_filename(hook_config_path);
			}
		}
	}

	rc=pbs_python_check_and_compile_script(&svr_interp_data,
		phook->script);

	/* reset global flag to allow modification of */
	/* attributes and resources for every new hook execution. */
	pbs_python_event_param_mod_allow();

	/* Reset flag to restart scheduling cycle */
	pbs_python_no_scheduler_restart_cycle();

	if (rq_type == PBS_BATCH_RunJob || rq_type == PBS_BATCH_AsyrunJob) {
		/* Clear dictionary that remembers previously */
		/* set ATTR_l resources in a hook script */
		/* Currently, only job ATTR_l resources can be */
		/* modified in a runjob hook. */
		if (pbs_python_event_jobresc_clear_hookset(ATTR_l) != 0) {
			log_event(PBSEVENT_DEBUG2,
				PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Failed to clear jobresc hookset dictionary.");
			if (fp_debug != NULL) {
				fclose(fp_debug);
				fp_debug = NULL;
				pbs_python_set_hook_debug_input_fp(NULL);
				pbs_python_set_hook_debug_input_file("");
			}
			if (fp2_debug != NULL) {
				fclose(fp2_debug);
				fp2_debug = NULL;
				pbs_python_set_hook_debug_data_fp(NULL);
				pbs_python_set_hook_debug_data_file("");
			}
			if (fp_debug_out != NULL) {
				fclose(fp_debug_out);
				fp_debug_out = NULL;
				pbs_python_set_hook_debug_output_fp(NULL);
				pbs_python_set_hook_debug_output_file("");
			}
			return (-1);
		}
	}

	if (fp_debug != NULL) {
		/* print name of user requested queue if I am a queuejob hook */
		if (rq_type == PBS_BATCH_QueueJob) {
			char *qname = ((struct rq_queuejob *)req_ptr->rq_job)->rq_destin;
			/* use default queue if user did not specify a queue for the job */
			if ((!qname || *qname == '\0' || *qname == '@') &&
				server.sv_attr[SRV_ATR_dflt_que].at_flags & ATR_VFLAG_SET)
				fprintf(fp_debug, "%s.queue=%s\n", EVENT_JOB_OBJECT,
						server.sv_attr[SRV_ATR_dflt_que].at_val.at_str);
			else
				fprintf(fp_debug, "%s.queue=%s\n", EVENT_JOB_OBJECT, qname);
		}
		fprintf(fp_debug, "%s.%s=%s\n", PBS_OBJ, GET_NODE_NAME_FUNC,
			(char *)server_host);
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_TYPE,
			hook_event_as_string(hook_event));
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_HOOK_NAME,
			phook->hook_name);
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, PY_EVENT_HOOK_TYPE,
			hook_type_as_string(phook->type));
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, "requestor",
			rq_user);
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, "requestor_host",
			rq_host);
		fprintf(fp_debug, "%s.%s=%s\n", EVENT_OBJECT, "user", hook_user_as_string(phook->user));
		fprintf(fp_debug, "%s.%s=%d\n", EVENT_OBJECT, "alarm", phook->alarm);
	}

	/* let rc pass through */
	if (rc==0)
		rc=pbs_python_run_code_in_namespace(&svr_interp_data,
			phook->script, 0);

	if (fp_debug != NULL) {
		fclose(fp_debug);
		fp_debug = NULL;
		pbs_python_set_hook_debug_input_fp(NULL);
		pbs_python_set_hook_debug_input_file("");

	}

	/* set hook_debug_output_file for recreate_request(), set_* calls */
	/* to dump any hook results in the file. */
	if (phook->debug || rq_type == PBS_BATCH_HookPeriodic) {
		if (rq_type == PBS_BATCH_HookPeriodic)
			snprintf(hook_outfile, MAXPATHLEN, FMT_HOOK_OUTFILE,
				path_hooks_workdir, hook_event_as_string(hook_event),
				phook->hook_name, mypid);
		else
			snprintf(hook_outfile, MAXPATHLEN, FMT_HOOK_OUTFILE,
				path_hooks_workdir, hook_event_as_string(hook_event),
				phook->hook_name, (int)time(0));

		fp_debug_out = fopen(hook_outfile, "w");
		if (fp_debug_out == NULL) {
			char *msgbuf;

			pbs_asprintf(&msgbuf,
				"warning: open of debug output file %s failed!",
				hook_inputfile);
			log_event(PBSEVENT_DEBUG3,
				PBS_EVENTCLASS_HOOK, LOG_ERR,
				phook->hook_name, msgbuf);
			free(msgbuf);
			if (rq_type == PBS_BATCH_HookPeriodic)
				/* we will need output file to read data from hook later */
				return (-1);
		} else {
			fp_debug_out_save = pbs_python_get_hook_debug_output_fp();
			if (fp_debug_out_save != NULL) {
				fclose(fp_debug_out_save);
			}
			pbs_python_set_hook_debug_output_fp(fp_debug_out);
			pbs_python_set_hook_debug_output_file(hook_outfile);
		}
	} else {
		fp_debug_out_save = pbs_python_get_hook_debug_output_fp();
		if (fp_debug_out_save != NULL) {
			fclose(fp_debug_out_save);
		}
		pbs_python_set_hook_debug_output_fp(NULL);
		/* NOTE: don't call */
		/* pbs_python_set_hook_debug_output_file() as */
		/* we still need a file to dump any remaining */
		/* debug output in case all hooks end */
		/* up accepting the current event with some */
		/* hooks with debug=true and some that are */
		/* debug=false */
	}

	if (fp2_debug != NULL) {
		fclose(fp2_debug);
		fp2_debug = NULL;
		pbs_python_set_hook_debug_data_fp(NULL);
		pbs_python_set_hook_debug_data_file("");
	}

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

	pbs_python_set_mode(C_MODE); /* PBS C mode - flexible */
	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
		LOG_INFO, phook->hook_name, "finished");
	set_alarm(0, NULL);

	switch (rc) {
		case -1:	/* internal error */
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name,
				"Internal server error encountered. Skipping hook.");
			if (fp_debug_out != NULL) {
				fclose(fp_debug_out);
				fp_debug_out = NULL;
				pbs_python_set_hook_debug_output_fp(NULL);
				pbs_python_set_hook_debug_output_file("");
			}
			return (-1); /* should not happen */
		case -2:	/* unhandled exception */
			pbs_python_event_reject(NULL);
			pbs_python_event_param_mod_disallow();

			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(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name, log_buffer);
			snprintf(hook_msg, msg_len-1,
				"request rejected as filter hook '%s' encountered an "
				"exception. Please inform Admin", phook->hook_name);
			write_hook_reject_debug_output_and_close(hook_msg);
			return (0);
		case -3:	/* alarm timeout */
			pbs_python_event_reject(NULL);
			pbs_python_event_param_mod_disallow();

			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(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name, log_buffer);
			snprintf(hook_msg, msg_len-1,
				"request rejected as filter hook '%s' got an "
				"alarm call. Please inform Admin", phook->hook_name);
			write_hook_reject_debug_output_and_close(hook_msg);
			return (0);
	}
	*num_run += 1;
	if (pbs_python_get_scheduler_restart_cycle_flag() == TRUE) {

		set_scheduler_flag(SCH_SCHEDULE_RESTART_CYCLE, dflt_scheduler);
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_INFO, phook->hook_name,
			"requested for scheduler to restart cycle");
	}

	/* reject if at least one hook script rejects */
	if (pbs_python_event_get_accept_flag() == FALSE) {
		char *emsg = NULL;

		if (rq_type == PBS_BATCH_RunJob || rq_type == PBS_BATCH_AsyrunJob) {
			char	*new_error_path_str = NULL;
			char	*new_output_path_str = NULL;

			new_error_path_str =
				pbs_python_event_job_getval_hookset(ATTR_e,
				NULL, 0, NULL, 0);

			if (new_error_path_str != NULL) {
				sprintf(log_buffer,
					"cannot modify job attribute '%s' after runjob "
					"request has been rejected.", ATTR_e);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
					LOG_ERR, phook->hook_name, log_buffer);

			}

			new_output_path_str =
				pbs_python_event_job_getval_hookset(ATTR_o,
				NULL, 0, NULL, 0);

			if (new_output_path_str != NULL) {
				sprintf(log_buffer,
					"cannot modify job attribute '%s' after runjob "
					"request has been rejected.", ATTR_o);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
					LOG_ERR, phook->hook_name, log_buffer);
			}


			if (do_runjob_reject_actions(pjob, phook->hook_name) != 0)
				attribute_jobmap_restore(pjob, runjob_reject_attrlist);
		}

		snprintf(hook_msg, msg_len-1,
			"%s request rejected by '%s'",
			hook_event_as_string(hook_event),
			phook->hook_name);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, phook->hook_name, hook_msg);
		if ((emsg=pbs_python_event_get_reject_msg()) != NULL) {
			snprintf(hook_msg, msg_len-1, "%s", emsg);
			/* log also the custom reject message */
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
				LOG_ERR, phook->hook_name, hook_msg);
		}

		pbs_python_do_vnode_set();
		write_hook_reject_debug_output_and_close(emsg);
		return (0);
	} else { 	/* hook request has been accepted */

		if (rq_type == PBS_BATCH_RunJob || rq_type == PBS_BATCH_AsyrunJob) {
			char	*new_exec_time_str = NULL;
			char	*new_hold_types_str = NULL;
			char	*new_project_str = NULL;
			char	*new_depend_str = NULL;
			char	hold_opval[HOOK_BUF_SIZE];
			char	hold_delval[HOOK_BUF_SIZE];
			int	job_modified = 0;
			int	vnode_modified = 0;

			new_exec_time_str =
				pbs_python_event_job_getval_hookset(ATTR_a,
				NULL, 0, NULL, 0);

			if (new_exec_time_str != NULL) {
				job_modified = 1;
				snprintf(log_buffer, sizeof(log_buffer),
					"Found job '%s' attribute flagged to be set",
					ATTR_a);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name, log_buffer);

			}

			if (job_modified != 1) {
				new_hold_types_str =
					pbs_python_event_job_getval_hookset(ATTR_h,
					hold_opval, HOOK_BUF_SIZE, hold_delval,
					HOOK_BUF_SIZE);

				if (new_hold_types_str != NULL) {
					job_modified = 1;
					snprintf(log_buffer, sizeof(log_buffer),
						"Found job '%s' attribute flagged to be set", ATTR_h);
					log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name, log_buffer);
				}
			}

			if (job_modified != 1) {
				new_project_str =
					pbs_python_event_job_getval_hookset(
					ATTR_project, NULL, 0, NULL, 0);

				if (new_project_str != NULL) {
					job_modified = 1;
					snprintf(log_buffer, sizeof(log_buffer),
						"Found job '%s' attribute flagged to be set",
						ATTR_project);
					log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name, log_buffer);

				}
			}

			if (job_modified != 1) {
					new_depend_str =
					pbs_python_event_job_getval_hookset(
					ATTR_depend, NULL, 0, NULL, 0);

				if (new_depend_str != NULL) {
					job_modified = 1;
					snprintf(log_buffer, sizeof(log_buffer),
						"Found job '%s' attribute flagged to be set",
						ATTR_depend);
					log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name, log_buffer);

				}
			}

			vnode_modified = pbs_python_has_vnode_set();

			if (job_modified || vnode_modified) {
				sprintf(log_buffer,
					"runjob request rejected by '%s': "
					"cannot modify %s after runjob "
					"request has been accepted.",
					phook->hook_name,
					(vnode_modified?PY_EVENT_PARAM_VNODE:PY_EVENT_PARAM_JOB));
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
					LOG_ERR, phook->hook_name, log_buffer);
				/* The following message will appear when */
				/* calling pbs_geterrmsg():		  */
				snprintf(hook_msg, msg_len-1,
					"request rejected by filter hook '%s': "
					"cannot modify %s after runjob "
					"request has been accepted.",
					phook->hook_name,
					(vnode_modified?PY_EVENT_PARAM_VNODE:PY_EVENT_PARAM_JOB));

				write_hook_reject_debug_output_and_close(hook_msg);
				return (0);
			}

			hook_msg[0] = '\0';
			if (do_runjob_accept_actions(pjob, phook->hook_name, hook_msg, msg_len-1) != 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"runjob request rejected: %s", hook_msg);
				log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name, log_buffer);
				snprintf(log_buffer, sizeof(log_buffer),
					"request rejected by filter hook: %s", hook_msg);
				strncpy(hook_msg, log_buffer, msg_len-1);
				attribute_jobmap_restore(pjob, runjob_accept_attrlist);
				write_hook_reject_debug_output_and_close(hook_msg);
				return (0);
			}
		}

		if(rq_type == PBS_BATCH_HookPeriodic) {
			if (fp_debug_out != NULL) {
				fprintf(fp_debug_out, "%s=True\n", EVENT_ACCEPT_OBJECT);
				fprintf(fp_debug_out, "%s=False\n", EVENT_REJECT_OBJECT);
			}
			hook_output_param_init(&req_params_out);
			CLEAR_HEAD(event_vnode);
			CLEAR_HEAD(event_resv);
			req_params_out.vns_list = (pbs_list_head *)&event_vnode;
			req_params_out.resv_list = (pbs_list_head *)&event_resv;
			rc = pbs_python_event_to_request(hook_event, &req_params_out);
			if (rc == -1) {
				log_err(PBSE_INTERNAL, phook->hook_name, "error occured recreating request!");
			}
			if (fp_debug_out != NULL)
				fprint_svrattrl_list(fp_debug_out, EVENT_VNODELIST_OBJECT, &event_vnode);
			free_attrlist(&event_vnode);
			CLEAR_HEAD(event_vnode);
			if (fp_debug_out != NULL)
				fclose(fp_debug_out);
			pbs_python_set_hook_debug_output_fp(NULL);
			return (1);
		}
	}

	write_hook_accept_debug_output_and_close();
	return (1);
}

/**
 * @brief
 *		Recreates the 'preq' structure based on the values specified by
 * 		the hook writer in the corresponding Python event request object.
 *		CAUTION: If this returns -1, don't process 'preq' as it could be
 *		in an incompletely filled state. It must be freed by any of the
 * 		functions that call reply_send() (which calls free_attrlist(preq)).
 *
 * @param[in] 	preq	- the batch request
 *
 * @return	int
 * @retval	0 	- success
 * @retval	-1	- failure
 */
int
recreate_request(struct batch_request *preq)
{
	int rc;
	hook_output_param_t req_params;
	FILE *fp_debug = NULL;
	char *hook_outfile = NULL;

	if (!svr_interp_data.interp_started) {
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
			LOG_ERR, __func__,
			"Python interpreter not started, so no request recreation");
		return (0);
	}

	if (pbs_python_get_hook_debug_output_fp() == NULL) {
		/* prepare to open file if output file pointer not stored */
		hook_outfile = pbs_python_get_hook_debug_output_file();
	}

	if ((hook_outfile != NULL) && (hook_outfile[0] != '\0')) {
		/* need to open in append mode, as process_hooks() may have */
		/* already written into this file. */
		fp_debug = fopen(hook_outfile, "a");
		if (fp_debug == NULL) {
			sprintf(log_buffer,
				"warning: open of hook debug output file %s failed!",
				hook_outfile);
		} else {
			pbs_python_set_hook_debug_output_fp(fp_debug);
		}
	}

	hook_output_param_init(&req_params);
	if (preq->rq_type == PBS_BATCH_QueueJob) {
		req_params.rq_job = (struct rq_quejob *)&preq->rq_ind.rq_queuejob;
		rc = pbs_python_event_to_request(HOOK_EVENT_QUEUEJOB,
			&req_params);
	} else if (preq->rq_type == PBS_BATCH_SubmitResv) {
		req_params.rq_job = (struct rq_quejob *)&preq->rq_ind.rq_queuejob;
		rc =pbs_python_event_to_request(HOOK_EVENT_RESVSUB,
			&req_params);
	} else if (preq->rq_type == PBS_BATCH_ModifyJob) {
		req_params.rq_manage = (struct manage *)&preq->rq_ind.rq_modify;
		rc =pbs_python_event_to_request(HOOK_EVENT_MODIFYJOB,
			&req_params);
	} else if (preq->rq_type == PBS_BATCH_MoveJob) {
		req_params.rq_move = (struct rq_move *)&preq->rq_ind.rq_move;
		rc = pbs_python_event_to_request(HOOK_EVENT_MOVEJOB,
			&req_params);
	} else {
		log_err(PBSE_INTERNAL, __func__, "unexpected request type");
		rc = -1;
	}
	if (rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "error occured recreating request!");
	}

	if (fp_debug != NULL) {
		fclose(fp_debug);
		fp_debug = NULL;
		pbs_python_set_hook_debug_output_fp(NULL);
		pbs_python_set_hook_debug_output_file("");
	}

	return (rc);
}

/* Mom hook action-related items */

/**
 * @brief
 *		add_mom_hook_action - create both a mom_hook_action_t entry and insert
 *		a pointer to that element into the *hookact_array which currently has
 *		a size of *hookact_array_size, which may be expanded if needed.
 *
 * @par Functionality:
 *		Searches for existing mom_hook_action_t entry in the
 *		'hookact_array', with matching hookname;
 *		if found and the tid field (transaction id) of the matching entry
 *		is <= input_tid, update that entry with 'action' and 'input_tid'
 *		values, and return the index to the updated entry. If no matching
 *		mom_hook_action_t entry is found, then find an existing empty slot in
 *		the 'hookact_array' and put in the 'hookname', 'action', and
 *		'input_tid' data entry to it, and return its index.
 * 		If there's no empty slot, the array is expanded by
 *		GROW_MOMHOOK_ARRAY_AMT amount. Then add the 'hookname' and 'action'
 *		data in the first newly created empty slot, and return its index.
 *
 * @par Note:
 *		Normally, the action value is appended to existing entries in
 *		*hookact_array. If the parameter 'set_action' is set to 1, the
 *		action value is not appended but directly assigned.
 *
 *		If hook action being added is for PBS_RESCDEF ("resourcedef"),
 *		then ensure this entry appears before other hooks in 'hookact_array',
 *		for mom hooks will depend on the PBS_RESCDEF file for custom resources.
 *
 * @see
 * 		hook_track_recov and add_pending_mom_hook_action
 *
 * @param[in/out] hookact_array - pointer to the hook action array,
 *					which if expanded would get a new pointer value.
 * @param[in/out] hookact_array_size - pointer to the number of entries in
 *					*hookact_array, which if expanded, would get a new
 *					number of elements value.
 * @param[in]	hookname - name of hook with pending action
 * @param[in]	action - flag specifying the type of pending action in hookname.
 * @param[in]	set_action - if set to 1, then the action value will be
 *				assigned directly and not appended to the list of
 *				action values.
 * @param[in]	input_tid - transaction id to assocate to newly added
 *				mom hook action (<hook_name>,<action>).
 *
 * @return	int
 * @retval	Returns the index to the 'hookact_array' containing the
 updated or added mom_hook_action_t entry.
 * @retval	-1 if no mom_hook_action_t entry got updated or added,
 *		perhaps due to an error.
 *
 * @par Side Effects: None
 *
 */

int
add_mom_hook_action(mom_hook_action_t ***hookact_array,
	int *hookact_array_size, char *hookname,
	unsigned int action, int set_action,
	long long int input_tid)
{
	int			empty = -1;
	int			i, j;
	mom_hook_action_t 	*pact, *pact2, *pact_tmp;
	mom_hook_action_t	**tp;

	if ((hookact_array == NULL) || (hookact_array_size == NULL) ||
		(hookname == NULL))
		return -1;

	for (i=0; i < *hookact_array_size; i++) {
		pact = (*hookact_array)[i];
		if (pact) {
			if (strcmp(pact->hookname, hookname) == 0) {
				/* check if existing entry is newer than */
				/* the entry being added */
				if (pact->tid > input_tid) {
					snprintf(log_buffer, sizeof(log_buffer),
						"not adding hook %s action %d as "
						"entry's tid=%lld > input_tid=%lld",
						hookname, pact->action, pact->tid,
						input_tid);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						"add_mom_hook_action", log_buffer);
					return (-1);
				}
				if (set_action) {
					pact->action = action;
				} else {
					if (action & MOM_HOOK_ACTION_DELETE) {
						if (pact->action & MOM_HOOK_SEND_ACTIONS) {
							/* there's a current send action, so delete action should not execute first */
							pact->do_delete_action_first = 0;
						} else {
							/* there's no current send action, so delete action should execute first */
							pact->do_delete_action_first = 1;
						}
					}
					pact->action |= action;
				}
				pact->tid = input_tid;
				do_sync_mom_hookfiles = 1;
				return i;
			} else if ((pact->action == MOM_HOOK_ACTION_NONE) &&
				(empty == -1)) {
				/* be sure to free up previous entry which */
				/* was previously malloc-ed */
				free(pact);
				(*hookact_array)[i] = NULL;
				empty = i;
			}
		} else if (empty == -1) {
			empty = i; /* save index of first empty slot */
		}
	}

	if (empty == -1) {
		/* there wasn't an empty slot in the array we can use */
		/* need to grow the array			      */

		tp = (mom_hook_action_t **)realloc(*hookact_array,
			(size_t)(sizeof(mom_hook_action_t *) * (*hookact_array_size+GROW_MOMHOOK_ARRAY_AMT)));
		if (tp != NULL) {
			empty = *hookact_array_size;
			*hookact_array = tp;
			*hookact_array_size += GROW_MOMHOOK_ARRAY_AMT;
			for (i = empty; i < *hookact_array_size; i++)
				(*hookact_array)[i] = NULL;
		} else {
			log_err(errno, __func__, merr);
			return (-1);
		}
	}

	/* now allocate the memory for the mom_hook_action_t element itself */

	pact = (mom_hook_action_t *)malloc(sizeof(mom_hook_action_t));
	if (pact != NULL) {
		snprintf(pact->hookname, sizeof(pact->hookname), "%s", hookname);
		pact->action = action;
		pact->do_delete_action_first = 0;
		pact->tid = input_tid;
		do_sync_mom_hookfiles = 1;
		(*hookact_array)[empty] = pact;
	} else {
		log_err(errno, __func__, merr);
		return (-1);
	}

	/* If hook action added is for PBS_RESCDEF, then we need to sort the */
	/* mom hook action array so that this resourcedef 		     */
	/* entry appear before regular mom hooks. This allows 		     */
	/* sync_mom_hookfiles() to send out resourcedef files first before   */
	/* the mom hook files, for the latter could be depending on the      */
	/* former. */
	if (strcmp(hookname, PBS_RESCDEF) == 0) {

		/* j indexed array goes from last element to first */
		/* i indexed array goes from first to last */
		/* slot entry in j to be exchanged with i's,and i must be < j */
		/* for we're moving later resourcedef entry in j into */
		/* the earliest entry in i */
		for (j=(*hookact_array_size)-1; j >= 0; j--) {
			pact = (*hookact_array)[j];
			if (pact && (strcmp(pact->hookname, PBS_RESCDEF) == 0)) {
				for (i=0; (i < *hookact_array_size) && (i < j); i++) {
					pact2 = (*hookact_array)[i];
					if (pact2 && (strcmp(pact2->hookname,
						PBS_RESCDEF) != 0)) {
						/* exchange places, moved later */
						/* resourcedef file entry to */
						/* earlier entry */
						pact_tmp = pact2;
						/* set resourcedef entry */
						(*hookact_array)[i] = pact;
						(*hookact_array)[j] = pact_tmp;
						break;
					}
				}
				break;	/* only be one PBS_RESCDEF entry */
			}
		}
	}

	return empty;
}


/**
 * @brief
 *	Removes from hookact_array of size hookact_array_size the mom hook
 *	action: (hookname, action).
 *
 * @see
 * 		delete_pending_mom_hook_action
 *
 * @param[in]	hookact_array - mom hook action array
 * @param[in]	hookact_array_size - number of elements in hookact_array
 * @param[in]	hookname - the hook in question
 * @param[in]	action - the mom hook action to unset
 *
 * @return int
 * @retval	index to the mom hook action entry in hookact_array where
 *		'action' flag has been removed.
 * @retval	-1	- if no entry found or error encountered.
 */

int
delete_mom_hook_action(mom_hook_action_t **hookact_array,
	int hookact_array_size, char *hookname, unsigned int action)
{
	int i;

	if ((hookact_array == NULL) || (hookname == NULL))
		return (-1);

	/* find the entry in the array that does point here */
	for (i=0; i < hookact_array_size; i++) {
		if ((hookact_array[i] != NULL) &&
			strcmp(hookact_array[i]->hookname, hookname) == 0) {
			hookact_array[i]->action &= ~action;
			return (i);
		}
	}
	return (-1);
}

/**S
 * @brief
 *		Find and return a pointer to a mom_hook_action_t element in
 *		hookact_array of size hookact_array_size, defined by hookname.
 *
 * @see
 * 		has_pending_mom_action_delete
 *
 * @param[in]	hookact_array - mom hook action array
 * @param[in]	hookact_array_size - number of elements in hookact_array
 * @param[in]	hookname - the hook to find.
 *
 * @return	mom_hook_action_t *
 * @retval	pointer to the the mom_hook_action_t entry
 * @retval	NULL if it did not find it.
 */

mom_hook_action_t *
find_mom_hook_action(mom_hook_action_t **hookact_array,
	int hookact_array_size, char *hookname)
{
	int i;

	mom_hook_action_t	*pact;

	for (i=0; i < hookact_array_size; i++) {
		pact = hookact_array[i];
		if (pact &&
			(strcmp(pact->hookname, hookname) == 0))
			return pact;
	}

	return NULL; 	/* didn't find it */
}

/**
 * @brief
 *		Adds a pending action to 'hookname' for the mom in 'minfo' if not NULL,
 *		or all the moms in the system.
 * @par NOTE:
 *		For every successful pending action add, a line of data is
 *		written in [PATH_HOOKS]/hook_tracking.TR file as:
 *		<mom_name>:<mom_port> <hook_name> <action>
 *		where <action> is the current action flag value.
 *
 * @param[in]	minfo		- if not NULL, then add mom hook action
 *				on this particular mom in 'minfo'.
 * @param[in]	hookname	- name of hook with pending action.
 * @param[in] 	action		- the type of action
 *				(MOM_HOOK_ACTION_SEND_ATTRS,
 *				MOM_HOOK_ACTION_SEND_SCRIPT, etc...)
 *
 * @return	void
 */
void
add_pending_mom_hook_action(void *minfo, char *hookname, unsigned int action)
{
	int	i, j;
	mominfo_t		**minfo_array = NULL;
	int			minfo_array_size;
	mominfo_t		*minfo_array_tmp[1];

	if ((mominfo_t *)minfo == NULL) {
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	} else {
		minfo_array_tmp[0] = (mominfo_t *)minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;

	}

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

		if (minfo_array[i] == NULL)
			break;

		j=add_mom_hook_action(&minfo_array[i]->mi_action,
			&minfo_array[i]->mi_num_action, hookname,
			action, 0, hook_action_tid);

		hook_track_save((mominfo_t *)minfo_array[i], j);
	}

}

/**
 * @brief
 *		Deletes a pending action to 'hookname' in mom described by 'minfo' if
 *		not NULL, or for all the moms in the system.
 *
 * @par Note:
 *		For every successful pending action delete, a line of data is
 *		written in [PATH_HOOKS]/<hookname>.TR file as:
 *		<mom_name>:<mom_port> <hook_name> <remaining_hook_action>
 *
 * @param[in]	minfo		- if not NULL, then delete mom hook action
 *				on this particular mom in 'minfo'.
 *				if NULL, then delete mom hook action on
 *				all the moms in the system.
 * @param[in]	hookname	- name of hook with pending hook action
 * @param[in] 	action		- the type of action
 *				(MOM_HOOK_ACTION_SEND_ATTRS,
 *				MOM_HOOK_ACTION_SEND_SCRIPT, etc...)
 *
 * @return void
 */
void
delete_pending_mom_hook_action(void *minfo, char *hookname,
	unsigned int action)
{
	int	i, k;
	mominfo_t		**minfo_array = NULL;
	int			minfo_array_size;
	mominfo_t		*minfo_array_tmp[1];

	if ((mominfo_t *)minfo == NULL) {
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	} else {
		minfo_array_tmp[0] = (mominfo_t *)minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;

	}

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

		if (minfo_array[i] == NULL)
			break;

		k=delete_mom_hook_action(minfo_array[i]->mi_action,
			minfo_array[i]->mi_num_action,
			hookname,
			action);

		hook_track_save((mominfo_t *)minfo_array[i], k);
	}

}

/**
 * @brief
 *		Determines if 'hookname' has a pending MOM_HOOK_ACTION_DELETE to
 *		the moms.
 *
 * @see
 * 		collapse_hook_tr and pbsd_init
 *
 * @param[in]	hookname - the hook in question
 *
 * @return	int
 * @retval	1	if there's a pending delete action
 * @retval	0	otherwise.
 */
int
has_pending_mom_action_delete(char *hookname)
{
	mom_hook_action_t	*pact;
	int			i;

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

		if (mominfo_array[i] == NULL)
			break;

		pact = find_mom_hook_action(mominfo_array[i]->mi_action,
			mominfo_array[i]->mi_num_action,
			hookname);

		if (pact && (pact->action & MOM_HOOK_ACTION_DELETE))
			return 1;

	}
	return 0;
}

/**
 * @brief
 *		Returns the number of pending hook actions, such as send hook
 *		attributes/scripts, resourcedef file,
 *		to a particular mom, or to all the moms in the
 *		system.
 *
 * @see
 * 		set_nodes
 *
 * @param[in]	minfo	- count referring to the mom described by this
 *				'minfo' only, or
 *			 	if NULL, then return total count for all the
 *				moms in the system.
 *
 * @return int
 * @retval	<num>	- Number of still pending mom hook actions.
 */
int
sync_mom_hookfiles_count(void *minfo)
{
	int		i, j;
	mominfo_t	**minfo_array = NULL;
	int		minfo_array_size;
	mominfo_t	*minfo_array_tmp[1];
	int		action_expected = 0;
	mom_hook_action_t *pact;

	if (minfo == NULL) {
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	} else {
		minfo_array_tmp[0] = minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;

	}

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

		if (minfo_array[i] == NULL)
			continue;

		for (j=0; j < minfo_array[i]->mi_num_action; j++) {
			pact = minfo_array[i]->mi_action[j];

			if ((pact == NULL) ||
				(pact->action == MOM_HOOK_ACTION_NONE))
				continue;

			if (pact->action & MOM_HOOK_ACTION_DELETE)
				action_expected++;

			if (pact->action & MOM_HOOK_ACTION_SEND_ATTRS)
				action_expected++;

			if (pact->action & MOM_HOOK_ACTION_SEND_SCRIPT)
				action_expected++;

			if (pact->action & MOM_HOOK_ACTION_SEND_CONFIG)
				action_expected++;

			if (pact->action & MOM_HOOK_ACTION_DELETE_RESCDEF) {
				action_expected++;
			} else if (pact->action & MOM_HOOK_ACTION_SEND_RESCDEF) {
				action_expected++;
			}
		}
	}

	return (action_expected);
}

/**
 * @brief
 * 		Handles the collapsing of a hook tracking file
 * 		Recovers the hooks tracking data from the hooks
 * 		tracking file, loops through the pending actions
 * 		and deletes all purged hooks, and finally does
 * 		a hook tracking file save, effectively collapsing
 * 		the tracking file.
 *
 * @see
 * 		post_sendhookRPP, post_sync_mom_hookfiles and next_sync_mom_hookfiles.
 */
static void
collapse_hook_tr()
{
	hook			*phook;
	hook			*phook_current;

	if (pbs_conf.pbs_use_tcp == 0) {
		/* recover what was written by child process (non-TPP mode only) */
		/* hook_action_tid value gets reset here to the highest */
		/* value found in the hooks tracking file. */
		hook_track_recov();
	}

	/* purge deleted hooks */
	phook = (hook *)GET_NEXT(svr_allhooks);
	while (phook) {
		phook_current = phook;
		phook = (hook *)GET_NEXT(phook->hi_allhooks);

		if (phook_current->pending_delete &&
			!has_pending_mom_action_delete(
			phook_current->hook_name)) {
			hook_purge(phook_current,
				pbs_python_ext_free_python_script);
		}
	}

	/* This collapses, purges the hook tracking file, which */
	/* could become empty, so the next call to */
	/* hook_track_recov() could cause the hook_action_tid to */
	/* reset back to 0, which is what we want. */
	hook_track_save(NULL, -1);
}

/**
 * @brief
 *		Performs actions such as send hook attributes/scripts, and also
 *		resourcedef file to a particular mom, or to all the moms in the
 *		system.
 *
 * @param[in]	minfo	- particular mom information to send hook request, or
 *			 			if NULL, then hook action request sent to all the
 *						moms in the system.
 * @Note
 *		If sending or deleting a hook to a mom has been rejected because
 *		mom is not acceping root remote scripts for security reasons, then
 *		this will be considered still a successful send.
 *
 * @return enum sync_hookfiles_result
 * @retval	SYNC_HOOKFILES_NONE	      if there are no mom hook actions
 *					      needed to be done.
 * @retval	SYNC_HOOKFILES_SUCCESS_ALL    if all mom hook actions were done
 *					      and no failures have been
 *					      encountered along the way.
 * @retval	SYNC_HOOKFILES_SUCCESS_PARTIAL if not all mom hook actions were
 *					      successfully done, but
 *					      at least 1 action was successfully
 *					      done.
 * @retval	SYNC_HOOKFILES_FAIL	      if all mom hook actions expected
 *					      to be done failed.
 */
enum sync_hookfiles_result
sync_mom_hookfiles(void *minfo)
{
	int		i, j, k;
	pbs_net_t	momaddr;
	unsigned int	momport;
	int		conn = -1;	/* a client style connection handle */
	char		hookfile[MAXPATHLEN+1];
	mominfo_t	**minfo_array = NULL;
	int		minfo_array_size;
	mominfo_t	*minfo_array_tmp[1];
	mom_hook_action_t *pact;
	int		action_done = 0;
	int		action_expected = 0;
	int		ret;

	if (minfo == NULL) {
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	} else {
		minfo_array_tmp[0] = minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;

	}

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

		if (minfo_array[i] == NULL)
			continue;

		momaddr = get_hostaddr(minfo_array[i]->mi_host);
		if (momaddr == 0)
			continue;
		momport =minfo_array[i]->mi_port;

		pbs_errno = 0;
		for (j = 0; j < minfo_array[i]->mi_num_action; j++) {
			char *msgbuf;
			hook *phook;

			pact = minfo_array[i]->mi_action[j];
			if ((pact == NULL) ||
				(pact->action == MOM_HOOK_ACTION_NONE))
				continue;
			if (pact->action & MOM_HOOK_ACTION_DELETE)
				action_expected++;
			if (pact->action & MOM_HOOK_ACTION_SEND_ATTRS)
				action_expected++;
			if (pact->action & MOM_HOOK_ACTION_SEND_SCRIPT)
				action_expected++;
			if (pact->action & MOM_HOOK_ACTION_SEND_CONFIG)
				action_expected++;
			if (pact->action & MOM_HOOK_ACTION_DELETE_RESCDEF)
				action_expected++;
			else if (pact->action & MOM_HOOK_ACTION_SEND_RESCDEF)
				action_expected++;

			if (action_expected == 0)
				continue;

			/* optimization: connect only if there's at least */
			/* 1 hook action to communicate to mom. */

			/* connect to receiving mom with retries */
			k = 0;
			do {

				if (conn >= 0)
					break;

				conn = svr_connect(momaddr, momport, NULL,
					ToServerDIS, PROT_TCP);
				if (conn >= 0) {
					break; /* good connection */
				} else if (conn == PBS_NET_RC_FATAL) {
					snprintf(log_buffer, sizeof(log_buffer),
						"connect to host %s port %d got fatal error (pbs_errno=%d)",
						minfo_array[i]->mi_host, momport,
						pbs_errno);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						__func__, log_buffer);
					break;
				} else if ((conn == PBS_NET_RC_RETRY) &&
					(should_retry_route(pbs_errno) == -1)) {

					snprintf(log_buffer, sizeof(log_buffer),
						"couldn't retry connecting to host %s %d",
						minfo_array[i]->mi_host, momport);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						__func__, log_buffer);
					break;
				}

				sprintf(log_buffer, "retrying after sleeping for %d seconds", (1<<k));
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
					LOG_ERR, __func__, log_buffer);
				sleep(1<<k);
				k++;
			} while (k <= CONN_RETRY);

			if (conn < 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"%s %s:%d", msg_norelytomom,
					minfo_array[i]->mi_host,
					minfo_array[i]->mi_port);
				log_event(PBSEVENT_DEBUG3,
					PBS_EVENTCLASS_REQUEST, LOG_WARNING,
					__func__, log_buffer);
				break;
			}

			/* delete or send resourcedef file, which does not */
			/* have a suffix */
			if (pact->action & MOM_HOOK_ACTION_DELETE_RESCDEF) {

				snprintf(hookfile, sizeof(hookfile), "%s", pact->hookname);

				if (PBSD_delhookfile(conn, hookfile, 0, NULL) != 0) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to delete rescdef file %s from %s",
						pbs_errno, hookfile, minfo_array[i]->mi_host);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					pbs_asprintf(&msgbuf,
						"successfully deleted rescdef file %s from %s:%d",
						hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG,
						PBS_EVENTCLASS_REQUEST, LOG_INFO,
						msg_daemonname, msgbuf);
					free(msgbuf);
					/* Delete all SEND_RESCDEF action */
					/* so it doesn't get retried for this */
					/* "deleted" resourcdef. */
					pact->action &= ~(MOM_HOOK_ACTION_DELETE_RESCDEF|MOM_HOOK_ACTION_SEND_RESCDEF);
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}

			} else if (pact->action & MOM_HOOK_ACTION_SEND_RESCDEF) {

				snprintf(hookfile, sizeof(hookfile), "%s%.*s", path_hooks,
					(int)(sizeof(hookfile) - strlen(path_hooks)), pact->hookname);

				if ((PBSD_copyhookfile(conn, hookfile, 0, NULL) != 0) &&
					(pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to copy rescdef file %s to %s:%d",
						pbs_errno, hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
						pbs_asprintf(&msgbuf,
							"successfully sent rescdef file %s to %s:%d",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					} else {
						snprintf(log_buffer, sizeof(log_buffer),
							"warning: sending resourcedef to %s:%d got rejected (mom's reject_root_scripts=1)",
							minfo_array[i]->mi_host,
							minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG3,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, log_buffer);
					}
					pact->action &= ~(MOM_HOOK_ACTION_SEND_RESCDEF);
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}
			} /* resourcedef else */

			/* execute delete action before the send actions */
			if (pact->do_delete_action_first && (pact->action & MOM_HOOK_ACTION_DELETE)) {
				/* delete a hook - overrides other hook actions */
				snprintf(hookfile, sizeof(hookfile), "%.*s%s",
					(int)(sizeof(hookfile) - strlen(HOOK_FILE_SUFFIX) - 1),
					pact->hookname, HOOK_FILE_SUFFIX);

				if (PBSD_delhookfile(conn, hookfile, 0, NULL) != 0) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to delete hook file %s from %s",
						pbs_errno, hookfile, minfo_array[i]->mi_host);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					pbs_asprintf(&msgbuf,
						"successfully deleted hook file %s from %s:%d",
						hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG,
						PBS_EVENTCLASS_REQUEST, LOG_INFO,
						msg_daemonname, msgbuf);
					free(msgbuf);

					/* Delete also any other hook-related actions */
					/* so they don't get erroneously retried */
					pact->action &= ~MOM_HOOK_ACTION_DELETE;
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}
			}

			phook = find_hook(pact->hookname);
			/* send with suffix if a regular hook file
			 * NOTE: There could be a hook named <resourcedef>
			 * and it's not the special <resourcedef> file
			 * that gets the special *RESCDEF aflags. In this
			 * case, the <resourcedef>.{HK,PY} would be sent
			 * instead send hook control file
			 */
			if (pact->action & MOM_HOOK_ACTION_SEND_ATTRS) {

				snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
					(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_FILE_SUFFIX)),
					path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_FILE_SUFFIX);
				if (!phook || (phook->event & MOM_EVENTS) == 0) {
					pact->action &= ~MOM_HOOK_ACTION_SEND_ATTRS;
				} else if ((PBSD_copyhookfile(conn, hookfile, 0, NULL) != 0) &&
					(pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to copy hook file %s to %s:%d",
						pbs_errno, hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
						pbs_asprintf(&msgbuf,
							"successfully sent hook file %s to %s:%d",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					} else {
						pbs_asprintf(&msgbuf,
							"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG3,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					}
					pact->action &= ~(MOM_HOOK_ACTION_SEND_ATTRS);
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}
			} /* send_attrs */

			/* send hook config */
			if (pact->action & MOM_HOOK_ACTION_SEND_CONFIG) {

				snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
					(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_CONFIG_SUFFIX)),
					path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_CONFIG_SUFFIX);

				if (!phook || (phook->event & MOM_EVENTS) == 0) {
					pact->action &= ~MOM_HOOK_ACTION_SEND_CONFIG;
				} else if ((PBSD_copyhookfile(conn, hookfile, 0, NULL) != 0) &&
					(pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to copy hook file %s to %s:%d",
						pbs_errno, hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
						pbs_asprintf(&msgbuf,
							"successfully sent hook file %s to %s:%d",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					} else {
						pbs_asprintf(&msgbuf,
							"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG3,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					}
					pact->action &= ~(MOM_HOOK_ACTION_SEND_CONFIG);
					hook_track_save((mominfo_t *)minfo_array[i],
						j);
					action_done++;
				}
			} /* send config */

			/* send hook content */
			if (pact->action & MOM_HOOK_ACTION_SEND_SCRIPT) {

				snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
					(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_SCRIPT_SUFFIX)),
					path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_SCRIPT_SUFFIX);

				if (!phook || (phook->event & MOM_EVENTS) == 0) {
					pact->action &= ~MOM_HOOK_ACTION_SEND_SCRIPT;
				} if ((PBSD_copyhookfile(conn, hookfile, 0, NULL) != 0) &&
					(pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to copy hook file %s to %s:%d",
						pbs_errno, hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
						pbs_asprintf(&msgbuf,
							"successfully sent hook file %s to %s:%d",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					} else {
						pbs_asprintf(&msgbuf,
							"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
							hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
						log_event(PBSEVENT_DEBUG3,
							PBS_EVENTCLASS_REQUEST, LOG_INFO,
							msg_daemonname, msgbuf);
						free(msgbuf);
					}
					pact->action &= ~(MOM_HOOK_ACTION_SEND_SCRIPT);
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}
			} /* send script */

			/* execute send actions above first, and then this delete action */
			if (!pact->do_delete_action_first && (pact->action & MOM_HOOK_ACTION_DELETE)) {
				/* delete a hook - overrides other hook actions */
				snprintf(hookfile, sizeof(hookfile), "%.*s%s",
					(int)(sizeof(hookfile) - strlen(HOOK_FILE_SUFFIX) - 1),
					pact->hookname, HOOK_FILE_SUFFIX);

				if (PBSD_delhookfile(conn, hookfile, 0, NULL) != 0) {
					pbs_asprintf(&msgbuf,
						"errno %d: failed to delete hook file %s from %s",
						pbs_errno, hookfile, minfo_array[i]->mi_host);
					log_event(PBSEVENT_DEBUG3,
						PBS_EVENTCLASS_REQUEST, LOG_WARNING,
						msg_daemonname, msgbuf);
					free(msgbuf);
				} else {
					pbs_asprintf(&msgbuf,
						"successfully deleted hook file %s from %s:%d",
						hookfile, minfo_array[i]->mi_host, minfo_array[i]->mi_port);
					log_event(PBSEVENT_DEBUG,
						PBS_EVENTCLASS_REQUEST, LOG_INFO,
						msg_daemonname, msgbuf);
					free(msgbuf);

					/* Delete also any other hook-related actions */
					/* so they don't get erroneously retried */
					pact->action &= ~MOM_HOOK_ACTION_DELETE;
					hook_track_save((mominfo_t *)minfo_array[i], j);
					action_done++;
				}
			}

		} /* j-loop */
		if (conn >= 0) {
			svr_disconnect_with_wait_option(conn, 1);
			conn = -1;
		}
	} /* i-loop */

	if (action_expected == 0) {
		ret = SYNC_HOOKFILES_NONE;
	} else if (action_expected == action_done) {
		ret = SYNC_HOOKFILES_SUCCESS_ALL;
	} else if (action_done > 0) {
		ret = SYNC_HOOKFILES_SUCCESS_PARTIAL;
	} else {
		ret = SYNC_HOOKFILES_FAIL;
	}

	if ((ret == SYNC_HOOKFILES_FAIL) ||
		(ret == SYNC_HOOKFILES_SUCCESS_PARTIAL))
		do_sync_mom_hookfiles = 1;

	return (ret);

}

/**
 * @brief
 *		Allocates a def_hk_cmd_info structure, filling it with 'index', 'event',
 *		'tid' values, and return a pointer to this structure.
 *
 * @see
 * 		check_add_hook_mcast_info
 *
 * @param[in] index - index value to the returned def_hk_cmd_info structure.
 * @param[in] event - event value to the returned def_hk_cmd_info structure.
 * @param[in] tid - transaction id value to the returned def_hk_cmd_info structure.
 *
 * @return struct def_hk_cmd_info *
 *
 * @retval pointer to the structure
 * @retval NULL - if an error occurred allocating and populating the structure.
 *
 * @Note
 *	The caller must call free() on the returned memory pointer if no longer
 *	needed.
 *
 */
struct
def_hk_cmd_info *
mk_deferred_hook_info(int index, int event, long long int tid)
{
	struct def_hk_cmd_info *info = malloc(sizeof(struct def_hk_cmd_info));
	if (info) {
		info->index = index;
		info->event = event;
		info->tid = tid;
	}
	return info;
}

/**
 * @brief
 *		Call back for the hook deferred requests over RPP.
 *		parm1 points to the mominfo_t
 *		parm2 points to more information about the hook cmd
 *		wt_aux has the reply code from mom
 *
 * @Note
 *		If sending or deleting a hook to a mom has been rejected because
 *		mom is not acceping root remote scripts for security reasons, then
 *		this will be considered still a successful send.
 *
 *		The globals g_hook_replies_recvd is incremented for
 *		each reply received. When this matches the global
 *		variable g_hook_replies_expected, the global variable
 *		sync_mom_hookfiles_proc_running is reset to 0, such
 *		that the next "hook transaction" can now start.
 *
 * @param[in] pwt - The work task pointer
 *
 * @return void
 */
void
post_sendhookRPP(struct work_task *pwt)
{
	mominfo_t	*minfo = pwt->wt_parm1;
	mom_hook_action_t *pact;
	int rc = pwt->wt_aux;
	struct def_hk_cmd_info *info = (struct def_hk_cmd_info *) pwt->wt_parm2;
	char hookfile[MAXPATHLEN+1];
	int j;
	int event;
	long long int tid;
	char *msgbuf;

	if (!info)
		return;

	j = info->index;
	event = info->event;
	tid = info->tid;

	free(info);

	if (tid != g_sync_hook_tid) {
		snprintf(log_buffer, sizeof(log_buffer),
			"sendhookRPP reply (tid=%lld) not from current "
			"batch of hook updates (tid=%lld) from mhost=%s",
			tid, g_sync_hook_tid, minfo->mi_host ? minfo->mi_host : "");
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER, LOG_INFO,
			"post_sendhookRPP", log_buffer);
		return;	/* return now as info->index no longer valid */
	}

	pact = minfo->mi_action[j];

	if (event == MOM_HOOK_ACTION_DELETE_RESCDEF) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%.*s",
			(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE),
			path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname);
		if (rc != 0) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to delete rescdef file %s from %s",
				pbs_errno, hookfile, minfo->mi_host);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			pbs_asprintf(&msgbuf,
				"successfully deleted rescdef file %s from %s:%d",
				hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
			free(msgbuf);
			/* Delete all SEND_RESCDEF action */
			/* so it doesn't get retried for this */
			/* "deleted" resourcdef. */
			pact->action &= ~(MOM_HOOK_ACTION_DELETE_RESCDEF | MOM_HOOK_ACTION_SEND_RESCDEF);
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	if (event == MOM_HOOK_ACTION_SEND_RESCDEF) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%.*s",
			(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE), path_hooks,
			PBS_HOOK_NAME_SIZE, pact->hookname);
		if ((rc != 0) && (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to copy rescdef file %s to %s:%d",
				pbs_errno, hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			if (rc != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
				pbs_asprintf(&msgbuf,
					"successfully sent rescdef file %s to %s:%d",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			} else {
				snprintf(log_buffer, sizeof(log_buffer),
					"warning: sending resourcedef to %s:%d got rejected (mom's reject_root_scripts=1)",
					minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, log_buffer);
			}
			pact->action &= ~(MOM_HOOK_ACTION_SEND_RESCDEF);
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	if (event == MOM_HOOK_ACTION_DELETE) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%s",
			(int)(sizeof(hookfile) - strlen(HOOK_FILE_SUFFIX) - 1),
			pact->hookname, HOOK_FILE_SUFFIX);
		if (rc != 0) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to delete hook file %s from %s",
				pbs_errno, hookfile, minfo->mi_host);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			pbs_asprintf(&msgbuf,
				"successfully deleted hook file %s from %s:%d",
				hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
			free(msgbuf);
			pact->action &= ~MOM_HOOK_ACTION_DELETE;
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	if (event == MOM_HOOK_ACTION_SEND_ATTRS) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
			(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_FILE_SUFFIX)),
			path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_FILE_SUFFIX);
		if ((rc != 0) && (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to copy hook file %s to %s:%d",
				pbs_errno, hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
				pbs_asprintf(&msgbuf,
					"successfully sent hook file %s to %s:%d",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			} else {
				pbs_asprintf(&msgbuf,
					"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			}
			pact->action &= ~(MOM_HOOK_ACTION_SEND_ATTRS);
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	if (event == MOM_HOOK_ACTION_SEND_CONFIG) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
			(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_CONFIG_SUFFIX)),
			path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_CONFIG_SUFFIX);
		if ((rc != 0) && (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to copy hook file %s to %s:%d",
				pbs_errno, hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
				pbs_asprintf(&msgbuf,
					"successfully sent hook file %s to %s:%d",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			} else {
				pbs_asprintf(&msgbuf,
					"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			}
			pact->action &= ~(MOM_HOOK_ACTION_SEND_CONFIG);
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	if (event == MOM_HOOK_ACTION_SEND_SCRIPT) {
		snprintf(hookfile, sizeof(hookfile), "%.*s%.*s%s",
			(int)(sizeof(hookfile) - PBS_HOOK_NAME_SIZE - strlen(HOOK_SCRIPT_SUFFIX)),
			path_hooks, PBS_HOOK_NAME_SIZE, pact->hookname, HOOK_SCRIPT_SUFFIX);
		if ((rc != 0) && (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS)) {
			pbs_asprintf(&msgbuf,
				"errno %d: failed to copy hook file %s to %s:%d",
				pbs_errno, hookfile, minfo->mi_host, minfo->mi_port);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, msgbuf);
			free(msgbuf);
		} else {
			if (pbs_errno != PBSE_MOM_REJECT_ROOT_SCRIPTS) {
				pbs_asprintf(&msgbuf,
					"successfully sent hook file %s to %s:%d",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			} else {
				pbs_asprintf(&msgbuf,
					"warning: sending hook file %s to %s:%d got rejected (mom's reject_root_scripts=1)",
					hookfile, minfo->mi_host, minfo->mi_port);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_INFO, msg_daemonname, msgbuf);
				free(msgbuf);
			}
			pact->action &= ~(MOM_HOOK_ACTION_SEND_SCRIPT);
			hook_track_save((mominfo_t *) minfo, j);
		}
	}

	g_hook_replies_recvd++;

	DBPRT(("expected=%d, replies=%d\n", g_hook_replies_expected, g_hook_replies_recvd));

	if (g_hook_replies_recvd == g_hook_replies_expected) {
		/*
		 * We are done with this batch of hook replies
		 * allow next set of hook requests to go out now
		 */
		sync_mom_hookfiles_proc_running = 0;
		g_hook_replies_recvd = 0;
		g_hook_replies_expected = 0;

		/* attempt a collapse of the hook tracking file now */
		collapse_hook_tr();
	}
}

/**
 * @brief
 *		static helper function to check and add a hook command to a mom
 *		to a list of multicast commands.
 *
 *		A RPP multicast command consists of the same command to be sent to
 *		a groups of target moms.
 *
 * @param[in] conn      - The stream to the mom
 * @param[in] minfo     - The pointer to the mom info
 * @param[in] hookname  - Name of the hook for which the command is being sent
 * @param[in] action    - The hook action/cmd being performed
 * @param[in] act_index - The index in the moms hook actions array
 *
 * @return hook_mcast_info_t	- structures required for TPP mcast communication of hooks to moms
 */
static hook_mcast_info_t *
check_add_hook_mcast_info(int conn, mominfo_t *minfo, char *hookname, int action, int act_index)
{
	int i;
	void *tmp;
	struct def_hk_cmd_info *info = NULL;
	char *dup_msgid = NULL;

	for(i = 0; i < g_hook_mcast_array_len; i++) {
		if (strcmp(g_hook_mcast_array[i].hookname, hookname) == 0 &&
				g_hook_mcast_array[i].action == action)
			break;
	}
	if (i < g_hook_mcast_array_len) {
		/* add this connection as part of the mcast connections */

		if ((info = mk_deferred_hook_info(act_index, action,
						g_sync_hook_tid)) == NULL)
			return NULL;

		if ((dup_msgid = strdup(g_hook_mcast_array[i].msgid)) == NULL)
			return NULL;

		if (add_mom_deferred_list(conn, minfo, post_sendhookRPP,
					dup_msgid, minfo, info) == NULL) {
			free(info);
			free(dup_msgid);
			return NULL;
		}

		if (tpp_mcast_add_strm(g_hook_mcast_array[i].mconn, conn) != 0) {
			free(info);
			free(dup_msgid);
			return NULL;
		}
		g_hook_replies_expected++;

		return &g_hook_mcast_array[i];
	}

	/* we did not find a match, allocate a new index */
	i = g_hook_mcast_array_len;

	tmp = realloc(g_hook_mcast_array, sizeof(hook_mcast_info_t) * (g_hook_mcast_array_len + 1));
	if (!tmp) {
		log_err(-1, __func__, "Could not allocate array of hook info");
		return NULL;
	}
	g_hook_mcast_array = tmp;

	g_hook_mcast_array[i].action = action;
	if ((g_hook_mcast_array[i].hookname = strdup(hookname)) == NULL)
		return NULL;

	if (get_msgid(&g_hook_mcast_array[i].msgid) != 0)
		return NULL;

	if ((info = mk_deferred_hook_info(act_index, action,
					g_sync_hook_tid)) == NULL)
		return NULL;

	if (add_mom_deferred_list(conn, minfo, post_sendhookRPP,
				strdup(g_hook_mcast_array[i].msgid), minfo, info) == NULL) {
		free(info);
		return NULL;
	}

	if ((g_hook_mcast_array[i].mconn = tpp_mcast_open()) == -1) {
		free(info);
		return NULL;
	}

	if (tpp_mcast_add_strm(g_hook_mcast_array[i].mconn, conn) != 0) {
		free(info);
		return NULL;
	}

	g_hook_replies_expected++;

	/* Increment size of the array here only when everything is successful
	 * This way, we do not have to reset anything back if we failed earlier
	 * The expanded array is okay to not be resized back
	 */
	g_hook_mcast_array_len++;

	return &g_hook_mcast_array[i];
}

/**
 * @brief
 *		static helper function to delete the deferred hook commands from the
 *		all the moms that are part of the multicast messages information
 *		tracked by index.
 *
 * @see
 * 		sync_mom_hookfilesRPP
 *
 * @param[in]	index - The index in the g_hook_mcast_array
 *
 * @return void
 */
static void
del_deferred_hook_cmds(int index)
{
	char *msgid = g_hook_mcast_array[index].msgid;
	int mconn = g_hook_mcast_array[index].mconn;
	int *conns, count, i, handle;
	mominfo_t *pmom = 0;
	struct work_task *ptask, *tmp_task;
	struct def_hk_cmd_info *info;
	int j;
	int event;
	mom_hook_action_t *pact;
	mominfo_t *minfo;

	conns = tpp_mcast_members(mconn, &count);
	for (i = 0; i < count; i++) {
		handle = conns[i];

		if ((pmom = tfind2((u_long) handle, 0, &streams)) == NULL)
			return;

		/* get the task list */
		ptask = (struct work_task *) GET_NEXT((((mom_svrinfo_t *)
					(pmom->mi_data))->msr_deferred_cmds));

		while (ptask) {
			/* no need to compare wt_event with handle, since the
			 * task list is for this mom and so it will always match
			 */
			tmp_task = ptask;
			ptask = (struct work_task *) GET_NEXT(ptask->wt_linkobj2);
			if (tmp_task->wt_type == WORK_Deferred_cmd &&
					strcmp(msgid, tmp_task->wt_event2)==0) {

				if (tmp_task->wt_event2)
					free(tmp_task->wt_event2);

				minfo = tmp_task->wt_parm1;
				info = (struct def_hk_cmd_info *) tmp_task->wt_parm2;

				if (!info)
					return;

				j = info->index;
				event = info->event;
				free(info);

				pact = minfo->mi_action[j];

				pact->action &= ~(event);
				hook_track_save((mominfo_t *) minfo, j);

				/* now dispatch the reply to the routine in the work task */
				delete_task(tmp_task);

				g_hook_replies_expected--;
			}
		}
	}
}

/**
 * @brief
 *		Performs actions such as send hook attributes/scripts, and also
 *		resourcedef file to a particular mom, or to all the moms in the
 *		system (this function performs this using RPP deferred requests).
 *
 * @see
 * 		bg_sync_mom_hookfiles and bg_delete_mom_hooks
 *
 * @param[in]	minfo	- particular mom information to send hook request, or
 *			 	if NULL, then hook action request sent to all the
 *				moms in the system.
 *
 * @return enum sync_hookfiles_result
 * @retval	SYNC_HOOKFILES_NONE	if all mom hook actions succeeded in sending.
 * @retval	SYNC_HOOKFILES_FAIL	if all mom hook actions failed to be sent.
 * @retval	SYNC_HOOKFILES_PARTAIL	if some (not all) mom hook actions failed to be sent.
 */
enum sync_hookfiles_result
sync_mom_hookfilesRPP(void *minfo)
{
	int		i, j;
	int		conn = -1;	/* a client style connection handle */
	char		hookfile[MAXPATHLEN+1];
	mominfo_t	**minfo_array = NULL;
	int		minfo_array_size;
	mominfo_t	*minfo_array_tmp[1];
	mom_hook_action_t *pact;
	int		skipped = 0;
	int ret = SYNC_HOOKFILES_NONE;

	if (minfo == NULL) {
		minfo_array = mominfo_array;
		minfo_array_size = mominfo_array_size;
	} else {
		minfo_array_tmp[0] = minfo;
		minfo_array = (mominfo_t **)minfo_array_tmp;
		minfo_array_size = 1;
	}

	sync_mom_hookfiles_proc_running = 1;
	g_sync_hook_tid = hook_action_tid_get();
	snprintf(log_buffer, sizeof(log_buffer),
		"g_sync_hook_tid=%lld", g_sync_hook_tid);
	log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
		LOG_INFO, __func__, log_buffer);

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

		if (minfo_array[i] == NULL)
			continue;

		conn = ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_stream;
		if (conn == -1) {
			skipped++;
			continue;
		}

		if (((mom_svrinfo_t *) (minfo_array[i]->mi_data))->msr_state & INUSE_DOWN) {
			skipped++;
			continue;
		}

		rpp_add_close_func(conn, process_DreplyRPP); /* register a close handler */

		pbs_errno = 0;
		for (j = 0; j < minfo_array[i]->mi_num_action; j++) {
			hook *phook;
			pact = minfo_array[i]->mi_action[j];

			if ((pact == NULL) || (pact->action == MOM_HOOK_ACTION_NONE))
				continue;

			if (pact->action & MOM_HOOK_ACTION_DELETE_RESCDEF) {
				if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname,
								MOM_HOOK_ACTION_DELETE_RESCDEF, j))
					ret = SYNC_HOOKFILES_FAIL;
			} else if (pact->action & MOM_HOOK_ACTION_SEND_RESCDEF) {
				if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname,
								MOM_HOOK_ACTION_SEND_RESCDEF, j))
					ret = SYNC_HOOKFILES_FAIL;
			}

			/* execute delete action before the send actions */
			if (pact->do_delete_action_first && (pact->action & MOM_HOOK_ACTION_DELETE)) {
				if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname,
								MOM_HOOK_ACTION_DELETE, j))
					ret = SYNC_HOOKFILES_FAIL;
			}

			phook = find_hook(pact->hookname);
			if (pact->action & MOM_HOOK_ACTION_SEND_ATTRS) {
				if (!phook || (phook->event & MOM_EVENTS) == 0)
					pact->action &= ~MOM_HOOK_ACTION_SEND_ATTRS;
				else if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname, MOM_HOOK_ACTION_SEND_ATTRS, j))
					ret = SYNC_HOOKFILES_FAIL;
			}

			if (pact->action & MOM_HOOK_ACTION_SEND_CONFIG) {
				if (!phook || (phook->event & MOM_EVENTS) == 0)
					pact->action &= ~MOM_HOOK_ACTION_SEND_CONFIG;
				else if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname, MOM_HOOK_ACTION_SEND_CONFIG, j))
					ret = SYNC_HOOKFILES_FAIL;
			}

			if (pact->action & MOM_HOOK_ACTION_SEND_SCRIPT) {
				if (!phook || (phook->event & MOM_EVENTS) == 0)
					pact->action &= ~MOM_HOOK_ACTION_SEND_SCRIPT;
				else if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname,
							MOM_HOOK_ACTION_SEND_SCRIPT, j))
					ret = SYNC_HOOKFILES_FAIL;
			}

			/* execute send actions above first, and then this delete action */

			if ((!pact->do_delete_action_first) && (pact->action & MOM_HOOK_ACTION_DELETE)) {
				if (!check_add_hook_mcast_info(conn, minfo_array[i], pact->hookname,
								MOM_HOOK_ACTION_DELETE, j))
					ret = SYNC_HOOKFILES_FAIL;
			}
		} /* j-loop */
	} /* i-loop */

	/* now do the actual transmissions */
	for (i = 0; i < g_hook_mcast_array_len; i++) {
		char *msgid = g_hook_mcast_array[i].msgid;
		char *hookname = g_hook_mcast_array[i].hookname;
		int mconn = g_hook_mcast_array[i].mconn;
		int rc = 0;
		int cmd;
		int filetype;

		if (g_hook_mcast_array[i].action == MOM_HOOK_ACTION_DELETE_RESCDEF) {
			snprintf(hookfile, sizeof(hookfile), "%s", hookname);
			cmd = 1;
			filetype = 1;
		} else if (g_hook_mcast_array[i].action & MOM_HOOK_ACTION_SEND_RESCDEF) {
			snprintf(hookfile, sizeof(hookfile), "%s%s", path_hooks, hookname);
			cmd = 2;
			filetype = 1;
		} else if (g_hook_mcast_array[i].action & MOM_HOOK_ACTION_DELETE) {
			snprintf(hookfile, sizeof(hookfile), "%s%s", hookname, HOOK_FILE_SUFFIX);
			cmd = 1;
			filetype = 2;
		} else if (g_hook_mcast_array[i].action & MOM_HOOK_ACTION_SEND_ATTRS) {
			snprintf(hookfile, sizeof(hookfile), "%s%s%s", path_hooks, hookname, HOOK_FILE_SUFFIX);
			cmd = 2;
			filetype = 2;
		} else if (g_hook_mcast_array[i].action & MOM_HOOK_ACTION_SEND_CONFIG) {
			snprintf(hookfile, sizeof(hookfile), "%s%s%s", path_hooks, hookname, HOOK_CONFIG_SUFFIX);
			cmd = 2;
			filetype = 2;
		} else if (g_hook_mcast_array[i].action & MOM_HOOK_ACTION_SEND_SCRIPT) {
			snprintf(hookfile, sizeof(hookfile), "%s%s%s", path_hooks, hookname, HOOK_SCRIPT_SUFFIX);
			cmd = 2;
			filetype = 2;
		} else {
			cmd = 0;
			filetype = 0;
			snprintf(log_buffer, sizeof(log_buffer), "Unrecognized hook action");
			rc = -1;
		}

		if (cmd == 1) {
			if (PBSD_delhookfile(mconn, hookfile, 1, &msgid) != 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"errno %d: failed to multicast deletion of %s file %s",
					pbs_errno, ((filetype == 1) ? "rscdef":"hook"), hookfile);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
				LOG_INFO, __func__, log_buffer);
				rc = -1;
			} else {
				snprintf(log_buffer, sizeof(log_buffer), "PBSD_delhookfile(hookfile=%s)", hookfile);
				log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);
			}
		} else if (cmd == 2) {

			rc = PBSD_copyhookfile(mconn, hookfile, 1, &msgid);
			if (rc == -2) {
				snprintf(log_buffer, sizeof(log_buffer), "PBSD_copyhookfile(mconn=%d, hookfile=%s): no hook file to copy (rc == -2)", mconn, hookfile);
				log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
				LOG_INFO, __func__, log_buffer);
				/* no hookfile to copy */
				del_deferred_hook_cmds(i);
				rc = 0;
			} else if (rc != 0) {
				snprintf(log_buffer, sizeof(log_buffer),
					"errno %d: failed to multicast copy %s file %s",
					pbs_errno, ((filetype == 1) ? "rscdef":"hook"), hookfile);
				log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);
				rc = -1;
			} else {
				snprintf(log_buffer, sizeof(log_buffer), "PBSD_copyhookfile(hookfile=%s)", hookfile);
				log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);
			}
		}

		if (rc == -1) {
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_REQUEST, LOG_WARNING, msg_daemonname, log_buffer);
			del_deferred_hook_cmds(i);
			ret = SYNC_HOOKFILES_FAIL;
		}

		/* we are done with the mcast for this index */
		tpp_mcast_close(mconn);
		free(g_hook_mcast_array[i].hookname);
		free(g_hook_mcast_array[i].msgid);
	}

	if (g_hook_mcast_array) {
		free(g_hook_mcast_array);
		g_hook_mcast_array = NULL;
		g_hook_mcast_array_len = 0;
	}

	if (g_hook_replies_expected == 0) {
		/* No hook requests sent, so we set the
		 * variable to 0, so that the next set of
		 * hook pending operations can get triggered
		 */
		sync_mom_hookfiles_proc_running = 0;
	}

	/* set success to partial so that we come back and try again later */
	if (skipped > 0)
		ret = SYNC_HOOKFILES_SUCCESS_PARTIAL;

	/* if we returned SYNC_HOOKFILES_NONE, then all hook actions were sent, no retry
	 * needs to be done. This is in sync with bg_sync_mom_hookfiles() return values.
	 */
	return (ret);
}

/**
 * @brief
 * 	 	Kill the sync_mom_hookfiles() process.
 *
 * @note
 * 		Kills the pid value found in the global variable 'g_sync_hook_pid',
 * 		and resets the values to -1.
 *
 * @see
 * 		next_sync_mom_hookfiles
 *
 * @return void
 */
static void
kill_sync_hook_process(void)
{
#ifdef WIN32
	HANDLE h_pid;
#endif

	if (g_sync_hook_pid <= 0)
		return;

#ifdef WIN32
	h_pid = OpenProcess(PROCESS_ALL_ACCESS, TRUE, g_sync_hook_pid);
	if (h_pid != NULL) {
		kill((HANDLE) h_pid, SIGTERM);
		CloseHandle(h_pid);
	}
#else
	if (kill(g_sync_hook_pid, SIGKILL) == -1) {
		log_err(errno, __func__, "error killing pid");
		return;
	}
#endif
	g_sync_hook_pid = -1;
}

/**
 * @brief
 * 		This function is called by the main process when child process
 *		finishes executing sync_mom_hookfiles().
 *
 * @par Functionality
 *		If exit status of the previous sync_mom_hookfiles() is either
 *		SYNC_HOOKFILES_SUCCESS_ALL or SYNC_HOOKFILES_SUCCESS_PARTIAL, then
 *		reload the hooks tracking data file into
 *		mominfo_array[]->mi_action[], since the previous process
 *		has updated that file. Then purge "pending to delete" hooks where a
 *		"delete hook files" request has been successfully sent to all the
 *		moms.
 *
 * 		If exit status is SYNC_HOOKFILES_SUCCESS_PARTIAL or SYNC_HOOKFILES_FAIL,
 *		then continue to tell pbs_server to call sync_mom_hookfiles()
 *		by setting the do_sync_mom_hookfiles to 1.
 *
 * @see
 * 		bg_sync_mom_hookfiles
 *
 * @param[in]	pwt - pointer to the work task entry.
 *
 * @return void
 */
static void
post_sync_mom_hookfiles(struct work_task *pwt)
{
	int	 		r;
	int	 		stat = pwt->wt_aux;

	sync_mom_hookfiles_proc_running = 0; /* reaped the process that was */
	/* running sync_mom_hookfiles() */

	if (WIFEXITED(stat)) {

		r = WEXITSTATUS(stat);
	} else {
		r = 3;
		(void)sprintf(log_buffer, msg_badexit, stat);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER, LOG_INFO,
			__func__, log_buffer);
	}

	if ((r == SYNC_HOOKFILES_SUCCESS_ALL) ||
		(r == SYNC_HOOKFILES_SUCCESS_PARTIAL)) {
		collapse_hook_tr();
	}

	if ((r == SYNC_HOOKFILES_SUCCESS_PARTIAL) ||
		(r == SYNC_HOOKFILES_FAIL)) {
		do_sync_mom_hookfiles = 1;
	}
	/* else: let's not reset do_sync_mom_hookfiles to 0 here since */
	/* it might have been set to 1 elsewhere */

}

/**
 * @brief
 *		The task wrapper to sync_mom_hookfiles(), which executes in a child
 *		process.
 *
 * @return int
 * @retval 0	for successfully executing the task process to
 *				sync_mom_hookfiles()
 * @retval != 0 if an error occurred.
 */
int
bg_sync_mom_hookfiles(void)
{
	int		rc;
	struct		work_task	*ptask;

#ifdef WIN32
	char	cmdline[BUFSIZ+1];
	pio_handles	pio;
	char	buf[BUFSIZ+1];
#else
	pid_t	pid;
	struct 	sigaction act;
#endif

	g_sync_hook_time = time(0);
	snprintf(log_buffer, sizeof(log_buffer), "g_sync_hook_time = %s",
			ctime(&g_sync_hook_time));
	log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
					LOG_INFO, __func__, log_buffer);
	if (pbs_conf.pbs_use_tcp == 1) {
		rc = sync_mom_hookfilesRPP(NULL);
		/* transaction id to use for next batch of updates */
		hook_action_tid_set(hook_action_tid_get()+1);
		return rc;
	}

#ifndef WIN32
	pid = fork();

	if (pid == -1) {	/* Error on fork */
		log_err(errno, __func__, "fork failed\n");
		pbs_errno = PBSE_SYSTEM;
		return (1);
	}

	if (pid != 0) {		/* The parent (main server) */

		hook_action_tid_set(hook_action_tid_get()+1);
		g_sync_hook_pid = pid;
		snprintf(log_buffer, sizeof(log_buffer), "g_sync_hook_pid = %d",
				g_sync_hook_pid);
		log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
					LOG_INFO, __func__, log_buffer);
		sync_mom_hookfiles_proc_running = 1;
		ptask = set_task(WORK_Deferred_Child, pid,
			post_sync_mom_hookfiles, NULL);
		if (!ptask) {
			log_err(errno, __func__, msg_err_malloc);
			return (1);
		}
		return (0);
	}

	/*
	 * the child process
	 */

	/* standard rpp closure and net close */
	net_close(-1);
	rpp_terminate();

	/* Reset signal actions for most to SIG_DFL */
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = SIG_DFL;
	(void)sigaction(SIGCHLD, &act, NULL);
	(void)sigaction(SIGHUP, &act, NULL);
	(void)sigaction(SIGINT, &act, NULL);
	(void)sigaction(SIGTERM, &act, NULL);

	/* Reset signal mask */
	(void)sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
	rc=sync_mom_hookfiles(NULL);
	exit(rc);

#else  /* Windows */
	sprintf(cmdline, "%s/sbin/pbs_send_hooks", pbs_conf.pbs_exec_path);

	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_sync_mom_hookfiles, NULL);
	if (!ptask) {
		log_err(errno, __func__, msg_err_malloc);
		errno = ENOMEM;
		pbs_errno = errno;
		win_pclose(&pio);
		return (1);
	}
	addpid(pio.pi.hProcess);

	sprintf(buf, "pbs_server_addr=%ld\n", pbs_server_addr);
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "pbs_server_port_dis=%d\n", pbs_server_port_dis);
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "log_file=%s\n", (log_file?log_file:""));
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "path_log=%s\n", (path_log?path_log:""));
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "path_hooks=%s\n", (path_hooks?path_hooks:""));
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "path_hooks_tracking=%s\n",
		(path_hooks_tracking?path_hooks_tracking:""));
	win_pwrite(&pio, buf, strlen(buf));

	sprintf(buf, "hook_action_tid=%lld\n", hook_action_tid);
	win_pwrite(&pio, buf, strlen(buf));

	strcpy(buf, "quit\n");
	win_pwrite(&pio, buf, strlen(buf));

	win_pclose2(&pio); /* closes all handles except the process handle */
	g_sync_hook_pid = (pid_t)pio.pi.dwProcessId;
	snprintf(log_buffer, sizeof(log_buffer), "g_sync_hook_pid = %d",
                        (pid_t)g_sync_hook_pid);
	log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER,
					LOG_INFO, __func__, log_buffer);
	sync_mom_hookfiles_proc_running = 1;
	hook_action_tid_set(hook_action_tid_get()+1);
	return (0);
#endif

}

/**
 * @brief
 *		Adds a pending action to all hooks for the mom in 'minfo' if not NULL,
 *		or all the moms in the system.
 * @par NOTE
 *		For every successful pending action add, a line of data is
 *		written in [PATH_HOOKS]/hook_tracking.TR file as:
 *			<mom_name> <mom_port> <hook_name> <action>
 *		where <action> is the current action flag value.
 *
 * @param[in]	minfo		- if not NULL, then add mom hook action
 *				on this particular mom in 'minfo'.
 * @param[in] 	action		- the type of action
 *				(MOM_HOOK_ACTION_SEND_ATTRS,
 *				MOM_HOOK_ACTION_SEND_SCRIPT, etc...)
 *
 * @return void
 */
void
add_pending_mom_allhooks_action(void *minfo, unsigned int action)
{
	hook			*phook;


	phook = (hook *)GET_NEXT(svr_execjob_begin_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_begin_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_prologue_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_prologue_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_epilogue_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_epilogue_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_end_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_end_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_preterm_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_preterm_hooks);
	}

	phook = (hook *)GET_NEXT(svr_exechost_periodic_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_exechost_periodic_hooks);
	}

	phook = (hook *)GET_NEXT(svr_exechost_startup_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_exechost_startup_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_launch_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_launch_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_attach_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_attach_hooks);
	}

	phook = (hook *)GET_NEXT(svr_execjob_resize_hooks);
	while (phook) {
		add_pending_mom_hook_action((mominfo_t *)minfo,
			phook->hook_name, action);
		phook = (hook *)GET_NEXT(phook->hi_execjob_resize_hooks);
	}

}

/**
 * @brief
 *		Checks to see if it's time to run bg_sync_mom_hookfiles() and if so,
 *		then run bg_sync_mom_hookfiles().
 *
 * @see
 * 		next_task
 *
 * @return	void
 */
void
next_sync_mom_hookfiles(void)
{
	unsigned long timeout_sec;
	time_t	timeout_time;
	time_t	current_time;
	short timed_out = 0;

	if (pbs_conf.pbs_use_tcp == 1) {
		timeout_sec = SYNC_MOM_HOOKFILES_TIMEOUT_TPP;
		if (server.sv_attr[(int)SRV_ATR_sync_mom_hookfiles_timeout].at_flags & ATR_VFLAG_SET)
			timeout_sec = server.sv_attr[(int)SRV_ATR_sync_mom_hookfiles_timeout].at_val.at_long;
	} else {
		timeout_sec = SYNC_MOM_HOOKFILES_TIMEOUT;
	}
	current_time = time(NULL);
	timeout_time = g_sync_hook_time + timeout_sec;

	if (sync_mom_hookfiles_proc_running) {
		if (current_time <= timeout_time)
			/* previous updates still in progress and not timed out */
			return;

		/* we're timing out previous sync mom hook files process/action */
		if (pbs_conf.pbs_use_tcp == 1) {
			snprintf(log_buffer, sizeof(log_buffer),
				"Timing out previous send of mom hook updates "
				"(send replies expected=%d received=%d)",
				g_hook_replies_expected, g_hook_replies_recvd);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
				LOG_INFO, __func__, log_buffer);
			snprintf(log_buffer, sizeof(log_buffer), "timeout_sec=%lu", timeout_sec);
			log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);

			g_hook_replies_recvd = 0;
			g_hook_replies_expected = 0;
		} else {
			sprintf(log_buffer,
				"Timing out previous send of mom hook updates "
				"(killing child process %d)", g_sync_hook_pid);
			log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
				LOG_INFO, __func__, log_buffer);
			kill_sync_hook_process();
		}
		/* attempt collapsing  the hook tracking file */
		collapse_hook_tr();
		sync_mom_hookfiles_proc_running = 0;
		timed_out = 1;
	}

	if ((do_sync_mom_hookfiles || timed_out) && bg_sync_mom_hookfiles() == 0)
		do_sync_mom_hookfiles = 0;
}

/**
 * @brief
 *		Mark that a mom hook has been seen, resulting in 'mom_hooks_seen'
 *		variable getting incremented.
 *
 * @see
 * 		pbsd_init
 *
 * @return void
 */
void
mark_mom_hooks_seen(void)
{

	if (mom_hooks_seen < 0) {	/* should not happen */
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
			LOG_INFO, __func__,
			"mom_hooks_seen went negative, resetting to 0");
		mom_hooks_seen = 0;
	}
	mom_hooks_seen++;

}

/**
 * @brief
 *		Returns the value of the 'mom_hooks_seen' variable.
 *
 * @see
 * 		create_mom_entry, delete_svrmom_entry and start_vnode_provisioning.
 *
 * @return int - the mom_hooks_seen value
 */
int
mom_hooks_seen_count(void)
{
	return (mom_hooks_seen);
}

/**
 * @brief
 *		Run a child process that will send the delete hook
 *		requests to the 'mom' represented by 'minfo' data.
 *		The child process will not get tracked by the
 *		calling process.
 *
 * @see
 * 		delete_svrmom_entry
 *
 * @param[in]	minfo	- data reprsenting the 'mom'.
 *
 * @return int
 * @retval 0	for success
 * @retval != 0 if an error occurred.
 */
int
bg_delete_mom_hooks(void *minfo)
{
	char		path_hooks_tracking_tmp[MAXPATHLEN+1];

#ifdef WIN32
	char	cmdline[BUFSIZ+1];
	struct	pio_handles	pio;
	char	buf[BUFSIZ+1];
	char	*path_hooks_tracking_save;
#else
	pid_t	pid;
	struct 	sigaction act;
#endif

	if (pbs_conf.pbs_use_tcp == 1) {
		/*
		 * add_pending* and sync_mom_hookfiles() use the
		 * path_hooks_tracking file for recording the hook
		 * actions to perform and their outcome.
		 */
		add_pending_mom_allhooks_action(minfo, MOM_HOOK_ACTION_DELETE);
		add_pending_mom_hook_action(minfo, PBS_RESCDEF,
			MOM_HOOK_ACTION_DELETE_RESCDEF);
		(void)sync_mom_hookfilesRPP(minfo);
		return 0;
	}

#ifndef WIN32
	pid = fork();

	if (pid == -1) {	/* Error on fork */
		log_err(errno, __func__, "fork failed\n");
		pbs_errno = PBSE_SYSTEM;
		return (1);
	}

	if (pid != 0) /* The parent (main server) */
		return (0);

	/*
	 * the child process
	 */

	/* standard rpp closure and net close */
	net_close(-1);
	rpp_terminate();

	/* Reset signal actions for most to SIG_DFL */
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = SIG_DFL;
	(void)sigaction(SIGCHLD, &act, NULL);
	(void)sigaction(SIGHUP, &act, NULL);
	(void)sigaction(SIGINT, &act, NULL);
	(void)sigaction(SIGTERM, &act, NULL);

	/* Reset signal mask */
	(void)sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);

	snprintf(path_hooks_tracking_tmp, MAXPATHLEN,
		"%s%s%s.%d", path_hooks_workdir, PBS_TRACKING,
		HOOK_TRACKING_SUFFIX, getpid());

	path_hooks_tracking = (char *)path_hooks_tracking_tmp;

	/*
	 * add_pending* and sync_mom_hookfiles() use the
	 * path_hooks_tracking file for recording the hook
	 * actions to perform and their outcome.
	 */
	add_pending_mom_allhooks_action(minfo, MOM_HOOK_ACTION_DELETE);
	add_pending_mom_hook_action(minfo, PBS_RESCDEF,
		MOM_HOOK_ACTION_DELETE_RESCDEF);
	(void)sync_mom_hookfiles(minfo);
	exit(0);

#else  /* Windows */

	snprintf(cmdline, sizeof(cmdline), "%s/sbin/pbs_send_hooks",
		pbs_conf.pbs_exec_path);

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

	snprintf(buf, BUFSIZ, "pbs_server_addr=%ld\n", pbs_server_addr);
	win_pwrite(&pio, buf, strlen(buf));

	snprintf(buf, BUFSIZ, "pbs_server_port_dis=%d\n", pbs_server_port_dis);
	win_pwrite(&pio, buf, strlen(buf));

	snprintf(buf, BUFSIZ, "log_file=%s\n", (log_file?log_file:""));
	win_pwrite(&pio, buf, strlen(buf));

	snprintf(buf, BUFSIZ, "path_log=%s\n", (path_log?path_log:""));
	win_pwrite(&pio, buf, strlen(buf));

	snprintf(buf, BUFSIZ, "path_hooks=%s\n", (path_hooks?path_hooks:""));
	win_pwrite(&pio, buf, strlen(buf));

	path_hooks_tracking_save = path_hooks_tracking;
	snprintf(path_hooks_tracking_tmp, MAXPATHLEN,
		"%s%s%s.%ld", path_hooks_workdir, PBS_TRACKING,
		HOOK_TRACKING_SUFFIX, time(NULL));
	path_hooks_tracking = (char *)path_hooks_tracking_tmp;
	/*
	 * add_pending* and pbs_send_hooks prgram use the
	 * path_hooks_tracking file for recording the hook
	 * actions to perform and their outcome.
	 */
	add_pending_mom_allhooks_action(minfo, MOM_HOOK_ACTION_DELETE);
	add_pending_mom_hook_action(minfo, PBS_RESCDEF,
		MOM_HOOK_ACTION_DELETE_RESCDEF);
	/*
	 * restore original 'path_hooks_tracking' value, since
	 * on Windows, this is still the main server
	 */
	path_hooks_tracking = path_hooks_tracking_save;
	sprintf(buf, "path_hooks_tracking=%s\n", path_hooks_tracking_tmp);
	win_pwrite(&pio, buf, strlen(buf));

	strcpy(buf, "quit\n");
	win_pwrite(&pio, buf, strlen(buf));

	win_pclose2(&pio); /* closes all handles except the process handle */
	close_valid_handle(&(pio.pi.hProcess)); /* no tracking involved so */
	/* we don't need this handle */
	return (0);
#endif

}

/**
 * @brief
 * 		Returns the hook resourcedef file checksum value.
 *
 * @see
 * 		is_request
 *
 * @return usigned long
 */
unsigned long
get_hook_rescdef_checksum(void)
{
	return (hook_rescdef_checksum);
}

/**
 *
 * @brief
 *	Get the results from 'output_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[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[out]		hook_output - struct of parameters to fill in output.
 *
 * @return int
 * @retval	0 for success
 * @retval	non-zero for failure;  the returned parameters (accept_flag,
 *		reject_flag and pjob) may be invalid and should be
 *		ignored.   The list svrvnalist could have mallocate space and
 *		should be freed by the calling program.
 */
int
get_server_hook_results(char *input_file, int *accept_flag, int *reject_flag, char *reject_msg,
	int reject_msg_size, job *pjob, hook *phook, hook_output_param_t *hook_output)  {

	char	*name_str;
	char	*resc_str;
	char	*obj_name;
	char	*data_value;
	char	*vname_str;
	int	rc = -1;
	char	*pc, *pc1, *pc2, *pc3, *pc4;
	char	*in_data = NULL;
	size_t	ll;
	FILE	*fp;
	char	*p;
	int	vn_obj_len = strlen(EVENT_VNODELIST_OBJECT);
	char	hook_job_outfile[MAXPATHLEN + 1];
	FILE	*fp2 = NULL;
	char	*line_data = NULL;
	int	line_data_sz;
	long int endpos;
	char	hook_euser[PBS_MAXUSER + 1] = {'\0'};
	int	arg_list_entries = 0;
	int	b_triple_quotes = 0;
	int	e_triple_quotes = 0;
	char	buf_data[STRBUF];
	int	buf_data_sz = STRBUF;
	int	valln = 0;
	svrattrl *plist = NULL;
	struct pbsnode *pnode;
	int	bad = 0;
	int	ndtype_flag = 0;
	char	*pbse_err;
	char	raw_err[10];
	int	update_db = 0;

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

	/* input_file will have content of the format: */
	/* pbs.event().accept=True */
	/* pbs.event().reject=False */
	/* pbs.event().vnode.pcpus=4 */
	/* pbs.event().vnode_list["node_name"].state=sleep */
	/* pbs.event().vnode_list["node_name"].resources_available[ncpus]=10 */
	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);
	}

	line_data_sz = STRBUF;
	line_data = (char *)malloc(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;
	}

	while (fgets(buf_data, buf_data_sz, 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) {
			/* string begins with three consecutive double quotes */
			b_triple_quotes = starts_with_triple_quotes(p+1);
		}

		ll = strlen(in_data);
		if (in_data[ll-1] == '\n') {
			/* string ends with three consecutive double quotes */
			e_triple_quotes = ends_with_triple_quotes(in_data, 0);

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

				while (fgets(buf_data, buf_data_sz, 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;
				}

				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;
				}
				/* 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);
			} 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) {
				if (hook_output != NULL) {
					char	**prog;
					/* 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) {
				arg_list_entries++;
				if (hook_output != NULL) {
					pbs_list_head *ar_list;
					ar_list = hook_output->argv_list;
					/* 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) {
					char	**env;
					env = hook_output->env;
					if (*env != NULL) {
						free(*env);
					}
					*env = strdup(data_value);
				}
			}

			/* if the hook is rejected we can go out now */
			if((*reject_flag == 1) && (reject_msg != NULL))
				goto get_hook_results_end;
		} else if (strncmp(obj_name, EVENT_VNODELIST_OBJECT,
			vn_obj_len) == 0) {

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

			/* server periodic hook vnode objects */
			valln = (int)strlen(data_value) + 1;
			plist = attrlist_create(name_str, resc_str, valln);
			if (plist == NULL) {
				(void)sprintf(log_buffer,"failed to add svrattrl list %s.%s.%s:%s",
						vname_str,name_str,resc_str,data_value);
				log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
					LOG_NOTICE, msg_daemonname, log_buffer);
			} else {
				strcpy(plist->al_value, data_value);
				(plist->al_link).ll_next->ll_struct = NULL;
				/* there are vnode hook updates */
				/* Push hook changes to server */
				pnode = find_nodebyname(vname_str);
				if (pnode == NULL) {
					log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
						LOG_INFO, phook->hook_name, "node_name not found");
				} else if ((pnode->nd_state & INUSE_DELETED) == 0) {
					save_characteristic(pnode);

					rc = mgr_set_attr(pnode->nd_attr, node_attr_def, ND_ATR_LAST,
								plist, ATR_DFLAG_WRACC, &bad, (void *)pnode, ATR_ACTION_ALTER);
					if (rc != 0) {
						pbse_err = pbse_to_txt(rc);
						snprintf(raw_err, sizeof(raw_err), "%d", rc);
						sprintf(log_buffer, "vnode %s: failed to set %s to %s: %s",
								pnode->nd_name, plist->al_name, plist->al_value ? plist->al_value : "",
								pbse_err ? pbse_err : raw_err);
						log_err(PBSE_SYSTEM, __func__, log_buffer);
					} else {
						(void)chk_characteristic(pnode, &ndtype_flag);
						update_db |= ndtype_flag;

						mgr_log_attr(msg_man_set, plist,
							PBS_EVENTCLASS_NODE, pnode->nd_name, NULL);
					}
				}
				free_svrattrl(plist);
			}
		}
		/* TODO: for job objects */
		/* TODO: for Server objects */
		/* TODO: for PBS objects */
		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 (update_db & WRITE_NEW_NODESFILE) {
		/*create/delete/prop/ntype change*/
		(void)save_nodes_db(0, NULL);
	} else if (update_db & WRITENODE_STATE) {
		write_node_state();
	}

	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
 *		Callback function for reaping server periodic hook child.
 * @param[in]	ptask	- work task pointer
 *
 * @return	void
 */
static void
post_server_periodic_hook(struct work_task *ptask)
{

	int	stat;
	hook	*phook;
	pid_t	mypid;
	char	hook_outfile[MAXPATHLEN + 1];
	time_t	next_time;
	int	accept_flag = 1;
	int	reject_flag = 0;
	stat = ptask->wt_aux;
	phook = (hook *)ptask->wt_parm1;
#ifdef WIN32
	mypid = ptask->wt_aux2;
#else
	mypid = ptask->wt_event;
#endif
	if (phook == NULL) {
		log_err(-1, __func__, "A periodic hook disappeared");
		return;
	}
	if (WIFEXITED(stat)) {
		char reject_msg[HOOK_MSG_SIZE + 1] = {'\0'};
		char *next_time_str;
		int hook_error_flag = 0;

		/* Check hook exit status */
		if (stat == 0) {
			snprintf(log_buffer, LOG_BUF_SIZE,
				"Hook got rejected");
			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_PERIODIC,
			phook->hook_name, mypid);

		if (hook_error_flag == 0) {
			/* hook exited normally, get results from file  */
			if (get_server_hook_results(hook_outfile, &accept_flag, &reject_flag,
				reject_msg, sizeof(reject_msg), NULL, phook, 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'",
				"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->debug)
				(void)unlink(hook_outfile);	/* remove file */
		}

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

		sprintf(log_buffer, "Server periodic hook ran successfully");
	} else
		sprintf(log_buffer, "Server periodic hook encountered errors: %d", stat);

	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER, LOG_INFO,
		__func__, log_buffer);

	return;
}

/**
 * @brief
 *		Callback function for Timed work tasks to run periodic hooks
 * @param[in]	ptask	- work task pointer
 *
 * @return  void
 */
void
run_periodic_hook(struct work_task *ptask)
{
	char hook_msg[HOOK_MSG_SIZE] = {'\0'};
	int ret;
	int num_run = 0;
	hook *phook;
	hook_input_param_t req_ptr;
	pid_t	pid;
#ifdef WIN32
	pid_t	win_pid;
	TCHAR	  hkFileName[MAX_PATH];
	STARTUPINFO             si = { 0 };
	PROCESS_INFORMATION     pi = { 0 };
	int	flags = CREATE_DEFAULT_ERROR_MODE|
		CREATE_NEW_CONSOLE|CREATE_NEW_PROCESS_GROUP;
#endif
	int event_initialized = 0;

	phook = (hook *)ptask->wt_parm1;
	hook_input_param_init(&req_ptr);
	if (phook == NULL) {
		log_err(-1, __func__, "A periodic hook disappeared");
		return;
	}

	if (phook->enabled == 0 || phook->script == NULL || phook->freq < 1) {
		sprintf(log_buffer, "periodic hook is missing information, check hook frequency and script");
		log_err(-1,__func__,log_buffer);
	}

	if (has_task_by_parm1(phook) == 1) {
		/* There is already a task present related to
		 * post processing for previously running hook.
		 * Don't run hook this time, just register a
		 * timed task for it's next occurance
		 */
		(void)set_task(WORK_Timed, time_now + phook->freq, run_periodic_hook, phook);
		return;
	}

#ifndef WIN32
	pid = fork();

	if (pid == -1) {	/* Error on fork */
		log_err(errno, __func__, "fork failed\n");
		pbs_errno = PBSE_SYSTEM;
		return;
	}
#else
	snprintf(hkFileName, sizeof(hkFileName), "%s/sbin/pbs_run_periodic_hook %s %s",pbs_conf.pbs_exec_path,
						phook->hook_name, path_priv);
	ret = CreateProcess(NULL, hkFileName, NULL, NULL, TRUE, flags , NULL, NULL, &si, &pi);
	if (ret == 0) {
		log_err(-1, "run_periodic_hook", "Failed to create server periodic hook");
		return;
	}
	pid = (long)pi.hProcess;
	win_pid = (long)pi.dwProcessId;
	addpid(pid);
#endif
	if (pid != 0) {		/* The parent (main server) */
		/* Set a task for post processing of the running hook */
		struct  work_task *ptask;
		ptask = set_task(WORK_Deferred_Child, (long)pid,
			post_server_periodic_hook, phook);
		if (!ptask) {
			log_err(errno, __func__, msg_err_malloc);
			return;
		}
#ifdef WIN32
		ptask->wt_aux2 = win_pid;
#endif
		/* Set a timed task for next occurance of this hook */
		(void)set_task(WORK_Timed, time_now + phook->freq,
			run_periodic_hook, phook);
	}
#ifndef WIN32
	else {
		/* Close all server connections */
		net_close(-1);
		rpp_terminate();
		/* Unprotect child from being killed by kernel */
		daemon_protect(0, PBS_DAEMON_PROTECT_OFF);

		/* set vnodes and reservation list to hook input parameter */
		req_ptr.vns_list = (pbs_list_head *)get_vnode_list();
		req_ptr.resv_list = (pbs_list_head *)get_resv_list();

		ret = server_process_hooks(PBS_BATCH_HookPeriodic, NULL, NULL, phook,
					HOOK_EVENT_PERIODIC, NULL, &req_ptr, hook_msg,
					sizeof(hook_msg), pbs_python_set_interrupt, &num_run, &event_initialized);
		if (ret == 0)
			log_event(PBSE_HOOKERROR, PBS_EVENTCLASS_HOOK, LOG_ERR, __func__, hook_msg);

#if defined(DEBUG)
		/* for valgrind, clear some stuff up */
		{
			hook *phook = (hook *) GET_NEXT(svr_allhooks);
			while (phook) {
				hook *tmp;
				free(phook->hook_name);
				if (phook->script) {
					struct python_script *scr = phook->script;
					free(scr->path);
					free(scr->py_code_obj);
				}
				free(phook->script);
				tmp = phook;
				phook = (hook *) GET_NEXT(phook->hi_allhooks);
				free(tmp);
			}
		}
#endif

		exit(ret);
	}
#endif
	return;
}
