/*
 *	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 declaration of static routines.
*       V01.4   JRR     02-Mar-1992	Added Cosmic V2 changes.
*       V01.5   JRR     09-Apr-1992     Added CERN enhancements.
*	V01.6	JRR	26-May-1992	Fixed support for RS6000.
*					Fixed up nqsacct record.
*					Added header.
*	V01.7	JRR	21-Sep-1992	Added remote request completion
*					processing.
*	V01.8	JRR	06-Apr-1993	Added support for DECOSF.
*	V01.9	JRR	20-Jul-1993	Added auto-install support.
*	V01.10	JRR	28-Feb-1994	Added support for SOLARIS.
*/
/*++ nqs_reqcom.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_reqcom.c,v $
 *
 * DESCRIPTION:
 *
 *	Process an NQS request completion event.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	August 12, 1985.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.10 $ $Date: 1994/03/30 20:36:49 $ $State: Exp $)
 * $Log: nqs_reqcom.c,v $
 * Revision 1.10  1994/03/30  20:36:49  jrroma
 * Version 3.35.6
 *
 * Revision 1.9  93/09/10  13:57:19  jrroma
 * Version 3.35
 * 
 * Revision 1.8  93/07/13  21:33:58  jrroma
 * Version 3.34
 * 
 * Revision 1.7  92/12/22  15:41:28  jrroma
 * Version 3.30
 * 
 * Revision 1.6  92/06/18  17:31:17  jrroma
 * Added gnu header
 * 
 * Revision 1.5  92/05/06  10:42:43  jrroma
 *  Version 3.20
 * 
 * Revision 1.4  92/03/02  15:44:18  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.3  92/02/12  13:52:20  jrroma
 * Fixed declaration of static routines.
 * 
 * Revision 1.2  92/01/17  11:08:52  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  11:08:14  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <errno.h>
#include <pwd.h>
#include "nqsxvars.h"			/* NQS global variables */
#include "nqsacct.h"
#include "netpacket.h"			/* For NPK_RREQCOM */
#include "transactcc.h"

#ifndef __CEXTRACT__
#if __STDC__
static int get_req_count ( void );
static void requeue ( struct request *req );
static void stopallqueues ( struct nqsqueue *serverq );
static void stopdev ( struct device *device, struct nqsqueue *serverq );
static long tell_nqs_scheduler ( int orig_seqno, Mid_t orig_mid, struct request *request );

#else /* __STDC__ */
static int get_req_count ( /* void */  );
static void requeue (/* struct request *req */);
static void stopallqueues (/* struct nqsqueue *serverq */);
static void stopdev (/* struct device *device, struct nqsqueue *serverq */);
static long tell_nqs_scheduler (/* int orig_seqno, Mid_t orig_mid, struct request *request */);

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

/*** nqs_reqcom
 *
 *
 *	void nqs_reqcom():
 *	Process an NQS request completion event.
 */
#if	HPUX | SGI | SYS52 | IBMRS | SOLARIS | LINUX
void nqs_reqcom (orig_seqno, orig_mid, exitcode, tms, queuename)
long orig_seqno;			/* Request original sequence# */
Mid_t orig_mid;				/* Request original machine-id */
int exitcode;				/* Exit code */
struct tms *tms;			/* CPU time usage by request */
char *queuename;			/* Queuename for which request */
					/* is reporting an exit code */
#else
#if	BSD43 | ULTRIX | DECOSF
void nqs_reqcom (orig_seqno, orig_mid, exitcode, rusage, queuename)
long orig_seqno;			/* Request original sequence# */
Mid_t orig_mid;				/* Request original machine-id */
int exitcode;				/* Exit code */
struct rusage *rusage;			/* Resource utilization by request */
char *queuename;			/* Queuename for which request */
					/* is reporting an exit code */
