/*
 *	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     20-Mar-1992     Initial version.
*					Started from base of qdel.c
*	V01.2	JRR	17-Jun-1992	Added header. Version 3.21
*	V01.3	JRR	11-Nov-1992	Added support for HPUX.
*	V01.4	JRR	04-Mar-1993	Add "unimplemented feature" -a.
*					Added Boeing enhancement for files.
*	V01.5	JRR	17-Aug-1993	Fixup usage message.
*					Remove second chdir to Nqs_root
*					(Thanks to Karsten Gaier)
*	V01.6	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/qsuspend.c,v $
 *
 * DESCRIPTION:
 *
 *	Suspend or resume a running request.
 *
 * RETURNS:
 *      0    -  all requests suspended/resumed succesfully
 *      -1   -  error in calling sequence
 *      n    -  number of requests not suspended/resumed
 *
 *      In all error cases, messages are sent to the standard output file.
 *
 *	Author:
 *	-------
 *	John Roman, Monsanto Company.
 *	March 20, 1992.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.6 $ $Date: 1994/03/30 20:37:12 $ $State: Exp $)
 */

#define MAX_USRRETTIM	15		/* Maximum user retry times */
#define	MAX_REQS	100		/* Maximum number of reqs that */
					/* can be suspended/resumed at */
					/* a time. */
#include "nqs.h"			/* NQS types and definitions */
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#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 qsuspend_cleanup ( int sig );
static void qsuspend_showhow ( void );
static void selrequest (struct qentry* qentry, struct gendescr *que_descr); 
static show_version ( void );
static suspendallreq ( void );

#else /* __STDC__ */

