/*
 *	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     12-Feb-1992	Fixed declaration of static routines.
*	V01.3	JRR	17-Jun-1992	Added header.
*	V01.4	JRR			Placeholder.
*	V01.5	JRR			Placeholder.
*	V01.6	JRR			Placeholder.
*	V01.7	JRR	01-Mar-1994	Added support for SOLARIS.
*/
/*++ nqs_upv.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/src/RCS/nqs_upv.c,v $
 *
 * DESCRIPTION:
 *
 *	NQS device state update module.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	June 16, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.7 $ $Date: 1994/03/30 20:37:00 $ $State: Exp $)
 * $Log: nqs_upv.c,v $
 * Revision 1.7  1994/03/30  20:37:00  jrroma
 * Version 3.35.6
 *
 * Revision 1.6  92/12/22  15:41:40  jrroma
 * Version 3.30
 * 
 * Revision 1.5  92/06/18  17:31:32  jrroma
 * Added gnu header
 * 
 * Revision 1.4  92/03/02  16:32:42  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.3  92/02/12  16:13:32  jrroma
 * *** empty log message ***
 * 
 * Revision 1.2  92/02/12  14:04:59  jrroma
 * Fixed declaration of static routines.
 * 
 * Revision 1.1  92/02/12  14:04:01  jrroma
 * Initial revision
 * 
 *
 */

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

#ifndef __CEXTRACT__
#if __STDC__

static struct device *finddev ( char *devname );

#else /* __STDC__ */

static struct device *finddev (/* char *devname */);

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

/*** upv_addquedev
 *
 *
 *	long upv_addquedev():
 *
 *	Add a queue to device mapping for the specified local device
 *	queue.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_ALREADEXI:	if the mapping is already present.
 *		TCML_INSUFFMEM:	if insufficient memory to create the
 *				queue/device mapping descriptor.
 *		TCML_NOSUCHDEV:	if the named local device did not exist.
 *		TCML_NOSUCHQUE:	if the named local queue did not exist.
 *		TCML_WROQUETYP:	if the named queue is not a device queue.
 */
long upv_addquedev (char *quename, char *devname)
{

	struct nqsqueue *queue;
	struct device *device;
	struct qdevmap *map;
	struct qdevmap *prevmap;
	int res;			/* Result code */

	queue = nqs_fndnnq (quename);
	if (queue == (struct nqsqueue *)0) {
		return (TCML_NOSUCHQUE);	/* No such local queue */
	}
	device = finddev (devname);
	if (device == (struct device *)0) {
		return (TCML_NOSUCHDEV); 	/* No such device */
	}
	if (queue->q.type != QUE_DEVICE) return (TCML_WROQUETYP);
	/*
	 *  Determine if the mapping is already present (in the lexicographic-
	 *  ally ordered queue-to-device mapping set.  If not, then remember
	 *  where to insert it into the mapping set so that the mapping set
	 *  remains in lexicographic order.
	 */
	prevmap = (struct qdevmap *) 0;
	map = queue->v1.device.qdmaps;
	while (map != (struct qdevmap *)0) {
		res = strcmp (map->device->name, devname);
		if (res < 0) {
			/*
			 *  Keep searching.
			 */
			prevmap = map;
			map = map->nextqd;	/* Get next queue/device map */
		}
		else if (res == 0) {
			/*
			 *  The mapping is already present.
			 */
			return (TCML_ALREADEXI);
		}
		else map = (struct qdevmap *)0;	/* Exit loop */
	}
	/*
	 *  The specified queue/device mapping does not exist, and when
	 *  the mapping is created, it should be added with "prevmap" as
	 *  its predecessor in the mapping set (unless "prevmap" is null,
	 *  in which case the mapping must be added at the very beginning
	 *  of the set).
	 */
	if ((map = (struct qdevmap *)
		    malloc (sizeof(struct qdevmap))) == (struct qdevmap *) 0) {
		return (TCML_INSUFFMEM);	/* Insufficient memory */
	}
	map->device = device;			/* Ptr to device */
	map->queue = queue;			/* Ptr to queue */
	/*
	 *  Add the mapping in the lexicographically ordered queue-to-device
	 *  mapping set.
	 */
	if (prevmap == (struct qdevmap *) 0) {
		map->nextqd = queue->v1.device.qdmaps;
						/* Add to mapping set head */
		queue->v1.device.qdmaps = map;	/* Update set header */
	}
	else {
		map->nextqd = prevmap->nextqd;	/* Add to mapping set */
		prevmap->nextqd = map;		/* Update next ptr */
	}
	/*
	 *  Add the inverse mapping in the UNORDERED device-to-queue
	 *  mapping set.
	 */
	map->nextdq = device->dqmaps;		/* Add to device/queue */
	device->dqmaps = map;			/* mapping set for device */
	if (Booted) {
		/*
		 *  NQS is completely "booted", and so this queue/device
		 *  mapping needs to be explicitly added to the NQS database.
		 *  Furthermore, it may be necessary to spawn a device queue
		 *  request....
		 */
		udb_addquedev (map);	/* Update the NQS database image */
					/* appropriately */
		if ((map->queue->q.status & QUE_RUNNING) &&
		     map->queue->q.queuedcount &&
		    (map->device->status & DEV_ENABLED) &&
		    (map->device->status & DEV_ACTIVE) == 0) {
			/*
			 *  The device is enabled, and is not active.
			 *  The queue is running, but other req(s) could run.
			 */
			dsc_spawn();	/* Maybe spawn a device req */
		}
	}
	else New_qdevmap = map;		/* Otherwise, update most recently */
					/* added map for the benefit of */
					/* nqs_ldconf.c */
	return (TCML_COMPLETE);
}


