/*
 * 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    req_quejob.c
 *
 * @brief
 * 		Functions relating to the Queue Job Batch Request sequence, including
 * 		Queue Job, Job Script, Ready to Commit, and Commit.
 *
 * Included functions are:
 * 	validate_perm_res_in_select()
 *	req_quejob()
 *	req_jobcredential()
 *	req_jobscript()
 *	req_mvjobfile()
 *	req_commit()
 *	locate_new_job()
 *	req_resvSub()
 *	get_queue_for_reservation()
 *	ignore_attr()
 *	act_resv_add_owner()
 *	handle_qmgr_reply_to_resvQcreate()
 *	validate_place_req_of_job_in_reservation()
 *	get_next_svr_sequence_id()
 *	reset_svr_sequence_window()
 *
 */
#include <pbs_config.h>   /* the master config generated by configure */

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <libutil.h>

#ifdef WIN32
#include  <io.h>
#include "win.h"
#include <sys/timeb.h>
#else
#include <unistd.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/time.h>
#endif

#include "libpbs.h"
#include "server_limits.h"
#include "list_link.h"
#include "attribute.h"
#include "resource.h"
#include "server.h"
#include "work_task.h"
#include "credential.h"
#include "ticket.h"
#include "batch_request.h"
#include "resv_node.h"
#include "queue.h"
#include "job.h"
#include "reservation.h"
#include "net_connect.h"
#include "pbs_error.h"
#include "pbs_nodes.h"
#include "svrfunc.h"
#include "sched_cmds.h"
#include "log.h"
#include "acct.h"
#include "rpp.h"
#include "user.h"
#include "hook.h"
#include "pbs_internal.h"
#include "pbs_sched.h"
#ifndef PBS_MOM
#include "pbs_db.h"
#define SEQ_WIN_INCR 1000 /*save jobid number to database in this increment*/
#endif

#ifdef PBS_MOM
#include "mom_hook_func.h"
#include "placementsets.h"

#ifdef WIN32
#include <direct.h>
#include "mom_func.h"
#else
#include <pwd.h>
#include "mom_func.h"

#if IRIX6_CPUSET == 1
/* Mutual exclusion to avoid race conditions while a job is starting up. */
#include "pbs_mutex.h"
extern volatile pbs_mutex       *pbs_commit_ptr;
#endif /* IRIX6_CPUSET */

#endif /* WIN32 */

extern	char mom_host[PBS_MAXHOSTNAME+1];
#endif	/* PBS_MOM */


/* External Functions Called: */

extern struct connection *svr_conn;
#ifndef PBS_MOM
extern int    remtree(char *);
#ifdef NAS /* localmod 005 */
extern int apply_aoe_inchunk_rules(resource *presc, attribute *pattr,
	void *pobj,
	int type);
#endif /* localmod 005 */
void post_sendmom(struct work_task *);
void post_sendmom_inner(job *jobp, struct batch_request *preq, int wstat, int isrpp, char *err_msg);
#endif	/* PBS_MOM */

/* Global Data Items: */

#ifndef PBS_MOM
extern char *path_spool;
extern struct server server;
extern struct attribute attr_jobscript_max_size;
extern char  server_name[];
extern char *resc_in_err;
#endif	/* PBS_MOM */

extern int	 resc_access_perm;
extern pbs_list_head svr_alljobs;
extern pbs_list_head svr_newjobs;
extern attribute_def job_attr_def[];
extern char *path_jobs;
extern char *path_resvs;
extern char *pbs_o_host;
extern char *msg_script_open;
extern char *msg_script_write;
extern char *msg_jobnew;
extern char *msg_resvQcreateFail;
extern char *msg_defproject;
extern char *msg_mom_reject_root_scripts;
extern int reject_root_scripts;
extern time_t time_now;

#ifndef PBS_MOM
extern char *pbs_server_name;
extern pbs_db_conn_t	*svr_db_conn;
extern char *msg_max_no_minwt;
extern char *msg_min_gt_maxwt;
extern char *msg_nostf_resv;
extern char *msg_nostf_jobarray;
#endif


/* Private Functions in this file */

static	job	*locate_new_job(struct batch_request *preq, char *jobid);

#ifndef PBS_MOM	/* SERVER only */
static	void	handle_qmgr_reply_to_resvQcreate(struct work_task *);
static	int	get_queue_for_reservation(resc_resv *);
static	int	ignore_attr(char *);
static	int	validate_place_req_of_job_in_reservation(job *pj);

/* To generate the job/resv id's locally */
static long long get_next_svr_sequence_id(void);
static long long svr_sequence_window_count = 0;
void reset_svr_sequence_window(void);
long long next_svr_sequence_id = 0;
long long svr_jobidnumber = 0;

