/*
 *	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  CB                      Initial version.
*       V01.20  JRR     09-Apr-1992	Monsanto modifications.
*	V01.3   JRR	26-May-1992	Added support for RS6000.
*					Added header.
*	V01.4	JRR	10-Nov-1992	Added support for HPUX.
*	V01.5	JRR	08-Mar-1993	Added Boeing enhancement for files.
*	V01.6	JRR	17-Aug-1993	Fixup usage message.
*	V01.7	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ qrls.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/qrls.c,v $
 *
 * DESCRIPTION:
 *
 *	Release a held request.
 *
 *
 *	Author:
 *	-------
 *	Christian Boissat, CERN, Geneva, Switzerland.
 *	January 6, 1992.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.7 $ $Date: 1994/03/30 20:37:09 $ $State: Exp $)
 * $Log: qrls.c,v $
 * Revision 1.7  1994/03/30  20:37:09  jrroma
 * Version 3.35.6
 *
 * Revision 1.6  93/09/10  13:57:30  jrroma
 * Version 3.35
 * 
 * Revision 1.5  93/07/13  21:34:12  jrroma
 * Version 3.34
 * 
 * Revision 1.4  92/12/22  15:43:25  jrroma
 * Version 3.30
 * 
 * Revision 1.3  92/06/17  08:45:40  jrroma
 * Version 3.21
 * 
 * Revision 1.2  92/05/06  10:46:19  jrroma
 *  Version 3.20
 * 
 * Revision 1.1  92/04/09  12:52:57  jrroma
 * Initial revision
 * 
 *
 */


#define	MAX_REQS	100		/* Maximum number of reqs that */
					/* can be released at a time. */
#include "nqs.h"			/* NQS types and definitions */
#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 qrls_cleanup ( int sig );
static void qrls_diagrel ( long code, char *reqid );
static void qrls_showhow ( void );
static show_version ( void );

#else /* __STDC__ */

static void qrls_cleanup (/* int sig */);
static void qrls_diagrel (/* long code, char *reqid */);
static void qrls_showhow (/* void */);
static show_version (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */
/*
 *	Global variables:
 */
char *Qrls_prefix = "Qrls";

/*** main
 *
 *
 *	qrls [ -u <username> ] <request-id(s)>
 */
main (argc, argv)
int argc;
char **argv;
{

	int n_reqs;			/* Number of reqs to release. */
	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 release */
	struct passwd *passwd;		/* Password structure ptr */
	char **scan_reqs;		/* Scan reqs */
	char *argument;			/* Ptr to cmd line arg text */
	uid_t real_uid;			/* Real user-id */
	Mid_t local_mid;		/* local machine-id */
	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, qrls_cleanup);
	signal (SIGQUIT, qrls_cleanup);
	signal (SIGHUP, qrls_cleanup);
	signal (SIGTERM, qrls_cleanup);
	if ( ! buildenv()) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qrls_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 ", Qrls_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 ",
                 Qrls_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 | SYS53 | ULTRIX | DECOSF | IBMRS | LINUX
#else
#if	BSD43
	if (interconn () < 0) {
	    fprintf (stderr, "%s(FATAL): Unable to get ", Qrls_prefix);
	    fprintf (stderr, "a pipe to the local daemon.\n");
	    exit (1);
	}
#else
BAD SYSTEM TYPE
#endif
#endif
	passwd = NULL;			/* No -u flag seen */
	while (*++argv != NULL && **argv == '-') {
	    argument = *argv;
	    switch (*++argument) {
	        case 'u':		/* User-name specification */
		    if (*++argv == NULL) {
			fprintf (stderr, "Missing username.\n");
			exit (1);
		    }
		    if (passwd != NULL) {
			fprintf (stderr, "Multiple -u specifications.\n");
			exit (1);
		    }
		    if ((passwd = fetchpwnam (*argv)) == NULL) {
			fprintf (stderr, "No such user on this ");
			fprintf (stderr, "machine.\n");
			exit (1);
		    }
		    if (localmid (&local_mid) != 0) {
			fprintf (stderr, "%s(FATAL): ", Qrls_prefix);
			fprintf (stderr, "Unable to get machine-id of local host.\n");
			exit(1);
		    }
		    if ((nqspriv (getuid(), local_mid, local_mid)
				& QMGR_OPER_PRIV) ||
			passwd->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 for -u specification.\n");
			exit (1);
		    }
		    break;
		case 'v':
		    show_version();
		    break;
		default:
		    fprintf (stderr, "Invalid option flag specified.\n");
		    qrls_showhow();
		    exit(1);
	     }
	}
	if (passwd == NULL) {
	    /*
	     *  No username specified.  We assume the invoker.
	     */
	    real_uid = getuid();		/* Get real user-id */
	}
	else closepwdb();		/* Close account/password database */
	/*
	 *  Build the set of reqs to be released.
	 */
	if (*argv == NULL) {
	    /*
	     *  No request-ids were specified.
	     */
	    fprintf (stderr, "No request-id(s) specified.\n");
	    qrls_showhow();
	}
	else {
	    n_reqs = 0;			/* #of reqs to release */
	    scan_reqs = argv;		/* Set req scan pointer */
	    while (*scan_reqs != NULL && 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 released.
		 */
		fprintf (stderr, "Too many requests given to release.\n");
		exit (1);
	    }
	    /*
	     *  Now that everything has been parsed and legitimized,
	     *  release the specified set of requests.
	     */
	    n_reqs = 0;
	    while (*argv != NULL) {		/* Loop to release reqs */
		qrls_diagrel (hdlreq (1, real_uid, reqs [n_reqs].orig_seqno,
				 reqs [n_reqs].machine_id), *argv);
		argv++;			/* One more req */
		n_reqs++;
	    }
	}
	exiting();			/* Delete our comm. file */
	exit (0);
}


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


