/*
 *	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  CDR     06-Aug-1986     Initial version.
*       V01.20  JRR     16-Apr-1992	Added complex user limits.
*	V01.3	JRR	17-Jun-1992	Added header.
*	V01.4	JRR	20-Nov-1992	Modified for C prototypes.
*	V01.5	JRR	06-Apr-1993	Fixed bug in upc_delcom
*					courtesy of Tom Schwab
*					(schwab@rzri6b.gsi.de)
*	V01.6	JRR	01-Mar-1994	Added support for Solaris.
*/
/*++ nqs_upc.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_upc.c,v $
 *
 * DESCRIPTION:
 *
 *	NQS queue complex update module.
 *
 *
 *	Author:
 *	-------
 *	Clayton D Andreasen, Cray Research, Incorporated.
 *	August 6, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.6 $ $Date: 1994/03/30 20:36:54 $ $State: Exp $)
 * $Log: nqs_upc.c,v $
 * Revision 1.6  1994/03/30  20:36:54  jrroma
 * Version 3.35.6
 *
 * Revision 1.5  93/07/13  21:34:03  jrroma
 * Version 3.34
 * 
 * Revision 1.4  92/12/22  15:41:34  jrroma
 * Version 3.30
 * 
 * Revision 1.3  92/06/18  17:31:26  jrroma
 * Added gnu header
 * 
 * Revision 1.2  92/05/06  10:44:16  jrroma
 *  Version 3.20
 * 
 * Revision 1.1  92/04/16  10:37:04  jrroma
 * Initial revision
 * 
 * Revision 3.2  91/02/11  16:58:41  root
 * Version 2.0 Source
 * 
 * Revision 2.2  87/04/22  15:11:41  hender
 * Sterling version 4/22/87
 * 
 *
 */

#include <errno.h>
#include "nqs.h"
#include "nqsxvars.h"
#include "transactcc.h"		/* Transaction completion codes */
#include <malloc.h>
#include <string.h>


 /*** upc_addcomque
  *
  *
  *    long upc_addcomque():
  *
  *    Add a queue to a complex.
  *
  *    Returns:
  *            TCML_COMPLETE:  if successful.
  *            TCML_ALREADEXI: if the mapping is already present.
  *            TCML_INSUFFMEM: if insufficient memory to create the
  *                            qcomplex descriptor.
  *            TCML_NOSUCHCOM: if the named local queue complex did not exist.
  *            TCML_NOSUCHQUE: if the named local queue did not exist.
  *            TCML_TOOMANQUE: if the qcomplex cannot accept another queue.
  *            TCML_TOOMANCOM: if the queue cannot accept another complex.
  *            TCML_WROQUETYP: if the named queue is not a device queue.
  */
 long upc_addquecom (que_name,complx_name)
 char *que_name;                        /* Local queue name */
 char *complx_name;                        /* Local queue complex name */
 {

        struct nqsqueue *queue;            /* Pointer to que structure */
        struct qcomplex *qcomplex;      /* Pointer to que complex structure */
        struct qcomplex **qcp;          /* Pointer to que complex pointer */
        int i;
 
        /*
         *  Find the queue to be added.
         */
        queue = nqs_fndnnq (que_name);
        if (queue == (struct nqsqueue *)0) {
                return(TCML_NOSUCHQUE); /* No such local queue */
        }

	/* Complexes do not exist for pipe queues */
        if (queue->q.type == QUE_PIPE) 
                return(TCML_WROQUETYP);

        /*
         *  Find the queue complex to add to.
         */
        qcomplex = Qcomplexset;
        while (qcomplex != (struct qcomplex *)0) {
                if (strcmp(complx_name, qcomplex->name) == 0) break;
                qcomplex = qcomplex->next;
        }
        if (qcomplex == (struct qcomplex *)0) {
                return(TCML_NOSUCHCOM); /* No such queue complex */
        }

        /*
         *  Locate where to add the queue complex to the queue.
         */
        qcp = (struct qcomplex **)0;
        if (queue->q.type == QUE_BATCH) { 
        for (i = MAX_COMPLXSPERQ; --i >= 0;) {
                if (queue->v1.batch.qcomplex[i] == (struct qcomplex *)0) {
                        qcp = &queue->v1.batch.qcomplex[i];
                        continue;
                }
                if (queue->v1.batch.qcomplex[i] == qcomplex) {
                        return(TCML_ALREADCOM); /* Already in queue complex */
                }
         }
        } else if (queue->q.type == QUE_DEVICE) { 
             for (i = MAX_COMPLXSPERQ; --i >= 0;) {
                if (queue->v1.device.qcomplex[i] == (struct qcomplex *)0) {
                        qcp = &queue->v1.device.qcomplex[i];
                        continue;
                }
                if (queue->v1.device.qcomplex[i] == qcomplex) {
                        return(TCML_ALREADCOM); /* Already in queue complex */
                }
           }
 
	}	/* end of DEVICE type */
        if (qcp == (struct qcomplex **)0) {
                return (TCML_TOOMANCOM); /* Too many complexes for the queue */
        }

        /*
         *  Locate where to add the queue to the queue complex.
         *  Then link everything together.
         */
        for (i = 0; i < MAX_QSPERCOMPLX; i++) {
                if (qcomplex->queue[i] == (struct nqsqueue *)0) {
                        qcomplex->queue[i] = queue;
                        *qcp = qcomplex;
                        if (Booted) {
                                udb_quecom(qcomplex);
                        }
                        return (TCML_COMPLETE); /* Successful completion */
                }
        }
        return(TCML_TOOMANQUE);         /* Too many queues in queue complex */
}