static char *pbs_o_que = "PBS_O_QUEUE=";
/**
 * @brief
 * 		validate_perm_res_in_select -	checks to see if the resources
 * 		appearing in select spec 'val' are  valid based on
 * 		"caller's" permission level (i.e. resc_access_perm).
 *
 * @param[in]	val	-	select spec 'val'
 *
 * @return	error code
 * @retval	0	: success
 * @retval	!0	: failure
 * @retval	PBSE_INVALSELECTRESC	: 'resc_in_err' is also set to the name of the offending resource.
 *
 * @note
 *		NOTE:	'resc_in_err' is a malloced-string which is used and
 *		freed inside req_reject().
 *		So upon PBS_INVALSELECTRES return, be sure to
 *		issue req_reject().
*/
static int
validate_perm_res_in_select(char *val)
{
	char        *chunk;
	int 	     nchk;
	int	     nelem;
	struct key_value_pair *pkvp;
	int	     rc = 0;
	int	     j;
	resource_def *presc;

	if (val == NULL)
		return (0);	/* nothing to validate */

	chunk = parse_plus_spec(val, &rc); /* break '+' seperated substrings */
	if (rc != 0)
		return (rc);

	while (chunk) {

#ifdef NAS /* localmod 082 */
		if (parse_chunk(chunk, 0, &nchk, &nelem, &pkvp, NULL) == 0) {
#else
		if (parse_chunk(chunk, &nchk, &nelem, &pkvp, NULL) == 0) {
#endif /* localmod 082 */

			/* first check for any invalid resources in the select */
			for (j=0; j<nelem; ++j) {

				presc = find_resc_def(svr_resc_def, pkvp[j].kv_keyw,
					svr_resc_size);
				if (presc) {
					if ((presc->rs_flags & resc_access_perm) == 0) {
						if ((resc_in_err = strdup(pkvp[j].kv_keyw)) == NULL)
							return PBSE_SYSTEM;
						return PBSE_INVALSELECTRESC;
					}
				}
			} /* for */
		} /* if */
		chunk = parse_plus_spec(NULL, &rc);
		if (rc != 0)
			return (rc);
	} /* while */
	return (0);
}
#endif


#define SET_RESC_SELECT	1
#define SET_RESC_PLACE	2

/**
 * @brief
 *		Queue Job Batch Request processing routine
 *
 * @param[in] - ptr to the decoded request
 *
 */

void
req_quejob(struct batch_request *preq)
{
	int             created_here = 0;
	int             index;
	char            *jid;
	attribute_def   *pdef;
	job             *pj;
	svrattrl        *psatl;
	int             rc;
	int             sock = preq->rq_conn;
	int             resc_access_perm_save;
#ifndef PBS_MOM
	int              set_project = 0;
	int		 i;
	attribute	 tempattr;
	char		 jidbuf[PBS_MAXSVRJOBID+1];
	pbs_queue	*pque;
	char		*qname;
	char            *result;
	resource_def	*prdefnod;
	resource_def	*prdefsel;
	resource_def	*prdefplc;
	resource	*presc;
	conn_t		*conn;
#else
	mom_hook_input_t  hook_input;
	mom_hook_output_t hook_output;
	char		namebuf[MAXPATHLEN+1];
	int		hook_errcode = 0;
	int		hook_rc = 0;
	char		hook_buf[HOOK_MSG_SIZE];
	hook		*last_phook = NULL;
	unsigned int	hook_fail_action = 0;
#endif
	char		hook_msg[HOOK_MSG_SIZE];

	/* set basic (user) level access permission */

	resc_access_perm = ATR_DFLAG_USWR | ATR_DFLAG_Creat;

#ifndef PBS_MOM		/* server server server server */

	conn = get_conn(sock);

	if (!conn) {
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	if (conn->cn_authen & PBS_NET_CONN_FORCE_QSUB_UPDATE) {
		req_reject(PBSE_FORCE_QSUB_UPDATE, 0, preq);
		conn->cn_authen &= ~PBS_NET_CONN_FORCE_QSUB_UPDATE;
		return;
	}

	psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
	while (psatl) {
		if (psatl->al_name == NULL || (!strcasecmp(psatl->al_name, ATTR_l) && psatl->al_resc == NULL)) {
			req_reject(PBSE_IVALREQ, 0, preq);
			return;
		}
		if (!strcasecmp(psatl->al_name, ATTR_l) &&
			!strcasecmp(psatl->al_resc, "select") &&
			((psatl->al_value != NULL) &&
			(psatl->al_value[0] != '\0'))) {

			if ((rc=validate_perm_res_in_select(psatl->al_value)) != 0) {
				req_reject(rc, 0, preq);
				return;
			}
		}
		psatl = (svrattrl *)GET_NEXT(psatl->al_link);
	}

	switch (process_hooks(preq, hook_msg, sizeof(hook_msg),
			pbs_python_set_interrupt)) {
		case 0:	/* explicit reject */
			reply_text(preq, PBSE_HOOKERROR, hook_msg);
			return;
		case 1:   /* explicit accept */
			if (recreate_request(preq) == -1) { /* error */
				/* we have to reject the request, as 'preq' */
				/* may have been partly modified            */
				strcpy(hook_msg,
					"queuejob event: rejected request");
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_HOOK,
					LOG_ERR, "", hook_msg);
				reply_text(preq, PBSE_HOOKERROR, hook_msg);
				return;
			}
			break;
		case 2:	/* no hook script executed - go ahead and accept event*/
			break;
		default:
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, "", "queuejob event: accept req by default");
	}

	prdefsel = find_resc_def(svr_resc_def, "select", svr_resc_size);
	prdefplc = find_resc_def(svr_resc_def, "place",  svr_resc_size);
	prdefnod = find_resc_def(svr_resc_def, "nodes", svr_resc_size);

	/*
	 * if the job id is supplied, the request had better be
	 * from another server
	 */

	if (preq->rq_fromsvr) {
		/* from another server - accept the extra attributes */
		resc_access_perm |= ATR_DFLAG_MGWR | ATR_DFLAG_SvWR;
		jid = preq->rq_ind.rq_queuejob.rq_jid;

	} else if (preq->rq_ind.rq_queuejob.rq_jid[0] != '\0') {
		/* a job id is not allowed from a client */
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	} else {
		/* assign it a job id */

		psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
		i = 0;
		while (psatl) {
			/* Ensure that array_indices_submitted has a proper   */
			/* value (non-"" and non-NULL) before asserting that  */
			/* current job is a job array.			  */
			/* The Hook script can set 	value to "" meaning none  */
			/* was specified making it a normal (non job array)   */
			/* job.						  */
			/* The value should never be NULL and if so, then     */
			/* it won't be a job array.                           */
			if (!strcasecmp(psatl->al_name,
				ATTR_array_indices_submitted) &&
				((psatl->al_value != NULL) &&
				(psatl->al_value[0] != '\0'))) {
				i = 1;
				break;
			}
			psatl = (svrattrl *)GET_NEXT(psatl->al_link);
		}
		/* fetch job id locally*/
		if ((next_svr_sequence_id = get_next_svr_sequence_id()) == -1) {
			req_reject(PBSE_SYSTEM, 0, preq);
			return;
		}
		created_here = JOB_SVFLG_HERE;
		if (i == 0) {	/* Normal job */
			(void)sprintf(jidbuf, "%lld.%s",
				next_svr_sequence_id, server_name);
		} else {	/* Array Job */
			(void)sprintf(jidbuf, "%lld[].%s",
					next_svr_sequence_id, server_name);
		}
		jid = jidbuf;
	}

#else		/* PBS_MOM mom mom mom mom mom mom*/

	if (preq->rq_fromsvr) {		/* must be from a server */
		/* from another server - accept the extra attributes */
		resc_access_perm |= ATR_DFLAG_MGWR | ATR_DFLAG_SvWR |
			ATR_DFLAG_MOM;
		jid = preq->rq_ind.rq_queuejob.rq_jid;
	} else {
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}
#endif		/* PBS_MOM all all all all all */

	/* does job already exist, check both old and new jobs */

	if ((pj = find_job(jid)) == NULL) {
		pj = (job *)GET_NEXT(svr_newjobs);
		while (pj) {
			if (!strcasecmp(pj->ji_qs.ji_jobid, jid))
				break;
			pj = (job *)GET_NEXT(pj->ji_alljobs);
		}
	}

#ifndef PBS_MOM		/* server server server server server server */
	/*
	 * Check if the server is configured for history job info. If yes and
	 * server has the history job with same job id, then don't reject the
	 * queue request with PBSE_JOBEXIST but purge the history job(i.e. with
	 * job state 'M') from the server and accept queue request. If you have
	 * the real job, keeping its history info does not make any sense.
	 * Otherwise SERVER will continue to reject queue request if job already
	 * exists.
	 */
	if (pj != NULL) {
		if ((svr_chk_history_conf()) &&
			(pj->ji_qs.ji_state == JOB_STATE_MOVED)) {
			job_purge(pj);
		} else {
			/* server rejects the queue request */
			req_reject(PBSE_JOBEXIST, 0, preq);
			return;
		}
	}


	/* find requested queue, is it there? */

	qname = preq->rq_ind.rq_queuejob.rq_destin;
	if ((*qname == '\0') || (*qname == '@')) {  /* use default queue */
		pque = get_dfltque();
		rc   = PBSE_QUENODFLT;
	} else { 		/* else find the named queue */
		pque = find_queuebyname(preq->rq_ind.rq_queuejob.rq_destin);
#ifdef NAS /* localmod 075 */
		if (pque == NULL)
			pque = find_resvqueuebyname(qname);
#endif /* localmod 075 */
		rc   = PBSE_UNKQUE;
	}
	if (pque == NULL) {
		req_reject(rc, 0, preq);	   /* not there   */
		return;
	}

	/* create the job structure */
	if ((pj = job_alloc()) == NULL) {
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	pj->ji_newjob = 1; /* set it as new job so wont get saved to DB */
	*pj->ji_qs.ji_fileprefix = '\0';

#else                /* PBS_MOM mom mom mom mom*/
	if (pj) {

		/*
		 * An existing job - likely was checkpointed but in rare
		 * cases may result from a Server-Mom communication error
		 *
		 * Update run version from the new request so server will
		 * accept the obit when the job finishes.
		 */

		psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
		while (psatl) {
			/* look for run count attribute */
			index = find_attr(job_attr_def, psatl->al_name,
				JOB_ATR_LAST);
			if (index == (int)JOB_ATR_run_version) {
				(void)job_attr_def[index].at_decode(
					&pj->ji_wattr[index],
					psatl->al_name, psatl->al_resc,
					psatl->al_value);
				break;
			}
			psatl = (svrattrl *)GET_NEXT(psatl->al_link);
		}

		/* if actually running, tell Server we already have it */
		if (pj->ji_qs.ji_substate == JOB_SUBSTATE_RUNNING) {
			req_reject(PBSE_JOBEXIST, 0, preq);
			return;
		}

		/* if checkpointed, then keep old and skip rest of process */

		if (pj->ji_qs.ji_svrflags & JOB_SVFLG_CHKPT) {
			pj->ji_qs.ji_substate = JOB_SUBSTATE_TRANSIN;
			if (reply_jobid(preq, pj->ji_qs.ji_jobid,
				BATCH_REPLY_CHOICE_Queue) == 0) {
				delete_link(&pj->ji_alljobs);
				append_link(&svr_newjobs, &pj->ji_alljobs, pj);
				pj->ji_qs.ji_un_type = JOB_UNION_TYPE_NEW;
				pj->ji_qs.ji_un.ji_newt.ji_fromsock = sock;
				if (!preq->isrpp) {
					pj->ji_qs.ji_un.ji_newt.ji_fromaddr = get_connectaddr(sock);
				} else {
					struct sockaddr_in* addr = rpp_getaddr(sock);
					if (addr)
						pj->ji_qs.ji_un.ji_newt.ji_fromaddr = (pbs_net_t)ntohl(addr->sin_addr.s_addr);
				}
				pj->ji_qs.ji_un.ji_newt.ji_scriptsz = 0;
			} else {
				close_client(sock);	/* error on reply */
			}
			return;
		}
		/* unlink job from svr_alljobs since will be place on newjobs */
		delete_link(&pj->ji_alljobs);
	} else {
		char basename[MAXPATHLEN + 1];

		/* if not already here, allocate job struct */

		if ((pj = job_alloc()) == NULL) {
			req_reject(PBSE_SYSTEM, 0, preq);
			return;
		}
		/*
		 *
		 * for MOM - rather than make up a hashname, we use the sent
		 * to us by the server as an attribute.
		 */
		psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
		while (psatl) {
			if (!strcmp(psatl->al_name, ATTR_hashname)) {
				(void)strcpy(basename, psatl->al_value);
				if (strlen(basename) <= PBS_JOBBASE)
					strcpy(pj->ji_qs.ji_fileprefix, basename);
				else
					*pj->ji_qs.ji_fileprefix = '\0';
				break;
			}
			psatl = (svrattrl *)GET_NEXT(psatl->al_link);
		}
		(void)strcpy(namebuf, path_jobs);      /* job directory path */
		(void)strcat(namebuf, basename);
		(void)strcat(namebuf, JOB_TASKDIR_SUFFIX);
		if (mkdir(namebuf, 0700) == -1) {
			job_purge(pj);
			req_reject(PBSE_SYSTEM, 0, preq);
			return;
		}
		created_here = JOB_SVFLG_HERE;
	}
#endif          /* PBS_MOM */

	(void)strcpy(pj->ji_qs.ji_jobid, jid);
	pj->ji_modified = 1;
	pj->ji_qs.ji_svrflags = created_here;
	pj->ji_qs.ji_un_type  = JOB_UNION_TYPE_NEW;


	/* decode attributes from request into job structure */

	psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
	resc_access_perm_save = resc_access_perm; /* save perm */
	while (psatl) {

		/* identify the attribute by name */

		index = find_attr(job_attr_def, psatl->al_name, JOB_ATR_LAST);
		if (index < 0) {

			/* didn`t recognize the name */
#ifndef PBS_MOM
			index = JOB_ATR_UNKN;	/* keep as "unknown" for now */
#else	/* is PBS_MOM */
			reply_badattr(PBSE_NOATTR, 1, psatl, preq);
			return;
#endif	/* PBS_MOM */
		}
		pdef = &job_attr_def[index];

#ifndef PBS_MOM
		if (index == JOB_ATR_project) {
			set_project = 1;
		}
#endif

		/* Is attribute not writeable by manager or by a server? */
		/* Exempt attributes set by the hook script */
		resc_access_perm = resc_access_perm_save; /* reset */
		if ((psatl->al_flags & ATR_VFLAG_HOOK)) {
			resc_access_perm = ATR_DFLAG_USWR | \
					    ATR_DFLAG_OPWR | \
					    ATR_DFLAG_MGWR | \
				            ATR_DFLAG_SvWR | \
					    ATR_DFLAG_Creat;
		}
		if (((pdef->at_flags & resc_access_perm) == 0)) {
			job_purge(pj);
			reply_badattr(PBSE_ATTRRO, 1, psatl, preq);
			return;
		}

		/* decode attribute */

		if ((index == JOB_ATR_resource) && (psatl->al_resc != NULL) &&
			(strcmp(psatl->al_resc, "neednodes") == 0))
			rc = 0;
		else
			rc = pdef->at_decode(&pj->ji_wattr[index],
				psatl->al_name, psatl->al_resc, psatl->al_value);
#ifndef PBS_MOM
		if (rc != 0) {
			if (rc == PBSE_UNKRESC) {

				/* unknown resources not allow in Exec queue */

				if (pque->qu_qs.qu_type == QTYPE_Execution) {
					job_purge(pj);
					reply_badattr(rc, 1, psatl, preq);
					return;
				}
			} else {
				/* any other error is fatal */
				job_purge(pj);
				reply_badattr(rc, 1, psatl, preq);
				return;
			}
		}
#else	/* PBS_MOM MOM MOM MOM */
		if (rc != 0) {
			/* all  errors are fatal for MOM */

			job_purge(pj);
			reply_badattr(rc, 1, psatl, preq);
			return;
		}
		if (psatl->al_op == DFLT) {
			if (psatl->al_resc) {

				resource	*presc;
				resource_def	*prdef;

				prdef = find_resc_def(svr_resc_def, psatl->al_resc,
					svr_resc_size);
				if (prdef == 0) {
					job_purge(pj);
					reply_badattr(rc, 1, psatl, preq);
					return;
				}
				presc = find_resc_entry(&pj->ji_wattr[index], prdef);
				if (presc)
					presc->rs_value.at_flags |= ATR_VFLAG_DEFLT;
			} else {
				pj->ji_wattr[index].at_flags |= ATR_VFLAG_DEFLT;
			}
		}
#endif	/* PBS_MOM */

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

#ifndef PBS_MOM

	/* perform any at_action routine declared for the attributes */

	for (i=0; i<JOB_ATR_LAST; ++i) {
		pdef = &job_attr_def[i];
		if ((pj->ji_wattr[i].at_flags & ATR_VFLAG_SET) &&
			(pdef->at_action)) {
			rc = pdef->at_action(&pj->ji_wattr[i], pj, ATR_ACTION_NEW);
			if (rc) {
				job_purge(pj);
				req_reject(rc, i, preq);
				return;
			}
		}
	}

	/*
	 * Now that the attributes have been decoded, we can setup some
	 * additional parameters and perform a few more checks.
	 *
	 * First, set some items based on who created the job...
	 */

	if (created_here) {	/* created here */
		int l;
		char buf[256];

		/* check that job has a jobname */

		if ((pj->ji_wattr[(int)JOB_ATR_jobname].at_flags &
			ATR_VFLAG_SET) == 0) {
			job_attr_def[(int)JOB_ATR_jobname].at_decode(
				&pj->ji_wattr[(int)JOB_ATR_jobname],
				NULL, NULL, "none");
		}

		/* check resources in the Resource_List are valid job wide */

		if (pj->ji_wattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET) {
			int have_selectplace = 0;
			resource_def *prdefbad;

			presc = (resource *)GET_NEXT(pj->ji_wattr[(int)JOB_ATR_resource].at_val.at_list);

			prdefbad = NULL;
			while (presc) {
				if (presc->rs_defin == prdefsel) {
					have_selectplace |= SET_RESC_SELECT;
				} else if (presc->rs_defin == prdefplc) {
					have_selectplace |= SET_RESC_PLACE;
				} else if ((presc->rs_defin == prdefnod) &&
					(presc->rs_value.at_val.at_str != NULL)) {
					/*
					 * if "nodes" is set and has non-NULL value,
					 * remember as potential bad resource
					 * if this appears along "select" or "place".
					 */
					prdefbad = presc->rs_defin;
				} else if (
					(presc->rs_defin->rs_flags & ATR_DFLAG_CVTSLT) &&
					(presc->rs_value.at_flags & ATR_VFLAG_SET)) {
					/*
					 * if this resource is not "select", "place",
					 * or "nodes", but is meant to appear inside
					 * a "select" line, then remember as potential
					 * bad resource if this appears along
					 * "select" or "place".
					 */

					prdefbad = presc->rs_defin;
				}
				presc = (resource *)GET_NEXT(presc->rs_link);
			}
			if (have_selectplace && prdefbad) {
				if (prdefbad == prdefnod)
					rc = PBSE_INVALNODEPLACE;
				else
					rc = PBSE_INVALJOBRESC;
				if ((resc_in_err = strdup(prdefbad->rs_name)) == NULL)
					rc = PBSE_SYSTEM;
				job_purge(pj);
				req_reject(rc, 0, preq);
				return;
			}
			if (have_selectplace == SET_RESC_PLACE) {
				/* cannot have place without select */
				job_purge(pj);
				req_reject(PBSE_PLACENOSELECT, 0, preq);
				return;
			}
		}

		/* check value of priority */

		if (pj->ji_wattr[(int)JOB_ATR_priority].at_flags &
			ATR_VFLAG_SET) {
			if ((pj->ji_wattr[(int)JOB_ATR_priority].
				at_val.at_long < -1024) ||
				(pj->ji_wattr[(int)JOB_ATR_priority].
				at_val.at_long > 1024)) {
				job_purge(pj);
				req_reject(PBSE_BADATVAL, 0, preq);
				return;
			}
		}

		/* set job owner attribute to user@host */

		job_attr_def[(int)JOB_ATR_job_owner].at_free(
			&pj->ji_wattr[(int)JOB_ATR_job_owner]);
		(void)strcpy(buf, preq->rq_user);
		(void)strcat(buf, "@");
		(void)strcat(buf, preq->rq_host);
		job_attr_def[(int)JOB_ATR_job_owner].at_decode(
			&pj->ji_wattr[(int)JOB_ATR_job_owner],
			NULL, NULL, buf);

		/* set create time */

		pj->ji_wattr[(int)JOB_ATR_ctime].at_val.at_long =(long)time_now;
		pj->ji_wattr[(int)JOB_ATR_ctime].at_flags |=
			ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;

		/* set hop count = 1 */

		pj->ji_wattr[(int)JOB_ATR_hopcount].at_val.at_long = 1;
		pj->ji_wattr[(int)JOB_ATR_hopcount].at_flags |=
			ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;

		/* need to set certain environmental variables per POSIX */

		clear_attr(&tempattr, &job_attr_def[(int)JOB_ATR_variables]);
		(void)strcpy(buf, pbs_o_que);
		(void)strcat(buf, pque->qu_qs.qu_name);
		if (get_variable(pj, pbs_o_host) == NULL) {
			(void)strcat(buf, ",");
			(void)strcat(buf, pbs_o_host);
			(void)strcat(buf, "=");
			(void)strcat(buf, preq->rq_host);
		}
		job_attr_def[(int)JOB_ATR_variables].at_decode(&tempattr,
			NULL, NULL, buf);
		job_attr_def[(int)JOB_ATR_variables].at_set(
			&pj->ji_wattr[(int)JOB_ATR_variables],
			&tempattr, INCR);
		job_attr_def[(int)JOB_ATR_variables].at_free(&tempattr);

		/* if JOB_ATR_outpath/JOB_ATR_errpath not set, set default */

		if (!(pj->ji_wattr[(int)JOB_ATR_outpath].at_flags &
			ATR_VFLAG_SET)) {
			pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str =
				prefix_std_file(pj, (int)'o');
			pj->ji_wattr[(int)JOB_ATR_outpath].at_flags |=
				ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;
		} else {
			l = strlen(pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str);
			if (l > 0) {
				if (pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str[l -1] == ':') {

					cat_default_std(pj, (int)'o', pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str, &result);
					if (result) {
						free(pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str);
						pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str = result;
					}
				}
			}
		}

		if (!(pj->ji_wattr[(int)JOB_ATR_errpath].at_flags &
			ATR_VFLAG_SET)) {
			pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str =
				prefix_std_file(pj, (int)'e');
			pj->ji_wattr[(int)JOB_ATR_errpath].at_flags |=
				ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;
		} else {
			l = strlen(pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str);
			if (l > 0) {
				if (pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str[l -1] == ':') {

					cat_default_std(pj, (int)'e', pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str, &result);
					if (result) {
						free(pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str);
						pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str = result;
					}
				}
			}
		}

		if ((pj->ji_wattr[(int)JOB_ATR_outpath].at_val.at_str==0) ||
			(pj->ji_wattr[(int)JOB_ATR_errpath].at_val.at_str==0)) {
			job_purge(pj);
			req_reject(PBSE_NOATTR, 0, preq);
			return;
		}

		if ((pj->ji_wattr[(int)JOB_ATR_interactive].at_flags & ATR_VFLAG_SET) && (pj->ji_wattr[(int)JOB_ATR_array_indices_submitted].at_flags & ATR_VFLAG_SET)) {
			job_purge(pj);
			req_reject(PBSE_NOSUP, 0, preq);
			return;

		}
		/* Interactive jobs are not rerunable */

		if ((pj->ji_wattr[JOB_ATR_interactive].at_flags & ATR_VFLAG_SET) &&
			pj->ji_wattr[JOB_ATR_interactive].at_val.at_long) {
			pj->ji_wattr[JOB_ATR_rerunable].at_val.at_long = 0;
			pj->ji_wattr[JOB_ATR_rerunable].at_flags |= ATR_VFLAG_SET;
		}

		if ( (pj->ji_wattr[(int)JOB_ATR_project].at_flags & \
							ATR_VFLAG_SET) == 0 ) {
			job_attr_def[(int)JOB_ATR_project].at_decode(
				&pj->ji_wattr[(int)JOB_ATR_project],
				NULL, NULL, PBS_DEFAULT_PROJECT);
		}

	} else {		/* job was created elsewhere and moved here */

		/* make sure job_owner is set, error if not */

		if (!(pj->ji_wattr[(int)JOB_ATR_job_owner].at_flags &
			ATR_VFLAG_SET)) {
			job_purge(pj);
			req_reject(PBSE_IVALREQ, 0, preq);
			return;
		}

		/* increment hop count */

		pj->ji_wattr[(int)JOB_ATR_hopcount].at_flags |=
			ATR_VFLAG_MODCACHE;
		if (++pj->ji_wattr[(int)JOB_ATR_hopcount].at_val.at_long >
			PBS_MAX_HOPCOUNT) {
			job_purge(pj);
			req_reject(PBSE_HOPCOUNT, 0, preq);
			return;
		}

		/* make sure that if job belonged to an advance reservation
		 * queue, that old information is wiped out.  If being moved
		 * into an advance reservation queue, the reservation's ID
		 * gets attached later in the code
		 */
		job_attr_def[(int)JOB_ATR_reserve_ID]
		.at_decode(&pj->ji_wattr[(int)JOB_ATR_reserve_ID],
			NULL, NULL, NULL);
	}

	/* set up at_server attribute for status */

	job_attr_def[(int)JOB_ATR_at_server].at_decode(
		&pj->ji_wattr[(int)JOB_ATR_at_server],
		NULL, NULL, pbs_server_name);

	/* If enabled, check the server's required cred type */

	if ((server.sv_attr[SRV_ATR_ReqCredEnable].at_flags & ATR_VFLAG_SET) &&
		server.sv_attr[SRV_ATR_ReqCredEnable].at_val.at_long &&
		(server.sv_attr[SRV_ATR_ReqCred].at_flags & ATR_VFLAG_SET)) {
		char	*reqc = server.sv_attr[SRV_ATR_ReqCred].at_val.at_str;
		char	*jobc = pj->ji_wattr[(int)JOB_ATR_cred].at_val.at_str;
		/*
		 **	The server requires a cred, if job has none, or
		 **	it is the wrong one, reject.
		 */
		if ((pj->ji_wattr[(int)JOB_ATR_cred].at_flags &
			ATR_VFLAG_SET) == 0 ||
			strcmp(reqc, jobc) != 0) {
			job_purge(pj);
			req_reject(PBSE_BADCRED, 0, preq);
			return;
		}
	}

	/*
	 * See if the job is qualified to go into the requested queue.
	 * Note, if an execution queue, then ji_qs.ji_un.ji_exect is set up
	 *
	 * svr_chkque is called way down here because it needs to have the
	 * job structure and attributes already set up.
	 */

	rc = svr_chkque(pj, pque, preq->rq_host, MOVE_TYPE_Move);
	if (rc) {
		if (pj->ji_clterrmsg)
			reply_text(preq, rc, pj->ji_clterrmsg);
		else
			req_reject(rc, 0, preq);
		job_purge(pj);
		return;
	}


	/*
	 * if single, signon password scheme is in place, only allow submission
	 * if a per user per server password exists.
	 *
	 */

	/* Important, only jobs in execution queues get euser attribute set */
	if (((pj->ji_wattr[(int)JOB_ATR_euser].at_flags & ATR_VFLAG_SET) &&
		pj->ji_wattr[(int)JOB_ATR_euser].at_val.at_str) &&
		(server.sv_attr[SRV_ATR_ssignon_enable].at_flags & ATR_VFLAG_SET) &&
		(server.sv_attr[SRV_ATR_ssignon_enable].at_val.at_long == 1)) {
			char *credb = NULL;
			size_t credl = 0;
			int	ret;

			ret = user_read_password(pj->ji_wattr[(int)JOB_ATR_euser].at_val.at_str,
				&credb, &credl);

			if (credb) {
				(void)free(credb);
				credb = NULL;
			}
			if (ret == 1) {	/* no entry */
				job_purge(pj);
				req_reject(PBSE_SSIGNON_NO_PASSWORD, 0, preq);
				return;
			}
	}

	(void)strcpy(pj->ji_qs.ji_queue, pque->qu_qs.qu_name);

	/* Is job being submitted to a reservation queue?
	 * If yes, have the job point to the resc_resv object and
	 * update the job attribute JOB_ATR_reservation.
	 *
	 * Also check for conflict for job and reservation place spec
	 */
	if (pque->qu_resvp) {
		job_attr_def[(int)JOB_ATR_reserve_ID].at_free(
			&pj->ji_wattr[(int)JOB_ATR_reserve_ID]);
		job_attr_def[(int)JOB_ATR_reserve_ID].at_decode(
			&pj->ji_wattr[(int)JOB_ATR_reserve_ID],
			NULL, NULL,
			pque->qu_resvp->ri_qs.ri_resvID);
		pj->ji_myResv = pque->qu_resvp;

		if (!validate_place_req_of_job_in_reservation(pj)) {
			job_purge(pj);
			psatl = (svrattrl *)GET_NEXT(
				preq->rq_ind.rq_queuejob.rq_attr);
			while (psatl) {
				if (!strcasecmp(psatl->al_name, ATTR_l) &&
					!strcasecmp(psatl->al_resc, "place")) {
					reply_badattr(PBSE_JOBINRESV_CONFLICT, 1,
						psatl, preq);
					return;
				}
				psatl = (svrattrl *)GET_NEXT(psatl->al_link);
			}
			if (!psatl) {
				req_reject(PBSE_JOBINRESV_CONFLICT, 0, preq);
				return;
			}
		}
	}

	pj->ji_wattr[(int)JOB_ATR_substate].at_val.at_long =
		JOB_SUBSTATE_TRANSIN;
	pj->ji_wattr[(int)JOB_ATR_substate].at_flags |=
		ATR_VFLAG_SET|ATR_VFLAG_MODCACHE;


	/* If this is a reservation job", "reserve_start", "reserve_end",
	 * and "reserve_duration" need to be consistent and determined,
	 * if yet to be determined.  Also, the reservation state needs to
	 * be initialized (to UNCONFIRMED) and a "resc_resv" structure
	 * associated to the job
	 */

	if (start_end_dur_wall((void*)pj, JOB_OBJECT) == -1) {
		job_purge(pj);
		req_reject(PBSE_BADTSPEC, 0, preq);
		return;
	}

	Update_Resvstate_if_resv(pj);
	if (add_resc_resv_if_resvJob(pj) != 0) {
		job_purge(pj);
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	/* action routine for select does not have reservation data hence
	 * returns without doing checks. Checks are called now.
	 */
	presc = find_resc_entry(&pj->ji_wattr[(int)JOB_ATR_resource], prdefsel);
	if (presc) {
		rc = apply_aoe_inchunk_rules(presc, &pj->ji_wattr[(int)JOB_ATR_resource],
			pj, PARENT_TYPE_JOB);
		if (rc) {
			job_purge(pj);
			req_reject(rc, 0, preq);
			return;
		}
	}
#endif		/* not PBS_MOM */

	/* set remaining job structure elements			*/

	pj->ji_qs.ji_state =    JOB_STATE_TRANSIT;
	pj->ji_qs.ji_substate = JOB_SUBSTATE_TRANSIN;
	pj->ji_wattr[(int)JOB_ATR_state].at_val.at_char = 'T';

	pj->ji_wattr[(int)JOB_ATR_mtime].at_val.at_long = (long)time_now;
	pj->ji_wattr[(int)JOB_ATR_mtime].at_flags |=
		ATR_VFLAG_SET|ATR_VFLAG_MODCACHE;

	pj->ji_qs.ji_un_type = JOB_UNION_TYPE_NEW;
	pj->ji_qs.ji_un.ji_newt.ji_fromsock = sock;
	pj->ji_qs.ji_un.ji_newt.ji_scriptsz = 0;

#ifdef PBS_MOM
	mom_hook_input_init(&hook_input);
	hook_input.pjob = pj;

	mom_hook_output_init(&hook_output);
	hook_output.reject_errcode = &hook_errcode;
	hook_output.last_phook = &last_phook;
	hook_output.fail_action = &hook_fail_action;

	switch ((hook_rc=mom_process_hooks(HOOK_EVENT_EXECJOB_BEGIN, PBS_MOM_SERVICE_NAME,
			mom_host, &hook_input, &hook_output, hook_buf, sizeof(hook_buf), 1))) {
		case 1:   /* explicit accept */
			break;
		case 2:	/* no hook script executed - go ahead and accept event*/
			break;
		default:
			/* a value of '0' means explicit reject encountered. */
			if (hook_rc != 0) {
				/* we've hit an internal error (malloc error, full disk, etc...), so */
				/* treat this now like a  hook error so hook fail_action  */
				/* will be consulted.  */
				/* Before, behavior of an internal error was to ignore it! */
				hook_errcode = PBSE_HOOKERROR;
			}
			if (hook_errcode == PBSE_HOOKERROR) { /* error */
				/* piggy back the hook_name in the message */
				/* to be stripped out by the server upon */
				/* processing hook fail_action */
				snprintf(hook_msg, sizeof(hook_msg), "%s,%.*s",
					last_phook ? last_phook->hook_name : "",
					(int)(sizeof(hook_msg) - (last_phook ? strlen(last_phook->hook_name) : 0) - 2),
					(hook_rc == 0) ? hook_buf : "internal error");
			} else {
				snprintf(hook_msg, sizeof(hook_msg), ",%.*s",
					(int)(sizeof(hook_msg) - 2), hook_buf);
			}

			reply_text(preq, hook_errcode, hook_msg);
			job_purge(pj);
			return;
	}
#endif

	/* acknowledge the request with the job id */
	if (!preq->isrpp) {
		pj->ji_qs.ji_un.ji_newt.ji_fromaddr = get_connectaddr(sock);
		/* acknowledge the request with the job id */
		if (reply_jobid(preq, pj->ji_qs.ji_jobid, BATCH_REPLY_CHOICE_Queue) != 0) {
			/* reply failed, purge the job and close the connection */

			close_client(sock);
			job_purge(pj);
			return;
		}
	} else {
		struct sockaddr_in* addr = rpp_getaddr(sock);
		if (addr)
			pj->ji_qs.ji_un.ji_newt.ji_fromaddr = (pbs_net_t) ntohl(addr->sin_addr.s_addr);
		free_br(preq);
		/* No need of acknowledge for RPP */
	}

#ifndef PBS_MOM
	if (set_project && (pj->ji_wattr[(int)JOB_ATR_project].at_flags &
	ATR_VFLAG_SET)  && \
	     (strcmp(pj->ji_wattr[(int)JOB_ATR_project].at_val.at_str,
		PBS_DEFAULT_PROJECT) == 0)) {
		sprintf(log_buffer, msg_defproject,
			ATTR_project, PBS_DEFAULT_PROJECT);

#ifdef NAS /* localmod 107 */
		log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_JOB, LOG_INFO,
			pj->ji_qs.ji_jobid, log_buffer);
#else
		log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
			pj->ji_qs.ji_jobid, log_buffer);
#endif /* localmod 107 */
	}
#endif

	/* link job into server's new jobs list request  */

	append_link(&svr_newjobs, &pj->ji_alljobs, pj);

#ifndef	PBS_MOM
	{
		job	*pjob;
		int	myport, port;
		char	*myhost, *host;

		/*
		 **	If the JOB_ATR_block attribute is set, look through the
		 **	other jobs to make sure the host/port combo is unique.
		 */
		if ((pj->ji_wattr[(int)JOB_ATR_block].at_flags & ATR_VFLAG_SET) == 0)
			return;

		myhost = get_hostPart(pj->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str);
		if (myhost == NULL)
			return;
		myport = (int)pj->ji_wattr[(int)JOB_ATR_block].at_val.at_long;
		if (myport == 0)
			return;

		for (pjob = (job *)GET_NEXT(svr_alljobs);
			pjob != NULL;
			pjob = (job *)GET_NEXT(pjob->ji_alljobs)) {
			if (pjob == pj)
				continue;
			if ((pjob->ji_wattr[(int)JOB_ATR_block].at_flags &
				ATR_VFLAG_SET) == 0)
				continue;

			port = (int)pjob->ji_wattr[(int)JOB_ATR_block].at_val.at_long;
			if (port != myport)
				continue;
			host = get_hostPart(pjob->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str);
			if (host == NULL)
				continue;
			if (strcmp(host, myhost) != 0)
				continue;

			/* we found a job with the same host/port */
			sprintf(log_buffer,
				"job %s has duplicate BLOCK host %s port %d",
				pjob->ji_qs.ji_jobid, host, port);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_ERR,
				pj->ji_qs.ji_jobid, log_buffer);

			/* unset the old job's JOB_ATR_block */
			pjob->ji_wattr[(int)JOB_ATR_block].at_val.at_long = 0;
			pjob->ji_wattr[(int)JOB_ATR_block].at_flags &= ~ATR_VFLAG_SET;
			pjob->ji_wattr[(int)JOB_ATR_block].at_flags |=
				ATR_VFLAG_MODCACHE;

		}
	}
#endif	/* not PBS_MOM */
}

