/*
 * 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_register.c
 *
 * @brief
 * 		req_register.c	-	This file hold all the functions dealing with job dependency
 *
 * Included functions are:
 * 	post_run_depend()
 * 	req_register()
 * 	post_doq()
 * 	alter_unreg()
 * 	depend_on_que()
 * 	post_doe()
 * 	depend_on_exec()
 * 	depend_on_term()
 * 	set_depend_hold()
 * 	find_depend()
 * 	make_depend()
 * 	register_dep()
 * 	unregister_dep()
 * 	find_dependjob()
 * 	make_dependjob()
 * 	send_depend_req()
 * 	decode_depend()
 * 	cpy_jobsvr()
 * 	dup_depend()
 * 	encode_depend()
 * 	set_depend()
 * 	comp_depend()
 * 	free_depend()
 * 	build_depend()
 * 	clear_depend()
 * 	del_depend()
 * 	del_depend_job()
 */
#include <pbs_config.h>   /* the master config generated by configure */

#include <assert.h>
#include <sys/types.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "libpbs.h"
#include "list_link.h"
#include "attribute.h"
#include "server_limits.h"
#include "credential.h"
#include "batch_request.h"
#include "job.h"
#include "reservation.h"
#include "queue.h"
#include "server.h"
#include "work_task.h"
#include "pbs_error.h"
#include "log.h"
#include "pbs_nodes.h"
#include "svrfunc.h"
#include "net_connect.h"

#define SYNC_SCHED_HINT_NULL	0
#define SYNC_SCHED_HINT_FIRST	1
#define SYNC_SCHED_HINT_OTHER	2



/* External functions */

extern int issue_to_svr(char *svr, struct batch_request *, void (*func)(struct work_task *));

/* Local Private Functions */

static void set_depend_hold(job *, attribute *);
static int register_dep(attribute *, struct batch_request *, int, int *);
static int unregister_dep(attribute *, struct batch_request *);
static struct depend *find_depend(int type, attribute *pattr);
static struct depend *make_depend(int type, attribute *pattr);
static struct depend_job *find_dependjob(struct depend *, char *name);
static struct depend_job *make_dependjob(struct depend *, char *jobid, char *host);
static void   del_depend_job(struct depend_job *pdj);
static int    build_depend(attribute *, char *);
static void   clear_depend(struct depend *, int type, int exists);
static void   del_depend(struct depend *);
static int    send_depend_req(job *, struct depend_job *pparent, int, int, int, void (*postfunc)(struct work_task *));

/* External Global Data Items */

extern struct server server;
extern char  server_name[];
extern char *pbs_server_name;
extern char *msg_unkjobid;
extern char *msg_movejob;
extern char *msg_err_malloc;
extern char *msg_illregister;
extern char *msg_registerdel;
extern char *msg_registerrel;
extern char *msg_regrej;
extern char *msg_job_moved;
extern char  log_buffer[];

/**
 * @brief
 * 		post_run_depend - this function is called via a work task when a
 *		register dependency "after" is received for a job that is already
 *		running.  We accept the dependency and then turn around and send a
 *		"release" back to the newly registered job to remove its hold.
 *
 * param[in]	pwt	-	work task
 */
static void
post_run_depend(struct work_task *pwt)
{
	job *pjob;

	pjob = (job *)pwt->wt_parm1;
	if (pjob->ji_wattr[(int)JOB_ATR_depend].at_flags&ATR_VFLAG_SET)
		(void)depend_on_exec(pjob);
	return;
}
/**
 * @brief
 * 		req_register - process the Register Dependency Request
 * @note
 *		We have an interesting problem here in that the request may well
 *		originate from ourself.  In that case we doen't really reply.
 *
 * @param[in]	preq	-	Register Dependency Request.
 */

