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