/*
 *	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     22-Jan-1992	If localmid fails, print the reason.
*       V01.40  JRR     23-Jan-1992	Fix call to setpgrp for RS6000.     
*       V01.5   JRR     02-Mar-1992	Added Cosmic V2 changes.     
*       V01.6   JRR     08-Apr-1992     Added CERN enhancements.
*	V01.7	JRR	26-May-1992	Fixed support for RS6000.
*					Added header.
*	V01.8	JRR	31-Jul-1992	Make sure name is correct for
*					the test version.
*					Fix TZ for SGI.
*					Write out startup record to acct file.	
*					Added support for HPUX.	
*	V01.9	JRR	01-Feb-1993	Use setsid for IBMRS.
*	V01.10	JRR	
*	V01.11	JRR	22-Feb-1993	Fix problem with SGI & unit 4 by
*					moving call to localmid.
*			08-Mar-1993	Added Boeing enhancement for files.
*			07-May-1993	Created #define for  ???
*	V01.12	JRR	18-Aug-1993	Miniscule clean-up.
*					Now using setsid for all.
*					_NFILE -> sysconf.
*	V01.13	JRR			Placeholder
*	V01.14	JRR	24-Feb-1994	Add Irix 5.n fixes back in.
*	V01.15	JRR	28-Feb-1994	Added support for SOLARIS.
*	V01.16	JRR	18-Apr-1994	Ranking Compute servers.
*/
/*++ nqs_boot.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.36/src/RCS/nqs_boot.c,v $
 *
 * DESCRIPTION:
 *
 *	This module handles the booting/initialization of NQS.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	June 19, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.16 $ $Date: 1994/09/02 17:39:39 $ $State: Exp $)
 * $Log: nqs_boot.c,v $
 * Revision 1.16  1994/09/02  17:39:39  jrroma
 * Version 3.36
 *
 * Revision 1.15  1994/03/30  20:36:28  jrroma
 * Version 3.35.6
 *
 * Revision 1.14  94/02/25  15:55:01  jrroma
 * Version 3.35.3
 * 
 * Revision 1.13  94/02/24  21:30:49  jrroma
 * Version 3.35.3
 * 
 * Revision 1.12  93/09/10  13:57:10  jrroma
 * Version 3.35
 * 
 * Revision 1.11  93/07/13  21:33:45  jrroma
 * Version 3.34
 * 
 * Revision 1.10  1993/02/05  23:16:44  jrroma
 * Version 3.31
 *
 * Revision 1.9  92/12/22  15:39:47  jrroma
 * Version 3.30
 * 
 * Revision 1.8  92/06/18  17:30:53  jrroma
 * Added gnu header
 * 
 * Revision 1.7  92/05/06  10:38:46  jrroma
 *  Version 3.20
 * 
 * Revision 1.6  92/03/02  13:23:33  jrroma
 * Started Cosmic V2 changes.
 * 
 * Revision 1.5  92/02/12  16:15:40  jrroma
 * *** empty log message ***
 * 
 * Revision 1.4  92/01/23  10:42:49  jrroma
 * Fix call to setpgrp for RS6000.
 * 
 * Revision 1.3  92/01/22  11:02:22  jrroma
 * If problems with localmid, print the reason.
 * 
 * Revision 1.2  92/01/17  10:43:22  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  10:42:46  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* Include NQS constants/types	*/
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "nqspacket.h"			/* NQS local message packet types */
#include "nqsxvars.h"			/* NQS global variables and */
					/* directories */
#include "transactcc.h"			/* NQS transaction completion codes */
#include "nqsacct.h"			/* NQS accounting data structures */


#ifndef __CEXTRACT__
#if __STDC__

static int start_load_daemon ( void );
static int start_network_daemon ( void );

#else /* __STDC__ */

static int start_load_daemon (/* void */);
static int start_network_daemon (/* void */);

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


#if     SOLARIS | LINUX
#include <sys/time.h>
#include <sys/resource.h>
#endif
#if	BSD43 | ULTRIX | DECOSF
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
#define DEFAULT_TZ_SIZE 128		/* Size of default_tz buffer */
extern int putenv();			/* Add environment variable */
extern long timezone;			/* Number of seconds from GMT */
extern char *tzname [2];		/* Time zone name */
#else
BAD SYSTEM TYPE
#endif
#endif


/*** nqs_boot
 *
 *
 *	void nqs_boot():
 *	Boot/initialize NQS.
 */
