/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)libadm:devreserv.c	1.2.7.1"
/*LINTLIBRARY*/

/*
 * Globals defined:
 *
 *	devreserv()	Reserve a set of OA&M devices
 *	devfree()	Free a reserved device
 *	reservdev()	Get a list of reserved devices
 *	_openlkfile()	Opens the lock file
 *	_rsvtabpath()	Get the pathname of the lock table file
 *	_closelkfile()	Closes the lock file
 */

/*
 * Headers referenced:
 *	<sys/types.h>	System data types
 *	<errno.h>	Error definitions (including "errno")
 *	<string.h>	String handling definitions
 *	<fcntl.h>	File control definitions
 *	<unistd.h>	Unix standard value definitions
 *	<devmgmt.h>	Global Device Management definitions
 *	"devtab.h"	Local Device Management definitions
 */

#include	<sys/types.h>
#include	<errno.h>
#include	<string.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	<devmgmt.h>
#include	"devtab.h"


/*
 * Externals referenced (and not defined in the header files)
 *	malloc()	Allocate a block of main memory
 *	free()		Free malloc()ed memory
 *	sleep()		Wait
 *	lseek()		Position a file pointer
 *	getenv()	Get data from the environment
 *	close()		Close an open file
 *	read()		Read from an open file
 *	write()		Write to an open file
 *	kill()		Send a signal to a process
 */

	void	       *malloc();
	void		free();
	unsigned	sleep();
	long		lseek();
	char	       *getenv();
	int		close();
	int		read();
	int		write();
	int		kill();

/*
 * Local Definitions:
 */


/*
 * Local data types:
 *	struct devlks	Structure that defines locking information (key
 *			with alias name (may be '\0' terminated)
 */

struct devlks {
	long	lk_key;
	char	lk_alias[((DTAB_MXALIASLN+2)/2)*2];
};


/*
 * Local Functions:
 *	isanullstr()	Is a character string a null string ("")?
 *	getlkcnt()	Get the number of devices locked
 *	locklkfile()	Lock the OA&M Device locking file
 *	getlocks()	Get the device locks from the device-lock file
 *	islocked()	Determines if a device is locked
 *	putlocks()	Close the device locks w/ update
 *	freelkfile()	Close the device locks w/o updating
 *	compresslks()	Compresses the table containing lock info
 */

#define	isanullstr(s)	(s[0]=='\0')

static	int	locklkfile();		/* Lock the lock file */
static	int	getlkcnt();		/* Get the number of locked devices */
static	int	getlocks();		/* Get the lock information */
static	int	putlocks();		/* Update lock information */
static	int	freelkfile();		/* Free lock information (no update) */
static	char   *islocked();		/* Determines if a device is locked */


/*
 * Static data
 */

static	struct flock	lkinfo = {0, 0, 0, 0, 0};
static	struct devlks  *locklist;
static	int		lockcount;
static	int		lkfilefd = -1;

/*
 * char *_rsvtabpath()
 *
 *	Determines the pathname of the device reservation table file
 *
 *	Uses the following sequential steps:
 *	     1)	If OAM_DEVLKFILE is defined and is not null, use that as
 *		the pathname to the file
 *	     2)	Otherwise, use the devault name found in DVLK_PATH (defined
 *		in the header file <devtab.h>
 *
 *  Arguments:  None
 *
 *  Returns:  char *
 *	A pointer to the filename in malloc()ed memory or (char *) NULL if
 *	it fails.  "errno" will indicate the error if it fails.
 */

char *
_rsvtabpath()
{
	/* Automatics */
	char	       *lockname;	/* Name of the lockfile */
#ifdef	DEBUG
	char	       *p;		/* Temporary pointer */
#endif

#ifdef	DEBUG
	p = getenv(OAM_DEVLKTAB);
	if ((p != (char *) NULL) && (*p != '\0')) {
	    if (lockname = (char *) malloc(strlen(p)+1))
		(void) strcpy(lockname, p);
	}
	else {
#endif
	    if (lockname = (char *) malloc(strlen(DVLK_PATH)+1)) (void) strcpy(lockname, DVLK_PATH);

#ifdef	DEBUG
	}
#endif

	/* Fini -- return a pointer to the lockfile pathname */
	return(lockname);
}