static void qsuspend_cleanup (/* int sig */);
static void qsuspend_showhow (/* void */);
static void selrequest (/* struct qentry* qentry, struct gendescr *que_descr */);
static show_version (/* void */);
static suspendallreq (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */
/*
 *	Global variables:
 */

int   SUSPEND;				/* True if suspend, false if resume */
int   DEBUG;				/* DEBUG flag */
char *Qsuspend_prefix;			/* Pointer to string of type of action*/
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 Mgr_priv;                           /* NQS manager privilege bits */
int monsanto_header;			/* Apparently we call some sho_ rtn */

/*** main
 *
 *
 *	qsuspend [-a] [-u username] [-v] <request-ids>
 *	qresume [-a] [-u username] [-v] <request-ids>
 */
main (argc, argv)
int argc;
char **argv;
{

        int n_errs;                     /* Number of requests not processed. */
	int n_reqs;			/* Number of reqs to suspend/resume. */
	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 suspend/resume */
	char **scan_reqs;		/* Scan reqs */
	char *argument;			/* Ptr to cmd line arg text */
	char *cp;			/* Scanning character ptr */
        char *hname = NULL;             /* host name */
        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];
        int 	mid;
	int	suspendall;		/* Flag true to suspend all */
	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, qsuspend_cleanup);
	signal (SIGQUIT, qsuspend_cleanup);
	signal (SIGHUP, qsuspend_cleanup);
	signal (SIGTERM, qsuspend_cleanup);
	/*
	 * Check to see if this is a suspend or resume.
	 */
	cp = strrchr(argv[0], '/');	/* Find last slash */
	if (cp != NULL) cp++;		/* Get to start of binary name */
	else (cp = argv[0]);		/* Otherwise use entire name */
	SUSPEND = 0;			/* Assume is a resume */
	Qsuspend_prefix = "Qresume";
	if ( !(strcmp( cp, "qsuspend")) || !(strcmp (cp, "xqsuspend")) ) {
	    SUSPEND = 1;
	    Qsuspend_prefix = "Qsuspend";
	}
        /*
         * 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;
            fprintf( stderr, "NQS version %s\n", NQS_VERSION);
        }
	if ( ! buildenv()) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qsuspend_prefix);
	    fprintf (stderr, "establish directory independent ");
	    fprintf (stderr, "environment.\n");
	    exit (1);
	}

	root_dir = getfilnam (Nqs_root, SPOOLDIR);
	if (root_dir == (char *)NULL) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qsuspend_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 ",
                 Qsuspend_prefix);
	    fprintf (stderr, "root directory.\n");
	    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 ", Qsuspend_prefix);
	    fprintf (stderr, "a pipe to the local daemon.\n");
	    exit (-1);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
        if (localmid (&Locmid) != 0) {
            fprintf (stderr, "Unable to get machine-id of local host.\n");
            exit(-1);
        }
        if ((chostname = nmap_get_nam (Locmid)) == (char *) 0)    {
            fprintf (stderr, "Unable to determine name 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;
        Mgr_priv = nqspriv (cuid, Locmid, Locmid);

	suspendall = 0;			/* By default, do not suspend/etc. all*/
	whom = NULL;			/* No -u flag seen */
	while (*++argv != NULL && **argv == '-') {
	    argument = *argv;
	    switch (*++argument) {
	    case 'a':
		suspendall++;
		break;
	    case 'v':
		show_version();
		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);
		}
		whom = *argv;
		break;
	    default:
		fprintf (stderr, "Invalid option flag specified.\n");
		qsuspend_showhow();
	    }
	}
        if (whom == NULL ) {
	    strcpy(buffer, cusername);
	    whom = buffer;
	}
        whompw = fetchpwnam (whom);

	if (!suspendall &&(*argv == NULL)) {
	    /*
	     *  No request-ids were specified.
	     */
	    fprintf (stderr, "No request-id(s) specified.\n");
	    qsuspend_showhow();
	}
	if (suspendall) {
	    suspendallreq();
	    exit (0);
	}
	/*
	 *  Build the set of reqs to be suspended/resumed.
	 */
	n_reqs = 0;			/* #of reqs to suspend/resume */
	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 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 suspended/etc.
	     */
	    fprintf (stderr, "Too many requests given to ");
	    if (SUSPEND) fprintf (stderr, "suspend.\n");
	    else fprintf (stderr, "resume.\n");
	    exit (-1);
	}
	/*
	 *  Now that everything has been parsed and legitimized,
	 *  take care of 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 {
		if (reqs[n_reqs].target_mid != Locmid) {
		    if (SUSPEND) fprintf (stderr, " Cannot suspend");
		    else fprintf (stderr, "Cannot resume");
		    fprintf (stderr, " remote request %s.\n", *argv);
		} else {
		    diagqsuspend (suspendreq (
			whompw->pw_uid,		/* Whom to do */
		 	reqs[n_reqs].orig_seqno,/* Request */
                       	reqs[n_reqs].machine_id, 
			reqs[n_reqs].target_mid, 
                       	Mgr_priv, 		/* Our privs */
			SUSPEND),		/* What to do */
			*argv);			/* Text to print */
		}
	    	argv++;			/* One more req */
	        n_reqs++;
	    }
	}
	exiting();			/* Delete our comm. file */
	exit (n_reqs);
}

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

/*** qsuspend_showhow
 *
 *
 *	qsuspend_showhow():
 *	Show how to use this command.
 */
static void qsuspend_showhow()
{
    if (SUSPEND) {
	fprintf (stderr, "qsuspend -- suspend running NQS requests\n");
	fprintf(stderr, "qsuspend [-a] [-u username] [-v] <request-ids>\n");
	fprintf (stderr, " -a            suspend all requests of user\n");
	fprintf (stderr, " -u username   username of request owner (if not yourself)\n");
	fprintf (stderr, " -v            print version information\n");
    } else {
	fprintf (stderr, "qresume -- resume suspended NQS requests\n");
	fprintf(stderr, "qresume [-a] [-u username] [-v] <request-ids>\n");
	fprintf (stderr, " -a            resume all requests of user\n");
	fprintf (stderr, " -u username   username of request owner (if not yourself)\n");
	fprintf (stderr, " -v            print version information\n");
	}
    exit (0);
}
static show_version()
{
	fprintf (stderr, "NQS version is %s.\n", NQS_VERSION);
}
/*
 * Suspend (or resume) all possible requests.
 */
