/*
 *	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.3   JRR     12-Feb-1992	Fixed static declaration of 
*					set_default_quota.
*       V01.4   JRR     28-Feb-1992	Added Cosmic V2 changes.
*	V01.5	JRR	17-Jun-1992	Added header. 
*	V01.6	JRR	02-Sep-1992	Default is not restartable.
*	V01.7	JRR	23-Dec-1992	Change to use getcwd.
*					Change index=>strchr.
*	V01.8	JRR	05-Mar-1993	Added Boeing enhancement for files.
*	V01.9	JRR	18-Aug-1993	Miniscule change to includes.
*	V01.10	JRR	28-Feb-1994	Added support for SOLARIS.
*/
/*++ mkreq.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/lib/RCS/mkreq.c,v $
 *
 * DESCRIPTION:
 *
 *	This module contains 4 functions which respectively:
 *
 *	  o Create a request control file (mkctrl),
 *	  o Create a request data file (mkdata),
 *	  o Unlink all files associated with a request from the
 *	    NQS new request staging directory (zapreq), and
 *	  o Submit a request to the NQS queueing system, (quereq).
 *
 *	for LOCALLY submitted requests ONLY.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.10 $ $Date: 1994/03/30 20:32:37 $ $State: Exp $)
 * $Log: mkreq.c,v $
 * Revision 1.10  1994/03/30  20:32:37  jrroma
 * Version 3.35.6
 *
 * Revision 1.9  93/09/10  13:55:16  jrroma
 * Version 3.35
 * 
 * Revision 1.8  93/07/13  21:31:29  jrroma
 * Version 3.34
 * 
 * Revision 1.7  93/02/05  23:13:17  jrroma
 * Version 3.31
 * 
 * Revision 1.6  92/12/22  15:46:14  jrroma
 * Version 3.30
 * 
 * Revision 1.5  92/06/18  13:24:13  jrroma
 * Added gnu header
 * 
 * Revision 1.4  92/02/28  15:07:49  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.3  92/02/12  12:53:54  jrroma
 * Fixed static declaration of set_default_quota
 * 
 * Revision 1.2  92/01/16  16:51:28  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/16  16:50:04  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"		/* NQS constants and data types */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>		/* Network database types */
#include <signal.h>		/* Signal definitions */
#include "nqspacket.h"		/* NQS local message packets */
#include "nqsxdirs.h"		/* NQS directories */
#include "informcc.h"		/* NQS information completion codes and masks */
#include "transactcc.h"		/* NQS transaction completion codes */
#include "mkreqcc.h"		/* Make request completion codes */


#ifndef __CEXTRACT__
#if __STDC__

static void set_default_quota ( struct quotalimit *quotafield );

#else /* __STDC__ */

static void set_default_quota (/* struct quotalimit *quotafield */);

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

/*
 *	Variables global to all functions within this module.
 */
static char ctrlname [26+1];	/* Synthesized pathname for control file */
				/* (11 character control-file name + */
				/*  14 character directory name + */
				/*   1 character for "/") */
static short n_datafiles = 0;	/* Number of data files for request. */
static int ctrlfile = -1;	/* Control file file-descriptor */

/*** mkctrl
 *
 *
 *	FILE *mkctrl():
 *
 *	WARNING/NOTE:
 *		The effective user-id of the caller MUST
 *		be the owner user-id of the NQS directory
 *		hierarchy, or root.
 *
 *	This function creates a control file in the NQS local
 *	new request directory opened for reading AND writing by
 *	the caller, and initializes an associated raw request
 *	structure.
 *
 *	The original caller's working directory is returned
 *	by this function in the parameter:  workdir, and the
 *	caller's working directory is changed to the NQS new
 *	(not local new) request directory.
 *
 *	The file creation mask (umask), of the calling process
 *	is set to zero.
 *
 *	Lastly, the effective user-id and group-id of the caller
 *	are set to their real values by this function.
 *
 *	Returns:
 *		MKREQ_SUCCESS	  Control file successfully created.
 *		MKREQ_NOCREATE	  Unable to open/create control file.
 *		MKREQ_NOMID	  Unable to get machine-id of local host.
 *		MKREQ_BADREQTYPE  Bad request type specified.
 *		MKREQ_NOUSERNAME  Could not get username for user-id.
 *		MKREQ_NOCWD	  Unable to get current working directory.
 *		MKREQ_CWDNEWLINE  Current working directory contained
 *				  a new line character.
 *		MKREQ_NOCHDIRNEW  Unable to chdir() to the NQS new request
 *				  directory.
 *		MKREQ_NOSETUGID	  Unable to set [uid,gid].
 *		MKREQ_NOPARMFILE  Unable to open NQS parameters file.
 *              MKREQ_INVALIDACC  Invalid account ID.
 *
 */