/*
 *  int _openlkfile()
 *
 *	The _openlkfile() function opens a device-reservation table file
 *	for read/write access.
 *
 *  Arguments: None
 *
 *  Returns:  int
 *	TRUE if successful, FALSE otherwise.
 *
 *  Statics Used:
 *	lkfilefd	Lock file file descriptor
 */

int
_openlkfile()
{
	/*
	 *  Automatic data
	 */

	char   *lockname;		/* Name of the lock file */


	/* Close the lockfile -- it might be open */
	(void) _closelkfile();

	/* If we can get the name of the lock file ... */
	if (lockname = _rsvtabpath()) {

	    /* Open it */
	    lkfilefd = open(lockname, O_RDWR|O_CREAT, 0600);
	    free(lockname);

	}

	/*  Finis  */
	return((lkfilefd != -1) ? TRUE : FALSE);
}

/*
 * int _closelkfile()
 *
 *	Function closes the device-reservation table file and sets the
 *	necessary external variables to indicate such.
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *	Same as close()
 *
 *  Statics referenced:
 *	lkfilefd	The device reservation table file's file descriptor
 */

int
_closelkfile()
{
	/* Automatics */
	int	rtnval;		/* Value to return */

	/* Close the lock file if it's open */
	if (lkfilefd != -1) rtnval = close(lkfilefd);
	else rtnval = 0;

	/* Indicate that the lock-file is closed */
	lkfilefd = -1;

	/* Finis */
	return(rtnval);
}

/*
 *  int locklkfile(lkflag)
 *	int		lkflag
 *
 *	This function locks the device lock file.  If the request cannot
 *	be serviced, it keeps on trying until it manages to lock the file
 *	or it encounters an error.
 *
 *  Arguments:
 *	lkflag		Flag (from FCNTL(BA_OS)) indicating which type
 *			of lock is being requested.  Values that make
 *			sense:
 *				F_RDLCK:	Read lock.
 *				F_WRLCK:	Write lock.
 *
 *  Returns: int
 *	TRUE (non-zero) if the function managed to lock the file, FALSE
 *	otherwise ("errno" will indicate the problem).
 *
 *  Statics used:
 *	int lkfilefd		File descriptor of the open lock file
 *	struct flock lkinfo	Structure used by fcntl() to lock a file
 */

static	int
locklkfile(lkflag)
	int		lkflag;		/* Type of lock requested */
{
	/* Automatic data */
	int		noerror;	/* TRUE if no error yet */
	int		locked;		/* TRUE if the file is locked */
	int		olderrno;	/* Value of errno on call */


	/* Set up the locking structure */
	lkinfo.l_type = (short) lkflag;

	/* Try to lock the file.  If it's locked, wait and try again */
	noerror = TRUE;
	locked = FALSE;
	olderrno = errno;
	while (noerror && !locked) {
	    if (fcntl(lkfilefd, F_SETLK, &lkinfo) != -1) locked = TRUE;
	    else {
		if ((errno == EACCES) || (errno == EAGAIN)) {
		    errno = olderrno;
		    if (sleep(2)) noerror = FALSE;
		}
		else noerror = FALSE;
	    }
	}

	/* Return a success flag */
	return(locked);
}

/*
 *  int getlkcnt()
 *
 *	This function extracts the number of currently-locked devices
 *	from the lock file.
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *	The number of devices locked or -1 if an error occurred.
 *
 *  Statics used:
 *	lkfilefd	File descriptor of the open lockfile
 *
 *  Assumptions:
 *    -	The file is positioned to the beginning-of-file
 */