void nqs_boot()
{
#if	BSD43 | ULTRIX | DECOSF
	char *new_envp [1];		/* SUbordinate daemons' environment */
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
	static char default_tz [DEFAULT_TZ_SIZE];
					/* Default timezone environment var */
	char *new_envp [2];		/* Subordinate daemons' environment */
#else
BAD SYSTEM TYPE
#endif
#endif
	char *new_argv [MAX_SERVERARGS+1];
					/* Subordinate daemons' arguments */
	int pipefd1 [2];		/* Pipe file descriptors used to */
	int pipefd2 [2];		/* communicate with the NQS log */
					/* process. */
	int pid;			/* Pid of requester */
	int i;				/* Loop variable. */
	int status;			/* Return status from setpgrp */
	int localmid_status;		/* Return status from localmid() */
	char pathname [MAX_PATHNAME + 1];	/* Holds path for execve() */
	struct nqsacct_startup	acct_startup;
	int fd_acct;
        char *fifo_pipe;                /* Fully qualified name of FIFO pipe*/
        char *daemon;                   /* Fully qualified file name */
        char *dirname;                  /* Fully qualified directory name */
	char *root_dir;                 /* Fully qualified file name */

	/*
	 *  Ensure that stdout and stderr are buffered.  This is quite
	 *  critical since messages must be sent in coherent chunks to
	 *  the NQS log process from multiple processes (NQS daemon and
	 *  servers).
	 *
	 *  Thou shalt NOT think to thyself, that you can get rid of stderr.
	 *  It is used in nqs_spawn.c, when stdout has been opened as the
	 *  device....
	 */
	bufstdout();
	bufstderr();
	/*
	 *  Disable certain signals for safety and paranoia sake.
	 */
	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGPIPE, SIG_IGN);
#if	BSD43 | ULTRIX | DECOSF
	signal (SIGTERM, SIG_IGN);
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
	signal (SIGTERM, SIG_IGN);
	signal (SIGUSR1, SIG_IGN);
	signal (SIGUSR2, SIG_IGN);
	signal (SIGCLD, SIG_DFL);	/* Quite rational paranoia when */
					/* dealing with the Cray-2 */
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  ALL SIGALRM signals are to be directed to the virtual timer
	 *  module.
	 */
	signal (SIGALRM, nqs_valarm);	
					/* Virtual timer SIGALRM catcher */
					/* in nqs_vtimer.c */

	/*
	 *  NQS is considered to be BOOTING!
	 */
	Booted = 0;			/* NQS is not yet booted. */
					/* See nqs_rbuild.c and nqs_upd.c. */
	Shutdown = 0;			/* We are starting up! */

	if (geteuid() != 0 || getuid() != 0) {
		/*
		 *  The real AND effective user-id of NQS MUST be "root".
		 */
		printf ("NQS(FATAL): NQS not running as root.\n");
		errno = 0;			/* Not a system call error */
		nqs_abort();
	}
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
	/*
	 *  System V based implementations of NQS require the presence
	 *  of the TZ environment variable.  This is used in
	 *  ../src/nqs_reqser.c and ../src/logdaemon.c.
	 */
        if (getenv ("TZ") == (char *)0) {
                /*
                 *  No TZ environment variable is present.
                 *  Make up a default TZ environment variable.
                 */
                sprintf (default_tz, "TZ=%-.3s%1d%-.3s", tzname [0],
                         timezone / 3600, tzname [1]);
        } else {
                sprintf (default_tz, "TZ=%s", getenv("TZ") );
        }
        new_envp[0] = default_tz;
        new_envp [1] = (char *) 0;