int mkctrl (rawreq, reqtype, ptrfile, workdir, local_mid)
register struct rawreq *rawreq;	/* Pointer to raw request structure. */
int reqtype;			/* Request type: RTYPE_BATCH, RTYPE_DEVICE */
FILE **ptrfile;			/* Ptr to FILE structure for data file */
char *workdir;			/* Ptr to where to return ptr to the */
				/* caller's original working directory */
Mid_t *local_mid;		/* Machine-id of local host */
{

	char path [MAX_PATHNAME+1];
	struct confd *paramfile;/* NQS general parameters file */
	struct gendescr *descr;	/* Database descriptor */
	struct passwd *passwd;	/* Password entry for caller */
	char *userqueue;	/* User-defined destination queue */
	register int i;		/* Loop var */
	char *root_dir;
	char *req_dir;

	*ptrfile = (FILE *) 0;			/* No file yet */
	if ( (getcwd(workdir, MAX_PATHNAME)) == (char *) 0) {
		/*
		 *  Unable to determine the caller's working directory.
		 */
		return (MKREQ_NOCWD);
	}
	if (strchr (workdir, '\n') != (char *) 0) {
		/*
		 *  A newline was located in the current working directory.
		 */
		return (MKREQ_CWDNEWLINE);
	}
        req_dir = getfilnam (Nqs_requests, SPOOLDIR);
        if (req_dir == (char *)NULL) return (MKREQ_NOCHDIRNEW);
	if (chdir (req_dir) == -1) {
		/*
		 *  Unable to chdir() to the NQS new request directory.
		 */
		return (MKREQ_NOCHDIRNEW);
	}
        relfilnam (req_dir);	
	/*
	 *  The caller would like us to initialize the request
	 *  structure.
	 */
	if (nmap_get_mid ((struct hostent *) 0, local_mid) != NMAP_SUCCESS) {
		/*
		 *  We could not get the machine-id of the local
		 *  machine.
		 */
		return (MKREQ_NOMID);
	}
	if (reqtype != RTYPE_BATCH && reqtype != RTYPE_DEVICE) {
		/*
		 *  Invalid request type.
		 */
		return (MKREQ_BADREQTYPE);
	}
	passwd = fetchpwuid ((int) getuid());
	closepwdb();				/* Close account/password */
						/* database */
	if (passwd == (struct passwd *) 0) {
		/*
		 *  Could not get username from effective
		 *  user-id.
		 */
		return (MKREQ_NOUSERNAME);
	}
	/*
	 *  Open the NQS general parameters file.
	 */
        root_dir = getfilnam (Nqs_root, SPOOLDIR);
        if (root_dir == (char *)NULL)  return (MKREQ_NOCHDIRNEW);
        sprintf (path, "%s/%s", root_dir, Nqs_params);
        relfilnam (root_dir);
	if ((paramfile = opendb (path, O_RDONLY)) == (struct confd *) 0) {
		/*
		 *  Unable to open the NQS general parameters file.
		 */
		return (MKREQ_NOPARMFILE);
	}
	/*
	 * Get a fifo pipe to the local daemon.
	 * (On systems with named pipes, we do this automatically
	 * inside inter().)
	 */
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
#else
#if	BSD43
	if (interconn () < 0) {
		return (MKREQ_NOLOCALDAE);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  We must now give up our set[uid,gid] privileges because the
	 *  control file MUST be created with the correct user and group
	 *  id of the caller.
	 */
	if (setuid (getuid()) == -1 || setgid (getgid()) == -1) {
		/*
		 *  Unable to set[uid,gid] to real values.
		 */
		return (MKREQ_NOSETUGID);
	}
	rawreq->magic1 = REQ_MAGIC1;		/* Request magic# */
	rawreq->trans_id = 0;			/* No transaction descriptor */
	rawreq->reserved1 = 0;			/* Set RESERVED fields to */
	rawreq->reserved2 = 0;			/* zero (0) */
	rawreq->reserved3 = 0;
	rawreq->reserved4 = 0;
	rawreq->reserved5 = 0;
	rawreq->reserved6 = 0;
	strncpy (rawreq->trans_quename, "", MAX_QUEUENAME+1);
						/* No transport queue */
						/* (set ALL zero */
	time (&rawreq->create_time);		/* Time at which req */
						/* was created */
	rawreq->enter_time=rawreq->create_time;	/* Time at which req */
						/* entered the queue */
	strncpy (rawreq->quename, "", MAX_QUEUENAME+1);
						/* Containing queue is */
						/* unknown (set ALL zero) */
	rawreq->type = reqtype;			/* Set req type */
	rawreq->orig_uid = getuid();		/* Real user-id */
	rawreq->orig_mid = *local_mid;		/* Machine-id of owner */
	rawreq->tcm = TCML_UNDEFINED;		/* No TCM yet assigned */
	rawreq->orig_seqno = -1;		/* No seq# yet assigned */
	rawreq->rpriority = -1;			/* Let NQS determine */
						/* priority by default */
        /* rawreq->flags = RQF_RECOVERABLE;*/   /* By default, request */
                                                /* is restartable */
	rawreq->flags = 0;
	/* rawreq->flags = RQF_RESTARTABLE; */	/* By default, request */
						/* is NOT restartable */
	rawreq->start_time = 0;			/* As soon as possible */
	rawreq->ndatafiles = 0;			/* Number of data files=0 */
	rawreq->reqname [0] = '\0';		/* No reqname yet */
	strncpy (rawreq->username, passwd->pw_name, MAX_ACCOUNTNAME+1);
						/* Preserve user name */
	strncpy (rawreq->mail_name, passwd->pw_name, MAX_ACCOUNTNAME+1);
						/* and for sending mail */
	rawreq->mail_mid = *local_mid;		/* Machine-id for mail */
	if (reqtype == RTYPE_DEVICE) {
		/*
		 *  The request is going to be a device oriented request.
		 */
		if ((userqueue = getenv ("QPR_QUEUE")) == (char *) 0) {
			/*
			 *  No user defined print queue exists.
			 *  Scan the NQS general parameters file for the
			 *  default print-queue name.
			 */
			descr = nextdb (paramfile);
			while (descr != (struct gendescr *)0) {
				if (descr->v.par.paramtype == PRM_GENPARAMS) {
				    strncpy (rawreq->quename,
					    descr->v.par.v.genparams.defprique,
					    MAX_QUEUENAME+1);
					break;
				}
				descr = nextdb (paramfile);
			}
		}
		else {
			/*
			 *  The user has defined a default queue in their
			 *  environment appropriate for the request type.
			 */
			strncpy (rawreq->quename, userqueue, MAX_QUEUENAME+1);
		}
		strncpy (rawreq->v.dev.forms, "", MAX_FORMNAME+1);
						/* No forms; set all zero */
		rawreq->v.dev.copies = 1;	/* Default 1 copy */
		rawreq->v.dev.reserved1 = 0;	/* These MUST be zero */
		rawreq->v.dev.reserved2 = 0;	/* at present */
		for (i = 0; i < MAX_DEVPREF; i++) {
			strncpy (rawreq->v.dev.devprefname [i], "",
				 MAX_DEVNAME+1);
			rawreq->v.dev.devprefmid [i] = *local_mid;
		}
		rawreq->v.dev.size = 0;		/* Print size unknown */
	}
	else {
		/*
		 *  The request is going to be a batch request.
		 */
		if ((userqueue = getenv ("QSUB_QUEUE")) == (char *) 0) {
			/*
			 *  No user defined batch queue exists.
			 *  Scan the NQS general parameters file for the
			 *  default batch-queue name.
			 */
			descr = nextdb (paramfile);
			while (descr != (struct gendescr *)0) {
				if (descr->v.par.paramtype == PRM_GENPARAMS) {
				    strncpy (rawreq->quename,
					    descr->v.par.v.genparams.defbatque,
					    MAX_QUEUENAME+1);
					break;
				}
				descr = nextdb (paramfile);
			}
		}
		else {
			/*
			 *  The user has defined a default queue in their
			 *  environment appropriate for the request type.
			 */
			strncpy (rawreq->quename, userqueue, MAX_QUEUENAME+1);
		}
		/*
		 *  Get/set umask.
		 *
		 *  WARNING:  It is critical that the file-creation mask
		 *	      be set to zero so that the 744 protections
		 *	      of the control file, and the 500 protections
		 *	      on any data files are exactly what we say
		 *	      they should be.
		 */
		rawreq->v.bat.umask = umask (0);
		strncpy (rawreq->v.bat.shell_name, "", MAX_SHELLNAME+1);
						/* No shell specified; set */
						/* all zero */
		/*
		 *  Below, set default so Qsub only has to turn bits on,
		 *  not off.
		 */
		rawreq->v.bat.explicit = 0;
		rawreq->v.bat.infinite = LIM_PPCORE | LIM_PPDATA | LIM_PPPFILE |
			LIM_PRPFILE | LIM_PPQFILE | LIM_PRQFILE | LIM_PPTFILE |
			LIM_PRTFILE | LIM_PPMEM | LIM_PRMEM | LIM_PPSTACK |
			LIM_PPWORK | LIM_PPCPUT | LIM_PRCPUT | LIM_PPNICE |
			LIM_PRDRIVES | LIM_PRNCPUS;
		/*
		 *  It is necessary to place values into the limits
		 *  fields that will not cause an error when read
		 *  by ../lib/readreq.c.
		 */
		set_default_quota (&rawreq->v.bat.ppcoresize);
		set_default_quota (&rawreq->v.bat.ppdatasize);
		set_default_quota (&rawreq->v.bat.pppfilesize);
		set_default_quota (&rawreq->v.bat.prpfilespace);
		set_default_quota (&rawreq->v.bat.ppqfilesize);
		set_default_quota (&rawreq->v.bat.prqfilespace);
		set_default_quota (&rawreq->v.bat.pptfilesize);
		set_default_quota (&rawreq->v.bat.prtfilespace);
		set_default_quota (&rawreq->v.bat.ppmemsize);
		set_default_quota (&rawreq->v.bat.prmemsize);
		set_default_quota (&rawreq->v.bat.ppstacksize);
		set_default_quota (&rawreq->v.bat.ppworkset);
		rawreq->v.bat.ppcputime.max_seconds = 0;
		rawreq->v.bat.ppcputime.max_ms = 0;
		rawreq->v.bat.ppcputime.warn_seconds = 0;
		rawreq->v.bat.ppcputime.warn_ms = 0;
		rawreq->v.bat.prcputime.max_seconds = 0;
		rawreq->v.bat.prcputime.max_ms = 0;
		rawreq->v.bat.prcputime.warn_seconds = 0;
		rawreq->v.bat.prcputime.warn_ms = 0;
		rawreq->v.bat.ppnice = 0;
		rawreq->v.bat.prdrives = 0;
		rawreq->v.bat.prncpus = 0;
		/*
		 *  Clear request precedence fields.
		 */
		for (i = 0; i < MAX_PREDECESSOR; i++) {
			strncpy (rawreq->v.bat.predecessors [i], "",
				 MAX_REQNAME+1);
		}
		/*
		 *  Initialize the output return fields appropriately.
		 */
		rawreq->v.bat.stderr_acc = OMD_SPOOL;
		rawreq->v.bat.stdlog_acc = OMD_SPOOL;
		rawreq->v.bat.stdout_acc = OMD_SPOOL;
		rawreq->v.bat.stderr_mid = *local_mid;
		rawreq->v.bat.stdlog_mid = *local_mid;
		rawreq->v.bat.stdout_mid = *local_mid;
		strncpy (rawreq->v.bat.stderr_name, "", MAX_REQPATH+1);
		strncpy (rawreq->v.bat.stdlog_name, "", MAX_REQPATH+1);
		strncpy (rawreq->v.bat.stdout_name, "", MAX_REQPATH+1);
		rawreq->v.bat.instacount = 0;
		rawreq->v.bat.oustacount = 0;
		rawreq->v.bat.instahiermask = 0;
		rawreq->v.bat.oustahiermask = 0;
		for (i = 0; i < MAX_INSTAPERREQ; i++) {
			rawreq->v.bat.instamid[i] = 0;
		}
		for (i = 0; i < MAX_OUSTAPERREQ; i++) {
			rawreq->v.bat.oustamid[i] = 0;
		}
	}
	closedb (paramfile);
	/*
	 *  Now, construct the name of the control file as it will exist
	 *  in the NQS new request staging directory hierarchy.
	 *
	 *  The name is constructed by using:
	 *
	 *	o The 32-bit time value as the the number of seconds since
	 *	  0:00:00.00 January 1, 1970 GMT.
	 *	o The low-order 30-bits of the calling process' process-id.
	 *
	 *  For a collision to occur, two or more processes must attempt
	 *  to create a control file in the same 1-second time quantum which
	 *  also have the same process-ids (taken modulo 2^30).
	 *
	 *  Since the submission of a batch request to the NQS daemon cannot
	 *  complete (the calling process is blocked), until the NQS daemon
	 *  queues the request and unlinks the request files from the new
	 *  request directory hierarchy, the submission of a steady stream
	 *  of reqs (even when submitted from the SAME process), cannot run
	 *  into trouble.
	 */
	pack6name (ctrlname, (char *) 0, -1, (char *) 0,
		  (long) time ((time_t *) 0), 6,
		  (long) getpid(), 5, 0, 0);
	if ((ctrlfile=open (ctrlname, O_RDWR | O_CREAT | O_EXCL, 0744)) == -1) {
		/*
		 *  Unable to create control file.
		 */
		return (MKREQ_NOCREATE);
	}
	if ((*ptrfile = fdopen (ctrlfile, "w+")) == (FILE *) 0) {
		return (MKREQ_NOCREATE);
	}
	return (MKREQ_SUCCESS);
}


/*** mkdata
 *
 *
 *	int mkdata():
 *
 *	Create a data file in the new request staging directory hierarchy
 *	opened for writing for the caller.
 *	
 *	WARNING/NOTE:
 *
 *		The current directory of the caller MUST BE
 *		the NQS new request staging directory!
 *
 *		It is critical that the effective user-id and
 *		effective group-id of the caller be the values
 *		for the req owner.  This is crucial for the
 *		NQS utilities which allow users to modify and
 *		delete their requests.  The owner of the control
 *		and data files describing a req MUST be the
 *		mapped submitter's user-id.
 *
 *	Returns:
 *		0 if the data file successfully created and opened
 *		  for writing, in which case *ptrfile is returned
 *		  as the open FILE structure providing access to
 *		  the newly created data file.
 *	       -1 if open error or fdopen() error.
 *	       -2 if too many data files would exist.
 */
int mkdata (ptrfile)
FILE **ptrfile;			/* Ptr to FILE structure for data file */
{
	char path [MAX_PATHNAME+1];		/* Pathname of data file */
	int fd;

	*ptrfile = (FILE *) 0;			/* No file yet */
	if (n_datafiles >= 32767) {
		/*
		 *  A maximum of 32767 data files may be created.
		 */
		return (-2);
	}
	pack6name (path, (char *) 0, -1, ctrlname, 0L, 0, 0L, 0,
		  (int) n_datafiles, 3);
	if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0500)) == -1) {
		/*
		 *  Unable to create data file.
		 */
		return (-1);
	}
	if ((*ptrfile = fdopen (fd, "w")) == (FILE *) 0) return (-1);
	n_datafiles++;			/* One more data file */
	return (0);			/* Success */
}


