/*
 *	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     20-Nov-1991	Added support for remote deletes.
*       V01.30  JRR     19-Dec-1991	Check for TCMP returns.  
*       V01.40  JRR     16-Jan-1992	Added support for RS6000.
*       V01.5   JRR     12-Feb-1992	Fixed static routine references.
*       V01.6   JRR     12-Feb-1992	Added declaration of monsanto_header.
*       V01.7   JRR     03-Mar-1992	Added Cosmic V2 changes.
*       V01.8   JRR     05-Mar-1992	Change to using matche (like the VAX).
*       V01.9   JRR     08-Apr-1992     Added CERN enhancements.
*	V01.10	JRR	17-Jun-1992	Added header, Version 3.21
*	V01.11	JRR	06-Nov-1992	Added support for HPUX.
*	V01.12	JRR	03-Mar-1993	Added Boeing enhancment for files.
*	V01.13	JRR	04-Aug-1993	Fixed small, but embarrasing, security
*					hole.
*	V01.14	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/qdel.c,v $
 *
 * DESCRIPTION:
 *
 *	Delete a queued req, or send a signal to a running request.
 *	If the request is running, and a signal was specified to be
 *	sent, then the signal is sent to all processes in the request.
 *
 * RETURNS:
 *      0    -  all requests deleted succesfully
 *      -1   -  error in calling sequence
 *      n    -  number of requests not deleted
 *
 *      In all error cases, messages are sent to the standard output file.
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	October 14, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.14 $ $Date: 1994/03/30 20:37:04 $ $State: Exp $)
 * $Log: qdel.c,v $
 * Revision 1.14  1994/03/30  20:37:04  jrroma
 * Version 3.35.6
 *
 * Revision 1.13  93/09/10  13:57:26  jrroma
 * Version 3.35
 * 
 * Revision 1.12  93/07/13  21:34:07  jrroma
 * Version 3.34
 * 
 * Revision 1.11  92/12/22  15:43:20  jrroma
 * Version 3.30
 * 
 * Revision 1.10  92/06/17  08:45:34  jrroma
 * Version 3.21
 * 
 * Revision 1.9  92/05/06  10:45:25  jrroma
 *  Version 3.20
 * 
 * Revision 1.8  92/03/05  17:03:57  jrroma
 * Changed to use matche like the VAX.
 * 
 * Revision 1.7  92/03/03  13:37:32  jrroma
 * Added Cosmic V2 changes (which will probably not stay).
 * 
 * Revision 1.6  92/02/12  12:48:58  jrroma
 * Added declaration of monsanto_header
 * 
 * Revision 1.5  92/02/12  12:24:59  jrroma
 * Fixed references to static routines.
 * 
 * Revision 1.4  92/01/17  11:23:52  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.3  91/12/19  10:01:22  jrroma
 * Add checking for TCMP statuses.
 * 
 * Revision 1.2  91/12/05  16:54:39  jrroma
 * Finished fixes to remote deletes.
 * 
 * Revision 1.1  91/10/10  12:58:31  jrroma
 * Initial revision
 * 
 *
 */

#define MAX_USRRETTIM	15		/* Maximum user retry times */
#define	MAX_REQS	100		/* Maximum number of reqs that */
					/* can be deleted/signalled at */
					/* a time. */
#include "nqs.h"			/* NQS types and definitions */
#include <stdlib.h>
#include <signal.h>			/* Signal definitions */
#include <netdb.h>			/* Network database header file; */
#include "nqsdirs.h"			/* NQS files and directories */
#include "transactcc.h"			/* Transaction completion codes */


#ifndef __CEXTRACT__
#if __STDC__

static void qdel_cleanup ( int sig );
static void qdel_showhow ( void );
static show_version ( void );

#else /* __STDC__ */

int main (/* int argc, char **argv */);
static void qdel_cleanup (/* int sig */);
static void qdel_showhow (/* void */);
static show_version (/* void */);

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

/*
 *	Global variables:
 */
