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

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

#include <unistd.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <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 "tpp.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_postqueuejob_hooks;
extern pbs_list_head svr_modifyjob_hooks;
extern pbs_list_head svr_resvsub_hooks;
extern pbs_list_head svr_modifyresv_hooks;
extern pbs_list_head svr_movejob_hooks;
extern pbs_list_head svr_runjob_hooks;
extern pbs_list_head svr_jobobit_hooks;
extern pbs_list_head svr_management_hooks;
extern pbs_list_head svr_modifyvnode_hooks;
extern pbs_list_head svr_provision_hooks;
extern pbs_list_head svr_periodic_hooks;
extern pbs_list_head svr_resv_confirm_hooks;
extern pbs_list_head svr_resv_begin_hooks;
extern pbs_list_head svr_resv_end_hooks;
extern pbs_list_head svr_execjob_begin_hooks;
extern pbs_list_head svr_execjob_prologue_hooks;
extern pbs_list_head svr_execjob_epilogue_hooks;
extern pbs_list_head svr_execjob_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;
extern pbs_list_head svr_execjob_abort_hooks;
extern pbs_list_head svr_execjob_postsuspend_hooks;
extern pbs_list_head svr_execjob_preresume_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_postqueuejob_hooks);
	delete_link(&phook->hi_modifyjob_hooks);
	delete_link(&phook->hi_resvsub_hooks);
	delete_link(&phook->hi_modifyresv_hooks);
	delete_link(&phook->hi_movejob_hooks);
	delete_link(&phook->hi_runjob_hooks);
	delete_link(&phook->hi_jobobit_hooks);
	delete_link(&phook->hi_provision_hooks);
	delete_link(&phook->hi_periodic_hooks);
	delete_link(&phook->hi_resv_confirm_hooks);
	delete_link(&phook->hi_resv_begin_hooks);
	delete_link(&phook->hi_resv_end_hooks);
	delete_link(&phook->hi_allhooks);
	delete_link(&phook->hi_management_hooks);
	delete_link(&phook->hi_modifyvnode_hooks);

	/* 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);
	delete_link(&phook->hi_execjob_abort_hooks);
	delete_link(&phook->hi_execjob_postsuspend_hooks);
	delete_link(&phook->hi_execjob_preresume_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_POSTQUEUEJOB) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_POSTQUEUEJOB, sizeof(eventstr) - strlen(eventstr) - 1);
		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_MODIFYRESV) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_MODIFYRESV, 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_JOBOBIT) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_JOBOBIT, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

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

	if (event & HOOK_EVENT_MODIFYVNODE) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_MODIFYVNODE, 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_CONFIRM) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_RESV_CONFIRM, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

	if (event & HOOK_EVENT_RESV_BEGIN) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_RESV_BEGIN, 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_EXECJOB_ABORT) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_ABORT, sizeof(eventstr) - strlen(eventstr) - 1);
		ev_ct++;
	}

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

	if (event & HOOK_EVENT_EXECJOB_PRERESUME) {
		if (ev_ct > 0)
			strncat(eventstr, ",", sizeof(eventstr) - strlen(eventstr) - 1);
		strncat(eventstr, HOOKSTR_EXECJOB_PRERESUME, 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_POSTQUEUEJOB) == 0)
		return HOOK_EVENT_POSTQUEUEJOB;
	if (strcmp(eventstr, HOOKSTR_MODIFYJOB) == 0)
		return HOOK_EVENT_MODIFYJOB;
	if (strcmp(eventstr, HOOKSTR_RESVSUB) == 0)
		return HOOK_EVENT_RESVSUB;
	if (strcmp(eventstr, HOOKSTR_MODIFYRESV) == 0)
		return HOOK_EVENT_MODIFYRESV;
	if (strcmp(eventstr, HOOKSTR_MOVEJOB) == 0)
		return HOOK_EVENT_MOVEJOB;
	if (strcmp(eventstr, HOOKSTR_RUNJOB) == 0)
		return HOOK_EVENT_RUNJOB;
	if (strcmp(eventstr, HOOKSTR_JOBOBIT) == 0)
		return HOOK_EVENT_JOBOBIT;
	if (strcmp(eventstr, HOOKSTR_MANAGEMENT) == 0)
		return HOOK_EVENT_MANAGEMENT;
	if (strcmp(eventstr, HOOKSTR_MODIFYVNODE) == 0)
		return HOOK_EVENT_MODIFYVNODE;
	if (strcmp(eventstr, HOOKSTR_PROVISION) == 0)
		return HOOK_EVENT_PROVISION;
	if (strcmp(eventstr, HOOKSTR_RESV_CONFIRM) == 0)
		return HOOK_EVENT_RESV_CONFIRM;
	if (strcmp(eventstr, HOOKSTR_RESV_BEGIN) == 0)
		return HOOK_EVENT_RESV_BEGIN;
	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;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_ABORT) == 0)
		return HOOK_EVENT_EXECJOB_ABORT;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_POSTSUSPEND) == 0)
		return HOOK_EVENT_EXECJOB_POSTSUSPEND;
	if (strcmp(eventstr, HOOKSTR_EXECJOB_PRERESUME) == 0)
		return HOOK_EVENT_EXECJOB_PRERESUME;

	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_POSTQUEUEJOB) {
		plink_elem = &phook->hi_postqueuejob_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_MODIFYRESV) {
		plink_elem = &phook->hi_modifyresv_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_JOBOBIT) {
		plink_elem = &phook->hi_jobobit_hooks;
	} else if (event == HOOK_EVENT_MANAGEMENT) {
		plink_elem = &phook->hi_management_hooks;
	} else if (event == HOOK_EVENT_MODIFYVNODE) {
		plink_elem = &phook->hi_modifyvnode_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_CONFIRM) {
		plink_elem = &phook->hi_resv_confirm_hooks;
	} else if (event == HOOK_EVENT_RESV_BEGIN) {
		plink_elem = &phook->hi_resv_begin_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 if (event == HOOK_EVENT_EXECJOB_ABORT) {
		plink_elem = &phook->hi_execjob_abort_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_POSTSUSPEND) {
		plink_elem = &phook->hi_execjob_postsuspend_hooks;
	} else if (event == HOOK_EVENT_EXECJOB_PRERESUME) {
		plink_elem = &phook->hi_execjob_preresume_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_POSTQUEUEJOB) {
			plink_cur = &phook_cur->hi_postqueuejob_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_MODIFYRESV) {
			plink_cur = &phook_cur->hi_modifyresv_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_JOBOBIT) {
			plink_cur = &phook_cur->hi_jobobit_hooks;
		} else if (event == HOOK_EVENT_MANAGEMENT) {
			plink_cur = &phook_cur->hi_management_hooks;
		} else if (event == HOOK_EVENT_MODIFYVNODE) {
			plink_cur = &phook_cur->hi_modifyvnode_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_CONFIRM) {
			plink_cur = &phook_cur->hi_resv_confirm_hooks;
		} else if (event == HOOK_EVENT_RESV_BEGIN) {
			plink_cur = &phook_cur->hi_resv_begin_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 if (event == HOOK_EVENT_EXECJOB_ABORT) {
			plink_cur = &phook_cur->hi_execjob_abort_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_POSTSUSPEND) {
			plink_cur = &phook_cur->hi_execjob_postsuspend_hooks;
		} else if (event == HOOK_EVENT_EXECJOB_PRERESUME) {
			plink_cur = &phook_cur->hi_execjob_preresume_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_postqueuejob_hooks);
		delete_link(&phook->hi_modifyjob_hooks);
		delete_link(&phook->hi_modifyvnode_hooks);
		delete_link(&phook->hi_management_hooks);
		delete_link(&phook->hi_resvsub_hooks);
		delete_link(&phook->hi_modifyresv_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_confirm_hooks);
		delete_link(&phook->hi_resv_begin_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);
		delete_link(&phook->hi_execjob_abort_hooks);
		delete_link(&phook->hi_execjob_postsuspend_hooks);
		delete_link(&phook->hi_execjob_preresume_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_POSTQUEUEJOB) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_postqueuejob_hooks);
			phook->event |= HOOK_EVENT_POSTQUEUEJOB;
			insert_hook_sort_order(HOOK_EVENT_POSTQUEUEJOB,
					       &svr_postqueuejob_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_MODIFYRESV) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_modifyresv_hooks);
			phook->event |= HOOK_EVENT_MODIFYRESV;
			insert_hook_sort_order(HOOK_EVENT_MODIFYRESV,
					       &svr_modifyresv_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_JOBOBIT) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_jobobit_hooks);
			phook->event |= HOOK_EVENT_JOBOBIT;
			insert_hook_sort_order(HOOK_EVENT_JOBOBIT,
					       &svr_jobobit_hooks, phook);
		} else if (strcmp(val, HOOKSTR_MANAGEMENT) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_management_hooks);
			phook->event |= HOOK_EVENT_MANAGEMENT;
			insert_hook_sort_order(HOOK_EVENT_MANAGEMENT,
					       &svr_management_hooks, phook);
		} else if (strcmp(val, HOOKSTR_MODIFYVNODE) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_modifyvnode_hooks);
			phook->event |= HOOK_EVENT_MODIFYVNODE;
			insert_hook_sort_order(HOOK_EVENT_MODIFYVNODE,
					       &svr_modifyvnode_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_CONFIRM) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_resv_confirm_hooks);
			phook->event |= HOOK_EVENT_RESV_CONFIRM;
			insert_hook_sort_order(HOOK_EVENT_RESV_CONFIRM,
					       &svr_resv_confirm_hooks, phook);
		} else if (strcmp(val, HOOKSTR_RESV_BEGIN) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_resv_begin_hooks);
			phook->event |= HOOK_EVENT_RESV_BEGIN;
			insert_hook_sort_order(HOOK_EVENT_RESV_BEGIN,
					       &svr_resv_begin_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_EXECJOB_ABORT) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_abort_hooks);
			phook->event |= HOOK_EVENT_EXECJOB_ABORT;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_ABORT,
					       &svr_execjob_abort_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_POSTSUSPEND) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_postsuspend_hooks);
			phook->event |= HOOK_EVENT_EXECJOB_POSTSUSPEND;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_POSTSUSPEND,
					       &svr_execjob_postsuspend_hooks, phook);
		} else if (strcmp(val, HOOKSTR_EXECJOB_PRERESUME) == 0) {
			if (phook->event & HOOK_EVENT_PROVISION)
				goto err;
			delete_link(&phook->hi_execjob_preresume_hooks);
			phook->event |= HOOK_EVENT_EXECJOB_PRERESUME;
			insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRERESUME,
					       &svr_execjob_preresume_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,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s "
				 "or %s for no event",
				 newval, HOOKSTR_QUEUEJOB, HOOKSTR_POSTQUEUEJOB, HOOKSTR_MODIFYJOB, HOOKSTR_MODIFYVNODE, HOOKSTR_MANAGEMENT,
				 HOOKSTR_RESVSUB, HOOKSTR_MODIFYRESV, HOOKSTR_MOVEJOB, HOOKSTR_JOBOBIT,
				 HOOKSTR_RUNJOB, HOOKSTR_PROVISION, HOOKSTR_PERIODIC, HOOKSTR_RESV_CONFIRM,
				 HOOKSTR_RESV_BEGIN, 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_EXECJOB_ABORT, HOOKSTR_EXECJOB_POSTSUSPEND, HOOKSTR_EXECJOB_PRERESUME, 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_POSTQUEUEJOB) == 0) {
			delete_link(&phook->hi_postqueuejob_hooks);
			phook->event &= ~HOOK_EVENT_POSTQUEUEJOB;
		} 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_MODIFYRESV) == 0) {
			delete_link(&phook->hi_modifyresv_hooks);
			phook->event &= ~HOOK_EVENT_MODIFYRESV;
		} 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_JOBOBIT) == 0) {
			delete_link(&phook->hi_jobobit_hooks);
			phook->event &= ~HOOK_EVENT_JOBOBIT;
		} else if (strcmp(val, HOOKSTR_MANAGEMENT) == 0) {
			delete_link(&phook->hi_management_hooks);
			phook->event &= ~HOOK_EVENT_MANAGEMENT;
		} else if (strcmp(val, HOOKSTR_MODIFYVNODE) == 0) {
			delete_link(&phook->hi_modifyvnode_hooks);
			phook->event &= ~HOOK_EVENT_MODIFYVNODE;
		} 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_func(phook, NULL, DELETE_ALL);
		} else if (strcmp(val, HOOKSTR_RESV_CONFIRM) == 0) {
			delete_link(&phook->hi_resv_confirm_hooks);
			phook->event &= ~HOOK_EVENT_RESV_CONFIRM;
		} else if (strcmp(val, HOOKSTR_RESV_BEGIN) == 0) {
			delete_link(&phook->hi_resv_begin_hooks);
			phook->event &= ~HOOK_EVENT_RESV_BEGIN;
		} 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_EXECJOB_ABORT) == 0) {
			delete_link(&phook->hi_execjob_abort_hooks);
			phook->event &= ~HOOK_EVENT_EXECJOB_ABORT;
		} else if (strcmp(val, HOOKSTR_EXECJOB_POSTSUSPEND) == 0) {
			delete_link(&phook->hi_execjob_postsuspend_hooks);
			phook->event &= ~HOOK_EVENT_EXECJOB_POSTSUSPEND;
		} else if (strcmp(val, HOOKSTR_EXECJOB_PRERESUME) == 0) {
			delete_link(&phook->hi_execjob_preresume_hooks);
			phook->event &= ~HOOK_EVENT_EXECJOB_PRERESUME;
		} 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,%s,%s,%s,%s,%s,%s,%s,%s,%s "
				 "or %s for no event.",
				 newval, HOOKSTR_QUEUEJOB, HOOKSTR_POSTQUEUEJOB, HOOKSTR_MODIFYJOB, HOOKSTR_MODIFYVNODE, HOOKSTR_MANAGEMENT,
				 HOOKSTR_RESVSUB, HOOKSTR_MODIFYRESV, HOOKSTR_MOVEJOB,
				 HOOKSTR_RUNJOB, HOOKSTR_PERIODIC, HOOKSTR_PROVISION, HOOKSTR_RESV_CONFIRM,
				 HOOKSTR_RESV_BEGIN, 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_EXECJOB_ABORT, HOOKSTR_EXECJOB_POSTSUSPEND, HOOKSTR_EXECJOB_PRERESUME, 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_POSTQUEUEJOB) {
		delete_link(&phook->hi_postqueuejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_POSTQUEUEJOB,
				       &svr_postqueuejob_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_MODIFYRESV) {
		delete_link(&phook->hi_modifyresv_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYRESV,
				       &svr_modifyresv_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_JOBOBIT) {
		delete_link(&phook->hi_jobobit_hooks);
		insert_hook_sort_order(HOOK_EVENT_JOBOBIT,
				       &svr_jobobit_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MANAGEMENT) {
		delete_link(&phook->hi_management_hooks);
		insert_hook_sort_order(HOOK_EVENT_MANAGEMENT,
				       &svr_management_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MODIFYVNODE) {
		delete_link(&phook->hi_modifyvnode_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYVNODE,
				       &svr_modifyvnode_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_BEGIN) {
		delete_link(&phook->hi_resv_begin_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESV_BEGIN,
				       &svr_resv_begin_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_CONFIRM) {
		delete_link(&phook->hi_resv_confirm_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESV_CONFIRM,
				       &svr_resv_confirm_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);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ABORT) {
		delete_link(&phook->hi_execjob_abort_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_ABORT,
				       &svr_execjob_abort_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_POSTSUSPEND) {
		delete_link(&phook->hi_execjob_postsuspend_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_POSTSUSPEND,
				       &svr_execjob_postsuspend_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRERESUME) {
		delete_link(&phook->hi_execjob_preresume_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRERESUME,
				       &svr_execjob_preresume_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_POSTQUEUEJOB)
		delete_link(&phook->hi_postqueuejob_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_MODIFYRESV)
		delete_link(&phook->hi_modifyresv_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_JOBOBIT)
		delete_link(&phook->hi_jobobit_hooks);

	if (phook->event & HOOK_EVENT_MANAGEMENT)
		delete_link(&phook->hi_management_hooks);

	if (phook->event & HOOK_EVENT_MODIFYVNODE)
		delete_link(&phook->hi_modifyvnode_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_CONFIRM)
		delete_link(&phook->hi_resv_confirm_hooks);

	if (phook->event & HOOK_EVENT_RESV_BEGIN)
		delete_link(&phook->hi_resv_begin_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);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ABORT) {
		delete_link(&phook->hi_execjob_abort_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_POSTSUSPEND) {
		delete_link(&phook->hi_execjob_postsuspend_hooks);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRERESUME) {
		delete_link(&phook->hi_execjob_preresume_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_POSTQUEUEJOB) {
		delete_link(&phook->hi_postqueuejob_hooks);
		insert_hook_sort_order(HOOK_EVENT_POSTQUEUEJOB,
				       &svr_postqueuejob_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_MODIFYRESV) {
		delete_link(&phook->hi_modifyresv_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYRESV,
				       &svr_modifyresv_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_JOBOBIT) {
		delete_link(&phook->hi_jobobit_hooks);
		insert_hook_sort_order(HOOK_EVENT_JOBOBIT,
				       &svr_jobobit_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MANAGEMENT) {
		delete_link(&phook->hi_management_hooks);
		insert_hook_sort_order(HOOK_EVENT_MANAGEMENT,
				       &svr_management_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_MODIFYVNODE) {
		delete_link(&phook->hi_modifyvnode_hooks);
		insert_hook_sort_order(HOOK_EVENT_MODIFYVNODE,
				       &svr_modifyvnode_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_CONFIRM) {
		delete_link(&phook->hi_resv_confirm_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESV_CONFIRM,
				       &svr_resv_confirm_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_RESV_BEGIN) {
		delete_link(&phook->hi_resv_begin_hooks);
		insert_hook_sort_order(HOOK_EVENT_RESV_BEGIN,
				       &svr_resv_begin_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);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_ABORT) {
		delete_link(&phook->hi_execjob_abort_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_ABORT,
				       &svr_execjob_abort_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_POSTSUSPEND) {
		delete_link(&phook->hi_execjob_postsuspend_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_POSTSUSPEND,
				       &svr_execjob_postsuspend_hooks, phook);
	}

	if (phook->event & HOOK_EVENT_EXECJOB_PRERESUME) {
		delete_link(&phook->hi_execjob_preresume_hooks);
		insert_hook_sort_order(HOOK_EVENT_EXECJOB_PRERESUME,
				       &svr_execjob_preresume_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_func(phook, NULL, 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.
 */
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_POSTQUEUEJOB) {
		l_elem = svr_postqueuejob_hooks;
		strcpy(ev_str, HOOKSTR_POSTQUEUEJOB);
	} 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_MODIFYRESV) {
		l_elem = svr_modifyresv_hooks;
		strcpy(ev_str, HOOKSTR_MODIFYRESV);
	} 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_JOBOBIT) {
		l_elem = svr_jobobit_hooks;
		strcpy(ev_str, HOOKSTR_JOBOBIT);
	} else if (event == HOOK_EVENT_MANAGEMENT) {
		l_elem = svr_management_hooks;
		strcpy(ev_str, HOOKSTR_MANAGEMENT);
	} else if (event == HOOK_EVENT_MODIFYVNODE) {
		l_elem = svr_modifyvnode_hooks;
		strcpy(ev_str, HOOKSTR_MODIFYVNODE);
	} 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_CONFIRM) {
		l_elem = svr_resv_confirm_hooks;
		strcpy(ev_str, HOOKSTR_RESV_CONFIRM);
	} else if (event == HOOK_EVENT_RESV_BEGIN) {
		l_elem = svr_resv_begin_hooks;
		strcpy(ev_str, HOOKSTR_RESV_BEGIN);
	} 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 if (event == HOOK_EVENT_EXECJOB_ABORT) {
		l_elem = svr_execjob_abort_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_ABORT);
	} else if (event == HOOK_EVENT_EXECJOB_POSTSUSPEND) {
		l_elem = svr_execjob_postsuspend_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_POSTSUSPEND);
	} else if (event == HOOK_EVENT_EXECJOB_PRERESUME) {
		l_elem = svr_execjob_preresume_hooks;
		strcpy(ev_str, HOOKSTR_EXECJOB_PRERESUME);
	} 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_POSTQUEUEJOB)
			phook = (hook *) GET_NEXT(phook->hi_postqueuejob_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_MODIFYRESV)
			phook = (hook *) GET_NEXT(phook->hi_modifyresv_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_JOBOBIT)
			phook = (hook *) GET_NEXT(phook->hi_jobobit_hooks);
		else if (event == HOOK_EVENT_MANAGEMENT)
			phook = (hook *) GET_NEXT(phook->hi_management_hooks);
		else if (event == HOOK_EVENT_MODIFYVNODE)
			phook = (hook *) GET_NEXT(phook->hi_modifyvnode_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_CONFIRM)
			phook = (hook *) GET_NEXT(phook->hi_resv_confirm_hooks);
		else if (event == HOOK_EVENT_RESV_BEGIN)
			phook = (hook *) GET_NEXT(phook->hi_resv_begin_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 if (event == HOOK_EVENT_EXECJOB_ABORT)
			phook = (hook *) GET_NEXT(phook->hi_execjob_abort_hooks);
		else if (event == HOOK_EVENT_EXECJOB_POSTSUSPEND)
			phook = (hook *) GET_NEXT(phook->hi_execjob_postsuspend_hooks);
		else if (event == HOOK_EVENT_EXECJOB_PRERESUME)
			phook = (hook *) GET_NEXT(phook->hi_execjob_preresume_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);
}

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

	switch (hook_event) {

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

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

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

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

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

		num_hooks++;
	}

	return (num_hooks);
}