/*** zapreq
 *
 *
 *	void zapreq():
 *
 *	Unlink the control file, and ALL data files associated with
 *	the request that was under construction (or was successfully
 *	submitted), from the NQS new request staging directory.
 *
 *	WARNING/NOTE:
 *
 *		The current directory of the caller MUST BE
 *		the NQS new request staging directory!
 */
void zapreq()
{
	register int i;
	char path [MAX_PATHNAME+1];	/* Pathname of data file */

	if (ctrlfile != -1) {
		/*
		 *  We have a control file and possible data file(s).
		 */
		unlink (ctrlname);	/* Unlink the control file name */
		/*
		 *  Loop to discard all data files associated with the req
		 *  that was under construction.
		 */
		for (i=0; i < n_datafiles; i++) {
			pack6name (path, (char *) 0, -1, ctrlname, 0L, 0,
				   0L, 0, (int) i, 3);
			unlink (path);	/* Discard a control file */
		}
	}
}


/*** quereq
 *
 *
 *	long quereq():
 *
 *	Send a queue request to the NQS daemon.
 *	This function should be invoked when ALL of the files
 *	describing the req have been constructed in the NQS
 *	new request staging directory.
 *
 *	ANOTHER WARNING/NOTE:
 *
 *		The current directory of the caller MUST BE
 *		the NQS new request staging directory!
 *
 *	Returns:
 *	      >=0: if successful, in which case the value returned
 *		   is the sequence number assigned to the request
 *		   (*explain refers to an explanatory text string
 *		   saying that the request was successfully queued.
 *	       -1: if an error occurred submitting the req and the
 *		   local NQS daemon was running.  An example of such
 *		   an error would be submitting the req to a non-existent
 *		   queue (*explain references diagnostic text).  ALL
 *		   request files describing the request are deleted.
 *	       -2: if the local NQS daemon is not running (*explain
 *		   references diagnostic text).  ALL request files 
 *		   describing the request are deleted.
 */
