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

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

#ifndef WIN32
#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <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 "log.h"
#include "server_limits.h"
#include "attribute.h"
#include "credential.h"
#include "batch_request.h"
#include "job.h"
#include "svrfunc.h"
#include "pbs_nodes.h"
#include <pbs_python.h>  /* for python interpreter */
#include "hook.h"
#include "rpp.h"
#include <signal.h>
#include "hook_func.h"


/**
 * @file	hook.c
 */
/* External functions */

/* Local Private Functions */

/* Global Data items */
static  void    (*python_interrupt_func)(void) = NULL;

extern char *path_priv;
extern char *path_hooks_workdir;

extern	char	*path_hooks;
extern	time_t	time_now;

extern pbs_list_head svr_allhooks;
extern pbs_list_head svr_queuejob_hooks;
extern pbs_list_head svr_modifyjob_hooks;
extern pbs_list_head svr_resvsub_hooks;
extern pbs_list_head svr_movejob_hooks;
extern pbs_list_head svr_runjob_hooks;
extern pbs_list_head svr_provision_hooks;
extern pbs_list_head svr_periodic_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_end_hooks;
extern pbs_list_head svr_execjob_preterm_hooks;
extern pbs_list_head svr_execjob_launch_hooks;
extern pbs_list_head svr_exechost_periodic_hooks;
extern pbs_list_head svr_exechost_startup_hooks;
extern pbs_list_head svr_execjob_attach_hooks;
extern pbs_list_head svr_execjob_resize_hooks;

/**
 *
 * @brief
 *	Removes 'phook' from all the hooks lists known to the system.
 *
 * @param[in]	phook - the hook to remove.
 *
 */
void
clear_hook_links(hook *phook)
{
	delete_link(&phook->hi_queuejob_hooks);
	delete_link(&phook->hi_modifyjob_hooks);
	delete_link(&phook->hi_resvsub_hooks);
	delete_link(&phook->hi_movejob_hooks);
	delete_link(&phook->hi_runjob_hooks);
	delete_link(&phook->hi_provision_hooks);
	delete_link(&phook->hi_periodic_hooks);
	delete_link(&phook->hi_resv_end_hooks);
	delete_link(&phook->hi_allhooks);

	/* mom hooks below */
	delete_link(&phook->hi_execjob_begin_hooks);
	delete_link(&phook->hi_execjob_prologue_hooks);
	delete_link(&phook->hi_execjob_epilogue_hooks);
	delete_link(&phook->hi_execjob_preterm_hooks);
	delete_link(&phook->hi_execjob_launch_hooks);
	delete_link(&phook->hi_execjob_end_hooks);
	delete_link(&phook->hi_exechost_periodic_hooks);
	delete_link(&phook->hi_exechost_startup_hooks);
	delete_link(&phook->hi_execjob_attach_hooks);
	delete_link(&phook->hi_execjob_resize_hooks);
}

/**
 *
 * @brief
 *	Return a comma separated set of strings giving
 *	the names of event bits turned on in 'event'.
 *
 * @param[in]	event - input event.
 *
 * @return char *
 * @retval <string>	comma-separated event names.
 *
 * @note
 *	This returns a static string that will get overwritten on the
 *	next call to this function.
 */
char *
hook_event_as_string(unsigned int event)
{
	static char eventstr[HOOK_BUF_SIZE];
	int	ev_ct = 0;

	eventstr[0] = '\0';
	if (event & HOOK_EVENT_QUEUEJOB) {
		snprintf(eventstr, sizeof(eventstr), HOOKSTR_QUEUEJOB);
		ev_ct++;
	}

	if (event & HOOK_EVENT_MODIFYJOB) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_MODIFYJOB, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_RESVSUB) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_RESVSUB, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_MOVEJOB) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_MOVEJOB, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_RUNJOB) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_RUNJOB, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_PERIODIC) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_PERIODIC, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_PROVISION) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_PROVISION, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_RESV_END) {
		if (ev_ct > 0)
	        	strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_RESV_END, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_BEGIN) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_BEGIN, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_PROLOGUE) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_PROLOGUE, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_EPILOGUE) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_EPILOGUE, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_END) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_END, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_PRETERM) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_PRETERM, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_LAUNCH) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_LAUNCH, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_ATTACH) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_ATTACH, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECJOB_RESIZE) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_RESIZE, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECHOST_PERIODIC) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECHOST_PERIODIC, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_EXECHOST_STARTUP) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECHOST_STARTUP, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (ev_ct == 0)
		snprintf(eventstr, sizeof(eventstr), HOOKSTR_NONE);
	return (eventstr);
}

/**
 * @brief
 *	Returns the internal value (in integer) of the given event string name.
 *
 * @param[in]	eventstr - an event string value (ex. "execjob_begin")
 *
 * @return int
 * @retval <n>	The internal,numeric value (ex. 5)
 */
unsigned int
hookstr_event_toint(char *eventstr)
{
	if (strcmp(eventstr, HOOKSTR_QUEUEJOB) == 0)
		return HOOK_EVENT_QUEUEJOB;
	if (strcmp(eventstr, HOOKSTR_MODIFYJOB) == 0)
		return HOOK_EVENT_MODIFYJOB;
	if (strcmp(eventstr, HOOKSTR_RESVSUB) == 0)
		return HOOK_EVENT_RESVSUB;
	if (strcmp(eventstr, HOOKSTR_MOVEJOB) == 0)
		return HOOK_EVENT_MOVEJOB;
	if (strcmp(eventstr, HOOKSTR_RUNJOB) == 0)
		return HOOK_EVENT_RUNJOB;
	if (strcmp(eventstr, HOOKSTR_PROVISION) == 0)
		return HOOK_EVENT_PROVISION;
	if (strcmp(eventstr, HOOKSTR_RESV_END) == 0)
		return HOOK_EVENT_RESV_END;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_BEGIN) == 0)
		return HOOK_EVENT_EXECJOB_BEGIN;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_PROLOGUE) == 0)
		return HOOK_EVENT_EXECJOB_PROLOGUE;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_EPILOGUE) == 0)
		return HOOK_EVENT_EXECJOB_EPILOGUE;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_END) == 0)
		return HOOK_EVENT_EXECJOB_END;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_PRETERM) == 0)
		return HOOK_EVENT_EXECJOB_PRETERM;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_LAUNCH) == 0)
		return HOOK_EVENT_EXECJOB_LAUNCH;
	if (strcmp(eventstr, HOOKSTR_EXECHOST_PERIODIC) == 0)
		return HOOK_EVENT_EXECHOST_PERIODIC;
	if (strcmp(eventstr, HOOKSTR_EXECHOST_STARTUP) == 0)
		return HOOK_EVENT_EXECHOST_STARTUP;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_ATTACH) == 0)
		return HOOK_EVENT_EXECJOB_ATTACH;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_RESIZE) == 0)
		return HOOK_EVENT_EXECJOB_RESIZE;

	return 0;
}

int
hookstr_type_toint(char *hookstr_type)
{
	if (strcmp(hookstr_type, HOOKSTR_SITE) == 0) {
		return (HOOK_SITE);
	}
	if (strcmp(hookstr_type, HOOKSTR_PBS) == 0) {
		return (HOOK_PBS);
	}
	return (-1);
}

/*
 *	Returns the string representation of hook 'type' value.
 */
char *
hook_type_as_string(hook_type type)
{
	switch (type) {

		case HOOK_SITE:
			return HOOKSTR_SITE;

		case HOOK_PBS:
			return HOOKSTR_PBS;

		default:
			return HOOKSTR_UNKNOWN;
	}
}

/*
 *	Returns the string representation of hook 'enabled' value.
 */
char *
hook_enabled_as_string(int enabled)
{
	if (enabled == TRUE)
		return HOOKSTR_TRUE;
	else
		return HOOKSTR_FALSE;
}

/*
 *	Returns the string representation of hook 'debug' value.
 */
char *
hook_debug_as_string(int debug)
{
	if (debug == TRUE)
		return HOOKSTR_TRUE;
	else
		return HOOKSTR_FALSE;
}

/**
 *
 * @brief
 *	Returns the string representation of hook 'user' value.
 *
 * @return char *
 * @reval  <string> - the string version of the internal hook 'user' value.
 */
char *
hook_user_as_string(hook_user user)
{
	switch (user) {

		case HOOK_PBSADMIN:
			return HOOKSTR_ADMIN;
		case HOOK_PBSUSER:
			return HOOKSTR_USER;

		default:
			return HOOKSTR_UNKNOWN;

	}
}

/**
 *
 * @brief
 *	Return a comma separated set of strings giving
 *	the names of fail_action bits turned on in 'fail_action'.
 *
 * @param[in]	fail_action - input fail_action.
 *
 * @return char *
 * @retval <string>	comma-separated fail_action names.
 *
 * @note
 *	This returns a static string that will get overwritten on the
 *	next call to this function.
 */
char *
hook_fail_action_as_string(unsigned int fail_action)
{
	static char fail_actionstr[HOOK_BUF_SIZE];
	char	*ptr = &fail_actionstr[0];
	int	ev_ct = 0;
	int	i;
	static struct {
		unsigned int	flags;
		char *			name;
	} array[] = {
		{ HOOK_FAIL_ACTION_NONE, HOOKSTR_FAIL_ACTION_NONE },
		{ HOOK_FAIL_ACTION_OFFLINE_VNODES, HOOKSTR_FAIL_ACTION_OFFLINE_VNODES },
		{ HOOK_FAIL_ACTION_CLEAR_VNODES, HOOKSTR_FAIL_ACTION_CLEAR_VNODES },
		{ HOOK_FAIL_ACTION_SCHEDULER_RESTART_CYCLE, HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE },
		{ 0, NULL }
	};

	*ptr = '\0';
	for (i=0; array[i].flags != 0; i++) {
		if (fail_action & array[i].flags) {
			size_t	namelen = strlen(array[i].name);
			size_t	actionlen = ptr - fail_actionstr;

			if (ev_ct > 0) {
				if (2 > sizeof(fail_actionstr) - actionlen)
					return NULL;	/* out of space */
				*ptr++ = ',';		/* replace nul */
				*ptr = '\0';		/* terminate string */
				actionlen++;
			}
			if (namelen + 1 > sizeof(fail_actionstr) - actionlen)
				return NULL; 		/* out of space */
			strncpy(ptr, array[i].name, sizeof(fail_actionstr) - actionlen);
			ptr += namelen;
			ev_ct++;
		}
	}

	if (ev_ct == 0)
		strcpy(fail_actionstr, HOOKSTR_FAIL_ACTION_NONE);
	return (fail_actionstr);
}

/*
 *	Returns the string representation of hook 'order' value.
 */
char *
hook_order_as_string(short order)
{
	static char order_str[HOOK_BUF_SIZE];

	sprintf(order_str, "%d", order);
	return (order_str);
}

/*
 *	Returns the string representation of hook 'alarm' value.
 */
char *
hook_alarm_as_string(int alarm)
{
	static char alarm_str[HOOK_BUF_SIZE];

	sprintf(alarm_str, "%d", alarm);
	return (alarm_str);
}

/**
 *
 * @brief
 *	Returns the string representation of hook 'freq' value.
 *
 * @return char *
 * @reval  <string> - the string version of the internal hook 'freq' value.
 */
char *
hook_freq_as_string(int freq)
{
	static char freq_str[HOOK_BUF_SIZE+1];

	snprintf(freq_str, HOOK_BUF_SIZE, "%d", freq);
	return (freq_str);
}