/*** upv_credev
 *
 *
 *	long upv_credev():
 *	Create an NQS device.
 *
 *	Returns:
 *		TCML_COMPLETE:	 if successful.
 *		TCML_ALREADEXI:	 if the device already exists.
 *		TCML_INSUFFMEM:	 if insufficient memory to create device
 *				 descriptor.
 *		TCML_NOSUCHFORM: if the forms associated with the device
 *				 do not exist.
 *		TCML_TOOMANDEV:  if the creation of this device would
 *				 result in exceeding the maximum number
 *				 of devices allowed for the local host
 *				 (see MAX_NOOFDEVICES in ../h/nqs.h).
 */
long upv_credev (
	char *devname,		/* Shorthand name for device */
	char *formsname,	/* Forms in device */
	char *fullname,		/* Fully qualified pathname of device */
	char *servername)	/* Server and args for device */
{
	struct device *device;
	struct device *prevdevice;
	int res;			/* Result code */

	if (formsname [0] != '\0') {		/* null name is valid */
		if (!upf_valfor (formsname)) {
			return(TCML_NOSUCHFORM);/* No such form */
		}
	}
	/*
	 *  Search the lexicographically ordered device set to see if the
	 *  device already exists.
	 */
	prevdevice = (struct device *)0;
	device = Devset;
	while (device != (struct device *)0) {
		res = strcmp (device->name, devname);
		if (res < 0) {
			/*
			 *  The device has not yet been found, keep
			 *  searching.
			 */
			prevdevice = device;
			device = device->next;
		}
		else if (res == 0) {
			/*
			 *  The device already exists.
			 */
			return (TCML_ALREADEXI);
		}
		else device = (struct device *)0;	/* Exit loop */
	}
	/*
	 *  Would the creation of this device result in too many devices,
	 *  exceeding the Runvars[] space allocated to device requests?
	 */
	if (Noofdevices >= MAX_NOOFDEVICES) return (TCML_TOOMANDEV);
	/*
	 *  The device does not already exist, and when the newly created
	 *  device is added to the device set, it should be added with
	 *  "prevdevice" as its predecessor (unless "prevdevice" is null,
	 *  in which case the device is added at the very beginning of the
	 *  device set.
	 */
	if ((device = (struct device *)
		       malloc (sizeof (struct device))) == (struct device *)0) {
		return (TCML_INSUFFMEM);	/* Insufficient memory */
	}
	bytezero ((char *) device, sizeof (struct device));
	device->status = 0;			/* Inactive, disabled, but */
						/* not failed */
	device->statmsg [0] = '\0';		/* No status message */
	strcpy (device->name, devname);		/* Save device name */
	if ((device->fullname = malloc (strlen (fullname) + 1)) == (char *)0) {
		free ((char *) device);		/* Free partial descriptor */
		return (TCML_INSUFFMEM);	/* Insufficient memory */
	}
	Noofdevices++;				/* One more device */
	strcpy (device->fullname, fullname);	/* Save full path name */
	strcpy (device->forms, formsname);	/* Save forms name */
	strcpy (device->server, servername);	/* Save server name */
	device->curreq = (struct request *)0;	/* No current request */
	device->curque = (struct nqsqueue *)0;	/* No current queue */
	device->dqmaps = (struct qdevmap *) 0;	/* No device/queue mappings */
	/*
	 *  The new device structure is now completely initialized; insert
	 *  it into the lexicographically ordered device set.
	 */
	if (prevdevice == (struct device *)0) {
		/*
		 *  Insert at the beginning of the list.
		 */
		device->next = Devset;
		Devset = device;
	}
	else {
		device->next = prevdevice->next;
		prevdevice->next = device;
	}
	if (Booted) {
		/*
		 *  NQS is completely "booted", and so this new device needs
		 *  to be added to the NQS database image.
		 */
		udb_credev (device);	/* Add the device descriptor to the */
					/* NQS database image. */
	}
	else New_device = device;	/* Otherwise, update most recently */
					/* added device for the benefit of */
					/* nqs_ldconf.c */
	return (TCML_COMPLETE);
}


