/*
 *	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     29-Oct-1991	Added debug flag for interactive pgms.
*       V01.30  JRR     21-Nov-1991     Fixup for remote deletes.
*       V01.40  JRR     16-Jan-1992	Added support for RS6000.
*       V01.50  JRR     20-Jan-1992	Fixed setvbuf call for RS6000.
*       V01.6   JRR     12-Feb-1992	Deja-vu.                      
*       V01.7   JRR     12-Feb-1992	Fix setvbuf call for IRIX 4.0 
*					Fix declaration of static routines.
*       V01.8   JRR     13-Feb-1992	Pass logfile to mapuser to log
*					reason for error.
*       V01.9   JRR     02-Mar-1992	Added Cosmic V2 changes.
*       V01.10  JRR     03-Mar-1992	Clean up previous change.
*       V01.11  JRR     16-Mar-1992	Add support for qstat -c.
*       V01.12  JRR     21-Apr-1992     Debugging.
*	V01.13  JRR	15-May-1992	RS6000 fixes.
*					Added support for test environment.
*					Added header.
*	V01.14	JRR	17-Jul-1992	Additional information if exec fails.
*					Use test version name.
*					Added remote qlimits.
*			21-Sep-1992	Added load & remote request completion
*					packet processing.
*			17-Nov-1992	If from loaddaemon,  don't check user.
*	V01.15	JRR	23-Dec-1992	Fix bugs from above.
*					Added groups security fixes.
*					HPUX needs to wait for children, too!
*	V01.16	JRR	10-Feb-1993	Add call to exiting after load proc.
*	V01.17	JRR	23-Feb-1993	Added Boeing enhancment for Mids.
*			28-Apr-1993	Fixed problem with rreqcom.
*	V01.18	JRR	10-Aug-1993	Rreqcom not require passwd entry here.
*	V01.19	JRR	23-Feb-1994	getdtablesize => sysconf.
*	V01.20	JRR	28-Feb-1994	Added support for SOLARIS.
*	V01.21	JRR	05-Aug-1994	Fixes for OSF/1 per Bruno Wolff.
*/
/*++ netdaemon.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.36/src/RCS/netdaemon.c,v $
 *
 * DESCRIPTION:
 *
 *
 *	This module is the main NQS network daemon module and processes
 *	requests from remote NQS client processes.
 *
 *	The NQS network daemon is a child of the local NQS daemon, and
 *	is in the same process group as the local NQS daemon.
 *
 *	When exec'ed from the local NQS daemon, the following file
 *	descriptors and file streams are open:
 *
 *
 *	  Stream stdout (fd 1):	writes to the NQS log
 *				process. 
 *
 *	  Stream stderr (fd 2):	writes to the NQS log
 *				process.
 *
 *	  File descriptor #3:	writes to the named request
 *				FIFO pipe of the local NQS daemon.
 *
 *
 *	The NQS network daemon CANNOT and MUST NOT be executed
 *	independently of the local NQS daemon.  The NQS network
 *	daemon must only be started up by a startup of the local
 *	NQS daemon.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Robert W. Sandstrom.
 *	Sterling Software Incorporated.
 *	December 19, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.21 $ $Date: 1994/09/02 17:39:38 $ $State: Exp $)
 * $Log: netdaemon.c,v $
 * Revision 1.21  1994/09/02  17:39:38  jrroma
 * Version 3.36
 *
 * Revision 1.20  1994/03/30  20:36:24  jrroma
 * Version 3.35.6
 *
 * Revision 1.19  94/02/24  21:30:33  jrroma
 * Version 3.35.3
 * 
 * Revision 1.18  93/09/10  13:56:59  jrroma
 * Version 3.35
 * 
 * Revision 1.17  93/07/13  21:33:43  jrroma
 * Version 3.34
 * 
 * Revision 1.16  93/02/22  10:34:58  jrroma
 * Version3.31.2
 * 
 * Revision 1.15  93/02/05  23:16:42  jrroma
 * Version 3.31
 * 
 * Revision 1.14  92/12/22  15:39:43  jrroma
 * Version 3.30
 * 
 * Revision 1.13  92/06/18  17:30:48  jrroma
 * Added gnu header
 * 
 * Revision 1.12  92/05/06  10:37:48  jrroma
 *  Version 3.20
 * 
 * Revision 1.11  92/03/20  10:58:20  jrroma
 * *** empty log message ***
 * 
 * Revision 1.10  92/03/03  17:13:18  jrroma
 * Fix to previous changes.
 * 
 * Revision 1.9  92/03/02  11:37:04  jrroma
 * Stated Cosmic V2 changes.
 * 
 * Revision 1.8  92/02/14  10:22:48  jrroma
 * Pass logfile as parameter to mapuser.
 * 
 * Revision 1.7  92/02/12  16:12:27  jrroma
 * *** empty log message ***
 * 
 * Revision 1.6  92/02/12  15:29:29  jrroma
 * Fixed setvbuf call for RS6000.
 * 
 * Revision 1.5  92/01/20  13:22:39  jrroma
 * Fixup setvbuf call for RS6000.
 * 
 * Revision 1.4  92/01/17  10:38:27  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.3  91/12/05  16:52:25  jrroma
 * Completed fixes for remote deletes.
 * 
 * Revision 1.2  91/10/29  10:21:28  jrroma
 * Added debug flag for interactive pgms.
 * 
 * Revision 1.1  91/10/03  16:08:33  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* Include NQS constants/types */
					/* (includes <sys/types.h>) */
#include <stdlib.h>
#include <unistd.h>
#define	WRITE_FIFO	3		/* Write pipe to daemon is on #3 */
#include "nqsvars.h"			/* NQS global vars */
#include "nqspacket.h"			/* NQS packet types */
#include "netpacket.h"			/* NQS network packet types */
#include "transactcc.h"			/* NQS transaction completion codes */
#include <netdb.h>			/* 4.2 BSD network database; */
#include <errno.h>			/* Error number */
#include <pwd.h>			/* Password stuff */
#include <signal.h>			/* SIGINT, etc. */
#include <sys/socket.h>			/* 4.2 BSD structs */
#include <netinet/in.h>			/* Internet architecture specific */
#include <grp.h>
#include <limits.h>
#if	SGI | SYS52 | IBMRS 
#else
#if     SOLARIS | LINUX
#include <sys/time.h>
#include <sys/resource.h>
#else
#if	BSD43 | ULTRIX | DECOSF
#include <sys/wait.h>			/* For WNOHANG, union wait */
#else
#if	HPUX
#include <unistd.h>
#include <sys/wait.h>			/* For WNOHANG, union wait */
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
#endif

#ifndef __CEXTRACT__
#if __STDC__

static void badpacket ( void );
static void delreqserver ( int sd, struct passwd *pw, Mid_t client_mid, char *chostname, uid_t whomuid, char *whomname, long orig_seqno, Mid_t orig_mid, int sig, int states );
static void execnetserver ( int newsd, struct passwd *pw, Mid_t client_mid, char *op );
static void loadserver ( int sd, Mid_t client_mid, int version, int nqs_jobs, char *string );
static char *netpacket ( int index );
static void net_abort ( void );
static void net_daeshutdown ( void );
static void outanderrto ( int sd );
static void pre_server ( int newsd, struct sockaddr_in *from );
static void qlimitserver ( int sd, Mid_t client_mid, char *chostname, int whomuid, int flags, char *whomname );
static void qstatserver ( int sd, struct passwd *whompw, Mid_t client_mid, char *chostname, int flags, int whomuid, char *queuename, char *whomname );
static void reload ( void );
static void rreqcomserver ( int sd, Mid_t client_mid, int req_seqno, Mid_t req_mid, int no_jobs );
static void sendandabort ( int sd, long tcm );
static void server_abort ( long tcm );
static void showbytes ( char *bytes, unsigned length );
static int validstr ( int str_n, int size_limit );
static void relaypackets ( void );