/**
 * @brief
 * 		req_jobcredential - receive a set of credentials to be used by the job
 *
 * @param[in]	preq	-	ptr to the decoded request
 */
void
req_jobcredential(struct batch_request *preq)
{
	job	*pj;
	int	type;
	char	*cred;
	size_t	len;

	DBPRT(("%s: entered\n", __func__))
	pj = locate_new_job(preq, NULL);
	if (pj == NULL) {
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}
	if (pj->ji_qs.ji_substate != JOB_SUBSTATE_TRANSIN) {
		delete_link(&pj->ji_alljobs);
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}

#ifndef PBS_MOM
	if (svr_authorize_jobreq(preq, pj) == -1) {
		req_reject(PBSE_PERM, 0, preq);
		return;
	}
#endif		/* PBS_MOM */

	type = pj->ji_extended.ji_ext.ji_credtype =
		preq->rq_ind.rq_jobcred.rq_type;
	cred = preq->rq_ind.rq_jobcred.rq_data;
	len = (size_t)preq->rq_ind.rq_jobcred.rq_size;

	switch (type) {

		default:
			if (write_cred(pj, cred, len) == -1) {
				delete_link(&pj->ji_alljobs);
				req_reject(PBSE_SYSTEM, 0, preq);
			}
			else
				reply_ack(preq);
			break;
	}

	return;
}