#else
BAD SYSTEM TYPE
#endif
#endif
{

	int childpid;			/* Process-id of exited child */
	short free_request_files;	/* BOOLEAN delete files associated */
					/* with the request flag.  This */
					/* flag is NEVER true without */
					/* free_request_struct ALSO being */
					/* true */
	short free_request_struct;	/* BOOLEAN delete request struct */
					/* for request flag */
	register struct nqsqueue *serverq;	/* Server was handling this queue */
	register struct request *req;	/* Request structure */
	struct request *predecessor;	/* Predecessor in request set in */
					/* queue */
	register struct device *device;	/* Device handling device request */
        register struct qcomplex *qcomplex; /* Queue complex pointer */
        register int i;
        int fd_acct;                    /* Accounting file descriptor */
        struct nqsacct_fin1 acct_fin1;  /* Accounting structure to report */
                                        /* Cpu usage                      */


	/*
	 *  Wait for a shepherd process to exit.
	 */
	while ((childpid = wait ((int *) 0)) == -1 && errno == EINTR)
		;
	/*
	 *  Childpid has the process-id of the exited child.
	 *  See if it was (heaven forbid), the NQS network
	 *  daemon or log daemon process!
	 */
	if (Logdaepid == childpid) {
		/*
		 *  The NQS log daemon exited!
		 *  It is better to leave the cave if your light burns out.
		 */
		nqs_abort ();
	}
	if (Netdaepid == childpid) {
		/*
		 *  The NQS network daemon exited!
		 */
		if (Shutdown) {
			printf ("I$Network daemon has exited.\n");
		}
		else {
			printf ("W$Network daemon has crashed.\n");
			printf ("W$No more network requests will be ");
			printf ("received.\n");
		}
		fflush (stdout);
		Netdaepid = 0;		/* Network daemon is gone */
		/*
		 *  Wait again to collect an NQS shepherd process.
		 */
		while ((childpid = wait ((int *) 0)) == -1 && errno == EINTR)
			;
	}
	if (childpid == -1) {		/* Wait call failed */
		if (errno == ECHILD) {
			/*
			 *  We were sent a message that an NQS request
			 *  server completed, but we have no servers!
			 *  No children!  None!
			 */
			printf ("E$Spurious request completion ");
			printf ("message received.\n");
			fflush (stdout);
			return;
		}
		else {
			printf ("F$Wait() call in nqs_reqcom.c ");
			printf ("failed.\n");
			nqs_abort();
		}
	}
	if (Debug > 2) {
		printf ("D$Nqs_reqcom() picked up child %1d.\n", childpid);
		fflush (stdout);
	}
	/*
	 *  Locate the queue containing the completed request.
	 */
	serverq = nqs_fndnnq (queuename);
	if (serverq == (struct nqsqueue *) 0) {
		/*
		 *  Bad queuename reported by shepherd!
		 */
		printf ("F$Bad queue name reported by request shepherd.\n");
		errno = 0;		/* Not a system call error */
		nqs_abort();
	}
	/*
	 *  Update resource usage statistics.
	 */
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	/*
	 *  Update system call time.
	 */
	serverq->q.ru_stime += tms->tms_stime;
	serverq->q.ru_stime += tms->tms_cstime;
	/*
	 *  Update user-space time.
	 */
	serverq->q.ru_utime += tms->tms_utime;
	serverq->q.ru_utime += tms->tms_cutime;
#else
#if	BSD43 | ULTRIX | DECOSF
	/*
	 *  Update system call time.
	 */
	serverq->q.ru_stime_usec += rusage->ru_stime.tv_usec;
	if (serverq->q.ru_stime_usec > 1000000) {
		serverq->q.ru_stime_usec -= 1000000;
		serverq->q.ru_stime += 1;
	}
	serverq->q.ru_stime += rusage->ru_stime.tv_sec;
	/*
	 *  Update user-space time.
	 */
	serverq->q.ru_utime_usec += rusage->ru_utime.tv_usec;
	if (serverq->q.ru_utime_usec > 1000000) {
		serverq->q.ru_utime_usec -= 1000000;
		serverq->q.ru_utime += 1;
	}
	serverq->q.ru_utime += rusage->ru_utime.tv_sec;
#else
BAD SYSTEM TYPE
#endif
#endif
	/*
	 *  If the request was routed by a pipe queue, then we must
	 *  do quite a bit of extra work....
	 */
	if ((exitcode & 0003) == 3) {
		/*
		 *  The request is presently in the arriving state and
		 *  was successfully routed by a local pipe queue.
		 *  Locate the request, and modify the state of the
		 *  request as appropriate (provided that the request
		 *  has not been deleted).
		 */
		pip_reqreceived (orig_seqno, orig_mid);
	}
	else {
		/*
		 *  The request is presently still in the running state.
		 */
		predecessor = (struct request *) 0;
		req = serverq->runset;
		while (req != (struct request *) 0 &&
		      (req->v1.req.orig_seqno != orig_seqno ||
		       req->v1.req.orig_mid != orig_mid)) {
			/*
			 *  Keep searching.
			 */
			predecessor = req;	/* Remember predecessor */
			req = req->next;	/* Examine the next request */
		}
		if (req == (struct request *) 0) {
			/*
			 *  Request not found!
			 */
			printf ("F$Request not found in nqs_reqcom().\n");
			errno = 0;		/* Not a system call error */
			nqs_abort();		/* Abort execution */
		}
		/*
		 *  Remove the request from its current position in the
		 *  queue.
		 */
		if (predecessor == (struct request *) 0) {
			serverq->runset = req->next;
		}
		else predecessor->next = req->next;
		serverq->q.runcount--;		/* One less running request */
                if (serverq->q.type == QUE_BATCH) {
                        for (i = MAX_COMPLXSPERQ; --i >= 0;) {
                                qcomplex = serverq->v1.batch.qcomplex[i];
                                if (qcomplex == (struct qcomplex *)0) continue;
                                qcomplex->runcount--;
                        }
                }
                /* addition by Intergraph Bill Mar - 122/08/89 TAC */
                if (serverq->q.type == QUE_DEVICE) {
                        for (i = MAX_COMPLXSPERQ; --i >= 0;) {
                                qcomplex = serverq->v1.device.qcomplex[i];
                                if (qcomplex == (struct qcomplex *)0) continue;
                                qcomplex->runcount--;
                        }
                }

		serverq->q.status |= QUE_UPDATE;/* Database update required */
		/*
		 *  Free up the allocated entry in the Runvars[] array for the
		 *  recently completed request.
		 */
		(Runvars + req->reqindex)->allocated = 0;
	}
	/*
	 *  Req points to the request structure for the request that
	 *  just sent its request completion packet to us.
	 *
	 *  Serverq points to the queue structure within which a
	 *  completed request is queued (or was queued).
	 */
        /* --------------------------------------
         * Report cpu, usage etc. for a batch queue;
         */
        if (serverq -> q.type == QUE_BATCH ) {
                fd_acct = open(NQSACCT_FILE, O_WRONLY|O_APPEND|O_CREAT, 0644);
                if (fd_acct < 0) {
                        fprintf (stderr, "E$Error opening NQS account");
                        fprintf (stderr, " file;  Errno = %d\n", errno);
                        fflush (stderr);
                } else {
                        bytezero((char *)&acct_fin1, sizeof(acct_fin1));
                        acct_fin1.h.type = NQSACCT_FIN_1;
                        acct_fin1.h.length = sizeof(acct_fin1);
                        acct_fin1.h.jobid = (Runvars + req->reqindex)->process_family;
                        strncpy(acct_fin1.user,getusenam(req->v1.req.uid),
                                sizeof(acct_fin1.user));
                        strncpy(acct_fin1.queue, serverq->q.namev.name,
                                sizeof(acct_fin1.queue));
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
                        acct_fin1.tms_stime = tms->tms_stime + tms->tms_cstime;
                        acct_fin1.tms_utime = tms->tms_utime + tms->tms_cutime;
#else
#if     BSD43 | ULTRIX | DECOSF
                        acct_fin1.s_sec = rusage->ru_stime.tv_sec;
                        acct_fin1.s_usec = rusage->ru_stime.tv_usec;
                        acct_fin1.u_sec = rusage->ru_utime.tv_sec;
                        acct_fin1.u_usec = rusage->ru_utime.tv_usec;
#else
BAD SYSTEM TYPE
#endif
#endif
                        acct_fin1.orig_mid = req->v1.req.orig_mid;
			acct_fin1.seqno = req->v1.req.orig_seqno;
			acct_fin1.fin_time = time ((time_t *) 0);
                }
                write(fd_acct, &acct_fin1, sizeof(acct_fin1));
                close(fd_acct);
        }
        /*-----------------------*/

	if (serverq->q.type == QUE_DEVICE) {
		/*
		 *  Identify the device that was servicing the recently
		 *  completed request.  The internal representation of
		 *  the device structure is updated to indicate that
		 *  the device is inactive, and that no current request
		 *  is being serviced by the device.  However, the NQS
		 *  database image for the device is NOT updated.
		 */
		device = Devset;
		while (device != (struct device *) 0 &&
		       device->curreq != req) {
			device = device->next;
		}
		if (device == (struct device *) 0) {
			/*
			 *  We did not find the device serving what
			 *  supposedly was a completed device req!
			 */
			printf ("F$NQS internal error.\n");
			printf ("I$Unable to find device that handled ");
			printf ("completed\n");
			printf ("I$device request.\n");
			fflush (stdout);
			nqs_abort();
		}
		device->status &= ~DEV_ACTIVE;	/* Device no longer active */
		device->curreq = (struct request *) 0;
						/* No current request */
		device->curque = (struct nqsqueue *) 0;
						/* No current queue */
	}
	/*
	 *  Based upon the request exitcode status, we must do
	 *  several things.  The exitcode bits have the following
	 *  definitions:
	 *
	 *  .---------------.
 	 *  |   |     | |   |	Request server shepherd process
	 *  |7|6|5|4|3|2|1|0|	exit code (of 8 bits)
	 *  `---------------'
	 *
	 *  Bits 1..0: contain the request disposition code:
	 *		0: Delete the request, provided that bit 2
	 *		   is clear, or the request is not to be
	 *		   requeued upon signal termination, or the
	 *		   request is not restartable.  Otherwise,
	 *		   the request must be requeued for restart.
	 *		1: Place the request in the failed directory;
	 *		2: Requeue the request for retry.
	 *		3: Request queued via local pipe queue.
	 *
	 *  Bit  2:    exit status:
	 *		0: Request exited via exit();
	 *		1: Request aborted by receipt of signal.
	 *
	 *  Bits 5..3: contain additional action bits which can
	 *	       be or'ed together as necessary.
	 *		1: Stop the device, that the request was
	 *		   running on, marking the device as
	 *		   failed;
	 *		2: Stop the queue that the request was
	 *		   (or is) residing in;
	 *		4: Stop all queues.
	 *
	 *  Bits 7..6: RESERVED for future use.
	 *
	 */
	switch (exitcode & 0003) {	/* Bits 1..0 */
	case 0: /*
		 *  Request completed normally.
		 */
		if ((req->status & RQF_SIGREQUEUE) &&
		    (req->status & RQF_RESTARTABLE) &&
		    (exitcode & 0004)) {
			/*
			 *  Requeue the request as ready to run.
			 *  The request must not be deleted.
			 */
			free_request_files = 0;
			free_request_struct = 0;
			requeue (req);
		}
		else {
			/*
			 *  The request should be deleted--later
			 *  (see below).
			 */
			free_request_files = 1;
			free_request_struct = 1;
		}
		break;
	case 1:	/*
		 *  The request is to be placed in the failed
		 *  directory.  The files associated with the
		 *  the request should be saved, but the memory-
		 *  resident information on the request should
		 *  be discarded.
		 */
		free_request_files = 0;		/* Save request files but */
		free_request_struct = 1;	/* free request structure */
		break;				/* Place req. in failed dir */
	case 2:	/*
		 *  The request is to be requeued for retry.
		 */
		free_request_files = 0;		/* The request must not */
		free_request_struct = 0;	/* be deleted. */
		requeue (req);			/* Requeue */
		break;
	case 3:	/*
		 *  The request is to be simply left-alone.
		 */
		free_request_files = 0;		/* The request must not */
		free_request_struct = 0;	/* be deleted. */
		break;
	}
	/*
	 *  Check for extra operations to be done depending
	 *  on the values of bits [5..3].
	 */
	if (exitcode & 0010) {
		/*
		 *  Stop the device that was servicing the request
		 *  and mark it as failed.
		 */
		if (serverq->q.type != QUE_DEVICE) {
			printf ("E$Shepherd process specified ");
			printf ("device shutdown for non-device ");
			printf ("request.\n");
			fflush (stdout);
		}
		else stopdev (device, serverq);
	}
	if (exitcode & 0020) {
		/*
		 *  Stop the queue that the request was in (or is
		 *  residing in).
		 */
		printf ("I$Stopping queue: %s.\n",
			serverq->q.namev.name);
		fflush (stdout);
		serverq->q.status &= ~QUE_RUNNING;
	}
	if (exitcode & 0040) {			/* Stop all queues */
		stopallqueues (serverq);	/* but don't update */
						/* database image of */
	}					/* the server queue */
	/*
	 * Check to see if we need to do an auto-install.
	 * If this was the only running request, then check further.
	 */
	if (Gblbatcount == 1) nqs_autoinst();
	/*
	 *  Inform proper scheduler of the request completion.
	 */
	switch (serverq->q.type) {
	case QUE_BATCH:			/* The req was a batch req */
		Gblbatcount--;		/* One less batch request */
		/* bsc_reqcom (req); */	/* Notify batch scheduler */
		nqs_wakreq();		/* See if there is something to run */
		tell_nqs_scheduler(orig_seqno, orig_mid, req);
		break;
	case QUE_DEVICE:		/* The req was a device req */
		/*
		 *  Delete the request and remove it from the queue.
		 */
		udb_device (device);	/* Update database image */
		if (device->status & DEV_FAILED) {
			/*
			 *  The device failed to successfully handle
			 *  the request.  However, it may be possible
			 *  for another device serving the same queue
			 *  to handle the request.
			 */
			dsc_spawn();	/* Maybe spawn it again */
		}			/* on a different device */
		else {
			/*
			 *  Notify device schedule that the device
			 *  has become available.
			 */
			dsc_reqcom (device, req);
		}
		break;
	case QUE_NET:			/* MOREHERE */
		Gblnetcount--;		/* One less network request */
		break;
	case QUE_PIPE:			/* The req was a pipe req */
		if ((exitcode & 0003) != 3) {
			/*
			 *  This will have already been done if the request
			 *  was queued from a local pipe queue into another
			 *  local queue.
			 */
			Gblpipcount--;	/* One less pipe request */
			psc_reqcom(req);/* Notify pipe scheduler */
		}
		break;
	}
	if (serverq->q.status & QUE_UPDATE) {
		/*
		 *  No requests from the specified queue were
		 *  activated (and therefore the NQS database
		 *  image for the queue has not been updated).
		 */
		udb_qorder (serverq);	/* Update queue ordering */
	}
	/*
	 *  It is now safe to free up resources associated with the
	 *  request, as indicated by the free_ flags.
	 */
	if (free_request_struct) {
		/*
		 *  Dispose of the request, as appropriate.
		 */
		nqs_disreq (req, free_request_files);
		if (!free_request_files) {
			/*
			 *  The request files are to be placed in the
			 *  failed directory, for the mystification of
			 *  the NQS maintainers.
			 */
			nqs_failed (req->v1.req.orig_seqno,
				    req->v1.req.orig_mid);
		}
	}

}