long quereq (transaction_code)
long *transaction_code;			/* Transaction completion code */
{
	struct rawreq rawreq;		/* Raw request structure for just */
					/* submitted request */

	/*
	 *  Ignore all of the usual signals to kill a process at this
	 *  point.  We want to make sure that we survive to tell the
	 *  user the request-id of the queue request.
	 */
	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGTERM, SIG_IGN);
	/*
	 *  Now, let's talk to the daemon.
	 */
	interclear();			/* Clear inter-process communication */
					/* message contents */
	interwstr (ctrlname);		/* Squirrel away the name of the */
					/* control file */
	if (inter (PKT_QUEREQ) == TCML_COMPLETE) {
		/*
		 *  The NQS daemon has responded to our request.
		 *  We are not yet sure of any more than that.  If
		 *  the req has been successfully submitted, then the
		 *  NQS daemon has linked the control file and ALL data
		 *  files for the req into its own private control and
		 *  data directories.
		 */
		lseek (ctrlfile, 0L, 0);	/* Seek to beginning of file */
		if (readhdr (ctrlfile, &rawreq) == -1 ||
		   (rawreq.tcm & XCI_FULREA_MASK) != TCML_SUBMITTED) {
			if ((rawreq.tcm & XCI_FULREA_MASK) == TCML_UNDEFINED) {
				/*
				 *  The NQS daemon could not open the control
				 *  file at all (and therefore could not set
				 *  a request completion code).
				 */
				rawreq.tcm = TCML_UNAFAILURE; /* Very bad news */
			}
			close (ctrlfile);		/* Close control file */
			zapreq();			/* Delete the request*/
			*transaction_code = rawreq.tcm;	/* Return code */
			return (-1);			/* Req not submitted */
		}
		/*
		 *  The request was successfully queued.
		 */
		close (ctrlfile);		/* Close control file */
		if (rawreq.orig_seqno > MAX_SEQNO_USER) {
			/*
			 *  The NQS daemon failed to set a valid
			 *  sequence number.
			 */
			zapreq();			/* Delete the request*/
			*transaction_code = TCML_UNAFAILURE;
			return (-1);			/* Req not submitted */
		}
		/*
		 *  Otherwise, the request was successfully submitted.
		 */
		*transaction_code = rawreq.tcm;	/* Completion code */
		return (rawreq.orig_seqno);	/* Success */
	}
	/*
	 *  The local NQS daemon is not running; therefore the request
	 *  cannot be queued and must be destroyed.
	 */
	zapreq();				/* Destroy the request */
	*transaction_code = TCML_NOLOCALDAE;	/* No daemon present */
	close (ctrlfile);			/* Close control file */
	return (-2);
}