/**
 * @brief
 *		Receive job script section
 * @par Functionality:
 *		For Mom, each section is appended to the file
 *		For Server, its appended to the ji_script member
 *		of the job structure, to be later saved to the DB
 *
 *  @param[in,out]	preq	-	Pointer to batch request structure
 */

void
req_jobscript(struct batch_request *preq)
{
	job	*pj;
#ifdef PBS_MOM
	int	 filemode = 0700;
	int	 fds;
	char namebuf[MAXPATHLEN];
#else
	char *temp;
	u_Long size;
#endif

	pj = locate_new_job(preq, NULL);
	if (pj == NULL) {
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}
	if (pj->ji_qs.ji_substate != JOB_SUBSTATE_TRANSIN) {
		delete_link(&pj->ji_alljobs);
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}
#ifndef PBS_MOM
	if (svr_authorize_jobreq(preq, pj) == -1) {
		req_reject(PBSE_PERM, 0, preq);
		return;
	}
#else
	/* mom - if job has been checkpointed, discard script,already have it */
	if (pj->ji_qs.ji_svrflags & JOB_SVFLG_CHKPT) {
		/* do nothing, ignore script */
		reply_ack(preq);
		return;
	}
#endif		/* PBS_MOM */

#ifdef PBS_MOM


	if (reject_root_scripts == TRUE) {
		if ((pj->ji_wattr[(int)JOB_ATR_euser].at_flags & \
							ATR_VFLAG_SET) &&
			(pj->ji_wattr[(int)JOB_ATR_euser].at_val.at_str != NULL)) {
#ifdef WIN32

			/* equivalent of root */
			if (!isAdminPrivilege(pj->ji_wattr[(int)JOB_ATR_euser].\
                                                              at_val.at_str))
#else
			struct passwd		*pwdp;

			pwdp = getpwnam(pj->ji_wattr[(int)JOB_ATR_euser].\
								at_val.at_str);
			if ((pwdp != NULL) && (pwdp->pw_uid == 0))
#endif
			{

				log_err(-1, "req_jobscript",
					msg_mom_reject_root_scripts);
				delete_link(&pj->ji_alljobs);
				req_reject(PBSE_MOM_REJECT_ROOT_SCRIPTS, 0, preq);
				return;
			}
		}
	}

	(void)strcpy(namebuf, path_jobs);
	if (*pj->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pj->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pj->ji_qs.ji_jobid);
	(void)strcat(namebuf, JOB_SCRIPT_SUFFIX);

	if (pj->ji_qs.ji_un.ji_newt.ji_scriptsz == 0) {
		fds = open(namebuf, O_WRONLY|O_CREAT, filemode);
	} else {
		fds = open(namebuf, O_WRONLY | O_APPEND, filemode);
	}
	if (fds < 0) {
		log_err(errno, "req_jobscript", msg_script_open);
		delete_link(&pj->ji_alljobs);
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

#ifdef WIN32
	secure_file2(namebuf, "Administrators", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED, "Everyone", READS_MASK|READ_CONTROL);
	setmode(fds, O_BINARY);
#endif /* WIN32 */

	if (write(fds, preq->rq_ind.rq_jobfile.rq_data,
		(unsigned)preq->rq_ind.rq_jobfile.rq_size) !=
		preq->rq_ind.rq_jobfile.rq_size) {
		log_err(errno, "req_jobscript", msg_script_write);
		delete_link(&pj->ji_alljobs);
		req_reject(PBSE_SYSTEM, 0, preq);
		(void)close(fds);
		return;
	}
	(void)close(fds);
#else /* server - server - server - server */
	/* add the script to the job */
	size = get_bytes_from_attr(&attr_jobscript_max_size);
	if (pj->ji_qs.ji_un.ji_newt.ji_scriptsz > size){
		req_reject(PBSE_JOBSCRIPTMAXSIZE, 0, preq);
		return;
	}
	temp = realloc(pj->ji_script, pj->ji_qs.ji_un.ji_newt.ji_scriptsz +
		preq->rq_ind.rq_jobfile.rq_size + 1);
	if (!temp) {
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}
	pj->ji_script = temp;
	memmove(pj->ji_script + pj->ji_qs.ji_un.ji_newt.ji_scriptsz,
		preq->rq_ind.rq_jobfile.rq_data,
		(size_t)preq->rq_ind.rq_jobfile.rq_size);
#endif
	pj->ji_qs.ji_un.ji_newt.ji_scriptsz += preq->rq_ind.rq_jobfile.rq_size;

#ifndef PBS_MOM
	pj->ji_script[pj->ji_qs.ji_un.ji_newt.ji_scriptsz] = '\0';
#endif

	pj->ji_qs.ji_svrflags = (pj->ji_qs.ji_svrflags & ~JOB_SVFLG_CHKPT) |
		JOB_SVFLG_SCRIPT;      /* has a script file */

	reply_ack(preq);
}

#ifndef PBS_MOM
/* the following is for the server only, MOM has her own version below */

/**
 * @brief
 * 		req_mvjobfile - receive a job file
 *		This request is used to move a file associated with a job, typically
 *		the standard output or error, between a server and a server or from
 *		a mom back to a server.  For a server, the destination is alway
 *		within the spool directory.
 *
 * @param[in,out]	preq	-	ptr to the decoded request
 */

void
req_mvjobfile(struct batch_request *preq)
{
	int	 fds;
	char	 namebuf[MAXPATHLEN+1];
	job	*pj;
#ifndef WIN32
	mode_t	 cur_mask;
	struct stat sb;
#endif

	pj = locate_new_job(preq, NULL);
	if (pj == NULL)
		pj = find_job(preq->rq_ind.rq_jobfile.rq_jobid);

	if ((preq->rq_fromsvr == 0) || (pj == NULL)) {
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}


	(void)strcpy(namebuf, path_spool);
	if (*pj->ji_qs.ji_fileprefix != '\0')
		(void)strcat(namebuf, pj->ji_qs.ji_fileprefix);
	else
		(void)strcat(namebuf, pj->ji_qs.ji_jobid);
	switch ((enum job_file)preq->rq_ind.rq_jobfile.rq_type) {
		case StdOut:
			(void)strcat(namebuf, JOB_STDOUT_SUFFIX);
			break;

		case StdErr:
			(void)strcat(namebuf, JOB_STDERR_SUFFIX);
			break;

		case Chkpt:
			(void)strcat(namebuf, JOB_CKPT_SUFFIX);
			break;

		default:
			req_reject(PBSE_IVALREQ, 0, preq);
			return;
	}

#ifndef WIN32
	/* Windows does not do symlinks, we only need to check on Unix/Linux */
	if (lstat(namebuf, &sb) == 0) {
		/* if it exists, the file must be a prior copy which means */
		/* it must be a regular file and owned by me (root)        */
		if (((sb.st_mode & S_IFMT) != S_IFREG) ||
			(sb.st_nlink != 1)                ||
			(sb.st_uid   != 0)) {
			/* this does not meet the above conditions    */
			/* someone may be trying to hack in a link to */
			/* cause the link target to be overwritten    */
			/* lets log it and leave the file as evidence */
			log_suspect_file(__func__, "wrong type or owner", namebuf, &sb);
		}
	}
	if (preq->rq_ind.rq_jobfile.rq_sequence == 0) {
		char ntmpbuf[MAXPATHLEN+1];

		/* receiving first piece, so create new file securely */
		/* will discard any existing file (via rename)	      */
		snprintf(ntmpbuf, sizeof(ntmpbuf), "%s", namebuf);
		if (strlen(ntmpbuf) > (sizeof(ntmpbuf) - 8))
			ntmpbuf[sizeof(ntmpbuf) - 8] = '\0';
		strcat(ntmpbuf, "XXXXXX");	/* template for mkstemp() */
		cur_mask = umask(077);		/* force to create -rw------ */
		fds = mkstemp(ntmpbuf);
		(void)umask(cur_mask);
		if (fds != -1) {
			/* now rename to the filename we want */
			if (rename(ntmpbuf, namebuf) == -1) {
				close(fds);
				unlink(ntmpbuf);
				fds = -1;
			}
		}

	} else {
		/* receiving a follow-on chunk of data, file	*/
		/* should already exist as regular file		*/
		fds = open(namebuf, O_WRONLY|O_APPEND|O_Sync, 0600);
		if (fds != -1) {
			if (lstat(namebuf, &sb) == 0) {
				/* if exists, file must be a regular file and be */
				/* owned by me (root) 				 */
				if (((sb.st_mode & S_IFMT) != S_IFREG) ||
					(sb.st_nlink != 1)                ||
					(sb.st_uid   != 0)) {
					/* this does not meet the above conditions */
					log_suspect_file(__func__, "wrong type or owner",
						namebuf, &sb);
					close(fds);
					unlink(namebuf);
					fds = -1;
				}
			} else {
				sprintf(log_buffer, "unable to lstat %s", namebuf);
				log_err(errno, __func__, log_buffer);
				close(fds);
				fds = -1;
			}
		}
	}

#else	/* is WIN32  */

	if (preq->rq_ind.rq_jobfile.rq_sequence == 0)
		fds = open(namebuf, O_WRONLY|O_CREAT|O_EXCL|O_Sync, 0600);
	else
		fds = open(namebuf, O_WRONLY|O_APPEND|O_Sync, 0600);

#endif	/* WIN32 */

	if (fds < 0) {
		log_err(errno, "req_mvjobfile", msg_script_open);
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}
#ifdef WIN32
	secure_file(namebuf, "Administrators", READS_MASK|WRITES_MASK|STANDARD_RIGHTS_REQUIRED);
	setmode(fds, O_BINARY);
#endif

	if (write(fds, preq->rq_ind.rq_jobfile.rq_data,
		(unsigned)preq->rq_ind.rq_jobfile.rq_size) !=
		preq->rq_ind.rq_jobfile.rq_size) {
		log_err(errno, "req_jobfile", msg_script_write);
		req_reject(PBSE_SYSTEM, 0, preq);
		(void)close(fds);
		return;
	}
	(void)close(fds);
	reply_ack(preq);
}
#else	/* PBS_MOM - MOM MOM MOM */
/**
 * @brief
 * 		req_mvjobfile - move the specifled job standard files
 *		This is MOM's version.  The files are owned by the user and placed
 *		in either the spool area or the user's home directory depending
 *		on the compile option, see std_file_name().
 *
 * @param[in,out]	preq	-	ptr to the decoded request
 */

void
req_mvjobfile(struct batch_request *preq)
{
	int		fds;
	enum job_file	jft;
	int		oflag;
	job		*pj;
	struct passwd	*check_pwd(job *);

	jft = (enum job_file)preq->rq_ind.rq_jobfile.rq_type;
	if (preq->rq_ind.rq_jobfile.rq_sequence == 0)
		oflag = O_CREAT | O_WRONLY | O_TRUNC;
	else
		oflag = O_CREAT | O_WRONLY | O_APPEND;

	pj = locate_new_job(preq, NULL);
	if (pj == NULL)
		pj = find_job(preq->rq_ind.rq_jobfile.rq_jobid);

	if (pj == NULL) {
		req_reject(PBSE_UNKJOBID, 0, preq);
		return;
	}
	/* this call sets up home/uid/gid information for the job */
	if (check_pwd(pj) == NULL) {
		req_reject(PBSE_MOMREJECT, 0, preq);
		return;
	}
	if ((pj->ji_wattr[(int)JOB_ATR_sandbox].at_flags & ATR_VFLAG_SET) &&
		(strcasecmp(pj->ji_wattr[JOB_ATR_sandbox].at_val.at_str, "PRIVATE")== 0)) {
		/* have a private sandbox which must be recreated */
		/* prior to copying standard out and err back     */

		char *pbs_jobdir;

		pbs_jobdir = jobdirname(pj->ji_qs.ji_jobid,
			pj->ji_grpcache->gc_homedir);
		/* call mkjobdir() with a NULL for the environment entry */
		/* We are not at a point where we can setup the job's    */
		/* environment and mkjobdir() will be called again in    */
		/* start_exec where the permissions are reset to match   */
		/* the user's umask and the environment is built.	 */
#ifdef WIN32

		if (mkjobdir(pj->ji_qs.ji_jobid,
			pbs_jobdir,
			(pj->ji_user != NULL) ? pj->ji_user->pw_name : NULL,
			INVALID_HANDLE_VALUE) != 0) {
			sprintf(log_buffer, "unable to create the job directory %s", pbs_jobdir);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO, pj->ji_qs.ji_jobid, log_buffer);
			req_reject(PBSE_MOMREJECT, 0, preq);
			return;
		}
#else
		if (mkjobdir(pj->ji_qs.ji_jobid,
			pbs_jobdir,
			pj->ji_grpcache->gc_uid,
			pj->ji_grpcache->gc_gid) != 0) {
			sprintf(log_buffer, "unable to create the job directory %s", pbs_jobdir);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO, pj->ji_qs.ji_jobid, log_buffer);
			req_reject(PBSE_MOMREJECT, 0, preq);
			return;
		}
#endif /* WIN32 */
	}

	if ((fds =open_std_file(pj, jft, oflag,
		pj->ji_grpcache->gc_gid)) < 0) {
		req_reject(PBSE_MOMREJECT, 0, preq);
		return;
	}

	if (write(fds, preq->rq_ind.rq_jobfile.rq_data,
		preq->rq_ind.rq_jobfile.rq_size) !=
		preq->rq_ind.rq_jobfile.rq_size)
		req_reject(PBSE_SYSTEM, 0, preq);
	else {
		reply_ack(preq);
	}
	(void)close(fds);
}
#endif /* PBS_MOM */



