/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.30  JRR     20-Jan-1992	Fixed call to signal.    
*       V01.40  JRR     22-Jan-1992	RS6000 use enf_bsdquo sometimes.
*       V01.50  JRR     23-Jan-1992	Fix call to setpgrp for RS6000.
*       V01.6   JRR     02-Mar-1992	Added Cosmic V2 changes.
*       V01.7   JRR     02-Apr-1992	Added broadcast messages.
*                       09-Apr-1992     Added CERN enhancements.
*	V01.8	JRR	26-May-1992	Fixed up support for RS6000.
*					Fixed up nqsacct structure.
*					Added header.
*	V01.9	JRR	21-Aug-1992	Check the /etc/environment file
*					for PATH.
*					Round-robin based on order in
*					destination list, not mid.
*			23-Nov-1992	If load information available, 
*					use it for load balanced queues.
*	V01.10	JRR	23-Dec-1992	Change rindex=>strrchr.
*					Add groups security fix.
*	V01.11	JRR	26-Feb-1993	Added Boeing enhancement for Mids.
*			05-Apr-1993	Write out new style init message.
*	V01.12	JRR	24-Aug-1993	Fix ordering of destinations.
*	V01.13	JRR	23-Feb-1994	getdtablesize => sysconf.
*	V01.14	JRR	28-Feb-1994	Added support for SOLARIS.
*	V01.15	JRR	22-Apr-1994	Ranking compute servers.
*	
*/

/*++ nqs_reqser.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.36/src/RCS/nqs_reqser.c,v $
 *
 * DESCRIPTION:
 *
 *
 *	This module contains all of the logic associated with the
 *	request server created for each spawned NQS request.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	May 5, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.15 $ $Date: 1994/09/02 17:39:43 $ $State: Exp $)
 * $Log: nqs_reqser.c,v $
 * Revision 1.15  1994/09/02  17:39:43  jrroma
 * Version 3.36
 *
 * Revision 1.14  1994/03/30  20:36:51  jrroma
 * Version 3.35.6
 *
 * Revision 1.13  94/02/24  21:30:55  jrroma
 * Version 3.35.3
 * 
 * Revision 1.12  93/09/10  13:57:20  jrroma
 * Version 3.35
 * 
 * Revision 1.11  93/07/13  21:33:59  jrroma
 * Version 3.34
 * 
 * Revision 1.10  93/02/05  23:16:48  jrroma
 * Version 3.31
 * 
 * Revision 1.9  92/12/22  15:41:30  jrroma
 * Version 3.30
 * 
 * Revision 1.8  92/06/18  17:31:20  jrroma
 * Added gnu header
 * 
 * Revision 1.7  92/05/06  10:43:11  jrroma
 *  Version 3.20
 * 
 * Revision 1.6  92/03/02  16:05:58  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.5  92/01/23  10:44:17  jrroma
 * Fix call to setpgrp for RS6000.
 * 
 * Revision 1.4  92/01/22  13:33:43  jrroma
 * Fix up RS6000 to use bsdquo sometimes.
 * 
 * Revision 1.3  92/01/20  14:29:01  jrroma
 * Fixed up call to signal.
 * 
 * Revision 1.2  92/01/17  11:12:40  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  11:11:58  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <malloc.h>
#include <time.h>
#include <grp.h>
#include <limits.h>
#include <signal.h>			/* Signal definitions */
#include "nqspacket.h"			/* NQS local message packet types */
#include "nqsmail.h"			/* NQS mail definitions */
#include "nqsxvars.h"			/* NQS global variables and dirs */
#include "requestcc.h"			/* NQS request completion codes */
#include "nqsacct.h"                    /* Accounting file structures */

#if SGI
#include <sys/schedctl.h>
#endif

#ifndef __CEXTRACT__
#if __STDC__

static char *get_the_path ( struct passwd *passwd );
static int mkspool ( struct rawreq *rawreq, char *(*namefn)(), struct passwd *passwd, int filemask );
static void seracknowledge ( void );
static void serexecute ( void );

#else /* __STDC__ */