static	int
getlkcnt()
{
	/* Automatics */
	int	cntread;		/* Number of bytes read */
	int	lkcnt;			/* Number of current locks */

	/* Get the lock count from the file */
	cntread = read(lkfilefd, (char *) &lkcnt, sizeof(int));

	/* If there wasn't one, set to 0.  If error, set to -1 */
	if (cntread != sizeof(int)) lkcnt = (cntread < 0) ? -1 : 0;

	/* Return the lock count */
	return(lkcnt);
}

/*
 *  int readlocks()
 *
 *	The readlocks() function reads the reserved-device list from
 *	the reserved-device file (which has already been opened)
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *	TRUE if all went well, FALSE otherwise.
 *
 *  Statics Used:
 *	lockcount	Sets this to the number of locks in the lock list
 *	locklist	Sets this to the malloc()ed space containing the
 *			list of reserved devices.
 *	lkfilefd	Reads data from this file
 */

static	int
readlocks()
{
	/* Automatics */
	struct devlks  *alloc;		/* Ptr to alloc'ed space */
	int		noerror;	/* TRUE if all is well */
	int		bufsiz;		/* # bytes needed for lock data */


	/* Initializations */
	noerror = TRUE;

	/* Get the number of devices currently locked */
	if ((lockcount = getlkcnt()) > 0) {

	    /* Allocate space for the locks */
	    bufsiz = lockcount * sizeof(struct devlks);
	    if (alloc = (struct devlks *) malloc((unsigned) bufsiz)) {

		/* Read the locks into the malloc()ed buffer */
		if (read(lkfilefd, (char *) alloc, (unsigned) bufsiz) != bufsiz)
		    noerror = FALSE;

		/* If the read failed, free malloc()ed buffer */
		if (!noerror) free((char *) alloc);

	    } else noerror = FALSE;  /* malloc() failed */

	}
	else if (lockcount < 0) noerror = FALSE;

	/* Finished */
	if (noerror) locklist = (lockcount > 0) ? alloc : (struct devlks *) NULL;
	return(noerror);
}

/*
 *  int getlocks()
 *
 *	getlocks() extracts the list of locked devices from the file
 *	containing that information.  It returns the number of locked
 *	devices.  If there are any locked devices, it allocates a buffer
 *	for the locked file information, saves that buffer address in
 *	the allocated buffer.  Also, the device lock file is open and
 *	locked if the function is successful.
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *	TRUE if successful, FALSE otherwise.  "errno" will reflect the
 *	error if the function returns FALSE.
 *
 *  Static data referenced:
 *	int lkfilefd			File descriptor of the lock file
 */

static	int
getlocks()
{
	/* Automatic data */
	int		noerror;	/* TRUE if all's well */


	/* Initializations */
	noerror = TRUE;

	/* Open the lock file */
	if (_openlkfile()) {

	    /* Lock the lock file */
	    if (locklkfile(F_WRLCK)) {

		/* Get the number of devices currently locked */
		if (!readlocks()) noerror = FALSE;

		/* If something happened, unlock the file */
		if (!noerror) (void) freelkfile();

	    } else noerror = FALSE;  /* Lock failed */

	    /* If something happened, close the lock file */
	    if (!noerror) _closelkfile();

	} else noerror = FALSE;				/* Open failed */

	/* Done */
	return(noerror);
}

/*
 *  int writelks(tblcnt)
 *	int	tblcnt
 *
 *	writelks() writes the lock information to the lock file.  Lock
 *	information includes the number of locks (to be) in the table.
 *	Note that functions may still be appending new locks after this
 *	call...
 *
 *  Arguments:
 *	tblcnt	Number of locks in the lock table
 *
 *  Returns:
 *	TRUE if successful, FALSE otherwise with "errno" containing an
 *	indication of the error.
 *
 *  Statics Used:
 *	lockcount	Number of locks to exist
 *	locklist	Table of locks (may not include new ones)
 *	lkfilefd	File descriptor of the lock file
 *
 *  Notes:
 *    - The number of locks that are going to be in the lock file
 *	is in the static variable "lockcount".  <tblcnt> indicates
 *	the number of entries in the lock table.
 */