void
req_register(struct batch_request *preq)
{
	int		   made;
	attribute	  *pattr;
	struct depend	  *pdep;
	struct depend_job *pdj;
	job		  *pjob;
	char		  *ps;
	struct work_task  *ptask;
	int		   rc = 0;
	int		   revtype;
	int		   type;
	int		   savetype = SAVEJOB_FULL;

	/*  make sure request is from a server */

	if (!preq->rq_fromsvr) {
#ifdef NAS /* localmod 109 */
		sprintf(log_buffer, "Dependency request not from server");
		log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB, LOG_INFO,
			preq->rq_ind.rq_register.rq_parent, log_buffer);
#endif /* localmod 109 */
		req_reject(PBSE_IVALREQ, 0, preq);
		return;
	}

	/* find the "parent" job specified in the request */

	if ((pjob = find_job(preq->rq_ind.rq_register.rq_parent)) == NULL) {

		/*
		 * job not found... if server is initializing, it may not
		 * yet recovered, that is not an error.
		 */

		if (server.sv_attr[(int)SRV_ATR_State].at_val.at_long != SV_STATE_INIT) {
			log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB, LOG_INFO,
				preq->rq_ind.rq_register.rq_parent,
				msg_unkjobid);
			req_reject(PBSE_UNKJOBID, 0, preq);
		} else {
			reply_ack(preq);
		}
		return;
	}

	pattr = &pjob->ji_wattr[(int)JOB_ATR_depend];
	type = preq->rq_ind.rq_register.rq_dependtype;
	pjob->ji_modified = 1;

	/* more of the server:port fix kludge */

	ps = strchr(preq->rq_ind.rq_register.rq_child, (int)'@');
	if (ps != NULL) {
		(void)strcpy(preq->rq_ind.rq_register.rq_svr, ps+1);
		*ps = '\0';
	} else {
		(void)strcpy(preq->rq_ind.rq_register.rq_svr, preq->rq_host);
	}

	if (pjob->ji_qs.ji_state == JOB_STATE_MOVED) {
		snprintf(log_buffer, sizeof(log_buffer), "Parent %s%s", msg_movejob, pjob->ji_qs.ji_destin);
		log_event(PBSEVENT_DEBUG|PBSEVENT_SYSTEM|PBSEVENT_ERROR,
			PBS_EVENTCLASS_REQUEST, LOG_INFO,
			preq->rq_ind.rq_register.rq_child, log_buffer);
		req_reject(PBSE_JOB_MOVED, 0, preq);
		return;
	}
	switch (preq->rq_ind.rq_register.rq_op) {

			/*
			 * Register a dependency
			 */

		case JOB_DEPEND_OP_REGISTER:
			switch (type) {

				case JOB_DEPEND_TYPE_AFTERSTART:
					if (pjob->ji_qs.ji_substate >= JOB_SUBSTATE_RUNNING) {
						/* job already running, setup task to send	*/
						/* release back to child and continue with	*/
						/* registration process 			*/
						ptask = set_task(WORK_Immed, 0, post_run_depend,
							(void *)pjob);
						if (ptask)
							append_link(&pjob->ji_svrtask,
								&ptask->wt_linkobj, ptask);
					}
					/* fall through to complete registration */
				case JOB_DEPEND_TYPE_AFTERANY:
				case JOB_DEPEND_TYPE_AFTEROK:
				case JOB_DEPEND_TYPE_AFTERNOTOK:
					rc = register_dep(pattr, preq, type, &made);
					break;

				case JOB_DEPEND_TYPE_BEFORESTART:
				case JOB_DEPEND_TYPE_BEFOREANY:
				case JOB_DEPEND_TYPE_BEFOREOK:
				case JOB_DEPEND_TYPE_BEFORENOTOK:

					/*
					 * Check job owner for permission, use the real
					 * job owner, not the sending server's name.
					 */

					(void)strcpy(preq->rq_user,
						preq->rq_ind.rq_register.rq_owner);
					if (svr_chk_owner(preq, pjob)) {
						rc = PBSE_PERM;		/* not same user */
					} else {
						/* ok owner, see if job has "on" */
						pdep = find_depend(JOB_DEPEND_TYPE_ON, pattr);
						if (pdep == 0) {
							/* on "on", see if child already registered */
							revtype = type ^ (JOB_DEPEND_TYPE_BEFORESTART -
								JOB_DEPEND_TYPE_AFTERSTART);
							pdep = find_depend(revtype, pattr);
							if (pdep == 0) {
								/* no "on" and no prior - return error */
								rc = PBSE_BADDEPEND;
							} else {
								pdj = find_dependjob(pdep,
									preq->rq_ind.rq_register.rq_child);
								if (pdj) {
									/* has prior register, update it */
									(void)strcpy(pdj->dc_svr,
										preq->rq_ind.rq_register.rq_svr);
								}
							}
						} else if ((rc=register_dep(pattr, preq, type, &made)) == 0) {
							if (made) {	/* first time registered */
								if (--pdep->dp_numexp <= 0)
									del_depend(pdep);
							}
						}
					}
					break;

				default:
#ifdef NAS /* localmod 109 */
					sprintf(log_buffer, "Unknown dep. op: %d", preq->rq_ind.rq_register.rq_op);
					log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB, LOG_INFO,
						preq->rq_ind.rq_register.rq_parent, log_buffer);
#endif /* localmod 109 */
					rc = PBSE_IVALREQ;
					break;
			}
			break;

			/*
			 * Release a dependency so job might run
			 */

		case JOB_DEPEND_OP_RELEASE:
			switch (type) {

				case JOB_DEPEND_TYPE_BEFORESTART:
				case JOB_DEPEND_TYPE_BEFOREANY:
				case JOB_DEPEND_TYPE_BEFOREOK:
				case JOB_DEPEND_TYPE_BEFORENOTOK:

					/* predecessor sent release-reduce "on", */
					/* see if this job can now run 		 */

					type ^= (JOB_DEPEND_TYPE_BEFORESTART -
						JOB_DEPEND_TYPE_AFTERSTART);
					if ((pdep = find_depend(type, pattr)) != NULL) {
						pdj = find_dependjob(pdep,
							preq->rq_ind.rq_register.rq_child);
						if (pdj) {
							del_depend_job(pdj);
							pattr->at_flags |= ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;
							savetype = SAVEJOB_FULLFORCE;
							(void)sprintf(log_buffer, msg_registerrel,
								preq->rq_ind.rq_register.rq_child);
							log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB,
								LOG_INFO,
								pjob->ji_qs.ji_jobid, log_buffer);

							if (GET_NEXT(pdep->dp_jobs) == 0) {
								/* no more dependencies of this type */
								del_depend(pdep);
								set_depend_hold(pjob, pattr);
							}
							break;
						}
#ifdef NAS /* localmod 109 */
						sprintf(log_buffer, "Dep.rls. job not found: %d/%s", type, preq->rq_ind.rq_register.rq_child);
					} else {
						sprintf(log_buffer, "Dep.rls. type not found: %d", type);
#endif /* localmod 109 */
					}
#ifdef NAS /* localmod 109 */
					log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB, LOG_INFO,
						preq->rq_ind.rq_register.rq_parent, log_buffer);
#endif /* localmod 109 */
					rc = PBSE_IVALREQ;
					break;

			}

			break;

		case JOB_DEPEND_OP_READY:
			rc = PBSE_NOSYNCMSTR;
			break;

		case JOB_DEPEND_OP_DELETE:
			(void)sprintf(log_buffer, msg_registerdel,
				preq->rq_ind.rq_register.rq_child);
			log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
				pjob->ji_qs.ji_jobid, log_buffer);
			job_abt(pjob, log_buffer);
			break;

		case JOB_DEPEND_OP_UNREG:
			unregister_dep(pattr, preq);
			set_depend_hold(pjob, pattr);
			break;


		default:
			sprintf(log_buffer, msg_illregister,
				preq->rq_ind.rq_register.rq_parent);
			log_event(PBSEVENT_DEBUG|PBSEVENT_SYSTEM|PBSEVENT_ERROR,
				PBS_EVENTCLASS_REQUEST, LOG_INFO,
				preq->rq_host, log_buffer);
			rc = PBSE_IVALREQ;
			break;;
	}

	if (rc) {
		pjob->ji_modified = 0;
		req_reject(rc, 0, preq);
	} else {
		/* If this is an array job, forcibly save it to ensure
		 * dependencies are recorded.
		 */
		if (pjob->ji_qs.ji_svrflags & JOB_SVFLG_ArrayJob)
			savetype = SAVEJOB_FULLFORCE;
		if (pjob->ji_modified)
			(void)job_save(pjob, savetype);
		reply_ack(preq);
	}
	return;
}

/**
 * @brief
 * 		post_doq (que not dog) - post request/reply processing for depend_on_que
 *		i.e. the sending of register operations.
 *
 * @param[in]	pwt	-	post request/reply
 */

