/*
 * nqsd/nqs_mov.c
 * 
 * DESCRIPTION:
 *
 *	Move a request between queues or move all requests on a queue to
 *	another queue.
 *
 *	Original Author:
 *	-------
 *	Clayton D. Andreasen, Cray Research, Incorporated.
 *	August 8, 1986.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <string.h>
#include <time.h>
#include <libnqs/transactcc.h>		/* Transaction completion codes */
#include <libnqs/nqsxvars.h>		/* Global vars */
#include <libnqs/informcc.h>	       	/* NQS information completion */
					/* codes and masks */

#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.h>
#include <unistd.h>

/*** 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 */

	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "nqs_mov().  seqno=%d; mid=%u; queue=%s\n", orig_seqno, orig_mid, destque);
	/*
	 *  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_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.
	 */
	sal_dprintf (SAL_DEBUG_INFO, SAL_DEBUG_MSG_DEBUGHIGH, "nqs_mov(): Read request (%s).\n", path);
  
	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);
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_mov: Move caused queuedcount to go negative!\n");
		return(TCML_UNAFAILURE);
	    }
	} else if (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);
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_mov: Move caused waitcount to go negative!\n");
		return(TCML_UNAFAILURE);
	    }
	} else if (state == RQS_HOLDING) {
	    if (predecessor == (struct request *) 0) {
	        req->queue->holdset = req->next;
	    } else {
	        predecessor->next = req->next;
	    }
	    req->queue->q.holdcount--;
	    /*
	     * If this count is < 0 we have serious problems!
	     */
	    if (req->queue->q.holdcount < 0) {
	        close (cfd);
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_WARNING, "nqs_mov: Move caused holdcount to go negative!\n");
		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) {
		sal_dprintf(SAL_DEBUG_INFO, SAL_DEBUG_MSG_INFO, "nqs_mov: verify_resources returned %o (octal)\n",  res);
		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 */
}