/*** requeue
 *
 *
 *	void requeue():
 *
 *	Requeue the request that just completed, because of some sort
 *	of failure that warrants a retry effort.
 *
 *	The NQS database image of the server queue is NOT updated
 *	by this function.
 */
static void requeue (req)
register struct request *req;			/* Request to requeue */
{

	register struct nqsqueue *serverq;		/* Request queue */

	serverq = req->queue;			/* Containing queue */
	req->status &= (~RQF_SIGQUEUED & ~RQF_SIGREQUEUE);
						/* No queued signal; no */
						/* requeue on abort */
	if (req->status & RQF_OPERHOLD) {
		/*
		 *  The request has been placed on operator hold.
		 */
		a2s_a2hset (req, req->queue);	/* Add to holding set */
	} else if (req->start_time > time ((time_t *) 0)) {
		/*
		 *  Place the request in the waiting set for the queue
		 *  (a2s_a2wset() calls nqs_vtimer()).
		 */
		a2s_a2wset (req, serverq);
	}
	else {
		/*
		 *  The request can start again as soon as possible.
		 */
		a2s_a2qset (req, serverq);	/* Add to queued set */
	}
	fflush (stdout);
}


/*** stopallqueues
 *
 *
 *	void stopallqueues():
 *	Stop all NQS queues.
 */
