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

#define	MAX_REQS	100		/* Maximum number of reqs that */
					/* can be held 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 qhold_cleanup ( int sig );
static void qhold_diaghold ( long code, char *reqid );
static void qhold_showhow ( void );
static show_version ( void );

#else /* __STDC__ */

static void qhold_cleanup (/* int sig */);
static void qhold_diaghold (/* long code, char *reqid */);
static void qhold_showhow (/* void */);
static show_version (/* void */);

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

/*
 *	Global variables:
 */
char *Qhold_prefix = "Qhold";

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

	int n_reqs;			/* Number of reqs to hold. */
	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 hold */
	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, qhold_cleanup);
	signal (SIGQUIT, qhold_cleanup);
	signal (SIGHUP, qhold_cleanup);
	signal (SIGTERM, qhold_cleanup);
	if ( ! buildenv()) {
	    fprintf (stderr, "%s(FATAL): Unable to ", Qhold_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 ", Qhold_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 ",
                 Qhold_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 ", Qhold_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): ", Qhold_prefix);
				fprintf (stderr, "Unable to get machine-id");
				fprintf (stderr, "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 ");
				fprintf (stderr, "for -u specification.\n");
				exit (1);
			}
			break;
		    case 'v':
			show_version();
			break;
		    default:
			fprintf (stderr, "Invalid option flag specified.\n");
			qhold_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 held.
	 */
	if (*argv == NULL) {
		/*
		 *  No request-ids were specified.
		 */
		fprintf (stderr, "No request-id(s) specified.\n");
		qhold_showhow();
	}
	else {
		n_reqs = 0;			/* #of reqs to hold */
		scan_reqs = argv;		/* Set req scan pointer */
		while (*scan_reqs != NULL &&	/* Loop to hold 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 held.
			 */
			fprintf (stderr, "Too many requests given to ");
			fprintf (stderr, "hold.\n");
			exit (1);
		}
		/*
		 *  Now that everything has been parsed and legitimized,
		 *  hold the specified set of requests.
		 */
		n_reqs = 0;
		while (*argv != NULL) {		/* Loop to hold reqs */
			qhold_diaghold (hdlreq (0, 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);
}


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


/*** qhold_diaghold
 *
 *
 *	qhold_diaghold():
 *	Diagnose hdlreq() completion code.
 */
static void qhold_diaghold (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 ");
		printf ("handling of a\n");
		printf ("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 held.\n", reqid);
		break;
	case TCML_REQRUNNING:
		printf ("Request %s is running.\n", reqid);
		break;
	default:
		printf ("Unexpected completion code from NQS daemon.\n");
		exiting();		/* Delete communication file */
		exit (1);
	}
}


/*** qhold_showhow
 *
 *
 *	qhold_showhow():
 *	Show how to use this command.
 */
static void qhold_showhow()
{
	fprintf (stderr, "qhold -- hold queued NQS requests\n");
	fprintf (stderr, "usage:    qhold [ -u <username> ] [-v] <request-id(s)>\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\n");	
	exit (0);
}
static show_version()
{
	fprintf (stderr, "NQS version is %s.\n", NQS_VERSION);
}
