/*
 *	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.0   JRR     23-Mar-1992     Initial version.
*                       10-Apr-1992     Integrated CERN enhancements.
*	V01.2	JRR	15-Jun-1992	Debugging RS6000 version.
*					Added header.
*	V01.3	JRR	31-Jul-1992	Whoa! A hole you could drive
*					a truck through!
*	V01.4	JRR	21-Sep-1992	Allow managers to suspend/
*					resume others requests.
*	V01.5	JRR	06-Apr-1993	Added support for DECOSF.
*	V01.6	JRR	19-Aug-1993	Trying kill rather than killpg.
*					Remember to close file in update_request
*					(Thanks to Karsten Gaier).
*/
/*++ nqs_suspendreq.c - Network Queueing System
 *
 * DESCRIPTION:
 *
 *	Suspend or resume an NQS request.
 *
 *
 *	Author:
 *	-------
 *	John Roman, Monsanto Company.
 *	March 23, 1992.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <errno.h>
#include "transactcc.h"			/* Transaction completion codes */
#include <signal.h>
#include "nqsxvars.h"			/* NQS external vars and dirs  */
#include "nqsxdirs.h"			/* NQS external vars and dirs  */

#ifndef __CEXTRACT__
#if __STDC__

static int update_request ( int do_suspend, long orig_seqno, Mid_t orig_mid );

#else /* __STDC__ */

static int update_request (/* int do_suspend, long orig_seqno, Mid_t orig_mid */);

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

/** nqs_suspendreq
 *
 *
 *	long nqs_suspendreq():
 *	Suspend or resume an NQS request.
 *
 *	Returns:
 *		TCML_NOSUCHREQ:	 if the specified request does not
 *				 exist on this machine.
 *		TCML_NOTREQOWN:	 if the mapped user-id does not match
 *				 the current mapped user-id of the
 *				 request owner.
 *		TCML_REQRESUMED: if the request was sucessfully resumed.
 *		TCML_REQSUSPENDED:
 *				 if the request was successfully suspended.
 *		TCML_REQWASRUNNING:
 *				 if the request was already running.
 *		TCML_REQWASSUSPENDED:
 *				 if the request was already suspended.
 *
 */