static void stopallqueues (serverq)
register struct nqsqueue *serverq;	/* Queue containing exited req */
{
	register struct nqsqueue *queue;

	queue = Nonnet_queueset;		/* Non-network queues */
	while (queue != (struct nqsqueue *) 0) {
		queue->q.status &= ~QUE_RUNNING;	/* Stop the queue */
		if (serverq != queue) {
			/*
			 *  Only update the queue database image if the
			 *  the queue is NOT the server queue (which we
			 *  will update later).
			 */
			udb_queue (queue);	/* Update queue header */
		}
		queue = queue->next;
	}
	queue = Net_queueset;			/* Network queues */
	while (queue != (struct nqsqueue *) 0) {
		queue->q.status &= ~QUE_RUNNING;	/* Stop the queue */
		if (serverq != queue) {
			/*
			 *  Only update the queue database image if the
			 *  the queue is NOT the server queue (which we
			 *  will update later).
			 */
			udb_queue (queue);	/* Update queue header */
		}
		queue = queue->next;
	}
	printf ("I$All queues stopped.\n");
	fflush (stdout);
}


/*** stopdev
 *
 *
 *	void stopdev():
 *
 *	Stop and mark the specified device as failed.
 *	Note, that the server queue will be effectively stopped if the
 *	device was the last device enabled in the queue/device set
 *	for the server queue.
 */