static char *get_the_path (/* struct passwd *passwd */);
static int mkspool (/* struct rawreq *rawreq, char *(*namefn)(), struct passwd *passwd, int filemask */);
static void seracknowledge (/* void */);
static void serexecute (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

/*
 *	Variables local to this module.
 */
static short Stateset;			/* Semaphore */
static long jid;                        /* job ID */

/*
 *      Internal data structure for ordering pipe destinations.
 */
struct pipe_dest {
    short status;                   /* Current pipe destination status */
                                        /* bits: DEST_ */
    time_t retry_at;                /* If in retry mode, then this value */                                        /* determines when the destination */
                                        /* should be reenabled */
    time_t retrytime;               /* If in retry mode, then this value */                                        /* records the time at which the */
                                        /* destination first failed */
    long retrydelta;                /* RESERVED for future use */
    char rqueue [MAX_QUEUENAME+1];  /* Name of destination queue */
    Mid_t rhost_mid;                /* Machine-id of remote destination */
    struct pipe_dest *next;         /* Ptr to next dest "pipeto" descr */
    time_t  time;                   /* When we got this information */
    int no_jobs;                    /* Number of NQS jobs currently running*/
    float load1;                    /* 1 minute load average */
    float load5;                    /* 5 minute load average */
    float load15;                   /* 15 minute load average */
    int rap;			    /* Relative Application Performance */
    float rap_per_job;		    /* RAP/no_jobs */
    float rap_per_la;		    /* RAP/load5 */
};


/*** nqs_reqser
 *
 *
 *	void nqs_reqser():
 *
 *	We are the request server or shell process.
 *	We are the child of the shepherd process created
 *	in nqs_spawn.c.
 *
 *	  At this point:
 *		File descriptor 0: Control file (O_RDWR).
 *		File descriptor 1: Stdout writing to log process.
 *		File descriptor 2: Stderr writing to log process.
 *		File descriptor 3: Write file descriptor for sending
 *				   back request completion messages
 *				   from the server to the shepherd.
 *		File descriptor 4: Write file descriptor to NQS daemon
 *				   request pipe, this is enforced
 *				   by startup code in nqs_boot.c!
 *
 *	  If we are spawning a batch request with no specific shell,
 *	  and the shell strategy is FREE, then
 *
 *		File descriptor 5: Read file descriptor from the
 *				   shepherd process to the server
 *				   shell process containing the
 *				   user-process visible shell
 *				   script link name.
 *
 *	  otherwise:
 *
 *		File descriptor 5: is closed.
 *
 *	  In all cases:
 *
 *		File descriptors [6.. sysconf ( _SC_OPEN_MAX )-1] are closed.
 *
*
 *
 *	One of two distinct situations now exists:
 *
 *	  Case 1:  We are spawning a batch request with a shell
 *		   strategy of FREE.
 *
 *	.-----------------------.
 *	|  NQS daemon process	|<============================================.
 *	|			|<==.					     ||
 *	`-----------------------'  || Named-pipe	Named-pipe connection||
 *		    |		   || connected to	to the local NQS     ||
 *		    V		   || the NQS daemon	daemon request/event ||
 *	.-----------------------.  || request/event	pipe.		     ||
 *	|  Server shepherd	|>==' pipe.				     ||
 *	|  process.		|>=======================================.   ||
 *	|			|<==.					||   ||
 *	`-----------------------'  ||			Pipe from	||   ||
 *		    |		   || Serexit() pipe	shepherd process||   ||
 *		    V		   || TO shepherd	to shell with	||   ||
 *	.-----------------------.  || from shell.	scriptfile name.||   ||
 *	|  Shell process.	|>=='					||   ||
 *	|			|<======================================='   ||
 *	|			|>============================================'
 *	`-----------------------'
 *
 *
 *
 *	  Case 2:  We are spawning a batch request with a shell
 *		   strategy of FIXED or LOGIN (or the request
 *		   specifies a particular shell), or we are spawning
 *		   a device, network, or pipe request.
 * 
 *	.-----------------------.
 *	|  NQS daemon process	|<============================================.
 *	|			|<==.					     ||
 *	`-----------------------'  || Named-pipe	Named-pipe connection||
 *		    |		   || connected to	to the local NQS     ||
 *		    V		   || the NQS daemon	daemon request/event ||
 *	.-----------------------.  || request/event	pipe.		     ||
 *	|  Server shepherd	|>==' pipe.				     ||
 *	|  process.		|<==.					     ||
 *	`-----------------------'  ||					     ||
 *		    |		   || Serexit() pipe			     ||
 *		    V		   || TO shepherd			     ||
 *	.-----------------------.  || from shell.			     ||
 *	|  Shell process.	|>=='					     ||
 *	|			|>============================================'
 *	`-----------------------'
 *
 *
 */
void nqs_reqser (request, rawreq, restartflag, queue, device, passwd)
struct request *request;		/* Request structure */
struct rawreq *rawreq;			/* Raw request header from */
					/* control file */
int restartflag;			/* BOOLEAN request is restarting */
					/* execution */
struct nqsqueue *queue;			/* Queue being served */
struct device *device;			/* Device to use (can be NIL) */
struct passwd *passwd;			/* Password entry for request */
{

#if	MAX_QUEUENAME > MAX_DEVNAME
	char argv0buffer [MAX_QUEUENAME + 8];
#else
	char argv0buffer [MAX_DEVNAME + 8];
#endif
					/* Space for argv [0] name of */
					/* queue or device name + " server" */
	char environment [MAX_ENVIRONMENT];
					/* Space for environment */
	char restart_msg [80];		/* Batch request restart message */
	char *server;			/* Server and/or server arguments, */
					/* or pathname of batch shell */
	char *argv [MAX_SERVERARGS+1];	/* Execve() argument pointers */
	char *envp [MAX_ENVIRONVARS+1];	/* Execve() environment pointers */
	struct qdestmap *mapdest;	/* Used to walk destset for pipe-q */
	int fd;				/* File descriptor for device */
	int envpcount;			/* Number of environment vars */
	int envpsize;			/* Size of environment in chars */
	int filemask;			/* = O_CREAT if restartflag is TRUE */
					/* = O_CREAT | O_TRUNC if */
					/*   restartflag is FALSE */
	long i, j;			/* Iteration counter & scratch var */
	char *cp;	   		/* Character pointer */
	int persist;			/* Persistent pipe queue request */
	/*
	 *  Variables specific to the spawning of a batch request.
	 */
	char minusname [257];		/* Name of shell (path removed) */
					/* prefixed with a "-" character */
	char buffer [MAX_REQPATH+2];	/* Batch request control file line */
					/* buffer */
	char *shellname;		/* Name of shell (path removed) */
	char *hostname;			/* Name of submitter's host */
	time_t timeval;			/* Current time */
	unsigned long retry_time;	/* Retry time */
	int script;			/* Shell script file descriptor */
	int standout;			/* Standard output file */
	int standerr;			/* Standard error file */
        int fd_acct;                    /* Accounting file descriptor */
        struct nqsacct_init1 acct_init1;  /* Accounting record */
	Mid_t mymid;			/* what is my machine id? */
	int dest_number;		/* Counter for destinations */
        char **cpp;
        int openflags;
        int cmask;
        struct stat sbuf;
        char fullname [MAX_REQPATH+2];
        char *retrymsg = "I$Device offline (%s:%s). retries = %d\n";
	int number_of_dests;		 /* Number of destinations */
	int empty_dests;	       	 /* TRUE if empty destinations */
	int loadinfo_dests;		 /* Destinations with load information */
	int start_destination;		 /* Destination number to start at */
	struct pipe_dest pipe_dest_header; /* Header of list of pipe destinations */
	struct pipe_dest *pd_ptr;	 /* Pipe destination pointer */
	struct pipe_dest *pd_last;	 /* Pipe destination pointer */
	struct pipe_dest *pd_curr;	 /* Pipe destination pointer */
	struct loadinfo *load_ptr;	 /* Load information pointer */
	struct pipe_dest *pd_order;	 /* Pointer to start of ordered list */
	struct pipe_dest *pd_list[MAX_QDESTS];	/* Ordered list of pointers */
	struct gendescr *server_descr;
	time_t	now_now;		 /* The time now */
	int ngroups;			 /* Number of groups in the group set */
	gid_t gidset[NGROUPS_MAX];	 /* The concurrent group set */
	char *filename;
	char *dirname;
	envpcount = 0;			/* No environment vars exist yet */
	envpsize = 0;

	if (Debug) {
	    fprintf (stderr, "D$nqs_reqser: entering for %d\n", 
			rawreq->orig_seqno);
	    fflush(stderr);
	}

        /*
         * If the current environment defines directory independent
         * pathnames, pass them into the new environment.
         */
        if ((dirname = getenv (Nqs_nmapdir)) != (char *)NULL) {
                cp = environment + envpsize;
                envp [envpcount++] = cp;
                sprintf (cp, "%s=%s", Nqs_nmapdir, dirname);
                envpsize += strlen (cp) + 1;
        }
        if ((dirname = getenv (Nqs_libdir)) != (char *)NULL) {
                cp = environment + envpsize;
                envp [envpcount++] = cp;
                sprintf (cp, "%s=%s", Nqs_libdir, dirname);
                envpsize += strlen (cp) + 1;
        }
        if ((dirname = getenv (Nqs_spooldir)) != (char *)NULL) {
                cp = environment + envpsize;
                envp [envpcount++] = cp;
                sprintf (cp, "%s=%s", Nqs_spooldir, dirname);
                envpsize += strlen (cp) + 1;
        }
	if (queue->q.type != QUE_BATCH) {
		/*
		 *  Device, network, and pipe queue servers always have
		 *  a debug level environment variable.
		 */
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "DEBUG=%1d", Debug);
		envpsize += strlen (cp) + 1;
		if (queue->q.type != QUE_DEVICE) {
			/*
			 *  Network queue and pipe queue servers are
			 *  spawned with a real AND effective user-id
			 *  of root.  This is necessary so that the
			 *  ../lib/establish.c module can bind to a
			 *  system socket port (<1024).
			 *
			 *  These servers however must know the account
			 *  upon whose behalf they are operating.  This
			 *  information is passed to them through the
			 *  environment.
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "UID=%1d", passwd->pw_uid);
			envpsize += strlen (cp) + 1;
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "USERNAME=%s", passwd->pw_name);
			envpsize += strlen (cp) + 1;
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
			/*
			 *  Network queue and pipe queue servers for
			 *  System V based implementations of NQS always
 			 *  have a TZ (timezone) environment variable
			 *  (for ../lib/establish.c as invoked by network
			 *  and pipe queue servers).  (Please note that
			 *  the TZ environment variable is guaranteed
			 *  to be present, since nqs_boot.c checks for
			 *  it.)
			 *
			 *  Berkeley style NQS implementations do not
			 *  need or use TZ.
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "TZ=%s", getenv ("TZ")) + 1;
		        envpsize += strlen (cp);
#else
#if	BSD43 | ULTRIX | DECOSF
#else
BAD SYSTEM TYPE
#endif
#endif
		}
	}
	switch (queue->q.type) {
	case QUE_BATCH:
		/*
		 *  We are spawning a batch request.
		 */
		filemask = O_CREAT;
		if (!restartflag) {
			/*
			 *  This request is being spawned for the very
			 *  first time.  Set filemask to also include
			 *  O_TRUNC.
			 */
			filemask |= O_TRUNC;
		}
		/*
		 *  Open the shell script file.
		 */
		if (Shell_strategy == SHSTRAT_FREE &&
		    rawreq->v.bat.shell_name [0] == '\0') {
			/*
			 *  The batch request is being run with a shell
			 *  strategy of FREE.  File descriptor #5 has the
			 *  the user-process visible link name of the
			 *  shell script file for the request.
			 */
			script = 5;	/* Read on file-descr #5 */
		}
		else {
			/*
			 *  The batch request is being run with a shell
			 *  strategy of FIXED or LOGIN (or the request
			 *  specifies a particular shell).
			 */
			if ((script = opendata (rawreq->orig_seqno,
						rawreq->orig_mid, 1)) == -1) {
				/*
				 *  We were unable to open the shell script
				 *  file.
				 */
				if (errno == ENFILE) {
					serexit (RCM_ENFILERUN, (char *) 0);
				}
				serexit (RCM_BADCDTFIL,
					"Unable to open shell script file.");
			}
		}
		/*
		 *  Determine the principal name of the request owner's
		 *  machine as seen from the execution machine.
		 */
		errno = 0;		/* Clear errno so that we can tell */
					/* if a system call error occurred */
					/* in the nmap_get_nam() function */
		hostname = nmap_get_nam (rawreq->orig_mid);
		if (hostname == (char *) 0) {
		    /*
		     *  Unable to determine the name of the
		     *  request owner's machine.
		     */
		    if (errno == ENFILE) {
			serexit (RCM_ENFILERUN, (char *) 0);
		    }
		    /*
		     *  The request owner's machine-id is no
		     *  longer known to the execution machine.
		     */
		    serexit (RCM_MIDUNKNOWN, (char *) 0);
		}
		/*
		 *  Build the login environment and select the proper
		 *  shell.
		 */
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "HOME=%s", passwd->pw_dir);
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		if (rawreq->v.bat.shell_name [0] == '\0') {
		    /*
		     *  The batch request does not explicitly
		     *  specify a shell.  NQS must choose the
		     *  shell used to execute the batch request
		     *  based upon the configured shell
		     *  strategy of the local system.
		     */
		    if (Shell_strategy == SHSTRAT_FIXED) {
			/*
			 *  A shell strategy of FIXED is in
			 *  effect.
			 */
			sprintf (cp, "SHELL=%s", Fixed_shell);
		    }
		    else {
			/*
			 *  A shell strategy of FREE of LOGIN
			 *  is presently in effect.
			 */
			if (passwd->pw_shell == (char *) 0 ||
			    passwd->pw_shell [0] == '\0') {
			    /*
			     *  The default shell is the Bourne shell.
			     */
			    sprintf (cp, "SHELL=%s", "/bin/sh");
			}
			else {
			    /*
			     *  The password file entry for the owner
			     *  identifies a shell to be used.
			     */
			    sprintf (cp, "SHELL=%s", passwd->pw_shell);
			}
		    }
		}
		else {
		    /*
		     *  The request specifies that a specific shell
		     *  be used.
		     */
		    sprintf (cp, "SHELL=%s", rawreq->v.bat.shell_name);
		}
		envpsize += strlen (cp) + 1;
		/*
		 *  At this time, the character pointer variable: cp
		 *  refers to the SHELL= environment variable.
		 *
		 *  Set the variable:  server  to point to the
		 *  fully qualified pathname of the shell that
		 *  is going to be spawned.
		 */
		server = cp + 6;		/* Step over "SHELL=" */
		/*
		 *  Build the remaining login environment variable
		 *  set.
		 */
                cp = environment + envpsize;
                envp [envpcount++] = cp;
                sprintf (cp, "QSUB_QUEUE=%s", queue->q.namev.name);
                envpsize += strlen(cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "USER=%s", passwd->pw_name);
		envpsize += strlen(cp) + 1;
                cp = environment + envpsize;
                envp [envpcount++] = cp;
		sprintf (cp, "ENVIRONMENT=BATCH");
		envpsize += strlen(cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		strcpy(cp, get_the_path(passwd) );
		envpsize += strlen(cp) + 1;				 
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "LOGNAME=%s", passwd->pw_name) + 1;
	        envpsize += strlen (cp);
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "MAIL=/usr/mail/%s", passwd->pw_name) + 1;
	        envpsize += strlen (cp);
		cp = environment + envpsize;
		envp [envpcount++] = cp;
                sprintf (cp, "TZ=%s", getenv ("TZ")) + 1;
	        envpsize += strlen (cp);
#else
#if	BSD43 | ULTRIX | DECOSF
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "USER=%s", passwd->pw_name);
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "MAIL=/usr/spool/mail/%s", passwd->pw_name);
		envpsize += strlen (cp) + 1;
