/*
 * nqsd/nqs_ups.c
 * 
 * DESCRIPTION:
 *
 *	NQS shutdown state update module.
 *
 *	Original Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	June 16, 1986.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <signal.h>
#include <errno.h>
#include <libnqs/nqsxvars.h>
#include <libnqs/transactcc.h>	/* Transaction completion codes */
#include <libnqs/nqsacct.h>	/* NQS accounting data structures */
#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.h>
#include <unistd.h>


/*** ups_shutdown
 *
 *
 *	long ups_shutdown():
 *
 *	Prepare to shutdown.
 *
 *	Non-Berkeley based UNIX implementations must disable group and
 *	others write-access on the named-pipe used to accept message
 *	packets from other processes.  The disabling of group and other
 *	write-access is critical for NQS to shutdown properly, rebuffing
 *	new message packets from client processes (see fstat() call in
 *	../lib/inter.c).
 *
 *	Berkeley UNIX based implementations release the advisory exclusive
 *	access lock on the Queuefile to accomplish the same thing.
 *
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful;
 *		TCML_SHUTERROR:	if some error has prevented
 *				the shutdown sequence from
 *				being started.
 */
long ups_shutdown (
	int pid,			/* Pid of requesting process.	*/
	uid_t ruid,			/* Real UID of requesting process. */
	int grace_period)		/* Number of seconds warning to */
					/* each running request. */
{
	struct nqsqueue *queue;	/* Used to queue walking */
	struct nqsacct_shutdown  acct_shutdown;
        char *fifo_file;                /* Fully qualified file name */
        int fd_acct;

	if (Shutdown == 1) return (TCML_COMPLETE);
					/* Ignore multiple shutdown */
					/* requests.  */
	if (Shutdown == 2) Shutdown = 1;
	/*
	 *  Record who asked us to stop.
	 */
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Received shutdown request from process %1d.\n", pid);
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Process real user-id is: %1d.\n", ruid);
  
	/*
	 *  Broadcast SIGTERM to all NQS shepherd processes, and to the
	 *  NQS network daemon (if one is present).  It is CRITICAL that
	 *  all NQS shepherd processes be notified of the impending
	 *  shutdown PRIOR to aborting all queues (see the loops below).
	 *  Otherwise, the shepherd processes will not know if the
	 *  signal death of the request server process or shell is
	 *  because of an NQS shutdown.
	 */
	kill (-getpid(), SIGTERM);	/* Signal process-group */
#if	!HAS_BSD_PIPE
	/*
	 *  Turn off ALL write permissions to the NQS pipe "mailbox" so
	 *  that other processes can tell that the NQS daemon is shutting
	 *  down.
	 */
        fifo_file = getfilnam (Nqs_ffifo, SPOOLDIR);
        if (fifo_file == (char *)NULL) {
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Shutdown request error.\n");
                sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Unable to determine name of FIFO file.\n");
                relfilnam (fifo_file);
                return (TCML_SHUTERROR);        /* Shutdown error */
        }
	if (chmod (fifo_file, 00600) == -1) {
		/*
		 *  We were unable to change the mode of the NQS
		 *  daemon's request pipe!
		 */
		sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "Shutdown request error.\nUnable to chmod() on the NQS request FIFO.\nFile=%s.\n%s.\n", fifo_file, asciierrno());
		return (TCML_SHUTERROR);	/* Shutdown error */
	}
        relfilnam (fifo_file);
#else
	/*
	 *  Release the advisory exclusive access lock on the Queuefile,
	 *  so that other NQS client processes can tell that NQS is shutdown,
	 *  or is in the process of doing so.
	 *
	 *  The advisory exclusive access lock on the Qmapfile however,
	 *  must be retained until the bloody end (see ../src/nqs_boot.c).
	 */
	flock (Queuefile->fd, LOCK_UN);	/* Release advisory exclusive lock */
#endif
	close (Write_fifo);		/* Close the file descriptor we had */
					/* open to prevent exiting when no  */
					/* req. activity was taking place.  */
					/* We do blocking reads for requests*/
					/* until ALL other processes close  */
					/* their write-descriptors on the   */
					/* mailbox request pipe.	    */
	/*
	 *  For each queue, perform the equivalent of an ABort Queue command,
	 *  with the exception that requests are to be requeued.
	 */
	Shutdown = 1;			/* Signal that no more reqs are to */
					/* be spawned.  We are shutting down*/
	queue = Nonnet_queueset;	/* Walk the non-network queues */
	while (queue != (struct nqsqueue *) 0) {
		nqs_aboque (queue, grace_period, 1);	/* Requeue req if */
		queue = queue->next;			/* possible */
	}
	queue = Net_queueset;		/* Walk the network queues */
	while (queue != (struct nqsqueue *) 0) {
		nqs_aboque (queue, grace_period, 1);	/* Requeue req if */
		queue = queue->next;			/* possible */
	}
        /*
         * Write out the shutdown 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_shutdown, sizeof(acct_shutdown));
            acct_shutdown.h.type = NQSACCT_SHUTDOWN;
            acct_shutdown.h.length = sizeof(acct_shutdown);
            acct_shutdown.h.jobid = 0;
            acct_shutdown.down_time = time ((time_t *)0);
            write(fd_acct, (char *)&acct_shutdown, sizeof(acct_shutdown));
	    close (fd_acct);
        }

	return (TCML_COMPLETE);		/* Return successful completion */
}