static void stopdev (device, serverq)
register struct device *device;		/* Device to be marked as failed */
register struct nqsqueue *serverq;	/* Queue that was being handled by */
					/* the device */
{
	register struct qdevmap *map;	/* Walk queue/device mappings */

	/*
	 *  Inform.
	 */
	printf ("I$Stopping and marking device: %s as failed.\n",
		device->name);
	/*
	 *  Mark device as failed.
	 */
	device->status = DEV_FAILED;
	udb_device (device);		/* Update NQS image */
	/*
	 *  Determine if the device queue has any more devices
	 *  to which reqs can be sent which are enabled.  If
	 *  not, then the queue is effectively stopped and we
	 *  want to print a warning message.
	 */
	map = serverq->v1.device.qdmaps;
	while (map != (struct qdevmap *) 0 &&
	      (map->device->status & DEV_ENABLED) == 0) {
		map = map->nextqd;
	}
	if (map == (struct qdevmap *) 0) {
		/*
		 *  The server queue has no enabled devices for
		 *  it to use.  Warn that the queue is effectively
		 *  stopped.
		 */
		printf ("W$Queue: %s is effectively stopped ",
			serverq->q.namev.name);
		printf ("since no enabled devices\n");
		printf ("W$remain in its queue/device mapping set.\n");
		fflush (stdout);
	}
}
/*
 * A remote system has reported a request has completed.  First lets
 * check if there is some load information on this node.  If so, 
 * adjust the number of jobs. Then, lets kick the
 * scheduler to see if anything can run on that system.
 */
