/*
 * nqsd/nqs_boot.c
 * 
 * DESCRIPTION:
 *
 *	This module handles the booting/initialization of NQS.
 *
 *	Original Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	June 19, 1986.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.h>
#include <libnqs/nqspacket.h>		/* NQS local message packet types */
#include <libnqs/nqsxvars.h>		/* NQS global variables and */
					/* directories */
#include <libnqs/transactcc.h>		/* NQS transaction completion codes */
#include <libnqs/nqsacct.h>		/* NQS accounting data structures */
#include <SETUP/autoconf.h>

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

#include <time.h>
void nqs_catch_child(int unused);
static time_t Netdae_start;

#define DEFAULT_TZ_SIZE 128

/*** nqs_boot
 *
 *
 *	void nqs_boot():
 *	Boot/initialize NQS.
 */
void nqs_boot(void)
{
#if 	GPORT_HAS_SYSV_TIME
	static char default_tz [DEFAULT_TZ_SIZE];
					/* Default timezone environment var */
	char *new_envp [2];		/* Subordinate daemons' environment */
#else
	char *new_envp [1];		/* SUbordinate daemons' environment */
#endif
#if	HAS_BSD_PIPE
    	int pipefd1[2];
#else
        char *fifo_pipe;                /* Fully qualified name of FIFO pipe*/
#endif /* HAS_BSD_PIPE */
	int pid;			/* Pid of requester */
	int status;			/* Return status from setpgrp */
	int localmid_status;		/* Return status from localmid() */
	struct nqsacct_startup	acct_startup;
	int fd_acct;
  	int i,j;
	char *root_dir;                 /* Fully qualified file name */

  	/* Generic NQS v3.50.0-pre7
	 * 
	 * First things first, we go and close all of the currently open
	 * file descriptors, because we simply do not need them.
	 */

  	i = sysconf ( _SC_OPEN_MAX );	/* #of file descrs per proc */
	while (--i >= 0) close (i);

  	/* Generic NQS 3.50.0-pre7
	 * 
	 * Let me take a moment to explain this code :
	 * 
	 * 	Since it was first invented, NQS has made the following
	 * 	assumptions about which file descriptors are pointing
	 * 	where :
	 * 
	 * 		fd 0 : Input from NQS command-line utils
	 * 		fd 1 : NQS log process
	 * 		fd 2 : NQS log process
	 * 		fd 3 : Input from NQS log process
	 * 		fd 4 : Output to child NQS processes
	 * 
	 * 	All this was fine until Generic NQS 3.50.0-pre1, which
	 * 	replaced the NQS log process by using the standard
	 * 	UNIX syslog daemon instead.  This broke the assumptions
	 * 	I have described above.
	 * 
	 * 	So, the aim of this code is to come 'close enough' to
	 * 	these assumptions, and end up with :
	 * 
	 * 		fd 0 : Input from NQS command-line processes
	 * 		fd 1 : Output to syslog daemon
	 * 		fd 2 : Output to syslog daemon
	 * 		fd 3 : Input from NQS log process
	 * 		fd 4 : Output to child NQS processes
	 * 
	 * The call to sal_debug_Init should leave a SINGLE file descriptor
	 * open.  We expect this to be file descriptor #1.
	 * 
	 * If this is NOT the case, then we have big, unsolvable problems!!
	 * 
	 * Stu's personal opinion : whoever designed NQS so that it relied
	 * on the exact file descriptors throughout should never be allowed
	 * to write another line of C ever again ...
	 * 
	 * Long term, someone needs to go through the NQS source code, and
	 * remove these assumptions.
	 */
  	j = (open ("/dev/null", O_RDWR) == 0);
  	i = errno;
 
  	/*
	 * Generic-NQS v3.50.4 patch
	 *
	 * Enable debugging level from the environment
	 * Ideally, we should be loading the debugging settings as the
	 * very first thing we do.  Unfortunately, we don't, so this
	 * is a compromise until GNQS v4
	 */

	{
		int d    = 1;
		char * c = getenv("DEBUG");

		if (c != NULL)
			d = atoi(c);

		/* 
		 * zero is lowest permissable value
		 * if i is negative, assume user error and reset to
		 * default value, as debugging level zero generates almost
		 * no messages at all
		 */
		if (d < 0)
			d = 1;

		if (d > 10)
			d = 10;

  		sal_debug_Init(d, "NQS daemon", nqs_abort);
	}
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Generic NQS %s\n", NQS_VERSION);
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Copyright (c) 1985 NASA\n");
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Copyright (c) 1992 John Roman, Monsanto Company\n");
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Copyright (c) 1995 The University of Sheffield\n");
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Copyright (c) 1996 Stuart Herbert\n");
  	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Released under the FSF General Public License v2\n\n");
  
  	if (!j)
          sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Unable to open /dev/null on fd #0\nErrno = %d", i);
  
  	/* 
	 * duplicate file descriptor #1, so that it's also file descriptor #2
	 * 
	 * Thanks to Glenn Carver and Owen Garrett!
	 */
  	i = dup2 (1, 2);
  
  	if (i != 2)
    	  sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_boot(): Unable to duplicate syslog file descriptor - expect #2, returned #%d\n", i);
  
  	/* use up fd #3 permanently ... */
  	if (open ("/dev/null", O_RDWR) != 3)
    	  sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Unable to open /dev/null on fd #3\n");
  
	/*
	 *  Disable certain signals for safety and paranoia sake.
	 */
	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGPIPE, SIG_IGN);
	signal (SIGTERM, SIG_IGN);
