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