/*** upv_deldev
 *
 *
 *	long upv_deldev():
 *	Delete an NQS device.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_DEVACTIVE:	if the device is active.
 *		TCML_DEVENABLE:	if the device is enabled; a device cannot
 *				be deleted unless it is disabled.
 *		TCML_NOSUCHDEV:	if no such device exists.
 */
long upv_deldev (char *devname)
{
	struct device *finddev();	/* Return device structure for device */
	struct qdevmap *map;
	struct qdevmap *prevmap;
	struct qdevmap *walkmap;
	struct device *prevdevice;
	struct device *device;

	device = finddev (devname);
	if (device == (struct device *)0) return (TCML_NOSUCHDEV);
	if (device->status & DEV_ENABLED) return (TCML_DEVENABLE);
	if (device->status & DEV_ACTIVE) return (TCML_DEVACTIVE);
	/*
	 *  Delete the device/queue mapping set for the device.
	 */
	map = device->dqmaps;
	while (map != (struct qdevmap *)0) {
		/*
		 *  Remove this mapping from the device set
		 *  as threaded from the queue.
		 */
		prevmap = (struct qdevmap *)0;
		walkmap = map->queue->v1.device.qdmaps;
		while (walkmap != map) {
			prevmap = walkmap;
			walkmap = walkmap->nextqd;
		}
		if (prevmap == (struct qdevmap *)0) {
			map->queue->v1.device.qdmaps = map->nextqd;
		}
		else prevmap->nextqd = map->nextqd;
		/*
		 *  Delete the queue/device mapping.
		 */
		walkmap = map->nextdq;
		udb_delquedev (map);	/* Update database */
		free ((char *) map);	/* Update memory */
		map = walkmap;
	}
	udb_deldev (device);		/* Delete the device from */
					/* the NQS database image */
	free (device->fullname);	/* Delete device fullname */
	/*
	 *  Remove the device descriptor from the device set.
	 */
	if (Devset == device) Devset = device->next;
	else {
		prevdevice = Devset;
		while (prevdevice->next != device) {
			prevdevice = prevdevice->next;
		}
		prevdevice->next = device->next;
	}
	free ((char *) device);		/* Delete device structure */
	Noofdevices--;			/* One less device */
	return (TCML_COMPLETE);
}


/*** upv_delquedev
 *
 *
 *	long upv_delquedev():
 *	Delete a device queue to device mapping.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHQUE:	if no such queue exists.
 *		TCML_NOSUCHMAP:	if no such mapping exists for queue.
 *		TCML_WROQUETYP:	if the queue is not a device queue.
 */
long upv_delquedev (char *quename, char *devname)
{
	struct nqsqueue *queue;
	struct qdevmap *map;
	struct qdevmap *prevmap;
	struct qdevmap *walkmap;

	queue = nqs_fndnnq (quename);
	if (queue == (struct nqsqueue *)0) return (TCML_NOSUCHQUE);
	if (queue->q.type != QUE_DEVICE) return (TCML_WROQUETYP);
	/*
	 *  Locate the device in the device set for the specified queue.
	 */
	prevmap = (struct qdevmap *) 0;
	map = queue->v1.device.qdmaps;
	while (map != (struct qdevmap *) 0 &&
	       strcmp (map->device->name, devname)) {
		prevmap = map;
		map = map->nextqd;
	}
	if (map == (struct qdevmap *) 0) return (TCML_NOSUCHMAP);
	/*
	 *  Delete the device from the device set for the specified queue.
	 */
	udb_delquedev (map);		/* Update the NQS database image */
	/*
	 *  Remove the map structure from the linked list as threaded
	 *  from the queue.
	 */
	if (prevmap == (struct qdevmap *) 0) {
		queue->v1.device.qdmaps = map->nextqd;
	}
	else prevmap->nextqd = map->nextqd;
	/*
	 *  Remove the map structure from the linked list as threaded
	 *  from the device.
	 */
	prevmap = (struct qdevmap *) 0;
	walkmap = map->device->dqmaps;
	while (walkmap != map) {
		prevmap = walkmap;
		walkmap = walkmap->nextdq;
	}
	if (prevmap==(struct qdevmap *) 0) map->device->dqmaps = map->nextdq;
	else prevmap->nextdq = map->nextdq;
	/*
	 *  Drop this mapping structure.
	 */
	free ((char *) map);
	return (TCML_COMPLETE);
}