static void
post_doq(struct work_task *pwt)
{
	struct batch_request *preq = (struct batch_request *)pwt->wt_parm1;
	char *jobid = preq->rq_ind.rq_register.rq_child;
	char *msg;
	job  *pjob;
	job  *ppjob;
	struct depend_job pparent;
	int rc;

	if (preq->rq_reply.brp_code) {
		/* request was rejected */

		(void)strcpy(log_buffer, msg_regrej);
		(void)strcat(log_buffer, preq->rq_ind.rq_register.rq_parent);

		log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO,
			jobid, log_buffer);
		pjob = find_job(jobid);
		if ((msg = pbse_to_txt(preq->rq_reply.brp_code)) != NULL) {
			(void)strcat(log_buffer, " ");
			(void)strcat(log_buffer, msg);
		}
		if (pjob) {
			if (preq->rq_reply.brp_code == PBSE_JOB_MOVED) {
				/* Creating a separate log buffer because if we end up aborting the submitted job
				 * we don't want to change what goes into accounting log via job_abt
				 */
				char log_msg[LOG_BUF_SIZE];
				snprintf(log_msg, sizeof(log_msg), "%s, %s", msg_job_moved,
					"sending dependency request to remote server");
				log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_INFO, jobid, log_msg);
				ppjob = find_job(preq->rq_ind.rq_register.rq_parent);
				if(ppjob && (ppjob->ji_qs.ji_state == JOB_STATE_MOVED) && (ppjob->ji_qs.ji_substate == JOB_SUBSTATE_MOVED)) {
					char *destin;
					/* job destination should be <remote queue>@<remote server> */
					destin = strchr(ppjob->ji_qs.ji_destin, (int)'@');
					if (destin != NULL) {
						strncpy(pparent.dc_child, ppjob->ji_qs.ji_jobid, sizeof(pparent.dc_child));
						destin++;
						strncpy(pparent.dc_svr, destin, sizeof(pparent.dc_svr) - (destin - ppjob->ji_qs.ji_destin));
						rc = send_depend_req(pjob, &pparent, preq->rq_ind.rq_register.rq_dependtype,
							JOB_DEPEND_OP_REGISTER,
							SYNC_SCHED_HINT_NULL, post_doq);
						if (rc) {
							snprintf(log_msg, sizeof(log_msg), "%s",
								"Failed to send dependency request to remote server, aborting job");
							log_event(PBSEVENT_JOB, PBS_EVENTCLASS_JOB, LOG_ERR, jobid, log_msg);
							check_block(pjob, log_buffer);
							job_abt(pjob, log_buffer);
						}
					}
					else {
						/* Ideally if a job is moved, destination can not be empty */
						/* If we come across an empty destination, abort the job */
						check_block(pjob, log_buffer);
						job_abt(pjob, log_buffer);
					}
				}
				else {
					check_block(pjob, log_buffer);
					job_abt(pjob, log_buffer);
				}
			}
			else {
				check_block(pjob, log_buffer);
				job_abt(pjob, log_buffer);
			}
		}
	}

	release_req(pwt);
}

/**
 * @brief
 * 		alter_unreg - if required, unregister dependencies on alter of attribute
 *		This is called from depend_on_que() when it is acting as the at_action
 *		routine for the dependency attribute.
 *
 * @param[in]	pjob	-	pointer to job structure
 * @param[in]	old	-	current job dependency attribure
 * @param[out]	new	-	job dependency attribute after alter
 */

static void
alter_unreg(job *pjob, attribute *old, attribute *new)
{
	struct depend	*poldd;
	struct depend	*pnewd;
	struct depend_job *oldjd;
	int		 type;

	for (poldd = (struct depend *)GET_NEXT(old->at_val.at_list);
		poldd;
		poldd = (struct depend *)GET_NEXT(poldd->dp_link)) {

		type = poldd->dp_type;
		if (type != JOB_DEPEND_TYPE_ON) {

			pnewd = find_depend(type, new);
			oldjd = (struct depend_job *)GET_NEXT(poldd->dp_jobs);
			while (oldjd) {
				if ((pnewd == 0) ||
					(find_dependjob(pnewd, oldjd->dc_child) == NULL)) {
					(void)send_depend_req(pjob, oldjd, type,
						JOB_DEPEND_OP_UNREG,
						SYNC_SCHED_HINT_NULL,
						release_req);
				}
				oldjd = (struct depend_job *)GET_NEXT(oldjd->dc_link);
			}
		}
	}
}



/**
 * @brief
 * 		depend_on_que - Perform a series of actions if job has a dependency
 *		that needs action when the job is queued into an execution queue.
 * @par
 *		Called from svr_enquejob() when a job enters an
 *		execution queue.  Also  the at_action routine for the attribute.
 *
 * @param[out]	pattr	-	job dependency attribute after alter
 * @param[in]	pobj	-	pointer to job structure
 * @param[in]	amode	-	"actmode" stands for the type of action
 */

int
depend_on_que(attribute *pattr, void *pobj, int mode)
{
	char	      *b1, *b2;
	struct depend *pdep;
	struct depend_job *pparent;
	int	       rc;
	int	       type;
	job           *pjob = (job *)pobj;

	if (((mode != ATR_ACTION_ALTER) && (mode != ATR_ACTION_NOOP)) ||
		(pjob->ji_qhdr == 0) ||
		(pjob->ji_qhdr->qu_qs.qu_type != QTYPE_Execution))
		return (0);


	if (mode == ATR_ACTION_ALTER) {
		/* if there are dependencies being removed, unregister them */

		alter_unreg(pjob, &pjob->ji_wattr[(int)JOB_ATR_depend], pattr);
	}

	/* First set a System hold if required */

	set_depend_hold(pjob, pattr);

	/* Check if there are dependencies that require registering */

	pdep = (struct depend *)GET_NEXT(pattr->at_val.at_list);
	while (pdep) {
		type = pdep->dp_type;
		if (type !=JOB_DEPEND_TYPE_ON) {

			pparent = (struct depend_job *)GET_NEXT(pdep->dp_jobs);
			while (pparent) {
				if (((b1 = strchr(pparent->dc_child, (int)'[')) != NULL) &&
					((b2 = strchr(pparent->dc_child, (int)']')) != NULL)) {
					if (b2 != b1+1)
						return PBSE_IVALREQ;
				}
				rc = send_depend_req(pjob, pparent, type,
					JOB_DEPEND_OP_REGISTER,
					SYNC_SCHED_HINT_NULL, post_doq);
				if (rc)
					return rc;
				pparent = (struct depend_job *)GET_NEXT(pparent->dc_link);
			}

		}
		pdep = (struct depend *)GET_NEXT(pdep->dp_link);
	}
	return (0);
}

/**
 * @brief
 * 		post_doe - Post (reply) processing of requests processing for depend_on_exec
 *
 * @param[in]	pwt	-	work task containing requests to be processed
 */