#if	IS_POSIX_1 | IS_SYSVr4
	signal (SIGUSR1, SIG_IGN);
	signal (SIGUSR2, SIG_IGN);
#endif
#if defined(SIGCLD)
	signal (SIGCLD, nqs_catch_child);
#endif
#if defined(SIGCHLD)
	signal (SIGCHLD, nqs_catch_child);
#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".
		 */
	  	errno = 0; 	/* ensure nqs_abort() doesn't output more info than is needed in this case */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "NQS(FATAL): NQS not running as root.\n");
	}
#if	GPORT_HAS_SYSV_TIME
	/*
	 *  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%1ld%-.3s", tzname [0],
                         timezone / 3600, tzname [1]);
	  	putenv(default_tz);
        } else {
                sprintf (default_tz, "TZ=%s", getenv("TZ") );
        }
        new_envp[0] = default_tz;
        new_envp [1] = (char *) 0;
#else
	new_envp [0] = (char *) 0;
#endif
	/*
	 *  Set real real-gid of this process to the effective group-id.
	 */
	if (setgid ((int) getegid ()) == -1) {
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "NQS(FATAL): Unable to setgid().\n");
	}
	/*
	 *  All files are to be created with the stated permissions.
	 *  No additional permission bits are to be turned off.
	 */
	umask (0);
  
  	/* Change for Generic NQS 3.41.0 - no more NQS log daemon */
	/* Large amount of code removed ... */
  
  	close(0);
  
	/*
	 *  At this point:
	 *
	 * 	(fd #0) is closed
	 * 	(fd #1)	writes to the system log
	 * 	(fd #2) writes to the system log
	 * 	(fd #3) and above are closed, ready for 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 nqsdaemon");
#else
	sprintf (Argv0, "%-*s", Argv0size, "NQS nqsdaemon");
#endif
	/*
	 *  Change directory to the NQS "root" directory.
	 */

	if ( ! buildenv()) {
	    sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to establish directory independent environment.\n");
	}
	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to determine root directory name.\n");
	}
	if (chdir (root_dir) == -1) {
	    sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to chdir() to %s.\n", Nqs_root);
	}
	relfilnam (root_dir);
  
