/*
 *	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                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.3   JRR     12-Feb-1992	Fixed static declaration of routines.
*       V01.4   JRR     29-Apr-1992     In rmtarrive, request may be waiting
*                                       as well as arriving.
*	V01.5	JRR	17-Jun-1992	Added header.
*	V01.6	JRR	30-Jul-1992	Added some debug code for state_time.
*	V01.7	JRR	01-Feb-1993	Fixes for c prototypes.
*	V01.8	JRR	18-Aug-1993	Minor changes to includes.
*	V01.9	JRR			Placeholder.
*	V01.10	JRR			Placeholder.
*	V01.11	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_pip.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_pip.c,v $
 *
 * DESCRIPTION:
 *
 *	Pipe-queue server, network-queue server, and network server
 *	interaction update module.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	May 31, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.11 $ $Date: 1994/03/30 20:36:45 $ $State: Exp $)
 * $Log: nqs_pip.c,v $
 * Revision 1.11  1994/03/30  20:36:45  jrroma
 * Version 3.35.6
 *
 * Revision 1.10  94/02/24  21:30:54  jrroma
 * Version 3.35.3
 * 
 * Revision 1.9  93/09/10  13:57:18  jrroma
 * Version 3.35
 * 
 * Revision 1.8  93/02/05  23:16:47  jrroma
 * Version 3.31
 * 
 * Revision 1.7  92/12/22  15:41:25  jrroma
 * Version 3.30
 * 
 * Revision 1.6  92/06/18  17:31:13  jrroma
 * Added gnu header
 * 
 * Revision 1.5  92/05/06  10:41:49  jrroma
 *  Version 3.20
 * 
 * Revision 1.4  92/02/12  16:14:05  jrroma
 * *** empty log message ***
 * 
 * Revision 1.3  92/02/12  13:45:03  jrroma
 * Fixed static declaration of routines.
 * 
 * Revision 1.2  92/01/17  11:04:37  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  11:03:39  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"
#include "nqsxvars.h"
#include "transactcc.h"			/* Transaction completion codes */

#ifndef __CEXTRACT__
#if __STDC__

static void abortrequest ( struct request *request );

#else /* __STDC__ */

static void abortrequest (/* struct request *request */);

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


/*** pip_reqreceived
 *
 *
 *	long pip_reqreceived():
 *
 *	Perform the necessary updates for the specified request
 *	that has now been completely received from the local machine,
 *	or from a remote machine.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_reqreceived (orig_seqno, orig_mid)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */
	register time_t timenow;	/* Current time */
	register struct nqsqueue *queue;	/* Queue containing request */

	/*
	 *  Search for the request.
	 */
	state = RQS_ARRIVING;		/* Search only arriving state */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_reqreceived: Couldn't find %d.%lu in arriving state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_reqreceived: Couldn't find anywhere.\n");
	    else printf("D$pip_reqreceived: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
	 */
	timenow = time ((time_t *) 0);
	queue = req->queue;		/* Containing queue */
	/*
	 *  It is necessary to dequeue the request from its current
	 *  position in the arriving set of the queue.
	 */
	if (predecessor == (struct request *) 0) {
		queue->arriveset = req->next;
	}
	else predecessor->next = req->next;
	queue->q.arrivecount--;		/* One less request in the arrive */
					/* state */
	/*
	 *  The request is now significantly changing state.
	 *  Place the request in the appropriate state set
	 *  within the same queue.
	 */
	req->v1.req.state_time=timenow;	/* Adjust request state time */
	if (Debug > 2) {
		printf("D$pip_reqreceived: Setting state_time\n");
		fflush (stdout);
	}
	if ((req->status & RQF_OPERHOLD) || (req->status & RQF_USERHOLD)) {
		/*
		 *  Add the request to the hold set for this queue,
		 *  and update the NQS database image.
		 */
		a2s_a2hset (req, queue);
		udb_qorder (queue);	/* Update NQS database image */
	}
	else if (req->start_time > timenow) {
		/*
		 *  The request has a start time in the future,
		 *  and update the NQS database image.
		 */
		a2s_a2wset (req, queue);
		udb_qorder (queue);	/* Update NQS database image */
	}
	else {
		/*
		 *  Place the request in the eligible to run set.
		 *  The QUE_UPDATE bit is set by a2s_a2qset().
		 */
		a2s_a2qset (req, queue);
		switch (queue->q.type) {
		case QUE_BATCH:
			bsc_spawn();	/* Maybe spawn the request */
			break;
		case QUE_DEVICE:
			dsc_spawn();	/* Maybe spawn the request */
			break;
		case QUE_PIPE:
			psc_spawn();	/* Maybe spawn the request */
			break;
		}
		if (queue->q.status & QUE_UPDATE) {
			/*
			 *  No requests were spawned from the queue in
			 *  which the request was just completely received.
			 *  Update the NQS database image of the queue.
			 */
			udb_qorder (queue);
		}
	}
	return (TCML_COMPLETE);		/* Return complete */
}


