/*
 * Copyright (C) 1994-2018 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * PBS Pro is free software. You can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * For a copy of the commercial license terms and conditions,
 * go to: (http://www.pbspro.com/UserArea/agreement.html)
 * or contact the Altair Legal Department.
 *
 * Altair’s dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of PBS Pro and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair’s trademarks, including but not limited to "PBS™",
 * "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
 * trademark licensing policies.
 *
 */
/**
 * @file	job_func.c
 * Functions which provide basic operation on the job structure
 *
 * Included public functions are:
 *
 *   job_abt	  				- abort (remove from server) a job
 *   job_alloc   				- allocate job struct and initialize defaults
 *   job_free	  				- free space allocated to the job structure and its
 *		  							childern structures.
 *   job_purge	  				- purge job from server
 *   tasks_free
 *   job_init_wattr				- initialize job working attribute array set the types and the "unspecified value" flag
 *   find_job					- find job by jobid
 *   write_cred					- Output credential into job file.
 *	 read_cred					- Check if this job has an associated credential file.  If it does,
 *	 								the credential file is opened and the credential is read into malloc'ed memory.
 *	 decode_sandbox				- decode sandbox into string attribute
 *	 decode_project				- Decode project into string attribute.
 *	 resc_resv_alloc			- functons for Reservation (resc_resv) structures
 *	 resv_free					- This just frees	any hanging substructures, deletes any attached work_tasks
 *	 								and frees the resc_resv	structure itself.
 *	 find_resv					- find resc_resv struct by reservation ID
 *	 resv_purge					- purge reservation from system
 *	 post_resv_purge			- handles the return reply from an internally generated request.
 *	 resv_abt					- abort a reservation
 *	 job_or_resv_init_wattr		- initialize job (resc_resv) working attribute array
 *	 add_resc_resv_to_job		- adds a resc_resv structure to a job if the job is a "reservation job"
 *	 add_resc_resv_if_resvJob	- Checks to see if the job is a reservation job
 *	 								and if so attempts to attach a "resc_resv" structure to the job.
 *	 set_resvAttrs_off_jobAttrs - set reservation attributes off those associated with the "resevation job"
 *	 resv_exclusive_handler     - Set node state to resv-exclusive
 *	 find_aoe_from_request      - Find aoe from the reservation request
 *	 spool_filename             - creates stdout/err file name in the spool area.
 *	 remove_stdouter_files      - remove stdout/err files from the spool directory
 *	 direct_write_requested     - checks whether direct_write is requested by the job.
 *
 * Include private function:
 *   job_init_wattr() initialize job working attribute array to "unspecified"
 */

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

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

#ifndef SIGKILL
#include <signal.h>
#endif
#include <memory.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pbs_ifl.h"
#include "libpbs.h"
#include "list_link.h"
#include "work_task.h"
#include "attribute.h"
#include "resource.h"
#include "server_limits.h"
#include "server.h"
#include "resv_node.h"
#include "queue.h"
#include "sched_cmds.h"
#include "pbs_sched.h"

#ifdef WIN32
#include <direct.h>
#include <windows.h>
#include "win.h"
#endif

#include "job.h"
#include "reservation.h"
#include "pbs_nodes.h"
#include "log.h"
#include "pbs_error.h"
#include "batch_request.h"
#include "pbs_entlim.h"

#ifndef PBS_MOM
#include "avltree.h"
#endif

#include "svrfunc.h"
#include "acct.h"
#include "credential.h"
#include "net_connect.h"
#include "pbs_reliable.h"


/* External functions */

#ifdef WIN32
extern int read_cred(job *pjob, char **cred, size_t *len);
#endif

void on_job_exit(struct work_task *);

/* Local Private Functions */

static void job_init_wattr(job *);

#ifndef PBS_MOM		/*SERVER ONLY*/
static void job_or_resv_init_wattr(void*, int);
static void post_resv_purge(struct work_task *pwt);
static int  set_resvAttrs_off_jobAttrs(resc_resv*, job*);
#endif

/* Global Data items */
#ifndef PBS_MOM
extern struct server   server;
#endif	/* PBS_MOM */
extern char *msg_abt_err;
extern char *path_jobs;
extern char *path_spool;
extern char *path_resvs;
extern char  server_name[];
extern char *pbs_server_name;
extern pbs_list_head svr_newjobs;
extern pbs_list_head svr_alljobs;
extern int is_called_by_job_purge;

#ifdef PBS_MOM
#include "mom_func.h"

extern void  rmtmpdir(char *);
void nodes_free(job *);
extern char* std_file_name(job *pjob, enum job_file which, int *keeping);
/**
 * @brief
 * 		free up the tasks from the list of tasks associated with particular job, delete links and close connection.
 *
 * @param[in]	pj - pointer to job structure
 *
 * @return void
 */
void
tasks_free(job *pj)
{
	pbs_task	*tp = (pbs_task *)GET_NEXT(pj->ji_tasks);
	obitent	*op;
	infoent *ip;
	int	i;

	while (tp) {
		op = (obitent *)GET_NEXT(tp->ti_obits);
		while (op) {
			delete_link(&op->oe_next);
			free(op);
			op = (obitent *)GET_NEXT(tp->ti_obits);
		}

		ip = (infoent *)GET_NEXT(tp->ti_info);
		while (ip) {
			delete_link(&ip->ie_next);
			free(ip->ie_name);
			free(ip->ie_info);
			free(ip);
			ip = (infoent *)GET_NEXT(tp->ti_info);
		}

		if (tp->ti_tmfd != NULL) {
			for (i=0; i<tp->ti_tmnum; i++)
				close_conn(tp->ti_tmfd[i]);
			free(tp->ti_tmfd);
		}
		delete_link(&tp->ti_jobtask);
		free(tp);
		tp = (pbs_task *)GET_NEXT(pj->ji_tasks);
	}
}
#else	/* PBS_MOM */
/**
 * @brief
 * 		job_abt - abort a job
 *
 *		The job removed from the system and a mail message is sent
 *		to the job owner.
 *
 * @param[in]	pjob - pointer to job structure
 * @param[in]	text - job status message
 */

int
job_abt(job *pjob, char *text)
{
	int	old_state;
	int	old_substate;
	int	rc = 0;

	/* save old state and update state to Exiting */

	old_state = pjob->ji_qs.ji_state;

	if (old_state == JOB_STATE_FINISHED)
		return 0;	/* nothing to do for this job */

	old_substate = pjob->ji_qs.ji_substate;

	/* notify user of abort if notification was requested */

	if (text) {	/* req_delete sends own mail and acct record */
		account_record(PBS_ACCT_ABT, pjob, text);
		svr_mailowner(pjob, MAIL_ABORT, MAIL_NORMAL, text);
	}

	if ((old_state == JOB_STATE_RUNNING) && (old_substate != JOB_SUBSTATE_PROVISION)) {
		(void)svr_setjobstate(pjob,
			JOB_STATE_RUNNING, JOB_SUBSTATE_ABORT);
		rc = issue_signal(pjob, "SIGKILL", release_req, 0);
		if (rc != 0) {
			(void)sprintf(log_buffer, msg_abt_err,
				pjob->ji_qs.ji_jobid, old_substate);
			log_err(-1, __func__, log_buffer);
			if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0) {
				/* notify creator that job is exited */
				pjob->ji_wattr[(int)JOB_ATR_state].at_val.at_char
				= 'E';
				issue_track(pjob);
			}
			/*
			 * Check if the history of the finished job can be saved or it needs to be purged .
			 */
			svr_saveorpurge_finjobhist(pjob);
		}
	} else if ((old_state == JOB_STATE_TRANSIT) &&
		(old_substate == JOB_SUBSTATE_TRNOUT)) {
		/* I don't know of a case where this could happen */
		(void)sprintf(log_buffer, msg_abt_err,
			pjob->ji_qs.ji_jobid, old_substate);
		log_err(-1, __func__, log_buffer);
	} else if (old_substate == JOB_SUBSTATE_PROVISION) {
		(void)svr_setjobstate(pjob, JOB_STATE_RUNNING,
			JOB_SUBSTATE_ABORT);
		/*
		 * Check if the history of the finished job can be saved or it needs to be purged .
		 */
		svr_saveorpurge_finjobhist(pjob);
	} else {
		(void)svr_setjobstate(pjob, JOB_STATE_EXITING,
			JOB_SUBSTATE_ABORT);
		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0) {
			/* notify creator that job is exited */
			issue_track(pjob);
		}
		/*
		 * Check if the history of the finished job can be saved or it needs to be purged .
		 */
		svr_saveorpurge_finjobhist(pjob);
	}

	return (rc);
}
#endif	/* PBS_MOM */

/**
 * @brief
 * 		job_alloc - allocate space for a job structure and initialize working
 *				attribute to "unset"
 *
 * @return	pointer to structure or null is space not available.
 */