#else /* __STDC__ */

static void badpacket (/* void */);
static void delreqserver (/* int sd, struct passwd *pw, Mid_t client_mid, char *chostname, uid_t whomuid, char *whomname, long orig_seqno, Mid_t orig_mid, int sig, int states */);
static void execnetserver (/* int newsd, struct passwd *pw, Mid_t client_mid, char *op */);
static void loadserver (/* int sd, Mid_t client_mid, int version, int nqs_jobs, char *string */);
static char *netpacket (/* int index */);
static void net_abort (/* void */);
static void net_daeshutdown (/* void */);
static void outanderrto (/* int sd */);
static void pre_server (/* int newsd, struct sockaddr_in *from */);
static void qlimitserver (/* int sd, Mid_t client_mid, char *chostname, int whomuid, int flags, char *whomname */);
static void qstatserver (/* int sd, struct passwd *whompw, Mid_t client_mid, char *chostname, int flags, int whomuid, char *queuename, char *whomname */);
static void reload (/* void */);
static void rreqcomserver (/* int sd, Mid_t client_mid, int req_seqno, Mid_t req_mid, int no_jobs */);
static void sendandabort (/* int sd, long tcm */);
static void server_abort (/* long tcm */);
static void showbytes (/* char *bytes, unsigned length */);
static int validstr (/* int str_n, int size_limit */);
static void relaypackets (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */
/*
 * Variables exported from this module.
 */
int monsanto_header = 0;
int DEBUG;				/* Debug flag for interactive pgms. */
/*
 * Variables local to this module.
 */
static FILE *logfile;
 

/*** main
 *
 *
 *	int main():
 *	Start of the NQS network daemon.
 */
int main (argc, argv)
int argc;
char *argv[];
{
#if	BSD43 | ULTRIX | DECOSF | HPUX
	void reapchild();		/* Wait for one or more kids */
#else
#if	SGI | SOLARIS | SYS52 | IBMRS | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif

	static char logfile_buffer [BUFSIZ];	/* logfile output buffer */

	int logfd;			/* logfile file-descriptor */
	struct sockaddr_in sin;		/* Internet socket */
	struct sockaddr_in from;	/* Socket-addr from accept() */
	struct servent *servent;	/* Server entry structure */
	struct stat stat_buf;		/* Stat() buffer */
	int sd;				/* Socket descriptor */
	int new_socket;			/* New socket from accept() */
	int from_addrlen;		/* Address length of client */
	char *root_dir;                 /* Fully qualified file name */

	if (getuid () != 0 || geteuid () != 0) {
		printf ("F$Netdaemon uid or euid wrong.\n");
		logfile = stdout;	/* Set pointer so that output is */
		errno = 0;		/* correctly produced in net_abort(). */
		net_abort();
	}
	Argv0 = argv[0];		/* Save pointer to daemon argv[0] */
	Argv0size = strlen (Argv0);	/* and size for later use */
	/*
	 *  Set the name of this process to a more appropriate string.
	 *
	 *  We use sprintf() to pad with spaces rather than null
	 *  characters so that slimy programs like ps(1) which
	 *  walk the stack will not get confused.
	 */
#if	TEST
	sprintf (Argv0, "%-*s", Argv0size, "xNQS netdaemon");
#else
	sprintf (Argv0, "%-*s", Argv0size, "NQS netdaemon");
#endif
	/*
	 *  Create a file stream in place of stdout and stderr that
	 *  writes to the NQS log process, since stdout and stderr
	 *  might be directed back on the socket connection below.
	 */
	if ((logfd = fcntl (1, F_DUPFD, 4)) == -1) {
		printf ("F$Netdaemon unable to duplicate stdout.\n");
		logfile = stdout;	/* Set pointer so that output */
		net_abort();		/* is correctly produced in */
	}				/* net_abort(). */
	if ((logfile = fdopen (logfd, "w")) == NULL) {
		printf ("F$Netdaemon unable to create logfile stream.\n");
		logfile = stdout;	/* Set pointer so that output */
		net_abort();		/* is correctly produced in */
	}				/* net_abort(). */
	/*
	 *  Buffer output to the log process. Messages must appear
	 *  in coherent chunks, and many processes write output to
	 *  the NQS log process.  Therefore, we must flush before
	 *  our buffers get full.
	 */
#if	BSD43 | ULTRIX | DECOSF
	setbuffer (logfile, logfile_buffer, BUFSIZ);
#else
#if	SYS52 
	setvbuf (logfile, _IOFBF, logfile_buffer, BUFSIZ);
#else
#if	SGI | SOLARIS | HPUX | IBMRS | LINUX
	setvbuf (logfile, logfile_buffer, _IOFBF, BUFSIZ);
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
	/*
	 *  On some UNIX implementations, stdio functions alter errno to
	 *  ENOTTY when they first do something where the file descriptor
	 *  corresponding to the stream happens to be a pipe (which is
	 *  the case here).
	 *
	 *  The fflush() calls are added to get this behavior out of the
	 *  way, since bugs have occurred in the past when a server ran
	 *  into difficultly, and printed out an errno value in situations
	 *  where the diagnostic printf() displaying errno occurred AFTER
	 *  the first stdio function call invoked.
	 */
	fflush (logfile);		/* Logfile is a pipe */
	/*
	 *  Initialize the in-core copy of the parameters.
	 *  The NQS local daemon uses SIGINT to let us know
	 *  when the parameters have changed.  Reload() assumes
	 *  that the variable "Paramfile" has been set.
	 */
	if ((Paramfile = opendb (Nqs_params, O_RDONLY)) == NULL) {
		fprintf (logfile, "F$Netdaemon: unable to open general ");
		fprintf (logfile, "parameters file.\n");
		net_abort();		/* Abort execution */
	}
	signal (SIGINT, reload);
	ldparam ();
	/*
	 *  Disable certain signals.
	 */
	signal (SIGHUP, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGPIPE, SIG_IGN);
#if	BSD43 | ULTRIX | DECOSF | HPUX 
	signal (SIGCHLD, reapchild);
#else
#if	SGI | SOLARIS | SYS52 | IBMRS | LINUX
	signal (SIGUSR1, SIG_IGN);
	signal (SIGUSR2, SIG_IGN);
	signal (SIGCLD, SIG_IGN);	/* Exiting children will disappear */
					/* without us having to wait.  This */
					/* is really quite disgusting, but */
					/* is an effective means of doing */
					/* what we want.... */
#else
BAD SYSTEM TYPE
#endif
#endif
	signal (SIGTERM, net_daeshutdown); /* Shutdown on receipt of SIGTERM */
	/*
	 *  All files are to be created with the stated permissions.
	 *  No additional permission bits are to be turned off.
	 */
	umask (0);

	if ( ! buildenv()) {
	    fprintf (logfile, "F$Netdaemon: Unable to ");
	    fprintf (logfile, "establish directory independent environment.\n");
	    net_abort();		/* Abort execution */
	}

	/*
	 *  Change directory to the NQS "root" directory.
	 */
	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    fprintf (logfile, "F$Netdaemon: Unable to determine root directory name.\n");
	    net_abort();		/* Abort execution */
	}
	if (chdir (root_dir) == -1) {
	    fprintf (logfile, 
		    "F$Netdaemon: Unable to chdir() to the NQS root directory.\n");
	    relfilnam (root_dir);
	    net_abort();		/* Abort execution */
	}
	relfilnam (root_dir);
	/*
	 *  Determine the machine-id of the local host.
	 */
	if (localmid (&Locmid) != 0) {
		fprintf (logfile, "F$Netdaemon: unable to determine ");
		fprintf (logfile, "machine-id of local host.\n");
		net_abort();		/* Abort execution */
	}
	/*
	 *  Build the address that we will bind() to.
	 */
#if	TEST
	servent = getservbyname ("xqs", (char *) 0);
	if (servent == (struct servent *) 0) {
		fprintf (logfile, "F$Netdaemon: Service [xqs] ");
#else
	servent = getservbyname ("nqs", (char *) 0);
	if (servent == (struct servent *) 0) {
		fprintf (logfile, "F$Netdaemon: Service [nqs] ");
#endif
		fprintf (logfile, "is unknown.\n");
		errno = 0;		/* Not a system call error */
		net_abort();		/* Abort execution */
	}
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = servent->s_port;
	if (Debug > 2) {
	    fprintf(logfile, "D$Netdaemon: Service on port %d\n",  sin.sin_port);
	    fflush(logfile);
	}
	sd = socket (AF_INET, SOCK_STREAM, 0);
	if (sd == -1) {
		fprintf (logfile, "F$Netdaemon: Socket() error.\n");
		fflush(logfile);
		net_abort();		/* Abort execution */
	}
	/*
	 *  Test to see that file descr #WRITE_FIFO is really open on the
	 *  local NQS daemon FIFO request pipe.  This partially tests whether
	 *  or not the NQS network daemon has been properly exec'ed from
	 *  the local NQS daemon.
	 */
	if (fstat (WRITE_FIFO, &stat_buf) == -1) {
		/*
		 *  Fstat can only fail by EBADF or EFAULT.
		 *  Since stat_buf is ok, the error is EBADF.
		 */
		fprintf (logfile, "F$Netdaemon: Improper invocation.\n");
		net_abort();		/* Abort execution */
	}
	/*
	 *  Inform inter() that file descr #WRITE_FIFO is connected to the
	 *  local NQS daemon.
	 */
	interset (WRITE_FIFO);
	/*
	 *  Prepare to listen for connections.
	 */
	if (bind (sd, (struct sockaddr_in *) &sin, sizeof (sin)) == -1) {
		fprintf (logfile, "F$Netdaemon: Bind() error.\n");
		fprintf (logfile, "I$Netdaemon: possible attempt to ");
		fprintf (logfile, "execute multiple Netdaemons.\n");
		net_abort();		/* Abort execution */
	}
	if (listen (sd, 10) == -1) {	/* Queue up to 10 connections */
		fprintf (logfile, "F$Netdaemon: Listen() error.\n");
		net_abort();		/* Abort execution */
	}
	from_addrlen = sizeof (from);
	/*
	 *  Loop forever handling NQS network requests, until a SIGTERM
	 *  shutdown signal is received from the local NQS daemon.
	 */
	for (;;) {
		new_socket = accept (sd, &from, &from_addrlen);
		if (getppid() <= 1) net_abort();
		if (new_socket == -1) {
			if (errno != EINTR) {
				fprintf (logfile, "F$Netdaemon: ");
				fprintf (logfile, "accept() error.\n");
				net_abort();	/* Abort execution */
			}
		}
		else {
			/*
			 *  Make sure to flush any diagnostic messages
			 *  PRIOR to the fork!
			 */
			fflush (logfile);
			switch (fork()) {
			case -1:
				/*
				 *  The fork() failed, due to a lack of
				 *  available processes.
				 */
				close (new_socket);	/* Break connection */
				fprintf (logfile, "W$Netdaemon: fork ");
				fprintf (logfile, "failed.\n");
				fprintf (logfile, "I$Netdaemon: net ");
				fprintf (logfile, "request aborted.\n");
				fflush (logfile);
				break;
			case 0:
				/*
				 *  The fork() succeeded.  We are the
				 *  child server process.
				 *
				 *  Close the original socket before handling
				 *  the new request.
				 *
				 *  System V: continue to ignore SIGCLD.
				 *  Berkeley: begin to ignore SIGCHLD.
				 */
				signal (SIGTERM, SIG_DFL);
				signal (SIGINT, SIG_IGN);
#if	BSD43 | ULTRIX | DECOSF
				signal (SIGCHLD, SIG_IGN);
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif
				close (sd);
				closedb (Paramfile);	/* Child opens fresh */
							/* copy */
				pre_server (new_socket, &from);
				break;
			default:
				/*
				 *  The fork() succeeded.  We are the
				 *  NQS network daemon.  Close new socket
				 *  and loop to handle another request.
				 */
				close (new_socket);
				break;
			}
		}
	}
}


/*** pre_server
 *
 *
 *	void pre_server():
 *
 *	Handle NQS network requests from a remote NQS client process.
 *	We are a server process for an NQS network client. If there
 *	is a lot of work to do, we will exec the Netdaemon code.
 *
 *	We are running with the effective and real user-id of root.
 */
static void pre_server (newsd, from)
int newsd;				/* Socket-descr */
struct sockaddr_in *from;		/* Address of the client */
{
	struct hostent *chostent;	/* Client hostent using OUR file */
	Mid_t client_mid;		/* Client machine-id using OUR nmap */
	struct passwd *pw;		/* Passwd entry */
	int integers;			/* Number of 32-bit precision signed-*/
					/* integer values present in the */
					/* received message packet */
	int strings;			/* Number of character/byte strings */
					/* in the received message packet */
	int i;				/* Loop index var */
	
	/*
	 *  Set the name of this process to a more appropriate string.
	 *  We use sprintf() to pad with spaces rather than null
	 *  characters so that disgusting programs like ps(1) which
	 *  walk the stack will not get confused.
	 */
#if	TEST
	sprintf (Argv0, "%-*s", Argv0size, "NQS Netdaemon");
#else
	sprintf (Argv0, "%-*s", Argv0size, "NQS Netdaemon");
#endif
	if (Debug > 2) {
		fprintf (logfile, "D$Netdaemon: born by fork.\n");
		fflush (logfile);
	}
	/*
	 *  True NQS clients always bind to a port < IPPORT_RESERVED.
	 */
	if (htons ((u_short) from->sin_port) >= IPPORT_RESERVED) {
		fprintf (logfile, "W$Netdaemon: Client on non-secure port ");
		fprintf (logfile, "(client port >= %1d).\n", IPPORT_RESERVED);
		sendandabort (newsd, TCMP_NONSECPORT);
	}
	/*
	 *  Determine the machine-id of the client.
	 */

	chostent = gethostbyaddr ((char *) &from->sin_addr,
				  sizeof (struct in_addr), AF_INET);

	if (chostent == (struct hostent *) 0) {
	    /*
	     *  The client host address is not recognized on this
	     *  system!
	     */
	    fprintf (logfile,"W$Netdaemon: client internet addr unknown ");
	    fprintf (logfile, "to local host.\n");
	    sendandabort (newsd, TCMP_NETDBERR);
	}
	switch (nmap_get_mid (chostent, &client_mid)) {
	case NMAP_ENOMAP:
	case NMAP_ENOPRIV:
	case NMAP_EUNEXPECT:
	    fprintf (logfile,"W$Netdaemon: client hostname %s unknown ",
			chostent->h_name);
	    fprintf (logfile, "to local host.\n");
	    fprintf (logfile, "W$Netdaemon: mid is %u.\n", client_mid);
	    fflush(logfile);
	    sendandabort (newsd, TCMP_CLIMIDUNKN);
	case NMAP_SUCCESS:
	    break;
	}
	/*
	 *  Perform NQS connection protocol preamble.
	 */
	setsockfd (newsd);		/* Teach getsockch() */
	switch (interread (getsockch)) {
	case 0: 
	        if (Debug > 2) {
			fprintf (logfile, "D$Netdaemon: got 1st packet.\n");
			fflush (logfile);
		}
		break;
	case -1:
		/*
		 *  The remote client has closed the connection.
		 *  This network request session is over.
		 */
		if (Debug > 2) {
			fprintf (logfile, "D$Netdaemon: session ");
			fprintf (logfile, "concluded, exiting.\n");
			fflush (logfile);
		}
		exit (0);
	case -2:			/* Bad packet received */
					/* Abort execution */
		fprintf (logfile, "W$Netdaemon received bad packet.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_PROTOFAIL);
	}
	/*
	 *  Otherwise, the packet is self-consistent.
	 *  We have a request to process.
	 */
	integers = intern32i();		/* Get number of 32-bit precision */
					/* integers in message packet */
	strings = internstr();		/* Get number of strings in packet */
#if	BSD43
	if (integers == 1 && strings == 0) {
		/*
		 *  The first message packet contains only a single integer.
		 *  The message packet must therefore be an NPK_SERVERCONN
		 *  packet, requesting the creation of a message packet relay
		 *  process.  NPK_SERVERCONN packets can ONLY come from
		 *  processes on the SAME machine.
		 */
		if (Locmid != client_mid) {
			/*
			 *  Client process is not local.
			 */
			fprintf (logfile, "W$Netdaemon: NPK_SERVERCONN ");
			fprintf (logfile, "received from remote client.\n");
			fflush(logfile);
			sendandabort (newsd, TCMP_PROTOFAIL);
		}
		if (interr32i (1) != NPK_SERVERCONN) {
			/*
			 *  Bad packet type or protocol error.
			 */
			sendandabort (newsd, TCMP_PROTOFAIL);
		}
		/*
		 *  The client process is running on the local machine.
		 *  Become a local message packet relay process to the
		 *  local NQS daemon.
		 */
		relaypackets ();	/* Relay message packets to the */
	}				/* local NQS daemon */
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  The message packet is of the ordinary variety, originating from
	 *  a remote client machine.
	 */
	if (integers < 6 || strings < 3) {
		/*
		 *  The first message packet at a minimum must contain
		 *  these integers and strings.
		 */
		fprintf (logfile, "I$Netdaemon received message ");
		fprintf (logfile, "packet lacking appended info.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_PROTOFAIL);
	}
	integers -= 6;			/* Focus on non-appended integers */
	strings -= 3;			/* Focus on non-appended strings */
	if (interr32u (integers + 1) != NPK_MAGIC1) {
		fprintf (logfile, "I$Netdaemon: client has bad magic.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_PROTOFAIL);
	}
	if ( ((int) interr32i (integers + 2) != NPK_LOAD) &&
	         ((int) interr32i (integers + 2) != NPK_RREQCOM) ) {
	    /* The 0 means map by username, not by nmap */
	    if ((pw = mapuser (interr32i (integers + 5), interrstr (strings + 2),
		client_mid, chostent->h_name, 0, logfile)) 
					== (struct passwd *) 0) {
		if (Debug) {
		    fprintf (logfile, "I$Netdaemon: mapuser() denies access.\n");
		    fflush(logfile);
		}
		sendandabort (newsd, TCMP_NOACCAUTH);
	    }
	}
	if (interr32u (integers + 3) != client_mid) {
		fprintf (logfile, "I$Netdaemon: client/local mid conflict.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_MIDCONFLCT);
	}
	if (interr32u (integers + 4) != Locmid) {
		fprintf (logfile, "I$Netdaemon: local/client mid conflict.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_MIDCONFLCT);
	}
	/*
	 *  Reopen the general parameters file.  In the future, we
	 *  will check the NQS network password for the client machine.
	 *  This password will be stored in the corresponding network
	 *  peer record stored in the parameters file.
	 *
	 *  The NQS parameters file must also be opened for the benefit
	 *  of execnetserver() (see below).
	 */
	if ((Paramfile = opendb (Nqs_params, O_RDONLY)) == NULL) {
		fprintf (logfile, "I$Netdaemon: unable to open general ");
		fprintf (logfile, "parameters file.\n");
		fflush(logfile);
		sendandabort (newsd, TCMP_INTERNERR);
	}
	/*
	 *  The packet has been minimally authenticated.
	 */
	if (Debug > 1) {
		fprintf (logfile, "D$Netdaemon: Received packet ");
		fprintf (logfile, "from remote client.\n");
		fprintf (logfile, "D$Netdaemon: Packet type is: %s\n",
			netpacket(interr32i (integers + 2)) );
		fflush (logfile);
		if (integers != 0 || strings != 0) {
			fprintf (logfile, "D$Netdaemon: Packet contents ");
			fprintf (logfile, "are as follows:\n");
			for (i = 1; i <= strings; i++) {
				fprintf (logfile, "D$Netdaemon: Str [%1d] = ",
					 i);
				showbytes (interrstr (i), interlstr (i));
				fflush (logfile);
			}
			for (i = 1; i <= integers; i++) {
				fprintf (logfile, "D$Netdaemon: Integer ");
				fprintf (logfile, "[%1d] = %1ld\n", i,
					 interr32i (i));
				fflush (logfile);
			}
		}
	}
	/*
	 *  Based upon the network packet type, we perform a case
	 *  statement.  Each case exits.  Note that we ignore SIGTERM
	 *  beyond this point.
	 */
	signal (SIGTERM, SIG_IGN);
	switch ((int) interr32i (integers + 2)) {
	/*
	 *  Batch requests and device requests.
	 */
	case NPK_QUEREQ:
		if (strings == 0 && integers == 0) {
			execnetserver (newsd, pw, client_mid, "NPK_QUEREQ");
		}
		else badpacket ();
		break;
	case NPK_DELREQ:
		fprintf(logfile, "D$Netdaemon: got delreq\n");
				fflush (logfile);
		if (strings == 1 && integers == 5 ) {
			delreqserver (newsd, pw, client_mid, chostent->h_name,
				interr32i (1), interrstr (1), interr32i (2),
				interr32u (3), interr32i (4), interr32i (5));
		}
		else badpacket ();
		break;
	case NPK_COMMIT:
		/*
		 * NPK_COMMIT should never be used to start a conversation.
		 */
		badpacket ();
		break;
	/*
	 * File system work.
	 */
	case NPK_DONE:
		/*
		 * NPK_DONE should never be used to start a conversation.
		 */
		badpacket ();
		break;
	case NPK_MKDIR:
		if (strings == 0 && integers == 0 ) {
			badpacket ();
		}
		else badpacket ();
		break;
	case NPK_REQFILE:
		if (strings == 0 && integers == 0 ) {
			execnetserver (newsd, pw, client_mid, "NPK_REQFILE");
		}
		else badpacket ();
		break;
	case NPK_CPINFILE:
	case NPK_CPOUTFILE:
	case NPK_MVINFILE:
		if (strings == 0 && integers == 0 ) {
			badpacket ();
		}
		else badpacket ();
		break;
	case NPK_MVOUTFILE:
		if (integers == 0 && strings == 0) {
			execnetserver (newsd, pw, client_mid, "NPK_MVOUTFILE");
		}
		else badpacket ();
		break;
	/*
	 * Status
	 */
	case NPK_QLIMIT:
		if (integers == 2 && strings == 1) {
			qlimitserver (newsd, client_mid, chostent->h_name,
				interr32i (1), interr32i (2), interrstr (1));
		}
		else badpacket ();
		break;
	case NPK_QDEV:
	case NPK_QMGR:
		if (strings == 0 && integers == 0 ) {
			badpacket ();
		}
		else badpacket ();
		break;
	case NPK_QSTAT:
		if (integers == 2 && strings == 2) {
			qstatserver (newsd, pw, client_mid, chostent->h_name,
				interr32i (1), interr32i (2), interrstr (1),
				interrstr (2));
		}
		else badpacket ();
		break;
	case NPK_LOAD:
		if (integers == 2 && strings == 1) {
			loadserver (newsd, client_mid,
				interr32i (1), interr32i (2),  interrstr (1));
		}
		else badpacket ();
		break;
	case NPK_RREQCOM:
		if (integers == 4 && strings == 0) { 
			rreqcomserver (newsd, client_mid,
				 interr32i (2), interr32i (3), interr32i (4));
		} 
		else badpacket ();
		break;

	default:
		badpacket ();
		break;
	}
	exit (0);			/* This line not reached */
}


/*** execnetserver
 *
 *
 *	void execnetserver():
 *
 */
static void execnetserver (newsd, pw, client_mid, op)
int newsd;				/* Socket descriptor */
struct passwd *pw;			/* Whom we are acting on behalf of */
Mid_t client_mid;			/* The client's machine id */
char *op;				/* The operation */
{
	
	struct gendescr *descr;		/* NQS database information */
	char *argv [MAX_SERVERARGS + 1];/* Construct argv for Netdaemon here */
	char environment [MAX_ENVIRONMENT];
	int envpsize;			/* First free byte in environment */
	char *envp [MAX_ENVIRONVARS +1];/* Construct env for Netdaemon here */
	int envpcount;			/* First free slot in envp */
	long i;				/* Loop variable */
	int ngroups;			/* size of group set */
	gid_t gidset[NGROUPS_MAX];	/* group set */

	/*
	 * Lose our privileges.
	 */
	initgroups (pw->pw_name, pw->pw_gid);
	ngroups = getgroups (NGROUPS_MAX, gidset);
	setgroups (ngroups, gidset);
	setgid (pw->pw_gid);
	setuid (pw->pw_uid);
	seekdb (Paramfile, Udbnetprocs);
	descr = nextdb (Paramfile);
	if (descr->v.par.paramtype != PRM_NETPROCS) {
		fprintf (logfile, "I$Netdaemon: Bad parameters file.\n");
		sendandabort (newsd, TCMP_INTERNERR);
	}
	/*
	 *  Enforce file descriptor associations:
	 *
	 *	0: new socket;
	 *	1,2,4: Log process pipe;
	 *	WRITE_FIFO: Local daemon FIFO;
	 *	remaining: closed
	 */
	if (newsd != 0) {
		close (0);
		fcntl (newsd, F_DUPFD, 0);
		close (newsd);
	}
	i = sysconf ( _SC_OPEN_MAX );		/* #of file descrs per proc */
	while (--i >= 5) close (i);
	if (parseserv (descr->v.par.v.netprocs.netserver,
			argv) == -1) {
		fprintf (logfile, "I$Netdaemon: Parse ");
		fprintf (logfile, "of server path and args failed.\n");
		sendandabort (newsd, TCMP_INTERNERR);
	}
	envpcount = 0;
	envpsize = 0;
	envp [envpcount++] = environment;
	sprintf (environment, "DEBUG=%1d", Debug);
	envpsize += strlen (environment) + 1;
	envp [envpcount++] = environment + envpsize;
	sprintf (environment + envpsize, "OP=%s", op);
	envpsize += strlen (environment + envpsize) + 1;
	envp [envpcount++] = environment + envpsize;
	sprintf (environment + envpsize, "CLIENTMID=%u", client_mid);
	envpsize += strlen (environment + envpsize) + 1;
	/*
	 * Mark the end of the array of character pointers.
	 */
	envp [envpcount] = (char *) 0;
	/* MOREHERE: set up a SIGPIPE handler in the exec'd process */
	fflush (logfile);
	execve (argv [0], argv, envp);
	fprintf (logfile, "I$Netdaemon: Exec error.  Errno=%d\n", errno);
	sendandabort (newsd, TCMP_INTERNERR);
}


/*** delreqserver
 *
 *
 *	void delreqserver():
 *
 */
static void
delreqserver (sd, pw, client_mid, chostname, whomuid, whomname, orig_seqno,
	      orig_mid, sig, states)
int sd;				/* Socket connection */
struct passwd *pw;		/* Password entry on this machine */
Mid_t client_mid;		/* Our guess as to their mid */
char *chostname;		/* Our guess as to their principal name */
uid_t whomuid;			/* The request belongs to this user */
char *whomname;			/* The request belongs to this user */
long orig_seqno;		/* Original sequence number */
Mid_t orig_mid;			/* Original machine id */
int sig;			/* Which signal to send */
int states;			/* Only proceed if in these states */
{

	char packet [MAX_PACKET];	/* To hold our reply */
	int packetsize;			/* Bytes in our reply */
	long tcm;			/* Transaction completion code */
	int ngroups;			/* size of group set */
	gid_t gidset[NGROUPS_MAX];	/* Group set */
	
	if (Debug > 2) {
		fprintf (logfile, "D$Netdaemon: NPK_DELREQ.\n");
		fprintf (logfile, "D$Netdaemon: whomuid = %d.\n", whomuid);
		fprintf (logfile, "D$Netdaemon: whomname = %s.\n", whomname);
		fprintf (logfile, "D$Netdaemon: client_mid = %u.\n", client_mid);
		fprintf (logfile, "D$Netdaemon: chostname = %s.\n", chostname);
		fprintf (logfile, "D$Netdaemon: uid = %d.\n", pw->pw_uid);
		fprintf (logfile, "D$Netdaemon: seqno = %d.\n", orig_seqno);
		fprintf (logfile, "D$Netdaemon: orig_mid = %u.\n", orig_mid);
		fflush (logfile);
	}
	/*
	 * Lose our privileges.
	 */
	initgroups (pw->pw_name, pw->pw_gid);
	ngroups = getgroups (NGROUPS_MAX, gidset);
	setgroups (ngroups, gidset);
        setgid (pw->pw_gid);
        setuid (pw->pw_uid);

	tcm = delreq (pw->pw_uid, orig_seqno, orig_mid, Locmid, sig, states);
	interclear ();
	interw32i (tcm);
	if ((packetsize = interfmt (packet)) == -1) {
	    fprintf (logfile, "I$Netdaemon: Packet would be too large.\n");
	    errno = 0;		/* Not a system call error */
	    server_abort ( NULL );	/* Exit nicely */
	}
	write (sd, packet, packetsize);	/* Write response */
        close (sd);
	exiting();
	exit (0);
}
/*** rreqcomserver
 *
 *
 *	void rreqcomserver():
 *
 */
static void
rreqcomserver (sd, client_mid, req_seqno, req_mid, no_jobs)
int sd;				/* Socket connection */
Mid_t client_mid;		/* Our guess as to their mid */
int req_seqno;			/* Sequence number of request that completed */
Mid_t req_mid;			/* Mid of the request that completed */
int no_jobs;			/* Number of jobs remaining on that mid */
{


    char packet [MAX_PACKET];	/* To hold our reply */
    int packetsize;		/* Bytes in our reply */
    long tcm;			/* Transaction completion code */

    if (Debug > 2) {
        fprintf (logfile,  "D$Netdaemon: NQK_RREQCOM.\n");
        fprintf (logfile,  "D$Netdaemon: Client mid = %u.\n",  client_mid);
        fprintf (logfile,  "D$Netdaemon: Request mid = %u.\n", req_mid);
	fprintf (logfile,  "D$Netdaemon: Request seqno = %d.\n", req_seqno);
	fprintf (logfile,  "D$Netdaemon: # jobs = %d.\n", no_jobs);
    }
    interclear();
    interw32u(client_mid);
    interw32u(req_mid);
    interw32i( (long) req_seqno);
    interw32i( (long) no_jobs);
    tcm = inter( PKT_RREQCOM);
    interclear ();
    tcm = TCMP_CONTINUE;		/* Tell them what we think */
    interw32i (tcm);
    if ((packetsize = interfmt (packet)) == -1) {
	fprintf (logfile, "I$Netdaemon: Packet would be too large.\n");
	errno = 0;		/* Not a system call error */
	server_abort ( NULL );	/* Exit nicely */
    }
    write (sd, packet, packetsize);	/* Write response */
    close (sd);
    exiting();
    exit (0);
    
}
/*** loadserver
 *
 *
 *	void loadserver():
 *
 */
static void
loadserver (sd, client_mid, version,  nqs_jobs,  string)
int sd;				/* Socket connection */
Mid_t client_mid;		/* Our guess as to their mid */
int version;			/* Version of this packet */
int nqs_jobs;			/* Number of current NQS jobs running on that node */
char *string;
{

    char packet [MAX_PACKET];	/* To hold our reply */
    int packetsize;		/* Bytes in our reply */
    long tcm;			/* Transaction completion code */

    if (Debug > 2) {
	fprintf (logfile, "D$Netdaemon: NPK_LOAD.\n");
	fprintf (logfile, "D$Netdaemon: Client mid = %u.\n",  client_mid);
	fprintf (logfile, "D$Netdaemon: version = %d\n",  version);
	fprintf (logfile, "D$Netdaemon: NQS jobs = %d\n",  nqs_jobs);
	fprintf (logfile, "D$Netdaemon: string = %s\n",  string);
	fflush(logfile);
    }
    interclear();
    interw32u( client_mid);
    interw32i( (long) version);
    interw32i( (long) nqs_jobs);
    interwstr( string );
    tcm = inter( PKT_LOAD);
    if ((tcm == TCML_COMPLETE)  || (tcm == TCMP_COMPLETE) )
        tcm = TCMP_CONTINUE;
    interclear ();
    interw32i (tcm);
    if ((packetsize = interfmt (packet)) == -1) {
	    fprintf (logfile, "I$Netdaemon: Packet ");
	    fprintf (logfile, "would be too large.\n");
	    errno = 0;		/* Not a system call error */
	    server_abort ( NULL );	/* Exit nicely */
    }
    write (sd, packet, packetsize);	/* Write response */
    close (sd);
    exiting();				/* Close pipe to nqsdaemon */
    exit (0);
    
}
/*** qlimitserver
 *
 *
 *	void qlimitserver():
 *
 */
static void
qlimitserver (sd, client_mid, chostname, whomuid, flags, whomname)
int sd;				/* Socket connection */
Mid_t client_mid;		/* Our guess as to their mid */
char *chostname;		/* Our guess as to their principal name */
int whomuid;			/* mapped uid */
int flags;			/* Display flags */
char *whomname;			/* mapped name */
{
	
	char packet [MAX_PACKET];	/* To hold our reply */
	int packetsize;			/* Bytes in our reply */
	
	if (Debug > 2) {
		fprintf (logfile, "D$Netdaemon: NPK_QLIMIT.\n");
		fprintf (logfile, "D$Netdaemon: whomuid = %d.\n", whomuid);
		fprintf (logfile, "D$Netdaemon: whomname = %s.\n", whomname);
		fprintf (logfile, "D$Netdaemon: client_mid = %u.\n", client_mid);
		fprintf (logfile, "D$Netdaemon: chostname = %s.\n", chostname);
		fprintf (logfile, "D$Netdaemon: flags = %o.\n", flags);
		fflush (logfile);
	}
	interclear ();
	interw32i (TCMP_CONTINUE);
	if ((packetsize = interfmt (packet)) == -1) {
		fprintf (logfile, "I$Netdaemon: Packet ");
		fprintf (logfile, "would be too large.\n");
		errno = 0;		/* Not a system call error */
		server_abort ( NULL );	/* Exit nicely */
	}
	write (sd, packet, packetsize);	/* Write response */
	/*
	 *  Cause file descriptors 1 and 2 to point to the socket.
	 */
	outanderrto (sd);
	if ((Paramfile = opendb (Nqs_params, O_RDONLY)) == NULL) {
	     printf ("Qlimit (FATAL): Unable to open the NQS parameters database file.\n");
	     exit (1);
	}
	shoalllim(Paramfile,  flags);
	exit (0);
}
/*** qstatserver
 *
 *
 *	void qstatserver():
 *
 */
static void
qstatserver (sd, whompw, client_mid, chostname, flags, whomuid, queuename, whomname)
int sd;				/* Socket connection */
struct passwd *whompw;		/* Whom wants to know */
Mid_t client_mid;		/* Our guess as to their mid */
char *chostname;		/* Our guess as to their principal name */
int flags;			/* Display flags */
int whomuid;			/* -u mapped uid */
char *queuename;		/* If 0 length, then show all queues */
char *whomname;			/* -u mapped name */
{
	
	char packet [MAX_PACKET];	/* To hold our reply */
	int packetsize;			/* Bytes in our reply */
        struct reqset *reqset;          /* request set we are interested in */

        reqset = NULL;                  /* assume no request set */
	
	if (Debug > 2) {
		fprintf (logfile, "D$Netdaemon: NPK_QSTAT.\n");
		fflush (logfile);
	}
	/*
	 *  Open the non-network queue descriptor file.
	 */
	if ((Queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
		fprintf (logfile, "I$Netdaemon: unable to open Queuefile.\n");
		sendandabort (sd, TCMP_INTERNERR);
	}
        if ((Qcomplexfile = opendb (Nqs_qcomplex, O_RDONLY)) == NULL) {
                fprintf(logfile, "I$Netdaemon: unable to open Qcomplexfile.\n");
		sendandabort (sd, TCMP_INTERNERR);
        }
	/*
	 *  Open the queue/device/destination mapping descriptor file.
	 */
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDONLY)) == NULL) {
		fprintf (logfile, "I$Netdaemon: unable to open Qmapfile.\n");
		sendandabort (sd, TCMP_INTERNERR);
	}
	/*
	 *  Open the pipe queue destination descriptor file.
	 */
	if ((Pipeqfile = opendb (Nqs_pipeto, O_RDONLY)) == NULL) {
		fprintf (logfile, "I$Netdaemon: unable to open Pipeqfile.\n");
		sendandabort (sd, TCMP_INTERNERR);
	}
	interclear ();
	interw32i (TCMP_CONTINUE);
	if ((packetsize = interfmt (packet)) == -1) {
		fprintf (logfile, "I$Netdaemon: Packet ");
		fprintf (logfile, "would be too large.\n");
		errno = 0;		/* Not a system call error */
		server_abort ( NULL );	/* Exit nicely */
	}
	write (sd, packet, packetsize);	/* Write response */
	/*
	 *  Cause file descriptors 1 and 2 to point to the socket.
	 */
	outanderrto (sd);
        /*
         *      This is kludge to allow old NQS versions to network
         *      to this machine.  We can tell as an old style NQS
         *      will not have SHO_BATCH SHO_DEVICE SHO_PIPE
         */
        /* if ( (!(flags & SHO_PIPE )) && (!(flags & SHO_BATCH)) &&
         *       (!(flags & SHO_DEVICE)))
         *       flags |= (SHO_PIPE | SHO_BATCH | SHO_DEVICE);
	 */

	/*
	 * If the user wants to see jobs originating on her machine,
	 * set up the reqset data structure here.
	 */
	if (flags & SHO_R_ORIGIN) {
        	reqset = (struct reqset *) malloc (sizeof(reqset) );
                if (reqset == NULL) {
                    fprintf(logfile, "I$Netdaemon:  Error allocating memory.\n");
                    errno = 0;
		    server_abort ( NULL );
                }
                reqset->v.reqid.orig_mid = client_mid;
                reqset->v.reqid.orig_seqno = 0;
                reqset->selecttype = SEL_REQID;
                reqset->next = NULL;
	}

	/*
	 *  To deny a user the right to learn about another user's
	 *  requests, add code in shoqbyname.c or qstat.c, not here.
	 */
	/* 
	 * Cosmic V2 code:
         * if (queuename [0] == '\0') {
         *       list(Queuefile,Qcomplexfile,NULL,flags,NULL,
	 *			Qmapfile,Pipeqfile);
	 *        }
         * else {
         *       list(Queuefile,Qcomplexfile,queuename,flags,NULL,
	 *			Qmapfile,Pipeqfile);
         * }
	 */

        if ( flags & SHO_R_COMPLEX) {
#if		BSD43 | ULTRIX | DECOSF
            showcomplex( Queuefile, queuename, flags, whompw->pw_uid, 
			reqset, daepres(Queuefile), Qmapfile, Pipeqfile, 
			Qcomplexfile);
#else
#if		HPUX | SGI |SOLARIS | SYS52 | IBMRS | LINUX
            showcomplex( Queuefile, queuename, flags, whompw->pw_uid, 
			reqset, daepres(), Qmapfile, Pipeqfile, 
			Qcomplexfile);
#else
BAD SYSTEM TYPE
#endif
#endif
	} else {			/* get info about a queue */
	    if (queuename [0] == '\0' ) {
		/*
		 *  Information should be shown about all queues.
		 */
#if		BSD43 | ULTRIX | DECOSF
		shoallque (Queuefile, flags, whompw->pw_uid,
			  reqset, daepres (Queuefile),
			   Qmapfile, Pipeqfile, 1, Qcomplexfile);
#else
#if		HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		shoallque (Queuefile, flags, whompw->pw_uid,
			  reqset, daepres (),
			   Qmapfile, Pipeqfile, 1, Qcomplexfile);
#else
BAD SYSTEM TYPE
#endif
#endif
	    } else {
#if		BSD43 | ULTRIX | DECOSF
		shoqbyname (Queuefile, queuename, flags, whompw->pw_uid,
			   reqset, daepres (Queuefile),
			    Qmapfile, Pipeqfile, Qcomplexfile);
#else
#if		HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		shoqbyname (Queuefile, queuename, flags, whompw->pw_uid,
			   reqset, daepres (),
			    Qmapfile, Pipeqfile, Qcomplexfile);
#else
BAD SYSTEM TYPE
#endif
#endif
	    }
	}
        if (Debug) {
	    fprintf(logfile, "D$qstatserver: done\n");
	    fflush(logfile);
	}
	exit (0);
}


#if	BSD43
/*** relaypackets
 *
 *
 *	void relaypackets():
 *
 *	The network server process is a "relay message packet" server,
 *	passing message packets from client processes to the local NQS
 *	daemon.
 *
 *	This function exists because Berkeley style UNIX does not support
 *	named-pipes.  This little function, and the additional network
 *	packet of: NPK_SERVERCONN, allow us to get by on Berkeley style UNIX
 *	systems.
 */
static void relaypackets ()
{
	char packet [MAX_PACKET];	/* Relay packet */
	register int n_integers;	/* Number of integers in packet */
	register int n_strings;		/* Number of strings in packet */
	register int i;

	while (1) {
		/*
		 *  Loop until EOF from client.
		 */
		switch (interread (getsockch)) {
		case 0:	/*
			 *  A message packet has been received from the
			 *  local client process.  Forward the message
			 *  packet in a single chunk to the local NQS
			 *  daemon.
			 */
			interclear();	/* Clear outgoing message packet */
			n_integers = intern32i();
			n_strings = internstr();
			for (i = 1; i <= n_integers; i++) {
				/*
				 *  Forward the integer data.
				 */
				if (interr32sign (i)) {
					interw32i (interr32i (i));
				}
				else interw32u (interr32u (i));
			}
			for (i = 1; i <= n_strings; i++) {
				/*
				 *  Forward the byte/string data.
				 */
				interwbytes (interrstr (i),
					    (unsigned) interlstr (i));
			}
			i = interfmt (packet);	/* Format message packet */
			/*
			 *  Now, send the message packet to the local
			 *  NQS daemon.
			 */
			if (write (WRITE_FIFO, packet, i) != i) {
				fprintf (logfile, "W$Netdaemon: Relay ");
				fprintf (logfile, "packet write error.\n");
				server_abort ( NULL );	/* Abort */
			}
			break;
		case -1:
			/*
			 *  The local client process has closed the
			 *  connection, or has exited.
			 */
			exit (0);
		case -2:
			/*
			 *  A bad message packet was received from the
			 *  local client process.
			 */
			fprintf (logfile, "E$Netdaemon: Bad message packet ");
			fprintf (logfile, "received.\n");
			errno = 0;		/* Not a system call error */
			server_abort( NULL );		/* Abort */
		}
	}
}
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif


/*** badpacket
 *
 *
 *	void badpacket():
 *
 *	Write a message to the NQS log process concerning the bad packet
 *	received.
 */
static void badpacket ()
{

	fprintf (logfile, "E$Netdaemon: bad network packet received.\n");
	fflush(logfile);
	errno = 0;			/* Not a system call error */
	server_abort ( NULL );		/* Abort execution */
}


/*** net_abort
 *
 *
 *	void net_abort():
 *
 *	Abort Network daemon execution, writing a message to
 *	the NQS log process.
 */
static void net_abort()
{
	if (errno) fprintf (logfile, "I$%s.\n", asciierrno());
	fprintf (logfile, "I$Netdaemon aborted.\n");
	fflush(logfile);
	exit (1);
}


/*** net_daeshutdown
 *
 *
 *	void net_daeshutdown():
 *	Catch SIGTERM signal to shutdown.
 */
static void net_daeshutdown()
{
	signal (SIGTERM, SIG_IGN);
	fprintf (logfile, "I$Netdaemon shutting down.\n");
	fflush(logfile);
	exit (0);
}


/*** outanderrto
 *
 *
 *	void outanderrto():
 *
 *	Force STDOUT and STDERR to be directed at the socket
 *	connection established between the remote client and
 *	this server.  The streams stdout and stderr stay open.
 */
static void outanderrto (sd)
int sd;						/* Socket descriptor */
{
	static char stdout_buffer [SOCKBUFSIZ];	/* Stdout output buffer */
	static char stderr_buffer [SOCKBUFSIZ];	/* Stderr output buffer */
	
	fflush (stdout);
	fflush (stderr);
	close (STDOUT);			/* Close original STDOUT */
	close (STDERR);			/* Close original STDERR */
	if (fcntl (sd, F_DUPFD, STDOUT) != STDOUT ) {
		fprintf (logfile, "I$Netdaemon cannot replicate ");
		fprintf (logfile, "socket descriptor.\n");
		sendandabort (sd, errnototcm ());
	}
	if (fcntl (sd, F_DUPFD, STDERR) != STDERR) {
		fprintf (logfile, "I$Netdaemon cannot replicate ");
		fprintf (logfile, "socket descriptor.\n");
		sendandabort (sd, errnototcm ());
	}
	/*
	 *  Make sure that formatted output returned to the remote
	 *  client process is transmitted in blocks for greater
	 *  efficiency.
	 */
#if	BSD43 | ULTRIX | DECOSF
	setbuffer (stdout, stdout_buffer, SOCKBUFSIZ);
#else
#if	SYS52 
	setvbuf (stdout, _IOFBF, stdout_buffer, SOCKBUFSIZ);
#else
#if	HPUX | SGI | SOLARIS | IBMRS | LINUX
	setvbuf (stdout, stdout_buffer, _IOFBF, SOCKBUFSIZ);
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
#if	BSD43 | ULTRIX | DECOSF
	setbuffer (stderr, stderr_buffer, SOCKBUFSIZ);
#else
#if	SYS52 
	setvbuf (stderr, _IOFBF, stderr_buffer, SOCKBUFSIZ);
#else
#if	HPUX | SOLARIS | SGI | IBMRS | LINUX
	setvbuf (stderr, stderr_buffer, _IOFBF, SOCKBUFSIZ);
#else
BAD SYSTEM TYPE
#endif
#endif
#endif
}


#if	BSD43 | ULTRIX | DECOSF | HPUX 
/*** reapchild
 *
 *
 *	void reapchild ():	
 *	SIGCHLD handler.
 *
 */
static void reapchild ()
{
#if	DECOSF
	union wait *status;
#else
	union wait status;
#endif

	/*
	 * Don't need to reinstate Berkeley signal handlers.
	 */
	while (wait3 (&status, WNOHANG, (struct rusage *) 0) > 0) {
	}
}
#else
#if	SGI | SOLARIS | SYS52 | IBMRS | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif


/*** reload
 *
 *
 *	void reload ():	
 *	When the local daemon updates a parameter,
 *	it will send a SIGINT to the net daemon.  The net daemon
 *	should then update its copy of the parameters off the disk.
 *
 */
static void reload ()
{
	signal (SIGINT, reload);
	seekdb (Paramfile, 0);
	ldparam ();
}


/*** sendandabort
 *
 *
 *	void sendandabort ():
 *
 *	Abort Network server execution, sending a TCM code
 *	to the client and writing a message to the NQS log.
 */
static void sendandabort (sd, tcm)
int sd;					/* Socket descriptor */
long tcm;				/* What to send */
{
	char packet [MAX_PACKET];
	int packetsize;
	
	interclear ();
	interw32i (tcm);
	if ((packetsize = interfmt (packet)) == -1) {
		fprintf (logfile, "I$Netdaemon: Packet ");
		fprintf (logfile, "would be too large.\n");
		errno = 0;		/* Not a system call error */
		server_abort ( NULL );	/* Exit nicely */
	}
	write (sd, packet, packetsize);	/* Write response */
	errno = 0;			/* Not a system call error */
	server_abort ( tcm );
}


/*** server_abort
 *
 *
 *	void server_abort():
 *
 *	Abort Network server execution, writing a message to
 *	the NQS log process.
 */
static void server_abort ( tcm )
long tcm;
{
	if (tcm != TCMP_NOACCAUTH) {
	    if (errno) fprintf (logfile, "I$%s.\n", asciierrno());
	    fprintf (logfile, "I$Netdaemon aborting.\n");
	    fflush(logfile);
	}
	exit (1);
}


/*** showbytes
 *
 *
 *	void showbytes():
 *	Show byte/string datum as received in message packet.
 */
static void showbytes (bytes, length)
register char *bytes;			/* Array of bytes. */
register unsigned length;		/* Length of byte array. */
{
	register short ch;		/* Byte/character */
	register int col;		/* Column counter */
	short truncate_flag;		/* String truncated flag */

	col = 0;			/* # of columns written */
	truncate_flag = 0;
	if (length > 300) {
		truncate_flag = 1;	/* Truncate to 300 chars on */
		length = 300;		/* display */
	}
	while (length--) {
		if (col >= 24) {
			fputc ('\n', logfile);
			fprintf (logfile, "D$Netdaemon: ");
			col = 0;
		}
		ch = (*bytes & 0377);	/* Mask to 8 bits */
		bytes++;		/* Increment to next byte */
		if (ch < ' ' || ch > 127) {
			/*
			 *  This character is unprintable, or is a tab
			 *  character.
			 */
			fprintf (logfile, "[\\%03o]", ch);
			col += 6;
		}
		else {
			fputc (ch, logfile);
			col += 1;
		}
	}
	fputc ('\n', logfile);
	if (truncate_flag) {
		/*
		 *  String truncated.
		 */
		fprintf (logfile, "D$Netdaemon: String truncated.\n");
	}
}

/*** netpacket
 *
 *   char *netpacket():
 *   Return string of type of packet
 *
 */
static char *netpacket(index)
int index;
{
    static char *quereq = "NPK_QUEREQ";
    static char *delreq = "NPK_DELREQ";
    static char *commit = "NPK_COMMIT";
    static char *done   = "NPK_DONE";
    static char *mkdir  = "NPK_MKDIR";
    static char *reqfile = "NPK_REQFILE";
    static char *cpinfile = "NPK_CPINFILE";
    static char *cpoutfile = "NPK_CPOUTFILE";
    static char *mvinfile = "NPK_MVINFILE";
    static char *mvoutfile = "NPK_MVOUTFILE";
    static char *suspendreq = "NPK_SUSPENDREQ";
    static char *qdev = "NPK_QDEV";
    static char *qlimit = "NPK_QLIMIT";
    static char *qmgr = "NPK_QMGR";
    static char *qstat = "NPK_QSTAT";
    static char *load = "NPK_LOAD";
    static char *rreqcom = "NPK_RREQCOM";
    static char *unknown = "Packet type is unknown";

    switch (index) {
      case NPK_QUEREQ:
	return(quereq);
        break;
      case NPK_DELREQ:
	return(delreq);
	break;
      case NPK_COMMIT:
	return(commit);
	break;
      case NPK_DONE:
	return(done);
	break;
      case NPK_MKDIR:
	return(mkdir);
	break;
      case NPK_REQFILE:
	return(reqfile);
	break;
      case NPK_CPINFILE:
	return(cpinfile);
	break;
      case NPK_CPOUTFILE:
	return(cpoutfile);
	break;
      case NPK_MVINFILE:
	return(mvinfile);
	break;
      case NPK_MVOUTFILE:
	return(mvoutfile);
	break;
      case NPK_SUSPENDREQ:
        return (suspendreq);
        break;
      case NPK_QDEV:
        return(qdev);
        break;
      case NPK_QLIMIT:
        return(qlimit);
        break;
      case NPK_QMGR:
        return (qmgr);
        break;
      case NPK_QSTAT:
        return(qstat);
        break;
      case NPK_LOAD:
        return(load);
        break;
      case NPK_RREQCOM:
        return(rreqcom);
        break;
      default:
        return(unknown);
	break;
    }
}

      


/*** validstr
 *
 *
 *	int validstr():
 *	Validate string.
 *
 *	Returns:
 *		1: if the specified string parameter in the received
 *		   message packet is not null, and is of length less
 *		   than, or equal to size_limit.
 *		0: otherwise.
 */
static int validstr (str_n, size_limit)
register int str_n;			/* Message packet string [n] */
register int size_limit;		/* Maximum size limit */
{
	register int size;

	size = interlstr (str_n);
	if (size && size <= size_limit) return (1);
	return (0);
}