long
nqs_rreqcom(client_mid,  req_mid,  req_seqno,  no_jobs)
Mid_t	client_mid;		/* Mid that is reporting completion */
Mid_t	req_mid;		/* Mid of request which completed */
int	req_seqno;		/* Seqno of request which completed */
int	no_jobs;		/* Remaining number of jobs */
{
    struct loadinfo *load_ptr;       /* Load information pointer */

    load_ptr = Loadinfo_head.next;
    while (load_ptr != (struct loadinfo *) NULL) {
        if (load_ptr->mid == client_mid) {
	    if (load_ptr->no_jobs >= 0) load_ptr->no_jobs = no_jobs;
	    break;
        }
        load_ptr = load_ptr->next;
    }
    if (Debug > 2) {
	printf("D$Nqs_rreqcom: Client mid is %d\n",  client_mid);
	printf("D$Nqs_rreqcom: Req_mid is %d\n",  req_mid);
	printf("D$Nqs_rreqcom: Req_seqno is %d\n",  req_seqno);
	if (load_ptr != (struct loadinfo *) NULL) 
	    printf("D$Nqs_rreqcom: Remaining jobs %d\n", load_ptr->no_jobs);
    }
    
    nqs_wakreq();
}
/*
 * Send a message back to the NQS scheduler node that we have completed
 * a request.
 */
