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

/**
 * @file    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
 * mk_deferred_hook_info
 * post_sendhookTPP
 * check_add_hook_mcast_info
 * del_deferred_hook_cmds
 * sync_mom_hookfilesTPP
 * mc_sync_mom_hookfiles
 * add_pending_mom_allhooks_action
 * next_sync_mom_hookfiles
 * mark_mom_hooks_seen
 * mom_hooks_seen_count
 * uc_delete_mom_hooks
 * get_hook_rescdef_checksum
 */

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

#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#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 "tpp.h"
#include "reservation.h"
#include "cmds.h"
#include "server.h"
#include "pbs_sched.h"
#include "dis.h"
#include "acct.h"

/* External functions */
extern void disable_svr_prov();
extern void set_srv_prov_attributes();
extern int should_retry_route(int);

/* Local Private Functions */

/* Global Data items */
int do_sync_mom_hookfiles = 1;
int sync_mom_hookfiles_replies_pending = 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 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 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_postqueuejob_hooks;
extern pbs_list_head svr_modifyjob_hooks;
extern pbs_list_head svr_resvsub_hooks;
extern pbs_list_head svr_modifyresv_hooks;
extern pbs_list_head svr_movejob_hooks;
extern pbs_list_head svr_runjob_hooks;
extern pbs_list_head svr_jobobit_hooks;
extern pbs_list_head svr_management_hooks;
extern pbs_list_head svr_modifyvnode_hooks;
extern pbs_list_head svr_periodic_hooks;
extern pbs_list_head svr_provision_hooks;
extern pbs_list_head svr_resv_confirm_hooks;
extern pbs_list_head svr_resv_begin_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 pbs_list_head svr_execjob_abort_hooks;
extern pbs_list_head svr_execjob_postsuspend_hooks;
extern pbs_list_head svr_execjob_preresume_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 mc_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_hookfilesTPP and mc_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];
	mom_hook_action_t *hook_act;

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

	if (lock_file(fileno(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 < ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_num_action; j++) {

			if ((minfo == NULL) || (k == j)) {
				hook_act = ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_action[j];
				if (hook_act) {
					fprintf(fp, "%s:%d %s %d %lld\n", minfo_array[i]->mi_host,
						minfo_array[i]->mi_port,
						hook_act->hookname,
						hook_act->action,
						hook_act->tid);
				}
			}
		}
	}
	fflush(fp);

	if (lock_file(fileno(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_hookfilesTPP() 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(fileno(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 < ((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_num_action; j++) {
			if (((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_action[j] != NULL) {
				free(((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_action[j]);
				((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_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 */
		hook_tid = atoll(p2);
		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) {
			add_mom_hook_action(&((mom_svrinfo_t *) minfo->mi_data)->msr_action,
					    &((mom_svrinfo_t *) minfo->mi_data)->msr_num_action,
					    hook_name, action, 1, hook_tid);
		}
	}

	hook_action_tid_set(max_tid_recov);

	if (fp != NULL) {
		if (lock_file(fileno(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) {
		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->pending_delete) {
		snprintf(hook_msg, sizeof(hook_msg), "%s is pending delete!",
			 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)
		delete_task_by_parm1_func(phook, NULL, 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_func(phook, NULL, DELETE_ALL);
			(void) set_task(WORK_Timed, time_now + phook->freq, run_periodic_hook, phook);
		}
	}

	add_to_svrattrl_list(&preq->rq_ind.rq_manager.rq_attr, OUTPUT_FILE_PARAM,
			     NULL, output_path, 0, NULL);
	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_func(phook, NULL, DELETE_ALL);
					if (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_func(phook, NULL, 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 ? plx->al_name : "", plx ? plx->al_op : -1);
				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 ((phook->enabled == TRUE) && (phook->freq > 0)) {
			/* Delete all existing hook related timed work tasks */
			delete_task_by_parm1_func(phook, run_periodic_hook, DELETE_ALL);
			if (phook->script != NULL)
				(void) set_task(WORK_Timed, time_now + phook->freq,
						run_periodic_hook, phook);
		}
	}

	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);
	preq->rq_reply.brp_count++;

	/* 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);
	preply->brp_count = 0;

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

	free_jattr(pjob, JOB_ATR_exectime);

	rc = set_jattr_str_slim(pjob, JOB_ATR_exectime, new_exec_time_str, NULL);

	if (rc == 0) {
		if (job_attr_def[(int) JOB_ATR_exectime].at_action) {
			rc = job_attr_def[(int) JOB_ATR_exectime].at_action(
				get_jattr(pjob, 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);
		free_jattr(pjob, JOB_ATR_exectime);
	} else {
		int newsub;
		char newstate;
		FILE *fp_debug_out = NULL;

		snprintf(msg, msg_len, "'%s' hook set job's %s = %s", hook_name, ATTR_a, exec_time_ctime);
		svr_evaljobstate(pjob, &newstate, &newsub, 0);
		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;
	char 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 = get_jattr_long(pjob, JOB_ATR_hold);

	rc = set_jattr_str_slim(pjob, JOB_ATR_hold, new_hold_types_str, NULL);

	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));
		free_jattr(pjob, 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 &&
	    (get_jattr_long(pjob, JOB_ATR_hold) != 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);
		set_jattr_str_slim(pjob, JOB_ATR_Comment, buf, NULL);
	}

	if (old_hold != get_jattr_long(pjob, JOB_ATR_hold)) {
		FILE *fp_debug_out = NULL;
		/* indicate attributes changed */
		svr_evaljobstate(pjob, &newstate, &newsub, 0);
		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;
	}

	free_jattr(pjob, attr_index);

	rc = set_jattr_str_slim(pjob, attr_index, new_attrval_str, NULL);
	if (rc == 0) {
		if (job_attr_def[attr_index].at_action) {
			rc = job_attr_def[attr_index].at_action(
				get_jattr(pjob, 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);
			}
		}
		snprintf(log_buffer, sizeof(log_buffer), "%s=%s", attr_name, new_attrval_str);
		account_record(PBS_ACCT_ALTER, pjob, 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);
		free_jattr(pjob, 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 (is_jattr_set(pjob, JOB_ATR_variables)) {

		/* transform raw Variable_List data into a string */
		/* of the form "<var1>=<val1>,<var2>=<val2>,..." with */
		/* special characters escaped with a backslash. */
		astr = get_jattr_arst(pjob, JOB_ATR_variables);
		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 = get_jattr(pjob, 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);
		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);

		snprintf(log_buffer, sizeof(log_buffer), "%s.%s=%s", ATTR_l, resc, new_rescval_str);
		account_record(PBS_ACCT_ALTER, pjob, log_buffer);

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

	pseldef = &svr_resc_def[RESC_SELECT];
	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 job attributes
 *		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 (is_attr_set(&a_map[index].attr_val))
			free_attr(job_attr_def, &a_map[index].attr_val, a_index);

		clear_attr(&a_map[index].attr_val, &job_attr_def[a_index]);
		if (is_jattr_set(pjob, a_index)) {
			job_attr_def[a_index].at_set(
				&a_map[index].attr_val,
				get_jattr(pjob, 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 (is_attr_set(&a_map[index].attr_val))
			free_attr(job_attr_def, &a_map[index].attr_val, a_index);
		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;
	char 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 = get_jattr(pjob, 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 (is_attr_set(pattr_o)) {

			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);
			}
		} else if (is_attr_set(pattr)) {
			/* 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);
			}
		}
	}

	svr_evaljobstate(pjob, &newstate, &newsub, 0);
	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}},
	{JOB_ATR_create_resv_from_job, {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_create_resv_from_job) == 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);
			}
		}
	}

	/* update the eligible time to JOB_INITIAL */
	if (is_sattr_set(SVR_ATR_EligibleTimeEnable) && get_sattr_long(SVR_ATR_EligibleTimeEnable) == 1)
		update_eligible_time(JOB_INITIAL, pjob);

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

/*
 * the following is a list of attributes modifiable in a HOOK_EVENT_POSTQUEUEJOB
 * hook that ends in a pbs.event().accept().
 */
struct attribute_jobmap postqueuejob_accept_attrlist[] = {
	{JOB_ATR_hold, {0}},
	{JOB_ATR_project, {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 POSTQUEUEJOB hook
 *		execution ending in a pbs.event().accept() call.
 *
 * @see
 * 		process_hooks
 * @param[in]	pjob	- job to modify.
 * @param[in]	hook_name - name of hook doing the accept action.
 *
 * @return int
 * @retval 0	- success
 * @retval 1	- fail
 */
static int
do_postqueuejob_accept_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, postqueuejob_accept_attrlist);

	for (index = 0; (aindex = (int) postqueuejob_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;
		}

		log_buffer[0] = '\0';
		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);
			}
		}
	}
	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(get_nattr(pnode, 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(get_rattr(presv, 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;
	conn_t *conn = NULL;
	char *hostname = NULL;

	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_PostQueueJob) {
		hook_event = HOOK_EVENT_POSTQUEUEJOB;
		req_ptr.rq_postqueuejob = (struct rq_postqueuejob *) &preq->rq_ind.rq_postqueuejob;
		head_ptr = &svr_postqueuejob_hooks;
		jobid = ((struct rq_postqueuejob *) (req_ptr.rq_postqueuejob))->rq_jid;
		t = is_job_array(jobid);
		if ((t == IS_ARRAY_Single) || (t == IS_ARRAY_NO)) {
			pjob = find_job(jobid);
		}
	} 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_ModifyResv) {
		hook_event = HOOK_EVENT_MODIFYRESV;
		req_ptr.rq_manage = (struct rq_quejob *) &preq->rq_ind.rq_modify;
		head_ptr = &svr_modifyresv_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 ||
		   preq->rq_type == PBS_BATCH_AsyrunJob_ack) {
		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_Single) || (t == IS_ARRAY_NO)) {
			pjob = find_job(jobid); /* regular job and single subjob */
		}

		/* an array job or range of subjobs 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_JobObit) {
		hook_event = HOOK_EVENT_JOBOBIT;
		req_ptr.rq_obit = (struct rq_jobobit *) &preq->rq_ind.rq_obit;
		head_ptr = &svr_jobobit_hooks;
	} else if (preq->rq_type == PBS_BATCH_Manager) {
		hook_event = HOOK_EVENT_MANAGEMENT;
		preq->rq_ind.rq_management.rq_reply = &preq->rq_reply;
		preq->rq_ind.rq_management.rq_time = preq->rq_time;
		/* Copying the pointer to rq_management below is safe since
		req_manager() bumps the reference count on preq */
		req_ptr.rq_manage = (struct rq_manage *) &preq->rq_ind.rq_management;
		head_ptr = &svr_management_hooks;
	} else if (preq->rq_type == PBS_BATCH_ModifyVnode) {
		hook_event = HOOK_EVENT_MODIFYVNODE;
		req_ptr.rq_modifyvnode = (struct rq_modifyvnode *) &preq->rq_ind.rq_modifyvnode;
		head_ptr = &svr_modifyvnode_hooks;
	} 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 if (preq->rq_type == PBS_BATCH_BeginResv) {
		hook_event = HOOK_EVENT_RESV_BEGIN;
		req_ptr.rq_manage = (struct rq_manage *) &preq->rq_ind.rq_resresvbegin;
		head_ptr = &svr_resv_begin_hooks;
	} else if (preq->rq_type == PBS_BATCH_ConfirmResv) {
		hook_event = HOOK_EVENT_RESV_CONFIRM;
		req_ptr.rq_run = (struct rq_runjob *) &preq->rq_ind.rq_run;
		head_ptr = &svr_resv_confirm_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_PostQueueJob) {
			phook_next = (hook *) GET_NEXT(phook->hi_postqueuejob_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_ModifyResv) {
			phook_next = (hook *) GET_NEXT(phook->hi_modifyresv_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 ||
			   preq->rq_type == PBS_BATCH_AsyrunJob_ack) {
			phook_next = (hook *) GET_NEXT(phook->hi_runjob_hooks);
		} else if (preq->rq_type == PBS_BATCH_JobObit) {
			phook_next = (hook *) GET_NEXT(phook->hi_jobobit_hooks);
		} else if (preq->rq_type == PBS_BATCH_Manager) {
			phook_next = (hook *) GET_NEXT(phook->hi_management_hooks);
		} else if (preq->rq_type == PBS_BATCH_ModifyVnode) {
			phook_next = (hook *) GET_NEXT(phook->hi_modifyvnode_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_ConfirmResv) {
			phook_next = (hook *) GET_NEXT(phook->hi_resv_confirm_hooks);
		} else if (preq->rq_type == PBS_BATCH_BeginResv) {
			phook_next = (hook *) GET_NEXT(phook->hi_resv_begin_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;
		}
		if (preq->rq_conn >= 0 && (conn = get_conn(preq->rq_conn)) != NULL) {
			hostname = conn->cn_physhost;
		} else {
			hostname = preq->rq_host;
		}
		rc = server_process_hooks(preq->rq_type, preq->rq_user, hostname, phook,
					  hook_event, pjob, &req_ptr, hook_msg, msg_len, pyinter_func,
					  &num_run, &event_initialized);
		pbs_python_ext_free_global_dict(phook->script);
		if ((rc == 0) || (rc == -1)) {
			pbs_python_clear_attributes();
			return (rc);
		}
	}

	if (num_run == 0)
		return (2);
	/* clear attributes for the requests which don't call recreate_request */
	if ((preq->rq_type != PBS_BATCH_SubmitResv) && (preq->rq_type != PBS_BATCH_ModifyJob) &&
			(preq->rq_type != PBS_BATCH_QueueJob) && (preq->rq_type != PBS_BATCH_MoveJob) &&
			(preq->rq_type != PBS_BATCH_ModifyResv)) {
				pbs_python_clear_attributes();
	}
	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]	hook_event  - hook event type
 * @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;
	char perf_label[MAXBUFLEN];

	if (phook == NULL) {
		log_event(PBSEVENT_DEBUG3,
			  PBS_EVENTCLASS_HOOK, LOG_ERR,
			  __func__, "no associated hook");
		return -1;
	}

	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 (pjob != NULL)
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%s", hook_event_as_string(hook_event), phook->hook_name, pjob->ji_qs.ji_jobid);
	else
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", hook_event_as_string(hook_event), phook->hook_name, mypid);

	hook_perf_stat_start(perf_label, "server_process_hooks", 1);

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

		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 = alloc_br(rq_type);
		if (temp_req != NULL) {
			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_PostQueueJob:
					CLEAR_HEAD(temp_req->rq_ind.rq_postqueuejob.rq_attr);
					do_recreate = 1;
					break;
				case PBS_BATCH_ModifyJob:
					CLEAR_HEAD(temp_req->rq_ind.rq_modify.rq_attr);
					do_recreate = 1;
					break;
				case PBS_BATCH_Manager:
					CLEAR_HEAD(temp_req->rq_ind.rq_manager.rq_attr);
					do_recreate = 0;
					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("");
		}
		rc = -1;
		goto server_process_hooks_exit;
	}

	/*
	 * 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("");
		}
		rc = -1;
		goto server_process_hooks_exit;
	}

	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("");
			}
			rc = -1;
			goto server_process_hooks_exit;
		}
	}

	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 || rq_type == PBS_BATCH_AsyrunJob_ack) {
		/* 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("");
			}
			rc = -1;
			goto server_process_hooks_exit;
		}
	}

	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 == '@') && is_sattr_set(SVR_ATR_dflt_que))
				fprintf(fp_debug, "%s.queue=%s\n", EVENT_JOB_OBJECT, get_sattr_str(SVR_ATR_dflt_que));
			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) {
		hook_perf_stat_start(perf_label, "run_code", 0);
		rc = pbs_python_run_code_in_namespace(&svr_interp_data, phook->script, 0);
		hook_perf_stat_stop(perf_label, "run_code", 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 */
				rc = -1;
				goto server_process_hooks_exit;
			}
		} 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("");
			}
			rc = -1;
			goto server_process_hooks_exit;
		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);
			rc = 0;
			goto server_process_hooks_exit;
		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);
			rc = 0;
			goto server_process_hooks_exit;
	}
	*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 || rq_type == PBS_BATCH_AsyrunJob_ack) {
			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);

			if (rq_type == PBS_BATCH_AsyrunJob) {
				char *jcomment = NULL;

				pbs_asprintf(&jcomment, "Not Running: PBS Error: %s", hook_msg);
				/* For async run, sched won't update job's comment, so let's do that */
				set_jattr_str_slim(pjob, JOB_ATR_Comment, jcomment, NULL);
				free(jcomment);
			}
		}

		pbs_python_do_vnode_set();
		write_hook_reject_debug_output_and_close(emsg);
		rc = 0;
		goto server_process_hooks_exit;
	} else { /* hook request has been accepted */

		if (rq_type == PBS_BATCH_PostQueueJob) {

			hook_msg[0] = '\0';
			if (do_postqueuejob_accept_actions(pjob, phook->hook_name) != 0) {
				log_eventf(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name,
					   "postqueuejob request rejected: %s", hook_msg);
				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, postqueuejob_accept_attrlist);
				write_hook_reject_debug_output_and_close(hook_msg);
				rc = 0;
				goto server_process_hooks_exit;
			}
		} else if (rq_type == PBS_BATCH_RunJob || rq_type == PBS_BATCH_AsyrunJob || rq_type == PBS_BATCH_AsyrunJob_ack) {
			char *new_exec_time_str = NULL;
			char *new_hold_types_str = NULL;
			char *new_project_str = NULL;
			char *new_depend_str = NULL;
			char *new_conv_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);
				}
			}

			if (job_modified != 1) {
				new_conv_str =
					pbs_python_event_job_getval_hookset(
						ATTR_create_resv_from_job, NULL, 0, NULL, 0);

				if (new_conv_str != NULL)
					log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_ERR, phook->hook_name,
						  "Found job " ATTR_create_resv_from_job " attribute flagged to be set");
			}

			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);
				rc = 0;
				goto server_process_hooks_exit;
			}

			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);
				rc = 0;
				goto server_process_hooks_exit;
			}
		}

		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, perf_label, HOOK_PERF_HOOK_OUTPUT);
			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);
			rc = 1;
			goto server_process_hooks_exit;
		}
	}

	write_hook_accept_debug_output_and_close();
	rc = 1;