#else
#if	BSD43 | ULTRIX | DECOSF
	new_envp [0] = (char *) 0;
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  Set real real-gid of this process to the effective group-id.
	 */
	if (setgid ((int) getegid ()) == -1) {
		printf ("NQS(FATAL): Unable to setgid().\n");
		nqs_abort();
	}
	/*
	 *  All files are to be created with the stated permissions.
	 *  No additional permission bits are to be turned off.
	 */
	umask (0);
	/*
	 *  Create pipes to the NQS log process.
	 */
	if (pipe (pipefd1) == -1 || pipe (pipefd2) == -1) {
		printf ("NQS(FATAL): Unable to create pipe between ");
		printf ("NQS local daemon and NQS log daemon.\n");
		nqs_abort();
	}
	/*
	 *  Fork off the NQS message log daemon.
	 */
	fflush (stdout);		/* Flush any diagnostic messages */
	fflush (stderr);		/* prior to the fork() */
	if ((Logdaepid = fork()) == -1) {
		printf ("NQS(FATAL): Unable to fork NQS log process.\n");
		nqs_abort();
	}
	else if (Logdaepid == 0) {
		/*
		 *  We are the NQS log process.
		 *  We will use:
		 *
		 *	pipefd1 [0] to read logfile and command output
		 *		from other NQS processes.
		 *	pipefd2 [1] to write to the NQS local daemon
		 *		confirming a successful switch of log files.
		 *
		 *  Divorce ourselves from any tty.
		 */
		status = setsid();
		if (status == -1) {
		    printf("E$nqs_boot: error setting log process group\n");
		    printf("E$nqs_boot: log process group errno %d\n",  errno);
		    fflush(stdout);
		}
		close (pipefd1 [1]);		/* Not needed */
		close (pipefd2 [0]);		/* Not needed */
		fcntl (STDOUT, F_SETFL,		/* Make sure stdout open */
		       O_APPEND);		/* for append. */
		/*
		 *  Make stdin read from the output pipe from the NQS
		 *  local daemon (and later from other processes).
		 */
		close (STDIN);
		fcntl (pipefd1 [0], F_DUPFD, STDIN);
		close (pipefd1 [0]);
		/*
		 *  Make stderr write back to the NQS local daemon.
		 */
		close (STDERR);
		fcntl (pipefd2 [1], F_DUPFD, STDERR);
		close (pipefd2 [1]);
		/*
		 *  Begin execution as the NQS log process.
		 *
		 *	Stdin reads messages from the NQS local daemon
		 *	and network daemon (and their associated server
		 *	processes).
		 *
		 *	Stdout writes to the original stdout of the
		 *	NQS local daemon, and is completely buffered.
		 *
		 *	Stderr writes back to the NQS local daemon in
		 *	response to certain NQS local daemon requests.
		 */
                if ((daemon = getfilnam ("logdaemon", LIBDIR)) != (char *)NULL){
		    strcpy (pathname, daemon);
                    relfilnam (daemon);
		    /*
		     *  Execve() logdaemon.
		     */
#if	TEST
		    new_argv [0] = "xNQS logdaemon";
#else
		    new_argv [0] = "NQS logdaemon";
#endif
		    new_argv [1] = (char *) 0;
		    execve (pathname, new_argv, new_envp);
		}
		/*
		 * If the NQS logdaemon cannot be started, there is no
		 * point in letting the NQS local daemon boot.
		 */
		kill (getppid (), SIGKILL);
	}
	/*
	 *  We are the NQS local daemon.
	 *  We will use:
	 *
	 *	pipefd1 [1] to write output to the NQS log process,
	 *		    and to send commands to the NQS log
	 *		    process.
	 *	pipefd2 [0] to read NQS log process completion codes.
	 *
	 *  Hold unit 4 open so that it is available.  We will close
	 *  it later.
	 */
	close (pipefd1 [0]);			/* Unnecessary */
	close (pipefd2 [1]);			/* Unnecessary */
	close (STDOUT);				/* Make stdout write to */
	fcntl (pipefd1 [1], F_DUPFD, STDOUT);	/* the NQS log process. */
	close (STDERR);				/* Make stderr write to */
	fcntl (pipefd1 [1], F_DUPFD, STDERR);	/* the NQS log process. */
	close (pipefd1[1]);
	Fromlog = pipefd2 [0];			/* Pipe from the NQS */
						/* log message process */
	if (Fromlog != 3) {
		/*
		 *  Force Fromlog to file-descriptor #3.
		 */
		close (3);
		fcntl (Fromlog, F_DUPFD, 3);
		Fromlog = 3;
	}
	/*
	 *  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 (stdout);			/* Stdout is a pipe */
	fflush (stderr);			/* Stderr is a pipe */
	errno = 0;				/* Clear errno */
	/*
	 *  At this point:
	 *
	 *	Stdout  (fd #1) writes (buffered) to the NQS log message
	 *			process.
	 *	Stderr  (fd #2) writes (buffered) to the NQS log message
	 *			process.
	 *	Fromlog (fd #3) reads completion messages from the NQS
	 *			log message process.
	 *
	 *
	 *  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 nqsdaemon");
#else
	sprintf (Argv0, "%-*s", Argv0size, "NQS nqsdaemon");
#endif
	/*
	 *  Change directory to the NQS "root" directory.
	 */

	if ( ! buildenv()) {
	    printf ("F$Unable to establish directory independent environment.\n");
	    nqs_abort();
	}
	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    printf ("F$Unable to determine root directory name.\n");
	    nqs_abort();
	}
	if (chdir (root_dir) == -1) {
	    printf ("F$Unable to chdir() to %s.\n", Nqs_root);
	    relfilnam (root_dir);
	    nqs_abort();
	}
	relfilnam (root_dir);
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
	/*
	 *  Check for the possibility of multiple NQS local daemons running.
	 */
	interclear();			/* Clear message buffer */
	if (inter (PKT_SENSEDAEMON) != TCML_NOLOCALDAE) {
		/*
		 *  Egads!  An NQS local daemon is already running, or
		 *  is still in the process of shutting down.
		 *  Bail-out NOW!
		 */
		printf ("F$Another NQS local daemon is already running.\n");
		errno = 0;		/* Not a system call error */
		nqs_abort();
	}
#else
#if	BSD43
#else
BAD SYSTEM TYPE
#endif
#endif
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
	/*
	 *  Create the named pipe that will serve as the "mailbox"
	 *  for NQS request packets.
	 */
        fifo_pipe = getfilnam (Nqs_ffifo, SPOOLDIR);
        if (fifo_pipe == (char *)NULL) {
                printf ("F$Unable to determine name of FIFO pipe.\n");
                nqs_abort ();
        }
        unlink (fifo_pipe);                     /* Delete old pipe */
        if (mknod (fifo_pipe, 0010622, NULL) == -1) {
                printf ("F$Mknod() error for: %s.\n", fifo_pipe);
                relfilnam (fifo_pipe);
                nqs_abort ();
        }
        relfilnam (fifo_pipe);
	/*
	 *  We open the NQS mailbox named-pipe for reading AND
	 *  WRITING (on separate file descriptors) so that when no
	 *  other processes have the pipe open for writing, we do
	 *  not immediately return from the read() call with zero
	 *  bytes read.
	 *
	 *  Furthermore, the Write_fifo file descriptor is used
	 *  by:
	 *
	 *	1.  Request server "shepherd processes"
	 *	    to send request completion messages;
	 *
	 *	2.  Pipe queue server processes to send
	 *	    pipe queue destination status messages
	 *	    to the NQS local daemon;
	 *
	 *	3.  Server processes to send process group/
	 *	    family-ids;
	 *
	 *	4.  The NQS network daemon (if present).
	 *
	 *  The descriptor opened explicitly for writing is closed
	 *  when a shutdown request is received so that we can tell
	 *  when no other processes have the request pipe open (and
	 *  can hence know when it is safe to shutdown).
	 *
	 *  Note that when opening the FIFO for reading, we do so
	 *  with O_NDELAY set, so that we do not block forever.
	 */
        fifo_pipe = getfilnam (Nqs_ffifo, SPOOLDIR);
        if (fifo_pipe == (char *)NULL) {
                printf ("F$Unable to determine name of FIFO request pipe.\n");
                nqs_abort ();
        }
        if ((Read_fifo = open (fifo_pipe, O_RDONLY | O_NDELAY)) == -1) {
		/*
		 *  Unable to open the FIFO request pipe.
		 */
		printf ("F$Error opening %s for reading.\n", Nqs_ffifo);
		nqs_abort();
	}
        relfilnam (fifo_pipe);
	/*
	 *  Now, clear O_NDELAY on the read FIFO, so that we will block
	 *  waiting for data, or until no one else has the pipe open
	 *  for writing.
	 */
	fcntl (Read_fifo, F_SETFL, O_RDONLY);