static void
post_doe(struct work_task *pwt)
{
	struct batch_request *preq = pwt->wt_parm1;
	char		     *jobid = preq->rq_ind.rq_register.rq_child;
	attribute	     *pattr;
	struct depend	     *pdep;
	struct depend_job    *pdj;
	job		     *pjob;

	pjob = find_job(jobid);
	if (pjob) {
		pattr = &pjob->ji_wattr[(int)JOB_ATR_depend];
		pdep = find_depend(JOB_DEPEND_TYPE_BEFORESTART, pattr);
		if (pdep != NULL) {
			pdj  = find_dependjob(pdep, preq->rq_ind.rq_register.rq_parent);
			if (pdj != NULL)
				del_depend_job(pdj);
			if (GET_NEXT(pdep->dp_jobs) == 0) {
				/* no more dependencies of this type */
				del_depend(pdep);
			}
		}
	}
	release_req(pwt);
}


/**
 * @brief
 * 		depend_on_exec - Perform actions if job has
 *		"beforestart" dependency - send "register-release" to child job; or
 * @note
 *		This function is called from svr_startjob().
 *
 * @param[in]	pjob	-	job
 *
 * @return	error code
 * @retval	0	: success
 */

int
depend_on_exec(job *pjob)
{
	struct depend     *pdep;
	struct depend_job *pdj;

	/* If any jobs come after my start, release them */

	pdep = find_depend(JOB_DEPEND_TYPE_BEFORESTART,
		&pjob->ji_wattr[(int)JOB_ATR_depend]);
	if (pdep) {
		pdj = (struct depend_job *)GET_NEXT(pdep->dp_jobs);
		while (pdj) {
			(void)send_depend_req(pjob, pdj, pdep->dp_type, JOB_DEPEND_OP_RELEASE, SYNC_SCHED_HINT_NULL, post_doe);
			pdj = (struct depend_job *)GET_NEXT(pdj->dc_link);
		}
	}
	return (0);
}


/**
 * @brief
 * 		depend_on_term - Perform actions if job has "afterany, afterok, afternotok"
 *		dependencies, send "register-release" or "register-delete" as
 *		appropriate.
 * @par
 *		This function is invoked from on_job_exit() in req_jobobit.c.
 *		When there are no depends to deal with, free the attribute and
 *		recall on_job_exit().
 *
 * @param[in]	pjob	-	job
 *
 * @return	error code
 * @retval	0	: success
 */

int
depend_on_term(job *pjob)
{
	int	       exitstat = pjob->ji_qs.ji_un.ji_exect.ji_exitstat;
	int	       op;
	attribute     *pattr;
	struct depend *pdep;
	struct depend_job *pparent;
	int	       rc;
	int	       type;

	pattr = &pjob->ji_wattr[(int)JOB_ATR_depend];

	pdep = (struct depend *)GET_NEXT(pattr->at_val.at_list);
	while (pdep) {
		op = -1;
		switch (type = pdep->dp_type) {

				/* for the first three, before... types, release or delete */
				/* next job depending on exit status			   */

			case JOB_DEPEND_TYPE_BEFOREOK:
				if (exitstat == 0)
					op = JOB_DEPEND_OP_RELEASE;
				else
					op = JOB_DEPEND_OP_DELETE;
				break;

			case JOB_DEPEND_TYPE_BEFORENOTOK:
				if (exitstat != 0)
					op = JOB_DEPEND_OP_RELEASE;
				else
					op = JOB_DEPEND_OP_DELETE;
				break;

			case JOB_DEPEND_TYPE_BEFOREANY:
				op = JOB_DEPEND_OP_RELEASE;
				break;

		}

		if (op != -1) {
			pparent = (struct depend_job *)GET_NEXT(pdep->dp_jobs);
			while (pparent) {

				/* "release" the job to execute */
				rc = send_depend_req(pjob, pparent, type, op,
					SYNC_SCHED_HINT_NULL, release_req);
				if (rc)
					return rc;
				pparent = (struct depend_job *)GET_NEXT(pparent->dc_link);
			}
		}
		pdep = (struct depend *)GET_NEXT(pdep->dp_link);
	}
	return (0);
}

/**
 * @brief
 * 		set_depend_hold - set a hold on the job required by the type of dependency
 *
 * @param[in]	pjob	-	job
 * @param[in]	pattr	-	attribute structure
 */

static void
set_depend_hold(job *pjob, attribute *pattr)
{
	int		loop = 1;
	int		newstate;
	int		newsubst;
	struct depend  *pdp = NULL;
	int		substate = -1;

	if (pattr->at_flags & ATR_VFLAG_SET)
		pdp = (struct depend *)GET_NEXT(pattr->at_val.at_list);
	while (pdp && loop) {
		switch (pdp->dp_type) {
			case JOB_DEPEND_TYPE_AFTERSTART:
			case JOB_DEPEND_TYPE_AFTEROK:
			case JOB_DEPEND_TYPE_AFTERNOTOK:
			case JOB_DEPEND_TYPE_AFTERANY:
				if ((struct depend_job *)GET_NEXT(pdp->dp_jobs))
					substate = JOB_SUBSTATE_DEPNHOLD;
				break;

			case JOB_DEPEND_TYPE_ON:
				if (pdp->dp_numexp)
					substate = JOB_SUBSTATE_DEPNHOLD;
				break;
		}
		pdp = (struct depend *)GET_NEXT(pdp->dp_link);
	}
	if (substate == -1) {

		/* No (more) dependencies, clear system hold and set state */

		if ((pjob->ji_qs.ji_substate == JOB_SUBSTATE_SYNCHOLD) ||
			(pjob->ji_qs.ji_substate == JOB_SUBSTATE_DEPNHOLD)) {
			pjob->ji_wattr[(int)JOB_ATR_hold].at_val.at_long &= ~HOLD_s;
			pjob->ji_wattr[(int)JOB_ATR_hold].at_flags |= ATR_VFLAG_MODCACHE;
			svr_evaljobstate(pjob, &newstate, &newsubst, 0);
			(void)svr_setjobstate(pjob, newstate, newsubst);
		}
	} else {

		/* there are dependencies, set system hold accordingly */

		pjob->ji_wattr[(int)JOB_ATR_hold].at_val.at_long |= HOLD_s;
		pjob->ji_wattr[(int)JOB_ATR_hold].at_flags |= ATR_VFLAG_SET|ATR_VFLAG_MODCACHE;
		(void)svr_setjobstate(pjob, JOB_STATE_HELD, substate);
	}
	return;
}

