/*
 *	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.3   JRR     12-Feb-1992	Fixed static declarations of routines.
*       V01.4   JRR     08-Apr-1992     Added CERN enhancements.
*	V01.5	JRR	17-Jun-1992	Added header.
*	V01.6	JRR	10-Nov-1992	Add support for HPUX.
*	V01.7	JRR	06-Apr-1993	Added support for DECOSF.
*	V01.8	JRR	18-Aug-1993	Miniscule change to includes.
*					and try to use kill and not killpg.
*	V01.9	JRR			Placeholder.
*	V01.10	JRR			Placeholder.
*	V01.11	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_aboque.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_aboque.c,v $
 *
 * DESCRIPTION:
 *
 *	Abort all running requests in an NQS queue.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.11 $ $Date: 1994/03/30 20:36:27 $ $State: Exp $)
 * $Log: nqs_aboque.c,v $
 * Revision 1.11  1994/03/30  20:36:27  jrroma
 * Version 3.35.6
 *
 * Revision 1.10  93/09/10  13:57:02  jrroma
 * Version 3.35
 * 
 * Revision 1.9  93/07/13  21:33:45  jrroma
 * Version 3.34
 * 
 * Revision 1.8  92/12/22  15:39:46  jrroma
 * Version 3.30
 * 
 * Revision 1.7  92/06/18  17:30:52  jrroma
 * Added gnu header
 * 
 * Revision 1.6  92/05/06  10:38:31  jrroma
 *  Version 3.20
 * 
 * Revision 1.5  92/03/03  17:00:04  jrroma
 * Fixed editing error with Cosmic V2 changes.
 * 
 * Revision 1.4  92/03/02  11:46:32  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.3  92/02/12  16:11:42  jrroma
 * *** empty log message ***
 * 
 * Revision 1.2  92/01/17  10:42:22  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  10:41:33  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <signal.h>
#include <errno.h>
#include "nqsxvars.h"			/* Global vars */

#ifndef __CEXTRACT__
#if __STDC__

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

#else /* __STDC__ */

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

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

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

/*** nqs_aboque
 *
 *
 *	void nqs_aboque():
 *	Abort all running requests in an NQS queue.
 */
void nqs_aboque (queue, grace_period, requeue_flag)
struct nqsqueue *queue;			/* Pointer to queue structure */
int grace_period;			/* Number of seconds to wait */
					/* after sending SIGINT to send */
					/* SIGKILL. */
short requeue_flag;			/* Boolean non-zero if aborted */
					/* requests are to be requeued */
{
	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!
			 */
			printf (problem);
			printf (info, asciierrno ());
			fflush (stdout);
		    }
		    /*  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()
{

	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 (queue)
register struct nqsqueue *queue;		/* Ptr to a queue structure */
{
	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!
				 */
				printf (problem);
				printf (info, asciierrno ());
				fflush (stdout);
			    }
			    /* 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 */
	}
}