/*** set_default_quota
 *
 *
 *	void set_default_quota():
 *
 *	Initialize a quota limit field to an appropriate default
 *	value that is valid when read by ../lib/readreq.c.
 */
static void set_default_quota (quotafield)
struct quotalimit *quotafield;			/* Field to be initialized */
{
	quotafield->max_units = QLM_BYTES;	/* Default units */
	quotafield->warn_units = QLM_BYTES;
	quotafield->max_quota = 0;		/* Default quota */
	quotafield->warn_quota = 0;
}
/*** mklink
 *
 *
 *    int mkdata():
 *
 *    Link a data file into the new request staging directory hierarchy
 *
 *    Returns:
 *            0 if the data file successfully linked
 *           -1 if symlink error.
 *           -2 if too many data files would exist.
 */
int mklink (datapath)
char *datapath;                       /* Ptr to pathname of data file */
{
      char path [MAX_PATHNAME+1];             /* Pathname of data file */
      int i;

      if (n_datafiles >= 32767) {
              /*
               *  A maximum of 32767 data files may be created.
               */
              return (-2);
      }
      pack6name (path, (char *) 0, -1, ctrlname, 0L, 0, 0L, 0,
                (int) n_datafiles, 3);
      i = umask(~0500);
      if (symlink (datapath, path) < 0) {
              /*
               *  Unable to symbolically link file
               */
              (void) umask (i);

              return (-1);
      }
      (void) umask(i);
      n_datafiles++;                  /* One more data file */
      return (0);                     /* Success */
}