static	int
writelks(tblcnt)
	int		tblcnt;		/* Number of locks in lock structure */
{
	/* Automatic data */
	int		noerr;		/* FLAG, TRUE if all's well */
	unsigned	tblsz;		/* Size of the table to write */

	/* Initializations */
	noerr = TRUE;

	/* Rewind the OA&M Device Lock File */
	if (lseek(lkfilefd, 0L, 0) >= 0L)

	    /* Write the number of locks that will (eventually) exist */
	    if (write(lkfilefd, (char *) &lockcount, sizeof(int)) == sizeof(int)) {

		/* Write the table as we currently know it */
		tblsz = tblcnt * sizeof(struct devlks);
		if (tblsz)
		    if (!write(lkfilefd, (char *) locklist, tblsz) == tblsz)
			noerr = FALSE;  /* Write of locks failed */

	    } else noerr = FALSE;  /* write() of count failed */

	else noerr = FALSE;  /* Rewind failed */

	/* Return an indicator of our success */
	return(noerr);
}

/*
 * int appendlk(key, alias)
 *	long	key
 *	char   *alias
 *
 *	Write device locking information to the device locking file.
 *
 *  Arguments:
 *	key	Key the device is being locked on
 *	alias	The device alias being locked
 *
 *  Returns:  int
 *	TRUE if we successfully appended a lock to the lock file,
 *	FALSE with "errno" set otherwise.
 *
 *  Static data used:
 *	lkfilefd	The open file descriptor for the open device
 *			locking file
 */

static	int
appendlk(key, alias)
	long	key;		/* Lock key */
	char   *alias;		/* Alias to lock */
{
	/* Automatic data */
	struct devlks	lk;	/* Structure for writing a lock */

	/* Set up the data to write */
	lk.lk_key = key;
	(void) strcpy(lk.lk_alias, alias);

	/* Write the data, returning an indicator of our success */
	return(write(lkfilefd, (char *) &lk, sizeof(struct devlks)) == sizeof(struct devlks));
}

/*
 *  int compresslks()
 *
 *	This function compresses the lock table, squeezing out the empty
 *	lock entries.
 *
 *  Arguments:  none
 *
 *  Returns:  int
 *	The number of non-empty entries in the table.  They will be the
 *	first 'n' entries in the table after compression.
 *
 *  Statics Used
 *	lockcount	Number of locks in the device lock list
 *	locklist	The device lock list
 */

static	int
compresslks()
{
	/* Automatics */
	struct devlks  *avail;		/* Pointer to empty slot */
	struct devlks  *p;		/* Running pointer to locks */
	int		nlocks;		/* Number of locks (up to date) */
	int		i;		/* Temporary counter */

	/* Initializations */
	p = locklist;
	nlocks = lockcount;
	avail = (struct devlks *) NULL;

	/* Loop through the lock list squeezing out unused slots */
	for (i = 0 ; i < lockcount ; i++) {

	    /* If we've found an empty slot ... */
	    if (isanullstr(p->lk_alias)) {

		/* If we've an empty slot to move to, just decrement
		 * count of used slots.  Otherwise, make it the next
		 * available slot */

		nlocks--;
		if (!avail) avail = p;
	    }

	    else if (avail) {

		/* If we found a slot in use and there's an
		 * available slot, move this one there */

		(void) strcpy(avail->lk_alias, p->lk_alias);
		avail->lk_key = p->lk_key;
		avail++;
	    }

	    /* Next, please */
	    p++;
	}

	return(nlocks);
}

/*
 *  int freelkfile()
 *
 *	This function unlocks the OA&M device locking file.
 *
 *  Arguments:  None
 *
 *  Returns:  int
 *	TRUE if it successfully unlocked the file, FALSE otherwise
 *	with "errno" set to indicate the problem.
 *
 *  Statics Used:
 *	lkinfo		File-locking structure
 *	lkfilefd	File-descriptor of the open lock file
 */

static	int
freelkfile()
{
	/* Automatic data */
	int		noerr;		/* TRUE if all's well */

	/* Initializations */
	noerr = TRUE;

	/* Set the action to "unlock" */
	lkinfo.l_type = F_UNLCK;

	/* Unlock the file */
	noerr = (fcntl(lkfilefd, F_SETLK, &lkinfo) != -1);

	/* Return an indication of our success */
	return(noerr);
}