/*** upv_disdev
 *
 *
 *	long upv_disdev():
 *	Disable an NQS device.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHDEV:	if no such device exists.
 */
long upv_disdev (char *devname)
{
	struct device *finddev();	/* Return device structure for device */
	struct device *device;

	device = finddev (devname);	/* Get device by name */
	if (device == (struct device *)0) return (TCML_NOSUCHDEV);
	device->status &= ~DEV_ENABLED;	/* Mark device as disabled */
	udb_device (device);		/* Update the NQS database image */
	return (TCML_COMPLETE);
}


/*** upv_enadev
 *
 *
 *	long upv_enadev():
 *	Enable an NQS device.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_NOSUCHDEV:	if no such device exists.
 */
long upv_enadev (char *devname)
{
	struct device *finddev();	/* Return device structure for device */
	struct device *device;

	device = finddev (devname);	/* Get device by name */
	if (device == (struct device *)0) return (TCML_NOSUCHDEV);
	if ((device->status & DEV_ACTIVE) == 0) {
		/*
		 *  The device is not active.
		 *  Enable it.
		 */
		device->status = DEV_ENABLED;
					/* Mark device as enabled */
					/* absolutely! */
		dsc_spawn();		/* Possibly spawn a device req */
	}
	else device->status |= DEV_ENABLED;
					/* Set the enabled bit, but */
					/* don't otherwise touch an */
					/* active device */
	udb_device (device);		/* Update NQS database image */
	return (TCML_COMPLETE);
}


/*** upv_setdevfor
 *
 *
 *	long upv_setdevfor():
 *	Set device forms.
 *
 *	Returns:
 *		TCML_COMPLETE:	 if successful;
 *		TCML_NOSUCHDEV:	 if the named device does not exist.
 *		TCML_NOSUCHFORM: if the specified form is not defined
 *				 in the local NQS forms set.
 */
long upv_setdevfor (char *devname, char *formsname)
{
	struct device *finddev();	/* Return device structure for device */
	struct device *device;

	device = finddev (devname);
	if (device == (struct device *)0) {
		return (TCML_NOSUCHDEV);	/* No such device */
	}
	if (upf_valfor (formsname)) {
		strcpy(device->forms,formsname);/* Save forms name */
                if ((device->status & DEV_ENABLED)  &&  /* Intergraph added */
                    (device->status & DEV_ACTIVE) == 0)
                        dsc_spawn();
		udb_device (device);	/* Update NQS database image */
		return (TCML_COMPLETE);
	}
	return (TCML_NOSUCHFORM);		/* No such form */
}


/*** upv_setdevser
 *
 *
 *	long upv_setdevser():
 *	Set device server.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful;
 *		TCML_NOSUCHDEV:	if the named device does not exist.
 */
long upv_setdevser (char *devname, char *servername)
{
	struct device *finddev();	/* Return device structure for device */
	struct device *device;

	device = finddev (devname);
	if (device == (struct device *)0) {
		return (TCML_NOSUCHDEV);	/* No such device */
	}
	strcpy (device->server, servername);	/* Copy new server */
	udb_device (device);			/* Update NQS database image */
	return (TCML_COMPLETE);
}


/*** upv_setquedev
 *
 *
 *	long upv_setquedev():
 *
 *	Set the queue to device mapping set for the specified local device
 *	queue to the single specified device.
 *
 *	Returns:
 *		TCML_COMPLETE:	if successful.
 *		TCML_INSUFFMEM:	if insufficient memory to create
 *				queue/device mapping descriptor.
 *		TCML_NOSUCHDEV:	if the named local device did not exist.
 *		TCML_NOSUCHQUE:	if the named local queue did not exist.
 *		TCML_WROQUETYP:	if the named queue is not a device queue.
 */