char *Qdel_prefix = "Qdel";

int   DEBUG;				/* DEBUG flag */
Mid_t Locmid;                           /* Caller's host machine-id */
char *chostname;                        /* Caller's host machine name */
uid_t cuid;                             /* Caller's userid */
char *cusername;                        /* Caller's username */
char *whom;                             /* Whom we are interested in */
struct passwd *whompw;                  /* Password entry of "whom" */
int monsanto_header;			/* Apparently we call some sho_ rtn */

/*** main
 *
 *
 *	qdel [ { -k , -<integer-signal> } ] [ -u <username> ] \
 *			[-r request-pattern -c] <request-id(s)>
 */
main (argc, argv)
int argc;
char **argv;
{

        int n_errs;                     /* Number of requests not deleted. */
	int n_reqs;			/* Number of reqs to delete/signal. */
	struct {
		long orig_seqno;	/* Sequence# for req */
		Mid_t machine_id;	/* Machine-id of originating machine */
		Mid_t target_mid;	/* target mid */
	} reqs [MAX_REQS];		/* Reqs to delete/signal */
	char **scan_reqs;		/* Scan reqs */
	char *argument;			/* Ptr to cmd line arg text */
	char *cp;			/* Scanning character ptr */
        char *hname = NULL;             /* host name */
	short sendsig;			/* Boolean non-zero if a signal */
					/* should be sent if the req is */
					/* running */
	short sig;			/* Signal to send, if the req is */
					/* running */
        struct passwd *passwd;          /* Pointer to password info */
	uid_t real_uid;			/* Real user-id */
	Mid_t local_mid;		/* local machine-id */
	char  *request = NULL;		/* Pointer to request pattern */
	int	status;
	int	confirm = 0;		/* confirm deletions with pattern */
	char	buffer[64];
	char *root_dir;                 /* Fully qualified file name */

	/*
	 *  Catch 4 common household signals:  SIGINT, SIGQUIT,SIGHUP, 
	 *  and SIGTERM.  This is quite important, because we do not want
	 *  to leave useless inter-process communication files hanging
	 *  around in the NQS directory hierarchy.  However, if we do,
	 *  it is not fatal, it will eventually be cleaned up by NQS.
	 */
	signal (SIGINT, qdel_cleanup);
	signal (SIGQUIT, qdel_cleanup);
	signal (SIGHUP, qdel_cleanup);
	signal (SIGTERM, qdel_cleanup);
        /*
         * Check to see if we want to print some debugging information.
         */
        DEBUG = 0;
        cp = getenv ("NQS_DEBUG");
        if ( (cp != NULL) && (!strcmp(cp, "yes") ) ) {
                DEBUG = 1;
                show_version();
        }
	if ( ! buildenv()) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qdel_prefix);
	    fprintf (stderr, "establish directory independent environment.\n");
	    exit (1);
	}

	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qdel_prefix);
	    fprintf (stderr, "determine root directory name.\n");
	    exit (1);
	}
	if (chdir (root_dir) == -1) {
	    fprintf (stderr, "%s(FATAL): Unable to chdir() to the NQS ",
                 Qdel_prefix);
	    fprintf (stderr, "root directory [%s].\n", root_dir);
	    relfilnam (root_dir);
	    exit (1);
	}
	relfilnam (root_dir);

	/*
	 *  On systems with named pipes, we get a pipe to the local
	 *  daemon automatically the first time we call inter().
	 */
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | ULTRIX | DECOSF | LINUX
#else
#if	BSD43
	if (interconn () < 0) {
		fprintf (stderr, "%s(FATAL): Unable to get ", Qdel_prefix);
		fprintf (stderr, "a pipe to the local daemon.\n");
		exit (-1);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
	nmap_get_mid((struct hostent * ) 0, &Locmid);
        if ((chostname = nmap_get_nam (Locmid)) == (char *) 0)    {
                fprintf (stderr, "Unable to determine name ");
                fprintf (stderr, "of local host.\n");
                exit (1);
        }

        cuid = getuid();
        if ((passwd = fetchpwuid (cuid)) == (struct passwd *) 0) {
                fprintf (stderr, "Unable to determine caller's username\n");
                exit (1);
        }
	closepwdb();
        cusername = passwd->pw_name;
        if (localmid (&Locmid) != 0) {
                fprintf (stderr, "Unable to get machine-id of local host.\n");
                exit(2);
        }
	sendsig = 0;			/* Don't kill a running req by default */
	sig = SIGINT;			/* Default signal to send */
	whom = NULL;			/* No -u flag seen */
	while (*++argv != NULL && **argv == '-') {
		argument = *argv;
		switch (*++argument) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			if (sendsig) {
				fprintf (stderr,
					 "Multiple signal specifications.\n");
				exit (-1);
			}
			cp = argument;
			sig = 0;
			while (*cp >= '0' && *cp <= '9') {
				sig *= 10;
				sig += *cp++ - '0';
			}
			if (*cp) {
				/*
				 *  A non-digit character has been
				 *  found.
				 */
				fprintf (stderr, "Invalid signal ");
				fprintf (stderr, "specification.\n");
				qdel_showhow();
			}
			sendsig = 1;	/* Send signal if req is running */
			break;
		case 'v':
			show_version();
			break;
		case 'c':
			confirm = 1;	/* confirm if using request pattern */
			break;
		case 'k':
			sendsig = 1;	/* Send signal if req is running */
			break;
		case 'u':		/* User-name specification */
			if (*++argv == NULL) {
				fprintf (stderr, "Missing username.\n");
				exit (-1);
			}
			if (whom != NULL) {
				fprintf (stderr, "Multiple -u specifications.\n");
				exit (-1);
			}
                        if ((whompw = fetchpwnam (*argv)) == NULL) {
                                fprintf (stderr, "No such user on this machine.\n");
                                exit (-1);
                        }
                        if ((nqspriv (getuid(), Locmid, Locmid)
                                & QMGR_OPER_PRIV) ||
                            whompw->pw_uid == getuid()) {
                                /*
                                 *  We have NQS operator privileges, or we
                                 *  are just going after our own requests.
                                 */
                                real_uid = passwd->pw_uid;
                        }
                        else {
                                fprintf (stderr, "Insufficient privilege ");
                                fprintf (stderr, "for -u specification.\n");
                                exit (-1);
                        }
			whom = *argv;
			break;
		case 'r':		/* request follows....*/
			if (*++argv == NULL) {
				fprintf(stderr, "Missing request pattern.\n");
				exit(-1);
			}
			if (request != NULL) {
				fprintf (stderr, "Multiple -r specifications.\n");
				exit (-1);
			}
			request = *argv;	/* save the pattern */
			break;
		default:
			fprintf (stderr, "Invalid option flag ");
			fprintf (stderr, "specified.\n");
			qdel_showhow();
		}
	}
        if (whom == NULL ) {
		strcpy(buffer, cusername);
		whom = buffer;
	}
        whompw = fetchpwnam (whom);

	if (!sendsig) {
		/*
		 *  No running req should be killed.
		 */
		sig = 0;
	}
	/*
	 *  Build the set of reqs to be deleted.
	 */
	if (request != NULL ){
		status = qdel_by_req(request, whompw->pw_uid, sig, confirm);
	} else if (*argv == NULL) {
		/*
		 *  No request-ids were specified.
		 */
		fprintf (stderr, "No request-id(s) specified.\n");
		qdel_showhow();
	} else {
		if (confirm) {
			fprintf(stderr, "Confirm only with request pattern.\n");
			qdel_showhow();
		}
		n_reqs = 0;			/* #of reqs to delete/signal */
		scan_reqs = argv;		/* Set req scan pointer */
		while (*scan_reqs != NULL &&	/* Loop to delete reqs */
		       n_reqs < MAX_REQS) {
			switch (reqspec (*scan_reqs, &reqs [n_reqs].orig_seqno,
					 &reqs [n_reqs].machine_id,
					 &reqs [n_reqs].target_mid)) {
			case -1:
				fprintf (stderr, "Invalid request-id syntax ");
				fprintf (stderr, "for request-id: %s.\n",
					*scan_reqs);
				exit (-1);
			case -2:
				fprintf (stderr, "Unknown machine for ");
				fprintf (stderr, "request-id: %s.\n",
					*scan_reqs);
				exit (-1);
			case -3:
				fprintf (stderr, "Network mapping database ");
				fprintf (stderr, "inaccessible.  Seek staff ");
				fprintf (stderr, "support.\n");
				exit (-1);
			case -4:
				fprintf (stderr, "Network mapping database ");
				fprintf (stderr, "error when parsing ");
				fprintf (stderr, "request-id: %s.\n",
					*scan_reqs);
				fprintf (stderr, "Seek staff support.\n");
				exit (-1);
			}
			/* If reqspec returns null in machine id, force to
			 * local machine id.
			 */
			if (reqs[n_reqs].machine_id == 0) 
				localmid(&reqs [n_reqs].machine_id);
			scan_reqs++;		/* One more req */
			n_reqs++;
		}
		if (*scan_reqs != NULL) {
			/*
			 *  Too many reqs were specified to be deleted.
			 */
			fprintf (stderr, "Too many requests given to ");
			fprintf (stderr, "delete.\n");
			exit (-1);
		}
		/*
		 *  Now that everything has been parsed and legitimized,
		 *  delete the specified set of requests.
		 */
                n_errs = 0;
		n_reqs = 0;
		while (*argv != NULL) {		/* Loop to delete reqs */
                    if (whompw == NULL && 
				((reqs[n_reqs].target_mid == (Mid_t) 0)||
                         	(reqs[n_reqs].target_mid == Locmid))) {
                            fprintf (stderr, "Unknown user %s on local host.\n",
                                whom);
                            fflush (stderr);
                    } else {
                        qdel_diagdel (delreq (whompw->pw_uid,
			 reqs[n_reqs].orig_seqno,
                         reqs[n_reqs].machine_id, reqs[n_reqs].target_mid, sig,
                         RQS_DEPARTING | RQS_RUNNING | RQS_STAGING |
                         RQS_QUEUED | RQS_WAITING | RQS_HOLDING |
                         RQS_ARRIVING), *argv);
		    	argv++;			/* One more req */
		        n_reqs++;
		    }
	        }
	}
	exiting();			/* Delete our comm. file */
	exit (0);
}


