/*
 * nqsd/nqs_upc.c
 *
 * DESCRIPTION
 *
 *	NQS queue complex update module.
 *
 *	Original Author:
 *	-------
 *	Clayton D Andreasen, Cray Research, Incorporated.
 *	August 6, 1986.
 */

#include <errno.h>
#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <libnqs/nqsxvars.h>
#include <libnqs/transactcc.h>		/* Transaction completion codes */
#include <malloc.h>
#include <string.h>
#include <libsal/license.h>
#include <libsal/proto.h>
#include <libsal/defines.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 */
        }

#if 0
   	/* Change for Generic NQS v3.50.0
	 * 
	 * We perform dynamic scheduling across clusters through the use
	 * of user-limits on complexes.  To do this, we need to allow
	 * pipe queues to be members of complexes.
	 */
   
	/* Complexes do not exist for pipe queues */
        if (queue->q.type == QUE_PIPE) 
                return(TCML_WROQUETYP);
#endif
        /*
         *  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 */
                }
           }
        } else if (queue->q.type == QUE_PIPE) {
             for (i = MAX_COMPLXSPERQ; --i >= 0;) {
                if (queue->v1.pipe.qcomplex[i] == (struct qcomplex *)0) {
                        qcp = &queue->v1.pipe.qcomplex[i];
                        continue;
                }
                if (queue->v1.pipe.qcomplex[i] == qcomplex) {
                        return(TCML_ALREADCOM); /* Already in queue complex */
                }
           }
 
	}	/* end of PIPE 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.
	 */
	sal_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->q.type == QUE_BATCH)
		  	{
			  if (queue->v1.batch.qcomplex[i] == qcomplex) {
				queue->v1.batch.qcomplex[i] =
					(struct qcomplex *)0;
				break;
			  }
			} else if (queue->q.type == QUE_DEVICE) 
		  	{
			  if (queue->v1.device.qcomplex[i] == qcomplex) {
				queue->v1.device.qcomplex[i] =
					(struct qcomplex *)0;
				break;
			  }
			} else if (queue->q.type == QUE_PIPE)
		  	{
			  if (queue->v1.pipe.qcomplex[i] == qcomplex) {
				queue->v1.pipe.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 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_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 */
	}

	/*
	 *  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;
		}
		if (queue->v1.device.qcomplex[i] == qcomplex) {
			queue->v1.batch.qcomplex[i] = (struct qcomplex *)0;
			break;
		}
		if (queue->v1.pipe.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 */  
}
