xref: /illumos-gate/usr/src/lib/libadm/common/devreserv.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1997, by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /*LINTLIBRARY*/
33 
34 /*
35  * Globals defined:
36  *
37  *	devreserv()	Reserve a set of OA&M devices
38  *	devfree()	Free a reserved device
39  *	reservdev()	Get a list of reserved devices
40  *	_openlkfile()	Opens the lock file
41  *	_rsvtabpath()	Get the pathname of the lock table file
42  *	_closelkfile()	Closes the lock file
43  */
44 
45 /*
46  * Headers referenced:
47  *	<sys/types.h>	System data types
48  *	<errno.h>	Error definitions (including "errno")
49  *	<string.h>	String handling definitions
50  *	<fcntl.h>	File control definitions
51  *	<unistd.h>	Unix standard value definitions
52  *	<devmgmt.h>	Global Device Management definitions
53  *	"devtab.h"	Local Device Management definitions
54  */
55 
56 #include	<sys/types.h>
57 #include	<errno.h>
58 #include	<string.h>
59 #include	<fcntl.h>
60 #include	<unistd.h>
61 #include	<stdlib.h>
62 #include	<devmgmt.h>
63 #include	"devtab.h"
64 
65 /*
66  * Local Definitions:
67  */
68 
69 
70 /*
71  * Local data types:
72  *	struct devlks	Structure that defines locking information (key
73  *			with alias name (may be '\0' terminated)
74  */
75 
76 struct devlks {
77 	int	lk_key;
78 	char	lk_alias[((DTAB_MXALIASLN+2)/2)*2];
79 };
80 
81 
82 /*
83  * Local Functions:
84  *	isanullstr()	Is a character string a null string ("")?
85  *	getlkcnt()	Get the number of devices locked
86  *	locklkfile()	Lock the OA&M Device locking file
87  *	getlocks()	Get the device locks from the device-lock file
88  *	islocked()	Determines if a device is locked
89  *	putlocks()	Close the device locks w/ update
90  *	freelkfile()	Close the device locks w/o updating
91  *	compresslks()	Compresses the table containing lock info
92  */
93 
94 #define	isanullstr(s)	(s[0] == '\0')
95 
96 static	int	locklkfile(short);	/* Lock the lock file */
97 static	int	getlkcnt(void);		/* Get the number of locked devices */
98 static	int	getlocks(void);		/* Get the lock information */
99 static	int	putlocks(char **, int); /* Update lock information */
100 static	int	freelkfile(void);	/* Free lock information (no update) */
101 static	char   *islocked(char *);	/* Determines if a device is locked */
102 
103 
104 /*
105  * Static data
106  */
107 
108 static	struct flock	lkinfo = {0, 0, 0, 0, 0};
109 static	struct devlks  *locklist;
110 static	int		lockcount;
111 static	int		lkfilefd = -1;
112 
113 /*
114  * char *_rsvtabpath()
115  *
116  *	Determines the pathname of the device reservation table file
117  *
118  *	Uses the following sequential steps:
119  *	     1)	If OAM_DEVLKFILE is defined and is not null, use that as
120  *		the pathname to the file
121  *	     2)	Otherwise, use the devault name found in DVLK_PATH (defined
122  *		in the header file <devtab.h>
123  *
124  *  Arguments:  None
125  *
126  *  Returns:  char *
127  *	A pointer to the filename in malloc()ed memory or (char *) NULL if
128  *	it fails.  "errno" will indicate the error if it fails.
129  */
130 
131 char *
132 _rsvtabpath(void)
133 {
134 	/* Automatics */
135 	char		*lockname;	/* Name of the lockfile */
136 #ifdef	DEBUG
137 	char		*p;		/* Temporary pointer */
138 #endif
139 
140 #ifdef	DEBUG
141 	p = getenv(OAM_DEVLKTAB);
142 	if ((p != NULL) && (*p != '\0')) {
143 	    if (lockname = malloc(strlen(p)+1))
144 		(void) strcpy(lockname, p);
145 	} else {
146 #endif
147 	    if (lockname = malloc(strlen(DVLK_PATH)+1))
148 		(void) strcpy(lockname, DVLK_PATH);
149 
150 #ifdef	DEBUG
151 	}
152 #endif
153 
154 	/* Fini -- return a pointer to the lockfile pathname */
155 	return (lockname);
156 }
157 
158 /*
159  *  int _openlkfile()
160  *
161  *	The _openlkfile() function opens a device-reservation table file
162  *	for read/write access.
163  *
164  *  Arguments: None
165  *
166  *  Returns:  int
167  *	TRUE if successful, FALSE otherwise.
168  *
169  *  Statics Used:
170  *	lkfilefd	Lock file file descriptor
171  */
172 
173 int
174 _openlkfile(void)
175 {
176 	/*
177 	 *  Automatic data
178 	 */
179 
180 	char   *lockname;		/* Name of the lock file */
181 
182 
183 	/* Close the lockfile -- it might be open */
184 	(void) _closelkfile();
185 
186 	/* If we can get the name of the lock file ... */
187 	if (lockname = _rsvtabpath()) {
188 
189 	    /* Open it */
190 	    lkfilefd = open(lockname, O_RDWR|O_CREAT, 0600);
191 	    free(lockname);
192 
193 	}
194 
195 	/*  Finis  */
196 	return ((lkfilefd != -1) ? TRUE : FALSE);
197 }
198 
199 /*
200  * int _closelkfile()
201  *
202  *	Function closes the device-reservation table file and sets the
203  *	necessary external variables to indicate such.
204  *
205  *  Arguments:  None
206  *
207  *  Returns:  int
208  *	Same as close()
209  *
210  *  Statics referenced:
211  *	lkfilefd	The device reservation table file's file descriptor
212  */
213 
214 int
215 _closelkfile(void)
216 {
217 	/* Automatics */
218 	int	rtnval;		/* Value to return */
219 
220 	/* Close the lock file if it's open */
221 	if (lkfilefd != -1) rtnval = close(lkfilefd);
222 	else rtnval = 0;
223 
224 	/* Indicate that the lock-file is closed */
225 	lkfilefd = -1;
226 
227 	/* Finis */
228 	return (rtnval);
229 }
230 
231 /*
232  *  int locklkfile(lkflag)
233  *	short		lkflag
234  *
235  *	This function locks the device lock file.  If the request cannot
236  *	be serviced, it keeps on trying until it manages to lock the file
237  *	or it encounters an error.
238  *
239  *  Arguments:
240  *	lkflag		Flag (from FCNTL(BA_OS)) indicating which type
241  *			of lock is being requested.  Values that make
242  *			sense:
243  *				F_RDLCK:	Read lock.
244  *				F_WRLCK:	Write lock.
245  *
246  *  Returns: int
247  *	TRUE (non-zero) if the function managed to lock the file, FALSE
248  *	otherwise ("errno" will indicate the problem).
249  *
250  *  Statics used:
251  *	int lkfilefd		File descriptor of the open lock file
252  *	struct flock lkinfo	Structure used by fcntl() to lock a file
253  */
254 
255 static	int
256 locklkfile(short lkflag)
257 {
258 	/* Automatic data */
259 	int		noerror;	/* TRUE if no error yet */
260 	int		locked;		/* TRUE if the file is locked */
261 	int		olderrno;	/* Value of errno on call */
262 
263 
264 	/* Set up the locking structure */
265 	lkinfo.l_type = lkflag;
266 
267 	/* Try to lock the file.  If it's locked, wait and try again */
268 	noerror = TRUE;
269 	locked = FALSE;
270 	olderrno = errno;
271 	while (noerror && !locked) {
272 	    if (fcntl(lkfilefd, F_SETLK, &lkinfo) != -1) locked = TRUE;
273 	    else {
274 		if ((errno == EACCES) || (errno == EAGAIN)) {
275 		    errno = olderrno;
276 		    if (sleep(2)) noerror = FALSE;
277 		} else noerror = FALSE;
278 	    }
279 	}
280 
281 	/* Return a success flag */
282 	return (locked);
283 }
284 
285 /*
286  *  int getlkcnt()
287  *
288  *	This function extracts the number of currently-locked devices
289  *	from the lock file.
290  *
291  *  Arguments:  None
292  *
293  *  Returns:  int
294  *	The number of devices locked or -1 if an error occurred.
295  *
296  *  Statics used:
297  *	lkfilefd	File descriptor of the open lockfile
298  *
299  *  Assumptions:
300  *    -	The file is positioned to the beginning-of-file
301  */
302 
303 static	int
304 getlkcnt(void)
305 {
306 	/* Automatics */
307 	int	cntread;		/* Number of bytes read */
308 	int	lkcnt;			/* Number of current locks */
309 
310 	/* Get the lock count from the file */
311 	cntread = (int)read(lkfilefd, &lkcnt, sizeof (int));
312 
313 	/* If there wasn't one, set to 0.  If error, set to -1 */
314 	if (cntread != (int)sizeof (int))
315 		lkcnt = (cntread < 0) ? -1 : 0;
316 
317 	/* Return the lock count */
318 	return (lkcnt);
319 }
320 
321 /*
322  *  int readlocks()
323  *
324  *	The readlocks() function reads the reserved-device list from
325  *	the reserved-device file (which has already been opened)
326  *
327  *  Arguments:  None
328  *
329  *  Returns:  int
330  *	TRUE if all went well, FALSE otherwise.
331  *
332  *  Statics Used:
333  *	lockcount	Sets this to the number of locks in the lock list
334  *	locklist	Sets this to the malloc()ed space containing the
335  *			list of reserved devices.
336  *	lkfilefd	Reads data from this file
337  */
338 
339 static	int
340 readlocks(void)
341 {
342 	/* Automatics */
343 	struct devlks  *alloc;		/* Ptr to alloc'ed space */
344 	int		noerror;	/* TRUE if all is well */
345 	size_t		bufsiz;		/* # bytes needed for lock data */
346 
347 
348 	/* Initializations */
349 	noerror = TRUE;
350 
351 	/* Get the number of devices currently locked */
352 	if ((lockcount = getlkcnt()) > 0) {
353 
354 	    /* Allocate space for the locks */
355 	    bufsiz = lockcount * sizeof (struct devlks);
356 	    if (alloc = malloc(bufsiz)) {
357 
358 		/* Read the locks into the malloc()ed buffer */
359 		if (read(lkfilefd, alloc, bufsiz) != (ssize_t)bufsiz)
360 		    noerror = FALSE;
361 
362 		/* If the read failed, free malloc()ed buffer */
363 		if (!noerror) free(alloc);
364 
365 	    } else noerror = FALSE;  /* malloc() failed */
366 
367 	} else if (lockcount < 0) noerror = FALSE;
368 
369 	/* Finished */
370 	if (noerror)
371 		locklist = (lockcount > 0) ? alloc : NULL;
372 	return (noerror);
373 }
374 
375 /*
376  *  int getlocks()
377  *
378  *	getlocks() extracts the list of locked devices from the file
379  *	containing that information.  It returns the number of locked
380  *	devices.  If there are any locked devices, it allocates a buffer
381  *	for the locked file information, saves that buffer address in
382  *	the allocated buffer.  Also, the device lock file is open and
383  *	locked if the function is successful.
384  *
385  *  Arguments:  None
386  *
387  *  Returns:  int
388  *	TRUE if successful, FALSE otherwise.  "errno" will reflect the
389  *	error if the function returns FALSE.
390  *
391  *  Static data referenced:
392  *	int lkfilefd			File descriptor of the lock file
393  */
394 
395 static	int
396 getlocks(void)
397 {
398 	/* Automatic data */
399 	int		noerror;	/* TRUE if all's well */
400 
401 
402 	/* Initializations */
403 	noerror = TRUE;
404 
405 	/* Open the lock file */
406 	if (_openlkfile()) {
407 
408 	    /* Lock the lock file */
409 	    if (locklkfile(F_WRLCK)) {
410 
411 		/* Get the number of devices currently locked */
412 		if (!readlocks()) noerror = FALSE;
413 
414 		/* If something happened, unlock the file */
415 		if (!noerror) (void) freelkfile();
416 
417 	    } else noerror = FALSE;  /* Lock failed */
418 
419 	    /* If something happened, close the lock file */
420 	    if (!noerror)
421 		(void) _closelkfile();
422 
423 	} else noerror = FALSE;				/* Open failed */
424 
425 	/* Done */
426 	return (noerror);
427 }
428 
429 /*
430  *  int writelks(tblcnt)
431  *	int	tblcnt
432  *
433  *	writelks() writes the lock information to the lock file.  Lock
434  *	information includes the number of locks (to be) in the table.
435  *	Note that functions may still be appending new locks after this
436  *	call...
437  *
438  *  Arguments:
439  *	tblcnt	Number of locks in the lock table
440  *
441  *  Returns:
442  *	TRUE if successful, FALSE otherwise with "errno" containing an
443  *	indication of the error.
444  *
445  *  Statics Used:
446  *	lockcount	Number of locks to exist
447  *	locklist	Table of locks (may not include new ones)
448  *	lkfilefd	File descriptor of the lock file
449  *
450  *  Notes:
451  *    - The number of locks that are going to be in the lock file
452  *	is in the static variable "lockcount".  <tblcnt> indicates
453  *	the number of entries in the lock table.
454  */
455 
456 static	int
457 writelks(int tblcnt)
458 {
459 	/* Automatic data */
460 	int		noerr;		/* FLAG, TRUE if all's well */
461 	size_t		tblsz;		/* Size of the table to write */
462 
463 	/* Initializations */
464 	noerr = TRUE;
465 
466 	/* Rewind the OA&M Device Lock File */
467 	if (lseek(lkfilefd, 0L, 0) >= 0L)
468 
469 	    /* Write the number of locks that will (eventually) exist */
470 	    if (write(lkfilefd, &lockcount, sizeof (int)) == sizeof (int)) {
471 
472 		/* Write the table as we currently know it */
473 		tblsz = tblcnt * sizeof (struct devlks);
474 		if (tblsz)
475 		    if (!write(lkfilefd, locklist, tblsz) == (ssize_t)tblsz)
476 			noerr = FALSE;  /* Write of locks failed */
477 
478 	    } else noerr = FALSE;  /* write() of count failed */
479 
480 	else noerr = FALSE;  /* Rewind failed */
481 
482 	/* Return an indicator of our success */
483 	return (noerr);
484 }
485 
486 /*
487  * int appendlk(key, alias)
488  *	int	key
489  *	char   *alias
490  *
491  *	Write device locking information to the device locking file.
492  *
493  *  Arguments:
494  *	key	Key the device is being locked on
495  *	alias	The device alias being locked
496  *
497  *  Returns:  int
498  *	TRUE if we successfully appended a lock to the lock file,
499  *	FALSE with "errno" set otherwise.
500  *
501  *  Static data used:
502  *	lkfilefd	The open file descriptor for the open device
503  *			locking file
504  */
505 
506 static	int
507 appendlk(
508 	int key,		/* Lock key */
509 	char *alias)		/* Alias to lock */
510 {
511 	/* Automatic data */
512 	struct devlks	lk;	/* Structure for writing a lock */
513 
514 	/* Set up the data to write */
515 	lk.lk_key = key;
516 	(void) strcpy(lk.lk_alias, alias);
517 
518 	/* Write the data, returning an indicator of our success */
519 	return (write(lkfilefd, &lk,
520 	    sizeof (struct devlks)) == sizeof (struct devlks));
521 }
522 
523 /*
524  *  int compresslks()
525  *
526  *	This function compresses the lock table, squeezing out the empty
527  *	lock entries.
528  *
529  *  Arguments:  none
530  *
531  *  Returns:  int
532  *	The number of non-empty entries in the table.  They will be the
533  *	first 'n' entries in the table after compression.
534  *
535  *  Statics Used
536  *	lockcount	Number of locks in the device lock list
537  *	locklist	The device lock list
538  */
539 
540 static	int
541 compresslks(void)
542 {
543 	/* Automatics */
544 	struct devlks  *avail;		/* Pointer to empty slot */
545 	struct devlks  *p;		/* Running pointer to locks */
546 	int		nlocks;		/* Number of locks (up to date) */
547 	int		i;		/* Temporary counter */
548 
549 	/* Initializations */
550 	p = locklist;
551 	nlocks = lockcount;
552 	avail = NULL;
553 
554 	/* Loop through the lock list squeezing out unused slots */
555 	for (i = 0; i < lockcount; i++) {
556 
557 	    /* If we've found an empty slot ... */
558 	    if (isanullstr(p->lk_alias)) {
559 
560 		/*
561 		 * If we've an empty slot to move to, just decrement
562 		 * count of used slots.  Otherwise, make it the next
563 		 * available slot
564 		 */
565 
566 		nlocks--;
567 		if (!avail) avail = p;
568 	    }
569 
570 	    else if (avail) {
571 
572 		/*
573 		 * If we found a slot in use and there's an
574 		 * available slot, move this one there
575 		 */
576 
577 		(void) strcpy(avail->lk_alias, p->lk_alias);
578 		avail->lk_key = p->lk_key;
579 		avail++;
580 	    }
581 
582 	    /* Next, please */
583 	    p++;
584 	}
585 
586 	return (nlocks);
587 }
588 
589 /*
590  *  int freelkfile()
591  *
592  *	This function unlocks the OA&M device locking file.
593  *
594  *  Arguments:  None
595  *
596  *  Returns:  int
597  *	TRUE if it successfully unlocked the file, FALSE otherwise
598  *	with "errno" set to indicate the problem.
599  *
600  *  Statics Used:
601  *	lkinfo		File-locking structure
602  *	lkfilefd	File-descriptor of the open lock file
603  */
604 
605 static	int
606 freelkfile(void)
607 {
608 	/* Automatic data */
609 	int		noerr;		/* TRUE if all's well */
610 
611 	/* Set the action to "unlock" */
612 	lkinfo.l_type = F_UNLCK;
613 
614 	/* Unlock the file */
615 	noerr = (fcntl(lkfilefd, F_SETLK, &lkinfo) != -1);
616 
617 	/* Return an indication of our success */
618 	return (noerr);
619 }
620 
621 /*
622  * int putlocks(newlist, key)
623  *	char  **newlist
624  *	int	key
625  *
626  *	This function updates the file containing OA&M device locks.
627  *
628  *  Arguments:
629  *	newlist		The address of the list of addresses of device
630  *			aliases to add to the list of locked devices
631  *	key		The key on which to lock the devices
632  *
633  *  Returns:  int
634  *	TRUE if all went well, FALSE otherwise with "errno" set to an
635  *	error code that indicates the problem.
636  *
637  *  Statics Used:
638  *	lockcount	Number of locks in the locked device structure
639  *	locklist	Locked device structure
640  */
641 
642 static	int
643 putlocks(
644 	char **newlist,	/* New devices to lock */
645 	int key)	/* Key we're locking stuff on */
646 {
647 	/* Automatic data */
648 	struct devlks  *plk;		/* Ptr into the locks list */
649 	char		**pp;		/* Pointer into the device list */
650 	char		**qq;		/* Another ptr into the dev list */
651 	int		lkndx;		/* Index into locks list */
652 	int		noerr;		/* TRUE if all's well */
653 	int		lksintbl;	/* Number of locks in the table */
654 
655 
656 	/*
657 	 * Look through the existing lock list, looking for holes we can
658 	 * use for the newly locked devices
659 	 */
660 
661 	plk = locklist;
662 	pp = newlist;
663 	lkndx = 0;
664 	while (*pp && (lkndx < lockcount)) {
665 	    if (isanullstr(plk->lk_alias)) {
666 		plk->lk_key = key;
667 		(void) strcpy(plk->lk_alias, *pp++);
668 	    }
669 	    lkndx++;
670 	    plk++;
671 	}
672 
673 	/*
674 	 * Update the locks file (algorithm depends on whether we're adding
675 	 * new locks or not.  May be replacing old locks!)
676 	 */
677 
678 	if (*pp) {
679 
680 	/*
681 	 * Need to expand the locks file
682 	 *  - Remember the old lock count (in existing lock buffer)
683 	 *  - Count the number of new locks we need to add
684 	 *  - Write out the old locks structure
685 	 *  - Append locks for the newly added locks
686 	 */
687 
688 	    lksintbl = lockcount;
689 	    for (qq = pp; *qq; qq++) lockcount++;
690 	    noerr = writelks(lksintbl);
691 	    while (noerr && *pp) noerr = appendlk(key, *pp++);
692 	} else {
693 
694 	/*
695 	 * Don't need to expand the locks file.  Compress the locks
696 	 * then write out the locks information
697 	 */
698 
699 	    lockcount = compresslks();
700 	    noerr = writelks(lockcount);
701 	}
702 
703 	/* Done.  Return an indication of our success */
704 	return (noerr);
705 }
706 
707 /*
708  * char *islocked(device)
709  *	char	       *device
710  *
711  *	This function checks a device to see if it is locked.  If it is
712  *	not locked, it returns the device alias.
713  *
714  *	A device is not locked if the device's alias does not appear in
715  *	the device locks table, or the key on which the device was locked
716  *	is no longer active.
717  *
718  *  Argumetns:
719  *	char *device		The device to be reserved.  This can be
720  *				a pathname to the device or a device
721  *				alias.
722  *
723  *  Returns:  char *
724  *	Returns a pointer to the device alias if it's not locked, or
725  *	(char *) NULL if it's locked or some error occurred.
726  *
727  *  Static data used:
728  *	struct devlks *locklist		Pointer to the list of device locks
729  *	int lockcount			The number of devices that are locked
730  */
731 
732 static	char *
733 islocked(char *device)
734 {
735 	/* Automatic data */
736 	char		*alias;		/* Alias of "device" */
737 	struct devlks	*plk;		/* Ptr to locking info */
738 	int		locked;		/* TRUE if device in locked list */
739 	int		i;		/* Temp counter */
740 
741 	/* Get the device's alias */
742 	if (alias = devattr(device, DTAB_ALIAS)) {
743 
744 	/*
745 	 * Look through the device locks to see if this device alias
746 	 * is locked
747 	 */
748 
749 	    locked = FALSE;
750 	    plk = locklist;
751 	    for (i = 0; !locked && (i < lockcount); i++) {
752 		if (strncmp(alias, plk->lk_alias, DTAB_MXALIASLN) == 0)
753 		    locked = TRUE;
754 		else plk++;
755 	    }
756 
757 	    if (locked) {
758 		    free(alias);
759 		    alias = NULL;
760 		    errno = EAGAIN;
761 	    }
762 
763 	}  /* devattr() failed, no such device? */
764 
765 	/* Return pointer to the device */
766 	return (alias);
767 }
768 
769 /*
770  *  int unreserv(key, device)
771  *	int	key
772  *	char   *device
773  *
774  *	This function removes a device reservation.
775  *
776  *  Arguments:
777  *	int key	The key on which the device was allocated
778  *	char *device	The device to be freed.
779  *
780  *  Returns:  int
781  *	TRUE if successful, FALSE otherwise with "errno" set.
782  *
783  *  Explicit "errno" settings:
784  *	(This follows the "signal()" model which gives one the ability
785  *	to determine if a device is allocated without having the
786  *	permission to free it.)
787  *
788  *	EINVAL	The device specified was not locked
789  *	EPERM	The device specified was locked but not on the
790  *		specified key
791  *
792  *  Static data used:
793  *	locklist	List of locked devices
794  *	lockcount	Number of entries in the locked-device list
795  */
796 
797 int
798 unreserv(int key, char *device)
799 {
800 	/* Automatics */
801 	char		*srchalias;	/* Device alias to search table with */
802 	char		*alias;		/* Device's alias (from devattr()) */
803 	struct devlks	*plk;		/* Pointer to a device lock */
804 	int		locked;		/* TRUE if device currently locked */
805 	int		noerr;		/* TRUE if all's well */
806 	int		olderrno;	/* Entry value of "errno" */
807 	int		i;		/* Counter of locks */
808 
809 
810 	/* Initializations */
811 	noerr = TRUE;
812 
813 	/*
814 	 * Get the device alias.  If none can be found, try to free
815 	 * whatever it is that was given to us (the possibility exists
816 	 * that the device has been removed from the device table since
817 	 * it was reserved, so the device not being in the table shouldn't
818 	 * pose too much of a problem with us...)
819 	 */
820 
821 	olderrno = errno;
822 	if (alias = devattr(device, DTAB_ALIAS)) srchalias = alias;
823 	else {
824 	    errno = olderrno;
825 	    srchalias = device;
826 	}
827 
828 	/* Loop through the locked-device list looking for what we've got... */
829 	locked = FALSE;
830 	plk = locklist;
831 	for (i = 0; !locked && (i < lockcount); i++) {
832 	    if (strcmp(srchalias, plk->lk_alias) == 0)
833 		locked = TRUE;
834 	    else plk++;
835 	}
836 
837 	/* Free the alias string (if any), we don't need it anymore */
838 	if (alias) free(alias);
839 
840 	/* If the device is locked ... */
841 	if (locked) {
842 
843 	/*
844 	 * If it's locked on the key we've been given, free it.
845 	 * Otherwise, don't free it and set errno to EPERM
846 	 */
847 
848 	    if (plk->lk_key == key) {
849 		plk->lk_alias[0] = '\0';
850 	    } else {
851 		noerr = FALSE;
852 		errno = EPERM;
853 	    }
854 	} else {
855 
856 	    /* The device isn't locked.  Set errno to EINVAL */
857 	    noerr = FALSE;
858 	    errno = EINVAL;
859 	}
860 
861 	/* Finished.  Return an indication of our success */
862 	return (noerr);
863 }
864 
865 /*
866  *  char **devreserv(key, rsvlst)
867  *	int		key
868  *	char	      **rsvlist[]
869  *
870  *	The devreserv() function reserves devices known to the OA&M Device
871  *	Management family of functions.  Once a device is reserved, it can't
872  *	be reserved by another until it is freed or the process with the
873  *	"key" is no longer active.  It returns a list aliases of the devices
874  *	it allocated.
875  *
876  *	The function attempts to reserve a single device from each of the
877  *	lists.  It scans each list sequentially until it was able to
878  *	reserve a requested device.  If it successfully reserved a device
879  *	from each of the lists, it updates the device-locked file and
880  *	returns those aliases to the caller.  If it fails, it allocates
881  *	nothing and returns (char **) NULL to the caller.  "errno"
882  *	indicates the error.
883  *
884  *  Arguments:
885  *	int key			The key on which this device is being reserved.
886  *
887  *	char **rsvlist[]	The address of the list of addresses of lists
888  *				of pointers to the devices to allocate.
889  *
890  *  Returns:  char **
891  *	A pointer to malloc()ed space containing pointers to the aliases
892  *	of the reserved devices.  The aliases are in malloc()ed space also.
893  *	The list is terminated by the value (char *) NULL.
894  *
895  *  Static Data Used:
896  *	None directly, but functions called share hidden information
897  *	that really isn't of concern to devreserv().
898  */
899 
900 char **
901 devreserv(
902 	int		key,		/* Key to reserve device on */
903 	char		**rsvlst[])	/* List of lists of devs to reserve */
904 {
905 	char		***ppp;		/* Ptr to current list in rsvlist */
906 	char		**pp;		/* Ptr to current item in list */
907 	char		**qq;		/* Ptr to item in rtnlist */
908 	char		**rr;		/* Ptr to item in aliases */
909 	char		**aliases;	/* List of aliases allocated */
910 	char		**rtnlist;	/* Ptr to buf to return */
911 	char		*alias;		/* Alias of dev to reserve */
912 	int		noerr;		/* TRUE if all's well */
913 	int		olderrno;	/* Old value of errno */
914 	int		gotone;		/* TRUE if unreserved dev found */
915 	int		foundone;	/* Found a valid device in the list */
916 	int		ndevs;		/* # of devs to reserve */
917 
918 	noerr = TRUE;
919 	ppp = rsvlst;
920 	olderrno = errno;
921 	for (ndevs = 0; *ppp++; ndevs++)
922 		;
923 	if (rtnlist = malloc((ndevs+1)*sizeof (char **))) {
924 	    if (aliases = malloc((ndevs+1)*sizeof (char **))) {
925 		if (getlocks()) {
926 		    qq = rtnlist;
927 		    rr = aliases;
928 
929 		    /* Go through the lists of devices we're to reserve */
930 
931 		    for (ppp = rsvlst; noerr && *ppp; ppp++) {
932 
933 			/* Try to reserve a device from each list */
934 			gotone = FALSE;
935 			foundone = FALSE;
936 			for (pp = *ppp; noerr && !gotone && *pp; pp++) {
937 
938 			/*
939 			 * Check the next device in the list.  If islocked()
940 			 * returns that device's alias, it's ours to have
941 			 */
942 
943 			    if (alias = islocked(*pp)) {
944 				gotone = TRUE;
945 				foundone = TRUE;
946 				if (*qq = malloc(strlen(*pp)+1)) {
947 				    (void) strcpy(*qq++, *pp);
948 				    *rr++ = alias;
949 				} else {
950 				    *rr = NULL;
951 				    noerr = FALSE;
952 				}
953 			    } else {
954 				if (errno == EAGAIN) {
955 				    foundone = TRUE;
956 				    errno = olderrno;
957 				} else if (errno == ENODEV) errno = olderrno;
958 				else {
959 				    noerr = FALSE;
960 				    *rr = NULL;
961 				}
962 			    }
963 			}
964 
965 			/*
966 			 * If no device from the list could be reserved,
967 			 * we've failed
968 			 */
969 
970 			if (noerr && !gotone) {
971 			    noerr = FALSE;
972 			    if (!foundone) errno = ENODEV;
973 			    else errno = EAGAIN;
974 			    *qq = NULL;
975 			    *rr = NULL;
976 			}
977 
978 		    } /* End of loop through lists loop */
979 
980 		/*
981 		 * If all went well, update lock file.
982 		 * Then, free locks
983 		 */
984 
985 		    if (noerr) {
986 			*qq = NULL;
987 			*rr = NULL;
988 			if (!putlocks(aliases, key)) noerr = FALSE;
989 		    }
990 
991 		    /* Free resources */
992 		    if (!freelkfile()) noerr = FALSE;
993 		    if (_closelkfile() != 0) noerr = FALSE;
994 		    for (qq = aliases; *qq; qq++) free(*qq);
995 		    if (!noerr)
996 			for (pp = rtnlist; *pp; pp++)
997 				free(*pp);
998 
999 		} else noerr = FALSE; /* Error getting locks */
1000 
1001 		free(aliases);
1002 
1003 	    } else noerr = FALSE;  /* Malloc() for alias list failed */
1004 
1005 	    if (!noerr) {
1006 		free(rtnlist);
1007 		rtnlist = NULL;
1008 	    }
1009 
1010 	} else noerr = FALSE;  /* malloc() failed */
1011 
1012 	/* Return list or an indication of an error */
1013 	return (noerr ? rtnlist : NULL);
1014 }
1015 
1016 /*
1017  *  int devfree(key, device)
1018  *	int	key
1019  *	char   *device
1020  *
1021  *	This function unreserves (frees) the given device.  It returns
1022  *	an indication of success with "errno" containing information about
1023  *	a failure.
1024  *
1025  *  Arguments:
1026  *	int	key	The key that the device is locked on
1027  *	char   *device	The device (alias, pathname to, etc.) to be freed.
1028  *
1029  *  Returns:  int
1030  *	0 if successful, -1 with "errno" set if fails.
1031  */
1032 
1033 int
1034 devfree(
1035 	int	key,			/* Key device is locked on */
1036 	char   *device)			/* Device to free */
1037 {
1038 	/* Automatics */
1039 	int	noerr;
1040 
1041 	/* Initializations */
1042 	noerr = TRUE;
1043 
1044 	/* Get the locks, locking the lock file */
1045 	if (getlocks()) {
1046 
1047 	    /* Attempt to unreserve the device */
1048 	    if (unreserv(key, device)) {
1049 
1050 		/*
1051 		 * Successful.  Compress the lock structure and
1052 		 * write the new locks
1053 		 */
1054 
1055 		lockcount = compresslks();
1056 		if (!writelks(lockcount)) noerr = FALSE;
1057 
1058 	    } else noerr = FALSE;  /* Couldn't unreserve the device */
1059 
1060 	    /* Unlock and close the locks file */
1061 	    if (!freelkfile()) noerr = FALSE;
1062 	    if (_closelkfile() != 0) noerr = FALSE;
1063 
1064 	} else noerr = FALSE;
1065 
1066 	/* Return 0 if successful, something else otherwise */
1067 	return (noerr? 0 : -1);
1068 }
1069 
1070 /*
1071  *  struct reservdev **reservdev()
1072  *
1073  *	This function returns the list of reserved devices
1074  *	along with the key on which those devices were locked.
1075  *
1076  *  Arguments:  None.
1077  *
1078  *  Returns:  struct reservdev **
1079  *	Pointer to the list of pointers to structures describing
1080  *	the reserved devices, or (struct reservdev **) NULL if an
1081  *	error occurred.  The list of pointers is terminated by
1082  *	(struct reservdev *) NULL.
1083  *
1084  *  Statics Used:
1085  *	locklist	List of reserved devices
1086  *	lockcount	Number of items in the reserved-devices list
1087  */
1088 
1089 struct reservdev **
1090 reservdev(void)
1091 {
1092 	/* Automatics */
1093 	struct reservdev	**rtnlist;	/* Ptr to return list */
1094 	struct devlks		*p;		/* Running ptr, locklist */
1095 	struct reservdev	**q;		/* Running ptr, rtnlist */
1096 	char			*r;		/* Temp ptr to char */
1097 	size_t			bufsiz;		/* Size of buffer to alloc */
1098 	int			noerr;		/* TRUE if all's well */
1099 	int			i;		/* Lock counter */
1100 
1101 
1102 	/* Initializations */
1103 	noerr = TRUE;
1104 
1105 	/* Open the lock file ... */
1106 	if (_openlkfile()) {
1107 
1108 	    /* Put a read-lock on the lock-file ... */
1109 	    if (locklkfile(F_RDLCK)) {
1110 
1111 		/* Read the locks ... */
1112 		if (readlocks()) {
1113 
1114 		    /* Alloc space for the return list */
1115 		    bufsiz = (lockcount+1) * sizeof (struct reservdev *);
1116 		    if (rtnlist = malloc(bufsiz)) {
1117 
1118 			/* Build the return list from the lock list */
1119 			p = locklist;
1120 			q = rtnlist;
1121 			for (i = 0; noerr && (i < lockcount); i++) {
1122 			    if (*q = malloc(sizeof (struct reservdev))) {
1123 				if (r = malloc(strlen(p->lk_alias)+1)) {
1124 				    (*q)->devname = strcpy(r, p->lk_alias);
1125 				    (*q)->key = p->lk_key;
1126 				} else noerr = FALSE;  /* malloc() error */
1127 			    } else noerr = FALSE;  /* malloc() error */
1128 			    p++;
1129 			    q++;
1130 			}
1131 
1132 			/*
1133 			 * If no error, terminate the list.  Otherwise, free
1134 			 * the space we've allocated
1135 			 */
1136 
1137 			if (noerr) *q = NULL;
1138 			else {
1139 			    for (q = rtnlist; *q; q++) {
1140 				free((*q)->devname);
1141 				free(*q);
1142 			    }
1143 			    free(rtnlist);
1144 			}
1145 
1146 		    } else noerr = FALSE;  /* Couldn't malloc() list space */
1147 
1148 		} else noerr = FALSE;  /* Problem reading locks */
1149 
1150 		/* Free the lock file */
1151 		(void) freelkfile();
1152 
1153 	    } else noerr = FALSE;  /* Error locking the lock file */
1154 
1155 	    /* Close the lock file */
1156 	    (void) _closelkfile();
1157 
1158 	} else noerr = FALSE;  /* Error opening the lock file */
1159 
1160 	/* Return ptr to list of locks or NULL if an error has occurred */
1161 	return (noerr ? rtnlist : NULL);
1162 }
1163