/**
 * @brief
 * 		find_depend - find a dependency struct of a certain type for a job
 *
 * @param[in]	type	-	type of dependency struct
 * @param[in]	pattr	-	attribute structure
 *
 * @return	dependent job structure
 * @retval	NULL	: fail
 */

static struct depend *find_depend(int type, attribute *pattr)
{
	struct depend *pdep = NULL;

#ifdef NAS /* localmod 109 */
	sprintf(log_buffer, "find_depend: t=%d, p.f=%#x, p.t=%#x", type, (int)pattr->at_flags, (int)pattr->at_type);
#endif /* localmod 109 */
	if (pattr->at_flags & ATR_VFLAG_SET) {
		pdep = (struct depend *)GET_NEXT(pattr->at_val.at_list);
		while (pdep) {
#ifdef NAS /* localmod 109 */
			sprintf(log_buffer+strlen(log_buffer), " %#p t=%d", (void *)pdep, (int)pdep->dp_type);
#endif /* localmod 109 */
			if (pdep->dp_type == type)
				break;
			pdep = (struct depend *)GET_NEXT(pdep->dp_link);
		}
	}
#ifdef NAS /* localmod 109 */
	log_event(PBSEVENT_DEBUG, PBS_EVENTCLASS_JOB, LOG_INFO,
		  "ibid", log_buffer);
#endif /* localmod 109 */
	return (pdep);
}

/**
 * @brief
 * 		make_depend - allocate and attach a depend struct to the attribute
 *
 * @param[in]	type	-	type of dependency struct
 * @param[in,out]	pattr	-	attribute structure
 *
 * @return	dependent job structure
 * @retval	NULL	: fail
 */

static struct depend *make_depend(int type, attribute *pattr)
{
	struct depend *pdep = NULL;

	pdep = (struct depend *)malloc(sizeof(struct depend));
	if (pdep) {
		clear_depend(pdep, type, 0);
		append_link(&pattr->at_val.at_list, &pdep->dp_link, pdep);
		pattr->at_flags |= ATR_VFLAG_SET|ATR_VFLAG_MODCACHE;
	}
	return (pdep);
}

/**
 * @brief
 * 		register_dep - Some job wants to run before/after the local job, so set up
 *		a dependency on the local job.
 *
 * @param[in,out]	pattr	-	attribute structure
 * @param[in,out]	preq	-	request structure
 * @param[in]	type	-	type of dependency struct
 * @param[out]	made	-	0 if dependency is found else 1
 *
 * @return	error code
 * @retval	0	-	success
 * @reval	PBSE_SYSTEM	- fail
 */

static int
register_dep(attribute *pattr, struct batch_request *preq, int type, int *made)
{
	struct depend     *pdep;
	struct depend_job *pdj;

	/* change into the mirror image type */

	type ^= (JOB_DEPEND_TYPE_BEFORESTART - JOB_DEPEND_TYPE_AFTERSTART);
	if ((pdep = find_depend(type, pattr)) == NULL) {
		if ((pdep = make_depend(type, pattr)) == NULL)
			return (PBSE_SYSTEM);
	}
	if ((pdj = find_dependjob(pdep,
		preq->rq_ind.rq_register.rq_child)) != NULL) {
		(void)strcpy(pdj->dc_svr, preq->rq_ind.rq_register.rq_svr);
		*made = 0;
		return (0);
	} else if ((pdj = make_dependjob(pdep,
		preq->rq_ind.rq_register.rq_child,
		preq->rq_ind.rq_register.rq_svr)) ==
		NULL) {
		return (PBSE_SYSTEM);
	} else
		*made = 1;
	return (0);
}

/**
 * @brief
 * 		unregister_dep - remove a registered dependency
 *		Results from a qalter call to remove existing dependencies
 *
 * @param[in]	pattr	-	attribute structure
 * @param[in]	preq	-	request structure
 *
 * @return	error code
 * @retval	0	: success
 * @retval	PBSE_IVALREQ	: Invalid request, dependency could not find.
 */
static int
unregister_dep(attribute *pattr, struct batch_request *preq)
{
	int type;
	struct depend     *pdp;
	struct depend_job *pdjb;

	/* get mirror image of dependency type */

	type = preq->rq_ind.rq_register.rq_dependtype ^
		(JOB_DEPEND_TYPE_BEFORESTART - JOB_DEPEND_TYPE_AFTERSTART);

	if (((pdp = find_depend(type, pattr)) == 0) ||
		((pdjb = find_dependjob(pdp, preq->rq_ind.rq_register.rq_child)) == NULL))
			return (PBSE_IVALREQ);

	del_depend_job(pdjb);
	return (0);
}

/**
 * @brief
 * 		find_dependjob - find a child dependent job with a certain job id
 *
 * @param[in]	pdep	-	dependent jobs
 * @param[in]	name	-	job id to be matched
 *
 * @return	child dependent job
 * @retval	NULL	: fail
 */

static struct depend_job *find_dependjob(struct depend *pdep, char *name)
{
	struct depend_job *pdj;

	if ((pdep == NULL) || (name == NULL))
		return NULL;

	pdj = (struct depend_job *)GET_NEXT(pdep->dp_jobs);
	while (pdj) {
		if (!strcmp(name, pdj->dc_child))
			break;

		pdj = (struct depend_job *)GET_NEXT(pdj->dc_link);
	}
	return (pdj);
}

/**
 * @brief
 * 		make_dependjob - add a depend_job structure
 *
 * @param[in,out]	pdep	-	ptr to head of depend list
 * @param[in]	jobid	-	child (dependent) job
 * @param[in]	host	-	server owning job
 *
 * @return	child dependent job
 */

static struct depend_job *make_dependjob(struct depend *pdep, char *jobid, char *host)
{
	struct depend_job *pdj;

	pdj = (struct depend_job *)malloc(sizeof(struct depend_job));
	if (pdj) {

		CLEAR_LINK(pdj->dc_link);
		pdj->dc_state   = 0;
		pdj->dc_cost    = 0;
		(void)strcpy(pdj->dc_child, jobid);
		(void)strcpy(pdj->dc_svr, host);
		append_link(&pdep->dp_jobs, &pdj->dc_link, pdj);
	}
	return (pdj);
}