/**
 * @brief
 *		Commit ownership of job
 * @par Functionality:
 *		Set state of job to JOB_STATE_QUEUED (or Held or Waiting) and
 *		enqueue the job into its destination queue.
 *
 *  @param[in]	preq	-	The batch request structure
 *
 */

void
req_commit(struct batch_request *preq)
{
	job			*pj;
#ifndef	PBS_MOM
	int			newstate;
	int			newsub;
	resc_resv	*presv;
	pbs_queue	*pque;
	int			rc;
	pbs_db_jobscr_info_t	jobscr;
	pbs_db_obj_info_t	obj;
	long			time_msec;
#ifdef	WIN32
	struct	_timeb		tval;
#else
	struct timeval		tval;
#endif
	pbs_db_conn_t		*conn = (pbs_db_conn_t *) svr_db_conn;
#endif

	pj = locate_new_job(preq, preq->rq_ind.rq_commit);
	if (pj == NULL) {
		req_reject(PBSE_UNKJOBID, 0, preq);
		return;
	}

	if (pj->ji_qs.ji_substate != JOB_SUBSTATE_TRANSIN) {
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}

	pj->ji_qs.ji_state = JOB_STATE_TRANSIT;
	pj->ji_wattr[(int) JOB_ATR_state].at_val.at_char = 'T';
	pj->ji_wattr[(int) JOB_ATR_state].at_flags |=
		ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;
	pj->ji_qs.ji_substate = JOB_SUBSTATE_TRANSICM;
	pj->ji_wattr[(int) JOB_ATR_substate].at_flags |=
		ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;

#ifdef PBS_MOM	/* MOM only */

#if IRIX6_CPUSET == 1
	/*
	 * Grab a mutex to allow machine dependent code to block while the
	 * job state is being set up.
	 */
	ACQUIRE_LOCK(*pbs_commit_ptr);
#endif  /* IRIX6_CPUSET */
	/* move job from new job list to "all" job list, set to running state */

	delete_link(&pj->ji_alljobs);
	append_link(&svr_alljobs, &pj->ji_alljobs, pj);
	/*
	 ** Set JOB_SVFLG_HERE to indicate that this is Mother Superior.
	 */
	pj->ji_qs.ji_svrflags |= JOB_SVFLG_HERE;

	pj->ji_qs.ji_state = JOB_STATE_RUNNING;
	pj->ji_wattr[(int)JOB_ATR_state].at_flags |= ATR_VFLAG_MODIFY;
	pj->ji_qs.ji_substate = JOB_SUBSTATE_PRERUN;
	pj->ji_wattr[(int)JOB_ATR_substate].at_flags |= ATR_VFLAG_MODIFY;
	pj->ji_qs.ji_un_type = JOB_UNION_TYPE_MOM;
	if (preq->isrpp) {
		struct sockaddr_in* addr = rpp_getaddr(preq->rq_conn);
		if (addr)
			pj->ji_qs.ji_un.ji_momt.ji_svraddr = (pbs_net_t) ntohl(addr->sin_addr.s_addr);
	} else
		pj->ji_qs.ji_un.ji_momt.ji_svraddr = get_connectaddr(preq->rq_conn);
	pj->ji_qs.ji_un.ji_momt.ji_exitstat = 0;
	if ((pj->ji_qs.ji_svrflags & (JOB_SVFLG_CHKPT|JOB_SVFLG_ChkptMig)) == 0) {
		pj->ji_qs.ji_stime = time_now; 	/* start of walltime */
		pj->ji_wattr[(int)JOB_ATR_stime].at_flags |= ATR_VFLAG_MODIFY;
	}

	/*
	 * For MOM - reply to the request and start up the job
	 * any errors will be dealt with via the mechanism
	 * used for a terminated job
	 */

	(void)reply_jobid(preq, pj->ji_qs.ji_jobid, BATCH_REPLY_CHOICE_Commit);
	start_exec(pj);
	job_or_resv_save((void *)pj, SAVEJOB_NEW, JOB_OBJECT);

	/* The ATR_VFLAG_MODIFY bit for several attributes used to be
	 * set here. Now we rely on these bits to be set when and where
	 * an attribute is modified. Several of these are also set in
	 * record_finish_exec().
	 */

#if IRIX6_CPUSET == 1
	(void)mom_get_sample();         /* Setup for sampling. */

	RELEASE_LOCK(*pbs_commit_ptr);  /* Release the lock. */
#endif /* IRIX6_CPUSET */

#else	/* PBS_SERVER */
	if (svr_authorize_jobreq(preq, pj) == -1) {
		req_reject(PBSE_PERM, 0, preq);
		return;
	}

	/* Set Server level entity usage */

	if ((rc = account_entity_limit_usages(pj, NULL, NULL, INCR, ETLIM_ACC_ALL)) != 0) {
		job_purge(pj);
		req_reject(rc, 0, preq);
		return;
	}

	/* remove job for the server new job list, set state, and enqueue it */

	delete_link(&pj->ji_alljobs);

	svr_evaljobstate(pj, &newstate, &newsub, 1);
	pj->ji_modified = 0; /* don't save from svr_setjobstate, we will save soon after */
	(void)svr_setjobstate(pj, newstate, newsub);

#ifdef WIN32
	_ftime_s(&tval);
	time_msec = (tval.time * 1000L) + tval.millitm;
#else
	gettimeofday(&tval, NULL);
	time_msec = (tval.tv_sec * 1000L) + (tval.tv_usec/1000L);
#endif
	/* set the queue rank attribute */
	pj->ji_wattr[(int)JOB_ATR_qrank].at_val.at_long = time_msec;
	pj->ji_wattr[(int)JOB_ATR_qrank].at_flags |=
		ATR_VFLAG_SET|ATR_VFLAG_MODCACHE;

	if ((rc = svr_enquejob(pj)) != 0) {
		job_purge(pj);
		req_reject(rc, 0, preq);
		return;
	}

	if (pj->ji_resvp) {
		/*we are supposedly dealing with a reservation job:
		 *1. make sure pointer is still valid - thoretically, there is a
		 *   tiny chance that the reservation structure went away
		 *	automatically, e.g. reservation window expired and the stucture
		 *	was removed prior to being at this point in the code
		 *2. remove resevation's link from list "svr_newresvs" and place
		 *	it on list "svr_allresvs"
		 */
		presv = (resc_resv *)GET_NEXT(svr_newresvs);
		while (presv) {
			if (presv == pj->ji_resvp &&
				presv->ri_qs.ri_type == RESV_JOB_OBJECT)
				break;
			presv = (resc_resv *)GET_NEXT(presv->ri_allresvs);
		}
		if (presv == NULL) {
			(void)job_purge(pj);
			req_reject(PBSE_SYSTEM, 0, preq);
			return;
		}
		delete_link(&presv->ri_allresvs);
		append_link(&svr_allresvs, &presv->ri_allresvs, presv);
		set_scheduler_flag(SCH_SCHEDULE_NEW, dflt_scheduler);
		Update_Resvstate_if_resv(pj);
	}

	/* save job and job script within single transaction */
	pbs_db_begin_trx(conn, 0, 0);

	/* Make things faster by writing job only once here  - at commit time */
	if (job_or_resv_save((void *) pj, SAVEJOB_NEW, JOB_OBJECT)) {
		(void) pbs_db_end_trx(conn, PBS_DB_ROLLBACK);
		job_purge(pj);
		req_reject(PBSE_SAVE_ERR, 0, preq);
		return;
	}

	strcpy(jobscr.ji_jobid, pj->ji_qs.ji_jobid);
	jobscr.script = pj->ji_script;
	obj.pbs_db_obj_type = PBS_DB_JOBSCR;
	obj.pbs_db_un.pbs_db_jobscr = &jobscr;

	if (pbs_db_save_obj(conn, &obj, PBS_INSERT_DB) != 0) {
		job_purge(pj);
		req_reject(PBSE_SYSTEM, 0, preq);
		(void) pbs_db_end_trx(conn, PBS_DB_ROLLBACK);
		return;
	}
	if (pj->ji_script) {
		free(pj->ji_script);
		pj->ji_script = NULL;
	}

	/* Now, no need to save server here because server
	   has already saved in the get_next_svr_sequence_id() */


	if (pbs_db_end_trx(conn, PBS_DB_COMMIT) != 0) {
		job_purge(pj);
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	/*
	 * if the job went into a Route (push) queue that has been started,
	 * try once to route it to give immediate feedback as a courtsey
	 * to the user.
	 */

	pque = pj->ji_qhdr;

	if ((preq->rq_fromsvr == 0) &&
		(pque->qu_qs.qu_type == QTYPE_RoutePush) &&
		(pque->qu_attr[(int)QA_ATR_Started].at_val.at_long != 0)) {
		if ((rc = job_route(pj)) != 0) {
			job_purge(pj);
			req_reject(rc, 0, preq);
			return;
		}
	}

	/* need to format message first, before request goes away */

	(void)snprintf(log_buffer, sizeof(log_buffer), msg_jobnew,
		preq->rq_user, preq->rq_host,
		pj->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str,
		pj->ji_wattr[(int)JOB_ATR_jobname].at_val.at_str,
		pj->ji_qhdr->qu_qs.qu_name);

	/* acknowledge the request with the job id */
	if ((rc = reply_jobid(preq, pj->ji_qs.ji_jobid, BATCH_REPLY_CHOICE_Commit))) {
		(void)snprintf(log_buffer, sizeof(log_buffer),
		                "Failed to reply with Job Id, error %d", rc);
		log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_ERR,
						pj->ji_qs.ji_jobid, log_buffer);
		job_purge(pj);
		return;
	}

	log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
		pj->ji_qs.ji_jobid, log_buffer);

	if ((pj->ji_qs.ji_svrflags & JOB_SVFLG_HERE) == 0)
		issue_track(pj);	/* notify creator where job is */
#endif		/* PBS_SERVER */
}

/**
 * @brief
 * 		locate_new_job - locate a "new" job which has been set up req_quejob on
 *		the servers new job list.
 *
 * @par Functionality:
 *		This function is used by the sub-requests which make up the global
 *		"Queue Job Request" to locate the job structure.
 *
 *		If the jobid is specified (will be for rdytocommit and commit, but not
 *		for script), we search for a matching jobid.
 *
 *		The job must (also) match the socket specified and the host associated
 *		with the socket unless ji_fromsock == -1, then its a recovery situation.
 *
 * @param[in]	preq	-	The batch request structure
 * @param[in]	jobid	-	Job Id which needs to be located
 *
 * @return	job structure associated with jobid.
 */

static job *
locate_new_job(struct batch_request *preq, char *jobid)
{
	job *pj;
	int sock = -1;
	pbs_net_t conn_addr = 0;

	if (preq == NULL)
		return NULL;

	sock = preq->rq_conn;

	if (!preq->isrpp) { /* Connection from TCP stream */
		conn_addr = get_connectaddr(sock);
	} else {
		struct sockaddr_in* addr = rpp_getaddr(sock);
		if (addr)
			conn_addr = (pbs_net_t) ntohl(addr->sin_addr.s_addr);
	}

	pj = (job *) GET_NEXT(svr_newjobs);
	while (pj) {

		if ((pj->ji_qs.ji_un.ji_newt.ji_fromsock == -1) ||
			((pj->ji_qs.ji_un.ji_newt.ji_fromsock == sock) &&
			(pj->ji_qs.ji_un.ji_newt.ji_fromaddr == conn_addr))) {

			if (jobid != NULL) {
				if (!strncmp(pj->ji_qs.ji_jobid, jobid, PBS_MAXSVRJOBID))
					break;
			} else
				break;
		}

		pj = (job *)GET_NEXT(pj->ji_alljobs);
	}
	return (pj);
}


#ifndef PBS_MOM	/* SERVER only */
/**
 * @brief
 *		"resvSub" Batch Request processing routine
 *
 *  @param[in]	-	ptr to the decoded request
 */