static suspendallreq ( )
{
	struct confd *queuefile;  
        struct gendescr *que_descr;
        int fd;                         /* Queue ordering file descriptor */
        struct qentry cache [QOFILE_CACHESIZ];
                                        /* Queue ordering file cache buffer */
        int cacheindex;                 /* Current buffer cache index */
        int cachesize;                  /* Number of queue entries in read */
                                        /* cache buffer */
	int i;
	
	if ((queuefile = opendb (Nqs_queues, O_RDONLY)) == NULL) {
                fprintf (stderr, "%s(FATAL): Unable to open the NQS queue ",
                         Qsuspend_prefix);
                fprintf (stderr, "definition database file.\n");
                exit (-1);
        }
	que_descr = nextdb (queuefile);
        while (que_descr != (struct gendescr *)0) {
            fd = openqord (queuefile, que_descr);
            /*
             *  fd >= 0 if the queue has requests, and we successfully
             *          opened the queue ordering file.
             *  fd = -1 if the queue has no requests.
             *  fd = -2 if the queue has requests, but an error prevented
             *          us from opening the queue ordering file for
             *          the queue.
             *  fd = -3 if the queue was deleted.
             */
            if (fd < -1) break;       /* Error or queue was deleted */
	    cachesize = 0;                          /* Mark read cache as invalid*/
	    cacheindex = 0;
	    lseek (fd, (long) (que_descr->v.que.departcount *
                        sizeof (struct qentry)), 0);
            /*
             * Find running requests.
             */
            for (i = 0; i < que_descr->v.que.runcount; i++) {
                if (cacheindex >= cachesize) {
                    cachesize = read (fd, (char *) cache,
                                                  sizeof (cache));
                    cachesize /= sizeof (struct qentry);
                    cacheindex = 0;
                }

                selrequest (&cache [cacheindex], que_descr);
		cacheindex++;
            }
            que_descr = nextdb (queuefile);  /* Get the next queue */
	}

}

static void selrequest (qentry, que_descr) 
struct qentry *qentry;                  /* Queue entry describing request */
struct gendescr *que_descr;             /* Queue file queue-descriptor */
{
	struct rawreq rawreq;
	int	cfd;
	char	fake_argv[64];
	
	/*
	 * If this request is owned by me, or I am a manager, then I can 
	 * affect this request.
	 */
	if ( (qentry->uid != whompw->pw_uid) && !(Mgr_priv & QMGR_MGR_PRIV) ) return;
	cfd = getreq ( (long) qentry->orig_seqno,  qentry->orig_mid, &rawreq);
	if (cfd == -1) {
            fprintf (stderr, "%s(FATAL): Unable to open raw request for ",
                         Qsuspend_prefix);
            fprintf (stderr, "request %d.\n", qentry->orig_seqno);
            exit (-1);	    
	}
	if (qentry->orig_mid == Locmid)
		sprintf(fake_argv, "%d", qentry->orig_seqno);
	else
		sprintf(fake_argv, "%d.%s", qentry->orig_seqno, 
				getmacnam(qentry->orig_mid) );
	    
	if ( SUSPEND ) {
	    if (rawreq.flags & RQF_SUSPENDED) {
		printf("Request %s is already suspended.\n",  fake_argv);
		return;
	    }
	}
	if ( !SUSPEND ) {
	    if (!(rawreq.flags & RQF_SUSPENDED)) {
		printf("Request %s is already running.\n", fake_argv);
		return;
	    }
	}
        diagqsuspend (suspendreq (
			whompw->pw_uid,		/* Whom to do */
		 	qentry->orig_seqno,	/* Request */
                       	qentry->orig_mid,	/* originating mid */
			0,			/* local machine */
                       	Mgr_priv, 		/* Our privs */
			SUSPEND),		/* What to do */
			fake_argv);		/* Text to print */
	    
}