#else
#if	BSD43
	/*
	 *  Berkeley based UNIX implementations do not support
	 *  named-pipes, and so we have to do things differently.
	 *
	 *  So much for the highly touted UNIX "portability"
	 *  properties.  Sigh....
	 *
	 *  Create the Read_fifo/Write_fifo packet request pipe.
	 */
	if (pipe (pipefd1) == -1) {
		printf("F$Unable to create read/write packet request pipe.\n");
		nqs_abort();
	}
	Read_fifo = pipefd1 [0];	/* Read FIFO is pipefd1 [0] */
	Write_fifo = pipefd1 [1];	/* Write FIFO is pipefd1 [1] */
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  Make STDIN into the Read_fifo since we do not need
	 *  the original STDIN anymore, and it is logical to have
	 *  STDIN as the file descriptor reading the request pipe.
	 */
	close (STDIN);
	fcntl (Read_fifo, F_DUPFD, STDIN);	/* Make STDIN */
						/* refer to Read_fifo */
	close (Read_fifo);	/* Don't need original Read_fifo now */
	Read_fifo = STDIN;	/* Read_fifo is STDIN */
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
        fifo_pipe = getfilnam (Nqs_ffifo, SPOOLDIR);
        if (fifo_pipe == (char *)NULL) {
                printf ("F$Unable to determine name of FIFO write pipe.\n");
                nqs_abort ();
        }
        if ((Write_fifo = open (fifo_pipe, O_WRONLY | O_APPEND)) == -1) {
		/*
		 *  Unable to open the FIFO request pipe.
		 */
		printf ("F$Error opening %s for writing.\n", Nqs_ffifo);
		nqs_abort ();
	}
        relfilnam (fifo_pipe);