void
req_resvSub(struct batch_request *preq)
{
	/*
	 * buf and buf1 are used to hold user@hostname stings together
	 * with a small amount (less than 64 characters) of text.
	 */
	char		 buf[PBS_MAXUSER + PBS_MAXHOSTNAME + 64];
	char		 buf1[PBS_MAXUSER + PBS_MAXHOSTNAME + 64] = {0};
	int		 created_here = 0;
	int		 i = 0;
	char		*rid = NULL;
	char		 ridbuf[PBS_MAXSVRRESVID+1] = {0};
	char		 qbuf[PBS_MAXSVRRESVID+1] = {0};
	char		*pc = NULL;
	attribute_def	*pdef = NULL;
	resc_resv	*presv = NULL;
	svrattrl	*psatl = NULL;
	int		 rc = 0;
	int		 sock = preq->rq_conn;
	char		 hook_msg[HOOK_MSG_SIZE] = {0};
	int		 resc_access_perm_save = 0;
	int		 qmove_requested = 0;
	pbs_db_conn_t	*conn = (pbs_db_conn_t *) svr_db_conn;
	char		*fmt = "%a %b %d %H:%M:%S %Y";
	char		 tbuf1[256] = {0};
	char		 tbuf2[256] = {0};

	switch (process_hooks(preq, hook_msg, sizeof(hook_msg),
			pbs_python_set_interrupt)) {
		case 0:	/* explicit reject */
			reply_text(preq, PBSE_HOOKERROR, hook_msg);
			return;
		case 1:   /* explicit accept */
			if (recreate_request(preq) == -1) { /* error */
				/* we have to reject the request, as 'preq' */
				/* may have been partly modified            */
				strcpy(hook_msg,
					"resvsub event: rejected request");
				log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_HOOK,
					LOG_ERR, "", hook_msg);
				reply_text(preq, PBSE_HOOKERROR, hook_msg);
				return;
			}
			break;
		case 2:	/* no hook script executed - go ahead and accept event*/
			break;
		default:
			log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK,
				LOG_INFO, "", "resvsub event: accept req by default");
	}

	/* Is the admin refusing to allow reservations on this server? */

	if ((server.sv_attr[(int)SRV_ATR_ResvEnable].at_flags & ATR_VFLAG_SET) &&
		(server.sv_attr[(int)SRV_ATR_ResvEnable].at_val.at_long == 0)) {

		snprintf(buf, sizeof(buf), "reservations disallowed on %s", server_name);
		if ((rc = reply_text(preq, PBSE_RESVAUTH_U, buf))) {
			/* reply failed,  close connection; purge resv */
			close_client(sock);
		}
		return;
	}
	/* Are reservations from submitting host allowed? */
	if (server.sv_attr[(int)SRV_ATR_acl_Resvhost_enable].at_val.at_long) {
		/* acl enabled so need to check it */
		if (acl_check(&server.sv_attr[(int)SRV_ATR_acl_Resvhosts],
			preq->rq_host, ACL_Host) == 0) {
				req_reject(PBSE_RESVAUTH_H, 0, preq);
				return;
		}
	}

	resc_access_perm = preq->rq_perm | ATR_DFLAG_Creat;

	/* get reseravtion id/queue name locally */
	if ((next_svr_sequence_id = get_next_svr_sequence_id()) == -1) {
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}
	/*
	 * if the reservation ID is supplied, the request had better be
	 * from another server
	 * Remark: would be the case if the reservation is being forwarded
	 *     to another server - something to think about for the future
	 */

	if (preq->rq_fromsvr) {
		/* from another server - accept the extra attributes */
		resc_access_perm |= ATR_DFLAG_MGWR | ATR_DFLAG_SvWR;
		rid = preq->rq_ind.rq_queuejob.rq_jid;

	} else if (preq->rq_ind.rq_queuejob.rq_jid[0] != '\0') {
		/* a reservation id is not allowed from a client */
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	} else {
		/* No reservation ID came with the request, create one    */
		/* Note: use server's job seq number generation mechanism */

		created_here = RESV_SVFLG_HERE;
		(void)snprintf(ridbuf, sizeof(ridbuf), "%c%lld.", PBS_RESV_ID_CHAR,
				next_svr_sequence_id);
		(void)strcat(ridbuf, server_name);
		rid = ridbuf;
	}

	/* generate a queue name then update sv_jobidnumber and
	 * save serve struct
	 * generate a queue name then update sv_resvidnumber and
	 * save serve struct
	 * The second comment above is the one we really want,
	 * but the structure field would be an addition to the
	 * "quick save" area of the server - can't do
	 */

	(void)snprintf(qbuf, sizeof(qbuf), "%c%lld", PBS_RESV_ID_CHAR,
			next_svr_sequence_id);

	/* does reservation already exist, check both old
	 * and new reservations?
	 * This could happen if we are allowing reservations
	 * submitted to one server to be passed off to another
	 * server to fulfill or reject;  we may or may not want
	 * this capability, but will have this code here
	 */

	if ((presv = find_resv(rid)) == NULL) {
		/* Not on "all_resvs" list try "new_resvs" list */
		presv = (resc_resv *)GET_NEXT(svr_newresvs);
		while (presv) {
			if (!strcmp(presv->ri_qs.ri_resvID, rid))
				break;
			presv = (resc_resv *)GET_NEXT(presv->ri_allresvs);
		}
	}

	if (presv != NULL) {

		/* server rejects resvSub request if already exists */
		req_reject(PBSE_RESVEXIST, 0, preq);
		return;
	}


	/* OK, we have created a name for the local backing
	 * store file and a zero length file of that name
	 * is on the disk.   Now, CREATE THE RESC_RESV STRUCTURE
	 * for managing the reservation and later on try and
	 * create a pbs_queue into which jobs submitted to the
	 * reservation get assigned
	 */

	if ((presv = resc_resv_alloc()) == NULL) {
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	/* Take a quick pass through the attribute list to see
	 * whether a qmove is being performed. If so, the operation
	 * is granted special permission to modify readonly
	 * resources.
	 */
	qmove_requested = 0;
	psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
	while (psatl) {
		if (strcmp(psatl->al_atopl.name, ATTR_convert) == 0) {
			qmove_requested = 1;
			break;
		}
		psatl = (svrattrl *)GET_NEXT(psatl->al_link);
	}

	/* decode attributes from resvSub request into
	 * the resc_resv structure's ri_wattr array
	 */

	resc_access_perm_save = resc_access_perm; /* save perm */
	psatl = (svrattrl *)GET_NEXT(preq->rq_ind.rq_queuejob.rq_attr);
	while (psatl) {
		int index;
		/* reservation does not support Shrink-to-fitness */
		if (!(strcasecmp(psatl->al_name, ATTR_l)) &&
			(!(strcasecmp(psatl->al_resc, MIN_WALLTIME)) ||
			!(strcasecmp(psatl->al_resc, MAX_WALLTIME)))) {
			req_reject(PBSE_NOSTF_RESV, 0, preq);
			log_event(PBSEVENT_ERROR, PBS_EVENTCLASS_REQUEST, LOG_ERR, "",  msg_nostf_resv);
			resv_free(presv);
			return;
		}
		/* identify the attribute by name */
		index = find_attr(resv_attr_def, psatl->al_name, RESV_ATR_LAST);
		if (index < 0) {

			if (ignore_attr(psatl->al_name) >= 0) {
				/*ignore some currently not handled options
				 *also helpful in debugging using qsub;
				 *remove later on
				 */

				psatl = (svrattrl *)GET_NEXT(psatl->al_link);
				continue;
			}

			/* didn`t recognize the name */
			resv_free(presv);
			reply_badattr(PBSE_NOATTR, 1, psatl, preq);
			return;
		}
		pdef = &resv_attr_def[index];

		/* Does attribute's definition flags indicate that
		 * we have sufficient permission to write the attribute?
		 */

		resc_access_perm = resc_access_perm_save; /* reset */
		if ((psatl->al_flags & ATR_VFLAG_HOOK) || qmove_requested) {
			resc_access_perm = ATR_DFLAG_USWR | \
					    ATR_DFLAG_OPWR | \
					    ATR_DFLAG_MGWR | \
				            ATR_DFLAG_SvWR | \
					    ATR_DFLAG_Creat;
		}
		if ((pdef->at_flags & resc_access_perm) == 0) {
			resv_free(presv);
			reply_badattr(PBSE_ATTRRO, 1, psatl, preq);
			return;
		}

		/* decode attribute */

		rc = pdef->at_decode(&presv->ri_wattr[index],
			psatl->al_name, psatl->al_resc, psatl->al_value);


		if (rc != 0) {
			resv_free(presv);
			reply_badattr(rc, 1, psatl, preq);
			return;
		}

		psatl = (svrattrl *)GET_NEXT(psatl->al_link);
	}
	resc_access_perm = resc_access_perm_save; /* restore perm */


	/* invoke any defined attribute at_action routines */

	for (i=0; i<RESV_ATR_LAST; ++i) {
		pdef = &resv_attr_def[i];
		if ((presv->ri_wattr[i].at_flags & ATR_VFLAG_SET) &&
			(pdef->at_action)) {
			rc = pdef->at_action(&presv->ri_wattr[i],
				presv, ATR_ACTION_NEW);
			if (rc) {
				resv_free(presv);
				req_reject(rc, i, preq);
				return;
			}
		}
	}

	/*"start", "end","duration", and "wall"; derive and check*/

	if (start_end_dur_wall(presv, RESC_RESV_OBJECT)) {
		resv_free(presv);
		req_reject(PBSE_BADTSPEC, 0, preq);
		return;
	}

	/* If standing reservation check the recurrence rule
	 * and possibly change the queue and reservation id to start with
	 * 'S' instead of 'R'
	 */
	if (presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long) {
		int resv_count;

		/* Check the recurrence rule. If this fails, an error message
		 * is sent back to the requestor. Otherwise, check the number
		 * of occurrences requested by the recurrence rule. If 1 then
		 * it is treated as an advance reservation.
		 */
		resv_count = check_rrule(
			presv->ri_wattr[RESV_ATR_resv_rrule].at_val.at_str,
			presv->ri_wattr[RESV_ATR_start].at_val.at_long,
			presv->ri_wattr[RESV_ATR_end].at_val.at_long,
			presv->ri_wattr[RESV_ATR_resv_timezone].at_val.at_str,
			&rc);

		/* rc is set by check_rrule to report any possible icalendar
		 * syntax or time problem
		 */
		if (rc!=0) {
			resv_free(presv);
			req_reject(rc, 0, preq);
			return;
		}
		presv->ri_alter_standing_reservation_duration =  presv->ri_qs.ri_duration;

		/* If more than 1 occurrence are requested then alter the
		 * reservation and queue first character
		 */
		if (resv_count > 1) {
			rid[0] = PBS_STDNG_RESV_ID_CHAR;
			qbuf[0] = PBS_STDNG_RESV_ID_CHAR;
		}
		else  { /* If only 1 occurrence, treat it as an advance reservation */
			presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long = 0;
			presv->ri_wattr[RESV_ATR_resv_standing].at_flags |=\
					ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;
		}
	}
	(void)strcpy(presv->ri_qs.ri_resvID, rid);
	presv->ri_modified = 1;
	if (created_here) {
		presv->ri_qs.ri_svrflags = created_here;
		presv->ri_qs.ri_type = RESC_RESV_OBJECT;
	}


	/*for resources that are not specified in the request and
	 *for which default values can be determined, set these values
	 *as the values for those resources
	 */

	if ((rc = set_resc_deflt((void *)presv, RESC_RESV_OBJECT, NULL)) != 0) {
		resv_free(presv);
		req_reject(rc, 0, preq);
		return;
	}

	/*
	 * Now that the attributes have been decoded, setup some
	 * additional parameters and perform a few more checks.
	 */


	/* set some items based on who created the job... */

	if (created_here) {
		/* reservation got created by this server */

		/* ck priority value - in future, reservations
		 * may support the notion of priority
		 */

		if (presv->ri_wattr[(int)RESV_ATR_priority]
			.at_flags & ATR_VFLAG_SET) {
			if ((presv->ri_wattr[(int)RESV_ATR_priority]
				.at_val.at_long < -1024) ||
				(presv->ri_wattr[(int)RESV_ATR_priority]
				.at_val.at_long > 1024)) {

				resv_free(presv);
				req_reject(PBSE_BADATVAL, 0, preq);
				return;
			}
		}

		/* set reservation name to "NULL" if not specified by user */

		if (!(presv->ri_wattr[(int)RESV_ATR_resv_name].at_flags
			& ATR_VFLAG_SET)) {

			resv_attr_def[(int)RESV_ATR_resv_name].at_free(
				&presv->ri_wattr[(int)RESV_ATR_resv_name]);
			strcpy(buf, "NULL");
			resv_attr_def[(int)RESV_ATR_resv_name].at_decode(
				&presv->ri_wattr[(int)RESV_ATR_resv_name],
				NULL, NULL, buf);
		}

		/* 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, preq->rq_user);
		(void)strcat(buf, "@");
		(void)strcat(buf, preq->rq_host);
		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 in reservation's Authorized_Users */

		if (act_resv_add_owner(&presv->ri_wattr[(int)RESV_ATR_auth_u],
			presv, ATR_ACTION_NEW)) {
			resv_free(presv);
			req_reject(PBSE_BADATVAL, 0, preq);
			return;
		}

		/* set create time */

		presv->ri_wattr[(int)RESV_ATR_ctime]
		.at_val.at_long =(long)time_now;
		presv->ri_wattr[(int)RESV_ATR_ctime].at_flags |=
			ATR_VFLAG_SET | ATR_VFLAG_MODCACHE;

		/* set hop count = 1 */
		presv->ri_wattr[(int)RESV_ATR_hopcount].at_val.at_long = 1;
		presv->ri_wattr[(int)RESV_ATR_hopcount]
		.at_flags |= ATR_VFLAG_SET;

	} else {
		/* reservation created elsewhere and being moved here */

		/* make sure resv_owner is set, ERROR IF NOT */
		if (!(presv->ri_wattr[(int)RESV_ATR_resv_owner]
			.at_flags & ATR_VFLAG_SET)) {
			(void)resv_purge(presv);
			req_reject(PBSE_IVALREQ, 0, preq);
			return;
		}

		/* increment hop count */

		if (++presv->ri_wattr[(int)RESV_ATR_hopcount].at_val.at_long >
			PBS_MAX_HOPCOUNT) {
			(void)resv_purge(presv);
			req_reject(PBSE_HOPCOUNT, 0, preq);
			return;
		}
	}

	/* determine values for the "euser" and "egroup" attributes */

	if ((rc = set_objexid((void *)presv, RESC_RESV_OBJECT, presv->ri_wattr))) {
		resv_free(presv);
		req_reject(rc, 0, preq);
		return;
	}

	/*
	 * Are reservation submissions being controlled by a group ACL?
	 * If yes, check if this one is allowed or denied
	 */

	if ((server.sv_attr[(int)SRV_ATR_acl_ResvGroup_enable].at_flags &
		ATR_VFLAG_SET) &&
		server.sv_attr[(int)SRV_ATR_acl_ResvGroup_enable].at_val.at_long) {

		if (acl_check(&server.sv_attr[(int)SRV_ATR_acl_ResvGroups],
#ifdef WIN32
			presv->ri_wattr[RESV_ATR_egroup].at_val.at_str,
#else
			presv->ri_wattr[RESV_ATR_euser].at_val.at_str,
#endif
			ACL_Group) == 0) {

			resv_free(presv);
			req_reject(PBSE_RESVAUTH_G, 0, preq);
			return;
		}
	}

	/* Is this user allowed to submit a reservation? */

	if ((server.sv_attr[(int)SRV_ATR_AclResvUserEnabled].at_flags &
		ATR_VFLAG_SET) &&
		server.sv_attr[(int)SRV_ATR_AclResvUserEnabled].at_val.at_long) {
		if (NULL != preq->rq_host) {
			snprintf(buf1, sizeof(buf1), "%s@%s",
				presv->ri_wattr[RESV_ATR_euser].at_val.at_str, preq->rq_host);
		}

		if (acl_check(&server.sv_attr[(int)SRV_ATR_AclResvUsers], buf1, ACL_User) == 0) {
			resv_free(presv);
			req_reject(PBSE_RESVAUTH_U, 0, preq);
			return;
		}
	}

	/* set up at_server attribute for status */

	resv_attr_def[(int)RESV_ATR_at_server].at_decode(
		&presv->ri_wattr[(int)RESV_ATR_at_server],
		NULL, NULL, pbs_server_name);

	/* set what will be the name of the reservation's associated queue */

	resv_attr_def[(int)RESV_ATR_queue].at_free(
		&presv->ri_wattr[(int)RESV_ATR_queue]);

	resv_attr_def[(int)RESV_ATR_queue].at_decode(
		&presv->ri_wattr[(int)RESV_ATR_queue],
		NULL, NULL, qbuf);
	/*
	 * Now that the resc_resv structure exists and and has been setup,
	 * try to acquire and setup a pbs_queue into which jobs submitted
	 * to the reservation get placed - actually, right now, the user
	 * directly does a "qsub" to this created queue but, at some point
	 * it's conceivable that the interface to user might change for
	 * submitting jobs to reservations and the user would specify the
	 * reservation ID string instead of the queue
	 */

	if ((rc = get_queue_for_reservation(presv)) !=0) {
		/* couldn't acquire the queue */

		if ((pc = pbse_to_txt(rc)) != 0)
			log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_INFO,
				presv->ri_qs.ri_resvID, pc);

		log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_INFO,
			presv->ri_qs.ri_resvID,
			"error - reservation being deleted");

		resv_free(presv);

		/* Single out duplicate list entries to inform end-user about
		 * erroneous input. Other errors are internal and will fall under
		 * a generic "reservation failure" message.
		 */
		if (rc != PBSE_DUPLIST)
			rc = PBSE_resvFail;

		req_reject(rc, 0, preq);
		return;
	}


	/* set remaining resc_resv structure elements */

	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;

	presv->ri_qs.ri_state = RESV_UNCONFIRMED;
	presv->ri_qs.ri_substate = RESV_UNCONFIRMED;

	presv->ri_wattr[(int)RESV_ATR_state].at_val.at_long = RESV_UNCONFIRMED;
	presv->ri_wattr[(int)RESV_ATR_state].at_flags |= ATR_VFLAG_SET |
		ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;

	presv->ri_wattr[(int)RESV_ATR_substate]
	.at_val.at_long = RESV_UNCONFIRMED;
	presv->ri_wattr[(int)RESV_ATR_substate].at_flags |= ATR_VFLAG_SET |
		ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;


	presv->ri_wattr[(int)RESV_ATR_mtime].at_val.at_long = (long)time_now;
	presv->ri_wattr[(int)RESV_ATR_mtime].at_flags |= ATR_VFLAG_SET |
		ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;

	presv->ri_alter_stime = 0;
	presv->ri_alter_etime = 0;
	presv->ri_alter_flags = 0;

	presv->ri_qs.ri_un_type = RESV_UNION_TYPE_NEW;
	presv->ri_qs.ri_un.ri_newt.ri_fromsock = sock;
	presv->ri_qs.ri_un.ri_newt.ri_fromaddr = get_connectaddr(sock);

	/* start a transaction and save resv and server structure */
	pbs_db_begin_trx(conn, 0, 0);

	if (job_or_resv_save((void *)presv, SAVERESV_NEW, RESC_RESV_OBJECT)) {
		(void) pbs_db_end_trx(conn, PBS_DB_ROLLBACK);
		(void)resv_purge(presv);
		req_reject(PBSE_SAVE_ERR, 0, preq);
		return;
	}

	/* Now, no need to save server here because server
	   has already saved in the get_next_svr_sequence_id() */

	if (pbs_db_end_trx(conn, PBS_DB_COMMIT) != 0) {
		(void)resv_purge(presv);
		req_reject(PBSE_SYSTEM, 0, preq);
		return;
	}

	/* If not a standing reservation, put onto the "timed task" list a task
	 * that causes	deletion of the reservation if the window passes
	 */
	if (!presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long) {
		if (presv->ri_wattr[RESV_ATR_start].at_val.at_long
			!= PBS_RESV_FUTURE_SCH) {
			if (gen_task_EndResvWindow(presv)) {
				(void)resv_purge(presv);
				req_reject(PBSE_SYSTEM, 0, preq);
				return;
			}
		}
	}

	/* acknowledge the request with the reservation id
	 * Remark: for reply we can use the function used for jobs
	 */

	if ((presv->ri_wattr[RESV_ATR_interactive].at_flags &
		ATR_VFLAG_SET) == 0) {
		/*Not "interactive" so don't wait on scheduler, reply now*/

		snprintf(buf, sizeof(buf), "%s UNCONFIRMED",  presv->ri_qs.ri_resvID);
		if (presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long)
			snprintf(buf1, sizeof(buf1), "requestor=%s@%s recurrence_rrule=%s timezone=%s",
				preq->rq_user, preq->rq_host,
				presv->ri_wattr[RESV_ATR_resv_rrule].at_val.at_str,
				presv->ri_wattr[RESV_ATR_resv_timezone].at_val.at_str);
		else
			snprintf(buf1, sizeof(buf1), "requestor=%s@%s",
				preq->rq_user, preq->rq_host);

		if ((rc = reply_text(preq, PBSE_NONE, buf))) {
			/* reply failed,  close connection; purge resv */
			close_client(sock);
			(void)resv_purge(presv);
			return;
		}
		account_recordResv(PBS_ACCT_UR, presv, buf1);
	} else {
		/*Don't reply back until scheduler decides*/
		long dt;
		presv->ri_brp = preq;
		dt = presv->ri_wattr[RESV_ATR_interactive].at_val.at_long;
		if (dt >= 0) {
			/*reply with id and state no decision in +dt secs*/
			(void)gen_future_reply(presv, dt);
		} else {
			/*no decision in -dt seconds, delete with msg*/
			(void)gen_negI_deleteResv(presv, -dt);
		}
		if (presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long)
			snprintf(buf, sizeof(buf), "requestor=%s@%s Interactive=%ld recurrence_rrule=%s timezone=%s",
				preq->rq_user, preq->rq_host, dt,
				presv->ri_wattr[RESV_ATR_resv_rrule].at_val.at_str,
				presv->ri_wattr[RESV_ATR_resv_timezone].at_val.at_str);
		else
			snprintf(buf, sizeof(buf), "requestor=%s@%s Interactive=%ld",
				preq->rq_user, preq->rq_host, dt);
		account_recordResv(PBS_ACCT_UR, presv, buf);
	}

	strftime(tbuf1, sizeof(tbuf1), fmt, localtime((time_t *) &presv->ri_wattr[RESV_ATR_start].at_val.at_long));
	strftime(tbuf2, sizeof(tbuf2), fmt, localtime((time_t *) &presv->ri_wattr[RESV_ATR_end].at_val.at_long));

	if (!presv->ri_wattr[RESV_ATR_resv_standing].at_val.at_long) {
		snprintf(log_buffer, sizeof(log_buffer), "New reservation submitted start=%s end=%s", tbuf1, tbuf2);
	} else {
		snprintf(log_buffer, sizeof(log_buffer), "New reservation submitted start=%s end=%s "
                                    "recurrence_rrule=%s timezone=%s",
				    tbuf1, tbuf2,
				    presv->ri_wattr[RESV_ATR_resv_rrule].at_val.at_str,
				    presv->ri_wattr[RESV_ATR_resv_timezone].at_val.at_str);
	}
	log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_INFO,
		presv->ri_qs.ri_resvID, log_buffer);

	/* link reservation into server's reservation list
	 * and let the scheduler know that something new
	 * is available for consideration
	 */
	append_link(&svr_allresvs, &presv->ri_allresvs, presv);
	set_scheduler_flag(SCH_SCHEDULE_NEW, dflt_scheduler);
}