/*
 * int putlocks(newlist, key)
 *	char  **newlist
 *	long	key
 *
 *	This function updates the file containing OA&M device locks.
 *
 *  Arguments:
 *	newlist		The address of the list of addresses of device
 *			aliases to add to the list of locked devices
 *	key		The key on which to lock the devices
 *
 *  Returns:  int
 *	TRUE if all went well, FALSE otherwise with "errno" set to an
 *	error code that indicates the problem.
 *
 *  Statics Used:
 *	lockcount	Number of locks in the locked device structure
 *	locklist	Locked device structure
 */

static	int
putlocks(newlist, key)
	char	      **newlist;	/* New devices to lock */
	long		key;		/* Key we're locking stuff on */
{
	/* Automatic data */
	struct devlks  *plk;		/* Ptr into the locks list */
	char	      **pp;		/* Pointer into the device list */
	char	      **qq;		/* Another ptr into the dev list */
	int		lkndx;		/* Index into locks list */
	int		noerr;		/* TRUE if all's well */
	int		lksintbl;	/* Number of locks in the table */


	/* Initializations */
	noerr = TRUE;

	/* Look through the existing lock list, looking for holes we can
	 * use for the newly locked devices */

	plk = locklist;
	pp = newlist;
	lkndx = 0;
	while (*pp && (lkndx < lockcount)) {
	    if (isanullstr(plk->lk_alias)) {
		plk->lk_key = key;
		(void) strcpy(plk->lk_alias, *pp++);
	    }
	    lkndx++;
	    plk++;
	}

	/* Update the locks file (algorithm depends on whether we're adding
	 * new locks or not.  May be replacing old locks!) */

	if (*pp) {

	    /*
	     * Need to expand the locks file
	     *  - Remember the old lock count (in existing lock buffer)
	     *  - Count the number of new locks we need to add
	     *  - Write out the old locks structure
	     *  - Append locks for the newly added locks
	     */

	    lksintbl = lockcount;
	    for (qq = pp; *qq; qq++) lockcount++;
	    noerr = writelks(lksintbl);
	    while (noerr && *pp) noerr = appendlk(key, *pp++);
	}
	else {

	    /* Don't need to expand the locks file.  Compress the locks
	     * then write out the locks information */

	    lockcount = compresslks();
	    noerr = writelks(lockcount);
	}

	/* Done.  Return an indication of our success */
	return(noerr);
}

/*
 * char *islocked(device)
 *	char	       *device
 *
 *	This function checks a device to see if it is locked.  If it is
 *	not locked, it returns the device alias.
 *
 *	A device is not locked if the device's alias does not appear in
 *	the device locks table, or the key on which the device was locked
 *	is no longer active.
 *
 *  Argumetns:
 *	char *device		The device to be reserved.  This can be
 *				a pathname to the device or a device
 *				alias.
 *
 *  Returns:  char *
 *	Returns a pointer to the device alias if it's not locked, or
 *	(char *) NULL if it's locked or some error occurred.
 *
 *  Static data used:
 *	struct devlks *locklist		Pointer to the list of device locks
 *	int lockcount			The number of devices that are locked
 */

static	char *
islocked(device)
	char   *device;
{
	/* Automatic data */
	char	       *alias;		/* Alias of "device" */
	struct devlks  *plk;		/* Ptr to locking info */
	int		locked;		/* TRUE if device in locked list */
	int		i;		/* Temp counter */

	/* Get the device's alias */
	if (alias = devattr(device, DTAB_ALIAS)) {

	    /* Look through the device locks to see if this device alias
	     * is locked */

	    locked = FALSE;
	    plk = locklist;
	    for (i = 0 ; !locked && (i < lockcount) ; i++) {
		if (strncmp(alias, plk->lk_alias, DTAB_MXALIASLN) == 0)
		    locked = TRUE;
		else plk++;
	    }

	    if (locked) {
		    free(alias);
		    alias = (char *) NULL;
		    errno = EAGAIN;
	    }

	}  /* devattr() failed, no such device? */

	/* Return pointer to the device */
	return(alias);
}