#else
#if	BSD43
#else
BAD SYSTEM TYPE
#endif
#endif
	if (Write_fifo != 4) {
		/*
		 *  It is absolutely CRITICAL that Write_fifo be
		 *  opened on descriptor #4!!!  This fact is used in
		 *  ../src/nqs_spawn.c, ../src/nqs_reqser.c, and
		 *  ../src/nqs_reqexi.c.
		 */
		close (4);
		fcntl (Write_fifo, F_DUPFD, 4);
		close (Write_fifo);
		Write_fifo = 4;
	}
	interset (Write_fifo);	/* Inform ../lib/inter.c.... */
	/*
 	 *
	 *  At this point:
	 *
	 *	STDIN	   (fd #0) reads from the FIFO request pipe.
	 *	Stdout     (fd #1) writes (buffered) to the NQS log
	 *			   message process.
	 *	Stderr     (fd #2) writes (buffered) to the NQS log
	 *			   message process.
	 *	Fromlog    (fd #3) reads completion messages from
	 *			   the NQS log message process.
	 *	Write_fifo (fd #4) writes to the FIFO request pipe.
	 * 
	 *
	 *  Open the network queue descriptor file (and create it if it
	 *  does not already exist).
	 */
	if ((Netqueuefile = opendb (Nqs_netqueues, O_RDWR |O_CREAT |
			O_SYNC )) == NULL) {
		/*
		 *  We could not open or create the network queue
		 *  descriptor file.
		 */
		printf ("F$Unable to create/open Netqueuefile.\n");
		nqs_abort();
	}
        /*
         *  Open the queue complex descriptor file (and create it if it
         *  does not already exist).
         */
        if ((Qcomplexfile = opendb (Nqs_qcomplex, O_RDWR | O_CREAT |
                O_SYNC)) == NULL) {
                /*
                 *  We could not open or create the queue complex
                 *  descriptor file.
                 */
                printf ("F$Unable to create/open Qcomplexfile.\n");
                nqs_abort();
        }
	/*
	 *  Open the non-network queue descriptor file (and create it if it
	 *  does not already exist).
	 */
	if ((Queuefile = opendb (Nqs_queues, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the non-network queue
		 *  descriptor file.
		 */
		printf ("F$Unable to create/open Queuefile.\n");
		nqs_abort();
	}
	/*
	 *  Open the device descriptor file (and create it if it does not
	 *  already exist).
	 */
	if ((Devicefile = opendb (Nqs_devices, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the device descriptor file.
		 */
		printf ("F$Unable to create/open Devicefile.\n");
		nqs_abort();
	}
	/*
	 *  Open the queue/device/destination mapping descriptor file
	 *  (and create it if it does not already exist).
	 */
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the queue/device/destination
		 *  mapping descriptor file.
		 */
		printf ("F$Unable to create/open Qmapfile.\n");
		nqs_abort();
	}
#if	BSD43 | ULTRIX | DECOSF
	/*
	 *  Berkeley UNIX based implementations do not have named pipes
	 *  (with the exception of ULTRIX), and so the mechanism used to
	 *  detect (and avoid) multiple NQS daemons, and to detect the
	 *  presence of a running NQS daemon (see ../lib/daepres.c) is
	 *  completely different....
	 *
	 *  Note that ULTRIX uses a hybrid of the approach used on System
	 *  V, and pure Berkeley based UNIX implementations....
	 *
	 *  If an advisory exclusive lock exists on the Qmapfile, then
	 *  another NQS daemon is already running, or is still running
	 *  and is in the process of shutting down.
	 */
	if (flock (Qmapfile->fd, LOCK_EX | LOCK_NB) == -1) {
		/*
		 *  Egads!  An NQS local daemon is already running, or
		 *  is still in the process of shutting down.
		 *  Bail-out NOW!
		 */
		printf ("F$Another NQS local daemon is already running.\n");
		errno = 0;		/* Not a system call error */
		nqs_abort();
	}
	/*
	 *  We were able to successfully apply an advisory exclusive lock
	 *  to the Qmapfile.  Therefore, we are the only local NQS daemon
	 *  running on the system, which is how things MUST be!
	 *
	 *  We must now apply an advisory exclusive lock on the Queuefile,
	 *  for the benefit of ../lib/daepres.c.
	 *
	 *  A local NQS daemon always has an advisory exclusive lock on
	 *  the Queuefile, while running.  This lock is released when the
	 *  local NQS system is first told to shutdown.  The lock on the
	 *  Qmapfile is retained however, until the NQS system is completely
	 *  shutdown.
	 */
	if (flock (Queuefile->fd, LOCK_EX | LOCK_NB) == -1) {
		/*
		 *  We should have gotten this lock....
		 */
		printf ("F$Unable to flock() Queuefile.\n");
		nqs_abort();		/* Abort execution */
	}
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  Open the pipe queue destination descriptor file
	 *  (and create it if it does not already exist).
	 */
	if ((Pipeqfile = opendb (Nqs_pipeto, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the pipe queue
		 *  destination descriptor file.
		 */
		printf ("F$Unable to create/open Pipeqfile.\n");
		nqs_abort();
	}
	/*
	 *  Open the general parameters file (and create it if it does not
	 *  already exist).
	 */
	if ((Paramfile = opendb (Nqs_params, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the general parameters
		 *  file.
		 */
		printf ("F$Unable to create/open Paramfile.\n");
		nqs_abort();
	}
	/*
	 *  Open the NQS managers file (and create it if it does not already
	 *  exist).
	 */
	if ((Mgrfile = opendb (Nqs_mgracct, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the NQS manager access
		 *  list file.
		 */
		printf ("F$Unable to create/open Mgrfile.\n");
		nqs_abort();
	}
	/*
	 *  Open the NQS forms file (and create it if it does not already
	 *  exist).
	 */
	if ((Formsfile = opendb (Nqs_forms, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the NQS forms
		 *  list file.
		 */
		printf ("F$Unable to create/open Formsfile.\n");
		nqs_abort();
	}
	/*
	 *  Open the NQS servers file (and create it if it does not already
	 *  exist).
	 */
	if ((Serverfile = opendb (Nqs_servers, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
		/*
		 *  We could not open or create the NQS servers
		 *  list file.
		 */
		printf ("F$Unable to create/open Serverfile.\n");
		nqs_abort();
	}
	/*
	 *  Determine the machine-id of the local host.
	 *  This MUST be done before nqs_ldconf() is called!!!!
	 */
	if ( (localmid_status = localmid (&Locmid)) != 0) {
		printf ("F$Unable to determine machine-id of local host.\n");
		printf ("F$Localmid says %d\n", localmid_status);
		nqs_abort();
	}
	/*
	 *  Load NQS queue, device, and parameter state/configuration.
	 *
	 *  WARNING/NOTE:
	 *	Nqs_ldconf() invokes the function:
	 *
	 *		fetchpwuid()
	 *
	 *	which opens the account/password database on the
	 *	local system, and keeps it open.  All subsequent
	 *	calls to fetchpwuid() and fetchpwnam() will use
	 *	the account/password database that existed at
	 *	the time NQS was booted.
	 *
	 *	This side-effect is bad in that NQS will NOT notice
	 *	NEW (different i-node) /etc/passwd files created
	 *	AFTER NQS was booted.  (However, if changes are
	 *	made DIRECTLY to the /etc/passwd file--that is, the
	 *	/etc/passwd file is not UNLINKED and replaced with a
	 *	new one, then NQS WILL notice the changes.)
	 *
	 *	This side-effect is good in that NQS always has access
	 *	to SOME version of the local account/password database,
	 *	thus preventing a system-wide shortage in the file-
	 *	table from affecting NQS when trying to access the
	 *	account/password database.
	 *
	 *	This side-effect is also beneficial to the extent that
	 *	NQS does not have to reopen the account/password data-
	 *	base every single time that an account entry must be
	 *	gotten from a user-id, or username.
 	 *
	 */
	nqs_ldconf();
	if (Debug) {
		printf ("D$main(): Configuration loaded.\n");
		fflush (stdout);
	}

	/*
	 *  Fork to create the detached NQS local daemon.
	 */
	fflush (stdout);		/* Flush any diagnostic messages */
	fflush (stderr);		/* prior to the fork() */
	if ((pid = fork()) == -1) {
		printf ("F$Unable to fork() NQS local daemon.\n");
		nqs_abort ();
	}
	else if (pid) exit (0);			/* Parent exits here */

	/*
	 *  We are now the NQS local daemon!
	 *  Divorce ourselves from any tty.
	 */
	status = setsid ();
	if (status == -1) {
	    printf("F$Unable to setsid,  errno = %d\n",  errno);
	    fflush(stdout);
	}
	/*
	 *  If a network daemon is configured for this system, then
	 *  fork off the network daemon at this time.
	 */
	Netdaepid = 0;			/* No network daemon at this time */
	if (Netdaemon [0] != '\0') {
		/*
		 *  A network daemon is configured.
		 */
		start_network_daemon();
	}
	/*
	 *  If a load daemon is configured for this system, then
	 *  fork off the load daemon at this time.
	 */
	Loaddaepid = 0;			/* No network daemon at this time */
	if ( LB_Scheduler && (Loaddaemon [0] != '\0')) {
		/*
		 *  A load daemon is configured.
		 */
		start_load_daemon();
	}
	/*
	 *  Rebuild the NQS queue state.
	 */
	if (Debug) {
		printf ("D$main(): Rebuild queue state.\n");
		fflush (stdout);
	}
	nqs_rbuild();
	if (Debug) {
		printf ("D$main(): Queue state rebuilt.\n");
		fflush (stdout);
	}
	/*
	 *  Enable virtual timers.
	 */
	if (Debug) {
		printf ("D$main(): Enabling virtual timers.\n");
		fflush (stdout);
	}
	nqs_valarm();
	/*
	 * Determine if there is a system doing the scheduling and
	 * remember it's mid.
	 */
	/*  nqs_get_scheduler(); */
	/*
	 * Write out the startup record to nqs accounting file.
	 */
	fd_acct = open(NQSACCT_FILE,
                        O_WRONLY|O_APPEND|O_CREAT, 0644);
        if (fd_acct < 0) {
            printf ("E$Error opening NQS account");
            printf(" file;  Errno = %d\n", errno);
            fflush (stdout);
        } else {
            bytezero((char *)&acct_startup, sizeof(acct_startup));
            acct_startup.h.type = NQSACCT_STARTUP;
            acct_startup.h.length = sizeof(acct_startup);
            acct_startup.h.jobid = 0;
	    acct_startup.start_time = time ((time_t *)0);
	    write(fd_acct, (char *)&acct_startup, sizeof(acct_startup));
	    close (fd_acct);
        }

}
/*** start_network_daemon
 * 
 *  If a network daemon is configured for this system, then
 *  fork off the network daemon at this time.
 *
 *  The following constraints are REQUIRED!
 *
 *	1.  All of the requisite NQS database files must have
 *	    been created prior to the forking of the NQS
 *	    NETWORK daemon, since the NETWORK daemon will
 *	    immediately open all of them for reading....
 *
 *	2.  The NQS NETWORK daemon and the NQS LOCAL daemon
 *	    MUST be in the same process group (for shutdown
 *	    purposes).
 *
 *	3.  The NQS NETWORK daemon MUST be the child of the
 *	    NQS LOCAL daemon, so that a crash of the NQS
 *	    NETWORK daemon can be detected by the NQS LOCAL
 *	    daemon.
 */
static
int start_network_daemon()
{
    long i;
    char *dirname;
    char envp1_buf[256];
    char envp2_buf[256];
    char envp3_buf[256];
    
#if	BSD43 | ULTRIX | DECOSF
    char *new_envp [4];		        /* Subordinate daemons' environment */
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
    static char default_tz [DEFAULT_TZ_SIZE];	
					/* Default timezone environment var */
    char *new_envp [5];		        /* Subordinate daemons' environment */
#else
BAD SYSTEM TYPE
#endif
#endif
    char *new_argv [MAX_SERVERARGS+1];
					/* Subordinate daemons' arguments */

    /*
     * If the current environment defines directory independent
     * pathnames, pass them into the new environment.
     */
    i = 0;
    if ((dirname = getenv (Nqs_nmapdir)) != (char *)NULL) {
        sprintf (envp1_buf, "%s=%s", Nqs_nmapdir, dirname);
	new_envp[i++] = envp1_buf;
    }
    if ((dirname = getenv (Nqs_libdir)) != (char *)NULL) {
        sprintf (envp2_buf, "%s=%s", Nqs_libdir, dirname);
	new_envp[i++] = envp2_buf;
    }
    if ((dirname = getenv (Nqs_spooldir)) != (char *)NULL) {
        sprintf (envp3_buf, "%s=%s", Nqs_spooldir, dirname);
	new_envp[i++] = envp3_buf;
    }

#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
	/*
	 *  System V based implementations of NQS require the presence
	 *  of the TZ environment variable.  This is used in
	 *  ../src/nqs_reqser.c and ../src/logdaemon.c.
	 */
        if (getenv ("TZ") == (char *)0) {
                /*
                 *  No TZ environment variable is present.
                 *  Make up a default TZ environment variable.
                 */
                sprintf (default_tz, "TZ=%-.3s%1d%-.3s", tzname [0],
                         timezone / 3600, tzname [1]);
        } else {
                sprintf (default_tz, "TZ=%s", getenv("TZ") );
        }
        new_envp[i++] = default_tz;
        new_envp [i++] = (char *) 0;
#else
#if	BSD43 | ULTRIX | DECOSF
	new_envp [i++] = (char *) 0;
#else
BAD SYSTEM TYPE
#endif
#endif
    fflush (stdout);	/* Flush any diagnostic messages */
    fflush (stderr);	/* prior to the fork() */
    if ((Netdaepid = fork()) == -1) {
	printf ("F$(Unable to fork() NQS network daemon.\n");
	nqs_abort();
    }
    else if (Netdaepid == 0) {
	/*
	 *  We are the child, and will become the NQS
	 *  NETWORK daemon, if all goes well....
	 */
	if (parseserv (Netdaemon, new_argv) == -1) {
	    /*
	     *  Too many arguments.
	     */
	    printf ("F$Too many NQS network daemon arguments.\n");
	    errno = 0;			/* Not a system call error */
	    nqs_abort();		/* Abort execution */
	}
	/*
	 *  Close ALL files with the exception of stdout,
	 *  stderr, and the Write_fifo descriptor writing
	 *  to the local NQS daemon request pipe.
	 *
	 *  When done here, all files will be closed with
	 *  the exceptions:
	 *
	 *	File-descr 1: writes to the NQS log process.
	 *	File-descr 2: writes to the NQS log process.
	 *	File-descr 3: writes to the local NQS daemon
	 *		      request pipe.
	 */
	closepwdb();		/* Close account/password */
						/* database */
	close (0);		/* Close stdin */
	if (Write_fifo != 3) {
	    /*
	     *  Force write-fifo to file-descriptor #3.
	     */
	    close (3);
	    fcntl (Write_fifo, F_DUPFD, 3);
            close (Write_fifo);
       	}
	interset (3);	/* Write to local daemon on #3 */
	i = sysconf ( _SC_OPEN_MAX );	/* #of file descrs per proc */
	while (--i >= 4) close (i);
	/*
	 *  The NQS NETWORK daemon MUST be vulnerable
	 *  to SIGTERM, since this signal will be broadcast
	 *  to all descendents of the LOCAL NQS daemon upon
	 *  shutdown.  (Queue and device servers are signalled
	 *  independently of this mechanism upon shutdown).
	 *
	 *  The NQS NETWORK daemon should, when appropriate,
	 *  establish a SIGTERM handler to gracefully shutdown
	 *  when so signalled by the LOCAL NQS daemon.
	 */
        signal (SIGTERM, SIG_DFL);
	if (Debug ) {
	    printf("D$Nqs_boot: starting network daemon %s\n",  new_argv[0]);
	    fflush(stdout);
	}
        /*
         *  Execute the NQS network daemon.
         */
        execve (new_argv [0], new_argv, new_envp);
        /*
         *  The exec of the NQS network daemon failed...
         */
        printf ("F$Unable to execve() NQS network daemon.\n");
        nqs_abort();		/* Abort execution */
    }
}
/*** start_load_daemon
 * 
 *  If a load daemon is configured for this system, then
 *  fork off the load daemon at this time.
 *
 *  The following constraints are REQUIRED!
 *
 *	1.  All of the requisite NQS database files must have
 *	    been created prior to the forking of the NQS
 *	    LOAD daemon, since the LOAD daemon will
 *	    immediately open all of them for reading....
 *
 *	2.  The NQS LOAD daemon and the NQS LOCAL daemon
 *	    MUST be in the same process group (for shutdown
 *	    purposes).
 *
 *	3.  The NQS LOAD daemon MUST be the child of the
 *	    NQS LOCAL daemon, so that a crash of the NQS
 *	    LOAD daemon can be detected by the NQS LOCAL
 *	    daemon.
 */
static
int start_load_daemon()
{
    long i;
    char *dirname;
    char envp1_buf[256];
    char envp2_buf[256];
    char envp3_buf[256];
#if	BSD43 | ULTRIX | DECOSF
    char *new_envp [4];		        /* Subordinate daemons' environment */
#else
#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
    static char default_tz [DEFAULT_TZ_SIZE];	
					/* Default timezone environment var */
    char *new_envp [5];		        /* Subordinate daemons' environment */
#else
BAD SYSTEM TYPE
#endif
#endif
    char *new_argv [MAX_SERVERARGS+1];
					/* Subordinate daemons' arguments */

    /*
     * If the current environment defines directory independent
     * pathnames, pass them into the new environment.
     */
    i = 0;
    if ((dirname = getenv (Nqs_nmapdir)) != (char *)NULL) {
        sprintf (envp1_buf, "%s=%s", Nqs_nmapdir, dirname);
	new_envp[i++] = envp1_buf;
    }
    if ((dirname = getenv (Nqs_libdir)) != (char *)NULL) {
        sprintf (envp2_buf, "%s=%s", Nqs_libdir, dirname);
	new_envp[i++] = envp2_buf;
    }
    if ((dirname = getenv (Nqs_spooldir)) != (char *)NULL) {
        sprintf (envp3_buf, "%s=%s", Nqs_spooldir, dirname);
	new_envp[i++] = envp3_buf;
    }

#if	SGI | SOLARIS | HPUX | SYS52 | IBMRS | LINUX
    /*
     *  System V based implementations of NQS require the presence
     *  of the TZ environment variable.  This is used in
     *  ../src/nqs_reqser.c and ../src/logdaemon.c.
     */
    if (getenv ("TZ") == (char *)0) {
        /*
         *  No TZ environment variable is present.
         *  Make up a default TZ environment variablle
         */
        sprintf (default_tz, "TZ=%-.3s%1d%-.3s", tzname [0],
                         timezone / 3600, tzname [1]);
    } else {
        sprintf (default_tz, "TZ=%s", getenv("TZ") );
    }
    new_envp[i++] = default_tz;
    new_envp [i++] = (char *) 0;
#else
#if	BSD43 | ULTRIX | DECOSF
    new_envp [i++] = (char *) 0;
#else
BAD SYSTEM TYPE
#endif
#endif

    fflush (stdout);	/* Flush any diagnostic messages */
    fflush (stderr);	/* prior to the fork() */
    if ((Loaddaepid = fork()) == -1) {
	printf ("F$(Unable to fork() NQS load daemon.\n");
	nqs_abort();
    }
    else if (Loaddaepid == 0) {
	/*
	 *  We are the child, and will become the NQS
	 *  Load daemon, if all goes well....
	 */
	if (parseserv (Loaddaemon, new_argv) == -1) {
	    /*
	     *  Too many arguments.
	     */
	    printf ("F$Too many NQS load daemon arguments.\n");
	    errno = 0;			/* Not a system call error */
	    nqs_abort();		/* Abort execution */
	}
	/*
	 *  Close ALL files with the exception of stdout,
	 *  stderr, and the Write_fifo descriptor writing
	 *  to the local NQS daemon request pipe.
	 *
	 *  When done here, all files will be closed with
	 *  the exceptions:
	 *
	 *	File-descr 1: writes to the NQS log process.
	 *	File-descr 2: writes to the NQS log process.
	 *	File-descr 3: writes to the local NQS daemon
	 *		      request pipe.
	 */
	closepwdb();		/* Close account/password */
						/* database */
	close (0);		/* Close stdin */
	if (Write_fifo != 3) {
	    /*
	     *  Force write-fifo to file-descriptor #3.
	     */
	    close (3);
	    fcntl (Write_fifo, F_DUPFD, 3);
            close (Write_fifo);
       	}
	interset (3);	/* Write to local daemon on #3 */
	i = sysconf ( _SC_OPEN_MAX );	/* #of file descrs per proc */
	while (--i >= 4) close (i);
	/*
	 *  The NQS LOAD daemon MUST be vulnerable
	 *  to SIGTERM, since this signal will be broadcast
	 *  to all descendents of the LOCAL NQS daemon upon
	 *  shutdown.  (Queue and device servers are signalled
	 *  independently of this mechanism upon shutdown).
	 *
	 *  The NQS LOAD daemon should, when appropriate,
	  *  establish a SIGTERM handler to gracefully shutdown
	 *  when so signalled by the LOCAL NQS daemon.
	 */
        signal (SIGTERM, SIG_DFL);
	if (Debug ) {
	    printf("D$Nqs_boot: starting load daemon %s\n",  new_argv[0]);
	    fflush(stdout);
	}
        /*
         *  Execute the NQS load daemon.
         */
        execve (new_argv [0], new_argv, new_envp);
        /*
         *  The exec of the NQS load daemon failed...
         */
        printf ("F$Unable to execve() NQS load daemon.\n");
        nqs_abort();		/* Abort execution */
    }
}
/***  nqs_ctldae()
 * 
 *  Control a daemon by stopping or starting it on command.
 * 
 */
nqs_ctldae(action,  daemon)
int action;				    /* START_DAEMON or STOP_DAEMON */
int daemon;				    /* LOAD_DAEMON or NET_DAEMON */
{
    if (action == START_DAEMON) {
	if (daemon == LOAD_DAEMON) {
	    if (Loaddaepid != 0) return (TCML_DAEALRRUN);
	    else start_load_daemon();
	} else if (daemon == NET_DAEMON) {
	    if (Netdaepid != 0) return (TCML_DAEALRRUN);
	    else start_network_daemon();
	} 
    } else {
	if (daemon == LOAD_DAEMON) {
	    if (Loaddaepid == 0) return (TCML_DAENOTRUN);
	    else {
		kill (Loaddaepid, SIGTERM);
		Loaddaepid = 0;
	    }
	} else if (daemon == NET_DAEMON) {
	    if (Netdaepid == 0) return (TCML_DAENOTRUN);
	    else {
		kill (Netdaepid, SIGTERM);
		Netdaepid = 0;
	   } 
	}
    }
    return (TCML_COMPLETE);
}