static struct dont_set_in_max {
	char	 *ds_name;		/* resource name */
	resource *ds_rescp;		/* ptr to resource entry */
} dont_set_in_max[] = {
	{"nodes",  	NULL},
	{"nodect", 	NULL},
	{"select", 	NULL},
	{"place",  	NULL},
	{"walltime",	NULL}
};

/**
 * @brief
 * 		create a queue to bind to the reservation
 *
 * @par Functionality:
 * 		get_queue_for_reservation - call this function to create and setup
 *		a queue that's associated with a general resources reservation.
 *
 *		An internally generated request is built up and issued to the
 *		"batch manager" subsystem to create a queue having the desired
 *		queue attributes
 *
 * @param[in]	presv	-	The reservation to which a queue is to be be associated
 *
 * @par
 * 		Note that the queue is created by issuing a request to "ourselves"
 * 		(the server) and that this request is fulfilled asynchronously. The queue
 * 		may fail to be created and cause the reservation to be queue-less.
 *
 * @return	error code
 * @retval	0	- success
 * @retval	PBS error code - error
 *
 * @par MT-safe: No
 */
static int
get_queue_for_reservation(resc_resv *presv)
{
	int			i;
	int			j;
	static	int		lenE = 10;	/*strlen("Execution") + 1*/
	static	int		lenF = 6;	/*strlen("False") + 1*/
	static	int		lenT = 5;	/*strlen("True") + 1*/
	struct batch_request	*newreq;
	attribute		*pattr;
	pbs_list_head		*plhed;
	int			rc = 0;
	svrattrl		*psatl;
	struct work_task	*pwt;

	newreq = alloc_br(PBS_BATCH_Manager);
	if (newreq == 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  (PBSE_SYSTEM);
	}

	newreq->rq_ind.rq_manager.rq_cmd = MGR_CMD_CREATE;
	newreq->rq_ind.rq_manager.rq_objtype = MGR_OBJ_QUEUE;
	newreq->rq_perm = ATR_DFLAG_MGWR | ATR_DFLAG_OPWR;
	(void)strcpy(newreq->rq_user, "pbs_server");
	(void)strcpy(newreq->rq_host, server_name);

	strcpy(newreq->rq_ind.rq_manager.rq_objname,
		presv->ri_wattr[RESV_ATR_queue].at_val.at_str);

	pattr = &presv->ri_wattr [RESV_ATR_resource];
	CLEAR_HEAD(newreq->rq_ind.rq_manager.rq_attr);
	plhed = &newreq->rq_ind.rq_manager.rq_attr;

	/* resources specified by the reservation become "resc_avail" for queue
	 * Have already (at_action processing for RESV_ATR_resource) checked
	 * that server has control of resources needed, in sufficient quantity
	 *
	 * Note: "resc_avail" and "resc_max" attributes on the queue should
	 * not include certain (string) resources from the reservation's
	 * "resource_list" attribute as specified in the array dont_set_in_max;
	 * so, that is why those links are deleted from the attribute
	 * and then appended back a few lines later
	 */

	j = sizeof(dont_set_in_max) / sizeof(struct dont_set_in_max);
	for (i = 0; i < j; ++i) {
		resource_def *prdef;
		prdef = find_resc_def(svr_resc_def, dont_set_in_max[i].ds_name,
			svr_resc_size);
		dont_set_in_max[i].ds_rescp = find_resc_entry(
			&presv->ri_wattr[RESV_ATR_resource],
			prdef);
		if (dont_set_in_max[i].ds_rescp)
			delete_link(&dont_set_in_max[i].ds_rescp->rs_link);
	}


	rc = resv_attr_def[RESV_ATR_resource].at_encode(pattr, plhed,
		ATTR_rescavail, NULL,
		ATR_ENCODE_CLIENT, NULL);

	rc += resv_attr_def[RESV_ATR_resource].at_encode(pattr, plhed,
		ATTR_rescmax, NULL,
		ATR_ENCODE_CLIENT, NULL);

	for (i=0; i<j; ++i) {
		if (dont_set_in_max[i].ds_rescp)
			append_link(
				&presv->ri_wattr[RESV_ATR_resource].at_val.at_list,
				&dont_set_in_max[i].ds_rescp->rs_link,
				dont_set_in_max[i].ds_rescp);

	}
	if (rc < 0) {
		free_br(newreq);
		return (PBSE_genBatchReq);
	}

	if ((psatl = attrlist_create(ATTR_qtype, NULL, lenE)) != NULL) {
		static char Execution[] = "Execution";

		psatl->al_flags = que_attr_def[QA_ATR_QType].at_flags;
		strcpy(psatl->al_value, Execution);
		append_link(plhed, &psatl->al_link, psatl);
	} else {
		free_br(newreq);
		return  (PBSE_genBatchReq);
	}

	/* Don't enable queue until reservation is RESV_CONFIRMED */
	if ((psatl = attrlist_create(ATTR_enable, NULL, lenF)) !=
		NULL) {
		psatl->al_flags = que_attr_def[QA_ATR_Enabled].at_flags;
		strcpy(psatl->al_value, ATR_FALSE);
		append_link(plhed, &psatl->al_link, psatl);
	} else {
		free_br(newreq);
		return  (PBSE_genBatchReq);
	}

	/* Don't start queue until reservation window here, RESV_TIME_TO_RUN */
	if ((psatl = attrlist_create(ATTR_start, NULL, lenF)) != NULL) {
		psatl->al_flags = que_attr_def[QA_ATR_Started].at_flags;
		strcpy(psatl->al_value, ATR_FALSE);
		append_link(plhed, &psatl->al_link, psatl);
	} else {
		free_br(newreq);
		return  (PBSE_genBatchReq);
	}

	/* Generate a "user_acl" for PBS_BATCH_manager req, use
	 * reservation's "Authorized_Users" attribute
	 * Remark: "Authorized_Users" has, atleast, the reservation's owner
	 */

	if (presv->ri_wattr[RESV_ATR_auth_u].at_flags & ATR_VFLAG_SET) {
		pattr = &presv->ri_wattr [RESV_ATR_auth_u];
		plhed = &newreq->rq_ind.rq_manager.rq_attr;
		rc = check_duplicates(pattr->at_val.at_arst);
		if (rc == 1) {
			free_br(newreq);
			return (PBSE_DUPLIST);
		}
		rc = resv_attr_def[RESV_ATR_auth_u].at_encode(pattr, plhed,
			ATTR_acluser, NULL,
			ATR_ENCODE_SVR, NULL);
		if (rc < 0) {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}

		/*let the que know user acl is to be enforced*/
		if ((psatl = attrlist_create(ATTR_acluren,
			NULL, lenT)) != NULL) {
			psatl->al_flags = que_attr_def[QA_ATR_AclUserEnabled].at_flags;
			strcpy(psatl->al_value, ATR_TRUE);
			append_link(plhed, &psatl->al_link, psatl);
		} else {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}
	}

	/* Generate a "group_acl" for PBS_BATCH_manager req, use
	 * reservation's "Authorized_Groups" attribute
	 */

	if (presv->ri_wattr[RESV_ATR_auth_g].at_flags & ATR_VFLAG_SET) {
		pattr = &presv->ri_wattr [RESV_ATR_auth_g];
		plhed = &newreq->rq_ind.rq_manager.rq_attr;
		rc = check_duplicates(pattr->at_val.at_arst);
		if (rc == 1) {
			free_br(newreq);
			return (PBSE_DUPLIST);
		}
		rc = resv_attr_def[RESV_ATR_auth_g].at_encode(pattr, plhed,
			ATTR_aclgroup, NULL,
			ATR_ENCODE_SVR, NULL);
		if (rc < 0) {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}

		/*let the que know user acl is to be enforced*/
		if ((psatl = attrlist_create(ATTR_aclgren,
			NULL, lenT)) != NULL) {
			psatl->al_flags = que_attr_def[QE_ATR_AclGroupEnabled].at_flags;
			strcpy(psatl->al_value, ATR_TRUE);
			append_link(plhed, &psatl->al_link, psatl);
		} else {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}
	}

	/* Generate a "host_acl" for PBS_BATCH_manager req, use
	 * reservation's "Authorized_Hosts" attribute
	 */

	if (presv->ri_wattr[RESV_ATR_auth_h].at_flags & ATR_VFLAG_SET) {
		pattr = &presv->ri_wattr [RESV_ATR_auth_h];
		plhed = &newreq->rq_ind.rq_manager.rq_attr;
		rc = check_duplicates(pattr->at_val.at_arst);
		if (rc == 1) {
			free_br(newreq);
			return (PBSE_DUPLIST);
		}
		rc = resv_attr_def[RESV_ATR_auth_h].at_encode(pattr, plhed,
			ATTR_aclhost, NULL,
			ATR_ENCODE_SVR, NULL);
		if (rc < 0) {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}

		/*let the que know user acl is to be enforced*/
		if ((psatl = attrlist_create(ATTR_aclhten,
			NULL, lenT)) != NULL) {
			psatl->al_flags = que_attr_def[QA_ATR_AclHostEnabled].at_flags;
			strcpy(psatl->al_value, ATR_TRUE);
			append_link(plhed, &psatl->al_link, psatl);
		} else {
			free_br(newreq);
			return  (PBSE_genBatchReq);
		}
	}

	/* Ok, everything is successfully built up, issue the Batch_Request */

	if (issue_Drequest(PBS_LOCAL_CONNECTION, newreq,
		handle_qmgr_reply_to_resvQcreate, &pwt, 0) == -1) {
		free_br(newreq);

		(void)sprintf(log_buffer, "%s", msg_resvQcreateFail);
		log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_ERR,
			presv->ri_qs.ri_resvID, log_buffer);

		return (PBSE_mgrBatchReq);
	}
	if (pwt)
		pwt->wt_parm2 = presv;	/*needed to handle qmgr's response*/

	return (0);
}
/**
 * @brief
 * 		ignore_attr	- wrapper function for find_attr.
 *
 * @param[in]	name	-	attribute name to find
 *
 * @return	return vlaue from find_attr()
 */