#else
BAD SYSTEM TYPE
#endif
#endif								       
		if ((shellname = strrchr (server, '/')) != (char *) 0)
			shellname += 1;		/* Step over last '/' */
		else shellname = server;	/* No '/' to step over */
		/*
		 *  Construct the proper argv array.
		 */
		sprintf (minusname, "-%s", shellname);
		argv [0] = minusname;
		argv [1] = (char *) 0;
		/*
		 *  Read the varying portion of the control file as a
		 *  sequence of lines terminated with newline characters,
		 *  adding environment variables as appropriate.
		 */
		fseek (stdin, lseek (fileno (stdin), 0L, 1), 0);
		while (!feof (stdin) &&
			fgets (buffer, MAX_REQPATH+2, stdin) != (char *) 0) {
		    i = strlen (buffer) - 1;	/* Get length of buffer -1 */
		    if (buffer [i] != '\n') {
			/*
			 *  We did not see a new line character.
			 *  The control file line is too long.
			 */
			serexit (RCM_BADCDTFIL,
				 "Varying control file line too long.");
		    }
		    buffer [i] = '\0';		/* Delete the newline char */
		    if (buffer [0] == 'D') {
			/*
			 *  This line has the value for the environment
			 *  variable:  QSUB_WORKDIR.
			 */
			i--;		/* i now equals the length of the */
					/* QSUB_WORKDIR directory string*/
			/*
			 *  Check to see if the environment variables:
			 *  QSUB_HOST, QSUB_REQID, QSUB_REQNAME,
			 *  and QSUB_WORKDIR will fit.
			 */
			if (envpcount + 4 >= MAX_ENVIRONVARS ||
			    strlen (hostname) * 2 + envpsize + 59 + i +
			    strlen (rawreq->reqname) > MAX_ENVIRONMENT) {
				/*
				 *  There is not sufficient space in
				 *  the environment array for all of
				 *  this information.
				 */
				serexit (RCM_2MANYENVARS, (char *) 0);
			}
			/*
			 *  Create the "QSUB_HOST=" environment variable.
			 *  QSUB_HOST=hostname
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "QSUB_HOST=%s", hostname);
			envpsize += strlen (cp) + 1;
			/*
			 *  Create the "QSUB_REQID=" environment variable.
			 *  QSUB_REQID=nnnnn.hostname
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "QSUB_REQID=%1ld.%s",
					    (long) rawreq->orig_seqno,
					     hostname);
			envpsize += strlen (cp) + 1;
			/*
			 *  Create the "QSUB_REQNAME=" environment variable.
			 *  QSUB_REQNAME=reqname
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "QSUB_REQNAME=%s", rawreq->reqname);
			envpsize += strlen (cp) + 1;
			/*
			 *  Create the "QSUB_WORKDIR=" environment variable.
			 *  QSUB_WORKDIR=workdir
			 */
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "QSUB_WORKDIR=%s", buffer+1);
			envpsize += strlen (cp) + 1;
		    }
		    else if (buffer [0] == 'E') {
			/*
			 *  This is a simple environment variable.
			 *  Get space for the environment variable
			 *  (i has the length of the entire environment
			 *  string + 1), and store the environment
			 *  string.
			 */
			if (envpcount >= MAX_ENVIRONVARS ||
			    envpsize + i > MAX_ENVIRONMENT) {
				/*
				 *  There is not sufficient space in
				 *  the environment array.
				 */
				serexit (RCM_2MANYENVARS, (char *) 0);
			}
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			strcpy (cp, buffer+1);
			envpsize += i;
		    }
		    /*
		     *  File staging event specifications: [I,O]
		     *  are, and should always be, ignored here.
		     *  Their implementation will always be done
		     *  elsewhere.
		     */
		}
		/*
		 *  If the output file is to be spooled, then create the
		 *  temporary version of it in the NQS output spooling
		 *  directory BEFORE we chdir() to the user's home
		 *  directory.
		 *
		 *  It is possible that NQS will someday be linked
		 *  with a Newcastle or equivalent distributed file
		 *  system library, which intercepts system calls always
		 *  passing ABSOLUTE file paths to the kernel on the
		 *  appropriate machine.  Since the spool files reside
		 *  within the NQS protected file hierarchy, it is
		 *  necessary to be running as root, when a spool file
		 *  is being created under Newcastle-like file system
		 *  implementations.
		 */
		if (rawreq->v.bat.stdout_acc & OMD_SPOOL) {
		    /*
		     *  The output file is to be spooled.
		     */
		    standout = mkspool (rawreq, namstdout, passwd,
					    filemask);
		    if (standout == -1) {
		        /*
			 *  Alas, we failed!
		         */
			if (errno == ENFILE) {
			    /*
			     *  The system file table is full.
			     */
			    serexit (RCM_ENFILERUN, (char *) 0);
			 }
			 if (errno == ENOSPC) {
			    /*
			     *  Insufficient space.
			     */
			    serexit (RCM_ENOSPCRUN, (char *) 0);
			 }
			 fprintf(stderr, "D$nqs_reqser: Unable to create\
 spooled stdout, errno %d.\n", errno);
			 fflush(stderr);
			 serexit (RCM_UNAFAILURE,
					"Unable to create spooled stdout.");
		    }
		}
		/*
		 *  If the error file is to be spooled, then create the
		 *  temporary version of it in the NQS output spooling
		 *  directory BEFORE we chdir() to the user's home
		 *  directory.
		 */
		if ((rawreq->v.bat.stderr_acc & OMD_SPOOL) &&
		    (rawreq->v.bat.stderr_acc & OMD_EO) == 0) {
		    /*
		     *  The error file is to be spooled.
		     */
		    standerr = mkspool (rawreq, namstderr, passwd,
					    filemask);
		    if (standerr == -1) {
			/*
			 *  Alas, we failed!
			 */
			if (errno == ENFILE) {
			    /*
			     *  The system file table is full.
			     */
			    serexit (RCM_ENFILERUN, (char *) 0);
			}
			if (errno == ENOSPC) {
			    /*
			     *  Insufficient space.
			     */
			    serexit (RCM_ENOSPCRUN, (char *) 0);
			}
			fprintf(stderr, "D$nqs_reqser: Unable to create\
 spooled stderr, errno %d.\n", errno);
			fflush(stderr);
			serexit (RCM_UNAFAILURE,
					"Unable to create spooled stderr.");
		    }
		}
		break;
	case QUE_DEVICE:
		/*
		 *  The request is a device request.  Try to open the device
		 *  for reading AND writing.  If this fails, then try to
		 *  open the device for writing only.  If that fails, then
		 *  go through a wait/retry process until the retry limit
		 *  is reached.
		 */
		close (1);		/* Stdout is going to become the */
					/* device leaving only stderr for */
					/* catastrophic error reporting */
		i = 0;
                openflags = 0;
                if ((stat (device->fullname, &sbuf)) == -1) {
                        serexit(RCM_DEVOPEFAI, asciierrno());
                }
                if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
                    sprintf(fullname,"%s/%ld.%s",device->fullname,
                                (long) rawreq->orig_seqno,
                                getmacnam(rawreq->orig_mid));
                    cmask = umask(0177);
                    openflags = O_CREAT;
                } else
                        strcpy(fullname, device->fullname);
		do {
                    if ((fd = open (fullname, O_RDWR |
                        openflags, 0777)) == -1) {
			/*
			 *  Would not open for read and write.
			 *  Try just write.
			 */
                        if ((fd = open (fullname, O_WRONLY |
                                    openflags,0777)) == -1) {
			    if (i < Maxoperet) {
                                fprintf(stderr,retrymsg, device->name,
                                            fullname,i+1);
                                fflush(stderr);
				if (Opewai == 0) nqssleep (1);
				else nqssleep (Opewai);
			    }
			    i++;	/* Retry count */
			}
		    }
		} while (i <= Maxoperet && fd == -1);
		if (fd == -1) serexit (RCM_DEVOPEFAI, asciierrno ());
                if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
                    /*
                     * Make sure the user pays for the spooled
                     * disk space (it was created while running
                     * under root).
                     */
                    umask (cmask);
                    chown(fullname, passwd->pw_uid, passwd->pw_gid);
                }
		/*
		 *  Make sure that the device is open as stdout (fd#1).
		 */
		if (fd != 1) {
		    fcntl (fd, F_DUPFD, 1);
		    close (fd);
		}
		/*
		 *  We have the device open.
		 *  Get the name of the server and args to execve.
		 */
		server = device->server;
		break;
	case QUE_NET:
		/*
		 *  We are spawning a network queue request.
		 */
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "DEFAULT_RETRYTIME=%1ld", (long) Defnetrettim);
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "DEFAULT_RETRYWAIT=%1ld", (long) Defnetretwai);
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "ELAPSED_RETRYTIME=0");
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "OP=O");		/* Output file transaction */
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "EVENT=%1d", request->v1.subreq.event);
		envpsize += strlen (cp) + 1;
		server = queue->q.v1.network.server;
		break;				/* Get server/args to execve */
	case QUE_PIPE:
		/*
		 *  We are spawning a pipe queue request.
		 *  Get the server, and format the destination set
		 *  into the environment for the pipe queue server.
		 */
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "DEFAULT_RETRYTIME=%1ld", (long) Defdesrettim);
		envpsize += strlen (cp) + 1;
		cp = environment + envpsize;
		envp [envpcount++] = cp;
		sprintf (cp, "DEFAULT_RETRYWAIT=%1ld", (long) Defdesretwai);
		envpsize += strlen (cp) + 1;
		/*
		 * ================================================
		 * Build list of destinations
		 * ================================================
		 */
		pipe_dest_header.next = (struct pipe_dest *) NULL;
		pd_last = &pipe_dest_header;
		i = 0;				/* Destination# */
		mapdest = queue->v1.pipe.destset;
		while (mapdest != (struct qdestmap *) 0) {
		    pd_ptr = malloc ( sizeof( pipe_dest_header ));
		    if (pd_ptr == (struct pipe_dest *) NULL ) {
			fprintf(stderr, "F$Nqs_reqser: Cannot allocate memory!\n");
			fflush(stderr);
			serexit (RCM_INSUFFMEM, (char *) 0);
		    }
		    bytezero(pd_ptr, sizeof(pipe_dest_header) );
		    pd_ptr->status = mapdest->pipeto->status;
		    pd_ptr->retry_at = mapdest->pipeto->retry_at;
		    pd_ptr->retrytime = mapdest->pipeto->retrytime;
		    pd_ptr->retrydelta = mapdest->pipeto->retrydelta;
		    strcpy(pd_ptr->rqueue, mapdest->pipeto->rqueue);
		    pd_ptr->rhost_mid = mapdest->pipeto->rhost_mid;
		    pd_ptr->no_jobs = LOAD_NO_JOBS;
						/* Marker for no load information */
		    pd_ptr->rap = 1;		/* Default RAP */	
		    /*
		     * Find the load information record (if any)
		     * associated with this mid.
		     */
		    time(&now_now);		    /* What time is it now? */
		    load_ptr = Loadinfo_head.next;
		    while (load_ptr != (struct loadinfo *) 0) {
			if (Debug > 3) {
			    fprintf(stderr, "D$nqs_reqser: load mid %u pd mid %u.\n", 
				  load_ptr->mid, pd_ptr->rhost_mid);
			    fflush(stderr);   
			}
			if (load_ptr->mid == pd_ptr->rhost_mid) {
			    pd_ptr->rap = load_ptr->rap;
			    /*
			     * If the load record is less than 15 minutes
			     * old, use it,  o/w ignore.
			     */
			    if ( (now_now - load_ptr->time) < 15*60) {
				pd_ptr->time = load_ptr->time;
				pd_ptr->no_jobs = load_ptr->no_jobs;
				pd_ptr->load1 = load_ptr->load1;
				pd_ptr->load5 = load_ptr->load5;
				pd_ptr->load15 = load_ptr->load15;
			    }
			    break;
			}
			load_ptr = load_ptr->next;
		    }
		    mapdest = mapdest->next;    /* Ordered destinations */
		    pd_last->next = pd_ptr;     /* The previous last now points here */
		    pd_last = pd_ptr;		/* This is now the previous last */
		    i++;			/* One more destination */
		}
		number_of_dests = i;		/* Remember how many */
		if (Debug > 3) {
		    i = 0;
		    pd_ptr = pipe_dest_header.next;
		    while (pd_ptr != (struct pipe_dest *) NULL) {
			fprintf (stderr, "D$nqs_reqser: Dest %d mid %u jobs %d\n",
				  i, pd_ptr->rhost_mid,  pd_ptr->no_jobs);
			fflush(stderr);
			pd_ptr = pd_ptr->next;
			i++;
		    }
		}
		/*
		 * Now set up the proper order of destinations.
		 */
		for (i = 0; i < MAX_QDESTS; i++ ) {
		    pd_list[i] = (struct pipe_dest *) NULL;
		}
		if (queue->q.status & QUE_LDB_OUT) { 
		    /*
		     * Load balanced ... Set up round robin order.
		     */
		    persist = 1;			/* Unassuming request */
		    start_destination = rawreq->orig_seqno % number_of_dests;
		    pd_ptr = pipe_dest_header.next;
		    for ( i = 0; i < start_destination; i++) {
			pd_ptr = pd_ptr->next;
		    }
		    i = 0;
		    while (pd_ptr != (struct pipe_dest *) NULL ) {
			pd_list[i++] = pd_ptr;
			pd_ptr = pd_ptr->next;
		    }
		    pd_ptr = pipe_dest_header.next;
		    for ( j = 0; j < start_destination; j++) {
		        pd_list[i++] = pd_ptr;
			pd_ptr = pd_ptr->next;
		    }
		    /*
		     * Do any of the destinations have no jobs or do we
		     * have load information for all destinations?
		     */
		    empty_dests = 0;       /* Start with no empty dests */
		    loadinfo_dests = 0;    /* Count of dests with load info */
		    for ( i = 0; i < number_of_dests; i++) {
		        if ( pd_list[i]->no_jobs == 0) empty_dests++;
			if ( pd_list[i]->no_jobs != LOAD_NO_JOBS) 
					loadinfo_dests++;
			/*
			 * Calculate performance.  If there are no jobs, 
			 * the rap_per_job is 10 times the rap. O/W it is
			 * rap/no_jobs.
			 */
			if (pd_list[i]->no_jobs == 0) 
				pd_list[i]->rap_per_job = 10. * pd_list[i]->rap;
			else
			    pd_list[i]->rap_per_job = (float) pd_list[i]->rap /
					(float) pd_list[i]->no_jobs;
			if (pd_list[i]->load5 == 0.0) pd_list[i]->load5 = 0.001;
			pd_list[i]->rap_per_la = (float) pd_list[i]->rap /
					pd_list[i]->load5;
			if (Debug > 3) {
			    fprintf (stderr, "D$nqs_reqser: Dest %d mid %u jobs %d\n",
				  i, pd_list[i]->rhost_mid,  pd_list[i]->no_jobs);
			    fprintf (stderr, "D$nqs_reqser: rap-per-job %f rap-per-load %f\n",
				  pd_list[i]->rap_per_job,  pd_list[i]->rap_per_la);
			    fflush(stderr);
			}
		    }
		    if (empty_dests  || (loadinfo_dests == number_of_dests) ) {
			if (Debug > 3) {
			    fprintf(stderr, "D$nqs_reqser: Reordering dests.\n");
			    fflush(stdout);
			}
		        /*
			 * We have at least one empty destination -or- load
			 * information for all the destinations.  Order them
			 * based on rap per job and the rap per 5 min load 
			 * avgerage.
			 */
			for (i = 0; i < number_of_dests - 1; i++) {
			    for (j = number_of_dests - 1 ; i < j; --j) {
			       /*
			        * Sort first on rap per job.
			        */
			       if (pd_list[j-1]->rap_per_job < 
				         pd_list[j]->rap_per_job) {
				    fflush(stderr);
				    pd_ptr = pd_list[j];
				    pd_list[j] = pd_list[j-1];
				    pd_list[j-1] = pd_ptr;
			        } else if ( pd_list[j-1]->rap_per_job ==
			                pd_list[j]->rap_per_job) {
				    /*
				     * Sort second on rap per 5 min load 
				     * average.
				     */
				    if (pd_list[j-1]->rap_per_la <
				         pd_list[j]->rap_per_la ) {
					pd_ptr = pd_list[j];
					pd_list[j] = pd_list[j-1];
					pd_list[j-1] = pd_ptr;
				    }
					
				}
			     } 
			 }
		    }
		} else {
		    /*
		     * Not load balanced ... Just create the list.
		     */
		    persist = 0;
		    pd_ptr = pipe_dest_header.next;
		    for (i = 0; i < number_of_dests; i++) {
			pd_list[i] = pd_ptr;
			pd_ptr = pd_ptr->next;
		    }
		}
		/*
		 * So what does it look like?
		 */
		if (Debug > 2) {
		    for (i = 0; i < number_of_dests; i++) {
		        fprintf(stderr, "D$nqs_reqser: dest %d, mid %u.\n", 
				i, pd_list[i]->rhost_mid);
		    }
		    fflush(stderr);
		}
		timeval = time ((time_t *) 0);	/* Current time */
		/*
		 * Go through the list of destinations creating
		 * the environment variables which indicate the
		 * destinations to try (and the order).
		 */
		dest_number = 0;
		for (i = 0; i < number_of_dests; i++) {
		    if (pd_list[i]->status & DEST_ENABLED) {
			/*
			 *  The destination is enabled.
			 *  Add this destination to the
			 *  set of pipe queue destinations
			 *  in the environment.
			 *
			 *  Note that no explicit checks are
			 *  made here to see if we are running
			 *  off the end of the environment arrays!
			 *
			 *  We are depending on the #defines in
			 *  nqs.h to have been honestly defined.
			 */
			retry_time = 0;
			if ((pd_list[i]->status & DEST_RETRY) &&
			     timeval > pd_list[i]->retrytime) {
			    retry_time = (unsigned long) timeval
				- (unsigned long) pd_list[i]->retrytime;
			}
			cp = environment + envpsize;
			envp [envpcount++] = cp;
			sprintf (cp, "D%03d=%1lu %1lu %s", dest_number++,
				(long) pd_list[i]->rhost_mid,
				 retry_time, pd_list[i]->rqueue);
			envpsize += strlen (cp) + 1;
		     } else {
			/*
			 *  A destination exists that is presently disabled
			 *  for this pipe queue.  Since not all destinations
			 *  are therefore available to the pipe queue server
			 *  (pipeclient), the request should be allowed to
			 *  persist even if none of the enabled destinations
			 *  accept it, since it is not known that all possible
			 *  destinations for request have been tried.
			 */
			if (Debug > 2) {
			    fprintf(stderr, "D$nqs_reqser: mid %u disabled\n", 
				    pd_list[i]->rhost_mid);
			    fflush(stderr);
			}
			persist = 1;		/* Let the request persist */
		    }				/* ENDif enabled */
		}
		if (persist) {
		    /*
		     *  The request should be allowed to persist, even
		     *  if no destination accepts it this time.
		     */
		    cp = environment + envpsize;
		    envp [envpcount++] = cp;
		    strcpy (cp, "PERSIST=Y");
		    envpsize += strlen (cp) + 1;
		}
		/*
		 * Now free up memory.
		 */
		pd_ptr = pipe_dest_header.next;
		while (pd_ptr != (struct pipe_dest *) NULL) {
		    pd_curr = pd_ptr;
		    pd_ptr = pd_ptr->next;
		    free (pd_curr);
		}
		server = queue->q.v1.pipe.server;
	}					/* Get server/args to execve */
	/*
	 *  Place the NIL pointer at the end of the environment var
	 *  list for the server.
	 */
	envp [envpcount] = (char *) 0;
	/*
	 *  In ALL cases, the current working directory is the NQS
	 *  root directory, and the appropriate environment has been
	 *  constructed for the server.
	 */
	if (queue->q.type != QUE_BATCH) {
		/*
		 *  A device, network, or pipe queue request is
		 *  being spawned.
		 */
		if (parseserv (server, argv) == -1) {	/* Break into args */
			/*
			 *  Too many server arguments in server command line.
			 */
			serexit (RCM_2MANYSVARGS, (char *) 0);
		}
		server = argv [0];	/* Get ptr to program to execve */
		if (queue->q.type == QUE_DEVICE) cp = device->name;
		else if (queue->q.type == QUE_NET) {
			cp = fmtmidname (queue->q.namev.to_destination);
		}
		else cp = queue->q.namev.name;
		argv [0] = argv0buffer;
		sprintf (argv [0], "%s %s", cp, "server");
	}
	/*
	 *  Show debugging information if required.
	 */
	if (Debug) {
		/*
		 *  Display debug information.  Note that we have to use
		 *  stderr, since stdout is used for devices....
		 */
		fprintf (stderr, "D$Execve'ing server: %s.\n", server);
		fflush (stderr);
		if (Debug > 1) {
			i = 0;
			while (argv [i] != (char *) 0) {
				fprintf (stderr, "D$argv[%1d]=%s.\n", i,
					 argv [i]);
				fflush (stderr);
				i++;
			}
			i = 0;
			while (envp [i] != (char *) 0) {
				fprintf (stderr, "D$envp[%1d]=%s.\n", i,
					 envp [i]);
				fflush (stderr);
				i++;
			}
		}
	}
	/*
	 *  Send mail (and broadcast messages) as necessary notifying the 
	 *  user that their request is beginning or restarting execution.
	 */
	if (queue->q.type == QUE_BATCH || queue->q.type == QUE_DEVICE) {
		if (restartflag) {	/* Restarting execution */
			if (rawreq->flags & RQF_RESTARTMAIL) {
				mai_send (rawreq, (struct requestlog *) 0,
					  MSX_RESTARTING);
			}
		}
		else {			/* Beginning execution for the first */
			if (rawreq->flags & RQF_BEGINMAIL) {	/* time */
				mai_send (rawreq, (struct requestlog *) 0,
					  MSX_BEGINNING);
			}
			if (rawreq->flags & RQF_BEGINBCST) {
				nqs_broadcast (rawreq, (struct requestlog *) 0,
					  MSX_BEGINNING);
			}
		}
	}
        /*
         *  Set nice value for batch requests.
         */
        if (queue->q.type == QUE_BATCH) {
#if             (VALID_LIMITS & LIM_PPNICE)
#if             BSD43 | ULTRIX | DECOSF
                enf_bsdnic (rawreq->v.bat.ppnice);
#else
#if             HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
                enf_sy5nic (rawreq->v.bat.ppnice);
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
        }
	/*
	 *  Set ourselves in our very own process group/family so that
	 *  the NQS daemon can kill us (and our children) as a group if
	 *  necessary.
	 */
	jid = getpid();
	setsid();
	if (queue->q.type == QUE_BATCH) {
		/*
		 *  We are spawning a batch request.
		 *  Enforce resource limits on the process or process group
		 *  while we're still root.
		 */
#if		(VALID_LIMITS & LIM_PPCORE)
#if		HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
		enf_bsdquo (&rawreq->v.bat.ppcoresize,
			    rawreq->v.bat.infinite & LIM_PPCORE, LIM_PPCORE);
#else
#if		SYS52 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

#if		(VALID_LIMITS & LIM_PPDATA)
#if		HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
		enf_bsdquo (&rawreq->v.bat.ppdatasize,
			    rawreq->v.bat.infinite & LIM_PPDATA, LIM_PPDATA);
#else
#if		SYS52 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

#if		(VALID_LIMITS & LIM_PPPFILE)
#if		BSD43 | ULTRIX | DECOSF
		enf_bsdquo (&rawreq->v.bat.pppfilesize,
			    rawreq->v.bat.infinite & LIM_PPPFILE, LIM_PPPFILE);
#else
#if		HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		enf_sy5quo (&rawreq->v.bat.pppfilesize,
			    rawreq->v.bat.infinite & LIM_PPPFILE, LIM_PPPFILE);
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