long nqs_suspendreq (
	uid_t mapped_uid,		/* Mapped owner user-id */
	long orig_seqno,		/* Req sequence number */
	Mid_t orig_mid,			/* Machine-id of request */
	Mid_t target_mid,		/* Target Machine-id of request */
	int privs,			/* User privleges. */
	int do_suspend)			/* TRUE to suspend, FALSE to resume */
{
	int    state;			/* State of transaction */
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	short reqindex;			/* Request index in Runvars if */
					/* the request is running, or */
					/* index in Runvars if subrequest */
					/* is transporting parent request */
					/* to its destination via a pipe/ */
					/* network queue pair */
	int status;			/* status return integer */

	if (Debug > 2) {
	          printf("D$nqs_suspendreq: mapped_uid = %d.\n", mapped_uid);
	          printf("D$nqs_suspendreq: orig_seqno = %d.\n", orig_seqno);
	          printf("D$nqs_suspendreq: orig_mid = %d.\n", orig_mid);
	          printf("D$nqs_suspendreq: target_mid = %d.\n", target_mid);
	          printf("D$nqs_suspendreq: privs = %d.\n", privs);
	          if (do_suspend) 
			printf("D$nqs_suspendreq: action = suspend.\n");
		  else 
			printf("D$nqs_suspendreq: action = resume.\n");
		  fflush(stdout);
	}
	/*
	 *  Search for the request.
	 */
	state = RQS_RUNNING;
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
		/*
		 *  The request was not found in any of the local queues.
		 */
		return (TCML_NOSUCHREQ);
	}
	
	if ( req->v1.req.uid != mapped_uid &&
	    ( !(privs & QMGR_MGR_PRIV) && mapped_uid != 0 ) ) {
		/*
		 * If the user is not the owner or not a manger or not root, 
		 * then this request cannot be affected by the client.
		 */
		return (TCML_NOTREQOWN);
	}
	reqindex = req->reqindex;	    /* Find out here! */
	if (do_suspend) {
	    if (req->status & RQF_SUSPENDED) return (TCML_REQWASSUSPENDED);
	    printf("D$nqs_suspendreq: request to be suspended.\n");
	    fflush(stdout);
	    /* 
	     * The request is running, or is being routed by a pipe queue 
	     */
	    if (req->queue->q.type == QUE_PIPE ||
		   (req->status & RQF_PREDEPART)) {	/* ****TRIAGE**** */
		/*
		 *  The request is presently being routed by a
		 *  pipe queue.
		 *
		 *  This ****TRIAGE**** implementation does not
		 *  allow the deletion of requests presently
		 *  being routed by a pipe queue.
		 */
		return (TCML_PEERDEPART);
	    }
	    /*
	     *  Send the signal.  The signal may not necessarily
	     *  kill the request (unless it is of the SIGKILL (9)
	     *  variety).
	     */
	    if ( (Runvars+reqindex)->process_family == 0) {
		/*
		 *  The process group/family of the server
		 *  has not yet been reported in....  Queue
		 *  the signal so that it can be sent later
		 *  upon receipt of the process group/family
		 *  packet for this request.
		 */
		if ((req->status & RQF_SIGQUEUED) == 0) {
		    /*
		     *  We are NOT allowed to overwrite
		     *  any previously pending signal!
		     *  For example, nqs_aboque() may
		     *  have queued a signal for this
		     *  request.  Nqs_aboque() always wins.
		     */
		    req->status |= RQF_SIGQUEUED;
		    (Runvars + reqindex)->queued_signal = SIGSTOP;
		}
	    } else {
		/*
		 *  The process group/family of the server
		 *  is known.  Send the signal.
		 */
		kill (-(Runvars+reqindex)->process_family, SIGSTOP);
		if (Debug > 2) {
		    printf("D$nqs_suspendreq: suspended process family %d.\n", 
			(Runvars+reqindex)->process_family);
		    fflush(stdout);
		}	
	    }
	    req->status |= RQF_SUSPENDED;
	    if ( (status = update_request(do_suspend, orig_seqno, orig_mid)) 
			!= TCML_COMPLETE) {
	        req->status &= ~(RQF_SUSPENDED);
		return (status);	
	    } 
	    return(TCML_REQSUSPENDED);	        /* Req has been suspended, */
						/* or req will be suspended*/
						/* immediately upon receipt*/
						/* of the process-family */
	} else {				/* Resume request */
	    if ( !(req->status & RQF_SUSPENDED) ) 
			return (TCML_REQWASRUNNING);
	    kill (-(Runvars+reqindex)->process_family, SIGCONT);
	    req->status &= ~(RQF_SUSPENDED);
	    if ( (status = update_request(do_suspend, orig_seqno, orig_mid)) 
			!= TCML_COMPLETE) {
	        req->status |= RQF_SUSPENDED;
		return (status);	
	    } 
	    printf("D$nqs_suspendreq: request resumed.\n");
	    fflush(stdout);
	    return (TCML_REQRESUMED);
	}
}
static int update_request(
	int do_suspend,		/* True if suspend, false if resume. */
	long orig_seqno,        /* Sequence number of req */
	Mid_t orig_mid)         /* Machine-id of req */
{
	char path[MAX_PATHNAME+1];     /* Control directory pathname */
	int fd;				/* file descriptor for control file */
	struct rawreq rawreq;		/* Raw request structure */

	pack6name (path, Nqs_control, (int) (orig_seqno % MAX_CTRLSUBDIRS),
                  (char *) 0, (long) orig_seqno, 5, (long) orig_mid, 6, 0, 0);
        if ((fd = open (path, O_RDWR)) == -1) {
            /*
             *  Unable to open the control file.
             *  Errno has error code.
             */
	    printf("D$nqs_suspendreq: error opening request, errno %d.\n",
				errno);
	    fflush(stdout);
	    close (fd);
            return (TCML_INTERNERR);
        }
        if (readreq (fd, &rawreq) != 0) {
	    printf("D$nqs_suspendreq: error reading request, errno %d.\n",
				errno);
	    fflush(stdout);
	    close (fd);
            return (TCML_INTERNERR);
	}
	if (do_suspend) rawreq.flags |= RQF_SUSPENDED;
	else rawreq.flags &= ~(RQF_SUSPENDED);
        if (writereq (fd, &rawreq) != 0) {
	    printf("D$nqs_suspendreq: error writing request, errno %d.\n",
				errno);
	    fflush(stdout);
	    close (fd);
            return (TCML_INTERNERR);
	}
	close (fd);
	return (TCML_COMPLETE);
}
