/*
 * qdel/qdel.c
 * 
 * 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.
 *
 *	Original Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	October 14, 1985.
 */

#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 <libnqs/license.h>
#include <libnqs/proto.h>
#include <stdlib.h>
#include <signal.h>			/* Signal definitions */
#include <netdb.h>			/* Network database header file; */
#include <libnqs/nqsdirs.h>		/* NQS files and directories */
#include <libnqs/transactcc.h>		/* Transaction completion codes */

#include <libsal/license.h>
#include <libsal/proto.h>
#include <unistd.h>
#include <SETUP/autoconf.h>

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

/*
 *	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)>
 */
int main (int argc, char *argv[])
{

	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 */
	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 */
	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 */

  	sal_debug_InteractiveInit(1, NULL);
  
	/*
	 *  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	HAS_BSD_PIPE
	if (interconn () < 0) {
		fprintf (stderr, "%s(FATAL): Unable to get ", Qdel_prefix);
		fprintf (stderr, "a pipe to the local daemon.\n");
		exit (-1);
	}
#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 = sal_fetchpwuid (cuid)) == (struct passwd *) 0) {
                fprintf (stderr, "Unable to determine caller's username\n");
                exit (1);
        }
	sal_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 = SIGKILL;			/* 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 = sal_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.
                                 */
                        }
                        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 = sal_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_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 (int sig)
{
	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(void)
{
  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 void show_version(void)
{
	fprintf (stderr, "NQS version is %s.\n", NQS_VERSION);
}