#if		(VALID_LIMITS & LIM_PPSTACK)
#if		HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
		enf_bsdquo (&rawreq->v.bat.ppstacksize,
			    rawreq->v.bat.infinite & LIM_PPSTACK, LIM_PPSTACK);
#else
#if		SYS52
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

#if		(VALID_LIMITS & LIM_PPWORK)
#if		HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
		enf_bsdquo (&rawreq->v.bat.ppworkset,
			    rawreq->v.bat.infinite & LIM_PPWORK, LIM_PPWORK);
#else
#if		SYS52 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

#if		(VALID_LIMITS & LIM_PPCPUT)
#if		HPUX | SGI | SOLARIS | BSD43 | ULTRIX | DECOSF | IBMRS | LINUX
		enf_bsdcpu (&rawreq->v.bat.ppcputime,
			    rawreq->v.bat.infinite & LIM_PPCPUT, LIM_PPCPUT);
#else
#if		SYS52 
#else
BAD SYSTEM TYPE
#endif
#endif
#endif

        }

        if (queue->q.type == QUE_BATCH) {
                /*
                *  Write an entry in an NQS accounting file.
                */
                fd_acct = open(NQSACCT_FILE,
                        O_WRONLY|O_APPEND|O_CREAT, 0644);
                if (fd_acct < 0) {
                        fprintf (stderr, "E$Error opening NQS account");
                        fprintf (stderr, " file;  Errno = %d\n", errno);
                        fflush (stderr);
                } else {
                        bytezero((char *)&acct_init1, sizeof(acct_init1));
                        acct_init1.h.type = NQSACCT_INIT_1;
                        acct_init1.h.length = sizeof(acct_init1);
                        acct_init1.h.jobid = jid;
                        strncpy(acct_init1.user, rawreq->username,
                                sizeof(acct_init1.user));
                        strncpy(acct_init1.queue, rawreq->quename,
                                sizeof(acct_init1.queue));
                        acct_init1.priority = queue->q.priority;
                        acct_init1.sub_time = rawreq->create_time;
                        acct_init1.start_time = rawreq->start_time;
                        acct_init1.init_time = time ((time_t *) 0);
                        acct_init1.orig_mid = rawreq->orig_mid;
			acct_init1.seqno = rawreq->orig_seqno;
                        strncpy(acct_init1.reqname, rawreq->reqname,
                                sizeof(acct_init1.reqname));
                        write(fd_acct, (char *)&acct_init1, sizeof(acct_init1));
			close (fd_acct);
                }
		localmid (&mymid);
		
        }

	if (queue->q.type == QUE_BATCH || queue->q.type == QUE_DEVICE) {
		/* 
		 * For SGI set priority down low out of way of interactives
		 * if set for queue.
		 */
#if	SGI
		if (queue->q.ndp != 0) {
			schedctl(NDPRI, 0, (int) queue->q.ndp);
		}
#endif
		/*
		 *  Batch and device requests must now set their real and
		 *  effective user and group-ids to that of the request
		 *  owner.
		 *
		 *  Network queue and pipe queue servers must NOT do this.
		 *  Network queue and pipe queue servers must be spawned
		 *  as root.
		 */
		initgroups (passwd->pw_name, passwd->pw_gid);
		ngroups = getgroups (NGROUPS_MAX, gidset);
		setgroups (ngroups, gidset);
		setgid ((int) passwd->pw_gid);	/* Set login group-id */
		setuid ((int) passwd->pw_uid);	/* Set mapped user-id */
	}
	/*
	 *  Explicitly set the signal receipt actions to their defaults so
	 *  that the server gets a completely "clean" signal environment.
	 *  (Note that for requests running in batch queues, every signal is
	 *  set to default except for SIGTERM, which is set to SIG_IGN.)
	 */
	signal (SIGHUP, SIG_DFL);
	signal (SIGINT, SIG_DFL);
	signal (SIGQUIT, SIG_DFL);
	signal (SIGILL, SIG_DFL);
	signal (SIGTRAP, SIG_DFL);
	signal (SIGIOT, SIG_DFL);