/*
 *	Sets the hook 'phook's name attribute to string 'newval'.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
set_hook_name(hook *phook, char *newval, char *msg, size_t msg_len)
{
	int		pbsprefix;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's name is NULL!", __func__);
		return (1);
	}

	pbsprefix = (strncmp(newval, HOOK_PBS_PREFIX,
		strlen(HOOK_PBS_PREFIX)) == 0);
	if ((phook->type == HOOK_PBS) && !pbsprefix) {
		snprintf(msg,  msg_len-1,
			"hook_name '%s', must use %s as a "
			"prefix since this is a %s hook.",
			newval, HOOK_PBS_PREFIX, HOOKSTR_PBS);
		return (1);
	} else if ((phook->type == HOOK_SITE) && pbsprefix) {
		snprintf(msg, msg_len-1,
			"hook_name '%s', cannot use %s as a "
			"prefix it is reserved for %s hooks",
			newval, HOOK_PBS_PREFIX, HOOKSTR_PBS);
		return (1);
	}
	phook->hook_name = strdup(newval);

	return (0);
}

/*
 *	Sets the hook 'phook's enabled attribute to a value
 *	representing 'newval'.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
set_hook_enabled(hook *phook, char *newval, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL!", __func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's value is NULL!", __func__);
		return (1);
	}


	if ((strcasecmp(newval, HOOKSTR_TRUE) == 0) ||
		(strcasecmp(newval, "t") == 0) ||
		(strcasecmp(newval, "y") == 0) ||
		(strcmp(newval, "1") == 0)) {
		phook->enabled = TRUE;
	} else if ((strcasecmp(newval, HOOKSTR_FALSE) == 0) ||
		(strcasecmp(newval, "f") == 0) ||
		(strcasecmp(newval, "n") == 0) ||
		(strcmp(newval, "0") == 0)) {
		phook->enabled = FALSE;
	} else {
		snprintf(msg, msg_len-1,
			"unexpected value \'%s\', must be (not case sensitive) "
			"%s|t|y|1|%s|f|n|0", newval,
			HOOKSTR_TRUE, HOOKSTR_FALSE);
		return (1);
	}
	return (0);
}

/*
 *	Sets the hook 'phook's debug attribute to a value
 *	representing 'newval'.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
set_hook_debug(hook *phook, char *newval, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL!", __func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's value is NULL!", __func__);
		return (1);
	}


	if ((strcasecmp(newval, HOOKSTR_TRUE) == 0) ||
		(strcasecmp(newval, "t") == 0) ||
		(strcasecmp(newval, "y") == 0) ||
		(strcmp(newval, "1") == 0)) {
		phook->debug = TRUE;
	} else if ((strcasecmp(newval, HOOKSTR_FALSE) == 0) ||
		(strcasecmp(newval, "f") == 0) ||
		(strcasecmp(newval, "n") == 0) ||
		(strcmp(newval, "0") == 0)) {
		phook->debug = FALSE;
	} else {
		snprintf(msg, msg_len-1,
			"unexpected value \'%s\', must be (not case sensitive) "
			"%s|t|y|1|%s|f|n|0", newval,
			HOOKSTR_TRUE, HOOKSTR_FALSE);
		return (1);
	}
	return (0);
}

/*
 *	Sets the hook 'phook's type attribute to a value
 *	representing 'newval'.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
set_hook_type(hook *phook, char *newval, char *msg, size_t msg_len,
	int allow_PBS_type)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's type is NULL!", __func__);
		return (1);
	}

	if (strcmp(newval, HOOKSTR_PBS) == 0) {
		if (!allow_PBS_type) {
			snprintf(msg, msg_len-1,
				"not allowed to set hook to '%s' type",
				HOOKSTR_PBS);
			return (1);
		}
		if (phook->hook_name &&
			(strncmp(phook->hook_name, HOOK_PBS_PREFIX,
			strlen(HOOK_PBS_PREFIX)) != 0)) {
			snprintf(msg, msg_len-1,
				"can't set hook to %s type - hook name (%s) "
				"not prefixed with %s", HOOKSTR_PBS,
				phook->hook_name, HOOK_PBS_PREFIX);
			return (1);
		}

		phook->type = HOOK_PBS;
	} else if (strcmp(newval, HOOKSTR_SITE) == 0) {
		if (phook->hook_name &&
			(strncmp(phook->hook_name, HOOK_PBS_PREFIX,
			strlen(HOOK_PBS_PREFIX)) == 0)) {
			snprintf(msg, msg_len-1,
				"can't set hook to %s type - hook name (%s) "
				"already prefixed with %s", HOOKSTR_SITE,
				phook->hook_name, HOOK_PBS_PREFIX);
			return (1);
		}

		if (phook->order <= 0) {
			snprintf(msg, msg_len-1,
				"can't set hook to %s type - hook order "
				"value is already set to <= 0", HOOKSTR_SITE);
			return (1);
		}
		phook->type = HOOK_SITE;
	} else {
		if (allow_PBS_type) {
			snprintf(msg, msg_len-1,
				"invalid argument to type, must be "
				"\"%s\" or \"%s\"", HOOKSTR_PBS, HOOKSTR_SITE);
		} else
			snprintf(msg, msg_len-1,
				"invalid argument to type, must be \"%s\"",
				HOOKSTR_SITE);
		return (1);

	}

	return (0);
}

/**
 * @brief
 *	Sets the hook 'phook's user attribute to a value
 *	representing 'newval'.
 *
 * @param[in/out] phook - hook being operated on
 * @param[in]	  newval - the new value to phook's user attribute.
 * @param[in/out] msg - filled in with error message if this function fails.
 * @param[in]	  msg_len - length of 'msg' buffer.
 * @param[in]	  strict - if 'strict' is set to 1, then only set 'user'
 *			  attribute to pbsuser if it is a mom hook and matches
 *			 one of the MOM_USER_EVENTS.
 * @return int
 * @retval 0 	for success
 * @retval 1 	for failure, with 'msg' of size 'msg_len' filled in.
 */
int
set_hook_user(hook *phook, char *newval, char *msg, size_t msg_len, int strict)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL!", __func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s:  hook's user is NULL!", __func__);
		return (1);
	}

	if (strcmp(newval, HOOKSTR_ADMIN) != 0) {
		if (phook->event & HOOK_EVENT_PERIODIC) {
			snprintf (msg, msg_len-1,"user value of a server periodic hook must be %s", HOOKSTR_ADMIN);
			return (1);
		}
		else if	(strcmp(newval, HOOKSTR_USER) != 0) {
			snprintf(msg, msg_len-1,
				"user value of a hook must be %s,%s", HOOKSTR_ADMIN,
				HOOKSTR_USER);
			return (1);
		}
	}

	if (strcmp(newval, HOOKSTR_ADMIN) == 0) {
		phook->user =  HOOK_PBSADMIN;
	} else if (strcmp(newval, HOOKSTR_USER) == 0) {

		if (strict && ((phook->event & USER_MOM_EVENTS) == 0)) {
			snprintf(msg, msg_len-1,
				"Can't set hook user value to '%s': hook event must contain at least %s", HOOKSTR_USER, hook_event_as_string(USER_MOM_EVENTS));
			return (1);
		}

		phook->user =  HOOK_PBSUSER;
	}

	return (0);
}

/**
 *
 * @brief
 *	Inserts the hook 'phook' into  'phook_head' that represents a list
 *	of hooks of the given 'event'. The hook is inserted following the
 *	phook->order, in ascending format.
 *
 * @param[in]	  event - the event of the hooks.
 * @param[in/out] phook_head - list of hooks of the given 'event' where 'phook'
 *				will be inserted.
 * @param[in]	  phook - hook to insert.
 *
 */
static void
insert_hook_sort_order(unsigned int event, pbs_list_head *phook_head, hook *phook)
{
	pbs_list_link	*plink_elem, *plink_cur;
	hook		*phook_cur;

	if ((phook_head == NULL) || (phook == NULL)) {
		log_err(PBSE_INTERNAL, __func__,
			"NULL arguments to phook_head and/or phook");
		return;
	}

	if (event == HOOK_EVENT_QUEUEJOB) {
		plink_elem = &phook->hi_queuejob_hooks;
	} else if (event == HOOK_EVENT_MODIFYJOB) {
		plink_elem = &phook->hi_modifyjob_hooks;
	} else if (event == HOOK_EVENT_RESVSUB) {
		plink_elem = &phook->hi_resvsub_hooks;
	} else if (event == HOOK_EVENT_MOVEJOB) {
		plink_elem = &phook->hi_movejob_hooks;
	} else if (event == HOOK_EVENT_RUNJOB) {
		plink_elem = &phook->hi_runjob_hooks;
	} else if (event == HOOK_EVENT_PROVISION) {
		plink_elem = &phook->hi_provision_hooks;
	} else if (event == HOOK_EVENT_PERIODIC) {
		plink_elem = &phook->hi_periodic_hooks;
	} else if (event == HOOK_EVENT_RESV_END) {
		plink_elem = &phook->hi_resv_end_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_BEGIN) {
		plink_elem = &phook->hi_execjob_begin_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_PROLOGUE) {
		plink_elem = &phook->hi_execjob_prologue_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_EPILOGUE) {
		plink_elem = &phook->hi_execjob_epilogue_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_PRETERM) {
		plink_elem = &phook->hi_execjob_preterm_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_LAUNCH) {
		plink_elem = &phook->hi_execjob_launch_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_END) {
		plink_elem = &phook->hi_execjob_end_hooks;
	} else if (event == HOOK_EVENT_EXECHOST_PERIODIC) {
		plink_elem = &phook->hi_exechost_periodic_hooks;
	} else if (event == HOOK_EVENT_EXECHOST_STARTUP) {
		plink_elem = &phook->hi_exechost_startup_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_ATTACH) {
		plink_elem = &phook->hi_execjob_attach_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_RESIZE) {
		plink_elem = &phook->hi_execjob_resize_hooks;
	} else {
		/* should not happen */
		log_err(PBSE_INTERNAL, __func__, "encountered a bad event");
		return;
	}

	if (is_linked(phook_head, plink_elem)) {
		return;
	}

	phook_cur = (hook *)GET_NEXT(*phook_head);
	plink_cur = phook_head;
	while (phook_cur) {

		if (event == HOOK_EVENT_QUEUEJOB) {
			plink_cur = &phook_cur->hi_queuejob_hooks;
		} else if (event == HOOK_EVENT_MODIFYJOB) {
			plink_cur = &phook_cur->hi_modifyjob_hooks;
		} else if (event == HOOK_EVENT_RESVSUB) {
			plink_cur = &phook_cur->hi_resvsub_hooks;
		} else if (event == HOOK_EVENT_MOVEJOB) {
			plink_cur = &phook_cur->hi_movejob_hooks;
		} else if (event == HOOK_EVENT_RUNJOB) {
			plink_cur = &phook_cur->hi_runjob_hooks;
		} else if (event == HOOK_EVENT_PROVISION) {
			plink_cur = &phook_cur->hi_provision_hooks;
		} else if (event == HOOK_EVENT_PERIODIC) {
			plink_cur = &phook_cur->hi_periodic_hooks;
		} else if (event == HOOK_EVENT_RESV_END) {
			plink_cur = &phook_cur->hi_resv_end_hooks;
	        } else if (event == HOOK_EVENT_EXECJOB_BEGIN) {
			plink_cur = &phook_cur->hi_execjob_begin_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_PROLOGUE) {
			plink_cur = &phook_cur->hi_execjob_prologue_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_EPILOGUE) {
			plink_cur = &phook_cur->hi_execjob_epilogue_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_PRETERM) {
			plink_cur = &phook_cur->hi_execjob_preterm_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_LAUNCH) {
			plink_cur = &phook_cur->hi_execjob_launch_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_END) {
			plink_cur = &phook_cur->hi_execjob_end_hooks;
		} else if (event == HOOK_EVENT_EXECHOST_PERIODIC) {
			plink_cur = &phook_cur->hi_exechost_periodic_hooks;
		} else if (event == HOOK_EVENT_EXECHOST_STARTUP) {
			plink_cur = &phook_cur->hi_exechost_startup_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_ATTACH) {
			plink_cur = &phook_cur->hi_execjob_attach_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_RESIZE) {
			plink_cur = &phook_cur->hi_execjob_resize_hooks;
		} else {
			/* should not happen */
			log_err(PBSE_INTERNAL, __func__, "encountered a bad event");
			return;
		}

		if (phook_cur->order > phook->order) {
			break;
		}

		phook_cur = (hook *)GET_NEXT(*plink_cur);
	}

	if (phook_cur) {
		/* link before 'current' hook in server's list */
		insert_link(plink_cur, plink_elem, phook, LINK_INSET_BEFORE);
	} else {
		/* attach either at the beginning or the last of the list */
		insert_link(plink_cur, plink_elem, phook, LINK_INSET_AFTER);
	}
}

/**
 *
 * @brief
 *	Sets the hook 'phook's fail_action attribute to a value
 *	representing 'newval'.
 *
 * @param[in/out]  phook - hook being operated on.
 * @param[in]	   newval - new fail_action value
 * @param[in/out]  msg - error message buffer
 * @param[in]	   msg_len - length of 'msg' buffer.
 * @param[in]	  strict - if 'strict' is set to 1, then only non- "none"
 * 			 value if the hook's event has "execjob_begin" or
 * 			 "exechost_periodic".
 *
 * @return int
 * @retval 0 	for success
 * @retval 1 	for failure with 'msg' of size 'msg_len' filled in.
 */
int
set_hook_fail_action(hook *phook, char *newval, char *msg,
	size_t msg_len, int strict)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's fail_action is NULL!", __func__);
		return (1);
	}

	if (phook->fail_action != 0) {
		phook->fail_action = 0;
	}

	return add_hook_fail_action(phook, newval, msg, msg_len, strict);
}

/**
 * @brief
 *	Adds to hook 'phook's fail_action attribute (bitmask) a value
 *	representing 'newval'.
 * @note
 * 	The valid 'newval' values are:
 *		HOOKSTR_FAIL_ACTION_NONE	   ("none")
 *		HOOKSTR_FAIL_ACTION_OFFLINE_VNODES ("offline_vnodes")
 *		HOOKSTR_FAIL_ACTION_CLEAR_VNODES   ("clear_vnodes_upon_recovery")
 *		HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE
 *						("scheduler_restart_cycle")
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - new hook fail_action value
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of message buffer
 * @param[in]	  	strict - if 'strict' is set to 1, then only non- "none"
 * 			 value if the hook's event has "execjob_begin" or
 * 			 "exechost_periodic".
 *
 * @return int
 * @retval 0	for success
 * @retval 1 *  for failure with 'msg' of size 'msg_len' filled in.
 */