/**
 * @brief
 * 		send_depend_req - build and send a Register Dependent request
 *
 * @param[in]	pjob	-	job structure
 * @param[in]	pparent	-	parent job
 * @param[in]	type	-	dependency type
 * @param[in]	op	-	dependent job operation
 * @param[in]	schedhint	-	not used here
 * @param[in]	postfunc	-	call back function to issue_to_svr()
 *
 * @return	error code
 * @retval	0	: success
 * @retval	PBSE_BADHOST	: Unable to perform dependency
 * @retval	PBSE_SYSTEM	: malloc failed
 */

static int
send_depend_req(job *pjob, struct depend_job *pparent, int type, int op, int schedhint, void (*postfunc)(struct work_task *))
{
	int		      i;
	char		     *pc;
	struct batch_request *preq;

	preq = alloc_br(PBS_BATCH_RegistDep);
	if (preq == NULL) {
		log_err(errno, __func__, msg_err_malloc);
		return (PBSE_SYSTEM);
	}

	for (i=0; i<PBS_MAXUSER; ++i) {
		preq->rq_ind.rq_register.rq_owner[i] = pjob->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str[i];
		if (preq->rq_ind.rq_register.rq_owner[i] == '@')
			break;
	}
	preq->rq_ind.rq_register.rq_owner[i] = '\0';
	(void)strcpy(preq->rq_ind.rq_register.rq_parent, pparent->dc_child);
	(void)strcpy(preq->rq_ind.rq_register.rq_child, pjob->ji_qs.ji_jobid);
	/* Append "@<server_name>" since server's name may not match host name */
	(void)strcat(preq->rq_ind.rq_register.rq_child, "@");
	(void)strcat(preq->rq_ind.rq_register.rq_child, pbs_server_name);
	/* kludge for server:port follows */
	if ((pc = strchr(server_name, (int)':')) != NULL) {
		strcat(preq->rq_ind.rq_register.rq_child, pc);
	}
	preq->rq_ind.rq_register.rq_dependtype = type;
	preq->rq_ind.rq_register.rq_op = op;
	(void)strcpy(preq->rq_host, pparent->dc_svr);  /* for issue_to_svr() */

	preq->rq_ind.rq_register.rq_cost = 0;

	if (issue_to_svr(pparent->dc_svr, preq, postfunc) == -1) {
		(void)sprintf(log_buffer, "Unable to perform dependency with job %s", pparent->dc_child);
		return (PBSE_BADHOST);
	}
	return (0);
}


/*
 * This section contains general function for dependency attributes
 *
 * Each attribute has functions for:
 *	Decoding the value string to the machine representation.
 *	Encoding the internal representation of the attribute to external
 *	Setting the value by =, + or - operators.
 *	Comparing a (decoded) value with the attribute value.
 *	Freeing the space malloc-ed to the attribute value.
 *
 * The prototypes are declared in "attribute.h"
 *
 * ----------------------------------------------------------------------------
 * Attribute functions for attributes of type "dependency".
 *
 * The "encoded" or external form of the value is a string with sub-strings
 * separated by commas and terminated by a null.
 *
 * The "decoded" or internal form is a list of depend (and depend_child)
 * structures, which are defined in job.h.
 * ----------------------------------------------------------------------------
 */



struct dependnames {
	int   type;
	char *name;
} dependnames[] = {
	{JOB_DEPEND_TYPE_AFTERSTART, "after" },
	{JOB_DEPEND_TYPE_AFTEROK,    "afterok" },
	{JOB_DEPEND_TYPE_AFTERNOTOK, "afternotok" },
	{JOB_DEPEND_TYPE_AFTERANY,   "afterany" },
	{JOB_DEPEND_TYPE_BEFORESTART, "before" },
	{JOB_DEPEND_TYPE_BEFOREOK,   "beforeok" },
	{JOB_DEPEND_TYPE_BEFORENOTOK, "beforenotok" },
	{JOB_DEPEND_TYPE_BEFOREANY,  "beforeany" },
	{JOB_DEPEND_TYPE_ON,         "on" },
	{-1,			     NULL }
};


/**
 * @brief
 * 		decode_depend - decode a string into an attr of type dependency
 *		String is of form: depend_type:job_id[:job_id:...][,depend_type:job_id]
 *
 * @param[out]	patr	-	an attr of type dependency
 * @param[in]	name	-	attribute name
 * @param[in]	rescn	-	resource name, unused here
 * @param[in]	val	-	attribute value
 *
 * @return	error code
 * @retval	0	: ok
 * @retval	>0	: error
 */

int
decode_depend(struct attribute *patr, char *name, char *rescn, char *val)
{
	int		 rc;
	char		*valwd;

	if ((val == NULL) || (*val == 0)) {
		free_depend(patr);
		patr->at_flags |= ATR_VFLAG_MODIFY;
		return (0);
	}

	/*
	 * for each sub-string (terminated by comma or new-line),
	 * add a depend or depend_child structure.
	 */
	valwd = parse_comma_string(val);
	while (valwd) {
		if ((rc=build_depend(patr, valwd)) != 0) {
			free_depend(patr);
			return (rc);
		}
		valwd = parse_comma_string(NULL);
	}

	patr->at_flags |= ATR_VFLAG_SET | ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;
	return (0);
}

/**
 * @brief
 * 		cpy_jobsvr() - a version of strcpy() that watches for an embedded colon
 *		and escapes it with a leading blackslash.  This is needed because
 *		the colon is overloaded, both as job_id separator within a set of
 *		depend jobs, and as the server:port separator.  Ugh!
 *
 * @param[in,out]	d	-	destination string
 * @param[in,out]	s	-	source string
 */

static void
cpy_jobsvr(char *d, char *s)
{
	while (*d)
		d++;

	while (*s) {
		if (*s == ':')
			*d++ = '\\';
		*d++ = *s++;
	}
	*d = '\0';
}

/**
 * @brief
 * 		dup_depend - duplicate a dependency (see set_depend())
 *
 * @param[in,out]	pattr	-	attribute structure
 * @param[in]	pd	-	ptr dependency list
 *
 * @return	int
 * @retval	0	: success
 */

static int
dup_depend(attribute *pattr, struct depend *pd)
{
	struct depend *pnwd;
	struct depend_job *poldj;
	struct depend_job *pnwdj;
	int		   type;

	type = pd->dp_type;
	if ((pnwd = make_depend(type, pattr)) == 0)
		return (-1);

	pnwd->dp_numexp   = pd->dp_numexp;
	pnwd->dp_numreg   = pd->dp_numreg;
	pnwd->dp_released = pd->dp_released;
	for (poldj = (struct depend_job *)GET_NEXT(pd->dp_jobs); poldj;
		poldj = (struct depend_job *)GET_NEXT(poldj->dc_link)) {
		if ((pnwdj = make_dependjob(pnwd, poldj->dc_child,
			poldj->dc_svr)) == 0)
			return (-1);
		pnwdj->dc_state = poldj->dc_state;
		pnwdj->dc_cost  = poldj->dc_cost;
	}

	return (0);
}