job *
job_alloc(void)
{
	job	*pj;

#ifndef PBS_MOM
	time_t 	ctm;
#endif

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

	/* explicity setting these licensing parameters just be sure */
	pj->ji_licneed = -1;	/* indicates uninitialized, invalid value */
	pj->ji_licalloc = 0;

	CLEAR_LINK(pj->ji_alljobs);
	CLEAR_LINK(pj->ji_jobque);
	CLEAR_LINK(pj->ji_unlicjobs);

	pj->ji_rerun_preq = NULL;

#ifdef	PBS_MOM
	CLEAR_HEAD(pj->ji_tasks);
	CLEAR_HEAD(pj->ji_failed_node_list);
	CLEAR_HEAD(pj->ji_node_list);
	pj->ji_taskid = TM_INIT_TASK;
	pj->ji_numnodes = 0;
	pj->ji_numrescs = 0;
	pj->ji_numvnod  = 0;
	pj->ji_num_assn_vnodes  = 0;
	pj->ji_hosts = NULL;
	pj->ji_vnods = NULL;
	pj->ji_assn_vnodes = NULL;
	pj->ji_resources = NULL;
	pj->ji_obit = TM_NULL_EVENT;
	pj->ji_postevent = TM_NULL_EVENT;
	pj->ji_preq = NULL;
	pj->ji_nodekill = TM_ERROR_NODE;
	pj->ji_flags = 0;
	pj->ji_jsmpipe = -1;
	pj->ji_mjspipe = -1;
	pj->ji_jsmpipe2 = -1;
	pj->ji_mjspipe2 = -1;
	pj->ji_child2parent_job_update_pipe = -1;
	pj->ji_parent2child_job_update_pipe = -1;
	pj->ji_parent2child_job_update_status_pipe = -1;
	pj->ji_parent2child_moms_status_pipe = -1;
	pj->ji_updated = 0;
#ifdef WIN32
	pj->ji_hJob = NULL;
	pj->ji_user = NULL;
	pj->ji_grpcache = NULL;
#endif
	pj->ji_stdout = 0;
	pj->ji_stderr = 0;
	pj->ji_setup = NULL;
#else	/* SERVER */
	pj->ji_prunreq = NULL;
	CLEAR_HEAD(pj->ji_svrtask);
	CLEAR_HEAD(pj->ji_rejectdest);
	pj->ji_terminated = 0;
	pj->ji_deletehistory = 0;
	pj->ji_newjob = 0;
	pj->ji_script = NULL;
#endif
	pj->ji_qs.ji_jsversion = JSVERSION;
	pj->ji_momhandle = -1;		/* mark mom connection invalid */
	pj->ji_mom_prot = PROT_INVALID; /* invalid protocol type */
#if defined(PBS_MOM) && defined(WIN32)
	pj->ji_momsubt = NULL;
#endif

	/* set the working attributes to "unspecified" */

	job_init_wattr(pj);


#ifndef PBS_MOM
	/* mark as JOB_INITIAL, set accrue times to zero */
	pj->ji_wattr[(int)JOB_ATR_accrue_type].at_val.at_long = JOB_INITIAL;
	pj->ji_wattr[(int)JOB_ATR_eligible_time].at_val.at_long = 0;
	/* start accruing time from the time job was created */
	time(&ctm);
	pj->ji_wattr[(int)JOB_ATR_sample_starttime].at_val.at_long = (long)ctm;

	/* if eligible_time_enable is not true, then job does not accrue eligible time */
	if (server.sv_attr[(int)SRV_ATR_EligibleTimeEnable].at_val.at_long == 1) {
		pj->ji_wattr[(int)JOB_ATR_accrue_type].at_flags |= ATR_VFLAG_SET;

		pj->ji_wattr[(int)JOB_ATR_eligible_time].at_flags |= ATR_VFLAG_SET;


		pj->ji_wattr[(int)JOB_ATR_sample_starttime].at_flags |= ATR_VFLAG_SET;
	}
#endif

	return (pj);
}

/**
 * @brief
 * 		job_free - free job structure and its various sub-structures
 *
 * @param[in]	pj - pointer to job structure
 *
 * @return	void
 */
void
job_free(job *pj)
{
	int	i;

#ifdef PBS_MOM

#ifdef WIN32
	if (pj->ji_wattr[(int) JOB_ATR_altid].at_flags & ATR_VFLAG_SET) {
		char *p;

		p = strstr(pj->ji_wattr[(int)JOB_ATR_altid].at_val.at_str,
			"HomeDirectory=");
		if (p) {
			struct passwd *pwdp = NULL;

			if ((pj->ji_wattr[JOB_ATR_euser].at_val.at_str) &&
				(pwdp = getpwnam(pj->ji_wattr[JOB_ATR_euser].at_val.at_str))) {
				if (pwdp->pw_userlogin != INVALID_HANDLE_VALUE) {
					if (impersonate_user(pwdp->pw_userlogin) == 0)
						return;
				}
				/* p+14 is the string after HomeDirectory= */
				unmap_unc_path(p+14);
				(void)revert_impersonated_user();
			}
			unmap_unc_path(p+14); /* also unmap under Admin to be sure */
		}
	}
#endif

#endif

	/* remove any malloc working attribute space */

	for (i=0; i < (int)JOB_ATR_LAST; i++) {
		job_attr_def[i].at_free(&pj->ji_wattr[i]);
	}

#ifndef PBS_MOM
	{
		/* Server only */

		struct work_task	*pwt;
		badplace		*bp;
		struct batch_request	*tbr = NULL;

		/*
		 * Delete any work task entries associated with the job.
		 * mom deferred tasks via TPP are also hooked into the
		 * ji_svrtask now, so they also get automatically cleared
		 * in this following loop
		 */
		while ((pwt = (struct work_task *)GET_NEXT(pj->ji_svrtask)) != NULL) {
			if (pwt->wt_type == WORK_Deferred_Reply) {
				tbr = (struct batch_request *)pwt->wt_parm1;
				if (tbr != NULL) {
					/* Check if the reply is for scheduler
					 * If so, then reject the request.
					 */
					if ((tbr->rq_orgconn != -1) &&
						(find_sched_from_sock(tbr->rq_orgconn) != NULL)) {
						tbr->rq_conn = tbr->rq_orgconn;
						req_reject(PBSE_HISTJOBID, 0, tbr);
					}
					/*
					* free batch request from task struct
					* if task is deferred reply
					*/
					else
					        free_br(tbr);
				}
			}

			/* wt_event2 either has additional data (like msgid) or NULL */
			free(pwt->wt_event2);

			delete_task(pwt);
		}

		/* free any bad destination structs */

		bp = (badplace *)GET_NEXT(pj->ji_rejectdest);
		while (bp) {
			delete_link(&bp->bp_link);
			free(bp);
			bp = (badplace *)GET_NEXT(pj->ji_rejectdest);
		}
	}
	if (pj->ji_qs.ji_svrflags & JOB_SVFLG_SubJob) {
		if ((pj->ji_parentaj) && (pj->ji_parentaj->ji_ajtrk))
			pj->ji_parentaj->ji_ajtrk->tkm_tbl[pj->ji_subjindx].trk_psubjob = NULL;
	} else if (pj->ji_ajtrk) {
		/* if Arrayjob, free the tracking table structure */
		for (i = 0; i < pj->ji_ajtrk->tkm_ct; i++) {
			job *psubj = pj->ji_ajtrk->tkm_tbl[i].trk_psubjob;
			if (psubj)
				psubj->ji_parentaj = NULL;
		}
		free(pj->ji_ajtrk);
		pj->ji_ajtrk = NULL;
	}
	pj->ji_parentaj = NULL;
	if (pj->ji_discard)
		free(pj->ji_discard);
	if (pj->ji_acctrec)
		free(pj->ji_acctrec);
	if (pj->ji_clterrmsg)
		free(pj->ji_clterrmsg);
	if (pj->ji_script)
		free(pj->ji_script);

#else	/* PBS_MOM  Mom Only */

	if (pj->ji_grpcache)
		(void)free(pj->ji_grpcache);

	assert(pj->ji_preq == NULL);
	nodes_free(pj);
	tasks_free(pj);
	if (pj->ji_resources) {
		for (i=0; i < pj->ji_numrescs; i++) {
			free(pj->ji_resources[i].nodehost);
			pj->ji_resources[i].nodehost = NULL;
			if  ((pj->ji_resources[i].nr_used.at_flags & ATR_VFLAG_SET) != 0) {
				job_attr_def[(int)JOB_ATR_resc_used].at_free(&pj->ji_resources[i].nr_used);
			}
		}
		pj->ji_numrescs = 0;
		free(pj->ji_resources);
		pj->ji_resources = NULL;
	}

	reliable_job_node_free(&pj->ji_failed_node_list);
	reliable_job_node_free(&pj->ji_node_list);

	/*
	 ** This gets rid of any dependent job structure(s) from ji_setup.
	 */
	if (job_free_extra != NULL)
		job_free_extra(pj);

#ifdef WIN32
	if (pj->ji_hJob) {
		CloseHandle(pj->ji_hJob);
		pj->ji_hJob = NULL;
	}
#endif

#endif	/* PBS_MOM */
	/* if a subjob (of a Array Job), do not free certain items */
	/* which are malloced and shared with the parent Array Job */
	/* They will be freed when the parent is removed           */

	pj->ji_qs.ji_jobid[0] = 'X';	/* as a "freed" marker */
	free(pj);	/* now free the main structure */
}

/**
 * @brief
 * 		job_init_wattr - initialize job working attribute array
 *		set the types and the "unspecified value" flag
 *
 * @see
 * 		job_alloc
 *
 * @param[in]	pj - pointer to job structure
 *
 * @return	void
 */
static void
job_init_wattr(job *pj)
{
	int	i;

	for (i=0; i<(int)JOB_ATR_LAST; i++) {
		clear_attr(&pj->ji_wattr[i], &job_attr_def[i]);
	}
}