/**
 *
 * @brief
 * 	Start profiling the next lines of code, collectively giving it
 *      a 'label' and an 'action' description.
 *	The 'label' usually identifies to some object being profiled
 *	(e.g. hook), while the 'action' describes what
 *	is being captured for the object (e.g. "initialization").
 *	Both 'label' and 'action' uniquely identify the
 *	data collected.
 *
 * @param[in]	label - describes a particular object
 * @param[in]	action - refers to the object's action
 *
 * @param[in]	print_start_msg - if 1, then log a "profile_start" message.
 *
 * @return void
 *
 */
void
hook_perf_stat_start(char *label, char *action, int print_start_msg)
{
	char instance[MAXBUFLEN];

	if (!will_log_event(PBSEVENT_DEBUG4))
		return;

	if ((label == NULL) || (action == NULL))
		return;

	snprintf(instance, sizeof(instance), "label=%s action=%s", label, action);
	perf_stat_start(instance);

	if (print_start_msg) {
		snprintf(log_buffer, sizeof(log_buffer), "%s profile_start", instance);
		log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_HOOK, LOG_INFO, "hook_perf_stat", log_buffer);
	}
}

/**
 *
 * @brief
 * 	Log summary of what has been tracked since hook_perf_stat_start()
 *	call on the same label, action given.
 *
 * @param[in]	label - refers to a particular object
 * @param[in]	action - refers to the object's action
 *
 * @param[in]	print_end_msg - if 1, then mark a "profile_stop" message.
 *
 * @return void
 *
 */
void
hook_perf_stat_stop(char *label, char *action, int print_end_msg)
{
	char instance[MAXBUFLEN];
	char *msg;

	if ((label == NULL) || (action == NULL))
		return;

	snprintf(instance, sizeof(instance), "label=%s action=%s", label, action);

	if (!will_log_event(PBSEVENT_DEBUG4)) {
		perf_stat_remove(instance);
		return;
	}

	msg = perf_stat_stop(instance);

	if (msg == NULL)
		return;

	if (print_end_msg)
		snprintf(log_buffer, sizeof(log_buffer), "%s profile_stop", msg);
	else
		snprintf(log_buffer, sizeof(log_buffer), "%s", msg);

	log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_HOOK, LOG_INFO, "hook_perf_stat", log_buffer);
}