#if !LINUX
	signal (SIGEMT, SIG_DFL);
#endif
	signal (SIGFPE, SIG_DFL);
	signal (SIGBUS, SIG_DFL);
	signal (SIGSEGV, SIG_DFL);
	signal (SIGSYS, SIG_DFL);
	signal (SIGPIPE, SIG_DFL);
	signal (SIGALRM, SIG_DFL);
	if (queue->q.type == QUE_BATCH) signal (SIGTERM, SIG_IGN);
	else signal (SIGTERM, SIG_DFL);
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	signal (SIGUSR1, SIG_DFL);
	signal (SIGUSR2, SIG_DFL);
	signal (SIGCLD, SIG_DFL);
	signal (SIGPWR, SIG_DFL);
#else
#if	BSD43 | ULTRIX | DECOSF
	signal (SIGURG, SIG_DFL);
	signal (SIGSTOP, SIG_DFL);
	signal (SIGTSTP, SIG_DFL);
	signal (SIGCONT, SIG_DFL);
	signal (SIGCHLD, SIG_DFL);
	signal (SIGTTIN, SIG_DFL);
	signal (SIGTTOU, SIG_DFL);
	signal (SIGIO, SIG_DFL);
	signal (SIGXCPU, SIG_DFL);
	signal (SIGXFSZ, SIG_DFL);
	signal (SIGVTALRM, SIG_DFL);
	signal (SIGPROF, SIG_DFL);
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  ***TRIAGE***
	 *
	 *  Extreme schedule constraints have forced us to leave
	 *  network queues unimplemented (software implementation
	 *  by triage).  When the day arrives that network queues
	 *  are a reality, then the obvious code section below
	 *  should be changed to read:
	 */  
	 	/*
	 	 *  Report our "process-family" to the NQS daemon.
	 	 */
		/*   interclear();		      */
		/*   interw32i ((long) getppid()); */ /* Shepherd process-id */
		/*   interw32i ((long) getpid());  */ /* Our process-id */
		/*   inter (PKT_FAMILY);	      */
	/*
	 *  WITHOUT the surrounding "if" statement that PREVENTS
	 *  network queue requests from sending PKT_FAMILY packets
	 *  to the local NQS daemon.
	 */
	if (queue->q.type != QUE_NET) {		/* THIS IF STATEMENT SHOULD */
						/* EVENTUALLY BE REMOVED */
						/* (SEE ABOVE COMMENTS) */
		/*
		 *  Report our "process-family" to the NQS daemon.
		 */
		interclear();
		interw32i ((long) getppid());	/* Shepherd process-id */
		interw32i ((long) jid);		/* Our process-id */
		inter (PKT_FAMILY);
	}					/* (SEE ABOVE COMMENTS) */
	/*
	 *  Batch and device requests are not allowed to retain their
	 *  connection to the NQS daemon event/request pipe.  Only pipe
	 *  and network queue servers need to be able to talk directly to
	 *  the NQS daemon.
	 */
	if (queue->q.type == QUE_BATCH || queue->q.type == QUE_DEVICE) {
		close (Write_fifo);
	}
	/*
	 *  Batch requests are handled differently....
	 */
	if (queue->q.type == QUE_BATCH) {
		/*
		 *  Change directory to the user's home directory.
		 */
		if (chdir (passwd->pw_dir) == -1) {
			/*
			 *  We were unable to chdir() to the user's
			 *  home directory.
			 */
			serexit (RCM_UNABLETOEXE,
				 "Unable to chdir() to user home directory.");
		}
		/*
		 *  Set the file creation mask to the value that it
		 *  had when the user first submitted the request, with
		 *  exception that the umask will NOT be allowed to
		 *  disable WRITE access for the request owner.
		 *
		 *  We do this so that any stdout or stderr output file
		 *  created at this point, will be created such that it
		 *  will be writeable by the request owner.  This is
		 *  required if the request is to be restartable, and
		 *  a stdout or stderr file exists from a previously
		 *  interrupted execution of the batch request.
		 *
		 *  The umask will be set precisely later on.
		 */
		umask (rawreq->v.bat.umask & 0577);	/* Set partial umask */
		if ((rawreq->v.bat.stdout_acc & OMD_SPOOL) == 0) {
			/*
			 *  The standard-output file is to be accessed
			 *  directly.  We had to wait until after the chdir()
			 *  because the pathname could be relative.
			 */
			while ((standout = open (rawreq->v.bat.stdout_name,
				O_WRONLY | filemask | O_APPEND, 0777)) == -1 &&
				errno == EINTR)
				;
			if (standout == -1) {
				/*
				 *  Unable to create standard output file.
				 */
				if (errno == ENFILE) {
					/*
					 *  The system file table is full.
					 */
					serexit (RCM_ENFILERUN, (char *) 0);
				}
				if (errno == ENOSPC) {
					/*
					 *  Insufficient space.
					 */
					serexit (RCM_ENOSPCRUN, (char *) 0);
				}
				serexit (mergertcm (RCM_UNCRESTDOUT,
						    errnototcm()), (char *) 0);
			}
		}
		if ((rawreq->v.bat.stderr_acc & OMD_SPOOL) == 0 &&
		    (rawreq->v.bat.stderr_acc & OMD_EO) == 0) {
			/*
			 *  The standard-error file is to be accessed
			 *  directly.  We had to wait until after the
			 *  chdir() because the pathname may have been
			 *  specified as a relative pathname.
			 */
			while ((standerr = open (rawreq->v.bat.stderr_name,
				O_WRONLY | filemask | O_APPEND, 0777)) == -1 &&
				errno == EINTR)
				;
			if (standerr == -1) {
				/*
				 *  Unable to create standard error file.
				 */
				if (errno == ENFILE) {
					/*
					 *  The system file table is full.
					 */
					serexit (RCM_ENFILERUN, (char *) 0);
				}
				if (errno == ENOSPC) {
					/*
					 *  Insufficient space.
					 */
					serexit (RCM_ENOSPCRUN, (char *) 0);
				}
				serexit (mergertcm (RCM_UNCRESTDERR,
						    errnototcm()), (char *) 0);
			}
		}
		else if (rawreq->v.bat.stderr_acc & OMD_EO) {	/* -eo */
			/*
			 *  The stderr output of the request is to be directed
			 *  to the stdout file.
			 */
			standerr = fcntl (standout, F_DUPFD, 0);
			fcntl (standerr, F_SETFL, O_APPEND);
		}
		/*
		 *  Set the file creation mask to the value that it
		 *  had when the user first submitted the request.
		 */
		umask (rawreq->v.bat.umask);	/* Set precise umask */
		if (restartflag) {
			/*
			 *  This batch request is being restarted.
			 *  Write an appropriate message on the stdout
			 *  file indicating this fact.
			 */
			time (&timeval);	/* Get current time */
			sprintf (restart_msg,
			       "\n\n%%NQS(WARN): Request restarted at %s.\n\n",
				fmttime (&timeval));
			write (standout, restart_msg, strlen (restart_msg));
			if ((rawreq->v.bat.stderr_acc & OMD_EO) == 0) {
				/*
				 *  Write an appropriate message on the
				 *  stderr file as well.
				 */
				write (standerr, restart_msg,
				       strlen (restart_msg));
			}
		}
		/*
		 *  Mark the request as executing, if the request is
		 *  being executed for the first time.
		 *
		 *  WARNING:  We must do this now, BEFORE rearranging
		 *	      the file descriptor associations for the
		 *	      shell (see below).  Otherwise, file
		 *	      descriptor #3 will no longer be open
		 *	      as the request message/completion pipe
		 *	      back to the shepherd process.
		 */
		serexecute ();
		/*
		 *  Force the shell script file to be on file descriptor
		 *  #0.  Force the stdout and stderr files to be on file
	 	 *  descriptors #1 and #2 respectively.
		 */
		if (script != 0) {
			close (0);
			fcntl (script, F_DUPFD, 0);
			close (script);
		}
		if (standout != 1) {
			close (1);
			fcntl (standout, F_DUPFD, 1);
			close (standout);
		}
		if (standerr != 2) {
			close (2);
			fcntl (standerr, F_DUPFD, 2);
			close (standerr);
		}
		/*
		 *  Close all remaining file descriptors.
		 */
		i = sysconf ( _SC_OPEN_MAX );	/* #of file descrs per proc */
		while (--i >= 3) close (i);	/* Close all other descrs */
		/*
		 *  Exec the proper shell.
		 */
		execve (server, argv, envp);
		/*
		 *  Fall through if execve() fails.
		 *
		 *  We have severed all connections to the shepherd
		 *  process that created us.  We must invoke a setuid
		 *  root program (shlexefai), to signal the shepherd
		 *  that the execve() of the shell, failed.
		 */
                filename = getfilnam ("shlexefai", LIBDIR);
                if (filename != (char *)NULL) {
                        /*
                         * getfilnam allocates memory for filename.  Copy
                         * filename into local buffer and free memory.
                         */
                        strcpy (buffer, filename);
                        relfilnam (filename);
                        /*
                         *  Format errno as argv [1] for shlexefai.
                         */
                        strcpy (argv0buffer, "shlexefai");
                        sprintf (minusname, "%1d", errno);
                        /*
                         *  Write shell execve() diagnostic on the stderr
                         *  file of the batch request.
                         */
                        fprintf (stderr,
                                 "FATAL:  Execve() of shell: %s failed.\n",
                                 server);
                        fprintf (stderr, "INFO:   %s.\n", asciierrno());
                        fflush (stdout);
                        fflush (stderr);
                        /*
                         *  Execve() shlexefai to inform the shepherd about
                         *  the shell execve() failure.
                         */
                        argv [0] = argv0buffer;
                        argv [1] = minusname;
                        argv [2] = (char *) 0;
                        envp [0] = (char *) 0;
                        execve (buffer, argv, envp);
                }
		/*
		 *  We are really having a bad day!  Nothing works!
		 *  Commit seppuku.  We cannot even display a diagnostic
		 *  to the NQS log file because our connection with the
		 *  NQS log process has been severed.
		 */
		kill (getpid(), SIGKILL);
	}
	/*
	 *  If the request is a device request, then the request must
	 *  be marked as executing.
	 */
	if (queue->q.type == QUE_DEVICE ) serexecute ();
	/*
	 *  We are spawning a device, network, or pipe queue request.
	 *  Execve() the appropriate server.
	 */
	execve (server, argv, envp);
	/*
	 *  Execve() failed.
	 */
	serexit (RCM_SEREXEFAI, asciierrno());
}