long upv_setquedev (char *quename, char *devname)
{
	struct device *finddev();	/* Return device structure for device */
	struct nqsqueue *queue;
	struct device *device;
	struct qdevmap *map;
	struct qdevmap *walkmap;
	struct qdevmap *walkmap2;
	struct qdevmap *prevmap;
	struct qdevmap *nextmap;

	queue = nqs_fndnnq (quename);
	if (queue == (struct nqsqueue *)0) {
		return (TCML_NOSUCHQUE);	/* No such local queue */
	}
	device = finddev (devname);
	if (device == (struct device *)0) {
		return (TCML_NOSUCHDEV); 	/* No such device */
	}
	if (queue->q.type != QUE_DEVICE) return (TCML_WROQUETYP);
	/*
	 *  Determine if the device is already in the device mapping set
	 *  for the specified queue.
	 */
	map = queue->v1.device.qdmaps;
	while (map != (struct qdevmap *) 0 &&
	       strcmp (map->device->name, devname) != 0) {
		map = map->nextqd;		/* Get next dev in set */
	}
	if (map == (struct qdevmap *) 0) {
		/*
		 *  The specified queue/device mapping does not exist.
		 *  Create it.
		 */
		map = (struct qdevmap *) malloc (sizeof (struct qdevmap));
		if (map == (struct qdevmap *) 0) {
			return (TCML_INSUFFMEM);/* Insufficient memory */
		}
		map->device = device;		/* Ptr to device */
		map->queue = queue;		/* Ptr to queue */
		/*
		 *  Now, complete the process of adding the device mapping to
		 *  the mapping set for this device queue.  The mapping is
		 *  not added added in lexicographic order, but this does
		 *  not matter, since was are going to delete all of the
		 *  queue to device mappings below....
		 */
		map->nextqd = queue->v1.device.qdmaps;
						/* Add to queue/device */
		queue->v1.device.qdmaps = map;	/* mapping set for queue */
		/*
		 *  Add the inverse mapping in the UNORDERED device-to-queue
		 *  mapping set.
		 */
		map->nextdq = device->dqmaps;	/* Add to device/queue */
		device->dqmaps = map;		/* mapping set for device */
		udb_addquedev (map);		/* Add the new mapping to */
						/* the NQS database image */
	}
	/*
	 *  Now, walk the device mapping set to delete ALL mappings with
	 *  the exception of the chosen single device mapping.
	 */
	walkmap = queue->v1.device.qdmaps;
	while (walkmap != (struct qdevmap *) 0) {
		nextmap = walkmap->nextqd;	/* Next queue/device mapping */
		if (walkmap != map) {
			/*
			 *  Remove the map structure from the linked list as
			 *  threaded from the device.
			 */
			prevmap = (struct qdevmap *) 0;
			walkmap2 = walkmap->device->dqmaps;
			while (walkmap2 != walkmap) {
				prevmap = walkmap2;
				walkmap2 = walkmap2->nextdq;
			}
			if (prevmap == (struct qdevmap *) 0) {
				walkmap->device->dqmaps = walkmap->nextdq;
			}
			else prevmap->nextdq = walkmap->nextdq;
			/*
			 *  Delete the queue/device mapping.
			 */
			udb_delquedev (walkmap);	/* Update database */
			free ((char *) walkmap);	/* Update memory */
		}
		walkmap = nextmap;
	}
	map->nextqd = (struct qdevmap *) 0;	/* Queue-to-device mapping */
	queue->v1.device.qdmaps = map;		/* set is a set of one entry */
	if ((map->queue->q.status & QUE_RUNNING) &&
	     map->queue->q.queuedcount &&
	    (map->device->status & DEV_ENABLED) &&
	    (map->device->status & DEV_ACTIVE) == 0) {
		/*
		 *  The device is enabled, and is not active.
		 *  The queue is running, but other req(s) could run.
		 */
		dsc_spawn();		/* Maybe spawn a device req */
	}
	return (TCML_COMPLETE);
}


/*** finddev
 *
 *
 *	struct device *finddev():
 *	Return pointer to device structure for the named device.
 *
 *	Returns:
 *		A pointer to the device structure for the named device,
 *		if the named device exists; otherwise (struct device *)0
 *		is returned.
 */
static struct device *finddev (char *devname)
{
	struct device *device;
	int res;

	/*
	 *  We take advantage of the fact that the device set is
	 *  lexicographically ordered.
	 */
	device = Devset;
	while (device != (struct device *)0) {
		if ((res = strcmp (device->name, devname)) < 0) {
			/*
			 *  Keep searching.
			 */
			device = device->next;
		}
		else if (res == 0) return (device);	/* Found! */
		else return ((struct device *)0);	/* Not found */
	}
	return (device);				/* Not found */
}