/**
 * @brief
 *      spool_filename - formulate stdout/err file name in the spool area.
 *
 * @param[in]    pjob     - pointer to job structure.
 * @param[out]   namebuf  - output/error file name.
 * @param[in]    suffix   - output/error file name suffix.
 *
 * @return  void
 */
void
spool_filename(job *pjob, char *namebuf, char *suffix) {
    if (*pjob->ji_qs.ji_fileprefix != '\0')
        (void) strcat(namebuf, pjob->ji_qs.ji_fileprefix);
    else
        (void) strcat(namebuf, pjob->ji_qs.ji_jobid);
    (void) strcat(namebuf, suffix);
}

/**
 * @brief
 * 		remove_stdouter_files - remove stdout/err files from the spool directory
 *
 * @param[in]   pjob    - pointer to job structure
 * @param[in]	suffix	- output/error file name suffix.
 *
 * @return	void
 */
void
remove_stdouterr_files(job *pjob, char *suffix) {
	char namebuf[MAXPATHLEN + 1];
	extern char *msg_err_purgejob;

	(void) strcpy(namebuf, path_spool);
	spool_filename(pjob, namebuf, suffix);
	if (unlink(namebuf) < 0)
		if (errno != ENOENT)
			log_joberr(errno, __func__, msg_err_purgejob, pjob->ji_qs.ji_jobid);
}


/**
 * @brief
 * 		direct_write_requested - checks whether direct_write is requested by the job.
 *
 * @param[in]	pjob	- pointer to job structure.
 *
 * @return	bool
 * @retval 1 : direct write is requested by the job.
 * @retval 0 : direct write is not requested by the job.
 */
int direct_write_requested(job *pjob) {
	char *pj_attrk = NULL;
	if ((pjob->ji_wattr[(int)JOB_ATR_keep].at_flags & ATR_VFLAG_SET)) {
		pj_attrk = pjob->ji_wattr[(int)JOB_ATR_keep].at_val.at_str;
		if (strchr(pj_attrk, 'd') && (strchr(pj_attrk, 'o') || (strchr(pj_attrk, 'e'))))
			return 1;
	}
	return 0;
}


/**
 * @brief
 * 		job_purge - purge job from system
 *
 * 		The job is dequeued; the job control file, script file and any spooled
 * 		output files are unlinked, and the job structure is freed.
 * 		If we are MOM, the task files and checkpoint files are also
 * 		removed.
 *
 * @param[in]	pj - pointer to job structure
 *
 * @return	void
 */