/*
 *  int unreserv(key, device)
 *	long	key
 *	char   *device
 *
 *	This function removes a device reservation.
 *
 *  Arguments:
 *	long key	The key on which the device was allocated
 *	char *device	The device to be freed.
 *
 *  Returns:  int
 *	TRUE if successful, FALSE otherwise with "errno" set.
 *
 *  Explicit "errno" settings:
 *	(This follows the "signal()" model which gives one the ability
 *	to determine if a device is allocated without having the
 *	permission to free it.)
 *
 *	EINVAL	The device specified was not locked
 *	EPERM	The device specified was locked but not on the
 *		specified key
 *
 *  Static data used:
 *	locklist	List of locked devices
 *	lockcount	Number of entries in the locked-device list
 */

int
unreserv(key, device)
	long	key;
	char   *device;
{
	/* Automatics */
	char	       *srchalias;	/* Device alias to search table with */
	char	       *alias;		/* Device's alias (from devattr()) */
	struct devlks  *plk;		/* Pointer to a device lock */
	int		locked;		/* TRUE if device currently locked */
	int		noerr;		/* TRUE if all's well */
	int		olderrno;	/* Entry value of "errno" */
	int		i;		/* Counter of locks */


	/* Initializations */
	noerr = TRUE;

	/* Get the device alias.  If none can be found, try to free
	 * whatever it is that was given to us (the possibility exists
	 * that the device has been removed from the device table since
	 * it was reserved, so the device not being in the table shouldn't
	 * pose too much of a problem with us...) */

	olderrno = errno;
	if (alias = devattr(device, DTAB_ALIAS)) srchalias = alias;
	else {
	    errno = olderrno;
	    srchalias = device;
	}

	/* Loop through the locked-device list looking for what we've got... */
	locked = FALSE;
	plk = locklist;
	for (i = 0 ; !locked && (i < lockcount) ; i++) {
	    if (strcmp(srchalias, plk->lk_alias) == 0)
		locked = TRUE;
	    else plk++;
	}

	/* Free the alias string (if any), we don't need it anymore */
	if (alias) free(alias);

	/* If the device is locked ... */
	if (locked) {

	    /* If it's locked on the key we've been given, free it.
	     * Otherwise, don't free it and set errno to EPERM */

	    if (plk->lk_key == key) {
		plk->lk_alias[0] = '\0';
	    } else {
		noerr = FALSE;
		errno = EPERM;
	    }
	} else {

	    /* The device isn't locked.  Set errno to EINVAL */
	    noerr = FALSE;
	    errno = EINVAL;
	}

	/* Finished.  Return an indication of our success */
	return(noerr);
}

/*
 *  char **devreserv(key, rsvlst)
 *	long		key
 *	char	      **rsvlist[]
 *
 *	The devreserv() function reserves devices known to the OA&M Device
 *	Management family of functions.  Once a device is reserved, it can't
 *	be reserved by another until it is freed or the process with the
 *	"key" is no longer active.  It returns a list aliases of the devices
 *	it allocated.
 *
 *	The function attempts to reserve a single device from each of the
 *	lists.  It scans each list sequentially until it was able to
 *	reserve a requested device.  If it successfully reserved a device
 *	from each of the lists, it updates the device-locked file and
 *	returns those aliases to the caller.  If it fails, it allocates
 *	nothing and returns (char **) NULL to the caller.  "errno"
 *	indicates the error.
 *
 *  Arguments:
 *	long key		The key on which this device is being reserved.
 *
 *	char **rsvlist[]	The address of the list of addresses of lists
 *				of pointers to the devices to allocate.
 *
 *  Returns:  char **
 *	A pointer to malloc()ed space containing pointers to the aliases
 *	of the reserved devices.  The aliases are in malloc()ed space also.
 *	The list is terminated by the value (char *) NULL.
 *
 *  Static Data Used:
 *	None directly, but functions called share hidden information
 *	that really isn't of concern to devreserv().
 */