/*** qdel_cleanup
 *
 *
 *	Catch certain signals, and delete the inter-process
 *	communication file we have been using.
 */
static void qdel_cleanup (sig)
int sig;				/* Signal that was caught */
{
	signal (sig, SIG_IGN);		/* Ignore multiple signals */
	exiting();			/* Delete our comm. file */
}


/*** qdel_showhow
 *
 *
 *	qdel_showhow():
 *	Show how to use this command.
 */
static void qdel_showhow()
{
fprintf (stderr, "qdel -- delete NQS requests\n");
fprintf (stderr, "usage:    qdel [ { -k , -<signal#> } ] [ -u <username> ] \\ \n");
fprintf (stderr, "                  [-r request-pattern [-c]] <request-id(s)>\n");
fprintf (stderr, " -c            confirm deletion (valid only with -r switch)\n");
fprintf (stderr, " -k            kill running job by sending SIGKILL\n");
fprintf (stderr, " -<signal#>    signal number to send to process family\n");
fprintf (stderr, " -r request-pattern   regular expression pattern for request name\n");
fprintf (stderr, " -u username   username of request identifier (if not yourself)\n");
fprintf (stderr, " -v            print version information\n");
fprintf (stderr, " <request-id>  NQS request identifier (required if not using -r)\n");
exit (0);
}
static show_version()
{
	fprintf (stderr, "NQS version is %s.\n", NQS_VERSION);
}