int
add_hook_fail_action(hook *phook, char *newval, char *msg, size_t msg_len,
	int strict)
{
	char	*val, *newval_dup;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's fail_action is NULL!", __func__);
		return (1);
	}

	if ((newval_dup=strdup(newval)) == NULL) {
		snprintf(msg, msg_len-1,
			"%s: failed to malloc newval=%s!", __func__, newval);
		return (1);
	}
	val = strtok(newval_dup, ",");
	while (val) {
		if (strcmp(val, HOOKSTR_FAIL_ACTION_NONE) == 0) {
			/* can't combine "none" with other fail_action values */
			if ((phook->fail_action != HOOK_FAIL_ACTION_NONE) &&
				(phook->fail_action != 0))
				goto err;
			phook->fail_action |= HOOK_FAIL_ACTION_NONE;
		} else if (strcmp(val, HOOKSTR_FAIL_ACTION_OFFLINE_VNODES) == 0) {
			/* can't combine "none" with other fail_action values */
			if (phook->fail_action & HOOK_FAIL_ACTION_NONE)
				goto err;
			/* must contain one of the events in FAIL_ACTION_EVENTS */
			/* in order to set an "offline_vnodes" fail_action value. */
			if (strict &&
				((phook->event & FAIL_ACTION_EVENTS) == 0)) {
				if (msg[0]=='\0') {
					snprintf(msg, msg_len-1,
						"Can't set hook fail_action value to '%s': "
						"hook event must contain at least one of %s",
						val, HOOKSTR_FAIL_ACTION_EVENTS);
				}
				free(newval_dup);
				return (1);
			}

			phook->fail_action |= HOOK_FAIL_ACTION_OFFLINE_VNODES;
		} else if (strcmp(val, HOOKSTR_FAIL_ACTION_CLEAR_VNODES) == 0) {
			/* can't combine "none" with other fail_action values */
			if (phook->fail_action & HOOK_FAIL_ACTION_NONE)
				goto err;
			/* must be an exechost_startup event value */
			/* in order to set a "clear_vnodes_upon_recovery" fail_action value. */
			if (strict &&
				((phook->event & HOOK_EVENT_EXECHOST_STARTUP) == 0)) {
				if (msg[0]=='\0') {
					snprintf(msg, msg_len-1,
						"Can't set hook fail_action value to '%s': "
						"hook event must contain at least an %s value",
						val, HOOKSTR_EXECHOST_STARTUP);
				}
				free(newval_dup);
				return (1);
			}
			phook->fail_action |= HOOK_FAIL_ACTION_CLEAR_VNODES;
		} else if (strcmp(val,
			HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE) == 0) {
			/* can't combine "none" with other fail_action values */
			if (phook->fail_action & HOOK_FAIL_ACTION_NONE)
				goto err;
			/* must contain one of the events in HOOK_EVENT_EXECJOB_BEGIN, HOOK_EVENT_EXECJOB_PROLOGUE */
			/* in order to set a "scheduler_restart_cycle" fail_action value. */
			if (strict &&
				((phook->event & HOOK_EVENT_EXECJOB_BEGIN) == 0) &&
				((phook->event & HOOK_EVENT_EXECJOB_PROLOGUE) == 0)) {
				if (msg[0]=='\0') {
					snprintf(msg, msg_len-1,
						"Can't set hook fail_action value to '%s': "
						"hook event must contain at least one of %s, %s",
						val, HOOKSTR_EXECJOB_BEGIN, HOOKSTR_EXECJOB_PROLOGUE);
				}
				free(newval_dup);
				return (1);
			}
			phook->fail_action |= HOOK_FAIL_ACTION_SCHEDULER_RESTART_CYCLE;
		} else {
			snprintf(msg, msg_len-1,
				"fail_action value of a hook "
				"must be \"%s\" or one or more of \"%s\","
				"\"%s\", \"%s\"",
				HOOKSTR_FAIL_ACTION_NONE,
				HOOKSTR_FAIL_ACTION_OFFLINE_VNODES,
				HOOKSTR_FAIL_ACTION_CLEAR_VNODES,
				HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE);
			free(newval_dup);
			return (1);
		}

		val = strtok(NULL, ",");
	}

	free(newval_dup);
	return (0);
err:
	if (msg[0]=='\0') {
		snprintf(msg, msg_len-1,
			"fail_action value of a hook "
			"must be \"%s\" or one or more of \"%s\","
			"\"%s\", \"%s\"",
			HOOKSTR_FAIL_ACTION_NONE,
			HOOKSTR_FAIL_ACTION_OFFLINE_VNODES,
			HOOKSTR_FAIL_ACTION_CLEAR_VNODES,
			HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE);
	}
	free(newval_dup);
	return (1);

}

/**
 * @brief
 *	Removes from hook 'phook's fail_action attribute (bitmask) the value
 *	representing 'newval'.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - the hook fail_action value to delete
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' illed in.
 */
int
del_hook_fail_action(hook *phook, char *newval, char *msg, size_t msg_len)
{
	char	*val, *newval_dup;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's fail_action is NULL!", __func__);
		return (1);
	}

	if ((newval_dup=strdup(newval)) == NULL) {
		snprintf(msg, msg_len-1,
			"%s: failed to malloc newval=%s!", __func__, newval);
		return (1);
	}

	val = strtok(newval_dup, ",");
	while (val) {
		if (strcmp(val, HOOKSTR_FAIL_ACTION_NONE) == 0) {
			phook->fail_action &= ~HOOK_FAIL_ACTION_NONE;
			if (phook->fail_action == 0) {
				phook->fail_action = HOOK_FAIL_ACTION_NONE;
			}
		} else if (strcmp(val, HOOKSTR_FAIL_ACTION_OFFLINE_VNODES) == 0) {
			phook->fail_action &= ~HOOK_FAIL_ACTION_OFFLINE_VNODES;
		} else if (strcmp(val, HOOKSTR_FAIL_ACTION_CLEAR_VNODES) == 0) {
			phook->fail_action &= ~HOOK_FAIL_ACTION_CLEAR_VNODES;
		} else if (strcmp(val,
			HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE) == 0) {
			phook->fail_action &= ~HOOK_FAIL_ACTION_SCHEDULER_RESTART_CYCLE;
		} else if (strcmp(val, HOOKSTR_NONE) != 0) {
			snprintf(msg, msg_len-1,
				"fail_action value of a hook "
				"must be \"%s\" or one or more of \"%s\","
				"\"%s\", \"%s\"",
				HOOKSTR_FAIL_ACTION_NONE,
				HOOKSTR_FAIL_ACTION_OFFLINE_VNODES,
				HOOKSTR_FAIL_ACTION_CLEAR_VNODES,
				HOOKSTR_FAIL_ACTION_SCHEDULER_RESTART_CYCLE);
			free(newval_dup);
			return (1);
		}

		val = strtok(NULL, ",");
	}

	free(newval_dup);
	return (0);
}

/*
 *
 * @brief
 *	Sets the hook 'phook's event attribute to a value
 *	representing 'newval'.
 *
 * @param[in/out]  phook - hook being operated on.
 * @param[in]	   newval - new event value
 * @param[in/out]  msg - error message buffer
 * @param[in]	   msg_len - length of 'msg' buffer.
 *
 * @return int
 * @retval 0 	for success
 * @retval 1 	for failure with 'msg' of size 'msg_len' filled in.
 */
int
set_hook_event(hook *phook, char *newval, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's event is NULL!", __func__);
		return (1);
	}

	if (phook->event != 0) {

		delete_link(&phook->hi_queuejob_hooks);
		delete_link(&phook->hi_modifyjob_hooks);
		delete_link(&phook->hi_resvsub_hooks);
		delete_link(&phook->hi_movejob_hooks);
		delete_link(&phook->hi_runjob_hooks);
		delete_link(&phook->hi_provision_hooks);
		delete_link(&phook->hi_periodic_hooks);
		delete_link(&phook->hi_resv_end_hooks);
		delete_link(&phook->hi_execjob_begin_hooks);
		delete_link(&phook->hi_execjob_prologue_hooks);
		delete_link(&phook->hi_execjob_epilogue_hooks);
		delete_link(&phook->hi_execjob_preterm_hooks);
		delete_link(&phook->hi_execjob_launch_hooks);
		delete_link(&phook->hi_execjob_end_hooks);
		delete_link(&phook->hi_exechost_periodic_hooks);
		delete_link(&phook->hi_exechost_startup_hooks);
		delete_link(&phook->hi_execjob_attach_hooks);
		delete_link(&phook->hi_execjob_resize_hooks);
		phook->event = 0;
	}

	return add_hook_event(phook, newval, msg, msg_len);
}

/**
 * @brief
 *	Adds to hook 'phook's event attribute (bitmask) a value
 *	representing 'newval'. Various hooks lists where 'phook'
 *	appear are automatically updated to reflect the changed
 *	event value.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - new hook event value
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of message buffer
 *
 * @return int
 * @retval 0	for success
 * @retval 1 *  for failure with 'msg' of size 'msg_len' filled in.
 */
int
add_hook_event(hook *phook, char *newval, char *msg, size_t msg_len)
{
	char	*val, *newval_dup;
	hook 	*anotherhook;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's event is NULL!", __func__);
		return (1);
	}

	if ((newval_dup=strdup(newval)) == NULL) {
		snprintf(msg, msg_len-1,
			"%s: failed to malloc newval=%s!", __func__, newval);
		return (1);
	}
	val = strtok(newval_dup, ",");
	while (val) {
		if (strcmp(val, HOOKSTR_QUEUEJOB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_queuejob_hooks);
			phook->event 	|= HOOK_EVENT_QUEUEJOB;
			insert_hook_sort_order(HOOK_EVENT_QUEUEJOB,
				&svr_queuejob_hooks, phook);
		} else if (strcmp(val, HOOKSTR_MODIFYJOB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_modifyjob_hooks);
			phook->event 	|= HOOK_EVENT_MODIFYJOB;
			insert_hook_sort_order(HOOK_EVENT_MODIFYJOB,
				&svr_modifyjob_hooks, phook);
		} else if (strcmp(val, HOOKSTR_RESVSUB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_resvsub_hooks);
			phook->event 	|= HOOK_EVENT_RESVSUB;
			insert_hook_sort_order(HOOK_EVENT_RESVSUB,
				&svr_resvsub_hooks, phook);
		} else if (strcmp(val, HOOKSTR_MOVEJOB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_movejob_hooks);
			phook->event 	|= HOOK_EVENT_MOVEJOB;
			insert_hook_sort_order(HOOK_EVENT_MOVEJOB,
				&svr_movejob_hooks, phook);
		} else if (strcmp(val, HOOKSTR_RUNJOB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_runjob_hooks);
			phook->event 	|= HOOK_EVENT_RUNJOB;
			insert_hook_sort_order(HOOK_EVENT_RUNJOB,
				&svr_runjob_hooks, phook);
		} else if (strcmp(val, HOOKSTR_PROVISION) == 0) {
			if (phook->event & ~HOOK_EVENT_PROVISION)
				goto err;
			anotherhook = find_hookbyevent(HOOK_EVENT_PROVISION);
			if (anotherhook) {
				snprintf(msg, msg_len-1,
					"Another hook '%s' already has "
					"event 'provision', only one 'provision' "
					"hook allowed",
					anotherhook->hook_name);
				return -1;
			}
			delete_link(&phook->hi_provision_hooks);
			phook->event 	|= HOOK_EVENT_PROVISION;
			insert_hook_sort_order(HOOK_EVENT_PROVISION,
				&svr_provision_hooks, phook);
		} else if (strcmp(val, HOOKSTR_PERIODIC) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_periodic_hooks);
			phook->event 	|= HOOK_EVENT_PERIODIC;
			insert_hook_sort_order(HOOK_EVENT_PERIODIC,
				&svr_periodic_hooks, phook);
		} else if (strcmp(val, HOOKSTR_RESV_END) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_resv_end_hooks);
			phook->event    |= HOOK_EVENT_RESV_END;
			insert_hook_sort_order(HOOK_EVENT_RESV_END,
				&svr_resv_end_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_BEGIN) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_begin_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_BEGIN;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_BEGIN,
				&svr_execjob_begin_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_PROLOGUE) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_prologue_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_PROLOGUE;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_PROLOGUE,
				&svr_execjob_prologue_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_EPILOGUE) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_epilogue_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_EPILOGUE;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_EPILOGUE,
				&svr_execjob_epilogue_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_END) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_end_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_END;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_END,
				&svr_execjob_end_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_PRETERM) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_preterm_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_PRETERM;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRETERM,
				&svr_execjob_preterm_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_LAUNCH) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_launch_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_LAUNCH;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_LAUNCH,
				&svr_execjob_launch_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECHOST_PERIODIC) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_exechost_periodic_hooks);
			phook->event 	|= HOOK_EVENT_EXECHOST_PERIODIC;
			insert_hook_sort_order(HOOK_EVENT_EXECHOST_PERIODIC,
				&svr_exechost_periodic_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECHOST_STARTUP) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_exechost_startup_hooks);
			phook->event 	|= HOOK_EVENT_EXECHOST_STARTUP;
			insert_hook_sort_order(HOOK_EVENT_EXECHOST_STARTUP,
				&svr_exechost_startup_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_ATTACH) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_attach_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_ATTACH;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_ATTACH,
				&svr_execjob_attach_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_RESIZE) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_resize_hooks);
			phook->event 	|= HOOK_EVENT_EXECJOB_RESIZE;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_RESIZE,
				&svr_execjob_resize_hooks, phook);
		} else if (strcmp(val, HOOKSTR_NONE) != 0) {
			snprintf(msg, msg_len-1,
				"invalid argument (%s) to event. "
				"Should be one or more of: %s,%s,%s,%s,%s,%s,%s,%s,"
				"%s,%s,%s,%s,%s,%s,%s,%s,%s,%s "
				"or %s for no event",
				newval, HOOKSTR_QUEUEJOB, HOOKSTR_MODIFYJOB,
				HOOKSTR_RESVSUB, HOOKSTR_MOVEJOB,
				HOOKSTR_RUNJOB, HOOKSTR_PROVISION, HOOKSTR_PERIODIC, HOOKSTR_RESV_END, 
				HOOKSTR_EXECJOB_BEGIN, HOOKSTR_EXECJOB_PROLOGUE,
				HOOKSTR_EXECJOB_EPILOGUE, HOOKSTR_EXECJOB_PRETERM,
				HOOKSTR_EXECJOB_END, HOOKSTR_EXECHOST_PERIODIC, HOOKSTR_EXECJOB_LAUNCH,
				HOOKSTR_EXECHOST_STARTUP, HOOKSTR_EXECJOB_ATTACH, HOOKSTR_EXECJOB_RESIZE, HOOKSTR_NONE);
			free(newval_dup);
			return (1);
		}

		val = strtok(NULL, ",");
	}

	free(newval_dup);
	return (0);