server_process_hooks_exit:
	hook_perf_stat_stop(perf_label, "server_process_hooks", 1);
	return (rc);
}

/**
 * @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;
	char perf_label[MAXBUFLEN];

	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;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_QUEUEJOB, preq->rq_ind.rq_queuejob.rq_jid, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_QUEUEJOB,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} else if (preq->rq_type == PBS_BATCH_PostQueueJob) {
		req_params.rq_postqueuejob = (struct rq_postqueuejob *) &preq->rq_ind.rq_postqueuejob;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_QUEUEJOB, preq->rq_ind.rq_postqueuejob.rq_jid, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_POSTQUEUEJOB,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} else if (preq->rq_type == PBS_BATCH_SubmitResv) {
		req_params.rq_job = (struct rq_quejob *) &preq->rq_ind.rq_queuejob;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_RESVSUB, preq->rq_ind.rq_queuejob.rq_jid, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_RESVSUB,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} else if (preq->rq_type == PBS_BATCH_ModifyResv) {
		req_params.rq_manage = (struct manage *) &preq->rq_ind.rq_modify;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_MODIFYRESV, preq->rq_ind.rq_modify.rq_objname, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_MODIFYRESV,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} else if (preq->rq_type == PBS_BATCH_ModifyJob) {
		req_params.rq_manage = (struct manage *) &preq->rq_ind.rq_modify;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_MODIFYJOB, preq->rq_ind.rq_modify.rq_objname, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_MODIFYJOB,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} else if (preq->rq_type == PBS_BATCH_MoveJob) {
		req_params.rq_move = (struct rq_move *) &preq->rq_ind.rq_move;
		snprintf(perf_label, sizeof(perf_label), "hook_%s_%s_%d", HOOKSTR_MOVEJOB, preq->rq_ind.rq_move.rq_jid, getpid());
		rc = pbs_python_event_to_request(HOOK_EVENT_MOVEJOB,
						 &req_params, perf_label, HOOK_PERF_HOOK_OUTPUT);
	} 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("");
	}
	/* clear python cached objects */
	pbs_python_clear_attributes();
	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 ((pact->action & action & pact->reply_expected)) {
					continue; /* dont reuse the action object if replies are still expected for same 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->reply_expected = 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_hookfilesTPP() 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)
			continue;

		if (!minfo_array[i]->mi_data ||
		    (minfo_array[i]->mi_dmn_info->dmn_state & (INUSE_UNKNOWN | INUSE_NEEDS_HELLOSVR))) {
			continue;
		}

		j = add_mom_hook_action(&((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_action,
					&((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_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(((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_action,
					   ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_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)
			continue;

		pact = find_mom_hook_action(((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_action,
					    ((mom_svrinfo_t *) mominfo_array[i]->mi_data)->msr_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 < ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_num_action; j++) {
			pact = ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_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_sendhookTPP and next_sync_mom_hookfiles.
 */
static void
collapse_hook_tr()
{
	hook *phook;
	hook *phook_current;

	/* 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
 *		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
 *		check if there is any new pending action
 *
 * @param[in] minfo - pointer to mom info
 * @param[in] pact - pointer to current hook action
 * @param[in] j - index of pact in minfo->mi_data->msr_action[]
 * @param[in] event - action event to consider
 *
 * @return int
 * @retval 0  - not found
 * @retval 1  - found
 */
int
check_for_latest_action(mominfo_t *minfo, mom_hook_action_t *pact, int j, int event)
{
	/* if the same action is marked in the next actions for the same hook then remove
	 * the pending flag
	 */
	int i;
	mom_hook_action_t *pact2;
	for (i = 0; i < ((mom_svrinfo_t *) minfo->mi_data)->msr_num_action; i++) {
		pact2 = ((mom_svrinfo_t *) minfo->mi_data)->msr_action[i];
		if (pact2 && (i != j) && (pact2->tid > pact->tid) && (pact2->action & event) &&
		    pact2->hookname && (strcmp(pact2->hookname, pact->hookname) == 0)) {
			return 1;
		}
	}
	return 0;
}

/**
 * @brief
 *		Call back for the hook deferred requests over TPP stream
 *		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_replies_pending is reset to 0, such
 *		that the next "hook transaction" can now start.
 *
 * @param[in] pwt - The work task pointer
 *
 * @return void
 */
void
post_sendhookTPP(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;
	bool failed_flag = FALSE;

	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),
			 "%s reply (tid=%lld) not from current "
			 "batch of hook updates (tid=%lld) from mhost=%s",
			 __func__, tid, g_sync_hook_tid, minfo->mi_host ? minfo->mi_host : "");
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER, LOG_INFO, __func__, log_buffer);
		return; /* return now as info->index no longer valid */
	}

	pact = ((mom_svrinfo_t *) minfo->mi_data)->msr_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);
			failed_flag = TRUE;
		} 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);
			failed_flag = TRUE;
		} 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);
			failed_flag = TRUE;
		} 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);
			failed_flag = TRUE;
		} 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);
			failed_flag = TRUE;
		} 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);
			failed_flag = TRUE;
		} 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);
		}
	}

	pact->reply_expected &= ~(event);
	if (failed_flag && check_for_latest_action(minfo, pact, j, event)) {
		pact->action &= ~(event);
		hook_track_save(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_replies_pending = 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 TPP 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) {
			free(info);
			return NULL;
		}

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

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

	/* 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_sendhookTPP,
				  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, FALSE) != 0) {
		free(info);
		return NULL;
	}

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

SUCCESS_RET:
	((mom_svrinfo_t *) minfo->mi_data)->msr_action[act_index]->reply_expected |= action;

	g_hook_replies_expected++;

	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_hookfilesTPP
 *
 * @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(pmom->mi_dmn_info->dmn_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 = ((mom_svrinfo_t *) minfo->mi_data)->msr_action[j];

				pact->action &= ~(event);
				pact->reply_expected &= ~(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 deferred requests on TPP stream)
 *
 * @see
 * 		mc_sync_mom_hookfiles and uc_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_hookfilesTPP(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_replies_pending = 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 = minfo_array[i]->mi_dmn_info->dmn_stream;
		if (conn == -1) {
			skipped++;
			continue;
		}

		if (minfo_array[i]->mi_dmn_info->dmn_state & INUSE_DOWN) {
			skipped++;
			continue;
		}

		tpp_add_close_func(conn, process_DreplyTPP); /* register a close handler */

		pbs_errno = 0;
		for (j = 0; j < ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_num_action; j++) {
			hook *phook;
			pact = ((mom_svrinfo_t *) minfo_array[i]->mi_data)->msr_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, PROT_TPP, &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, PROT_TPP, &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_replies_pending = 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 mc_sync_mom_hookfiles() return values.
	 */
	return (ret);
}

/**
 * @brief
 *	Multi cast to moms all the pending mom hook sync operations
 *
 * @return int
 * @retval 0	for successfully executing the task process to sync_mom_hookfilesTPP()
 * @retval != 0 if an error occurred.
 */
int
mc_sync_mom_hookfiles(void)
{
	int rc;

	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);
	rc = sync_mom_hookfilesTPP(NULL);
	/* transaction id to use for next batch of updates */
	hook_action_tid_set(hook_action_tid_get() + 1);
	return rc;
}

