/*
 *	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.2   JRR     03-Mar-1992	Changed mid_t to Mid_t.
*	V01.3	JRR	26-May-1992	Added support for RS6000.
*					Added header.
*					Allow to move Waiting requests.
*	V01.4	JRR	19-Jun-1992	Tweaks to improve.
*	V01.5	JRR	26-Feb-1993	Added Boeing enhancement to Mids.
*	V01.6	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_mov.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_mov.c,v $
 *
 * DESCRIPTION:
 *
 *	Move a request between queues or move all requests on a queue to
 *	another queue.
 *
 *
 *	Author:
 *	-------
 *	Clayton D. Andreasen, Cray Research, Incorporated.
 *	August 8, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.6 $ $Date: 1994/03/30 20:36:43 $ $State: Exp $)
 * $Log: nqs_mov.c,v $
 * Revision 1.6  1994/03/30  20:36:43  jrroma
 * Version 3.35.6
 *
 * Revision 1.5  93/07/13  21:33:55  jrroma
 * Version 3.34
 * 
 * Revision 1.4  92/12/22  15:41:24  jrroma
 * Version 3.30
 * 
 * Revision 1.3  92/06/18  17:31:11  jrroma
 * Added gnu header
 * 
 * Revision 1.2  92/03/13  16:56:16  jrroma
 * Changed mid_t to Mid_t.
 * 
 * Revision 1.1  92/03/03  17:04:44  jrroma
 * Initial revision
 * 
 * Revision 3.2  91/02/11  16:58:16  root
 * Version 2.0 Source
 * 
 * Revision 2.2  87/04/22  15:11:45  hender
 * Sterling version 4/22/87
 * 
 *
 */

#include "nqs.h"			/* NQS constants and data types */
#include <string.h>
#include <time.h>
#include "transactcc.h"			/* Transaction completion codes */
#include "nqsxvars.h"			/* Global vars */
#include "informcc.h"			/* NQS information completion */
					/* codes and masks */

/*** nqs_movreq
 *
 *
 *	long nqs_movreq():
 *	Move a queued NQS request.
 *
 *	Returns:
 *		TCML_COMPLETE:	 if the request was successfully
 *				 modified.
 *		TCML_NOSUCHQUE:	 if the specified queue does not
 *				 exist on this machine.
 *		TCML_QUEDISABL:	 if the specified queue is disabled
 *		TCML_NOSUCHREQ:	 if the specified request does not
 *				 exist on this machine.
 *		TCML_REQRUNNING: if the specified request is running.
 *		TCML_WROQUETYP:  if the destination queue type is not
 *				 compatible with the source queue type.
 */
