/*
 * nqsd/nqs_aboque.c
 * 
 * DESCRIPTION:
 *
 *	Abort all running requests in an NQS queue.
 *
 *	Original Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <signal.h>
#include <errno.h>
#include <libnqs/nqsxvars.h>			/* Global vars */

#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.h>

static void nqs_kill ( void );
static void nqs_killset ( struct nqsqueue *queue );

static char *info = "%s.\n";
static char *problem = "Problem sending signal in nqs_aboque.c.\n";

/*** nqs_aboque
 *
 *
 *	void nqs_aboque():
 *	Abort all running requests in an NQS queue.
 */
void nqs_aboque (struct nqsqueue *queue, int grace_period, short requeue_flag)
{
	short sig;			/* Signal to send */
	time_t timeval;			/* Timer value */
	register short reqindex;	/* Index into the Runvars[] array */
					/* of the running structure for */
					/* the request being aborted */
	register struct request *req;	/* Ptr to request structure for */
					/* the request being aborted */

	if (queue->sigkill) {
	    /*
	     *  Multiple pending ABort Queue commands are NOT allowed
	     *  for the same queue.
	     */
	    return;
	}
	sig = SIGINT;                    /* Was SIGTERM */
	if (grace_period == 0) sig = SIGKILL;
	else {
	    time (&timeval);
	    timeval += grace_period;
	    nqs_vtimer (&timeval, nqs_kill);	/* Set timer for */
	    queue->sigkill = timeval;		/* SIGKILL. */
	}
	req = queue->runset;
	while (req != (struct request *) 0) {
	    /*
	     *  It looks like this request is still running, though
	     *  it could have finished running without our hearing
	     *  about it just yet.  Send sig to all of its processes.
	     */
	    reqindex = req->reqindex;
	    if ( (Runvars+reqindex)->process_family == 0) {
		/*
		 *  The process group/family of the server
		 *  has not yet been reported in....  Queue
		 *  the signal so that it can be sent later
		 *  upon receipt of the process group/family
		 *  packet for this request.
		 */
		if ((req->status & RQF_SIGQUEUED) == 0 ||
		     (Runvars+reqindex)->queued_signal != SIGKILL) {
		    /*
		     *  Queue the signal, unless a SIGKILL
		     *  has already been queued.
		     */
		    (Runvars+reqindex)->queued_signal = sig;
		}
		req->status |= RQF_SIGQUEUED;	/* Queued signal */
		if (requeue_flag) req->status |= RQF_SIGREQUEUE;
		else req->status &= ~RQF_SIGREQUEUE;
	    }
	    else {
		/*
		 *  The process group/family of the server
		 *  is known.  Send the signal.
		 */
		if ((grace_period == 0 || requeue_flag == 0) && kill (
			-(Runvars + reqindex)->process_family, sig) == -1) {
		    if (errno != ESRCH) {
			/*
			 *  Something went really wrong!
			 */
			sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, problem);
			sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, asciierrno ());
		    }
		    /*  Else the request is already done! */
		}
		else if (requeue_flag) req->status |= RQF_SIGREQUEUE;
		else req->status &= ~RQF_SIGREQUEUE;
	    }
	    req = req->next;		/* Get the next request in the */
	}				/* running set */
}


/*** nqs_kill
 *
 *
 *	void nqs_kill():
 *
 *	Terminate ALL remaining request processes for the NQS queues that
 *	have been aborted.
 */
static void nqs_kill(void)
{

	nqs_killset (Nonnet_queueset);	/* Non-network queues */
	nqs_killset (Net_queueset);	/* Network queues only */
}


/*** nqs_killset
 *
 *
 *	void nqs_killset():
 *
 *	Zealously murder ALL remaining request processes for a set of queues
 *	that have been aborted, and whose SIGKILL times have arrived.  We
 *	sally forth, bandying our bloody axe with the words "SIGKILL" written
 *	upon it.
 */
static void nqs_killset (struct nqsqueue *queue)
{
	time_t timenow;			/* Time now */
	register short reqindex;	/* Index in the Runvars array of */
					/* running structure allocated for */
					/* the request being aborted */
	register struct request *req;	/* Ptr to request structure for */
					/* the request being aborted */

	time (&timenow);
	/*
	 *  Walk the queue set looking for aborted queues, whose SIGKILL
	 *  time has arrived.
	 */
	while (queue != (struct nqsqueue *) 0) {
	    if (queue->sigkill && queue->sigkill <= timenow) {
		/*
		 *  Swing the axe and cutlass!
		 */
		queue->sigkill = 0;	/* Clear SIGKILL abort flag */
		req = queue->runset;
		while (req != (struct request *) 0) {
		    /*
		     *  It looks like this request is still
		     *  running, though it could have finished
		     *  without our hearing about it just yet.
		     *  Send SIGKILL to all of its processes.
		     */
		    reqindex = req->reqindex;
		    if ( (Runvars+reqindex)->process_family == 0) {
			/*
			 *  The process group/family of the
			 *  server has not yet been reported
			 *  in....  Queue the SIGKILL so that
			 *  it can be sent later upon receipt
			 *  of the process group/family
			 *  packet for this request.
			 */
			req->status |= RQF_SIGQUEUED;
			(Runvars+reqindex)->queued_signal = SIGKILL;
		    }
		    else {
			/*
			 *  The process group/family of the
			 *  server is known.  Send SIGKILL.
			 */
			if (kill ( -(Runvars+reqindex)->process_family,
				    SIGKILL) == -1) {
			    if (errno != ESRCH) {
				/*
				 *  Something went really wrong!
				 */
				sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, problem);
				sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, info, asciierrno ());
			    }
			    /* Else the request is already done! */
			}
		    }
		    req = req->next;	/* Get the next request in the */
		}			/* running set */
	    }
	    else if (queue->sigkill) {
		/*
		 *  This queue has an abort SIGKILL time in the
		 *  future.  Set the virtual timer appropriately.
		 */
		nqs_vtimer (&queue->sigkill, nqs_kill);
	    }
	    queue = queue->next;	/* Get the next queue */
	}
}