/**
 * @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_allhooks);
	while (phook) {
		if (phook->hook_name && (phook->event & MOM_EVENTS)) {
			add_pending_mom_hook_action((mominfo_t *) minfo, phook->hook_name, action);
		}
		phook = (hook *) GET_NEXT(phook->hi_allhooks);
	}
}

/**
 * @brief
 *		clears out reply_expected flags of timed out actions
 *		and delete their wait tasks
 *
 * @see
 * 		handle_hook_sync_timeout
 *
 * @param[in]	tid	- transaction id of timedout hook sync sequence
 *
 * @return void
 */
void
clear_timed_out_reply_expected(long long int tid)
{
	int i, j;
	mom_hook_action_t *pact;
	struct work_task *ptask, *tmp_task;
	mominfo_t *pmom;
	struct def_hk_cmd_info *info;

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

		if ((pmom = mominfo_array[i]) == NULL)
			continue;

		if (((mom_svrinfo_t *) pmom->mi_data)->msr_num_action && ((mom_svrinfo_t *) pmom->mi_data)->msr_action) {

			for (j = 0; j < ((mom_svrinfo_t *) pmom->mi_data)->msr_num_action; j++) {
				pact = ((mom_svrinfo_t *) pmom->mi_data)->msr_action[j];
				if (pact && pact->reply_expected && (pact->tid == tid)) {
					log_eventf(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
						   LOG_INFO, __func__, "timedout, clearing reply_expected for %d event[%lld] of %s hook for %s",
						   pact->reply_expected, tid, pact->hookname, pmom->mi_host);
					/* get the task list */
					ptask = (struct work_task *) GET_NEXT(pmom->mi_dmn_info->dmn_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) &&
						    (pmom == tmp_task->wt_parm1)) {

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

							if (!info || (j != info->index) || !(pact->reply_expected & info->event)) {
								log_eventf(PBSEVENT_DEBUG3, PBS_EVENTCLASS_SERVER,
									   LOG_INFO, __func__, "timedout, skipped deleting pending WORK_Deferred_cmd for %s:%s",
									   pact->hookname, pmom->mi_host);
								continue;
							}

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

							if (check_for_latest_action(pmom, pact, j, info->event))
								pact->action &= ~(info->event);

							free(info);

							delete_task(tmp_task);
						}
					}
					pact->reply_expected = 0U;
					pact->tid = hook_action_tid_get();
					hook_track_save(pmom, j);
				}
			}
		}
	}
}