/**
 * @brief
 * 		encode_depend - encode dependency attr into attrlist entry
 *
 * @param[in]	attr	-	ptr to attribute to encode
 * @param[in,out]	phead	-	ptr to head of attrlist list
 * @param[in]	atname	-	attribute name
 * @param[in]	rsname	-	resource name or null
 * @param[in]	mode	-	ncode mode, unused here
 * @param[out]	rtnl	-	Return ptr to svrattrl
 *
 * @return	error code
 * @retval	>0	: ok, entry created and linked into list
 * @retval	=0	: no value to encode, entry not created
 * @retval	-1	: if error
 */
/*ARGSUSED*/

int
encode_depend(attribute *attr, pbs_list_head *phead, char *atname, char *rsname, int mode, svrattrl **rtnl)
{
	int		    ct = 0;
	char		    cvtbuf[22];
	int		    numdep = 0;
	struct depend 	   *nxdp;
	struct svrattrl	   *pal;
	struct depend 	   *pdp;
	struct depend_job  *pdjb = NULL;
	struct dependnames *pn;

	if (!attr)
		return (-1);
	if (!(attr->at_flags & ATR_VFLAG_SET))
		return (0);	/* no values */

	pdp = (struct depend *)GET_NEXT(attr->at_val.at_list);
	if (pdp == NULL)
		return (0);

	/* scan dependencies types to compute needed base size of svrattrl */

	for (nxdp=pdp; nxdp; nxdp=(struct depend *)GET_NEXT(nxdp->dp_link)) {
		if (nxdp->dp_type == JOB_DEPEND_TYPE_ON)
			ct += 30;   /* a guess at a reasonable amt of spece */
		else {
			ct += 12;	/* for longest type */
			pdjb = (struct depend_job *)GET_NEXT(nxdp->dp_jobs);
			while (pdjb) {
				ct += PBS_MAXSVRJOBID + PBS_MAXSERVERNAME + 3;
				pdjb = (struct depend_job *)GET_NEXT(pdjb->dc_link);
			}
		}
	}

	if ((pal = attrlist_create(atname, rsname, ct)) == NULL) {
		return (-1);
	}
	*pal->al_value = '\0';
	for (nxdp=pdp; nxdp; nxdp=(struct depend *)GET_NEXT(nxdp->dp_link)) {
		if ((nxdp->dp_type != JOB_DEPEND_TYPE_ON)       &&
			!(pdjb = (struct depend_job *)GET_NEXT(nxdp->dp_jobs)))
				continue;	/* no value, skip this one */
		if (nxdp != pdp)
			strcat(pal->al_value, ",");	/* comma between */
		pn = &dependnames[nxdp->dp_type];
		strcat(pal->al_value, pn->name);
		if (pn->type == JOB_DEPEND_TYPE_ON) {
			sprintf(cvtbuf, ":%d", nxdp->dp_numexp);
			strcat(pal->al_value, cvtbuf);
		} else {
			while (pdjb) {
				strcat(pal->al_value, ":");
				cpy_jobsvr(pal->al_value, pdjb->dc_child);
				if (*pdjb->dc_svr != '\0') {
					strcat(pal->al_value, "@");
					cpy_jobsvr(pal->al_value, pdjb->dc_svr);
				}
				pdjb = (struct depend_job *)GET_NEXT(pdjb->dc_link);
			}
		}
		++numdep;
	}
	if (numdep) {
		/* there are dependencies recorded, added to the list	*/
		pal->al_flags = attr->at_flags;
		append_link(phead, &pal->al_link, pal);
		if (rtnl)
			*rtnl = pal;
		return (1);
	} else {
		/* there are no dependencies, just the base structure,	*/
		/* so remove this svrattrl from ths list		*/
		(void)free(pal);
		if (rtnl)
			*rtnl = NULL;
		return (0);
	}
}

/**
 * @brief
 * 		set_depend - set value of attribute of dependency type to another
 * @par Functionality:
 *		A=B --> set of dependencies in A replaced by set in B
 *		A+B --> dependencies in B added to list in A
 *		A-B --> not defined
 *
 * @param[in,out]	pattr	-	attribute structure
 * @param[in]	new	-	ptr dependency list
 * @param[in]	op	-	operation which needs to be performed
 * 						Ex. SET, INCR, DECR.
 *
 * @return	error code
 * @retval	0	: ok
 * @retval	>0	: error
 */

int
set_depend(attr, new, op)
struct attribute *attr;
struct attribute *new;
enum batch_op op;
{
	struct depend *pdnew;
	struct depend *pdold;
	int	       rc;

	assert(attr && new);

	switch (op) {
		case SET:

			/*
			 * if the type of dependency entry already exists, we are
			 * going to replace it, so get rid of the old and dup the new
			 */

			pdnew = (struct depend *)GET_NEXT(new->at_val.at_list);
			while (pdnew) {
				pdold = find_depend(pdnew->dp_type, attr);
				if (pdold)
					del_depend(pdold);
				if ((rc=dup_depend(attr, pdnew)) != 0)
					return (rc);
				pdnew = (struct depend *)GET_NEXT(pdnew->dp_link);
			}
			break;


		case INCR:	/* not defined */
		case DECR:	/* not defined */
		default:	return (PBSE_IVALREQ);
	}
	attr->at_flags |= ATR_VFLAG_SET | ATR_VFLAG_MODIFY | ATR_VFLAG_MODCACHE;
	return (0);
}

/*
 * comp_depend - compare two attributes of type dependency
 *	This operation is undefined.
 *
 *	Returns: 0
 *		+1
 *		-1
 */

int
comp_depend(attr, with)
struct attribute *attr;
struct attribute *with;
{

	return (-1);

}
/**
 * @brief
 * 		free_depend	-	free the dependency link and dependent jobs from the
 * 						list of resources in the attribute structure.
 *
 * @param[in,out]	attr	-	attribute structure which contains the resource list to be freed
 */