err:
	if (msg[0]=='\0') {
		snprintf(msg, msg_len-1,
			"Event provision is exclusive with other events, "
			"and only one provision hook allowed");
	}
	free(newval_dup);
	return (1);
}

/**
 * @brief
 *	Removes from hook 'phook's event attribute (bitmask) the value
 *	representing 'newval'. Various hooks lists where 'phook' appears
 *	are automatically updated to reflect the changed event value.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - the hook event value to delete
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' illed in.
 */
int
del_hook_event(hook *phook, char *newval, char *msg, size_t msg_len)
{
	char	*val, *newval_dup;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's event is NULL!", __func__);
		return (1);
	}

	if ((newval_dup=strdup(newval)) == NULL) {
		snprintf(msg, msg_len-1,
			"%s: failed to malloc newval=%s!", __func__, newval);
		return (1);
	}

	val = strtok(newval_dup, ",");
	while (val) {
		if (strcmp(val, HOOKSTR_QUEUEJOB) == 0) {
			delete_link(&phook->hi_queuejob_hooks);
			phook->event 	&= ~HOOK_EVENT_QUEUEJOB;
		} else if (strcmp(val, HOOKSTR_MODIFYJOB) == 0) {
			delete_link(&phook->hi_modifyjob_hooks);
			phook->event 	&= ~HOOK_EVENT_MODIFYJOB;
		} else if (strcmp(val, HOOKSTR_RESVSUB) == 0) {
			delete_link(&phook->hi_resvsub_hooks);
			phook->event 	&= ~HOOK_EVENT_RESVSUB;
		} else if (strcmp(val, HOOKSTR_MOVEJOB) == 0) {
			delete_link(&phook->hi_movejob_hooks);
			phook->event 	&= ~HOOK_EVENT_MOVEJOB;
		} else if (strcmp(val, HOOKSTR_RUNJOB) == 0) {
			delete_link(&phook->hi_runjob_hooks);
			phook->event 	&= ~HOOK_EVENT_RUNJOB;
		} else if (strcmp(val, HOOKSTR_PROVISION) == 0) {
			delete_link(&phook->hi_provision_hooks);
			phook->event 	&= ~HOOK_EVENT_PROVISION;
		} else if (strcmp(val, HOOKSTR_PERIODIC) == 0) {
			delete_link(&phook->hi_periodic_hooks);
			phook->event 	&= ~HOOK_EVENT_PERIODIC;
			delete_task_by_parm1(phook, DELETE_ALL);
		} else if (strcmp(val, HOOKSTR_RESV_END) == 0) {
			delete_link(&phook->hi_resv_end_hooks);
			phook->event    &= ~HOOK_EVENT_RESV_END;
		} else if (strcmp(val, HOOKSTR_EXECJOB_BEGIN) == 0) {
			delete_link(&phook->hi_execjob_begin_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_BEGIN;
		} else if (strcmp(val, HOOKSTR_EXECJOB_PROLOGUE) == 0) {
			delete_link(&phook->hi_execjob_prologue_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_PROLOGUE;
		} else if (strcmp(val, HOOKSTR_EXECJOB_EPILOGUE) == 0) {
			delete_link(&phook->hi_execjob_epilogue_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_EPILOGUE;
		} else if (strcmp(val, HOOKSTR_EXECJOB_END) == 0) {
			delete_link(&phook->hi_execjob_end_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_END;
		} else if (strcmp(val, HOOKSTR_EXECJOB_PRETERM) == 0) {
			delete_link(&phook->hi_execjob_preterm_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_PRETERM;
		} else if (strcmp(val, HOOKSTR_EXECJOB_LAUNCH) == 0) {
			delete_link(&phook->hi_execjob_launch_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_LAUNCH;
		} else if (strcmp(val, HOOKSTR_EXECHOST_PERIODIC) == 0) {
			delete_link(&phook->hi_exechost_periodic_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECHOST_PERIODIC;
		} else if (strcmp(val, HOOKSTR_EXECHOST_STARTUP) == 0) {
			delete_link(&phook->hi_exechost_startup_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECHOST_STARTUP;
		} else if (strcmp(val, HOOKSTR_EXECJOB_ATTACH) == 0) {
			delete_link(&phook->hi_execjob_attach_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_ATTACH;
		} else if (strcmp(val, HOOKSTR_EXECJOB_RESIZE) == 0) {
			delete_link(&phook->hi_execjob_resize_hooks);
			phook->event 	&= ~HOOK_EVENT_EXECJOB_RESIZE;
		} else if (strcmp(val, HOOKSTR_NONE) != 0) {
			snprintf(msg, msg_len-1,
				"invalid argument (%s) to event. "
				"Should be one or more of: %s,%s,%s,%s,%s,%s,%s,%s,"
				"%s,%s,%s,%s,%s,%s,%s,%s,%s,%s "
				"or %s for no event.",
				newval, HOOKSTR_QUEUEJOB, HOOKSTR_MODIFYJOB,
				HOOKSTR_RESVSUB, HOOKSTR_MOVEJOB,
				HOOKSTR_RUNJOB, HOOKSTR_PERIODIC, HOOKSTR_PROVISION, HOOKSTR_RESV_END,
				HOOKSTR_EXECJOB_BEGIN, HOOKSTR_EXECJOB_PROLOGUE,
				HOOKSTR_EXECJOB_EPILOGUE, HOOKSTR_EXECJOB_END,
				HOOKSTR_EXECJOB_PRETERM, HOOKSTR_EXECHOST_PERIODIC,
				HOOKSTR_EXECJOB_LAUNCH, HOOKSTR_EXECHOST_STARTUP,
				HOOKSTR_EXECJOB_ATTACH, HOOKSTR_EXECJOB_RESIZE, HOOKSTR_NONE);
			free(newval_dup);
			return (1);
		}

		val = strtok(NULL, ",");
	}

	free(newval_dup);
	return (0);
}

/**
 * @brief
 *	Sets the hook 'phook's order attribute to a value
 *	representing 'newval'. Various hooks lists where
 *	'phook' appears are updated to reflect the new phook->order value.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - the hook order value to set to
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' filled in.
 *
 */
int
set_hook_order(hook *phook, char *newval, char *msg, size_t msg_len)
{

	int 		val;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1, "%s: hook parameter is NULL!",
			__func__);
		return (1);
	}

	if (newval == NULL) {
		snprintf(msg, msg_len-1, "%s: hook's order is NULL!", __func__);
		return (1);
	}


	val = atoi(newval);

	if (phook->type == HOOK_SITE) {

		if ((val < HOOK_SITE_ORDER_MIN)  ||
			(val > HOOK_SITE_ORDER_MAX)) {
			snprintf(msg, msg_len-1,
				"order given (%d) is outside the acceptable "
				"range of [%d, %d] for type \'%s\'.",
				val, HOOK_SITE_ORDER_MIN, HOOK_SITE_ORDER_MAX,
				HOOKSTR_SITE);
			return (1);
		}
	} else {
		if ((val < HOOK_PBS_ORDER_MIN)  ||
			(val > HOOK_PBS_ORDER_MAX)) {
			snprintf(msg, msg_len-1,
				"order given (%d) is outside the acceptable "
				"range of [%d, %d] for type \'%s\'.", val,
				HOOK_PBS_ORDER_MIN, HOOK_PBS_ORDER_MAX,
				HOOKSTR_PBS);
			return (1);
		}
	}

	phook->order = val;

	if (phook->event & HOOK_EVENT_QUEUEJOB) {
		delete_link(&phook->hi_queuejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_QUEUEJOB,
			&svr_queuejob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MODIFYJOB) {
		delete_link(&phook->hi_modifyjob_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYJOB,
			&svr_modifyjob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESVSUB) {
		delete_link(&phook->hi_resvsub_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESVSUB,
			&svr_resvsub_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MOVEJOB) {
		delete_link(&phook->hi_movejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_MOVEJOB,
			&svr_movejob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RUNJOB) {
		delete_link(&phook->hi_runjob_hooks);
		insert_hook_sort_order(HOOK_EVENT_RUNJOB,
			&svr_runjob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_END) {
	    	delete_link(&phook->hi_resv_end_hooks);
	    	insert_hook_sort_order(HOOK_EVENT_RESV_END,
	        	&svr_resv_end_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_BEGIN) {
		delete_link(&phook->hi_execjob_begin_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_BEGIN,
			&svr_execjob_begin_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PROLOGUE) {
		delete_link(&phook->hi_execjob_prologue_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PROLOGUE,
			&svr_execjob_prologue_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_EPILOGUE) {
		delete_link(&phook->hi_execjob_epilogue_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_EPILOGUE,
			&svr_execjob_epilogue_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_END) {
		delete_link(&phook->hi_execjob_end_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_END,
			&svr_execjob_end_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRETERM) {
		delete_link(&phook->hi_execjob_preterm_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRETERM,
			&svr_execjob_preterm_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_LAUNCH) {
		delete_link(&phook->hi_execjob_launch_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_LAUNCH,
			&svr_execjob_launch_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECHOST_PERIODIC) {
		delete_link(&phook->hi_exechost_periodic_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECHOST_PERIODIC,
			&svr_exechost_periodic_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECHOST_STARTUP) {
		delete_link(&phook->hi_exechost_startup_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECHOST_STARTUP,
			&svr_exechost_startup_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ATTACH) {
		delete_link(&phook->hi_execjob_attach_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_ATTACH,
			&svr_execjob_attach_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_RESIZE) {
		delete_link(&phook->hi_execjob_resize_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_RESIZE,
			&svr_execjob_resize_hooks, phook);
	}
	return (0);
}

/*
 *	Sets the hook 'phook's alarm attribute to a value
 *	representing 'newval'.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
set_hook_alarm(hook *phook, char *newval, char *msg, size_t msg_len)
{
	int	alarm;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL!", __func__);
		return (1);
	}
	if (newval == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook's alarm is NULL!", __func__);
		return (1);
	}

	alarm = atoi(newval);

	if (alarm <= 0) {
		snprintf(msg, msg_len-1,
			"%s: alarm value '%s' of a hook must be > 0", __func__,
			newval);
		return (1);
	}

	phook->alarm = alarm;
	return (0);

}

/**
 * @brief
 *	Sets the hook 'phook's freq attribute to a value
 *	representing 'newval'.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in]		newval - the hook freq value to set to
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' filled in.
 */
int
set_hook_freq(hook *phook, char *newval, char *msg, size_t msg_len)
{
	int	freq;
	char		*pc = NULL;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook  == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL!", __func__);
		return (1);
	}
	if (newval == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook's freq is NULL!", __func__);
		return (1);
	}

	pc = newval;
	if (*pc == '-')
		++pc; /* move past negative number, it will be caught later  */

	while (isdigit((int)*pc))
		++pc;

	if (*pc != '\0') {
		snprintf(msg, msg_len-1,
			"%s: encountered a non-digit freq value: %c",
			__func__, *pc);
		return (1);
	}

	freq = atoi(newval);

	if (freq <= 0) {
		snprintf(msg, msg_len-1,
			"%s: freq value '%s' of a hook must be > 0", __func__,
			newval);
		return (1);
	}

	if (((phook->event & HOOK_EVENT_EXECHOST_PERIODIC) == 0) && ((phook->event & HOOK_EVENT_PERIODIC) == 0)) {
		snprintf(msg, msg_len-1,
			"%s: Can't set hook freq value: hook event must contain at least'%s' or '%s'", __func__,
			HOOKSTR_EXECHOST_PERIODIC, HOOKSTR_PERIODIC);
		return (1);
	}

	phook->freq = freq;
	return (0);

}

/*
 *
 * unset_hook* functions.
 *
 */

/*
 *	Unsets 'phook's enabled value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_enabled(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->enabled = HOOK_ENABLED_DEFAULT;
	return (0);
}

/*
 *
 * unset_hook* functions.
 *
 */

/*
 *	Unsets 'phook's debug value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_debug(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->debug = HOOK_DEBUG_DEFAULT;
	return (0);
}

/*
 *	Unsets 'phook's type value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_type(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	if (phook->hook_name &&
		(strncmp(phook->hook_name, HOOK_PBS_PREFIX,
		strlen(HOOK_PBS_PREFIX)) == 0)) {
		snprintf(msg, msg_len-1,
			"can't unset hook's type since hook name is %s",
			phook->hook_name);
		return (1);
	}
	phook->type = HOOK_TYPE_DEFAULT;
	return (0);
}

/*
 *	Unsets 'phook's user value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_user(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->user = HOOK_USER_DEFAULT;
	return (0);
}

/*
 *	Unsets 'phook's fail_action value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_fail_action(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->fail_action = HOOK_FAIL_ACTION_DEFAULT;
	return (0);
}

/**
 * @brief
 *	Unsets 'phook's event value, resetting back to default.
 *	Various hosts list where 'phook' appears are updated to
 *	reflect the changed event value.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' filled in.
 */
int
unset_hook_event(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	if (phook->event & HOOK_EVENT_QUEUEJOB)
		delete_link(&phook->hi_queuejob_hooks);

	if (phook->event & HOOK_EVENT_MODIFYJOB)
		delete_link(&phook->hi_modifyjob_hooks);

	if (phook->event & HOOK_EVENT_RESVSUB)
		delete_link(&phook->hi_resvsub_hooks);

	if (phook->event & HOOK_EVENT_MOVEJOB)
		delete_link(&phook->hi_movejob_hooks);

	if (phook->event & HOOK_EVENT_RUNJOB)
		delete_link(&phook->hi_runjob_hooks);

	if (phook->event & HOOK_EVENT_PROVISION)
		delete_link(&phook->hi_provision_hooks);

	if (phook->event & HOOK_EVENT_PERIODIC)
		delete_link(&phook->hi_periodic_hooks);

	if (phook->event & HOOK_EVENT_RESV_END)
		delete_link(&phook->hi_resv_end_hooks);

	if (phook->event & HOOK_EVENT_EXECJOB_BEGIN) {
		delete_link(&phook->hi_execjob_begin_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PROLOGUE) {
		delete_link(&phook->hi_execjob_prologue_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_EPILOGUE) {
		delete_link(&phook->hi_execjob_epilogue_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRETERM) {
		delete_link(&phook->hi_execjob_preterm_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_LAUNCH) {
		delete_link(&phook->hi_execjob_launch_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_END) {
		delete_link(&phook->hi_execjob_end_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECHOST_PERIODIC) {
		delete_link(&phook->hi_exechost_periodic_hooks);
	}
	if (phook->event & HOOK_EVENT_EXECHOST_STARTUP) {
		delete_link(&phook->hi_exechost_startup_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ATTACH) {
		delete_link(&phook->hi_execjob_attach_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_RESIZE) {
		delete_link(&phook->hi_execjob_resize_hooks);
	}

	phook->event = HOOK_EVENT_DEFAULT;

	return (0);
}

/**
 * @brief
 *	Unsets 'phook's order value, resetting back to default.
 *	Various hosts list where 'phook' appears are updated to
 *	reflect the changed phook->order value.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' filled in.
 */
int
unset_hook_order(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->order = HOOK_ORDER_DEFAULT;

	if (phook->event & HOOK_EVENT_QUEUEJOB) {
		delete_link(&phook->hi_queuejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_QUEUEJOB,
			&svr_queuejob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MODIFYJOB) {
		delete_link(&phook->hi_modifyjob_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYJOB,
			&svr_modifyjob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESVSUB) {
		delete_link(&phook->hi_resvsub_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESVSUB,
			&svr_resvsub_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MOVEJOB) {
		delete_link(&phook->hi_movejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_MOVEJOB,
			&svr_movejob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RUNJOB) {
		delete_link(&phook->hi_runjob_hooks);
		insert_hook_sort_order(HOOK_EVENT_RUNJOB,
			&svr_runjob_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_END) {
		delete_link(&phook->hi_resv_end_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESV_END,
			&svr_resv_end_hooks, phook);
        }

	if (phook->event & HOOK_EVENT_EXECJOB_BEGIN) {
		delete_link(&phook->hi_execjob_begin_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_BEGIN,
			&svr_execjob_begin_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PROLOGUE) {
		delete_link(&phook->hi_execjob_prologue_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PROLOGUE,
			&svr_execjob_prologue_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_EPILOGUE) {
		delete_link(&phook->hi_execjob_epilogue_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_EPILOGUE,
			&svr_execjob_epilogue_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_END) {
		delete_link(&phook->hi_execjob_end_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_END,
			&svr_execjob_end_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRETERM) {
		delete_link(&phook->hi_execjob_preterm_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRETERM,
			&svr_execjob_preterm_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_LAUNCH) {
		delete_link(&phook->hi_execjob_launch_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_LAUNCH,
			&svr_execjob_launch_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECHOST_PERIODIC) {
		delete_link(&phook->hi_exechost_periodic_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECHOST_PERIODIC,
			&svr_exechost_periodic_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECHOST_STARTUP) {
		delete_link(&phook->hi_exechost_startup_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECHOST_STARTUP,
			&svr_exechost_startup_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ATTACH) {
		delete_link(&phook->hi_execjob_attach_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_ATTACH,
			&svr_execjob_attach_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_RESIZE) {
		delete_link(&phook->hi_execjob_resize_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_RESIZE,
			&svr_execjob_resize_hooks, phook);
	}

	return (0);
}

/*
 *	Unsets 'phook's alarm value, resetting back to default.
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
int
unset_hook_alarm(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->alarm = HOOK_ALARM_DEFAULT;

	return (0);
}

/**
 * @brief
 *	Unsets 'phook's freq value, resetting back to default.
 *
 * @param[in/out]	phook - hook being operated on.
 * @param[in/out]	msg - error message buffer
 * @param[in]		msg_len - size of 'msg' buffer.
 *
 * @return int
 * @retval 0 for success
 * @retval 1 for failure with 'msg' of size 'msg_len' filled in.
 */
int
unset_hook_freq(hook *phook, char *msg, size_t msg_len)
{
	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if (phook == NULL) {
		snprintf(msg, msg_len-1,
			"%s: hook parameter is NULL", __func__);
		return (1);
	}

	phook->freq = HOOK_FREQ_DEFAULT;

	return (0);
}

/**
 *
 * @brief
 *	Initializes all the entries except the 'name' attribute of a hook.
 * @param[in/out] phook	- the hook in question
 * @param[in]	  pyfree_func - the "free" function to be used on a python
 *				script attribute.
 *
 */
static void
hook_init(hook *phook, void (*pyfree_func)(struct python_script *))
{

	phook->type = HOOK_TYPE_DEFAULT;
	phook->user = HOOK_USER_DEFAULT;
	phook->fail_action = HOOK_FAIL_ACTION_DEFAULT;
	phook->enabled = HOOK_ENABLED_DEFAULT;
	phook->debug = HOOK_DEBUG_DEFAULT;
	phook->event = HOOK_EVENT_DEFAULT;
	phook->order = HOOK_ORDER_DEFAULT;
	phook->alarm = HOOK_ALARM_DEFAULT;
	phook->freq = HOOK_FREQ_DEFAULT;
	phook->pending_delete = HOOK_PENDING_DELETE_DEFAULT;

	if (phook->script != NULL) {
		/* Free up also any Python objects that may have been */
		/* instantiated for the hook script */
		if (pyfree_func != NULL)
			pyfree_func(phook->script);
		free(phook->script);
	}
	phook->script = NULL;
	phook->hook_control_checksum = 0;
	phook->hook_script_checksum = 0;
	phook->hook_config_checksum = 0;
}

/**
 * @brief
 * 	Allocates space for a hook structure and initialize working
 *	attributes to "unset"
 *
 * @return hook *
 * @retval <pointer to hook>	pointer to structure
 * @retval NULL 		if malloc space not available.
 */
hook	*
hook_alloc(void)
{
	hook		*phook;

	phook = (hook *)malloc(sizeof(hook));
	if (phook == NULL) {
		log_err(errno, __func__, "no memory");
		return NULL;
	}
	(void)memset((char *)phook, (int)0, (size_t)sizeof(hook));

	phook->hook_name = NULL;

	hook_init(phook, NULL);

	clear_hook_links(phook);
	append_link(&svr_allhooks, &phook->hi_allhooks, phook);

	return (phook);
}

/**
 * @brief
 *	Frees the hook structure and its sub-structures.
 *
 * @param[in/out]  phook	- pointer to the hook structure.
 * @param[in] 	   pyfree_func - the special free function for the
 *				Python-related field.
 *			Ex. pbs_python_ext_free_python_script()
 *
 */
void
hook_free(hook *phook, void (*pyfree_func)(struct python_script *))
{
	if (phook->hook_name != NULL) {
		free(phook->hook_name);
	}
	phook->hook_name = NULL;
	hook_init(phook, pyfree_func);

	free(phook);	/* now free the main structure */
}

/**
 *
 * @brief
 *	Mark the given hook-related 'filename' as bad by renaming it
 *	with a HOOK_BAD_SUFFIX.
 *
 * @note
 *	This function expects filename with HOOK_FILE_SUFFIX,
 *	HOOK_SCRIPT_SUFFIX, HOOK_CONFIG_SUFFIX, or named PBS_RESCDEF.
 *
 */
void
mark_hook_file_bad(char *filename)
{
	char	bad_filename[MAXPATHLEN+1];
	char	*p;
	int	is_hook_cntrl_file = 0;
	int	is_hook_config_file = 0;
	int	is_hook_script_file = 0;

	if (filename == NULL)
		return;

	p = strstr(filename, HOOK_FILE_SUFFIX);
	if ((p != NULL) && (strcmp(p, HOOK_FILE_SUFFIX) == 0)) {
		is_hook_cntrl_file = 1;
	}
	if (!is_hook_cntrl_file) {
		p = strstr(filename, HOOK_SCRIPT_SUFFIX);
		if ((p != NULL) && (strcmp(p, HOOK_SCRIPT_SUFFIX) == 0))
			is_hook_script_file = 1;
	}

	if (!is_hook_cntrl_file && !is_hook_script_file) {
		p = strstr(filename, HOOK_CONFIG_SUFFIX);
		if ((p != NULL) && (strcmp(p, HOOK_CONFIG_SUFFIX) == 0))
			is_hook_config_file = 1;
	}

	if (!is_hook_cntrl_file && !is_hook_script_file &&
		!is_hook_config_file) {
		p = strstr(filename, PBS_RESCDEF);
		if ((p == NULL) || (strcmp(p, PBS_RESCDEF) != 0))
			/* not a recognized file, so not moving it */
			return;
	}

	snprintf(bad_filename, sizeof(bad_filename), "%s%s",
		filename, HOOK_BAD_SUFFIX);

#ifdef WIN32
	if (MoveFileEx(filename, bad_filename,
		MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0) {
		errno = GetLastError();
		snprintf(log_buffer, sizeof(log_buffer),
			"MoveFileEx(%s, %s) failed!", filename, bad_filename);
		log_err(errno, __func__, log_buffer);

	} else {
		snprintf(log_buffer, sizeof(log_buffer),
			"renamed hook-related file %s as %s", filename,
			bad_filename);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK,
			LOG_WARNING, __func__, log_buffer);
	}
	secure_file(bad_filename, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#else
	if (rename(filename, bad_filename) == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			"error renaming hook file %s", filename);
		log_err(errno, __func__, log_buffer);
	} else {
		snprintf(log_buffer, sizeof(log_buffer),
			"renamed hook-related file %s as %s", filename,
			bad_filename);
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK,
			LOG_WARNING, __func__, log_buffer);
	}
#endif
}

/**
 * @brief
 *	Purge hook from system.
 * 	The hook is dequeued; the hook control file, script file
 * 	are unlinked, and the hook structure is freed.
 * @note
 *	If the hook-related file fails to unlink, then it is moved out
 *	of the way (renamed via mark_hook_file_bad() call) so as not to
 *	get rediscovered by hook_recov().
 *
 * @param[in/out] phook	- pointer to the hook structure.
 * @param[in] 	  pyfree_func - the special free function for the Python-related
 *			field.
 *			Ex. pbs_python_ext_free_python_script()
 *
 */
void
hook_purge(hook	*phook, void (*pyfree_func)(struct python_script *))
{
	char		namebuf[MAXPATHLEN+1];

	if (phook == NULL) {
		log_err(PBSE_INTERNAL, __func__, "phook is NULL!");
		return;
	}

	clear_hook_links(phook);
	if (phook->hook_name == NULL) {
		/* hook_name should not be NULL, but if so, we already    */
		/* malloced hook structure so don't return here so as     */
		/* to catch hook_free() at the end.                       */
		log_err(PBSE_INTERNAL, __func__,
			"phook->hook_name is NULL!");
	}
	else {
		memset(namebuf, '\0', MAXPATHLEN+1);

		snprintf(namebuf, MAXPATHLEN, "%s%s%s", path_hooks,
			phook->hook_name, HOOK_CONFIG_SUFFIX);
		if ((phook->event & HOOK_EVENT_PERIODIC) && (phook->enabled == TRUE))
			delete_task_by_parm1(phook, DELETE_ALL);

#ifdef WIN32
		/* in case file permission got corrupted */
		secure_file(namebuf, "Administrators",
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif
		if (unlink(namebuf) < 0) {
			if (errno != ENOENT) {
				sprintf(log_buffer,
					"Failed to delete hook config file %s",
					namebuf);
				log_err(errno, __func__, log_buffer);
				mark_hook_file_bad(namebuf);

			}
		}

		snprintf(namebuf, MAXPATHLEN, "%s%s%s", path_hooks,
			phook->hook_name, HOOK_SCRIPT_SUFFIX);

#ifdef WIN32
		/* in case file permission got corrupted */
		secure_file(namebuf, "Administrators",
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif

		if (unlink(namebuf) < 0) {
			if (errno != ENOENT) {
				sprintf(log_buffer,
					"Failed to delete hook script %s",
					namebuf);
				log_err(errno, __func__, log_buffer);
				mark_hook_file_bad(namebuf);
			}
		}

		snprintf(namebuf, MAXPATHLEN, "%s%s%s", path_hooks,
			phook->hook_name, HOOK_FILE_SUFFIX);

#ifdef WIN32
		/* in case file permission got corrupted */
		secure_file(namebuf, "Administrators",
			READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif
		if (unlink(namebuf) < 0) {
			if (errno != ENOENT) {
				sprintf(log_buffer,
					"Failed to delete hook control file %s",
					namebuf);
				log_err(errno, __func__, log_buffer);
				mark_hook_file_bad(namebuf);

			}
		}
	}

	hook_free(phook, pyfree_func);
	return;
}

/**
 * @brief
 * 	Saves non-default attribute values into  the hook control file.
 *
 * @param[in/out]	phook - hook being operated on.
 *
 * @return int
 * @retval	0	for success
 * @retval	-1	for failure
 */
int
hook_save(hook *phook)
{
	char		hookfile[MAXPATHLEN+1];
	char		hookfile_new[MAXPATHLEN+1];
	FILE		*hkfp = NULL;

	if (phook == NULL) {
		log_err(PBSE_INTERNAL, __func__, "phook is NULL!");
		return (-1);
	}
	if (phook->hook_name == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"phook->hook_name is NULL!");
		return (-1);
	}

	memset(hookfile, '\0', MAXPATHLEN+1);
	memset(hookfile_new, '\0', MAXPATHLEN+1);
	snprintf(hookfile, MAXPATHLEN, "%s%s%s", path_hooks, phook->hook_name,
		HOOK_FILE_SUFFIX);
	snprintf(hookfile_new, MAXPATHLEN, "%s%s%s.new", path_hooks,
		phook->hook_name, HOOK_FILE_SUFFIX);

#ifdef WIN32
	fix_perms2(hookfile, hookfile_new);
#endif

	if ((hkfp = fopen(hookfile_new, "w")) == NULL) {
		log_event(PBSEVENT_ADMIN, PBS_EVENTCLASS_HOOK,
			LOG_WARNING, __func__,
			"Hook control file update failed!");
		return (-1);
	}

	fprintf(hkfp, "%s=%s\n", HOOKATT_NAME, phook->hook_name);

	/* Save only the non-defaults */
	if (phook->type != HOOK_TYPE_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_TYPE,
			hook_type_as_string(phook->type));

	if (phook->enabled != HOOK_ENABLED_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_ENABLED,
			hook_enabled_as_string(phook->enabled));

	if (phook->debug != HOOK_DEBUG_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_DEBUG,
			hook_debug_as_string(phook->debug));

	if (phook->user != HOOK_USER_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_USER,
			hook_user_as_string(phook->user));

	if (phook->fail_action != HOOK_FAIL_ACTION_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_FAIL_ACTION,
			hook_fail_action_as_string(phook->fail_action));

	if (phook->event != HOOK_EVENT_DEFAULT) {
		fprintf(hkfp, "%s=%s\n", HOOKATT_EVENT,
			hook_event_as_string(phook->event));
	}

	if (phook->order != HOOK_ORDER_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_ORDER,
			hook_order_as_string(phook->order));

	if (phook->alarm != HOOK_ALARM_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_ALARM,
			hook_alarm_as_string(phook->alarm));

	if (phook->freq != HOOK_FREQ_DEFAULT)
		fprintf(hkfp, "%s=%s\n", HOOKATT_FREQ,
			hook_freq_as_string(phook->freq));

	/* need to save on disk that the hook is pending to be deleted */
	if (phook->pending_delete != HOOK_PENDING_DELETE_DEFAULT) {
		fprintf(hkfp, "%s=%d\n", "pending_delete", phook->pending_delete);
	}

#ifdef WIN32
	if ((fflush(hkfp) != 0) ||
		(fclose(hkfp) != 0)) {
		sprintf(log_buffer, "Failed to flush/close hook file %s",
			hookfile_new);
		log_err(errno, __func__, log_buffer);
		return (-1);
	}

	if (MoveFileEx(hookfile_new, hookfile,
		MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0) {
		errno = GetLastError();
		sprintf(log_buffer, "MoveFileEx(%s, %s) failed! Deleting file.",
			hookfile_new, hookfile);
		log_err(errno, __func__, log_buffer);
		(void)unlink(hookfile_new);
		return (-1);
	}
	secure_file(hookfile, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#else
	if ((fflush(hkfp) != 0) ||
		(fsync(fileno(hkfp)) != 0) ||
		(fclose(hkfp) != 0)) {
		sprintf(log_buffer, "Failed to flush/close hook file %s",
			hookfile_new);
		log_err(errno, __func__, log_buffer);
		return (-1);
	}
	if (rename(hookfile_new, hookfile) < 0) {
		char *msgbuf;

		pbs_asprintf(&msgbuf, "rename(%s, %s) failed!", hookfile_new, hookfile);
		log_err(errno, __func__, msgbuf);
		free(msgbuf);
		(void)unlink(hookfile_new);
		return (-1);
	}
#endif
	phook->hook_control_checksum = crc_file(hookfile);

	return (0);
}

/*
 * find_hook() - find hook by hook_name
 *
 *	Search list of all server hooks for one with same hook name
 *	Return NULL if not found or pointer to hook struct if found
 */

hook *
find_hook(char *hook_name)
{
	hook  *phook;

	phook = (hook *)GET_NEXT(svr_allhooks);
	while (phook) {
		if (phook->hook_name &&
			(strcmp(hook_name, phook->hook_name) == 0)) {
			break;
		}
		phook = (hook *)GET_NEXT(phook->hi_allhooks);
	}
	return (phook);  /* may be a null pointer */
}


/**
 * @brief
 *	Locate a hook by its event.
 *
 * @par Functionality:
 *      This function locates a hook by its event number.
 *
 * @see
 *	add_hook_event
 *	set_srv_prov_attributes
 *	start_vnode_provisioning
 *	#hook in hook.h
 *	#HOOK_EVENT_PROVISION in hook.h
 *
 * @param[in]   hook_event           -       number identifying a hook event
 *
 * @return      poiner to hook
 * @retval       pointer to hook : if hook is found
 * @retval       NULL : if hook is not found
 *
 * @par Side Effects:
 *      Unknown
 *
 * @par MT-safe: No
 *
 */

hook *
find_hookbyevent(int hook_event)
{
	hook  *phook;

	phook = (hook *)GET_NEXT(svr_allhooks);
	while (phook) {
		if (phook->event & hook_event)
			break;
		phook = (hook *)GET_NEXT(phook->hi_allhooks);
	}
	DBPRT(("hook pointer is %p\n", (void *)phook))
	return (phook);  /* may be a null pointer */
}

/* The following are for encoding and decoding of a hook file in base 64 */
/* Much of the code has been taken from Python-2.5.1/Modules/binascii.c  */
/* as written by: Jack Jansen, CWI, July 1995.                           */

static char table_a2b_base64[] = {
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, /* Note PAD->0 */
	-1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9, 10, 11, 12, 13, 14,
	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
};


#define BASE64_PAD '='

static unsigned char table_b2a_base64[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int
find_valid_base64_char(unsigned char *s, ssize_t slen, int num)
{
	/* Finds & returns the (num+1)th
	 ** valid character for base64, or -1 if none.
	 */

	int ret = -1;
	unsigned char c, b64val;

	while ((slen > 0) && (ret == -1)) {
		c = *s;
		b64val = table_a2b_base64[c & 0x7f];
		if (((c <= 0x7f) && (b64val != (unsigned char)-1))) {
			if (num == 0)
				ret = *s;
			num--;
		}

		s++;
		slen--;
	}
	return ret;
}

/*
 *	Decodes a base64-encoded block of data (ascii_data, ascii_len), and
 *	store the result in (bin_data, p_bin_len).
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *		filled in.
 */
static int
decode_block_base64(unsigned char *ascii_data, ssize_t	ascii_len,
	unsigned char *bin_data, ssize_t *p_bin_len,
	char *msg, size_t msg_len)
{
	int leftbits = 0;
	unsigned char this_ch;
	unsigned int leftchar = 0;
	int quad_pos = 0;
	ssize_t		bin_len = 0;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	for (; ascii_len > 0; ascii_len--, ascii_data++) {
		this_ch = *ascii_data;
		if (this_ch > 0x7f ||
			this_ch == '\r' || this_ch == '\n' || this_ch == ' ')
			continue;

		/* Check for pad sequences and ignore
		 ** the invalid ones.
		 */
		if (this_ch == BASE64_PAD) {
			if ((quad_pos < 2) ||
				((quad_pos == 2) &&
				(find_valid_base64_char(ascii_data, ascii_len, 1)
				!= BASE64_PAD))) {
				continue;
			}
			else {
				/* A pad sequence means no more input.
				 ** We've already interpreted the data
				 ** from the quad at this point.
				 */
				leftbits = 0;
				break;
			}
		}

		this_ch = table_a2b_base64[*ascii_data];
		if (this_ch == (unsigned char) -1)
			continue;

		/*
		 ** Shift it in on the low end, and see if there's
		 ** a byte ready for output.
		 */
		quad_pos = (quad_pos + 1) & 0x03;
		leftchar = (leftchar << 6) | (this_ch);
		leftbits += 6;

		if (leftbits >= 8) {
			leftbits -= 8;
			*bin_data++ = (leftchar >> leftbits) & 0xff;
			bin_len++;
			leftchar &= ((1 << leftbits) - 1);
		}
	}

	if (leftbits != 0) {
		snprintf(msg, msg_len-1, "Incorrect padding");
		return (1);
	}

	/* And set string size correctly. If the result string is empty
	 ** (because the input was all invalid) return the shared empty
	 ** string instead.
	 */
	if (bin_len <= 0) {
		snprintf(msg, msg_len-1, "Unable to decode...bad input");
		return (1);
	}
	*p_bin_len = bin_len;

	return (0);

}

/*
 *	Encodes a non-encoded block of data (bin_data, bin_len), and store
 *	the result in (asci_data, p_ascii_len).
 *	RETURNS: 0 for success; 1 otherwise with 'msg' of size 'msg_len'
 *	filled in.
 */
static int
encode_block_base64(unsigned char *bin_data, ssize_t bin_len,
	unsigned char *ascii_data, ssize_t *p_ascii_len,
	char *msg, size_t msg_len)
{
	int leftbits = 0;
	unsigned char this_ch;
	unsigned int leftchar = 0;
	unsigned char *ascii_data_start;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	ascii_data_start = ascii_data;

	for (; bin_len > 0 ; bin_len--, bin_data++) {
		/* Shift the data into our buffer */
		leftchar = (leftchar << 8) | *bin_data;
		leftbits += 8;

		/* See if there are 6-bit groups ready */
		while (leftbits >= 6) {
			this_ch = (leftchar >> (leftbits-6)) & 0x3f;
			leftbits -= 6;
			*ascii_data++ = table_b2a_base64[this_ch];
		}
	}
	if (leftbits == 2) {
		*ascii_data++ = table_b2a_base64[(leftchar&3) << 4];
		*ascii_data++ = BASE64_PAD;
		*ascii_data++ = BASE64_PAD;
	} else if (leftbits == 4) {
		*ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2];
		*ascii_data++ = BASE64_PAD;
	}
	*ascii_data++ = '\n';	/* Append a courtesy newline */

	*p_ascii_len = (ssize_t)(ascii_data - ascii_data_start);

	return (0);
}

/**
 * @brief
 *	Encodes the contents of 'inline' file as 'content_encoding', and storing
 *      the result in 'outfile'
 *
 * @param[in]       inflie          - the content of this input file will be encoded
 * @param[in/out]   outfile         - the output file in which the encoded output is stored
 * @param[in]       conent_encoding - specifies the encoding
 * @param[in/out]   msg             - error message
 * @param[in]       msg_len         - specifiles length of error message
 *
 * @return int
 * @retval = 0	SUCCESS
 * @retval = 1	if not SUCCESS, then also sets 'msg' of size 'msg_len'
 */
int
encode_hook_content(char *infile, char *outfile, char *content_encoding,
	char *msg, size_t msg_len)
{
	FILE *infp = NULL;
	FILE *outfp = NULL;
	unsigned char in_data[HOOK_BUF_SIZE];
	unsigned char out_data[(HOOK_BUF_SIZE*2)+3];
	/* an upper bound that should work for */
	/* both encoded and decoded data */
	size_t nread;
	ssize_t in_len;
	ssize_t out_len;
	int	ret = 0;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if ((infile == NULL) || (outfile == NULL)) {
		snprintf(msg, msg_len-1, "no infile or outfile");
		return (1);
	}

	if (content_encoding == NULL) {
		snprintf(msg, msg_len-1, "no content_encoding");
		return (1);
	}
	outfp = fopen(outfile, "wb");
	if (outfp == NULL) {
		snprintf(msg, msg_len-1, "failed to open %s - error %s", outfile,
			strerror(errno));
		ret = 1;
		goto encode_hook_content_exit;
	}

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


	infp = fopen(infile, "rb");

	if (infp == NULL) {
		if (errno == ENOENT) {
			ret = 0;
		} else {

			snprintf(msg, msg_len-1,
				"failed to open %s - error %s", infile,
				strerror(errno));
			ret = 1;
		}

		goto encode_hook_content_exit;
	}

	while ((nread=fread(in_data, 1, HOOK_BUF_SIZE, infp)) > 0) {
		in_len = nread;
		if (strcmp(content_encoding, HOOKSTR_DEFAULT) == 0) {
			out_len = nread;
			memcpy((char *)out_data, (char *)in_data, in_len);
		} else if (strcmp(content_encoding, "base64") == 0) {
			if (encode_block_base64(in_data, in_len,
				out_data, &out_len, msg, msg_len) != 0) {
				ret = 1;
				goto encode_hook_content_exit;
			}
		} else {
			snprintf(msg, msg_len-1,
				"encountered bad content_encoding=%s",
				content_encoding);
			ret = 1;
			goto encode_hook_content_exit;
		}

		if (out_len > 0) {
			if (fwrite(out_data, 1, out_len, outfp) != out_len) {
				snprintf(msg, msg_len-1,
					"write to %s failed! Aborting...",
					outfile);
				ret = 1;
				goto encode_hook_content_exit;
			}
		}
	}

	if (fflush(outfp) != 0) {
		snprintf(msg, msg_len-1,
			"Failed to flush/close hook file %s (error %s)", outfile,
			strerror(errno));
		ret = 1;
	}

encode_hook_content_exit:
	if (infp != NULL)
		fclose(infp);
	if (outfp != NULL)
		fclose(outfp);

	if (ret != 0) {
		if (outfile)
			(void)unlink(outfile);
	}

	return (ret);

}

/**
 * @brief
 *	Decodes the contents of 'infile' encoded as 'content_encoding',
 *	and storing the result in 'outfile'.
 *
 * @param[in]   infile - the encoded input file
 * @param[in]   outfile - used to store the decoded output
 * @param[in]   content_encoding - specifies the encoding of 'infile'
 * @param[in]   msg - message in case unsuccessful
 * @param[in]   msg_len - length of message
 *
 * @return int
 * @retval  0	indicates SUCCESS
 * @retval  1   otherwise, setting 'msg' of size 'msg_len'
 */
int
decode_hook_content(char *infile, char *outfile, char *content_encoding,
	char *msg, size_t msg_len)
{
	FILE *infp = NULL;
	FILE *outfp;
	unsigned char in_data[HOOK_BUF_SIZE+1];
	unsigned char out_data[((HOOK_BUF_SIZE+1)*2)+3];
	/* an upper bound that should work for */
	/* both encoded and decoded data */
	int ret = 0;
	ssize_t in_len;
	ssize_t out_len = 0;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return (1);
	}
	memset(msg, '\0', msg_len);

	if ((infile == NULL) || (outfile == NULL)) {
		snprintf(msg, msg_len-1, "no infile or outfile");
		return (1);
	}

	if (content_encoding == NULL) {
		snprintf(msg, msg_len-1, "no content_encoding");
		return (1);
	}

	outfp = fopen(outfile, "wb");
	if (outfp == NULL) {
		snprintf(msg, msg_len-1,
			"failed to open %s - error %s", outfile,
			strerror(errno));
		ret = 1;
		goto decode_hook_content_exit;
	}

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

	infp = fopen(infile, "rb");

	if (infp == NULL) {
		if (errno == ENOENT) {
			ret = 0;	/* success since no file to decode */
		} else {

			snprintf(msg, msg_len-1,
				"failed to open %s - error %s", infile,
				strerror(errno));
			ret = 1;
		}
		goto decode_hook_content_exit;
	}

	while (fgets((char *)in_data, sizeof(in_data), infp) != NULL) {
		in_len = strlen((char *)in_data);
		if (strcmp(content_encoding, HOOKSTR_DEFAULT) == 0) {
			out_len = in_len;
			memcpy((char *)out_data, (char *)in_data, in_len);
		} else if (strcmp(content_encoding, "base64") == 0) {
			if (decode_block_base64(in_data, in_len,
				out_data, &out_len, msg, msg_len) != 0) {
				ret = 1;
				goto decode_hook_content_exit;
			}
			out_data[out_len] = '\0';
		} else {
			snprintf(msg, msg_len-1,
				"encountered bad content_encoding=%s",
				content_encoding);
			ret = 1;
			goto decode_hook_content_exit;
		}

		if (out_len > 0) {
			if (fwrite(out_data, 1, out_len, outfp) != out_len) {
				snprintf(msg, msg_len-1,
					"write to %s failed! Aborting...",
					outfile);
				ret = 1;
				goto decode_hook_content_exit;
			}
			out_len = 0;
		}
	}

	if (fflush(outfp) != 0) {
		snprintf(msg, msg_len-1,
			"Failed to flush/close hook file %s (error %s)", outfile,
			strerror(errno));
		ret = 1;
	}

decode_hook_content_exit:
	if (infp != NULL)
		fclose(infp);

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

	if (ret != 0) {
		if (outfile)
			(void)unlink(outfile);
	}
	return (ret);

}

/**
 * @brief
 * 	Prints all the attributes and their values of 'phook'
 *
 * @param[in]	phook - hook whoso values are being printed out.
 * @param[in]	heading - a string to print out before printing out values.
 */
void
print_hook(hook	*phook, char *heading)
{
	if (phook == NULL)
		return;

	snprintf(log_buffer, sizeof(log_buffer),
		"%s = {%s, %s=%d, %s=%d, %s=%d %s=%d, "
		"%s=(%d) %s=(%d), %s=(%s), %s=%d, %s=%d}",
		heading, phook->hook_name?phook->hook_name:"",
		HOOKATT_ORDER, phook->order,
		HOOKATT_TYPE, phook->type,
		HOOKATT_ENABLED, phook->enabled,
		HOOKATT_USER, phook->user,
		HOOKATT_DEBUG, phook->debug,
		HOOKATT_FAIL_ACTION, phook->fail_action,
		HOOKATT_EVENT, hook_event_as_string(phook->event),
		HOOKATT_ALARM, phook->alarm,
		HOOKATT_FREQ, phook->freq);
	log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_HOOK,
		LOG_INFO, __func__, log_buffer);

	snprintf(log_buffer, sizeof(log_buffer),
		"checksums: %s: hook_control_checksum=%lu hook_script_checksum=%lu hook_config_checksum=%lu",
		phook->hook_name?phook->hook_name:"",
		phook->hook_control_checksum,
		phook->hook_script_checksum,
		phook->hook_config_checksum);
	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK,
		LOG_INFO, __func__, log_buffer);
}

/**
 * @brief
 * 	Prints all the attributes and their values of all hooks appearing
 *	in the system hooks list representing 'event'.
 *
 * @param[in] event - the event whose hooks are being printed. If 'event' is 0,
 * 	means to print out the svr_allhooks list.
 */
void
print_hooks(unsigned int event)
{
	hook	*phook;
	int	i;
	pbs_list_head l_elem;
	char    ev_str[40];
	char	heading[80];

	if (event == HOOK_EVENT_QUEUEJOB) {
		l_elem = svr_queuejob_hooks;
		strcpy(ev_str, HOOKSTR_QUEUEJOB);
	} else if (event == HOOK_EVENT_MODIFYJOB) {
		l_elem = svr_modifyjob_hooks;
		strcpy(ev_str, HOOKSTR_MODIFYJOB);
	} else if (event == HOOK_EVENT_RESVSUB) {
		l_elem = svr_resvsub_hooks;
		strcpy(ev_str, HOOKSTR_RESVSUB);
	} else if (event == HOOK_EVENT_MOVEJOB) {
		l_elem = svr_movejob_hooks;
		strcpy(ev_str, HOOKSTR_MOVEJOB);
	} else if (event == HOOK_EVENT_RUNJOB) {
		l_elem = svr_runjob_hooks;
		strcpy(ev_str, HOOKSTR_RUNJOB);
	} else if (event == HOOK_EVENT_PERIODIC) {
		l_elem = svr_periodic_hooks;
		strcpy(ev_str, HOOKSTR_PERIODIC);
	} else if (event == HOOK_EVENT_PROVISION) {
		l_elem = svr_provision_hooks;
		strcpy(ev_str, HOOKSTR_PROVISION);
	} else if (event == HOOK_EVENT_RESV_END) {
		l_elem = svr_resv_end_hooks;
		strcpy(ev_str, HOOKSTR_RESV_END);
	} else if (event == HOOK_EVENT_EXECJOB_BEGIN) {
		l_elem = svr_execjob_begin_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_BEGIN);
	} else if (event == HOOK_EVENT_EXECJOB_PROLOGUE) {
		l_elem = svr_execjob_prologue_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_PROLOGUE);
	} else if (event == HOOK_EVENT_EXECJOB_EPILOGUE) {
		l_elem = svr_execjob_epilogue_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_EPILOGUE);
	} else if (event == HOOK_EVENT_EXECJOB_END) {
		l_elem = svr_execjob_end_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_END);
	} else if (event == HOOK_EVENT_EXECJOB_PRETERM) {
		l_elem = svr_execjob_preterm_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_PRETERM);
	} else if (event == HOOK_EVENT_EXECJOB_LAUNCH) {
		l_elem = svr_execjob_launch_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_LAUNCH);
	} else if (event == HOOK_EVENT_EXECHOST_PERIODIC) {
		l_elem = svr_exechost_periodic_hooks;
		strcpy(ev_str, HOOKSTR_EXECHOST_PERIODIC);
	} else if (event == HOOK_EVENT_EXECHOST_STARTUP) {
		l_elem = svr_exechost_startup_hooks;
		strcpy(ev_str, HOOKSTR_EXECHOST_STARTUP);
	} else if (event == HOOK_EVENT_EXECJOB_ATTACH) {
		l_elem = svr_execjob_attach_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_ATTACH);
	} else if (event == HOOK_EVENT_EXECJOB_RESIZE) {
		l_elem = svr_execjob_resize_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_RESIZE);
	} else {
		l_elem = svr_allhooks;
		strcpy(ev_str, "ALLHOOKS");
	}

	phook = (hook *)GET_NEXT(l_elem);
	i=0;
	while (phook) {

		sprintf(heading, "%s hook[%d]", ev_str,  i);
		print_hook(phook, heading);
		if (event == HOOK_EVENT_QUEUEJOB)
			phook = (hook *)GET_NEXT(phook->hi_queuejob_hooks);
		else if (event == HOOK_EVENT_MODIFYJOB)
			phook = (hook *)GET_NEXT(phook->hi_modifyjob_hooks);
		else if (event == HOOK_EVENT_RESVSUB)
			phook = (hook *)GET_NEXT(phook->hi_resvsub_hooks);
		else if (event == HOOK_EVENT_MOVEJOB)
			phook = (hook *)GET_NEXT(phook->hi_movejob_hooks);
		else if (event == HOOK_EVENT_RUNJOB)
			phook = (hook *)GET_NEXT(phook->hi_runjob_hooks);
		else if (event == HOOK_EVENT_PROVISION)
			phook = (hook *)GET_NEXT(phook->hi_provision_hooks);
		else if (event == HOOK_EVENT_PERIODIC)
			phook = (hook *)GET_NEXT(phook->hi_periodic_hooks);
		else if (event == HOOK_EVENT_RESV_END)
			phook = (hook *)GET_NEXT(phook->hi_resv_end_hooks);
		else if (event == HOOK_EVENT_EXECJOB_BEGIN)
			phook = (hook *)GET_NEXT(phook->hi_execjob_begin_hooks);
		else if (event == HOOK_EVENT_EXECJOB_PROLOGUE)
			phook = (hook *)GET_NEXT(phook->hi_execjob_prologue_hooks);
		else if (event == HOOK_EVENT_EXECJOB_EPILOGUE)
			phook = (hook *)GET_NEXT(phook->hi_execjob_epilogue_hooks);
		else if (event == HOOK_EVENT_EXECJOB_END)
			phook = (hook *)GET_NEXT(phook->hi_execjob_end_hooks);
		else if (event == HOOK_EVENT_EXECJOB_PRETERM)
			phook = (hook *)GET_NEXT(phook->hi_execjob_preterm_hooks);
		else if (event == HOOK_EVENT_EXECJOB_LAUNCH)
			phook = (hook *)GET_NEXT(phook->hi_execjob_launch_hooks);
		else if (event == HOOK_EVENT_EXECHOST_PERIODIC)
			phook = (hook *)GET_NEXT(phook->hi_exechost_periodic_hooks);
		else if (event == HOOK_EVENT_EXECHOST_STARTUP)
			phook = (hook *)GET_NEXT(phook->hi_exechost_startup_hooks);
		else if (event == HOOK_EVENT_EXECJOB_ATTACH)
			phook = (hook *)GET_NEXT(phook->hi_execjob_attach_hooks);
		else if (event == HOOK_EVENT_EXECJOB_RESIZE)
			phook = (hook *)GET_NEXT(phook->hi_execjob_resize_hooks);
		else
			phook = (hook *)GET_NEXT(phook->hi_allhooks);

		i++;

	}
}

/**
 * @brief
 * 	Reads a <filename> under global variable path_hooks,
 *		where <filename> must have the format: <basename>.HK,
 *		creating a hook structure out of the read data.
 *
 * @param[in]   filename - contains the hook data, where the filename must have
 *			 the format <basename>.HK. <basename> must match a
 *			"hook_name=<basename>" entry, which becomes the hook
 *			hook structure's hook_name value.
 * @param[in]	hookfp - If this is not NULL, then use this file`pointer to
 *			to get contents of hook file.
 * @param[in/out] msg	- buffer that is filled with error message, if any.
 * @param[in]	  msg_len - size of 'msg'.
 * @param[in] 	  pyalloc_func - the special function  that
 *			creates a bytecode reprsentation of <basename>.PY,
 *			the actual Python hook script.
 *			Ex.  pbs_python_ext_alloc_python_script()
 * @param[in] 	pyfree_func - the special free function for the Python-related
 *			field.
 *			Ex. pbs_python_ext_free_python_script()
 * @return	hook *
 * @retval	pointer to hook structure mapping the recovered hook.
 * @reval	NULL - for any error encountered.
 *
 */
hook *
hook_recov(char	*filename, FILE	*hookfp, char *msg, size_t msg_len,
	int (*pyalloc_func)(const char *, struct python_script **),
	void (*pyfree_func)(struct python_script *))
{
	hook	*phook;
	char	basename[MAXPATHLEN+1];
	char	*p;
	FILE	*fp  = NULL;
	char	linebuf[BUFSIZ];
	int	linenum;
	char	hook_script[MAXPATHLEN+1];
	char	hook_config[MAXPATHLEN+1];
	char	*hook_name;
	int	created_here = 0;

	if (msg == NULL) { /* should not happen */
		log_err(PBSE_INTERNAL, __func__, "'msg' buffer is NULL");
		return NULL;
	}
	memset(msg, '\0', msg_len);
	memset(basename, '\0', MAXPATHLEN+1);

	p = strstr(filename, HOOK_FILE_SUFFIX);
	if ((p == NULL) || (strcmp(p, HOOK_FILE_SUFFIX) != 0)) {
		snprintf(msg, msg_len-1,
			"bad filename %s format: should have %s suffix",
			filename, HOOK_FILE_SUFFIX);
		return NULL;
	}

	strncpy(basename, filename, p - filename);

	hook_name = basename;
	if ((p=strrchr(basename, '/')) != NULL) {
		hook_name = p+1;
	}

	/* Reuse hook entry if any */
	phook = find_hook(hook_name);
	if (phook != NULL) {
		hook_init(phook, pyfree_func);
		clear_hook_links(phook);
		append_link(&svr_allhooks, &phook->hi_allhooks, phook);
		created_here = 0;
	} else {
		phook = hook_alloc();
		if (phook == NULL) {
			snprintf(msg, msg_len-1, "hook_alloc() returned NULL!");
			return NULL;
		}
		created_here = 1;
		phook->hook_name = strdup(hook_name);

		if (phook->hook_name == NULL) {
			log_err(errno, __func__, "no memory");
			snprintf(msg, msg_len-1,
				"Hook name could not be determined!");
			goto hook_recov_error;
		}
	}


	if (strncmp(phook->hook_name, HOOK_PBS_PREFIX,
		strlen(HOOK_PBS_PREFIX)) == 0) {
		/* Actually initializing a PBS* prefix hook conrol file */
		phook->type = HOOK_PBS;
	}

#ifdef WIN32
	/* in case file's permission got corrupted */
	secure_file(filename, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif

	if (hookfp == NULL) {
		if ((fp = fopen(filename, "r")) == NULL) {
			sprintf(log_buffer, "%s", filename);
			log_err(errno, __func__, log_buffer);
			snprintf(msg, msg_len-1,
				"error %s opening file %s", strerror(errno), filename);
			goto hook_recov_error;
		}
	} else {
		fp = hookfp;
	}

	linenum = 1;
	while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
		char *p;
		char *pequal;

		char  *attname, *attval;

		if (linebuf[0] == '#') {
			continue;	/* ignore comment lines */
		}

		if ((p = strrchr(linebuf, '\n')) != NULL) {
			*p = '\0';
		} else {
			snprintf(msg, msg_len-1,
				"line %d is too long", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			goto hook_recov_error;
		}
		/* ignore initial white space;  skip blank lines */
		/*
		 *      Parse lines of the form
		 *
		 *	<attribute name>=<attribute value>
		 *
		 */
		p = linebuf;
		while ((*p != '\0') && isspace(*p))
			p++;

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

		if ((pequal = strchr(linebuf, '=')) == NULL) {
			snprintf(msg, msg_len-1,
				"line %d:  missing '='", linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			goto hook_recov_error;
		}

		if (*(pequal+1) == '\0') {
			snprintf(msg, msg_len-1,
				"line %d:  no <attribute value>",
				linenum);
			log_err(PBSE_SYSTEM, __func__, msg);
			goto hook_recov_error;
		}
		*pequal = '\0';
		attname = p;
		attval = pequal+1;

		if (strcmp(attname, HOOKATT_NAME) == 0  ) {
			if (strcmp(attval, hook_name) != 0) {
				snprintf(msg, msg_len-1,
					"failed integrity check - "
					"found %s=%s not match \'%s\'",
					HOOKATT_NAME, attval, hook_name);
				goto hook_recov_error;
			}
		} else if (strcmp(attname, HOOKATT_TYPE) == 0) {
			if (set_hook_type(phook, attval, msg, msg_len,
				1) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_USER) == 0) {
			if (set_hook_user(phook, attval, msg, msg_len, 0) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_FAIL_ACTION) == 0) {
			if (set_hook_fail_action(phook, attval, msg, msg_len, 0) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_ENABLED) == 0) {
			if (set_hook_enabled(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_DEBUG) == 0) {
			if (set_hook_debug(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_EVENT) == 0) {
			if (set_hook_event(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_ORDER) == 0) {
			if (set_hook_order(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_ALARM) == 0) {
			if (set_hook_alarm(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_FREQ) == 0) {
			if (set_hook_freq(phook, attval, msg, msg_len) != 0)
				goto hook_recov_error;
		} else if (strcmp(attname, HOOKATT_PENDING_DELETE) == 0) {
			phook->pending_delete = atoi(attval);
		} else {
			snprintf(msg, msg_len-1,
				"unknown attribute name \'%s\' in line %d",
				attname, linenum);
			goto hook_recov_error;
		}
		linenum++;
	}

	/* Better not fclose the passed file pointer (hookfp) since the caller */
	/* will be fclose-ing that outside. */
	if ((fp != NULL) && (fp != hookfp))
		(void)fclose(fp);

	phook->hook_control_checksum = crc_file(filename);

	/* now do checksum of hook config file (if any) */
	snprintf(hook_config, MAXPATHLEN, "%s%s%s", path_hooks,
		phook->hook_name, HOOK_CONFIG_SUFFIX);
	phook->hook_config_checksum = crc_file(hook_config);

	/* now set the hook script */
	snprintf(hook_script, MAXPATHLEN, "%s%s%s", path_hooks,
		phook->hook_name, HOOK_SCRIPT_SUFFIX);

	if (pyalloc_func != NULL) {
		if (pyalloc_func(hook_script,
			(struct python_script **)&phook->script) == -1) {
			snprintf(msg, msg_len-1,
				"failed to allocate storage for python script %s",
				hook_script);
			log_err(errno, __func__, msg);
		} else {
			phook->hook_script_checksum = crc_file(hook_script);
		}
	}

#ifdef WIN32
	/* in case file's permission got corrupted  - ok if none existent */
	secure_file(hook_script, "Administrators",
		READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
#endif

	return (phook);

hook_recov_error:
	/* Better not fclose the passed file pointer (hookfp) since the caller */
	/* will be fclose-ing that outside. */
	if ((fp != NULL) && (fp != hookfp))
		(void)fclose(fp);

	if (created_here) {
		clear_hook_links(phook);
		hook_free(phook, pyfree_func);
		return NULL;
	} else {
		/* reuse phook later */
		hook_init(phook, pyfree_func);
		clear_hook_links(phook);
		append_link(&svr_allhooks, &phook->hi_allhooks, phook);
		return NULL;
	}
}

/*
 ************************************************************************
 *   Hook-related Alarm operations.
 ************************************************************************
 */

#ifdef WIN32
#define	ALARM_HANDLER_ARG void
#else
#define	ALARM_HANDLER_ARG int sig
#endif

/**
 * @brief
 *	The ALARM signal handler.
 *
 * @param[in]   sig	- The signal received causing this function to get
 *			called.
 * @param[in] 	pyinter_func - the interrupt function that raised some signal
 *			to the calling process.
 *			Ex. pbs_python_set_interrupt() which sends an
 *			    an INT signal (ctrl-C)
 *
 */
void
catch_hook_alarm(ALARM_HANDLER_ARG)
{
	snprintf(log_buffer, LOG_BUF_SIZE-1,
		"alarm call received, interrupting hook execution.");
	log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_NOTICE, __func__,
		log_buffer);

	if (python_interrupt_func != NULL)
		python_interrupt_func();
}

/**
 * @brief
 *	The ALARM signal handler.
 *	Set or unset an alarm signal.
 *	If 'sec' > 0, then start an alarm of the given # of 'sec'; otherwise,
 *      if 'sec'is 0, then to stop the previous alarm.
 *
 *	Return: 0 for success; -1 otherwise.
 *
 * @param[in]   sec - # of seconds to alarm.
 * @param[in] 	pyinter_func - the interrupt function that raised some signal
 *			to the calling process.
 *			Ex. pbs_python_set_interrupt() which sends an
 *			    an INT signal (ctrl-C)
 * @return	int
 * @retval	0	for success.
 * @retval	-1 	otherwise.
 *
 */
int
set_alarm(int sec, void (*pyinter_func)(void))
{
#ifndef WIN32
	static struct sigaction act, oact;

	python_interrupt_func = pyinter_func;

	sigemptyset(&act.sa_mask);

	if (sec > 0) {
		/* set SIGALRM hander to catch_hook_alarm() */
		act.sa_flags = 0;
		act.sa_handler = catch_hook_alarm;
		if (sigaction(SIGALRM, &act, &oact) == -1) {
			log_event(PBSEVENT_ADMIN | PBSEVENT_SYSTEM,
				PBS_EVENTCLASS_HOOK, LOG_ERR, __func__,
				"Failed to install alarm");
			return (-1);
		}
		(void)alarm(sec);
	} else {
		(void)alarm(0);
		(void)sigaction(SIGALRM, &oact, NULL);
		/* reset handler for SIGALRM */
	}
#else	/* Windows */

	python_interrupt_func = pyinter_func;

	if (sec > 0)
		(void)win_alarm(sec, catch_hook_alarm);
	else
		(void)win_alarm(0, NULL);

#endif  /* end of Windows */
	return (0);
}

/**
 *
 * @brief
 * 	Cleans up files older than HOOKS_TMPFILE_MAX_AGE under
 *	<path_hooks_workdir> periodically as driven by the
 *	HOOKS_TMPFILE_NEXT_CLEANUP_PERIOD.
 *
 * @param[in]	ptask	- pointer to the task structure.
 *
 */
void
cleanup_hooks_workdir(struct work_task *ptask)
{
	DIR	*dir;
	struct 	dirent *pdirent;
	struct 	stat sbuf;
	char	hook_file[MAXPATHLEN+1];

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

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

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

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

	}
	if (errno != 0 && errno != ENOENT) {
		log_err(errno, __func__, "readdir");
	}
	if (dir) {
		(void)closedir(dir);
	}
	/*  cleanup of hooks temp files happen in the next */
	/* 'HOOKS_TMPFILE_NEXT_CLEANUP_PERIOD' secs.	   */
	(void)set_task(WORK_Timed, time_now+HOOKS_TMPFILE_NEXT_CLEANUP_PERIOD,
		cleanup_hooks_workdir, NULL);

}