/**
 * @brief
 *		checks for hook sync operation's timeout
 *		and handles timeout activities if so
 *
 * @see
 * 		next_sync_mom_hookfiles
 *
 * @return int
 * @retval 0	if no timeout has occured
 * @retval != 0 if a timeout occurred.
 */
int
handle_hook_sync_timeout(void)
{
	unsigned long timeout_sec;
	time_t timeout_time;
	time_t current_time;
	timeout_sec = SYNC_MOM_HOOKFILES_TIMEOUT_TPP;
	if (is_sattr_set(SVR_ATR_sync_mom_hookfiles_timeout))
		timeout_sec = get_sattr_long(SVR_ATR_sync_mom_hookfiles_timeout);
	current_time = time(NULL);
	timeout_time = g_sync_hook_time + timeout_sec;
	if (sync_mom_hookfiles_replies_pending) {
		if (current_time <= timeout_time) {
			/* previous updates still in progress and not timed out */
			return 0;
		}

		/* we're timing out previous sync mom hook files process/action */
		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);

		clear_timed_out_reply_expected(g_sync_hook_tid);
		g_hook_replies_recvd = 0;
		g_hook_replies_expected = 0;
		/* attempt collapsing  the hook tracking file */
		collapse_hook_tr();
		sync_mom_hookfiles_replies_pending = 0;
		return 1;
	}

	return 0;
}