/*** qrls_diagrel
 *
 *
 *	qrls_diagrel():
 *	Diagnose hdlreq() completion code.
 */
static void qrls_diagrel (code, reqid)
long code;				/* Transaction completion code */
char *reqid;				/* Character version of request-id */
{
	switch (code) {
	case TCML_INTERNERR:
	    printf ("Internal error.\n");
	    exiting();		/* Delete communication file */
	    exit (1);
	case TCML_NOESTABLSH:
	    printf ("Unable to establish inter-process communications ");
	    printf ("with NQS daemon.\n");
	    printf ("Seek staff support.\n");
	    exiting();		/* Delete communication file */
	    exit (1);
	case TCML_NOLOCALDAE:
	    printf ("The NQS daemon is not running.\n");
	    printf ("Seek staff support.\n");
	    exiting();		/* Delete communication file */
	    exit (1);
	case TCML_NOSUCHREQ:
	    printf ("Request %s does not exist.\n", reqid);
	    break;
	case TCML_NOTREQOWN:
	    printf ("Not owner of request %s.\n", reqid);
	    break;
	case TCML_PEERDEPART:
	    printf ("Request %s is presently being routed by a ", reqid);
	    printf ("pipe queue,\n");
	    printf ("and this NQS implementation does not support the\n");
	    printf ("handling of a request under such conditions.\n");
	    break;
	case TCML_PROTOFAIL:
	    printf ("Protocol failure in inter-process communications ");
	    printf ("with NQS daemon.\n");
	    printf ("Seek staff support.\n");
	    exiting();		/* Delete communication file */
	    exit (1);
	case TCML_COMPLETE:
	    printf ("Request %s has been released.\n", reqid);
	    break;
	case TCML_REQRUNNING:
	    printf ("Request %s is running.\n", reqid);
	    break;
	case TCML_REQNOTHELD:
	    printf ("Request %s is not on hold.\n", reqid);
	    break;
	default:
	    printf ("Unexpected completion code from NQS daemon.\n");
	    exiting();		/* Delete communication file */
	    exit (1);
    }
}


/*** qrls_showhow
 *
 *
 *	qrls_showhow():
 *	Show how to use this command.
 */
static void qrls_showhow()
{
    fprintf (stderr, "qrls -- release held NQS requests\n");
    fprintf (stderr, "usage:    qrls [-u username] [-v] <request-id(s)>\n");
    fprintf (stderr, " -u username   username of request owner (if not yourself)\n");
    fprintf (stderr, " -v            print version information\n");
    fprintf (stderr, " <request-id>  NQS request identifier\n");
    exit (0);
}
static show_version()
{
    fprintf (stderr, "NQS version is %s.\n", NQS_VERSION);
}