char **
devreserv(key, rsvlst)
	long		key;		/* Key to reserve device on */
	char	      **rsvlst[];	/* List of lists of devs to reserve */
{
	char	     ***ppp;		/* Ptr to current list in rsvlist */
	char	      **pp;		/* Ptr to current item in list */
	char	      **qq;		/* Ptr to item in rtnlist */
	char	      **rr;		/* Ptr to item in aliases */
	char	      **aliases;	/* List of aliases allocated */
	char	      **rtnlist;		/* Ptr to buf to return */
	char	       *alias;		/* Alias of dev to reserve */
	int		noerr;		/* TRUE if all's well */
	int		olderrno;	/* Old value of errno */
	int		gotone;		/* TRUE if unreserved dev found */
	int		foundone;	/* Found a valid device in the list */
	int		ndevs;		/* # of devs to reserve */

	noerr = TRUE;
	ppp = rsvlst;
	olderrno = errno;
	for (ndevs = 0 ; *ppp++ ; ndevs++) ;
	if (rtnlist = (char **) malloc((unsigned) (ndevs+1)*sizeof(char **))) {
	    if (aliases = (char **) malloc((unsigned) (ndevs+1)*sizeof(char **))) {
		if (getlocks()) {
		    qq = rtnlist;
		    rr = aliases;

		    /* Go through the lists of devices we're to reserve */

		    for (ppp = rsvlst ; noerr && *ppp ; ppp++) {

			/* Try to reserve a device from each list */
			gotone = FALSE;
			foundone = FALSE;
			for (pp = *ppp ; noerr && !gotone && *pp ; pp++) {

			    /* Check the next device in the list.  If islocked()
			     * returns that device's alias, it's ours to have */

			    if (alias = islocked(*pp)) {
				gotone = TRUE;
				foundone = TRUE;
				if (*qq = (char *) malloc((unsigned) strlen(*pp)+1)) {
				    (void) strcpy(*qq++, *pp);
				    *rr++ = alias;
				} else {
				    *rr = (char *) NULL;
				    noerr = FALSE;
				}
			    }
			    else {
				if (errno == EAGAIN) {
				    foundone = TRUE;
				    errno = olderrno;
				} else if (errno == ENODEV) errno = olderrno;
				else {
				    noerr = FALSE;
				    *rr = (char *) NULL;
				}
			    }
			}

			/* If no device from the list could be reserved,
			 * we've failed */

			if (noerr && !gotone) {
			    noerr = FALSE;
			    if (!foundone) errno = ENODEV;
			    else errno = EAGAIN;
			    *qq = (char *) NULL;
			    *rr = (char *) NULL;
			}

		    } /* End of loop through lists loop */

		    /*
		     * If all went well, update lock file.
		     * Then, free locks
		     */

		    if (noerr) {
			*qq = (char *) NULL;
			*rr = (char *) NULL;
			if (!putlocks(aliases, key)) noerr = FALSE;
		    }

		    /* Free resources */
		    if (!freelkfile()) noerr = FALSE;
		    if (_closelkfile() != 0) noerr = FALSE;
		    for (qq = aliases ; *qq ; qq++) free(*qq);
		    if (!noerr) for (pp = rtnlist ; *pp ; pp++) free(*pp);

		} else noerr = FALSE; /* Error getting locks */

		free((char *) aliases);

	    } else noerr = FALSE;  /* Malloc() for alias list failed */

	    if (!noerr) {
		free((char *) rtnlist);
		rtnlist = (char **) NULL;
	    }

	} else noerr = FALSE;  /* malloc() failed */

	/* Return list or an indication of an error */
	return(noerr ? rtnlist : (char **) NULL);
}