/*** upc_crecom
 *
 *
 *	long upc_crecom():
 *
 *	Create a queue complex.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_ALREADEXI:	if the named local queue complex already exists.
 *		TCML_INSUFFMEM: if there was insufficient memory to allocate
 *				the queue complex structure.
 */
long upc_crecom (char *complx_name)
{
	struct qcomplex *qcomplex;	/* Pointer to queue complex structure */
	struct qcomplex *prevqcom;	/* Pointer to previous queue complex */

	/*
	 *  Find the end of the queue complex set and check for the 
	 *  possibility of the queue complex already existing.
	 */
	prevqcom = (struct qcomplex *)0;
	qcomplex = Qcomplexset;
	while (qcomplex != (struct qcomplex *)0) {
		if (strcmp(complx_name, qcomplex->name) == 0) {
			return(TCML_ALREADEXI);
		}
		prevqcom = qcomplex;
		qcomplex = qcomplex->next;
	}

	/*
	 *  The queue complex does not exist already.  Allocate one and
	 *  link it into the set.
	 */
	qcomplex = (struct qcomplex *)malloc( sizeof(struct qcomplex) );
	if (qcomplex == (struct qcomplex *)0) {
		return (TCML_INSUFFMEM);	/* insufficient memory */
	}
	if (prevqcom == (struct qcomplex *)0) {
		Qcomplexset = qcomplex;
	} else {
		prevqcom->next = qcomplex;
	}

	/*
	 *  Initialize the queue complex and then add the queue to it.
	 */
	bytezero( (char *)qcomplex, sizeof(struct qcomplex) );
	qcomplex->runlimit = 1;
        qcomplex->userlimit = 1;
	strcpy( qcomplex->name, complx_name );
	if (Booted) {
		udb_crecom(qcomplex);
	}
	return (TCML_COMPLETE);		/* Successful completion */
}



/*** upc_delcom
 *
 *
 *	long upc_delcom():
 *
 *	Delete a queue complex.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHCOM:	if the named local queue complex does not exist.
 */
long upc_delcom (char *complx_name)
{
	struct nqsqueue *queue;		/* Pointer to queue structure */
	struct qcomplex *qcomplex;	/* Pointer to queue complex structure */
	struct qcomplex *prevqcom;	/* Pointer to previous queue complex */
	int i;

	/*
	 *  Find the queue complex to delete.
	 */
	prevqcom = (struct qcomplex *)0;
	qcomplex = Qcomplexset;
	while (qcomplex != (struct qcomplex *)0) {
		if (strcmp(complx_name, qcomplex->name) == 0) break;
		prevqcom = qcomplex;
		qcomplex = qcomplex->next;
	}
	if (qcomplex == (struct qcomplex *)0) {
		return(TCML_NOSUCHCOM);	/* No such queue complex */
	}
	if (prevqcom == (struct qcomplex *)0) {
                /* Changed by Thomas Schwab, GSI, 7.04.93 */
                /* Qcomplexset = (struct qcomplex *)0; */
                Qcomplexset = qcomplex->next;
	} else {
		prevqcom->next = qcomplex->next;
	}

	/*
	 *  Now remove all references to the queue complex.
	 */
	queue = Nonnet_queueset;
	while (queue != (struct nqsqueue *)0) {
		for (i = MAX_COMPLXSPERQ; --i >= 0;) {
			if (queue->v1.batch.qcomplex[i] == qcomplex) {
				queue->v1.batch.qcomplex[i] =
					(struct qcomplex *)0;
				break;
			}
		}
		queue = queue->next;
	}
	udb_delcom(qcomplex);
	free( (char *)qcomplex );	/* Release the space used by complex */
	return (TCML_COMPLETE);		/* Successful completion */
}



/*** upc_remquecom
 *
 *
 *	long upc_remquecom():
 *
 *	Remove a batch queue from a queue complex.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHQUE:	if the named local queue does not exist.
 *		TCML_NOSUCHCOM:	if the named local queue complex does not exist.
 *		TCML_WROQUETYP:	if the named local queue is not a batch queue.
 *		TCML_NOTMEMCOM:	if the named local queue is not a member of
 *				the queue complex.
 */