/*** pip_rmtaccept
 *
 *
 *	long pip_rmtaccept():
 *
 *	Record that the remote queue destination for a pipe queue
 *	request has tentatively accepted the request (transaction
 *	state = RTS_PREDEPART).
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_rmtaccept (orig_seqno, orig_mid)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */

	/*
	 *  Search for the request.
	 */
	state = RQS_RUNNING;		/* Search only running state */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_rmtaccept: Couldn't find %d.%lu in running state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_rmtaccept: Couldn't find anywhere.\n");
	    else printf("D$pip_rmtaccept: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
	 */
	req->status |= RQF_PREDEPART;	/* Set the pre-depart flag */
	return (TCML_COMPLETE);		/* Return complete */
}


/*** pip_rmtarrive
 *
 *
 *	long pip_rmtarrive():
 *
 *	The specified tentatively queued request has been committed
 *	(transaction state = RTS_ARRIVE).
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_rmtarrive (orig_seqno, orig_mid)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */

	/*
	 *  Search for the request.
	 */
        fflush(stdout);
	state = RQS_ARRIVING | RQS_WAITING;		
	                   /* Search only arriving state (and also waiting)*/
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_rmtarrive: Couldn't find %d.%lu in arriving or waiting state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_rmtarrive: Couldn't find anywhere.\n");
	    else printf("D$pip_rmtarrive: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
	 */
	req->status &= ~RQF_PREARRIVE;	/* Clear the pre-arrive flag */
	return (TCML_COMPLETE);		/* Return complete */
}


/*** pip_rmtdepart
 *
 *
 *	long pip_rmtdepart():
 *
 *	Record that the remote queue destination for the specified
 *	pipe queue request has committed the request for arrival.
 *
 *	****TRIAGE****
 *	Until network queues are implemented, this procedure is
 *	a no-op.  When network queues are implemented, this
 *	procedure will clear the pre-depart flag for the request,
 *	and move the request into the depart set of the containing
 *	pipe queue.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_rmtdepart (orig_seqno, orig_mid)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */

	/*
	 *  Search for the request.
	 */
	state = RQS_RUNNING;		/* Search only routing state */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_rmtdepart: Couldn't find %d.%lu in running state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_rmtdepart: Couldn't find anywhere.\n");
	    else printf("D$pip_rmtdepart: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
	 *
 	 *  ****TRIAGE****
	 *  Until network queues are implemented, this procedure is
	 *  a no-op.  When network queues are implemented, this
	 *  procedure will clear the pre-depart flag for the request,
	 *  and move the request into the depart set of the containing
	 *  pipe queue.
	 */
	return (TCML_COMPLETE);		/* Return complete */
}


/*** pip_rmtstasis
 *
 *
 *	long pip_rmtstasis():
 *
 *	Record that the remote queue destination for a pipe queue
 *	request has been aborted.  The request is now free to be
 *	targeted to any destination.  The request state WAS
 *	RTS_PREDEPART, and has now been restored to RTS_STASIS.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_rmtstasis (orig_seqno, orig_mid)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */

	/*
	 *  Search for the request.
	 */
	state = RQS_RUNNING;		/* Search only running state */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_rmtstasis: Couldn't find %d.%lu in running state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_rmtstasis: Couldn't find anywhere.\n");
	    else printf("D$pip_rmtstasis: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
	 */
	req->status &= (~RQF_PREDEPART); /* Clear the pre-depart flag */
	return (TCML_COMPLETE);
}


/*** pip_schedulereq
 *
 *
 *	long pip_schedulereq():
 *
 *	Adjust the in-memory version of the start-after time for the
 *	specified request WITHOUT modifying the start-after time
 *	defined in the request control file.  This allows pipe and
 *	network queues to delay delivery of a request until a more
 *	appropriate time.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHREQ:	if the specified request does not
 *				exist on this machine.
 *
 */