#if	!HAS_BSD_PIPE
	/*
	 *  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!
		 */
		errno = 0;		/* Not a system call error */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Another NQS local daemon is already running.\n");
	}
	/*
	 *  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) {
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to determine name of FIFO pipe.\n");
        }
        unlink (fifo_pipe);                     /* Delete old pipe */
        if (mkfifo (fifo_pipe, 0010622) == -1) {
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Mknod() error for: %s.\n", fifo_pipe);
        }
        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) {
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to determine name of FIFO request pipe.\n");
        }
        if ((Read_fifo = open (fifo_pipe, O_RDONLY | O_NDELAY)) == -1) {
		/*
		 *  Unable to open the FIFO request pipe.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Error opening %s for reading.\n", Nqs_ffifo);
	}
        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);

        fifo_pipe = getfilnam (Nqs_ffifo, SPOOLDIR);
        if (fifo_pipe == (char *)NULL) {
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to determine name of FIFO write pipe.\n");
        }
        if ((Write_fifo = open (fifo_pipe, O_WRONLY | O_APPEND)) == -1) {
		/*
		 *  Unable to open the FIFO request pipe.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Error opening %s for writing.\n", Nqs_ffifo);
	}
  
        relfilnam (fifo_pipe);

#else
	/*
	 *  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) {
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create read/write packet request pipe.\n");
	}
	Read_fifo = pipefd1 [0];	/* Read FIFO is pipefd1 [0] */
	Write_fifo = pipefd1 [1];	/* Write FIFO is pipefd1 [1] */
#endif
  	/* Generic NQS 3.50.0-pre8
	 * 
	 * If Read_fifo isn't on file descriptor #0, we want to know which one
	 * it *is* on ...
	 */
  
  	if (Read_fifo != 0)
    		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_boot(): Read_fifo is on file descriptor %d\n", Read_fifo);
  
  	/* Generic NQS 3.50.0-pre8
	 * 
	 * If Write_fifo is not on file descriptor #4, we want to know where
	 * it is to be found ...
	 */
  
  	if (Write_fifo != 4)
    		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_boot(): Write_fifo is on file descriptor %d\n", Write_fifo);
  
  	/* Generic NQS 3.50.0-pre7
	 * 
	 * (fd #0) should now refer to Read_fifo
	 * (fd #4) should now refer to Write_fifo
	 * 
	 * We use these assertions to ensure that NQS goes no further if
	 * this is not the case.
	 */
  
	assert(Read_fifo  == 0);
  	assert(Write_fifo == 4);
  
	interset (Write_fifo);	/* Inform ../lib/inter.c.... */
	/*
 	 *
	 *  At this point:
	 *
	 * 	(fd #0) reads from the NQS FIFO pipe
	 *	(fd #1) writes to the system log file
	 * 	(fd #2) writes to the system log file
	 * 	(fd #4) writes to the NQS FIFO pipe.
	 * 	(fd #5) and above are closed, ready for use.
	 */
#if	GPORT_HAS_OSYNC
	if ((Netqueuefile = opendb (Nqs_netqueues, O_RDWR |O_CREAT |
			O_SYNC )) == NULL) {
#else
	if ((Netqueuefile = opendb (Nqs_netqueues, O_RDWR |O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the network queue
		 *  descriptor file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE,"Unable to create/open Netqueuefile.\n");
	}
  	if (Netqueuefile->fd < 5)
  	{
		/*
		 * For some reason, an earlier file descriptor has been
		 * lost.  Report the error and abort.
		 */
	  	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "nqs_boot: File descriptor is < 5\n");
	}
  
        /*
         *  Open the queue complex descriptor file (and create it if it
         *  does not already exist).
         */
#if	GPORT_HAS_OSYNC
        if ((Qcomplexfile = opendb (Nqs_qcomplex, O_RDWR | O_CREAT |
                O_SYNC)) == NULL) {
#else
        if ((Qcomplexfile = opendb (Nqs_qcomplex, O_RDWR | O_CREAT )) == NULL) {
#endif
                /*
                 *  We could not open or create the queue complex
                 *  descriptor file.
                 */
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE,"Unable to create/open Qcomplexfile.\n");
        }
	/*
	 *  Open the non-network queue descriptor file (and create it if it
	 *  does not already exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Queuefile = opendb (Nqs_queues, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Queuefile = opendb (Nqs_queues, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the non-network queue
		 *  descriptor file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE,"Unable to create/open Queuefile.\n");
	}
	/*
	 *  Open the device descriptor file (and create it if it does not
	 *  already exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Devicefile = opendb (Nqs_devices, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Devicefile = opendb (Nqs_devices, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the device descriptor file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Devicefile.\n");
	}
	/*
	 *  Open the queue/device/destination mapping descriptor file
	 *  (and create it if it does not already exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Qmapfile = opendb (Nqs_qmaps, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the queue/device/destination
		 *  mapping descriptor file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Qmapfile.\n");
	}
#if	HAS_BSD_PIPE
	/*
	 *  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!
		 */
		errno = 0;		/* Not a system call error */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Another NQS local daemon is already running.\n");
	}
	/*
	 *  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....
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to flock() Queuefile.\n");
	}
#endif
	/*
	 *  Open the pipe queue destination descriptor file
	 *  (and create it if it does not already exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Pipeqfile = opendb (Nqs_pipeto, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Pipeqfile = opendb (Nqs_pipeto, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the pipe queue
		 *  destination descriptor file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Pipeqfile.\n");
	}
	/*
	 *  Open the general parameters file (and create it if it does not
	 *  already exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Paramfile = opendb (Nqs_params, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Paramfile = opendb (Nqs_params, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the general parameters
		 *  file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Paramfile.\n");
	}
	/*
	 *  Open the NQS managers file (and create it if it does not already
	 *  exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Mgrfile = opendb (Nqs_mgracct, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Mgrfile = opendb (Nqs_mgracct, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the NQS manager access
		 *  list file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Mgrfile.\n");
	}
	/*
	 *  Open the NQS forms file (and create it if it does not already
	 *  exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Formsfile = opendb (Nqs_forms, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Formsfile = opendb (Nqs_forms, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the NQS forms
		 *  list file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Formsfile.\n");
	}
	/*
	 *  Open the NQS servers file (and create it if it does not already
	 *  exist).
	 */
#if	GPORT_HAS_OSYNC
	if ((Serverfile = opendb (Nqs_servers, O_RDWR | O_CREAT |
		O_SYNC)) == NULL) {
#else
	if ((Serverfile = opendb (Nqs_servers, O_RDWR | O_CREAT )) == NULL) {
#endif
		/*
		 *  We could not open or create the NQS servers
		 *  list file.
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to create/open Serverfile.\n");
	}
	/*
	 *  Determine the machine-id of the local host.
	 *  This MUST be done before nqs_ldconf() is called!!!!
	 */
	if ( (localmid_status = localmid (&Locmid)) != 0) {
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORDATA, "Unable to determine machine-id of local host.\nLocalmid says %d\n", localmid_status);
	}
	/*
	 *  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();
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "main(): Configuration loaded.\n");

	/*
	 *  Fork to create the detached NQS local daemon.
	 */
  
	if ((pid = fork()) == -1) {
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to fork() NQS local daemon.\n");
	}
	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) {
	    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Unable to setsid,  errno = %d\n",  errno);
	}
	/*
	 *  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.
	 */
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "main(): Rebuild queue state.\n");
  
	nqs_rbuild();
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "main(): Queue state rebuilt.\n");
  
	/*
	 *  Enable virtual timers.
	 */
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "main(): Enabling virtual timers.\n");
	nqs_valarm(0);
	/*
	 * 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) {
            sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Error opening NQS account file;  Errno = %d\n", errno);
        } else {
            sal_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 void start_network_daemon(void)
{
    long i;
    char *dirname;
    char envp1_buf[256];
    char envp2_buf[256];
    char envp3_buf[256];
    
#if	GPORT_HAS_SYSV_TIME
    static char default_tz [DEFAULT_TZ_SIZE];	
					/* Default timezone environment var */
    char *new_envp [5];		        /* Subordinate daemons' environment */