/*** mkspool
 *
 *
 *	int mkspool():
 *
 *	Make a standard error or standard output file for a batch
 *	request in the NQS output spooling directory.
 *
 *	WARNING!!!!!!!!!!!!!!!!
 *		The EXTREMELY serious student will note that this
 *		function is invoked while the calling shell process
 *		is still running as root.
 *
 *		This is done because someday, NQS may be linked
 *		with the Newcastle distributed file access library.
 *		Since the Newcastle library converts ALL file paths
 *		to ABSOLUTE paths on the appropriate machine when
 *		invoking the requisite system call, NQS must still
 *		be running as root to safely create the spool output
 *		files in the spooling directory, since the spooling
 *		directory resides BELOW the NQS private directory,
 *		which only allows root access.
 *
 *	Returns:
 *	      >=0: if successful, as the opened file descriptor
 *		   for the stderr file;
 *	       -1: if an error occurs in which case errno is set.
 */
static int mkspool (rawreq, namefn, passwd, filemask)
struct rawreq *rawreq;			/* Rawreq structure for request */
char *(*namefn)();			/* Ptr to fn returning ptr to char */
struct passwd *passwd;			/* Password file entry for owner */
int filemask;				/* File create/truncate flag */
{
	register int fd;		/* File descriptor */
	register char *path;		/* Ptr to temp file path */
	register int cmask;		/* umask */

	/*
	 *  Generate name of temporary spooled output file.
	 */
	path = (*namefn) (rawreq->orig_seqno, rawreq->orig_mid);
	/*
	 *  Set the file creation mask to the value that it
	 *  had when the user first submitted the request, with
	 *  the exception that the umask will NOT be allowed to
	 *  disable WRITE access for the request owner.
	 *
	 *  We do this so that the output return algorithm on
	 *  retry will not have to worry about being rebuffed
	 *  at the output file destination by a lack of write-
	 *  access.  (Note that the output return algorithm
	 *  replicates the permissions of the original file
	 *  for the destination file.)
	 */
	cmask = umask (rawreq->v.bat.umask & 0577);	/* Set partial umask */
	if ((fd = open (path, filemask | O_WRONLY | O_APPEND, 0777)) == -1) {
		return (-1);
	}
	umask (cmask);			/* Restore the file create mask */
	/*
	 *  Make sure that the user pays for the spooled disk space.
	 *  (The spool file was created while we were running as root).
	 */
	chown (path, passwd->pw_uid, passwd->pw_gid);
	return (fd);
}


