xref: /illumos-gate/usr/src/lib/libadm/common/putdev.c (revision 9ee48d48fcedfa1c02bcd16d6abbbfd28b9726c1)
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 /*
23  * Copyright (c) 1996-1997, by Sun Microsystems, Inc.
24  * All Rights reserved.
25  */
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
31  */
32 
33 /* LINTLIBRARY */
34 
35 /*
36  * putdev.c
37  *
38  * Global Definitions:
39  *	_adddevtabrec()		Add a record to the device table
40  *	_putdevtabrec()		Write a record to the device table
41  *	_moddevtabrec()		Modify a device-table record
42  *	_rmdevtabrec()		Remove a device-table record
43  *	_rmdevtabattrs()	Remove attributes from a device-table record
44  *	oam_devtab		File descriptor of the open device table
45  */
46 
47 /*
48  *  G L O B A L   R E F E R E N C E S
49  *
50  *	Header Files
51  *	Externals Referenced
52  */
53 
54 /*
55  * Header Files
56  *	<sys/types.h>		UNIX(r) Data Types
57  *	<sys/stat.h>
58  *	<stdio.h>		Standard I/O definitions
59  *	<fcntl.h>		Definitions for file control
60  *	<errno.h>		Error handling definitions
61  *	<string.h>		String Handling Definitions
62  *	<devmgmt.h>		Device Management Definitions
63  *	<unistd.h>		Get UNIX(r) Standard Definitions
64  *	"devtab.h"		Local Device Management Definitions
65  */
66 
67 #include	<sys/types.h>
68 #include	<sys/stat.h>
69 #include	<stdio.h>
70 #include	<fcntl.h>
71 #include	<errno.h>
72 #include	<string.h>
73 #include	<devmgmt.h>
74 #include	<unistd.h>
75 #include	<stdlib.h>
76 #include	"devtab.h"
77 
78 /*
79  *  L O C A L   D E F I N I T I O N S
80  *
81  *	TDTABNM		Name of the temporary device table (in the
82  *			directory of the existing table)
83  */
84 
85 #define	TDTABNM		"%sdevtab.%6.6d"
86 
87 
88 /*
89  * Static functions
90  *	strcatesc	Copies a character-string from one place to another
91  *			escaping the appropriate characters
92  *	lkdevtab	Locks the device table
93  *	unlkdevtab	Unlocks the device table
94  *	mkdevtabent	Builds a device-table entry from the alias and the
95  *			list of attr=val pairs given
96  *	opennewdevtab	Opens a new device table (as a temp file)
97  *	mknewdevtab	Makes the temp device table the new devtab
98  *	rmnewdevtab	Remove the temporary device table and free space
99  *			allocated to the filename of that file.
100  */
101 
102 static	char			*strcatesc(char *, char *);
103 static	int			lkdevtab(char *, short);
104 static	int			unlkdevtab(void);
105 static	struct devtabent	*mkdevtabent(char *, char **);
106 static	FILE			*opennewdevtab(char **);
107 static	int			mknewdevtab(char *);
108 static	int			rmnewdevtab(char *);
109 
110 /*
111  * char *strcatesc(p, q)
112  *	char   *p
113  *	char   *q
114  *
115  *	Write the character-string pointed to by "q" to the place
116  *	pointed to by "p", escaping those characters in "q" found in the
117  *	string "DTAB_ESCS" by preceding them with '\\'.  Return a pointer to
118  *	the byte beyond the last character written to "p".
119  *
120  *  Arguments:
121  *	p		The place to begin writing to
122  *	q		The string to write
123  *
124  *  Returns:  char *
125  *	The address of the byte beyond the last character written into "p"
126  */
127 
128 static char *
129 strcatesc(
130 	char   *p,		/* Place to write to */
131 	char   *q)		/* Thing to write */
132 {
133 	while (*q) {
134 	    if (strchr(DTAB_ESCS, *q)) *p++ = '\\';
135 	    *p++ = *q++;
136 	}
137 	return (p);
138 }
139 
140 /*
141  * FILE *opennewdevtab(pname)
142  *	char   **pname
143  *
144  *	Generates a temporary device-table name from the existing
145  *	device table name (in the same directory) and opens that
146  *	file for writing.  It puts a pointer to the malloc()ed space
147  *	containing the temp device table's name at the place referenced
148  *	by <pname>.
149  *
150  *  Arguments:
151  *	pname	Pointer to the char * to contain the address of the name
152  *		of the temporary file
153  *
154  *  Returns:  FILE *
155  *	A pointer to the opened stream or (FILE *) NULL if an error occurred.
156  *	If an error occurred, "errno" will be set to reflect the problem.
157  */
158 
159 static FILE *
160 opennewdevtab(char  **pname)		/* A(ptr to temp filename's path) */
161 {
162 	char   *oldname;		/* Ptr to the device-table's name */
163 	char   *buf;			/* Ptr to the temp file's name */
164 	char   *dirname;		/* Directory containing devtab */
165 	char   *p;			/* Ptr to last '/' in devtab name */
166 	int    fd;			/* Opened file descriptor */
167 	FILE   *fp;			/* Opened file pointer */
168 	struct stat64	sbuf;		/* stat buf for old devtab file */
169 
170 	fp = NULL;
171 	if (oldname = _devtabpath()) {
172 	/*
173 	 * It is possible for us to have sufficient permissions to create
174 	 * the new file without having sufficient permissions to write the
175 	 * original devtab file.  For consistency with the operations which
176 	 * modify the original file by writing it directly we require write
177 	 * permissions for the original file in order to make a new one.
178 	 */
179 	    if ((fd = open(oldname, O_WRONLY)) == -1)
180 		return (NULL);
181 
182 	    if (fstat64(fd, &sbuf) == -1) {
183 		(void) close(fd);
184 		return (NULL);
185 	    }
186 	    (void) close(fd);
187 
188 	    if (p = strrchr(oldname, '/')) {
189 		*(p+1) = '\0';
190 		dirname = oldname;
191 	    } else dirname = "./";
192 	    if (asprintf(&buf, TDTABNM, dirname, getpid()) >= 0) {
193 
194 		    /*
195 		     * Build the name of the temp device table and
196 		     * open the file.  We must reset the owner, group
197 		     * and perms to those of the original devtab file.
198 		     */
199 		    if (fp = fopen(buf, "w")) {
200 			    *pname = buf;
201 			    (void) fchmod(fileno(fp), sbuf.st_mode & 0777);
202 			    (void) fchown(fileno(fp), sbuf.st_uid, sbuf.st_gid);
203 		    } else {
204 			    free(buf);
205 		    }
206 	    }
207 
208 	/*
209 	 *
210 	 * Free the space containing the device table's name.
211 	 */
212 	    free(oldname);
213 	}
214 
215 	/* Finished.  Return what we've got */
216 	return (fp);
217 }
218 
219 /*
220  *  int rmnewdevtab(tempname)
221  *	char   *tempname
222  *
223  *	Unlink the temp device table and free the memory allocated to
224  *	contain the name of that file
225  *
226  *  Arguments:
227  *	tempname	Name of the temporary file
228  *
229  *  Returns: int
230  *	TRUE if successful, FALSE otherwise
231  */
232 
233 static int
234 rmnewdevtab(char *tempname)	/* Filename of new device table */
235 {
236 	int	noerr;		/* Flag, TRUE if no error, FALSE otherwise */
237 
238 	/* Unlink the file */
239 	noerr = (unlink(tempname) == 0);
240 
241 	/* Free the space allocated to the filename */
242 	free(tempname);
243 
244 	/* Return success indicator */
245 	return (noerr);
246 }
247 
248 /*
249  *  int mknewdevtab(tempname)
250  *	char   *tempname
251  *
252  *	Make the temporary device-table the new system device table
253  *
254  *  Arguments:
255  *	tempname	Name of the temporary file
256  *
257  *  Returns:  int
258  *	TRUE if successful, FALSE otherwise
259  *
260  *  Notes:
261  *	- Need to use rename() someday instead of link()/unlink()
262  *	- This code is somewhat ineffecient in that asks for the name
263  *	  of the device-table more than once.  Done so that we don't
264  *	  have to manage that space, but this may be somewhat lazy.
265  */
266 
267 static int
268 mknewdevtab(char   *tempname)		/* Ptr to name of temp dev tab */
269 {
270 	char   *devtabname;		/* Ptr to the device table's name */
271 	int	noerr;			/* FLAG, TRUE if all's well */
272 
273 	/* Get the device table's pathname */
274 	if (devtabname = _devtabpath()) {
275 
276 	    /* Unlink the existing file */
277 	    if (unlink(devtabname) == 0) {
278 
279 		/* Make the temp file the real device table */
280 		noerr = (link(tempname, devtabname) == 0) ? TRUE : FALSE;
281 
282 		/* Remove the temp file (and resources) */
283 		if (noerr) (void) rmnewdevtab(tempname);
284 
285 	    } else noerr = FALSE;	/* unlink() failed */
286 
287 	    /* Free the device table's name */
288 	    free(devtabname);
289 
290 	} else noerr = FALSE; 	/* devtabpath() failed */
291 
292 	/* Finished.  Return success indicator */
293 	return (noerr);
294 }
295 
296 /*
297  * static int lkdevtab(o_mode, lktype)
298  *	char   *o_mode
299  *	short	lktype
300  *
301  *	Lock the device table for writing.  If it isn't available, it waits
302  *	until it is.
303  *
304  *  Arguments:
305  *	o_mode	The open() mode to use when opening the device table
306  *	lktype	The type of lock to apply
307  *
308  *  Returns:  int
309  *	TRUE if successful, FALSE with errno set otherwise
310  */
311 
312 static int
313 lkdevtab(
314 	char   *o_mode,				/* Open mode */
315 	short	lktype)				/* Lock type */
316 {
317 	/* Automatic data */
318 	struct flock	lockinfo;		/* File locking structure */
319 	int		noerr;			/* FLAG, TRUE if no error */
320 	int		olderrno;		/* Old value of "errno" */
321 
322 
323 	/* Close the device table (if it's open) */
324 	_enddevtab();
325 
326 	/* Open the device table for read/append */
327 	noerr = TRUE;
328 	if (_opendevtab(o_mode)) {
329 
330 	/*
331 	 * Lock the device table (for writing).  If it's not
332 	 * available, wait until it is, then close and open the
333 	 * table (modify and delete change the table!) and try
334 	 * to lock it again
335 	 */
336 
337 	    /* Build the locking structure */
338 	    lockinfo.l_type = lktype;
339 	    lockinfo.l_whence = 0;
340 	    lockinfo.l_start = 0L;
341 	    lockinfo.l_len = 0L;
342 	    olderrno = errno;
343 
344 	    /* Keep on going until we lock the file or an error happens */
345 	    while ((fcntl(fileno(oam_devtab), F_SETLK, &lockinfo) == -1) &&
346 		!noerr) {
347 		if (errno == EACCES) {
348 		    if (fcntl(fileno(oam_devtab), F_SETLKW, &lockinfo) == -1)
349 			noerr = FALSE;
350 		    else {
351 			/* Reopen the file (maybe it's moved?) */
352 			_enddevtab();
353 			if (!_opendevtab(o_mode)) noerr = FALSE;
354 			else errno = olderrno;
355 		    }
356 		} else noerr = FALSE;
357 	    }
358 
359 	    if (!noerr) _enddevtab();  /* Don't keep open if in error */
360 
361 	} else noerr = FALSE;
362 
363 	/* Done */
364 	return (noerr);
365 }
366 
367 /*
368  * int unlkdevtab()
369  *
370  *	Unlock the locked device table.
371  *
372  *  Arguments:  None
373  *
374  *  Returns:  int
375  *	Whatever fcntl() returns...
376  */
377 
378 static int
379 unlkdevtab(void)
380 {
381 	/* Automatic data */
382 	struct flock	lockinfo;		/* Locking structure */
383 	int		noerr;			/* FLAG, TRUE if all's well */
384 
385 	/* Build the locking structure */
386 	lockinfo.l_type = F_UNLCK;		/* Lock type */
387 	lockinfo.l_whence = 0;			/* Count from top of file */
388 	lockinfo.l_start = 0L;			/* From beginning */
389 	lockinfo.l_len = 0L;			/* Length of locked data */
390 
391 	/* Unlock it */
392 	noerr = (fcntl(fileno(oam_devtab), F_SETLK, &lockinfo) != -1);
393 	_enddevtab();
394 
395 	/* Finished */
396 	return (noerr);
397 }
398 
399 /*
400  * struct devtabent *mkdevtabent(alias, attrlist)
401  *	char   *alias
402  *	char  **attrlist
403  *
404  *	This function builds a struct devtabent structure describing the
405  *	alias <alias> using the information in the attribute list <attrlist>.
406  *	The <attrlist> contains data of the form attr=value where attr is
407  *	the name of an attribute and value is the value of that attribute.
408  *
409  *  Arguments:
410  *	alias		The alias being added to the device table
411  *	attrlist	The attributes and values for that alias
412  *
413  *  Returns:  struct devtabent *
414  *	A completed struct devtabent structure containing the description
415  *	of the alias.  The structure, and all of the data in the structure
416  *	are each in space allocated using the malloc() function and should
417  *	be freed using the free() function (or the _freedevtabent() function).
418  *
419  *  Errors:
420  *	EINVAL	If "alias" is used as an attribute in an attr=val pair
421  *	EAGAIN	If an attribute is specified more than once
422  */
423 
424 static struct devtabent *
425 mkdevtabent(
426 	char   *alias,		/* Alias of entry */
427 	char  **attrlist)	/* Attributes of new entry */
428 {
429 	/* Automatic data */
430 	struct devtabent	*devtabent;	/* * to struct we're making */
431 	struct attrval		*prevattrval;	/* * to prev attr/val struct */
432 	struct attrval		*attrval;	/* * to current struct */
433 	char			**pp;		/* Ptr into list of ptrs */
434 	char			*peq;		/* Ptr to '=' in string */
435 	char			*val;		/* Ptr to space for value */
436 	char			*name;		/* Ptr to space for name */
437 	ssize_t			len;		/* Length of name */
438 	int			noerr;		/* TRUE if all's well */
439 	int			found;		/* TRUE the attr is found */
440 
441 
442 	/* No problems (yet) */
443 	noerr = TRUE;
444 
445 	/* Get space for the structure */
446 	if (devtabent = malloc(sizeof (struct devtabent))) {
447 
448 	    /* Fill in default values */
449 	    if (devtabent->alias = malloc(strlen(alias)+1)) {
450 
451 		(void) strcpy(devtabent->alias, alias);		/* alias */
452 		devtabent->comment = FALSE;			/* data rec */
453 		devtabent->cdevice = NULL;			/* cdevice */
454 		devtabent->bdevice = NULL;			/* bdevice */
455 		devtabent->pathname = NULL;			/* pathname */
456 		devtabent->attrstr = NULL;			/* string */
457 		devtabent->attrlist = NULL;			/* attr list */
458 
459 		/* Add attributes to the structure */
460 		prevattrval = NULL;
461 		if ((pp = attrlist) != NULL)
462 		    while (*pp && noerr) {
463 
464 		    /* Valid attr=value pair? */
465 		    if (((peq = strchr(*pp, '=')) != NULL) &&
466 			((len = peq - *pp) > 0)) {
467 
468 			/* Get space for the value */
469 			if (val = malloc(strlen(peq))) {
470 			    (void) strcpy(val, peq+1);		/* Copy it */
471 
472 			    /* Get space for attribute name */
473 			    if (name = malloc((size_t)(len + 1))) {
474 				(void) strncpy(name, *pp, (size_t)len);
475 				*(name+len) = '\0';
476 
477 				/* Specifying the alias?  If so, ERROR */
478 				if (strcmp(name, DTAB_ALIAS) == 0) {
479 				    noerr = FALSE;
480 				    free(name);
481 				    free(val);
482 				    errno = EINVAL;
483 				}
484 
485 				/* Specifying the char device path? */
486 				else if (strcmp(name, DTAB_CDEVICE) == 0) {
487 				    if (!devtabent->cdevice) {
488 					if (val[0] != '/') {
489 						noerr = FALSE;
490 						free(name);
491 						free(val);
492 						errno = ENXIO;
493 					} else {
494 						devtabent->cdevice = val;
495 						free(name);
496 					}
497 				    } else {
498 					noerr = FALSE;
499 					free(name);
500 					free(val);
501 					errno = EAGAIN;
502 				    }
503 				}
504 
505 				/* Specifying the block device path? */
506 				else if (strcmp(name, DTAB_BDEVICE) == 0) {
507 				    if (!devtabent->bdevice) {
508 					if (val[0] != '/') {
509 						noerr = FALSE;
510 						free(name);
511 						free(val);
512 						errno = ENXIO;
513 					} else {
514 						devtabent->bdevice = val;
515 						free(name);
516 					}
517 				    } else {
518 					noerr = FALSE;
519 					free(name);
520 					free(val);
521 					errno = EAGAIN;
522 				    }
523 				}
524 
525 				/* Specifying the pathname (generic)? */
526 				else if (strcmp(name, DTAB_PATHNAME) == 0) {
527 				    if (!devtabent->pathname) {
528 					if (val[0] != '/') {
529 						noerr = FALSE;
530 						free(name);
531 						free(val);
532 						errno = ENXIO;
533 					} else {
534 						devtabent->pathname = val;
535 						free(name);
536 					}
537 				    } else {
538 					noerr = FALSE;
539 					free(name);
540 					free(val);
541 					errno = EAGAIN;
542 				    }
543 				}
544 
545 				/* Some other attribute */
546 				else {
547 				    found = FALSE;
548 				    if ((attrval = devtabent->attrlist) != NULL)
549 					do {
550 					    if (strcmp(attrval->attr,
551 						name) == 0) {
552 
553 						noerr = FALSE;
554 						free(name);
555 						free(val);
556 						errno = EAGAIN;
557 					    }
558 					} while (!found && noerr &&
559 					    (attrval = attrval->next));
560 
561 				    if (!found && noerr) {
562 
563 					/* Get space for attr/val structure */
564 					if (attrval =
565 					    malloc(sizeof (struct attrval))) {
566 
567 					    /* Fill attr/val structure */
568 					    attrval->attr = name;
569 					    attrval->val = val;
570 					    attrval->next = NULL;
571 
572 					/*
573 					 * Link into the list of attributes
574 					 */
575 					    if (prevattrval)
576 						prevattrval->next = attrval;
577 					    else devtabent->attrlist = attrval;
578 					    prevattrval = attrval;
579 
580 					} else {
581 					    /* malloc() for attrval failed */
582 					    noerr = FALSE;
583 					    free(name);
584 					    free(val);
585 					}
586 				    }
587 				}   /* End else (some other attribute) */
588 
589 			    } else { 	/* malloc() for attribute name failed */
590 				noerr = FALSE;
591 				free(val);
592 			    }
593 
594 			} else noerr = FALSE;	/* Malloc() for "val" failed */
595 
596 			/* If we saw an error, free structure, returning NULL */
597 			if (!noerr) {
598 			    _freedevtabent(devtabent);
599 			    devtabent = NULL;
600 			}
601 
602 		    } 	/* Ignore invalid attr=val pair */
603 
604 		    if (noerr) pp++;
605 
606 		}   /* End attribute processing loop */
607 
608 	    } else {	/* malloc() failed */
609 		free(devtabent);
610 		devtabent = NULL;
611 	    }
612 	}
613 
614 	/* Finished */
615 	return (devtabent);
616 }
617 
618 /*
619  * int _putdevtabrec(stream, rec)
620  *	FILE		       *stream
621  *	struct devtabent       *rec
622  *
623  *	Write a device table record containing the information in the struct
624  *	devtab structure <rec> to the current position of the standard I/O
625  *	stream <stream>.
626  *
627  *  Arguments:
628  *	stream		The stream to write to
629  *	rec		The structure containing the information to write
630  *
631  *  Returns:  int
632  *	The number of characters written or EOF if there was some error.
633  */
634 
635 int
636 _putdevtabrec(
637 	FILE			*stream,	/* Stream to which to write */
638 	struct devtabent	*rec)		/* Record to write */
639 {
640 	/* Automatic Data */
641 	struct attrval		*attrval;	/* Ptr to attr/val pair */
642 	char			*buf;		/* Allocated buffer */
643 	char			*p;		/* Temp char pointer */
644 	int			count;		/* Number of chars written */
645 	size_t			size = 0;	/* Size of needed buffer */
646 
647 
648 	/* Comment or data record? */
649 	if (rec->comment) {
650 
651 	/*
652 	 * Record is a comment
653 	 */
654 
655 	    /* Copy (escaping chars) record into temp buffer */
656 	    size = (strlen(rec->attrstr)*2)+1;		/* Max rec size */
657 	    if (buf = malloc(size+1)) {
658 		/* Alloc space */
659 		p = strcatesc(buf, rec->attrstr);	/* Copy "escaped" */
660 		*(p-2) = '\n';				/* Unescape last \n */
661 		*(p-1) = '\0';				/* Terminate string */
662 
663 		/* Write the record */
664 		count = fputs(buf, stream);
665 		free(buf);
666 
667 	    } else count = EOF;  /* malloc() failed */
668 	}
669 
670 	else {
671 
672 		/*
673 		 * Record is a data record
674 		 */
675 
676 		/*
677 		 * Figure out maximum amount of space you're going to need.
678 		 * (Assume every escapable character is escaped to determine the
679 		 * maximum size needed)
680 		 */
681 
682 	    if (rec->cdevice)
683 		size += (strlen(rec->cdevice)*2) + 1;	/* cdevice: */
684 	    if (rec->bdevice)
685 		size += (strlen(rec->bdevice)*2) + 1;	/* bdevice: */
686 	    if (rec->pathname)
687 		size += (strlen(rec->pathname)*2) + 1;	/* pathname: */
688 	    if ((attrval = rec->attrlist) != NULL) do {	/* Attributes */
689 		if (attrval->attr)
690 			size += (strlen(attrval->attr)*2);	    /* attr */
691 		if (attrval->val) {
692 			/* val & '="" ' or val & '=""\n' */
693 			size += (strlen(attrval->val)*2) +4;
694 		}
695 	    } while ((attrval = attrval->next) != NULL);    /* Next attr/val */
696 	    else size++;		/* Else make room for trailing '\n' */
697 
698 	    /* Alloc space for "escaped" record */
699 	    if (buf = malloc(size+1)) {
700 
701 		/* Initializations */
702 		p = buf;
703 
704 		/* Write the alias ("alias" attribute) */
705 		p = strcatesc(p, rec->alias);
706 		*p++ = ':';
707 
708 		/* Write the character device ("cdevice" attribute) */
709 		if (rec->cdevice) p = strcatesc(p, rec->cdevice);
710 		*p++ = ':';
711 
712 		/* Write the block device ("bdevice" attribute) */
713 		if (rec->bdevice) p = strcatesc(p, rec->bdevice);
714 		*p++ = ':';
715 
716 		/* Write the pathname ("pathname" attribute) */
717 		if (rec->pathname) p = strcatesc(p, rec->pathname);
718 		*p++ = ':';
719 
720 		/* Write the rest of the attributes */
721 		if ((attrval = rec->attrlist) != NULL)
722 		    do {
723 			p = strcatesc(p, attrval->attr);
724 			*p++ = '=';
725 			*p++ = '"';
726 			p = strcatesc(p, attrval->val);
727 			*p++ = '"';
728 			if ((attrval = attrval->next) != NULL)
729 			    *p++ = ' ';
730 		    } while (attrval);
731 
732 		/* Terminate the record */
733 		*p++ = '\n';
734 		*p = '\0';
735 
736 		/* Write the record */
737 		count = fputs(buf, stream);
738 		free(buf);
739 	    } else count = EOF;  /* malloc() failed */
740 	}
741 
742 	/* Finished */
743 	return (count);
744 }
745 
746 /*
747  *  int _adddevtabrec(alias, attrval)
748  *	char   *alias
749  *	char  **attrval
750  *
751  *	This function adds a record to the device table.  That record will
752  *	have the alias <alias> and will have the attributes described in
753  *	the list referenced by <attrval>.
754  *
755  *	It always adds the record to the end of the table.
756  *
757  *  Arguments:
758  *	alias		The alias of the device whose description is being
759  *			added to the device table.
760  *	attrval		The pointer to the first item of a list of attributes
761  *			defining the device whose description is being added.
762  *			(This value may be (char **) NULL).
763  *
764  *  Returns:  int
765  *	TRUE if successful, FALSE with errno set otherwise.
766  */
767 
768 int
769 _adddevtabrec(
770 	char   *alias,			/* Alias to add to the device table */
771 	char  **attrval)		/* Attributes for that device */
772 {
773 	/* Automatic data */
774 	struct devtabent	*devtabent;	/* Ptr to dev tab entry */
775 	int			olderrno;	/* Errno on entry */
776 	int			noerr;		/* FLAG, TRUE if all's well */
777 
778 	/* Validate the device alias.  Error (EINVAL) if it's not valid */
779 	if (!_validalias(alias)) {
780 	    errno = EINVAL;
781 	    return (FALSE);
782 	}
783 
784 	/*
785 	 * Lock the device table.  This only returns if the table is locked or
786 	 * some error occurred.  It waits until the table is available.
787 	 */
788 	if (!lkdevtab("a+", F_WRLCK))
789 		return (FALSE);
790 
791 	/* Make sure that the alias isn't already in the table */
792 	noerr = TRUE;
793 	olderrno = errno;
794 	if (devtabent = _getdevrec(alias)) {
795 
796 	    /* The alias is already in the table */
797 	    _freedevtabent(devtabent);		/* Free device table info */
798 	    errno = EEXIST;			/* Set errno, entry exists */
799 	    noerr = FALSE;			/* All's not well */
800 	} else if ((errno == ENOENT) || (errno == ENODEV)) {
801 
802 	    /* The alias wasn't in the table or there wasn't a table. */
803 
804 	    errno = olderrno;			/* Reset errno */
805 
806 	    /* Build a struct devtabent that describes the new alias */
807 	    if (devtabent = mkdevtabent(alias, attrval)) {
808 
809 		/* Position to the end of the existing table */
810 		if (fseek(oam_devtab, 0, SEEK_END) == 0)
811 
812 		    /* Write the new entry */
813 		    noerr = (_putdevtabrec(oam_devtab, devtabent) != EOF);
814 
815 		/* Free the info we just wrote */
816 		_freedevtabent(devtabent);
817 
818 	    } else noerr = FALSE;	/* mkdevtabent() failed */
819 	} else noerr = FALSE;		/* Some odd error, _devtab */
820 
821 	/* Unlock and close the device table */
822 	(void) unlkdevtab();
823 
824 	/* Fini */
825 	return (noerr);
826 }
827 
828 /*
829  * int _moddevtabrec(device, attrval)
830  *	char   *device
831  *	char  **attrval
832  *
833  *	This function modifies the description for the specified device
834  *	so that it has the attributes and values as specified in the
835  *	given list.
836  *
837  *  Arguments:
838  *	device		The name of the device whose description
839  *			is being modified
840  *	attrval		The first attr/val value in the list (attr=val) of
841  *			the attributes that are to change
842  *
843  *  Returns:  int
844  *	TRUE if all went well, FALSE with errno set otherwise
845  */
846 
847 int
848 _moddevtabrec(
849 	char   *device,			/* Device to modify */
850 	char  **attrval)		/* Attributes to add or change */
851 {
852 	/* Automatic data */
853 	FILE			*fd;	/* File ptr, new device table */
854 	struct devtabent	*ent;	/* Device's current description */
855 	struct devtabent	*chg;	/* Changes to make to description */
856 	struct attrval		*new;	/* New attribute/value desc */
857 	struct attrval		*old;	/* Old attribute/value desc */
858 	struct attrval		*newnew; /* Next "new" value to look at */
859 	struct attrval		*prevnew; /* Previous item in the 'new' list */
860 	char			*tname;	/* name of temp devtab file */
861 	int			noerr;	/* FLAG, TRUE if all's well */
862 	int			found;	/* FLAG, TRUE if attr found for dev */
863 
864 	/* Lock the device table */
865 	if (!lkdevtab("r", F_WRLCK))
866 		return (FALSE);
867 
868 	/* No problems (so far) */
869 	noerr = TRUE;
870 
871 	/* Get the entry to modify */
872 	if (ent = _getdevrec(device)) {
873 
874 	    /* Build a structure describing the changes */
875 	    if (chg = mkdevtabent(device, attrval)) {
876 
877 		/* If the "cdevice" field is specified, change it */
878 		if (chg->cdevice) {
879 		    if (ent->cdevice) free(ent->cdevice);
880 		    ent->cdevice = chg->cdevice;
881 		    chg->cdevice = NULL;
882 		}
883 
884 		/* If the "bdevice" field is specified, change it */
885 		if (chg->bdevice) {
886 		    if (ent->bdevice) free(ent->bdevice);
887 		    ent->bdevice = chg->bdevice;
888 		    chg->bdevice = NULL;
889 		}
890 
891 		/* If the "pathname" field is specified, change it */
892 		if (chg->pathname) {
893 		    if (ent->pathname) free(ent->pathname);
894 		    ent->pathname = chg->pathname;
895 		    chg->pathname = NULL;
896 		}
897 
898 		/* Change the other attributes (if any) */
899 		if (ent->attrlist) {
900 		    prevnew = NULL;
901 		    if ((new = chg->attrlist) != NULL) do {
902 
903 			found = FALSE;
904 			for (old = ent->attrlist; !found && old;
905 			    old = old->next) {
906 			    if (strcmp(old->attr, new->attr) == 0) {
907 				found = TRUE;
908 				free(old->val);
909 				old->val = new->val;
910 				new->val = NULL;
911 			    }
912 			}   /* Loop through the existing attribute list */
913 
914 			/*
915 			 * If the attribute wasn't found, add it to the list
916 			 * of attributes for the device.  If it was found, just
917 			 * bump to the next one and look for it
918 			 */
919 
920 			if (!found) {
921 
922 			/*
923 			 * Not found.  Move attr/val description to the
924 			 * device's list of attributes
925 			 */
926 
927 			    if (prevnew) prevnew->next = new->next;
928 			    else chg->attrlist = new->next;
929 			    newnew = new->next;
930 			    new->next = ent->attrlist;
931 			    ent->attrlist = new;
932 			    new = newnew;
933 			} else {
934 
935 			    /* Attribute changed, bump to the next one */
936 			    prevnew = new;
937 			    new = new->next;
938 			}
939 		    } while (new);  /* Loop for each attr to add or modify */
940 
941 		} else {
942 
943 		    /* Device had no attributes -- add entire list */
944 		    ent->attrlist = chg->attrlist;
945 		    chg->attrlist = NULL;
946 		}
947 
948 		/* Free the structure containing the changes */
949 		_freedevtabent(chg);
950 
951 	    } else noerr = FALSE;   /* Couldn't build changes struct */
952 
953 	    /* If there hasn't been an error (so far), write the new record */
954 	    if (noerr) {
955 
956 		/* Open the new device table */
957 		if (fd = opennewdevtab(&tname)) {
958 
959 		/*
960 		 * For each entry in the existing table, write that entry
961 		 * to the new table.  If the entry is the one being
962 		 * modified, write the modified entry instead of the
963 		 * original entry.
964 		 */
965 
966 		    _setdevtab();		/* Rewind existing table */
967 		    chg = ent;			/* Remember new record */
968 		    while (((ent = _getdevtabent()) != NULL) && noerr) {
969 			if (ent->entryno != chg->entryno)
970 			    noerr = _putdevtabrec(fd, ent) != EOF;
971 			else noerr = _putdevtabrec(fd, chg) != EOF;
972 			_freedevtabent(ent);
973 		    }
974 
975 		/*
976 		 * If we successfully generated the new table, make it the
977 		 * new system device table.  Otherwise, just remove the
978 		 * temporary file we've created.
979 		 */
980 
981 		    if (noerr) {
982 			(void) fclose(fd);
983 			noerr = mknewdevtab(tname);
984 		    } else {
985 			(void) fclose(fd);
986 			(void) rmnewdevtab(tname);
987 		    }
988 
989 		    /* Free the changed device structure */
990 		    _freedevtabent(chg);
991 
992 		}   /* if (_opennewdevtab()) */
993 		else noerr = FALSE;
994 
995 	    } else _freedevtabent(ent);  /* if (noerr) */
996 
997 	} else noerr = FALSE;	/* Device not found? */
998 
999 	/* Finished.  Unlock the device table and quit */
1000 	(void) unlkdevtab();
1001 	return (noerr);
1002 }
1003 
1004 /*
1005  * int _rmdevtabrec(device)
1006  *	char   *device
1007  *
1008  *	This function removes the record in the device table for the specified
1009  *	device.
1010  *
1011  *  Arguments:
1012  *	device	The device (alias, cdevice, bdevice, pathname, or link to one)
1013  *		whose entry is to be removed
1014  *
1015  *  Returns:  int
1016  *	Success indicator:  TRUE if successful, FALSE with errno set otherwise.
1017  */
1018 
1019 int
1020 _rmdevtabrec(char *device)		/* Device to remove */
1021 {
1022 	struct devtabent	*rment;
1023 	struct devtabent	*devtabent;
1024 	char			*tempname;
1025 	FILE			*fd;
1026 	int			noerr;
1027 
1028 	if (!lkdevtab("r", F_WRLCK))
1029 		return (FALSE);
1030 	noerr = TRUE;
1031 	if (rment = _getdevrec(device)) {
1032 	    if (fd = opennewdevtab(&tempname)) {
1033 		_setdevtab();
1034 		while (((devtabent = _getdevtabent()) != NULL) && noerr) {
1035 		    if (devtabent->entryno != rment->entryno)
1036 			noerr = _putdevtabrec(fd, devtabent) != EOF;
1037 		    _freedevtabent(devtabent);
1038 		}
1039 		if (noerr) {
1040 		    (void) fclose(fd);
1041 		    noerr = mknewdevtab(tempname);
1042 		} else {
1043 		    (void) fclose(fd);
1044 		    (void) rmnewdevtab(tempname);
1045 		}
1046 	    } else noerr = FALSE;
1047 	    _freedevtabent(rment);
1048 	} else noerr = FALSE;
1049 	(void) unlkdevtab();
1050 	return (noerr);
1051 }
1052 
1053 /*
1054  * int _rmdevtabattrs(device, attributes, notfounds)
1055  *	char   *device
1056  *	char  **attributes
1057  *	char ***notfounds
1058  *
1059  *	Remove the specified attributes from the specified device.  The
1060  *	device is specified by <device>, <attributes> is the address of
1061  *	the first char * in the list of char * pointing to the attributes
1062  *	to remove from the device, and <notfounds> is the address of a
1063  *	char ** to put the address of the first element in the malloc()ed
1064  *	list of (char *) pointing to requested attributes that were not
1065  *	defined for the device <device>.
1066  *
1067  *  Arguments:
1068  *	device		The device from which attributes are to be removed
1069  *	attributes	The address of the first element in the list of
1070  *			attributes to remove.  This list is terminated by
1071  *			(char *) NULL.
1072  *	notfounds	The place to put the address of the list of addresses
1073  *			referencing the requested attributes that are not
1074  *			defined for the specified device.
1075  *
1076  *  Returns: int
1077  *	TRUE if successful, FALSE with errno set otherwise.
1078  *
1079  *  Notes:
1080  *    -	"alias" may not be undefined
1081  *    - "cdevice", "bdevice", and "pathname" are made "null", not really
1082  *	undefined
1083  */
1084 
1085 int
1086 _rmdevtabattrs(
1087 	char   *device,			/* Device to modify */
1088 	char  **attributes,		/* Attributes to remove */
1089 	char ***notfounds)		/* Attributes req'd but not found */
1090 {
1091 	/* Automatics */
1092 	char			**pnxt;		/* Ptr to next attribute */
1093 	char			**pp;		/* Ptr to current attr name */
1094 	struct devtabent	*modent;	/* Entry being modified */
1095 	struct devtabent	*devtabent;	/* Entry being copied */
1096 	struct attrval		*attrval;	/* Ptr to attr/val desc */
1097 	struct attrval		*prevattrval;	/* Ptr to prev attr/val */
1098 	FILE			*fd;		/* File desc, temp file */
1099 	char			*tempname;	/* Name of temp file */
1100 	int			nattrs;		/* Number of attrs to remove */
1101 	int			nobaderr;	/* TRUE if no fatal error */
1102 	int			noerr;		/* TRUE if no non-fatal error */
1103 	int			found;		/* TRUE if attribute found */
1104 	int			nonotfounds;	/* TRUE if no attrs not fount */
1105 
1106 
1107 	/* Initializations */
1108 	nobaderr = TRUE;
1109 	noerr = TRUE;
1110 
1111 	/* Count attributes to remove -- make sure "alias" isn't specified */
1112 	for (pp = attributes, nattrs = 0; *pp; pp++, nattrs++)
1113 	    if (strcmp(*pp, DTAB_ALIAS) == 0) {
1114 		*notfounds = NULL;
1115 		errno = EINVAL;
1116 		return (FALSE);
1117 	    }
1118 
1119 	/* Lock the device table */
1120 	if (!lkdevtab("r", F_WRLCK))
1121 		return (FALSE);
1122 
1123 	/* Is there a record for the requested device? */
1124 	if (modent = _getdevrec(device)) {
1125 
1126 	    /* Record found.  Try to modify it */
1127 	    nonotfounds = TRUE;
1128 
1129 	    /* For each of the attributes in the attribute list ... */
1130 	    for (pp = attributes; nobaderr && *pp; pp++) {
1131 
1132 		/*
1133 		 * Modify the device description, removing the requested
1134 		 * attributes from the structure
1135 		 */
1136 
1137 		found = FALSE;				/* Not found yet */
1138 
1139 		/* If it's the "cdevice" attribute, make it a null-string */
1140 		if (strcmp(*pp, DTAB_CDEVICE) == 0) {
1141 		    if (modent->cdevice) {
1142 			free(modent->cdevice);
1143 			modent->cdevice = NULL;
1144 		    }
1145 		    found = TRUE;
1146 		}
1147 
1148 		/* If it's the "bdevice" attribute, make it a null-string */
1149 		else if (strcmp(*pp, DTAB_BDEVICE) == 0) {
1150 		    if (modent->bdevice) {
1151 			free(modent->bdevice);
1152 			modent->bdevice = NULL;
1153 		    }
1154 		    found = TRUE;
1155 		}
1156 
1157 		/* If it's the "pathname" attribute, make it a null-string */
1158 		else if (strcmp(*pp, DTAB_PATHNAME) == 0) {
1159 		    if (modent->pathname) {
1160 			free(modent->pathname);
1161 			modent->pathname = NULL;
1162 		    }
1163 		    found = TRUE;
1164 		}
1165 
1166 		/* Must be one of the other "auxilliary" attributes */
1167 		else {
1168 
1169 		    /* Search the attribute list for the attribute */
1170 		    prevattrval = NULL;
1171 		    if ((attrval = modent->attrlist) != NULL) do {
1172 			if (strcmp(*pp, attrval->attr) == 0) {
1173 
1174 			    /* Found.  Remove from attribute list */
1175 			    found = TRUE;
1176 			    free(attrval->attr);
1177 			    free(attrval->val);
1178 			    if (prevattrval) {
1179 				prevattrval->next = attrval->next;
1180 				free(attrval);
1181 				attrval = prevattrval->next;
1182 			    } else {
1183 				modent->attrlist = attrval->next;
1184 				free(attrval);
1185 				attrval = modent->attrlist;
1186 			    }
1187 			} else {
1188 			    prevattrval = attrval;	/* Advance to next */
1189 			    attrval = attrval->next;
1190 			}
1191 		    } while (!found && attrval);
1192 
1193 		}   /* End attribute search loop */
1194 
1195 		/*
1196 		 * If the requested attribute wasn't defined for the device,
1197 		 * put it in the list of attributes not found
1198 		 */
1199 
1200 		if (!found) {
1201 
1202 			/*
1203 			 * If there's no list (yet), alloc enough space for
1204 			 * the list
1205 			 */
1206 
1207 		    if (nonotfounds)
1208 			if (*notfounds = malloc(sizeof (char **)*(nattrs+1))) {
1209 
1210 			    /* List allocated -- put in the first entry */
1211 			    nonotfounds = FALSE;
1212 			    pnxt = *notfounds;
1213 			    if (*pnxt = malloc(strlen(*pp)+1)) {
1214 				errno = EINVAL;
1215 				noerr = FALSE;
1216 				(void) strcpy(*pnxt++, *pp);
1217 			    } else {
1218 				/* malloc() failed, free list */
1219 				free(*notfounds);
1220 				*notfounds = NULL;
1221 				nonotfounds = TRUE;
1222 				nobaderr = FALSE;
1223 			    }
1224 
1225 			} else nobaderr = FALSE;  /* malloc() failed */
1226 
1227 		    else {
1228 			/* Already a list, add this attribute to it */
1229 			if (*pnxt = malloc(strlen(*pp)+1))
1230 			    (void) strcpy(*pnxt++, *pp);
1231 			else {
1232 			    /* Out of memory, clean up */
1233 			    for (pnxt = *notfounds; *pnxt; pnxt++)
1234 				free(*pnxt);
1235 			    free(*notfounds);
1236 			    *notfounds = NULL;
1237 			    nonotfounds = TRUE;
1238 			    nobaderr = FALSE;
1239 			}
1240 		    }
1241 
1242 		}    /* end if (!found) */
1243 
1244 		/* Terminate the not-found list */
1245 		if (!nonotfounds) *pnxt = NULL;
1246 
1247 	    }	/* end (for each attribute in attribute list) loop */
1248 
1249 
1250 		/*
1251 		 * If we haven't seen any problems so far,
1252 		 * write the new device table
1253 		 */
1254 
1255 	    if (nobaderr) {
1256 
1257 		/* Open the new device table */
1258 		if (fd = opennewdevtab(&tempname)) {
1259 
1260 		/*
1261 		 * For each entry in the existing table, write that entry
1262 		 * to the new table.  If the entry is the one being
1263 		 * modified, write the modified entry instead of the
1264 		 * original entry.
1265 		 */
1266 
1267 		    _setdevtab();		/* Rewind existing table */
1268 		    while (((devtabent = _getdevtabent()) != NULL) &&
1269 			nobaderr) {
1270 
1271 			if (devtabent->entryno != modent->entryno)
1272 			    nobaderr = _putdevtabrec(fd, devtabent) != EOF;
1273 			else nobaderr = _putdevtabrec(fd, modent) != EOF;
1274 			_freedevtabent(devtabent);
1275 		    }
1276 
1277 		/*
1278 		 * If we successfully generated the new table, make it the
1279 		 * new system device table.  Otherwise, just remove the
1280 		 * temporary file we've created.
1281 		 */
1282 
1283 		    if (nobaderr) {
1284 			(void) fclose(fd);
1285 			nobaderr = mknewdevtab(tempname);
1286 		    } else {
1287 			(void) fclose(fd);
1288 			(void) rmnewdevtab(tempname);
1289 		    }
1290 
1291 		}   /* if (_opennewdevtab()) */
1292 		else nobaderr = FALSE;
1293 
1294 		/*
1295 		 * If there was some error, we need to clean up
1296 		 * allocated resources
1297 		 */
1298 		if (!nobaderr && !nonotfounds) {
1299 		    for (pnxt = *notfounds; *pnxt; pnxt++)
1300 			free(*pnxt);
1301 		    free(*notfounds);
1302 		    *notfounds = NULL;
1303 		    nonotfounds = TRUE;
1304 		}
1305 
1306 	    }	/* if (nobaderr) */
1307 
1308 	    /* Free the resources alloc'ed for <device>'s entry */
1309 	    _freedevtabent(modent);
1310 
1311 	} else {
1312 	    /* _getdevrec(device) failed */
1313 	    nobaderr = FALSE;
1314 	    *notfounds = NULL;
1315 	}
1316 
1317 	/* Unlock the device table */
1318 	(void) unlkdevtab();
1319 
1320 	/* We're finished */
1321 	return (noerr && nobaderr);
1322 }
1323