long pip_schedulereq (orig_seqno, orig_mid, waittime)
long orig_seqno;			/* Req sequence number */
Mid_t orig_mid;				/* Machine-id of request */
long waittime;				/* Number of seconds to wait */
{
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	struct request *req;		/* Ptr to request structure for */
					/* located req, if found */
	int state;			/* Request state */
	struct rawreq rawreq;		/* Raw request structure. */
	int rawreq_fd;			/* Fd left when rawreq opened */

	/*
	 *  Search for the request.
	 */
	state = RQS_RUNNING;		/* Search only routing state */
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) {
	    /*
	     *  The request was not found in any of the local queues.
	     */
	    printf("D$pip_schedulereq: Couldn't find %d.%lu in running state.\n",
		   orig_seqno, orig_mid);
	    state = -1;     /* look in all states */
	    if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor,
			       &state)) == (struct request *) 0) 
		printf("D$pip_schedulereq: Couldn't find anywhere.\n");
	    else printf("D$pip_schedulereq: Found in state %d\n", state);
	    fflush (stdout);
	    return (TCML_NOSUCHREQ);
	}
	/*
	 *  The request has been located.
 	 *  Set the start-after time
	 */
	if (req->status & RQF_AFTER) {
	    if ( (rawreq_fd = getrreq(orig_seqno,  orig_mid, &rawreq)) == -1) {
		printf("E$Error reading rawreq,  Mid: %d,  Seqno: %d\n", 
			orig_mid,  orig_seqno);
		fflush(stdout);
		req->start_time = time ((time_t *) 0) + waittime;
	    } else {
		req->start_time = rawreq.start_time;
		close (rawreq_fd);
	    }
	} else
	    req->start_time = time ((time_t *) 0) + waittime;	
	return (TCML_COMPLETE);		/* Return complete */
}


/*** pip_timeout
 *
 *
 *	void pip_timeout():
 *	Abort uncommitted timed-out remote transactions.
 */
void pip_timeout ()
{

	register struct nqsqueue *queue;	/* Queue set walking */
	register struct request *preq;	/* Previous request */
	register struct request *req;	/* Request set walking */
	register struct request *next;	/* next request */
	register time_t timenow;	/* Current GMT time */
	time_t abortat;			/* Time to abort transaction at */

	queue = Nonnet_queueset;	/* Non-network queue set */
	timenow = time ((time_t *) 0);	/* Get current time */
	while (queue != (struct nqsqueue *) 0) {
	    preq = (struct request *) 0;
	    req = queue->arriveset;	/* Requests in the arriving set */
	    while (req != (struct request *) 0) {
		next = req->next;
		if (req->status & RQF_PREARRIVE) {
		    /*
		     *  This request is in the pre-arrive state.
		     */
		    abortat = req->v1.req.state_time+TRANS_TIMEOUT;
		    if (abortat <= timenow) {
			/*
			 *  This request was not committed for
			 *  arrival within the specified time
			 *  limits.  Abort the transaction.
			 */
			if (Debug) abortrequest (req);
			if (preq == (struct request *) 0) {
			    queue->arriveset = req->next;
			}
			else preq->next = req->next;
			queue->q.status |= QUE_UPDATE;
					/* Database image is */
					/* now out of date */
			queue->q.arrivecount--; 	/* Dequeue request */
			nqs_disreq (req, 1);	        /* Delete data files */
		    }			                /* as well */
		    else {
			/*
			 *  Unless this request is committed
			 *  before the transaction timeout
			 *  time sometime in the future, this
			 *  request transaction will have to
			 *  be aborted.
			 */
			nqs_vtimer (&abortat, pip_timeout);
			preq = req;	/* Remember prev req */
		    }
		}
		else preq = req;/* Remember previous request */
		req = next;	/* Check the next request */
	    }
	    if (queue->q.status & QUE_UPDATE) {
		/*
		 *  The NQS database image of this queue is now
		 *  out of date, and needs to be updated.
		 */
		udb_qorder (queue);
	    }
	    queue = queue->next;	/* Check the next queue */
	}
}


/*** abortrequest
 *
 *
 *	void abortrequest():
 *
 *	Display debug diagnostic to report aborted remote request queueing
 *	transaction.
 */
static void abortrequest (request)
register struct request *request;	/* Request being aborted */
{
	printf ("D$Remote queueing transaction of %1ld.%s aborted.\n",
		request->v1.req.orig_seqno,
		fmtmidname (request->v1.req.orig_mid));
	fflush (stdout);
}