/*** seracknowledge
 *
 *
 *	void seracknowledge():
 *
 *	Catch SIGPIPE request state set to executing acknowledgement
 *	from the server shepherd process.
 */
static void seracknowledge()
{
	Stateset = 1;			/* Request state has been set */
}

 
/*** serexecute
 *
 *
 *	void serexecute():
 *
 *	Tell the shepherd process to set the request state to
 *	executing.
 */
static void serexecute ()
{
	void (*prevsigfn)();		/* Previous signal function */
	struct servermssg msg_packet;	/* Message packet */

	msg_packet.rcm = RCM_EXECUTING;
        msg_packet.id = jid;
	msg_packet.mssg [0] = '\0';
	/*
	 *  Set signal handler to catch acknowledge signal from the
	 *  server shepherd process.
	 */
	Stateset = 0;			/* Clear semaphore */
	prevsigfn = signal (SIGPIPE, seracknowledge);
	/*
	 *  WARNING:	The file descriptor below (#3) must agree with
	 *		../src/nqs_spawn and ../lib/serexit.c.
	 */
	if (write (3, (char *) &msg_packet, sizeof (msg_packet)) !=
		sizeof (msg_packet)) {
		/*
		 * We cannot communicate with the shepherd through
		 * the pipe. Exit so the shepherd's read() will return.
		 */
		exit (0);
	}
	/*
	 *  Loop waiting for acknowlege signal from shepherd process.
	 */
	while (!Stateset) pause();	/* Wait for signal */
	signal (SIGPIPE, prevsigfn);	/* Restore SIGPIPE handler */
}
/*
 * This routine determines the path.  It first checks a file in /etc called
 * environment.  This file is present on AIX,  and can be added to other
 * systems.  The routine checks the lines of the file for a line starting
 * with "PATH=" and if found, uses the rest of the line as the path (without
 * checking it!).  Otherwise,  it uses guess for the proper path.
 */