static long
tell_nqs_scheduler(orig_seqno, orig_mid, request)
int orig_seqno;
Mid_t orig_mid;
struct request *request;
{

    struct passwd *whompw;           /* Whose request it is */
    int sd;                          /* Socket descriptor */
    short timeout;                   /* Seconds between tries */
    long transactcc;                 /* Holds establish() return value */
    Mid_t my_mid;		     /* Local mid */
    int nqs_jobs;		     /* Number of NQS jobs */
    int status;		             /* Status from close */	
	
    localmid( &my_mid );
    if ( (LB_Scheduler == 0) || (LB_Scheduler == my_mid) ) return;
    whompw = fetchpwuid (request->v1.req.uid);
    if (whompw == (struct passwd *) 0) {
	printf ("E$tell_nqs_scheduler: Can't find password for uid %d\n",
		request->v1.req.uid);
	fflush (stdout);
	return;
    }
    nqs_jobs = get_req_count();
    interclear();
    interw32i (my_mid);
    interw32i (orig_seqno);         /* Original sequence number */
    interw32i ((long) orig_mid);    /* Original machine id */
    interw32i ((long) nqs_jobs);    /* Jobs left on the system */
 
    sd = establish (NPK_RREQCOM, LB_Scheduler, request->v1.req.uid, 
			whompw->pw_name, &transactcc);
    fflush(stdout);
    if (sd == -2) {
        /*
         * Retry is in order.
         */
        timeout = 1;
        do {
            nqssleep (timeout);
            interclear ();
            interw32i (my_mid);
            interw32i (orig_seqno);
            interw32i ((long) orig_mid);
	    interw32i ((long) nqs_jobs);    
            sd = establish (NPK_RREQCOM, LB_Scheduler,
                        request->v1.req.uid, whompw->pw_name, &transactcc);
            timeout *= 2;
        } while (sd == -2 && timeout <= 16);
    }
    status = close (sd);		/* We don't need it anymore */
    if (status != 0) {
        printf("D$tell_nqs_scheduler: errno from close == %d\n",errno);
        fflush(stdout);
    }
    return (transactcc);

}
/**** get_req_count:
 * 
 * Get the count of running and arriving requests on the system.
 * Returns either -1 for a failure, or 0 or positive number for count
 * of jobs on the system.
 */
static int get_req_count()
{
    register struct nqsqueue *queue;   /* Queue set to traverse */
    register struct request *req;
    int req_count;

    queue = Nonnet_queueset;        /* The request will always be */
                                    /* located in a non-network queue */
    req_count = 0;
    while (queue != (struct nqsqueue *) 0) {
       req = queue->runset;
       while (req != (struct request *) 0 ) {
	    req_count++;
            req = req->next;
       }
       req = queue->arriveset;
       while (req != (struct request *) 0 ) {
	    req_count++;
            req = req->next;
       }
       /*
        *  Examine the next queue in the queue set.
        */
       queue = queue->next;
    }
    return(req_count);
}