/**
 * @brief
 *		Checks to see if it's time to run mc_sync_mom_hookfiles() and if so,
 *		then run mc_sync_mom_hookfiles().
 *
 * @see
 * 		next_task
 *
 * @return	void
 */
void
next_sync_mom_hookfiles(void)
{
	int timed_out = handle_hook_sync_timeout();

	if ((do_sync_mom_hookfiles || timed_out) && !sync_mom_hookfiles_replies_pending && mc_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
 *		unicast delete mom hook requests and delete rescdef request
 *		to the 'mom' represented by 'minfo' data.
 *		do not care on request failures
 *
 * @see
 * 		delete_svrmom_entry
 *
 * @param[in]	minfo	- data reprsenting the 'mom'.
 *
 * @return void
 */
void
uc_delete_mom_hooks(void *minfo)
{
	/*
	 * unicast delete hook batch request
	 */
	hook *phook;
	char hookfile[MAXPATHLEN + 1];
	mominfo_t *mom_info = (mominfo_t *) minfo;
	char *msgid = NULL;

	phook = (hook *) GET_NEXT(svr_allhooks);
	while (phook) {
		if (phook->hook_name &&
		    (phook->event & MOM_EVENTS)) {
			msgid = NULL;
			snprintf(hookfile, sizeof(hookfile), "%s%s", phook->hook_name, HOOK_FILE_SUFFIX);
			PBSD_delhookfile(mom_info->mi_dmn_info->dmn_stream, hookfile, PROT_TPP, &msgid);
			free(msgid);
			msgid = NULL;
		}
		phook = (hook *) GET_NEXT(phook->hi_allhooks);
	}
	PBSD_delhookfile(mom_info->mi_dmn_info->dmn_stream, PBS_RESCDEF, PROT_TPP, &msgid);
	free(msgid);
	return;
}

/**
 * @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;
	char *pbse_err;
	char raw_err[10];

	/* 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,
			get_jattr_str(pjob, JOB_ATR_euser),
			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 != NULL) && (*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, vname_str, "node_name not found");
				} else if ((pnode->nd_state & INUSE_DELETED) == 0) {

					rc = mgr_set_attr(pnode->nd_attr, node_attr_idx, 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 {
						mgr_log_attr(msg_man_set, plist,
							     PBS_EVENTCLASS_NODE, pnode->nd_name, NULL);
						pnode->nd_modified = 1;
					}
				}
				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';
	}

	save_nodes_db(0, NULL);

	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;
	mypid = ptask->wt_event;

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

	pid = fork();

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

	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;
		}
		/* Set a timed task for next occurance of this hook */
		(void) set_task(WORK_Timed, time_now + phook->freq,
				run_periodic_hook, phook);
	} else {
		/* Close all server connections */
		net_close(-1);
		tpp_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);
	}
	return;
}