static char *get_the_path(passwd)
struct passwd    *passwd;
{
    static char    the_path[MAX_PATHNAME+1];
    FILE    *environment_file;
    char    env_line[128];
    char    *cp;
    
    environment_file = fopen("/etc/environment",  "r");
    if ( environment_file != (FILE *) 0 ) {
	while ( (fgets (env_line, sizeof(env_line), environment_file )
		!= (char *) 0) ) {
	    if (env_line[0] == '#') continue;
	    cp = strchr( env_line,  '\n');
            if (cp != (char *) 0 ) *cp = '\0';
	    if ( !strncmp(env_line,  "PATH=",  5) ) {
		 strcpy(the_path,  env_line);
		 fclose ( environment_file );
		 return (the_path); 
	    }   
	}
    }
    fclose ( environment_file );
    /*
     * The file was not found, or the path was not in it.
     */
    if (passwd->pw_uid == 0) {
	/*
	 *  We are running as root.  The default path
	 *  is slightly different.
	 */
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	strcpy (the_path,  "PATH=/bin:/etc:/usr/bin");
     } else {
	/*
	 *  The request is not owned by root.
	 */
	strcpy (the_path,  "PATH=:/bin:/usr/bin");
#else
#if	BSD43 | ULTRIX | DECOSF
	strcpy (the_path, "PATH=/usr/ucb:/bin:/etc:/usr/bin");
     } else {
	/*
	 *  The request is not owned by root.
	 */
	strcpy (the_path, "PATH=:/usr/ucb:/bin:/usr/bin");
#else
BAD SYSTEM TYPE
#endif
#endif
    }
    return (the_path);
}