void
job_purge(job *pjob)
{
	char		namebuf[MAXPATHLEN + 1] = {'\0'};
	extern	char	*msg_err_purgejob;
#ifdef	PBS_MOM
	extern	char	*path_checkpoint;
	int keeping = 0;
	attribute *jrpattr = NULL;

#ifndef WIN32
	pid_t pid = -1;
	int child_process = 0;
#endif

#else
	extern	char	*msg_err_purgejob_db;
	pbs_db_obj_info_t obj;
	pbs_db_job_info_t dbjob;
	pbs_db_conn_t *conn = (pbs_db_conn_t *) svr_db_conn;
#endif	/* PBS_MOM */

	if (pjob->ji_rerun_preq != NULL) {
		log_joberr(PBSE_INTERNAL, __func__, "rerun request outstanding",
			pjob->ji_qs.ji_jobid);
		reply_text(pjob->ji_rerun_preq, PBSE_INTERNAL, "job rerun");
		pjob->ji_rerun_preq = NULL;
	}
#ifdef	PBS_MOM
	delete_link(&pjob->ji_jobque);
	delete_link(&pjob->ji_alljobs);
	delete_link(&pjob->ji_unlicjobs);

	if (pjob->ji_preq != NULL) {
		log_joberr(PBSE_INTERNAL, __func__, "request outstanding",
			pjob->ji_qs.ji_jobid);
		reply_text(pjob->ji_preq, PBSE_INTERNAL, "job deleted");
		pjob->ji_preq = NULL;
	}
#ifndef WIN32
	if (pjob->ji_momsubt != 0) {	/* child running */
		(void)kill(pjob->ji_momsubt, SIGKILL);
		pjob->ji_momsubt = 0;
	}
	/* if open, close pipes to/from Mom starter process */
	if (pjob->ji_jsmpipe != -1) {
		conn_t *connection = NULL;

		if ((pjob->ji_wattr[(int)JOB_ATR_session_id].at_flags & ATR_VFLAG_SET) == 0 &&
				!(pjob->ji_wattr[(int)JOB_ATR_session_id].at_val.at_long) &&
				(connection = get_conn(pjob->ji_jsmpipe)) != NULL) {
			/*
			 * If session id for the job is not set, retain pjob->ji_jsmpipe.
			 * Set cn_data to NULL so that we can kill the process when
			 * record_finish_exec is called.
			 */
			connection->cn_data = NULL;
		} else
			(void)close_conn(pjob->ji_jsmpipe);
	}
	if (pjob->ji_mjspipe != -1) {
		(void)close(pjob->ji_mjspipe);
	}

	/* if open, close 2nd pipes to/from Mom starter process */
	if (pjob->ji_jsmpipe2 != -1) {
		(void)close_conn(pjob->ji_jsmpipe2);
	}
	if (pjob->ji_mjspipe2 != -1) {
		(void)close(pjob->ji_mjspipe2);
	}

	/* if open, close 3rd pipes to/from Mom starter process */
	if (pjob->ji_child2parent_job_update_pipe != -1) {
		(void)close_conn(pjob->ji_child2parent_job_update_pipe);
	}
	if (pjob->ji_parent2child_job_update_pipe != -1) {
		(void)close(pjob->ji_parent2child_job_update_pipe);
	}

	/* if open, close 4th pipes to/from Mom starter process */
	if (pjob->ji_parent2child_job_update_status_pipe != -1) {
		(void)close(pjob->ji_parent2child_job_update_status_pipe);
	}

	/* if open, close 5th pipes to/from Mom starter process */
	if (pjob->ji_parent2child_moms_status_pipe != -1) {
		(void)close(pjob->ji_parent2child_moms_status_pipe);
	}
#endif
#else	/* not PBS_MOM */
	if ((pjob->ji_qs.ji_substate != JOB_SUBSTATE_TRANSIN) &&
		(pjob->ji_qs.ji_substate != JOB_SUBSTATE_TRANSICM)) {
		if ((pjob->ji_qs.ji_svrflags & JOB_SVFLG_SubJob) && (pjob->ji_qs.ji_state != JOB_STATE_FINISHED)) {
			if ((pjob->ji_qs.ji_substate == JOB_SUBSTATE_RERUN3) || (pjob->ji_qs.ji_substate == JOB_SUBSTATE_QUEUED))
				update_subjob_state(pjob, JOB_STATE_QUEUED);
			else
				update_subjob_state(pjob, JOB_STATE_EXPIRED);
		}

		(void)account_entity_limit_usages(pjob, NULL, NULL, DECR,
				pjob->ji_etlimit_decr_queued ? ETLIM_ACC_ALL_MAX : ETLIM_ACC_ALL);

		svr_dequejob(pjob);
	}
#endif	/* PBS_MOM */

#ifdef PBS_MOM

#ifndef WIN32
	/* on the mom end, perform file-system related cleanup in a forked process
	 * only if job is executed successfully with exit status 0(JOB_EXEC_OK)
	 */
	if (pjob->ji_qs.ji_un.ji_momt.ji_exitstat == JOB_EXEC_OK) {
		child_process = 1;
		pid = fork();
		if (pid > 0) {
			/* parent mom */
			job_free(pjob);
			return;
		}
	}
	/* Parent Mom process will continue the job cleanup itself, if call to fork is failed */
#endif
	(void)strcpy(namebuf, path_jobs);	/* delete script file */
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
	(void)strcat(namebuf, JOB_SCRIPT_SUFFIX);
	if (unlink(namebuf) < 0) {
		if (errno != ENOENT) {
			log_joberr(errno, __func__, msg_err_purgejob,
				pjob->ji_qs.ji_jobid);
		}
	}

	if (pjob->ji_preq != NULL) {
		req_reject(PBSE_MOMREJECT, 0, pjob->ji_preq);
		pjob->ji_preq = NULL;
	}
	(void)strcpy(namebuf, path_jobs);      /* job directory path */
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
	(void)strcat(namebuf, JOB_TASKDIR_SUFFIX);
	(void)remtree(namebuf);

	rmtmpdir(pjob->ji_qs.ji_jobid);		/* remove tmpdir */

	/* remove the staging and execution directory when sandbox=PRIVATE
	 ** and there are no stage-out errors
	 */
	if ((pjob->ji_wattr[(int)JOB_ATR_sandbox].at_flags & ATR_VFLAG_SET) &&
		(strcasecmp(pjob->ji_wattr[JOB_ATR_sandbox].at_val.at_str, "PRIVATE") == 0)) {
		if (!(pjob->ji_qs.ji_svrflags & JOB_SVFLG_StgoFal)) {
			if (pjob->ji_grpcache != NULL)
				rmjobdir(pjob->ji_qs.ji_jobid,
					jobdirname(pjob->ji_qs.ji_jobid, pjob->ji_grpcache->gc_homedir),
					pjob->ji_grpcache->gc_uid,
					pjob->ji_grpcache->gc_gid);
			else
				rmjobdir(pjob->ji_qs.ji_jobid,
					jobdirname(pjob->ji_qs.ji_jobid, NULL),
					0,
					0);
		}
	}

	if (path_checkpoint != NULL) {	/* delete checkpoint files */
		(void)strcpy(namebuf, path_checkpoint);
		if (*pjob->ji_qs.ji_fileprefix != '\0')
			(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
		else
			(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
		(void)strcat(namebuf, JOB_CKPT_SUFFIX);
		(void)remtree(namebuf);
		(void)strcat(namebuf, ".old");
		(void)remtree(namebuf);
	}

	jrpattr = &pjob->ji_wattr[(int) JOB_ATR_remove];
	/* remove stdout/err files if remove_files is set. */
	if ((jrpattr->at_flags & ATR_VFLAG_SET)
			&& (pjob->ji_qs.ji_un.ji_momt.ji_exitstat == JOB_EXEC_OK)) {
		if (strchr(jrpattr->at_val.at_str, 'o')) {
			(void) strcpy(namebuf, std_file_name(pjob, StdOut, &keeping));
			if (*namebuf && (unlink(namebuf) < 0))
				if (errno != ENOENT)
					log_err(errno, __func__, msg_err_purgejob);
		}
		if (strchr(jrpattr->at_val.at_str, 'e')) {
			(void) strcpy(namebuf, std_file_name(pjob, StdErr, &keeping));
			if (*namebuf && (unlink(namebuf) < 0))
				if (errno != ENOENT)
					log_err(errno, __func__, msg_err_purgejob);
		}
	}


#ifdef WIN32
	/* following introduced by fix to BZ 6363 for executing scripts */
	/* directly on the command line */
	(void)strcpy(namebuf, path_jobs);      /* delete any *.BAT file */
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
	(void)strcat(namebuf, ".BAT");

	if (unlink(namebuf) < 0) {
		if (errno != ENOENT)
			log_err(errno, __func__, msg_err_purgejob);
	}
#endif
#else	/* PBS_MOM */

	/* server code */
	remove_stdouterr_files(pjob, JOB_STDOUT_SUFFIX);
	remove_stdouterr_files(pjob, JOB_STDERR_SUFFIX);
#endif	/* PBS_MOM */

#ifdef PBS_MOM
	(void)strcpy(namebuf, path_jobs);	/* delete job file */
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
	(void)strcat(namebuf, JOB_FILE_SUFFIX);
	if (unlink(namebuf) < 0) {
		if (errno != ENOENT) {
			log_joberr(errno, __func__, msg_err_purgejob,
				pjob->ji_qs.ji_jobid);
		}
	}
#else
	/* delete job and dependants from database */
	obj.pbs_db_obj_type = PBS_DB_JOB;
	obj.pbs_db_un.pbs_db_job = &dbjob;
	strcpy(dbjob.ji_jobid, pjob->ji_qs.ji_jobid);
	if (pbs_db_delete_obj(conn, &obj) == -1) {
		log_joberr(-1, __func__, msg_err_purgejob_db,
			pjob->ji_qs.ji_jobid);
	}

	if (pjob->ji_qs.ji_svrflags & JOB_SVFLG_HasNodes) {
		is_called_by_job_purge = 1;
		free_nodes(pjob);
		is_called_by_job_purge = 0;
	}

	if (pjob->ji_resvp) {
		int		rc;

		if (pjob->ji_resvp->ri_qs.ri_type == RESV_JOB_OBJECT) {

			/*reservation structure should not point to a
			 *job structure that is going away
			 */
			pjob->ji_resvp->ri_jbp = 0;

			/*relinquish any nodes belonging to this reservation-job*/

			if (pjob->ji_resvp->ri_qs.ri_svrflags & RESV_SVFLG_HasNodes)
				free_resvNodes(pjob->ji_resvp);

			if ((rc = gen_deleteResv(pjob->ji_resvp, 0)))
				log_err(rc, __func__, "no reservation delete task generated");
		}
		else
			log_err(PBSE_INTERNAL, __func__,
				"ji_resvp or ri_qs.ri_type not correct");
	}
#endif

	(void)strcpy(namebuf, path_jobs);	/* delete cred file */
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pjob->ji_qs.ji_jobid);
	(void)strcat(namebuf, JOB_CRED_SUFFIX);
	if (unlink(namebuf) < 0) {
		if (errno != ENOENT) {
			log_joberr(errno, __func__, msg_err_purgejob,
				pjob->ji_qs.ji_jobid);
		}
	}

	/* Clearing purge job info from svr_newjobs list */
        if(pjob == (job *)GET_NEXT(svr_newjobs))
                delete_link(&pjob->ji_alljobs);

	job_free(pjob);

#ifdef PBS_MOM
#ifndef WIN32
	if (child_process && pid == 0) {
		/* I am child of the forked process. Deleted all the
		 * particular job related files, thus exiting.
		 */
		exit(0);
	}
#endif
#endif

	return;
}

/**
 * @brief
 * 		find_job() - find job by jobid
 *
 *		Search list of all server jobs for one with same job id
 *		Return NULL if not found or pointer to job struct if found.
 *
 *		If the host portion of the job ID contains a dot, it is
 *		assumed that the string represents the FQDN. If no dot is
 *		present, the string represents the short (unqualified)
 *		hostname. For example, "foo" will match "foo.bar.com", but
 *		"foo.bar" will not match "foo.bar.com".
 *
 *		If server, then search in AVL tree otherwise Linked list.
 *
 * @param[in]	jobid - job ID string.
 *
 * @return	pointer to job struct
 * @retval NULL	- if job by jobid not found.
 */

job *
find_job(char *jobid)
{
#ifndef PBS_MOM
	size_t len;
	AVL_IX_REC *pkey;
	char *host_dot;
	char *serv_dot;
	char *host;
#endif
	char *at;
	job  *pj = NULL;
	char buf[PBS_MAXSVRJOBID+1];

	/* Make a copy of the job ID string before we modify it. */
	snprintf(buf, sizeof(buf), "%s", jobid);
	/*
	 * If @server_name was specified, it was used to route the
	 * request to this server. It will not be part of the string
	 * we are searching for, so truncate the string at the '@'
	 * character.
	 */
	if ((at = strchr(buf, (int)'@')) != NULL)
		*at = '\0';

#ifndef PBS_MOM
	/*
	 * Avl tree search cannot find partially formed jobid's.
	 * While storing we supplied the full jobid.
	 * So while retrieving also we have to provide
	 * the exact key that was used while storing the job
	 */
	if ((host_dot = strchr(buf, '.')) != NULL) {
		/* The job ID string contains a host string */
		host = host_dot + 1;
		if (strncasecmp(server_name, host, PBS_MAXSERVERNAME+1) != 0) {
			/*
			 * The server_name and host strings do not match.
			 * Try to determine if one is the FQDN and the other
			 * is the short name. If there is no match, do not
			 * modify the string we will be searching for. If
			 * there is a match, replace host with server_name.
			 *
			 * Do not call is_same_host() to avoid DNS lookup
			 * because server_name may not resolve to a real
			 * host when PBS_SERVER_HOST_NAME is set or when
			 * failover is enabled. The lookup could hang the
			 * server for some amount of time.
			 */
			host_dot = strchr(host, '.');
			serv_dot = strchr(server_name, '.');
			if (host_dot != NULL) {
				/* the host string is FQDN */
				if (serv_dot == NULL) {
					/* the server_name is not FQDN */
					len = strlen(server_name);
					if (len == (host_dot - host)) {
						if (strncasecmp(host, server_name, len) == 0) {
							/* Use server_name to ensure cases match. */
							strcpy(host, server_name);
						}
					}
				}
			} else if (serv_dot != NULL) {
				/* the host string is not FQDN */
				/* the server_name is FQDN */
				len = strlen(host);
				if (len == (serv_dot - server_name)) {
					if (strncasecmp(host, server_name, len) == 0) {
						/* Use server_name to ensure cases match. */
						strcpy(host, server_name);
					}
				}
			}
		} else {
			/*
			 * Case insensitive compare was successful.
			 * Use server_name to ensure cases match.
			 */
			strcpy(host, server_name);
		}
	} else {
		/* The job ID string does not contain a host string */
		strcat(buf, ".");
		strcat(buf, server_name);
	}

	if ((AVL_jctx != NULL) && ((pkey = svr_avlkey_create(buf)) != NULL)) {
		if (avl_find_key(pkey, AVL_jctx) == AVL_IX_OK)
			pj = (job *) pkey->recptr;
		free(pkey);
		return (pj);
	}
#endif
	pj = (job *)GET_NEXT(svr_alljobs);
	while (pj != NULL) {
		if (!strncasecmp(jobid, pj->ji_qs.ji_jobid, sizeof(pj->ji_qs.ji_jobid)))
			break;
		pj = (job *)GET_NEXT(pj->ji_alljobs);
	}
	return (pj);  /* may be a null pointer */
}

/**
 *  @brief
 *		Output credential into job file.
 *
 * @param[in]		pjob - pointer to job struct
 * @param[in]		cred - JobCredential
 * @param[in]		len - size of credentials.
 *
 * @return	int
 * @retval	0	- success
 * @retval	-1	- fail
 */
int
write_cred(job *pjob, char *cred, size_t len)
{
	extern char     *path_jobs;
	char		name_buf[MAXPATHLEN+1];
	int		cred_fd;
	int		ret = -1;

	(void)strcpy(name_buf, path_jobs);
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(name_buf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(name_buf, pjob->ji_qs.ji_jobid);
	(void)strcat(name_buf, JOB_CRED_SUFFIX);

	if ((cred_fd = open(name_buf, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1) {
		log_err(errno, __func__, name_buf);
		return -1;
	}

#ifdef WIN32
	secure_file(name_buf, "Administrators", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
	setmode(cred_fd, O_BINARY);
#endif

	if (write(cred_fd, cred, len) != len) {
		log_err(errno, __func__, "write cred");
		goto done;
	}

#if !defined(PBS_MOM) && defined(WIN32)
	cache_usertoken_and_homedir(pjob->ji_wattr[JOB_ATR_euser].at_val.at_str,
		NULL, 0, read_cred, (job *)pjob, pbs_decrypt_pwd, 1);
#endif
	ret = 0;

done:
	close(cred_fd);
	return ret;
}

/**
 * @brief
 *		Check if this job has an associated credential file.  If it does,
 *		the credential file is opened and the credential is read into
 *		malloc'ed memory.
 *
 * @param[in]		pjob - job whose credentials needs to be read.
 * @param[out]		cred - JobCredential
 * @param[in]		len - size of credentials.
 *
 * @return	int
 * @retval	1	- no cred
 * @retval	0	- success
 * @retval	-1	- error
 */
int
read_cred(job *pjob, char **cred, size_t *len)
{
	extern char     *path_jobs;
	char		name_buf[MAXPATHLEN+1];
	char		*hold = NULL;
	struct	stat	sbuf;
	int		fd;
	int		ret = -1;

	(void)strcpy(name_buf, path_jobs);
	if (*pjob->ji_qs.ji_fileprefix != '\0')
		(void)strcat(name_buf, pjob->ji_qs.ji_fileprefix);
	else
		(void)strcat(name_buf, pjob->ji_qs.ji_jobid);
	(void)strcat(name_buf, JOB_CRED_SUFFIX);

	if ((fd = open(name_buf, O_RDONLY)) == -1) {
		if (errno == ENOENT)
			return 1;
		log_err(errno, __func__, "open");
		return ret;
	}

	if (fstat(fd, &sbuf) == -1) {
		log_err(errno, __func__, "fstat");
		goto done;
	}

	hold = malloc(sbuf.st_size);
	assert(hold != NULL);

#ifdef WIN32
	setmode(fd, O_BINARY);
#endif

	if (read(fd, hold, sbuf.st_size) != sbuf.st_size) {
		log_err(errno, __func__, "read");
		goto done;
	}
	*len = sbuf.st_size;
	*cred = hold;
	hold = NULL;
	ret = 0;

done:
	close(fd);
	if (hold != NULL)
		free(hold);
	return ret;
}

/**
 * @brief
 *		decode_sandbox - decode sandbox into string attribute
 *
 * @param[in,out]	patr - the string attribute that holds the decoded value
 * @param[in]		name - project attribute name
 * @param[in]		resc - resource name (unused here)
 * @param[in]		val - project attribute value
 *
 * @return int
 * @retval	0	- success
 * @retval	>0	- error number if error.
 *
 * @note
 *		argument rescn is unused here.
 */

int
decode_sandbox(struct attribute *patr, char *name, char *rescn, char *val)
{
	char *pc;

	pc = val;
	while (isspace((int)*pc))
		++pc;
	if (*pc == '\0' || !isalpha((int)*pc))
		return PBSE_BADATVAL;

	/* compare to valid values of sandbox */
	if ((strcasecmp(pc, "HOME") != 0) &&
		(strcasecmp(pc, "O_WORKDIR") != 0) &&
		(strcasecmp(pc, "PRIVATE") != 0)) {
		return PBSE_BADATVAL;
	}

	return (decode_str(patr, name, rescn, val));
}

/**
 * @brief
 *	Decode project into string attribute.
 *
 * @param[in,out]	patr - the string attribute that holds the decoded value
 * @param[in]		name - project attribute name
 * @param[in]		resc - resource name (unused here)
 * @param[in]		val - project attribute value
 *
 * @return	int
 * @retval      0 if success
 * @retval      > 0 error number if error
 * @retval      *patr members set
 */

int
decode_project(struct attribute *patr, char *name, char *rescn, char *val)
{
	char *pc;

	pc = val;
	while (isspace((int)*pc))
		++pc;

	if (strpbrk(pc, ETLIM_INVALIDCHAR) != NULL)
		return PBSE_BADATVAL;

	return (decode_str(patr, name, rescn,
		(*val == '\0')?PBS_DEFAULT_PROJECT:val));
}

/**
 * @brief
 * 	Returns 1 if job 'job' should remain running in spite of node failures.
 * @param[in]	pjob	- job being queried
 *
 * @return int
 * @retval	1 - if true
 * @retval	0 - if false or 'tolerate_node_failures' attribute is unset
 */
int
do_tolerate_node_failures(job *pjob)
{

	if (pjob == NULL)
		return (0);

#if     MOM_ALPS
	/* not currently supported on the Crays */
	return (0);
#endif

	if ((pjob->ji_wattr[(int)JOB_ATR_tolerate_node_failures].at_flags & ATR_VFLAG_SET) &&
	   ((strcmp(pjob->ji_wattr[(int)JOB_ATR_tolerate_node_failures].at_val.at_str, TOLERATE_NODE_FAILURES_ALL) == 0) ||
	   ((strcmp(pjob->ji_wattr[(int)JOB_ATR_tolerate_node_failures].at_val.at_str, TOLERATE_NODE_FAILURES_JOB_START) == 0) &&
	    pjob->ji_qs.ji_substate != JOB_SUBSTATE_RUNNING))) {
		return (1);
	}
	return (0);
}
/**
 * @brief
 *	This function updates/creates the resource list named
 *	'res_list_name' and indexed in pjob as
 *	'res_list_index', using resources assigned values specified
 *	in 'exec_vnode'. This also saves the previous values in
 *	pjob's 'backup_res_list_index' attribute if not already
 *	set.
 *
 * @param[in,out] pjob - job structure
 * @param[in]	  res_list_name - resource list name
 * @param[in]	  rel_list_index - attribute index in job structure
 * @param[in]	  exec_vnode - string containing  the various resource
 *			assignments
 * @param[in]	  op - kind of operation to be performed while setting
 *		     the resource value.
 * @param[in]	  always_set  - if set, even if there is no resulting
 *			resource list, try to have at least one entry
 *			(e.g., ncpus=0) to keep the list set.
 * @param[in]	  backup_res_list_index - index to  job's attribute
 *			resource list to hold original values.
 *
 * @return int
 * @retval 0  - success
 * @retval 1 - failure
 */

int
update_resources_list(job *pjob, char *res_list_name,
		int res_list_index, char *exec_vnode, enum batch_op op,
		int always_set, int backup_res_list_index)
{
	char *chunk;
	int j;
	int rc;
	int nelem;
	char *noden;
	struct key_value_pair *pkvp;
	resource_def *prdef;
	resource *presc, *pr, *next;
	attribute tmpattr;

	if (exec_vnode == NULL || pjob == NULL) {
		log_err(PBSE_INTERNAL, __func__, "bad input parameter");

		return (1);
	}

	/* Save current resource values in backup resource list */
	/* if backup resources list is not already set */
	if (pjob->ji_wattr[res_list_index].at_flags & ATR_VFLAG_SET) {

		if ((pjob->ji_wattr[backup_res_list_index].at_flags & ATR_VFLAG_SET) == 0) {
			job_attr_def[backup_res_list_index].at_free(&pjob->ji_wattr[backup_res_list_index]);
			job_attr_def[backup_res_list_index].at_set(&pjob->ji_wattr[backup_res_list_index], &pjob->ji_wattr[res_list_index], INCR);

		}

		pr = (resource *)GET_NEXT(pjob->ji_wattr[res_list_index].at_val.at_list);
		while (pr != NULL) {
			next = (resource *)GET_NEXT(pr->rs_link);
			if (pr->rs_defin->rs_flags & (ATR_DFLAG_RASSN | ATR_DFLAG_FNASSN | ATR_DFLAG_ANASSN)) {
				delete_link(&pr->rs_link);
				if (pr->rs_value.at_flags & ATR_VFLAG_INDIRECT)
					free_str(&pr->rs_value);
				else
					pr->rs_defin->rs_free(&pr->rs_value);
				(void)free(pr);
			}
			pr = next;
		}
		pjob->ji_modified = 1;
	}

	rc = 0;
	for (chunk = parse_plus_spec(exec_vnode, &rc); chunk && (rc == 0);
	     chunk = parse_plus_spec(NULL, &rc)) {

		if ((rc = parse_node_resc(chunk, &noden, &nelem, &pkvp)) != 0) {
			log_err(rc, __func__, "parse of exec_vnode failed");
			goto update_resources_list_error;
		}
		for (j = 0; j < nelem; j++) {
			prdef = find_resc_def(svr_resc_def,
					pkvp[j].kv_keyw, svr_resc_size);
			if (prdef == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
				  "unknown resource %s in exec_vnode",
					pkvp[j].kv_keyw);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto update_resources_list_error;
			}

			if (prdef->rs_flags & (ATR_DFLAG_RASSN | ATR_DFLAG_FNASSN | ATR_DFLAG_ANASSN)) {
				presc = add_resource_entry(
					&pjob->ji_wattr[res_list_index],
								prdef);
				if (presc == NULL) {
					snprintf(log_buffer,
						sizeof(log_buffer),
				  		"failed to add resource"
						"  %s",prdef->rs_name);
					log_err(PBSE_INTERNAL, __func__,
							log_buffer);
					goto update_resources_list_error;
				}
				if ((rc = prdef->rs_decode( &tmpattr,
					res_list_name, prdef->rs_name,
						pkvp[j].kv_val)) != 0) {
					snprintf(log_buffer,
						  sizeof(log_buffer),
						  "decode of %s failed",
						 prdef->rs_name);

					log_err(PBSE_INTERNAL, __func__,
							log_buffer);
					goto update_resources_list_error;
				}
				(void)prdef->rs_set( &presc->rs_value,
							&tmpattr, op);
			}
		}
	}

	if (rc != 0) {
		log_err(PBSE_INTERNAL, __func__, "error parsing exec_vnode");
		goto update_resources_list_error;
	}

	if (always_set &&
	   ((pjob->ji_wattr[res_list_index].at_flags & ATR_VFLAG_SET) == 0)) {
		/* this means no resources got freed during suspend */
		/* let's put a dummy entry for ncpus=0 */
		prdef = find_resc_def(svr_resc_def, "ncpus",
							svr_resc_size);
		if (prdef == NULL) {
			log_err(PBSE_INTERNAL, __func__,
				"no ncpus in svr_resc_def!");
			return (1);
		}
		presc = add_resource_entry(
				&pjob->ji_wattr[res_list_index], prdef);
		if (presc == NULL) {
			log_err(PBSE_INTERNAL, __func__,
				"failed to add ncpus in resource list");
			return (1);
		}
		if ((rc = prdef->rs_decode(&tmpattr, res_list_name,
					prdef->rs_name, "0")) != 0) {
			log_err(rc, __func__,
				"decode of ncpus=0 failed");
			return (1);
		}
		(void)prdef->rs_set(&presc->rs_value, &tmpattr, op);
	}


	return (0);

update_resources_list_error:
	job_attr_def[backup_res_list_index].at_free(
			&pjob->ji_wattr[backup_res_list_index]);
	pjob->ji_wattr[backup_res_list_index].at_flags &= ~ATR_VFLAG_SET;
	job_attr_def[res_list_index].at_set(
			&pjob->ji_wattr[res_list_index],
			&pjob->ji_wattr[backup_res_list_index], INCR);
	pjob->ji_modified = 1;
	return (1);
}

#ifndef PBS_MOM		/*SERVER ONLY*/

/**
 * @brief
 * 		functons for Reservation (resc_resv) structures
 * 		resc_resv_alloc - allocate space for a "resc_resv" structure and initialize
 *		appropriately
 *
 * @return	resc_resv *
 * @retval	nonzero	- successful
 * @retval	0	- unsuccessful
 */

resc_resv *
resc_resv_alloc(void)
{
	resc_resv	*resvp;

	resvp = (resc_resv *)malloc(sizeof(resc_resv));
	if (resvp == NULL) {
		log_err(errno, "resc_resv_alloc", "no memory");
		return NULL;
	}
	(void)memset((char *)resvp, (int)0, (size_t)sizeof(resc_resv));
	CLEAR_LINK(resvp->ri_allresvs);
	CLEAR_HEAD(resvp->ri_svrtask);
	CLEAR_HEAD(resvp->ri_rejectdest);

	/* set the reservation structure's version number and
	 * the working attributes to "unspecified"
	 */

	resvp->ri_qs.ri_rsversion = RSVERSION;
	job_or_resv_init_wattr((void *)resvp, RESC_RESV_OBJECT);

	return (resvp);
}


/**
 * @brief
 * 		resv_free - deals only with the actual "freeing" of a reservation,
 *		accounting, notifying, removing the reservation from linked lists
 *		are handled before hand by resv_abt, resv_purge.  This just frees
 *		any hanging substructures, deletes any attached work_tasks and frees
 *		the resc_resv	structure itself.
 *
 * @param[in,out]		presv - reservation struct which needs to be freed.
 *
 * @return void
 */

void
resv_free(resc_resv *presv)
{
	int			 i;
	struct work_task	*pwt;
	badplace		*bp;

	/* remove any malloc working attribute space */

	for (i=0; i < (int)RESV_ATR_LAST; i++) {
		resv_attr_def[i].at_free(&presv->ri_wattr[i]);
	}

	/* delete any work task entries associated with the resv */

	while ((pwt = (struct work_task *)GET_NEXT(presv->ri_svrtask)) != 0) {
		delete_task(pwt);
	}

	/* free any bad destination structs */
	/* We may never use this code if reservations can't be routed */

	bp = (badplace *)GET_NEXT(presv->ri_rejectdest);
	while (bp) {
		delete_link(&bp->bp_link);
		free(bp);
		bp = (badplace *)GET_NEXT(presv->ri_rejectdest);
	}

	/* any "interactive" batch request? (shouldn't be); free it now */
	if (presv->ri_brp)
		free_br(presv->ri_brp);

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

/**
 * @brief
 * 		find_resv() - find resc_resv struct by reservation ID
 *
 *		Search list of all server resc_resv structs for one with same
 *		reservation ID as input "resvID"
 *
 * @param[in]	resvID - reservation ID
 *
 * @return	pointer to resc_resv struct
 * @retval	NULL	- not found
 */

resc_resv *
find_resv(char *resvID)
{
	char *at;
	resc_resv  *presv;

	if ((at = strchr(resvID, (int)'@')) != 0)
		*at = '\0';	/* strip of @server_name */
	presv = (resc_resv *)GET_NEXT(svr_allresvs);
	while (presv != NULL) {
		if (!strcmp(resvID, presv->ri_qs.ri_resvID))
			return (presv);
		presv = (resc_resv *)GET_NEXT(presv->ri_allresvs);
	}
	if (at)
		*at = '@';	/* restore @server_name */

	return (presv);		/* pointer value is null */
}


/**
 * @brief
 * 		resv_purge - purge reservation from system
 *
 * 		The reservation is unlinked from the server's svr_allresvs;
 * 		the reservation control file is unlinked, any attached work_task's
 * 		are deleted and the resc_resv structure is freed along with any
 * 		hanging, malloc'd memory areas.
 *
 * 		This function - ASSUMES - that if the reservation is supported by a
 * 		pbs_queue that queue is empty OR having history jobs only (i.e. job
 * 		in state JOB_STATE_MOVED/JOB_STATE_FINISHED). So, whatever mechanism
 * 		is being used to remove the jobs from such a supporting queue should,
 * 		at the outset, store the value "False" into the queue's "enabled"
 * 		attribute (blocks new jobs from being placed in the queue while the
 * 		server attempts to delete those currently in the queue) and into its
 * 		"scheduling" attribute (to disable servicing by the scheduler).
 *
 * 		Any hanging, empty pbs_queue will be handled by creating and issuing
 * 		to the server a PBS_BATCH_Manager request to delete this queue.  This
 * 		will be dispatched immediately and a work_task having a function whose
 * 		sole job is to free the batch_request struct is placed on the "immediate"
 * 		task list, for processing by the "next_task" function in the main loop
 * 		of the server.
 *
 * 		This function should only be called after a check has been made to
 * 		to verify that the party deleting the reservation has proper permission
 *
 * @param[in]	presv - pointer to reservation which needs to be puged.
 *
 * @return	void
 */

void
resv_purge(resc_resv *presv)
{
	struct  batch_request	*preq;
	struct	work_task	*pwt;
	extern char *msg_purgeResvFail;
	extern char *msg_purgeResvDb;
	pbs_db_obj_info_t	obj;
	pbs_db_resv_info_t	dbresv;

	if (presv == NULL)
		return;

	if (presv->ri_qp != NULL) {
		/*
		 * Issue a batch_request to remove the supporting pbs_queue
		 * As Stated: Assumption is that the queue is empty of jobs
		 */
		preq = alloc_br(PBS_BATCH_Manager);
		if (preq == NULL) {
			(void)sprintf(log_buffer, "batch request allocation failed");
			log_event(PBSEVENT_SYSTEM, PBS_EVENTCLASS_RESV, LOG_ERR,
				presv->ri_qs.ri_resvID, log_buffer);
			return;
		}

		CLEAR_LINK(preq->rq_ind.rq_manager.rq_attr);
		preq->rq_ind.rq_manager.rq_cmd = MGR_CMD_DELETE;
		preq->rq_ind.rq_manager.rq_objtype = MGR_OBJ_QUEUE;

		(void)strcpy(preq->rq_user, "pbs_server");
		(void)strcpy(preq->rq_host, pbs_server_name);
		/*
		 * Copy the queue name from the attributes rather than use the
		 * presv->ri_qp->qu_qs.qu_name value. The post_resv_purge()
		 * function could modify it at any time. See SPID 352225.
		 */
		strcpy(preq->rq_ind.rq_manager.rq_objname,
			presv->ri_wattr[RESV_ATR_queue].at_val.at_str);

		/* It is assumed that the prior check on permission was OK */
		preq->rq_perm |= ATR_DFLAG_MGWR;

		if (issue_Drequest(PBS_LOCAL_CONNECTION, preq, post_resv_purge, &pwt, 0) == -1) {
			/* Failed to delete queue. */
			free_br(preq);
			log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_WARNING,
				presv->ri_qs.ri_resvID, msg_purgeResvFail);
			return;
		}
		/*
		 * Queue was deleted. Invocation of post_resv_purge() will
		 * re-call resv_purge() (passing wt_parm2)
		 */
		if (pwt)
			pwt->wt_parm2 = presv;
		return;
	}

	/*reservation no longer has jobs or a supporting queue*/

	if (presv->ri_giveback) {
		/*ok, resources were actually assigned to this reservation
		 *and must now be accounted back into the loaner's pool
		 */

		set_resc_assigned((void *)presv, 1, DECR);
		presv->ri_giveback = 0;
	}

	/*Remove reservation's link element from whichever of the server's
	 *global lists (svr_allresvs or svr_newresvs) has it
	 */
	delete_link(&presv->ri_allresvs);

	/*Release any nodes that were associated to this reservation*/
	free_resvNodes(presv);
	set_scheduler_flag(SCH_SCHEDULE_TERM, dflt_scheduler);

	strcpy(dbresv.ri_resvid, presv->ri_qs.ri_resvID);
	obj.pbs_db_obj_type = PBS_DB_RESV;
	obj.pbs_db_un.pbs_db_resv = &dbresv;
	if (pbs_db_delete_obj(svr_db_conn, &obj) == -1)
		log_err(errno, __func__, msg_purgeResvDb);

	/*Free resc_resv struct, any hanging substructs, any attached
	 *work_task structs
	 */
	resv_free(presv);
	return;
}


/**
 * @brief
 *  	post_resv_purge - As with the other "post_*" functions, this
 *		handles the return reply from an internally generated request.
 *		Function resv_purge() ended up having to generate an internal
 *		request to qmgr() to delete the reservation's attached queue.
 *		When the reply to that is received and indicates success,
 *		resv_purge() will be re-called and this time the latter half
 *		of the resv_purge() code will execute to finish the purge.
 *		Otherwise, the reservation just won't get purged.  It will
 *		just be defunct
 *
 * @param[in]	pwt - work structure which contains internally generated request.
 *
 * @return void
 */

static void
post_resv_purge(struct work_task *pwt)
{
	int	   code;
	resc_resv  *presv;
	struct batch_request	*preq;

	preq = (struct batch_request *)pwt->wt_parm1;
	presv = (resc_resv *)pwt->wt_parm2;
	code = preq->rq_reply.brp_code;

	/*Release the batch_request hanging (wt_parm1) from the
	 *work_task structure
	 */
	release_req(pwt);

	if (code) {
		/*response from the request is that an error occured
		 *So, we failed on deleting the reservation's queue -
		 *should mail owner about the failure
		 */
		return;
	}

	/*qmgr gave no error in doing MGR_CMD_DELETE on the queue
	 *So it's safe to clear the reservation's queue pointer
	 */
	presv->ri_qp = NULL;

	/*now re-call resv_purge to execute the function's lower part*/
	resv_purge(presv);
}


/**
 * @brief
 * 		resv_abt - abort a reservation
 *
 * 		The reservation removed from the system and a mail message is sent
 * 		to the reservation owner.
 *
 * @param[in]	presv - reservation structure
 * @param[in]	text - matter/content in the mail.
 *
 * @return	error code
 * @retval	0	- success
 * @retval	-1	- error
 */

int
resv_abt(resc_resv *presv, char *text)
{
	int	old_state;
	int	rc = 0;

	old_state = presv->ri_qs.ri_state;

	if (old_state == RESV_BEING_DELETED) {
		if (presv->ri_qs.ri_type == RESV_JOB_OBJECT) {
			if (presv->ri_jbp == NULL) {

				account_recordResv(PBS_ACCT_ABT, presv, "");
				svr_mailownerResv(presv, MAIL_ABORT, MAIL_NORMAL, text);
				resv_purge(presv);
			} else
				rc = -1;
		} else if (presv->ri_qs.ri_type == RESC_RESV_OBJECT) {
			if ((presv->ri_qp != NULL &&
				presv->ri_qp->qu_numjobs == 0) ||
				presv->ri_qp == NULL) {

				account_recordResv(PBS_ACCT_ABT, presv, "");
				svr_mailownerResv(presv, MAIL_ABORT, MAIL_NORMAL, text);
				resv_purge(presv);
			} else
				rc = -1;
		}
	}
	return (rc);
}

/**
 * @brief
 * 		job_or_resv_init_wattr - initialize job (resc_resv) working attribute array
 *		set the types and the "unspecified value" flag
 *
 * @see
 * 		resc_resv_alloc
 *
 * @param[in]	pobj - job (resc_resv) working attribute array
 * @param[in]	obj_type - This value decides the type of pobj attribute.
 *
 * @return	void
 */
static void
job_or_resv_init_wattr(void *pobj, int obj_type)
{
	int	i;
	int		attr_final;
	attribute	*wattr;
	attribute_def	*p_attr_def;

	if (obj_type == RESC_RESV_OBJECT) {
		attr_final = RESV_ATR_LAST;
		wattr = ((resc_resv *)pobj)->ri_wattr;
		p_attr_def = resv_attr_def;
	} else {
		attr_final = JOB_ATR_LAST;
		wattr = ((job *)pobj)->ji_wattr;
		p_attr_def = job_attr_def;
	}

	for (i=0; i<attr_final; i++) {
		clear_attr(&wattr[i], &p_attr_def[i]);
	}
}


/**
 * @brief
 * 		add_resc_resv_to_job - adds a resc_resv structure to
 *		a job if the job is a "reservation job"
 *		Specifically, generates and fills out a resc_resv
 *		structure, attaches it to the job, and appends the
 *		reservation to the server's "svr_allresvs" list.
 *
 * @see
 * 		add_resc_resv_if_resvJob
 *
 * @param[in,out]	pobj - pointer to job struct
 *
 * @return error code
 * @retval	0		- successful
 * @retval	nonzero	- failure
 */
int
add_resc_resv_to_job(job *pjob)
{
	resc_resv	*presv;
	char            buf[256];
	int		state, sub;
	int		rc;

	if (pjob == NULL) {
		return  (PBSE_NONE);
	}

	if ((pjob->ji_wattr[JOB_ATR_reserve_start]
		.at_flags & ATR_VFLAG_SET) == 0
		&& (pjob->ji_wattr[JOB_ATR_reserve_duration]
		.at_flags & ATR_VFLAG_SET) == 0)
		return  (PBSE_NOTRESV);

	if ((presv = resc_resv_alloc()) == NULL)
		return  (PBSE_SYSTEM);

	/* First, fill out the "non-saved" and "quick-save"
	 * area of the resc_resv structure
	 *
	 * remark: pbs_list_head structures in the resc_resv are
	 * initialized as part of the above "resc_resv_alloc()"
	 * Start, end and duration in "quick save" will be set
	 * further on by "start_end_dur_wall ()"
	 */

	presv->ri_jbp = pjob;
	pjob->ji_resvp = presv;

	if (pjob->ji_myResv)
		/*case where have "reservation job" in a reservation*/
		presv->ri_parent = pjob->ji_myResv;

	presv->ri_modified = 1;

	presv->ri_qs.ri_state = pjob->ji_wattr[JOB_ATR_reserve_state]
		.at_val.at_long;
	presv->ri_qs.ri_substate = presv->ri_qs.ri_state;
	presv->ri_qs.ri_type = RESV_JOB_OBJECT;

	(void)strcpy(presv->ri_qs.ri_resvID, pjob->ji_qs.ji_jobid);

	(void)strcpy(presv->ri_qs.ri_fileprefix,
		pjob->ji_qs.ji_jobid);


	/* A resc_resv's "ri_queue" is not the empty string if a
	 * queue has been specifically established to support the
	 * reservation's job(s)
	 */
	(void)strcpy(presv->ri_qs.ri_queue, "");
	presv->ri_qp = NULL;

	/* Now set various of the reservation attributes
	 * based on what is specified for the job
	 * Makes use of the correspondence table located
	 * in resv_attr_def.c
	 */
	rc =  set_resvAttrs_off_jobAttrs(presv, pjob);
	if (rc != 0) {
		resv_purge(presv);
		return (PBSE_INTERNAL);
	}

	/* Set any other needed attributes and resc_resv fields */


	/* set reservation "owner" attribute to user@host */
	resv_attr_def[(int)RESV_ATR_resv_owner].at_free(
		&presv->ri_wattr[(int)RESV_ATR_resv_owner]);
	(void)strcpy(buf, pjob->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str);
	resv_attr_def[(int)RESV_ATR_resv_owner].at_decode(
		&presv->ri_wattr[(int)RESV_ATR_resv_owner],
		NULL, NULL, buf);

	/* make sure owner is put in reservation's User_List */
	if (act_resv_add_owner(&presv->ri_wattr[(int)RESV_ATR_userlst],
		presv, ATR_ACTION_NEW)) {
		resv_purge(presv);
		return (PBSE_BADATVAL);
	}


	presv->ri_wattr[(int)RESV_ATR_resv_type]
	.at_val.at_long = presv->ri_qs.ri_type;
	presv->ri_wattr[(int)RESV_ATR_resv_type].at_flags |= ATR_VFLAG_SET |
		ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;

	eval_resvState(presv, RESVSTATE_add_resc_resv_to_job,
		0, &state, &sub);
	(void)resv_setResvState(presv, state, sub);

	/* process reservation window, duration, wall info */

	if (start_end_dur_wall(presv->ri_jbp, RESV_JOB_OBJECT)) {
		(void)resv_purge(presv);
		return (PBSE_SYSTEM);
	}

	/*For any resource limits that aren't specified,
	 *see if a default value can be determined from
	 *queue, server, etc. and use that value for the limit
	 */

	(void)set_resc_deflt((void *)presv, RESC_RESV_OBJECT, NULL);

	/* write of disk image for this resc_resv structure
	 * occurs when function job_or_resv_save () is called
	 */

	/* put onto the "timed task" list a task that causes
	 * deletion of the reservation if the window passes
	 */

	if (gen_task_EndResvWindow(presv)) {
		(void)resv_purge(presv);
		return (PBSE_SYSTEM);
	}

	/*
	 *We don't want to attach the reservation to the job at this
	 *point but rather in "req_commit" where the job is taken from
	 *the servers's "svr_newjobs" list and placed on the server's
	 *"svr_alljobs" list.
	 *This avoids a timing issue where "stat" requests coming from
	 *the scheduler and the "req_commit" request can be such that
	 *the scheduler sees the reservation before the reservation's
	 *job is on svr_alljobs.  Immediate deleting of such a reservation
	 *ends up deleting the reservation but leaves the job structure
	 *still in existence (on the svr_alljobs list) following the
	 *req_commit request).
	 */

	append_link(&svr_newresvs, &presv->ri_allresvs, presv);
	return (PBSE_NONE);
}


/**
 * @brief
 * 		add_resc_resv_if_resvjob - Checks to see if the job is a
 *		reservation job and if so attempts to attach a
 *		"resc_resv" structure to the job.  If successful, it
 *		also appends the structure to the "svr_allresvs" list
 *
 * @param[in]	pobj - pointer to job struct
 *
 * @return error code
 * @retval	0		- successful
 * @retval	nonzero	- failure
 */
int
add_resc_resv_if_resvJob(job *pjob)
{
	if (pjob == NULL)
		return (PBSE_INTERNAL);

	if ((pjob->ji_wattr[JOB_ATR_reserve_start]
		.at_flags & ATR_VFLAG_SET) == 0)
		return (0);

	return (add_resc_resv_to_job(pjob));
}


/**
 * @brief
 * 		set_resvAttrs_off_jobAttrs - set reservation attributes off
 *		those associated with the "resevation job"
 *
 * @param[in,out]	presv - pointer to reservation struct
 * @param[in,out]	pobj - pointer to job struct
 *
 * @see
 * 		req_quejob
 *
 * @return	int
 * @retval	0	- success
 * @retval	-1	- failed
 */
static	int
set_resvAttrs_off_jobAttrs(resc_resv *presv, job *pjob)
{
	int	i = 0;
	int	ri, ji;
	int	rc = -1;
	resource	*prent;
	resource_def    *prdef;

	attribute_def	*pjd = job_attr_def;
	attribute_def	*prd = resv_attr_def;
	svrattrl	*tpsatl;
	svrattrl	*psatl = NULL;
	pbs_list_head	lhead;	  /*list of attrlist structs*/

	CLEAR_HEAD(lhead);
	for (i=0, ji = index_atrJob_to_atrResv [i][1],
		ri = index_atrJob_to_atrResv [i][0];

		ji != JOB_ATR_LAST;

		++i, ji = index_atrJob_to_atrResv [i][1],
		ri = index_atrJob_to_atrResv [i][0]) {


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

			rc = pjd[ji].at_encode(&pjob->ji_wattr[ji], &lhead,
				prd[ri].at_name, NULL,
				ATR_ENCODE_CLIENT, NULL);
			if (rc < 0) {
				rc = -1;
			} else if (rc > 0) {
				/*at_encode appended something to list*/

				if (psatl)
					psatl = (svrattrl *)GET_NEXT(psatl->al_link);
				else
					psatl = (svrattrl *)GET_NEXT(lhead);

				do {

					if (psatl->al_resc == NULL) {
						rc = prd[ri].at_decode(&presv->ri_wattr[ri],
							psatl->al_name,
							psatl->al_resc,
							psatl->al_value);
						if (rc > 0) {
							rc = -1;
							break;
						}
					} else {
						prdef = find_resc_def(svr_resc_def,
							psatl->al_resc,
							RESV_ATR_LAST);
						if (prdef) {
							if ((prent = add_resource_entry(&presv->ri_wattr[ri],
								prdef)) != 0) {
								rc = prdef->rs_decode(&prent->rs_value,
									psatl->al_name,
									psatl->al_resc,
									psatl->al_value);
								if (rc)
									rc = -1;
							} else
								rc = -1;
						} else
							rc = -1;
					}
					if (rc == -1)
						break;

					tpsatl = (svrattrl *)GET_NEXT(psatl->al_link);
					if (tpsatl)
						psatl = tpsatl;
				} while (tpsatl);	/*Bottom of inner do while loop*/

			} /*Bottom of if/elseif*/
			if (rc == -1)
				break;
		} /*Bottom of outer if */
	} /*Bottom of for loop*/

	free_attrlist(&lhead);
	return (rc);
}

/**
 * @brief
 * 		Set node state to resv-exclusive if either reservation requests
 * 		exclusive placement or the node sharing attribute is to be exclusive or
 * 		reservation requests AOE.
 *
 * @param[in]	presv The reservation being considered
 *
 * @return void
 *
 * @MT-safe: No
 */
void
resv_exclusive_handler(resc_resv *presv)
{
	attribute	*patresc;
	resource_def	*prsdef;
	resource	*pplace;
	pbsnode_list_t 	*pnl;
	int	share_node = VNS_DFLT_SHARED;
	int	share_resv = VNS_DFLT_SHARED;
	char		*scdsel;

	patresc = &presv->ri_wattr[(int) RESV_ATR_resource];
	prsdef = find_resc_def(svr_resc_def, "place", svr_resc_size);
	pplace = find_resc_entry(patresc, prsdef);
	if (pplace && pplace->rs_value.at_val.at_str) {
		if ((place_sharing_type(pplace->rs_value.at_val.at_str,
			VNS_FORCE_EXCLHOST) != VNS_UNSET) ||
			(place_sharing_type(pplace->rs_value.at_val.at_str,
			VNS_FORCE_EXCL) != VNS_UNSET)) {
			share_resv = VNS_FORCE_EXCL;
		}
		if (place_sharing_type(pplace->rs_value.at_val.at_str,
			VNS_IGNORE_EXCL) == VNS_IGNORE_EXCL) {
			share_resv = VNS_IGNORE_EXCL;
		}
	}

	if (share_resv != VNS_FORCE_EXCL) {
		scdsel = presv->ri_wattr[(int)RESV_ATR_SchedSelect].at_val.at_str;
		if (scdsel && strstr(scdsel, "aoe="))
			share_resv = VNS_FORCE_EXCL;
	}
	for (pnl = presv->ri_pbsnode_list; pnl != NULL; pnl= pnl->next) {
		share_node = pnl->vnode->nd_attr[(int)ND_ATR_Sharing].at_val.at_long;

		/*
		 * set node state to resv-exclusive if either node forces exclusive
		 * or reservation requests exclusive and node does not ignore
		 * exclusive.
		 */
		if ((share_node == VNS_FORCE_EXCL) || (share_node == VNS_FORCE_EXCLHOST) ||
			((share_node != VNS_IGNORE_EXCL) && (share_resv == VNS_FORCE_EXCL)) ||
			(((share_node == VNS_DFLT_EXCL) || (share_node == VNS_DFLT_EXCLHOST))
			&& (share_resv != VNS_IGNORE_EXCL))) {
			set_vnode_state(pnl->vnode, INUSE_RESVEXCL, Nd_State_Or);
		}
	}
}

/**
 * @brief
 *  	Find aoe from the reservation request
 *
 * @see
 *		resc_select_action
 *
 * @param[in]	presv	- pointer to the reservation
 *
 * @return	char *
 * @retval	NULL     - no aoe requested
 * @retval	NON NULL - value of aoe requested
 *
 * @par Side Effects:
 *	Memory returned is to be freed by caller
 *
 * @par MT-safe: yes
 *
 */
char*
find_aoe_from_request(resc_resv *presv)
{
	char			*aoe_req = NULL;
	char			*p, *q;
	int			i = 0;

	/* look into schedselect as this is expanded form of select
	 * after taking into account default_chunk.res.
	 */
	if (presv == NULL)
		return NULL;

	if (presv->ri_wattr[(int)RESV_ATR_SchedSelect].at_val.at_str) {
		/* just get first appearance of aoe */
		q = presv->ri_wattr[(int)RESV_ATR_SchedSelect].at_val.at_str;
		if ((p = strstr(q, "aoe=")) != NULL) {
			p += 4; /* strlen("aoe=") = 4 */
			/* get length of aoe name in i. */
			for (q = p; *q && *q != ':' && *q != '+'; i++, q++);
			aoe_req = malloc(i + 1);
			if (aoe_req == NULL) {
				log_err(ENOMEM, __func__, "out of memory");
				return NULL;
			}
			strncpy(aoe_req, p, i);
			aoe_req[i] = '\0';
		}
	}
	return aoe_req;
}
#endif	/*ifndef PBS_MOM*/