void
free_depend(struct attribute *attr)
{
	struct depend	  *pdp;
	struct depend_job *pdjb;

	while ((pdp = (struct depend *)
		GET_NEXT(attr->at_val.at_list)) != NULL) {
		while ((pdjb = (struct depend_job *)
			GET_NEXT(pdp->dp_jobs)) != NULL) {
			delete_link(&pdjb->dc_link);
			(void)free(pdjb);
		}
		delete_link(&pdp->dp_link);
		(void)free(pdp);
	}
	/* free any data cached for stats */
	if (attr->at_user_encoded != NULL || attr->at_priv_encoded != NULL) {
		free_svrcache(attr);
	}
	attr->at_flags &= ~ATR_VFLAG_SET;
}


/**
 * @brief
 * 		build_depend -  build a dependency structure
 * 		parse the string and turn it into a list of depend structures
 *
 * @param[in,out]	attr	-	attribute structure which contains the dependency structure
 * @param[in]	value	-	attribute value which is a set of sub-string (terminated by comma or new-line)
 *
 * @return	error code
 * @retval	0	: success
 * @retval	non-zero	: error number
 */

static int
build_depend(attribute *pattr, char *value)
{
	char			fhn[PBS_MAXHOSTNAME+1];
	struct depend		*have[JOB_DEPEND_NUMBER_TYPES];
	int			i;
	int			numwds;
	struct depend		*pd;
	struct depend_job	*pdjb;
	struct dependnames	*pname;
	char		   	*pwhere;
	char			*valwd;
	char			*nxwrd;
	int			type;

	/*
	 * Map first subword into dependency type.  If there is just the type
	 * with no following job id or count, then leave an empty depend
	 * struct;  set_depend will "remove" any of that kind.
	 */

	if ((nxwrd = strchr(value, (int)':')) != NULL)
		*nxwrd++ = '\0';


	for (pname = dependnames; pname->type != -1; pname++)
		if (!strcmp(value, pname->name))
			break;

	if (pname->type == -1)
		return (PBSE_BADATVAL);
	type = pname->type;

	/* what types do we have already? */

	for (i=0; i<JOB_DEPEND_NUMBER_TYPES; i++)
		have[i] = NULL;
	for (pd = (struct depend *)GET_NEXT(pattr->at_val.at_list);
		pd; pd = (struct depend *)GET_NEXT(pd->dp_link))
		have[pd->dp_type] = pd;

	if ((pd = have[type]) == NULL) {
		pd = make_depend(type, pattr);
		if (pd == NULL)
			return (PBSE_SYSTEM);
	}


	/* now process the value string */

	numwds = 0;
	while (nxwrd && (*nxwrd != '\0')) {

		numwds++;		/* number of arguments */
		valwd = nxwrd;

		/* find end of next word delimited by a : but not a '\:' */

		while (((*nxwrd != ':') || (*(nxwrd-1) == '\\')) && *nxwrd)
			nxwrd++;
		if (*nxwrd)
			*nxwrd++ = '\0';

		/* now process word (argument) depending on "depend type" */

		if (type == JOB_DEPEND_TYPE_ON) {

			/* a single word argument, a count */

			if (numwds == 1) {
				pd->dp_numexp = strtol(valwd, &pwhere, 10);
				if ((pd->dp_numexp < 1) ||
					(pwhere && (*pwhere != '\0'))) {
					return (PBSE_BADATVAL);
				}
			} else {
				return (PBSE_BADATVAL);
			}

		} else {	/* all other dependency types */


			/* a set of job_id[\:port][@server[\:port]] */

			pdjb = (struct depend_job *)malloc(sizeof(*pdjb));
			if (pdjb) {
				CLEAR_LINK(pdjb->dc_link);
				pdjb->dc_state = 0;
				pdjb->dc_cost  = 0;
				pdjb->dc_svr[0] = '\0';
				pwhere = pdjb->dc_child;

				while (*valwd) {
					if (*valwd == '@') {	/* switch to @server */
						*pwhere = '\0';
						pwhere = pdjb->dc_svr;
					} else if ((*valwd=='\\') && (*(valwd+1)==':')) {
						*pwhere++ = *++valwd;  /* skip over '\' */
					} else {
						*pwhere++ = *valwd;	/* copy jobid */
					}
					++valwd;
				}
				*pwhere = '\0';

				if (pdjb->dc_svr[0] == '\0') {
					pwhere = strchr(pdjb->dc_child, (int)'.');
					if (pwhere) {
						pwhere++;
						if (strncmp(pwhere, pbs_conf.pbs_server_name, PBS_MAXSERVERNAME) == 0) {
							(void)strcpy(pdjb->dc_svr, pbs_default());
						} else if (get_fullhostname(pwhere, fhn, (sizeof(fhn) - 1)) == 0) {
							(void)strcpy(pdjb->dc_svr, fhn);
						} else {
							(void)free(pdjb);
							return (PBSE_BADATVAL);
						}
					} else {
						(void)free(pdjb);
						return (PBSE_BADATVAL);
					}
				}

				append_link(&pd->dp_jobs, &pdjb->dc_link, pdjb);
			} else {
				return (PBSE_SYSTEM);
			}

		}
	}

	return (0);
}


/**
 * @brief
 * 		clear_depend - clear a single dependency set
 *		If the "exist" flag is set, any depend_job sub-structures are freed.
 *
 * @param[out]	pd	-	a single dependency set
 * @param[in]	type	-	freed dependency type
 * @param[in]	exist	-	If the "exist" flag is set,
 * 							any depend_job sub-structures are freed.
 */

static void
clear_depend(struct depend *pd, int type, int exist)
{
	struct depend_job *pdj;

	if (exist) {
		while ((pdj = (struct depend_job *)
			GET_NEXT(pd->dp_jobs)) != NULL) {
			del_depend_job(pdj);
		}
	} else {
		CLEAR_HEAD(pd->dp_jobs);
		CLEAR_LINK(pd->dp_link);
	}
	pd->dp_type = type;
	pd->dp_numexp = 0;
	pd->dp_numreg = 0;
	pd->dp_released = 0;
}

/**
 * @brief
 * 		del_depend - delete a single dependency set, including any depend_jobs
 *
 * @param[in,out]	pd	-	a single dependency set
 */

static void
del_depend(struct depend *pd)
{
	struct depend_job *pdj;

	while ((pdj = (struct depend_job *)GET_NEXT(pd->dp_jobs)) != NULL) {
		del_depend_job(pdj);
	}
	delete_link(&pd->dp_link);
	(void)free(pd);
}

/**
 * @brief
 * 		del_depend_job - delete a single depend_job structure
 *
 *  @param[in,out]	pdj	-	a single depend_job structure
 */

static void
del_depend_job(struct depend_job *pdj)
{
	delete_link(&pdj->dc_link);
	(void)free(pdj);
}