#else
    char *new_envp [4];		        /* Subordinate daemons' environment */
#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	GPORT_HAS_SYSV_TIME
	/*
	 *  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%1ld%-.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
	new_envp [i++] = (char *) 0;
#endif
    
    Netdae_start = time(NULL);
    if ((Netdaepid = fork()) == -1) {
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to fork() NQS network daemon.\n");
    }
    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.
	     */
	    errno = 0;			/* Not a system call error */
	    sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Too many NQS network daemon arguments.\n");
	}
	/*
	 *  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: left untouched
	 *	File-descr 2: still writes to syslog
	 *	File-descr 3: writes to the local NQS daemon
	 *		      request pipe.
	 */
	sal_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);
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "Nqs_boot: starting network daemon %s\n",  new_argv[0]);
        /*
         *  Execute the NQS network daemon.
         */
        execve (new_argv [0], new_argv, new_envp);
        /*
         *  The exec of the NQS network daemon failed...
         */
        sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to execve() NQS network daemon %s.\n", new_argv[0]);
    }
}

/*** 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 void start_load_daemon(void)
{
    long i;
    char *dirname;
    char envp1_buf[256];
    char envp2_buf[256];
    char envp3_buf[256];
#if	GPORT_HAS_SYSV_TIME
    static char default_tz [DEFAULT_TZ_SIZE];	
					/* Default timezone environment var */
    char *new_envp [5];		        /* Subordinate daemons' environment */