/*
 *  int devfree(key, device)
 *	long	key
 *	char   *device
 *
 *	This function unreserves (frees) the given device.  It returns
 *	an indication of success with "errno" containing information about
 *	a failure.
 *
 *  Arguments:
 *	long	key	The key that the device is locked on
 *	char   *device	The device (alias, pathname to, etc.) to be freed.
 *
 *  Returns:  int
 *	0 if successful, -1 with "errno" set if fails.
 */

int
devfree(key, device)
	long	key;			/* Key device is locked on */
	char   *device;			/* Device to free */
{
	/* Automatics */
	int	noerr;

	/* Initializations */
	noerr = TRUE;

	/* Get the locks, locking the lock file */
	if (getlocks()) {

	    /* Attempt to unreserve the device */
	    if (unreserv(key, device)) {

		/* Successful.  Compress the lock structure and
		 * write the new locks */

		lockcount = compresslks();
		if (!writelks(lockcount)) noerr = FALSE;

	    } else noerr = FALSE;  /* Couldn't unreserve the device */

	    /* Unlock and close the locks file */
	    if (!freelkfile()) noerr = FALSE;
	    if (_closelkfile() != 0) noerr = FALSE;

	} else noerr = FALSE;

	/* Return 0 if successful, something else otherwise */
	return(noerr? 0 : -1);
}

/*
 *  struct reservdev **reservdev()
 *
 *	This function returns the list of reserved devices
 *	along with the key on which those devices were locked.
 *
 *  Arguments:  None.
 *
 *  Returns:  struct reservdev **
 *	Pointer to the list of pointers to structures describing
 *	the reserved devices, or (struct reservdev **) NULL if an
 *	error occurred.  The list of pointers is terminated by
 *	(struct reservdev *) NULL.
 *
 *  Statics Used:
 *	locklist	List of reserved devices
 *	lockcount	Number of items in the reserved-devices list
 */

struct reservdev **
reservdev()
{
	/* Automatics */
	struct reservdev      **rtnlist;	/* Ptr to return list */
	struct devlks	       *p;		/* Running ptr, locklist */
	struct reservdev      **q;		/* Running ptr, rtnlist */
	char		       *r;		/* Temp ptr to char */
	unsigned		bufsiz;		/* Size of buffer to alloc */
	int			noerr;		/* TRUE if all's well */
	int			i;		/* Lock counter */


	/* Initializations */
	noerr = TRUE;

	/* Open the lock file ... */
	if (_openlkfile()) {

	    /* Put a read-lock on the lock-file ... */
	    if (locklkfile(F_RDLCK)) {

		/* Read the locks ... */
		if (readlocks()) {

		    /* Alloc space for the return list */
		    bufsiz = (lockcount+1) * sizeof(struct reservdev *);
		    if (rtnlist = (struct reservdev **) malloc(bufsiz)) {

			/* Build the return list from the lock list */
			p = locklist;
			q = rtnlist;
			for (i = 0 ; noerr && (i < lockcount) ; i++) {
			    if (*q = (struct reservdev *) malloc(sizeof(struct reservdev))) {
				if (r = (char *) malloc(strlen(p->lk_alias)+1)) {
				    (*q)->devname = strcpy(r, p->lk_alias);
				    (*q)->key = p->lk_key;
				} else noerr = FALSE;  /* malloc() error */
			    } else noerr = FALSE;  /* malloc() error */
			    p++;
			    q++;
			}

			/* If no error, terminate the list.  Otherwise, free
			 * the space we've allocated */

			if (noerr) *q = (struct reservdev *) NULL;
			else {
			    for (q = rtnlist ; *q ; q++) {
				free((*q)->devname);
				free(*q);
			    }
			    free(rtnlist);
			}

		    } else noerr = FALSE;  /* Couldn't malloc() list space */

		} else noerr = FALSE;  /* Problem reading locks */

		/* Free the lock file */
		freelkfile();

	    } else noerr = FALSE;  /* Error locking the lock file */

	    /* Close the lock file */
	    _closelkfile();

	} else noerr = FALSE;  /* Error opening the lock file */

	/* Return ptr to list of locks or NULL if an error has occurred */
	return(noerr ? rtnlist : (struct reservdev **) NULL);
}