long upc_remquecom (char *que_name, char *complx_name)
{
	struct nqsqueue *queue;		/* Pointer to queue structure */
	struct qcomplex *qcomplex;	/* Pointer to queue complex structure */
	int i;

	/*
	 *  Find the queue to be removed from the complex.
	 */
	queue = nqs_fndnnq (que_name);
	if (queue == (struct nqsqueue *)0) {
		return(TCML_NOSUCHQUE);		/* No such local queue */
	}
	if (queue->q.type != QUE_BATCH) {
		return(TCML_WROQUETYP);
	}

	/*
	 *  Find the queue complex to remove from.
	 */
	qcomplex = Qcomplexset;
	while (qcomplex != (struct qcomplex *)0) {
		if (strcmp(complx_name, qcomplex->name) == 0) break;
		qcomplex = qcomplex->next;
	}
	if (qcomplex == (struct qcomplex *)0) {
		return(TCML_NOSUCHCOM);	/* No such queue complex */
	}

	/*
	 *  Remove the pointer from the queue to the queue complex.
	 */
	for (i = 0; i < MAX_COMPLXSPERQ; i++) {
		if (queue->v1.batch.qcomplex[i] == qcomplex) {
			queue->v1.batch.qcomplex[i] = (struct qcomplex *)0;
			break;
		}
	}

	/*
	 *  Remove the pointer from the queue complex to the queue.
	 */
	for (i = 0; i < MAX_QSPERCOMPLX; i++) {
		if (qcomplex->queue[i] == queue) {
			qcomplex->queue[i] = (struct nqsqueue *)0;
			if (Booted) {
				udb_quecom(qcomplex);
			}
			return (TCML_COMPLETE);	/* Successful completion */
		}
	}
	return (TCML_NOTMEMCOM);	/* Not a member of the queue complex */
}



/*** upc_setcomlim
 *
 *
 *	long upc_setcomlim():
 *
 *	Set a runlimit for a queue complex.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHCOM:	if the named local queue complex does not exist.
 */
long upc_setcomlim (char *complx_name, long limit)
{
	struct qcomplex *qcomplex;	
				/* Pointer to queue complex structure */
	short prevlim;			/* Previous runlimit for q complex */

	/*
	 *  Find the queue complex to add to.
	 */
	qcomplex = Qcomplexset;
	while (qcomplex != (struct qcomplex *)0) {
		if (strcmp(complx_name, qcomplex->name) == 0) break;
		qcomplex = qcomplex->next;
	}
	if (qcomplex == (struct qcomplex *)0) {
		return(TCML_NOSUCHCOM);	/* No such queue complex */
	}

	/*
	 *  Set the new limit for the queue complex and call the spawning
	 *  routine to possibly spawn new batch requests if the limit has
	 *  increased.
	 */
	prevlim = qcomplex->runlimit;
	qcomplex->runlimit = (short) limit;
	if (Booted) {
		udb_quecom(qcomplex);
	}
	if (limit > prevlim) {
		bsc_spawn();
	}
	return (TCML_COMPLETE);		/* Successful completion */
}
/*** upc_setcomuserlim
 *
 *
 *	long upc_setcomuserlim():
 *
 *	Set a user runlimit for a queue complex.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHCOM:	if the named local queue complex does not exist.
 */
long upc_setcomuserlim (char *complx_name, long limit)
{
	struct qcomplex *qcomplex;	/* Pointer to queue complex structure */
	short prevlim;			/* Previous user runlimit for q complex */

	/*
	 *  Find the queue complex to add to.
	 */
	qcomplex = Qcomplexset;
	while (qcomplex != (struct qcomplex *)0) {
		if (strcmp(complx_name, qcomplex->name) == 0) break;
		qcomplex = qcomplex->next;
	}
	if (qcomplex == (struct qcomplex *)0) {
		return(TCML_NOSUCHCOM);	/* No such queue complex */
	}

	/*
	 *  Set the new limit for the queue complex and call the spawning
	 *  routine to possibly spawn new batch requests if the limit has
	 *  increased.
	 */
	prevlim = qcomplex->userlimit;
	qcomplex->userlimit = (short) limit;
	if (Booted) {
		udb_quecom(qcomplex);
	}
	if (limit > prevlim) {
		bsc_spawn();
	}
	return (TCML_COMPLETE);		/* Successful completion */
}

 /*** nqs_fndcomplex
  *
  *
  *    struct qcomplex *nqs_fndcomplex():
  *
  *    Return pointer to queue structure for the named qcomplex.
  *
  *    Returns:
  *            A pointer to the qcomplex structure for the named
  *            qcomplex, if the qcomplex exists;
  *            otherwise (struct qcomplex *)0 is returned.
  */
struct qcomplex *nqs_fndcomplex (char *comname)
{
       struct qcomplex *qcomplex;
       int res;
 
        /*
         *  We take advantage of the fact that qcomplex set
         *  is lexicographically ordered.
         */

       qcomplex = Qcomplexset;
       while (qcomplex != (struct qcomplex *)0) {
               if ((res = strcmp (qcomplex->name, comname)) < 0) {
                       /*
                        *  We have not yet found the qcomplex, but we should
                        *  keep on searching.
                        */
                       qcomplex = qcomplex->next;
               }
               else if (res == 0) return (qcomplex);   /* Found! */
               else return ((struct qcomplex *)0);     /* Not found */
        }
       return (qcomplex);                                      /* Not found */  
}