long nqs_movreq (
	long	orig_seqno,	/* Originating sequence number of request */
	Mid_t	orig_mid,	/* Originating machine id */
	char	*destque)	/* Destination queue name */
{
	struct request *predecessor;	/* Predecessor in req set in queue */
	int state;			/* Request queue state: RQS_ */
	char path [MAX_PATHNAME+1];	/* Control file pathname */
	struct rawreq rawreq;		/* Raw request structure */
	int cfd;			/* Control file file-descriptor */
	register struct nqsqueue *queue;	/* Queue in which req is placed */
	struct nqsqueue *oldqueue;		/* name of the old queue */
	register struct request *req;	/* Request struct allocated for req */
	long res;                       /* Result of verify_resources */



	if (Debug > 2) {
	    printf ("D$nqs_mov().  seqno=%d; mid=%u; queue=%s\n",
			orig_seqno, orig_mid, destque);
	    fflush (stdout);
	}
	/*
	 *  Locate the queue in which to place the request.
	 */
	queue = nqs_fndnnq (destque);
	if (queue == (struct nqsqueue *) 0) {
	    return (TCML_NOSUCHQUE);	/* No such queue */
	}
	if (!(queue->q.status & QUE_ENABLED)) {
	    return (TCML_QUEDISABL);	/* Queue is disabled */
	}
	/*
	 *  Locate the request.
	 */
	state = RQS_STAGING | RQS_QUEUED | RQS_WAITING | RQS_HOLDING
		| RQS_ARRIVING | RQS_DEPARTING | RQS_RUNNING;
	if ((req = nqs_fndreq (orig_seqno, orig_mid, &predecessor, &state)) ==
	    (struct request *) 0) {
	    return (TCML_NOSUCHREQ);	/* Request not found */
	}
	if ( (state == RQS_STAGING) || (state == RQS_HOLDING) ||
	    (state == RQS_ARRIVING) || (state == RQS_DEPARTING) || 
	    (state == RQS_RUNNING) ) {
				    /* if request not queued or waiting */
	    /*
	     *  The request is departing, staging output files,
	     *  running, or routing.  For now, we return TCML_REQRUNNING.
	     */
	    return (TCML_REQRUNNING);
	}
	/*
	 *  Build the control file name and open it.
	 */
	pack6name (path, Nqs_control,
		  (int) (orig_seqno % MAX_CTRLSUBDIRS), (char *) 0,
		  (long) orig_seqno, 5, (long) orig_mid, 6, 0, 0);
	if ((cfd = open (path, O_RDWR)) < 0) {
	    return (TCML_UNAFAILURE);
	}
	/*
	 *  Read the request header.
	 */
	if (Debug > 2) {
	    printf ("D$nqs_mov(): Read request (%s).\n", path);
	    fflush (stdout);
	}
	if (readreq (cfd, &rawreq) == -1) {
	    close (cfd);			/* Close request file */
	    return (TCML_UNAFAILURE);	/* Failure */
	}
	/*
	 *  Verify that the request type and destination queue type agree
	 *  if the queue is a batch or device queue.
	 */
	if ((queue->q.type == QUE_BATCH && rawreq.type != RTYPE_BATCH) ||
	    (queue->q.type == QUE_DEVICE && rawreq.type != RTYPE_DEVICE)) {
	    close (cfd);			/* Close request file */
	    return (TCML_WROQUETYP);	/* wrong queue type */
	}
	/*
	 *  The request has been located.  Remove the request from the
	 *  the containing queue and set the QUE_UPDATE bit.
	 */
	if (state == RQS_QUEUED) {
	    if (predecessor == (struct request *) 0) {
		req->queue->queuedset = req->next;
	    } else {
		predecessor->next = req->next;
	    }
	    req->queue->q.queuedcount--;
	    /*
	     * If this count is < 0 we have serious problems!
	     */
	    if (req->queue->q.queuedcount < 0) {
		close (cfd);
		printf("F$nqs_mov: Move caused queuedcount to go negative!\n");
		fflush(stdout);
		return(TCML_UNAFAILURE);
	    }
	} else {				 /* state = RQS_WAITING */
	    if (predecessor == (struct request *) 0) {
		req->queue->waitset = req->next;
	    } else {
		predecessor->next = req->next;
	    }
	    req->queue->q.waitcount--;
	    /*
	     * If this count is < 0 we have serious problems!
	     */
	    if (req->queue->q.waitcount < 0) {
		close (cfd);
		printf("F$nqs_mov: Move caused waitcount to go negative!\n");
		fflush(stdout);
		return(TCML_UNAFAILURE);
	    }
	}	
	req->next = (struct request *)0;	/* No more requests */
	req->queue->q.status |= QUE_UPDATE;
	oldqueue = req->queue;
	req->queue = (struct nqsqueue *)0;
	/*
	 * If the request originally went to a pipe queue and now is going directly
	 * to a batch queue,  we need to verify resources (see nqs_nsq.c for 
	 * verify_resources).  Unfortunately,  we do not know if this request already
	 * went through verify_resources,  so we do it again.  This may mean that
	 * a request may have different resource requests than expected.  A few
	 * brief tests seem to indicate that it may work reasonably well.
	 */
	if ( queue->q.type == QUE_BATCH ) {
	    res = verify_resources (queue, &rawreq);
	    if ((res & XCI_FULREA_MASK) != TCML_SUBMITTED) {
		printf("I$nqs_mov: verify_resources returned %o (octal)\n",  res);
		fflush(stdout);
		return (res);   /* Return failure result */
            }
	}
       	/*
	 *  Copy the destination queue name into the queue field of the
	 *  rawreq structure for the request and rewrite the request header.
	 */
	strcpy (rawreq.quename, destque);
	writereq (cfd, &rawreq);		/* Update request header */
	close (cfd);				/* Close request file */

	/*
	 *  Now, schedule the request for execution.
	 */
	switch (queue->q.type) {
	case QUE_BATCH:
	    req->v1.req.priority = bsc_sched (&rawreq);
	    break;
	case QUE_DEVICE:
	    req->v1.req.priority = dsc_sched (&rawreq);
	    break;
	case QUE_PIPE:
	    req->v1.req.priority = psc_sched (&rawreq);
	    break;
	}
	if (queue->q.type == QUE_PIPE) {
	    /*
	     *  When a request is placed in a pipe queue, it is
	     *  routed and delivered to its destination as quickly
	     *  as possible, regardless of the -a time.  The -a
	     *  time only becomes effective when the request has
	     *  reached its final destination batch or device
	     *  queue.
	     */
	    req->start_time = 0;
	} else {
	    /*
	     *  The request is being placed in a batch or device
	     *  queue, and so the -a time parameter of the request
	     *  is now meaningful.
	     */
	    req->start_time = rawreq.start_time;
					/* Remember start after time */
	}
	/*
	 *  Place the prioritized request into the queue.
	 */
	if ((rawreq.flags & RQF_OPERHOLD) ||
	    (rawreq.flags & RQF_USERHOLD)) {
	    /*
	     *  Add the request to the hold set for this queue.
	     *  The QUE_UPDATE bit is set by a2s_a2hset().
	     */
	    a2s_a2hset (req, queue);/* Add to hold set */
	} else if (rawreq.start_time > time ((time_t *) 0)) {
	    /*
	     *  The request has a start time in the future.
	     *  The QUE_UPDATE bit is set by a2s_a2wset().
	     */
	    a2s_a2wset (req, queue);/* Add to wait set */
	} else {
	    /*
	     *  Place the request in the eligible to run set.
	     *  The QUE_UPDATE bit is set by a2s_a2qset().
	     */
	    a2s_a2qset (req, queue);/* Add to queued set */
	}
	switch (queue->q.type) {
	case QUE_BATCH:
	    bsc_spawn();		/* Maybe spawn some batch reqs */
	    break;
	case QUE_DEVICE:
	    dsc_spawn();		/* Maybe spawn some device reqs */
	    break;
	case QUE_PIPE:
	    psc_spawn();		/* Maybe spawn some pipe reqs */
	    break;
	}
	if (queue->q.status & QUE_UPDATE) {
	    /*
	     *  No requests were spawned from the queue in which
	     *  the most recent request was placed.
	     */
	    udb_qorder (queue);	/* Update and clear QUE_UPDATE bit */
	}
	udb_qorder (oldqueue);		/* Always update the old queue. */
	return (TCML_COMPLETE);		/* Return transaction code */
}
/*** nqs_movque
 *
 *
 *	long nqs_movque():
 *	Move all of the NQS requests from one queue to another.
 *
 *	Returns:
 *		TCML_COMPLETE:	 if the request was successfully
 *				 modified.
 *		TCML_NOSUCHQUE:	 if the specified queue does not
 *				 exist on this machine.
 *		TCML_QUEDISABL:	 if the specified queue is disabled
 *		TCML_NOSUCHREQ:	 if the specified request does not
 *				 exist on this machine.
 *		TCML_REQRUNNING: if the specified request is running.
 *		TCML_WROQUETYP:  if the destination queue type is not
 *				 compatible with the source queue type.
 */
long nqs_movque (char *from_que, char *to_que)
{
	register struct nqsqueue *queue;	/* Queue to move requests from */
	register struct request *req;	/* Request struct allocated for req */
	register struct request *next;	/* Next request */
	register long rc;		/* return code from nqs_movreq() */

	if (strcmp(from_que, to_que) == 0) {	/* if same queue */
	    return (TCML_COMPLETE);
	}

	/*
	 *  Locate the queue from which requests will be moved.
	 */
	queue = nqs_fndnnq (from_que);
	if (queue == (struct nqsqueue *) 0) {
	    return (TCML_NOSUCHQUE);	/* No such queue */
	}

	/*
	 *  Move all requests to the destination queue.
	 */
	next = queue->queuedset;
	while ((req = next) != (struct request *)0) {
	    next = req->next;
	    rc = nqs_movreq( req->v1.req.orig_seqno,
			req->v1.req.orig_mid, to_que);
	    if (rc != TCML_COMPLETE) return(rc);
	}
	return (TCML_COMPLETE);		/* Return transaction code */
}