#else
    char *new_envp [4];		        /* Subordinate daemons' environment */
#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	GPORT_HAS_SYSV_TIME
    /*
     *  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%1ld%-.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
    new_envp [i++] = (char *) 0;
#endif

    if ((Loaddaepid = fork()) == -1) {
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to fork() NQS load daemon.\n");
    }
    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.
	     */
	    errno = 0;			/* Not a system call error */
	    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORAUTHOR, "Too many NQS load daemon arguments.\n");
	}
	/*
	 *  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: is left untouched
	 *	File-descr 2: writes to syslog
	 *	File-descr 3: writes to the local NQS daemon
	 *		      request pipe.
	 */
	sal_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);
	sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "Nqs_boot: starting load daemon %s\n",  new_argv[0]);
        /*
         *  Execute the NQS load daemon.
         */
        execve (new_argv [0], new_argv, new_envp);
        /*
         *  The exec of the NQS load daemon failed...
         */
        sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_ERRORRESOURCE, "Unable to execve() NQS load daemon '%s'.\n", new_argv[0]);
    }
}

/***  nqs_ctldae()
 * 
 *  Control a daemon by stopping or starting it on command.
 * 
 */
int nqs_ctldae(int action,  int 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 {
		Netdae_start = time(NULL);  /* prevent it from restarting */
		kill (Netdaepid, SIGTERM);
		Netdaepid = 0;
	   } 
	}
    }
    return (TCML_COMPLETE);
}

void nqs_catch_child(int signum)
{
    pid_t child_pid;
    int status;
    int errno_old;

    errno_old = errno;
    while ((child_pid = waitpid(-1,&status,WNOHANG)) > 0) {
	if (child_pid == Netdaepid) {
	    if (!Shutdown) {
		if (WIFEXITED(status))
		    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Caught Netdaemon exit with return code=%d",WEXITSTATUS(status));
		if (WIFSIGNALED(status))
		    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Caught Netdaemon exit with signal %d",WTERMSIG(status));
	    }
	    Netdaepid = 0;
#if 0
	    /* If not shutting down, try restarting */
	    if ((!Shutdown) && ((time(NULL) - Netdae_start) > 60)) {
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Attempting to restart Netdaemon");
		start_network_daemon();
	    }
#endif
	} else if (child_pid == Loaddaepid) {
	    if (!Shutdown) {
		if (WIFEXITED(status))
		    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Caught Loaddaemon exit with return code=%d",WEXITSTATUS(status));
		if (WIFSIGNALED(status))
		    sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "Caught Loaddaemon exit with signal %d",WTERMSIG(status));
	    }
	    Loaddaepid = 0;
	}
    }
    /* Reinstall yourself */
    errno = errno_old;
    signal (signum, nqs_catch_child);
}