static  int
ignore_attr(char *name)
{
	return  (find_attr(job_attr_def, name, JOB_ATR_LAST));
}


/**
 * @brief
 * 		act_resv_add_owner - This is a special action function
 *		for a reservation's  "Authorized_Users" attribute - i.e. who is
 *		allowed to submit jobs to the reservation.
 *
 * @param[in]	pattr	-	not used here
 * @param[in]	pobj	-	reservation structure
 * @param[in]	amode	-	"actmode" stands for the type of action,
 * 							if ATR_ACTION_NEW, just returns from the function.
 *
 * @return	error code
 * @retval	0	: Success
 * @retval	!=0	: fails
 */

int
act_resv_add_owner(attribute *pattr, void *pobj, int amode)
{
	attribute		dummy, *ap;
	struct	array_strings   dumarst;
	enum batch_op		op;
	resc_resv		*presv;
	char			*ps;
	int			len;


	if (amode != ATR_ACTION_NEW)
		return   (0);	/*success - nothing to do*/

	presv = (resc_resv *)pobj;
	if ((presv->ri_wattr[RESV_ATR_resv_owner]
		.at_flags & ATR_VFLAG_SET) == 0)
		return   (0);	/*success - nothing to do*/

	ps = presv->ri_wattr[RESV_ATR_resv_owner].at_val.at_str;
	len = strlen(ps);

	ap = &presv->ri_wattr[RESV_ATR_auth_u];
	if (ap->at_flags & ATR_VFLAG_SET) {
		int i;

		for (i = 0; i < ap->at_val.at_arst->as_usedptr; ++i)
			if (!strcmp(ps, ap->at_val.at_arst->as_string[i]))
				return  (0);	/*resv owner in Authorized_Users*/
		op = INCR;
	} else
		op = SET;			/*Authorized_Users is NULL, must set*/

	(void)memset(&dummy, 0, sizeof(dummy));
	dummy.at_flags = NO_USER_SET | ATR_VFLAG_SET;
	dummy.at_type = ATR_TYPE_ACL;
	dummy.at_val.at_arst = &dumarst;
	dumarst.as_npointers = 1;
	dumarst.as_usedptr = 1;
	dumarst.as_bufsize = strlen(ps) + len;
	dumarst.as_buf = ps;
	dumarst.as_next = ps + len;
	dumarst.as_string[0] = ps;

	/*"at_set" function returns 0 on success and NZ on failure*/
	/*Remark: nice thing would be to have owner appear first  */
	return (resv_attr_def[RESV_ATR_auth_u].at_set(ap, &dummy, op));
}


/**
 * @brief
 * 		handle_qmgr_reply_to_resvQcreate - this is the function that's to be
 *		called to handle the qmgr's response to the earlier issued request
 *		for queue creation for a reservation.  If the response from the
 *		qmgr is successful, copy the queue's name into the ri_queue field
 *		of the reservation and set the reservation's ri_qp field pointing
 *		to this newly established queue.  If not successful log a message.
 * @par Functionality:
 *		This function should only be called through an INTERNALLY GENERATED
 *		request to another server (including ourself).
 *		It frees the request structure and closes the connection (handle).
 * @par
 *		In the work task entry, wt_event is the connection handle and
 *		wt_parm1 is a pointer to the request structure (containing the reply).
 *		wt_parm2 should have the address of the reservation structure
 *
 * @note
 *		THIS SHOULD NOT BE USED IF AN EXTERNAL (CLIENT) REQUEST IS "relayed",
 *		because the request/reply structure is still needed to reply back
 *		to the client.
 *
 * @param[in,out]	pwt	-	earlier issued request for queue creation for a reservation.
 */

static void
handle_qmgr_reply_to_resvQcreate(struct work_task *pwt)
{
	struct batch_request	*preq = pwt->wt_parm1;
	resc_resv		*presv = pwt->wt_parm2;
	pbs_queue		*pque;

	if (preq->rq_reply.brp_code) {

		(void)sprintf(log_buffer, msg_resvQcreateFail,
			presv->ri_jbp->ji_qs.ji_jobid,
			presv->ri_qs.ri_resvID);
		log_event(PBSEVENT_RESV, PBS_EVENTCLASS_RESV, LOG_INFO,
			presv->ri_qs.ri_resvID, log_buffer);
	} else {
		pque = find_queuebyname(preq->rq_ind.rq_manager.rq_objname);
		if ((presv->ri_qp = pque) != 0)
			pque->qu_resvp = presv;
		(void)strcpy(presv->ri_qs.ri_queue,
			presv->ri_wattr[RESV_ATR_queue].at_val.at_str);
		if (job_or_resv_save((void *)presv, SAVERESV_QUICK,
			RESC_RESV_OBJECT)) {
			(void)resv_purge(presv);
			req_reject(PBSE_SYSTEM, 0, preq);
			return;
		}
	}

	free_br((struct batch_request *)pwt->wt_parm1);
	if (pwt->wt_event != -1)
		svr_disconnect(pwt->wt_event);
}

/**
 * @brief
 * 		Validate job and reservation place directives.
 *
 * @param[in]	pj	-	The job to validate.
 *
 * @note
 * 		The reservation associated to the job is obtained
 * 		from the job structure.
 *
 * @return	Whether the place directive between the job
 * 			and the reservation are in conflict or not.
 *
 * @retval	1	: job and reservation place directives do not conflict
 * @retval	0	: job and reservation place directives conflict
 *
 * @par MT-safe: No
 */
static int
validate_place_req_of_job_in_reservation(job *pj)
{
	resource_def *prsdef;
	resource *job_place;
	resource *resv_place;
	attribute *jattr;
	attribute *rattr;
	enum vnode_sharing job_sharetype;
	enum vnode_sharing resv_sharetype;

	/* A job not in reservation is implicitly considered valid */
	if (pj->ji_myResv == NULL)
		return 1;

	prsdef = find_resc_def(svr_resc_def, "place", svr_resc_size);
	jattr = &pj->ji_wattr[(int) JOB_ATR_resource];
	rattr = &pj->ji_myResv->ri_wattr[(int) RESV_ATR_resource];

	job_place = find_resc_entry(jattr, prsdef);
	if (!job_place || !job_place->rs_value.at_val.at_str)
		return 1;

	resv_place = find_resc_entry(rattr, prsdef);
	if (!resv_place || !resv_place->rs_value.at_val.at_str)
		return 1;

	/* Cehck for exclhost should come before excl because exclhost contains
	 * the excl prefix
	 */
	job_sharetype =  place_sharing_type(
		job_place->rs_value.at_val.at_str,
		VNS_FORCE_EXCLHOST);
	if (job_sharetype == VNS_UNSET) {
		job_sharetype =  place_sharing_type(
			job_place->rs_value.at_val.at_str,
			VNS_FORCE_EXCL);

	}
	resv_sharetype = place_sharing_type(
		resv_place->rs_value.at_val.at_str,
		VNS_FORCE_EXCLHOST);
	if (resv_sharetype == VNS_UNSET) {
		resv_sharetype = place_sharing_type(
			resv_place->rs_value.at_val.at_str,
			VNS_FORCE_EXCL);
	}

	/* Reject the request if job requests exclusive and reservation not */
	if ((resv_sharetype == VNS_UNSET) &&
		(job_sharetype != VNS_UNSET))
		return 0;

	/* Reject request if job requests exclhost and reservation doesn't */
	if ((resv_sharetype != VNS_FORCE_EXCLHOST) &&
		(job_sharetype == VNS_FORCE_EXCLHOST))
		return 0;

	return 1;
}

/**
 * @brief
 * 		Provides the next job id
 *
 * @param[in] void
 *
 * @return	long long
 * @retval (>=0 to <= max_job_sequence_id): Success
 * @retval	-1	: database error
 *
 */

static
long long get_next_svr_sequence_id(void)
{
	long long ret_svr_sequence_id = 0;

	/* JobId's save to the database after every 1000 jobs */
	if (svr_sequence_window_count == 0) {
		/* Save the job-id numbers in the database by a SEQ_WIN_INCR(1000) in advance.
		 * So this way, we have to save it once only after every SEQ_WIN_INCR(1000) submissions.
		 */
		long long update_svr_sv_jobidnumber = 0;
		svr_jobidnumber = server.sv_qs.sv_jobidnumber;
		update_svr_sv_jobidnumber = server.sv_qs.sv_jobidnumber + SEQ_WIN_INCR;
		if (update_svr_sv_jobidnumber > svr_max_job_sequence_id) {
			update_svr_sv_jobidnumber = SEQ_WIN_INCR;
		}
		/* sv_jobidnumber has been increased by 1000 and then update it to the database */
		server.sv_qs.sv_jobidnumber = update_svr_sv_jobidnumber;
		if (svr_save_db(&server, SVR_SAVE_QUICK) != 0) {
			/* reset back stuff to the current count, which gets written at server exit anyway */
			server.sv_qs.sv_jobidnumber = svr_jobidnumber;
			return -1;
		}
	}
	++svr_sequence_window_count;
	ret_svr_sequence_id = svr_jobidnumber;
	/* sequence window count is more than 1000, reset back to zero*/
	if (svr_sequence_window_count >= SEQ_WIN_INCR) {
		svr_sequence_window_count = 0;
	}
	/* If server job limit is over, reset back to zero */
	if (++svr_jobidnumber > svr_max_job_sequence_id) {
		svr_sequence_window_count = 0;
		server.sv_qs.sv_jobidnumber = 0;
	}
	return ret_svr_sequence_id;
}

/**
 * @brief
 * 			Resets server sequence window count and server sv_jobidnumber
 *
 * @return None
 *
 */
void reset_svr_sequence_window(void)
{
	svr_sequence_window_count = 0;
	server.sv_qs.sv_jobidnumber = 0;
	(void)svr_save_db(&server, SVR_SAVE_QUICK);
}

#endif	/*SERVER ONLY*/
