xref: /illumos-gate/usr/src/lib/libbsm/common/devalloc.c (revision df3cd224ef765c29101e4110546062199562f757)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <utime.h>
35 #include <synch.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <libintl.h>
39 #include <errno.h>
40 #include <auth_list.h>
41 #include <syslog.h>
42 #include <bsm/devices.h>
43 #include <bsm/devalloc.h>
44 
45 #define	DA_DEFS	"/etc/security/tsol/devalloc_defaults"
46 
47 extern int _readbufline(char *, int, char *, int, int *);
48 extern char *strtok_r(char *, const char *, char **);
49 extern char *_strtok_escape(char *, char *, char **);
50 extern int getdaon(void);
51 extern int da_matchname(devalloc_t *, char *);
52 extern int da_match(devalloc_t *, da_args *);
53 extern int dmap_matchname(devmap_t *, char *);
54 extern int dm_match(devmap_t *, da_args *);
55 extern int dmap_matchtype(devmap_t *dmap, char *type);
56 extern int dmap_matchdev(devmap_t *dmap, char *dev);
57 extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
58 extern char *dmap_physname(devmap_t *dmap);
59 
60 /*
61  * The following structure is for recording old entries to be retained.
62  * We read the entries from the database into a linked list in memory,
63  * then turn around and write them out again.
64  */
65 typedef struct strentry {
66 	struct strentry	*se_next;
67 	char		se_str[4096 + 1];
68 } strentry_t;
69 
70 /*
71  * da_check_longindevperm -
72  *	reads /etc/logindevperm and checks if specified device is in the file.
73  *	returns 1 if specified device found in /etc/logindevperm, else returns 0
74  */
75 int
76 da_check_logindevperm(char *devname)
77 {
78 	int		ret = 0;
79 	int		fd = -1;
80 	int		nlen, plen, slen, lineno, fsize;
81 	char		line[MAX_CANON];
82 	char		*field_delims = " \t\n";
83 	char		*fbuf = NULL;
84 	char		*ptr, *device;
85 	char		*lasts = NULL;
86 	FILE		*fp;
87 	struct stat	f_stat;
88 
89 	/*
90 	 * check if /etc/logindevperm exists and get its size
91 	 */
92 	if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1)
93 		return (0);
94 	if (fstat(fd, &f_stat) != 0) {
95 		(void) close(fd);
96 		return (0);
97 	}
98 	fsize = f_stat.st_size;
99 	if ((fbuf = (char *)malloc(fsize)) == NULL) {
100 		(void) close(fd);
101 		return (0);
102 	}
103 	if ((fp = fdopen(fd, "rF")) == NULL) {
104 		free(fbuf);
105 		(void) close(fd);
106 		return (0);
107 	}
108 
109 	/*
110 	 * read and parse /etc/logindevperm
111 	 */
112 	plen = nlen = lineno = 0;
113 	while (fgets(line, MAX_CANON, fp) != NULL) {
114 		lineno++;
115 		if ((ptr = strchr(line, '#')) != NULL)
116 			*ptr = '\0';	/* handle comments */
117 		if (strtok_r(line, field_delims, &lasts) == NULL)
118 			continue;	/* ignore blank lines */
119 		if (strtok_r(NULL, field_delims, &lasts) == NULL)
120 			/* invalid entry */
121 			continue;
122 		if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
123 			/* empty device list */
124 			continue;
125 		nlen = strlen(ptr) + 1;		/* +1 terminator */
126 		nlen += (plen + 1);
127 		if (plen == 0)
128 			slen = snprintf(fbuf, nlen, "%s", ptr);
129 		else
130 			slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr);
131 		if (slen >= fsize) {
132 			fbuf[0] = '\0';
133 			(void) fclose(fp);
134 			return (slen);
135 		}
136 		plen += slen;
137 	}
138 	(void) fclose(fp);
139 
140 	/*
141 	 * check if devname exists in /etc/logindevperm
142 	 */
143 	device = strtok_r(fbuf, ":", &lasts);
144 	while (device != NULL) {
145 		/*
146 		 * device and devname may be one of these types -
147 		 *    /dev/xx
148 		 *    /dev/xx*
149 		 *    /dev/dir/xx
150 		 *    /dev/dir/xx*
151 		 *    /dev/dir/"*"
152 		 */
153 		if (strcmp(device, devname) == 0) {
154 			/* /dev/xx, /dev/dir/xx */
155 			free(fbuf);
156 			return (1);
157 		}
158 		if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
159 			/* all wildcard types */
160 			*ptr = '\0';
161 			if (strncmp(device, devname, strlen(device)) == 0) {
162 				free(fbuf);
163 				return (1);
164 			}
165 		}
166 		device = strtok_r(NULL, ":", &lasts);
167 	}
168 
169 	return (ret);
170 }
171 
172 /*
173  * _da_read_file -
174  *	establishes readers/writer lock on fname; reads in the file if its
175  *	contents changed since the last time we read it.
176  *	returns size of buffer read, or -1 on failure.
177  */
178 int
179 _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock,
180     int flag)
181 {
182 	int		fd = -1;
183 	int		fsize = 0;
184 	time_t		newtime;
185 	struct stat	f_stat;
186 
187 	if (flag & DA_FORCE)
188 		*ftime = 0;
189 
190 	/* check the size and the time stamp on the file */
191 	if (rw_rdlock(flock) != 0)
192 		return (-1);
193 	if (stat(fname, &f_stat) != 0) {
194 		(void) rw_unlock(flock);
195 		return (-1);
196 	}
197 	fsize = f_stat.st_size;
198 	newtime = f_stat.st_mtime;
199 	(void) rw_unlock(flock);
200 
201 	while (newtime > *ftime) {
202 		/*
203 		 * file has been modified since we last read it; or this
204 		 * is a forced read.
205 		 * read file into the buffer with rw lock.
206 		 */
207 		if (rw_wrlock(flock) != 0)
208 			return (-1);
209 		if ((fd = open(fname, O_RDONLY)) == -1) {
210 			(void) rw_unlock(flock);
211 			return (-1);
212 		}
213 		if (*fbuf != NULL) {
214 			free(*fbuf);
215 			*fbuf = NULL;
216 		}
217 		if ((*fbuf = malloc(fsize)) == NULL) {
218 			(void) rw_unlock(flock);
219 			(void) close(fd);
220 			return (-1);
221 		}
222 		if (read(fd, *fbuf, fsize) < fsize) {
223 			free(*fbuf);
224 			(void) rw_unlock(flock);
225 			(void) close(fd);
226 			return (-1);
227 		}
228 		(void) rw_unlock(flock);
229 		/*
230 		 * verify that the file did not change just after we read it.
231 		 */
232 		if (rw_rdlock(flock) != 0) {
233 			free(*fbuf);
234 			(void) close(fd);
235 			return (-1);
236 		}
237 		if (stat(fname, &f_stat) != 0) {
238 			free(*fbuf);
239 			(void) rw_unlock(flock);
240 			(void) close(fd);
241 			return (-1);
242 		}
243 		fsize = f_stat.st_size;
244 		newtime = f_stat.st_mtime;
245 		(void) rw_unlock(flock);
246 		(void) close(fd);
247 		*ftime = newtime;
248 	}
249 
250 	return (fsize);
251 }
252 
253 /*
254  * _update_zonename -
255  *	add/remove current zone's name to the given devalloc_t.
256  */
257 void
258 _update_zonename(da_args *dargs, devalloc_t *dap)
259 {
260 	int		i, j;
261 	int		oldsize, newsize;
262 	int		has_zonename = 0;
263 	char		*zonename;
264 	kva_t		*newkva, *oldkva;
265 	kv_t		*newdata, *olddata;
266 	devinfo_t	*devinfo;
267 
268 	devinfo = dargs->devinfo;
269 	oldkva = dap->da_devopts;
270 	if (oldkva == NULL) {
271 		if (dargs->optflag & DA_REMOVE_ZONE)
272 			return;
273 		if (dargs->optflag & DA_ADD_ZONE) {
274 			newkva = _str2kva(devinfo->devopts, KV_ASSIGN,
275 			    KV_TOKEN_DELIMIT);
276 			if (newkva != NULL)
277 				dap->da_devopts = newkva;
278 			return;
279 		}
280 	}
281 	newsize = oldsize = oldkva->length;
282 	if (kva_match(oldkva, DAOPT_ZONE))
283 		has_zonename = 1;
284 	if (dargs->optflag & DA_ADD_ZONE) {
285 		if ((zonename = index(devinfo->devopts, '=')) == NULL)
286 			return;
287 		zonename++;
288 		if (has_zonename) {
289 			(void) _insert2kva(oldkva, DAOPT_ZONE, zonename);
290 			return;
291 		}
292 		newsize += 1;
293 	} else if (dargs->optflag & DA_REMOVE_ZONE) {
294 		if (has_zonename) {
295 			newsize -= 1;
296 			if (newsize == 0) {
297 				/*
298 				 * If zone name was the only key/value pair,
299 				 * put 'reserved' in the empty slot.
300 				 */
301 				_kva_free(oldkva);
302 				dap->da_devopts = NULL;
303 				return;
304 			}
305 		} else {
306 			return;
307 		}
308 	}
309 	newkva = _new_kva(newsize);
310 	newkva->length = 0;
311 	newdata = newkva->data;
312 	olddata = oldkva->data;
313 	for (i = 0, j = 0; i < oldsize; i++) {
314 		if ((dargs->optflag & DA_REMOVE_ZONE) &&
315 		    (strcmp(olddata[i].key, DAOPT_ZONE) == 0))
316 			continue;
317 		newdata[j].key = strdup(olddata[i].key);
318 		newdata[j].value = strdup(olddata[i].value);
319 		newkva->length++;
320 		j++;
321 	}
322 	if (dargs->optflag & DA_ADD_ZONE) {
323 		newdata[j].key = strdup(DAOPT_ZONE);
324 		newdata[j].value = strdup(zonename);
325 		newkva->length++;
326 	}
327 	_kva_free(oldkva);
328 	dap->da_devopts = newkva;
329 }
330 
331 /*
332  * _dmap2str -
333  *	converts a device_map entry into a printable string
334  *	returns 0 on success, -1 on error.
335  */
336 /*ARGSUSED*/
337 static int
338 _dmap2str(devmap_t *dmp, char *buf, int size, const char *sep)
339 {
340 	int	length;
341 
342 	length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep);
343 	if (length >= size)
344 		return (-1);
345 	length += snprintf(buf + length, size - length, "%s%s",
346 	    dmp->dmap_devtype, sep);
347 	if (length >= size)
348 		return (-1);
349 	length += snprintf(buf + length, size - length, "%s\n",
350 	    dmp->dmap_devlist);
351 	if (length >= size)
352 		return (-1);
353 	return (0);
354 }
355 
356 /*
357  * _dmap2strentry -
358  *	calls dmap2str to break given devmap_t into printable entry.
359  *	returns pointer to decoded entry, NULL on error.
360  */
361 static strentry_t *
362 _dmap2strentry(devmap_t *devmapp)
363 {
364 	strentry_t	*sep;
365 
366 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
367 		return (NULL);
368 	if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str),
369 	    KV_TOKEN_DELIMIT"\\\n\t") != 0) {
370 		free(sep);
371 		return (NULL);
372 	}
373 	return (sep);
374 }
375 
376 /*
377  * fix_optstr -
378  * 	removes trailing ':' from buf.
379  */
380 void
381 fix_optstr(char *buf)
382 {
383 	char	*p = NULL;
384 
385 	if (p = rindex(buf, ':'))
386 		*p = ';';
387 }
388 
389 /*
390  * _da2str -
391  *	converts a device_allocate entry into a printable string
392  *	returns 0 on success, -1 on error.
393  */
394 static int
395 _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep,
396     const char *osep)
397 {
398 	int	length;
399 	int	matching_entry = 0;
400 	char	**dnames;
401 
402 	if (dargs->optflag & DA_UPDATE &&
403 	    (dargs->optflag & DA_ADD_ZONE ||
404 	    dargs->optflag & DA_REMOVE_ZONE) &&
405 	    dargs->devnames) {
406 		for (dnames = dargs->devnames; *dnames != NULL; dnames++) {
407 			if (da_matchname(dap, *dnames)) {
408 				matching_entry = 1;
409 				break;
410 			}
411 		}
412 	}
413 	length = snprintf(buf, size, "%s%s", dap->da_devname, sep);
414 	if (length >= size)
415 		return (-1);
416 	length += snprintf(buf + length, size - length, "%s%s",
417 	    dap->da_devtype, sep);
418 	if (length >= size)
419 		return (-1);
420 	if (matching_entry)
421 		_update_zonename(dargs, dap);
422 	if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) &&
423 	    (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) {
424 		length += snprintf(buf + length, size - length, "%s%s",
425 		    DA_RESERVED, sep);
426 	} else {
427 		if (_kva2str(dap->da_devopts, buf + length, size - length,
428 		    KV_ASSIGN, (char *)osep) != 0)
429 			return (-1);
430 		length = strlen(buf);
431 	}
432 	if (dap->da_devopts)
433 		fix_optstr(buf);
434 	if (length >= size)
435 		return (-1);
436 	length += snprintf(buf + length, size - length, "%s%s",
437 	    DA_RESERVED, sep);
438 	if (length >= size)
439 		return (-1);
440 	length += snprintf(buf + length, size - length, "%s%s",
441 	    dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep);
442 	if (length >= size)
443 		return (-1);
444 	length += snprintf(buf + length, size - length, "%s\n",
445 	    dap->da_devexec ? dap->da_devexec : "");
446 	if (length >= size)
447 		return (-1);
448 
449 	return (0);
450 }
451 
452 /*
453  * _da2strentry -
454  *	calls da2str to break given devalloc_t into printable entry.
455  *	returns pointer to decoded entry, NULL on error.
456  */
457 static strentry_t *
458 _da2strentry(da_args *dargs, devalloc_t *dap)
459 {
460 	strentry_t	*sep;
461 
462 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
463 		return (NULL);
464 	if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str),
465 	    KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) {
466 		free(sep);
467 		return (NULL);
468 	}
469 	return (sep);
470 }
471 
472 /*
473  * _def2str
474  *	converts da_defs_t into a printable string.
475  *	returns 0 on success, -1 on error.
476  */
477 static int
478 _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep)
479 {
480 	int length;
481 
482 	length = snprintf(buf, size, "%s%s", da_defs->devtype, sep);
483 	if (length >= size)
484 		return (-1);
485 	if (da_defs->devopts) {
486 		if (_kva2str(da_defs->devopts, buf + length, size - length,
487 		    KV_ASSIGN, KV_DELIMITER) != 0)
488 			return (-1);
489 		length = strlen(buf);
490 	}
491 	if (length >= size)
492 		return (-1);
493 
494 	return (0);
495 }
496 
497 /*
498  * _def2strentry
499  *	calls _def2str to break given da_defs_t into printable entry.
500  *	returns pointer decoded entry, NULL on error.
501  */
502 static strentry_t *
503 _def2strentry(da_defs_t *da_defs)
504 {
505 	strentry_t	*sep;
506 
507 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
508 		return (NULL);
509 	if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str),
510 	    KV_TOKEN_DELIMIT) != 0) {
511 		free(sep);
512 		return (NULL);
513 	}
514 
515 	return (sep);
516 }
517 
518 /*
519  * _build_defattrs
520  *	cycles through all defattr entries, stores them in memory. removes
521  *	entries with the given search_key (device type).
522  *	returns 0 if given entry not found, 1 if given entry removed, 2 on
523  *	error.
524  */
525 static int
526 _build_defattrs(da_args *dargs, strentry_t **head_defent)
527 {
528 	int		rc = 0;
529 	da_defs_t	*da_defs;
530 	strentry_t	*tail_str, *tmp_str;
531 
532 	setdadefent();
533 	while ((da_defs = getdadefent()) != NULL) {
534 		rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype));
535 		if (rc && dargs->optflag & DA_ADD &&
536 		    !(dargs->optflag & DA_FORCE)) {
537 			/*
538 			 * During DA_ADD, we keep an existing entry unless
539 			 * we have DA_FORCE set to override that entry.
540 			 */
541 			dargs->optflag |= DA_NO_OVERRIDE;
542 			rc = 0;
543 		}
544 		if (rc == 0) {
545 			tmp_str = _def2strentry(da_defs);
546 			if (tmp_str == NULL) {
547 				freedadefent(da_defs);
548 				enddadefent();
549 				return (2);
550 			}
551 			/* retaining defattr entry: tmp_str->se_str */
552 			tmp_str->se_next = NULL;
553 			if (*head_defent == NULL) {
554 				*head_defent = tail_str = tmp_str;
555 			} else {
556 				tail_str->se_next = tmp_str;
557 				tail_str = tmp_str;
558 			}
559 		}
560 		freedadefent(da_defs);
561 	}
562 	enddadefent();
563 
564 	return (rc);
565 }
566 
567 /*
568  * _rebuild_lists -
569  *
570  *      If dargs->optflag & DA_EVENT, does not assume the dargs list is
571  *      complete or completely believable, since devfsadm caches
572  *      ONLY what it has been exposed to via syseventd.
573  *
574  *	Cycles through all the entries in the /etc files, stores them
575  *      in memory, takes note of device->dname numbers (i.e. rmdisk0,
576  *      rmdisk12)
577  *
578  *      Cycles through again, adds dargs entry
579  *	with the name tname%d (lowest unused number for the device type)
580  *	to the list of things for the caller to write out to a file,
581  *      IFF it is a new entry.
582  *
583  *      It is an error for it to already be there.
584  *
585  *	Add:
586  *         Returns 0 if successful and 2 on error.
587  *      Remove:
588  *         Returns 0 if not found, 1 if found,  2 on error.
589  */
590 static int
591 _rebuild_lists(da_args *dargs, strentry_t **head_devallocp,
592     strentry_t **head_devmapp)
593 {
594 	int		rc = 0;
595 	devalloc_t	*devallocp;
596 	devmap_t	*devmapp;
597 	strentry_t	*tail_str;
598 	strentry_t	*tmp_str;
599 	uint64_t	tmp_bitmap = 0;
600 	int		tmp = 0;
601 	char		new_devname[DA_MAXNAME + 1];
602 	char		errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
603 	char		*realname;
604 	int		suffix = DA_MAX_DEVNO + 1;
605 	int		found = 0;
606 
607 	if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
608 		return (2);
609 
610 	if (dargs->optflag & DA_FORCE)
611 		return (2);
612 
613 	/* read both files, maps first so we can compare actual devices */
614 
615 	/* build device_maps */
616 	setdmapent();
617 	while ((devmapp = getdmapent()) != NULL) {
618 		if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
619 		    == 1) {
620 			if (dargs->optflag & DA_REMOVE) {
621 				if ((devmapp->dmap_devarray == NULL) ||
622 				    (devmapp->dmap_devarray[0] == NULL)) {
623 					freedmapent(devmapp);
624 					enddmapent();
625 					return (2);
626 				}
627 				realname = dmap_physname(devmapp);
628 				if (realname == NULL) {
629 					freedmapent(devmapp);
630 					enddmapent();
631 					return (2);
632 				}
633 				if (strstr(realname, dargs->devinfo->devlist)
634 				    != NULL) {
635 					if (dargs->devinfo->devname != NULL)
636 						free(dargs->devinfo->devname);
637 					dargs->devinfo->devname =
638 					    strdup(devmapp->dmap_devname);
639 					found = 1;
640 					freedmapent(devmapp);
641 					continue; /* don't retain */
642 				}
643 			} else if (dargs->optflag & DA_ADD) {
644 				/*
645 				 * Need to know which suffixes are in use
646 				 */
647 				rc = (dmap_exact_dev(devmapp,
648 				    dargs->devinfo->devlist, &suffix));
649 
650 				if (rc == 0) {
651 					/*
652 					 * Same type, different device.  Record
653 					 * device suffix already in use.
654 					 */
655 					if (suffix > DA_MAX_DEVNO) {
656 						freedmapent(devmapp);
657 						enddmapent();
658 						return (2);
659 					}
660 					tmp_bitmap |= (uint64_t)(1LL << suffix);
661 				} else {
662 					/*
663 					 * Match on add is an error
664 					 * or mapping attempt returned error
665 					 */
666 					freedmapent(devmapp);
667 					enddmapent();
668 					return (2);
669 				}
670 			} else
671 				/* add other transaction types as needed */
672 				return (2);
673 
674 		}  /* if same type */
675 
676 		tmp_str = _dmap2strentry(devmapp);
677 		if (tmp_str == NULL) {
678 			freedmapent(devmapp);
679 			enddmapent();
680 			return (2);
681 		}
682 		/* retaining devmap entry: tmp_str->se_str */
683 		tmp_str->se_next = NULL;
684 		if (*head_devmapp == NULL) {
685 			*head_devmapp = tail_str = tmp_str;
686 		} else {
687 			tail_str->se_next = tmp_str;
688 			tail_str = tmp_str;
689 		}
690 		freedmapent(devmapp);
691 	}
692 	enddmapent();
693 
694 	/*
695 	 * No need to rewrite the files if the item to be removed is not
696 	 * in the files -- wait for another call on another darg.
697 	 */
698 	if ((dargs->optflag & DA_REMOVE) && !found)
699 		return (0);
700 
701 
702 	if (dargs->optflag & DA_ADD) {
703 		/*
704 		 * Since we got here from an event, we know the stored
705 		 * devname is a useless guess, since the files had not
706 		 * been read when the name was chosen, and we don't keep
707 		 * them anywhere else that is sufficiently definitive.
708 		 */
709 
710 		for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
711 			if (!(tmp_bitmap & (1LL << tmp)))
712 				break;
713 		/* Future: support more than 64 hotplug devices per type? */
714 		if (tmp > DA_MAX_DEVNO)
715 			return (2);
716 
717 		(void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
718 		    dargs->devinfo->devtype, tmp);
719 		if (dargs->devinfo->devname != NULL)
720 			free(dargs->devinfo->devname);
721 		dargs->devinfo->devname = strdup(new_devname);
722 	}
723 
724 	/*
725 	 * Now adjust devalloc list to match devmaps
726 	 * Note we now have the correct devname for da_match to use.
727 	 */
728 	setdaent();
729 	while ((devallocp = getdaent()) != NULL) {
730 		rc = da_match(devallocp, dargs);
731 		if (rc == 1) {
732 			if (dargs->optflag & DA_ADD) {
733 				/* logging is on if DA_EVENT is set */
734 				if (dargs->optflag & DA_EVENT) {
735 					(void) snprintf(errmsg, sizeof (errmsg),
736 					    "%s and %s out of sync,"
737 					    "%s only in %s.",
738 					    DEVALLOC, DEVMAP,
739 					    devallocp->da_devname, DEVALLOC);
740 					syslog(LOG_ERR, "%s", errmsg);
741 				}
742 				freedaent(devallocp);
743 				enddaent();
744 				return (2);
745 			} else if (dargs->optflag & DA_REMOVE) {
746 				/* make list w/o this entry */
747 				freedaent(devallocp);
748 				continue;
749 			}
750 		}
751 		tmp_str = _da2strentry(dargs, devallocp);
752 		if (tmp_str == NULL) {
753 			freedaent(devallocp);
754 			enddaent();
755 			return (2);
756 		}
757 		/* retaining devalloc entry: tmp_str->se_str */
758 		tmp_str->se_next = NULL;
759 		if (*head_devallocp == NULL) {
760 			*head_devallocp = tail_str = tmp_str;
761 		} else {
762 			tail_str->se_next = tmp_str;
763 			tail_str = tmp_str;
764 		}
765 		freedaent(devallocp);
766 	}
767 	enddaent();
768 
769 	/* the caller needs to know if a remove needs to rewrite files */
770 	if (dargs->optflag & DA_REMOVE)
771 		return (1);  /* 0 and 2 cases returned earlier */
772 
773 	return (0);  /* Successful DA_ADD */
774 }
775 /*
776  * _build_lists -
777  *	Cycles through all the entries, stores them in memory. removes entries
778  *	with the given search_key (device name or type).
779  *	returns 0 if given entry not found, 1 if given entry removed, 2 on
780  *	error.
781  */
782 static int
783 _build_lists(da_args *dargs, strentry_t **head_devallocp,
784     strentry_t **head_devmapp)
785 {
786 	int		rc = 0;
787 	devalloc_t	*devallocp;
788 	devmap_t	*devmapp;
789 	strentry_t	*tail_str;
790 	strentry_t	*tmp_str;
791 
792 	if (dargs->optflag & DA_MAPS_ONLY)
793 		goto dmap_only;
794 
795 	/* build device_allocate */
796 	setdaent();
797 	while ((devallocp = getdaent()) != NULL) {
798 		rc = da_match(devallocp, dargs);
799 		if (rc && dargs->optflag & DA_ADD &&
800 		    !(dargs->optflag & DA_FORCE)) {
801 			/*
802 			 * During DA_ADD, we keep an existing entry unless
803 			 * we have DA_FORCE set to override that entry.
804 			 */
805 			dargs->optflag |= DA_NO_OVERRIDE;
806 			rc = 0;
807 		}
808 		if (rc == 0) {
809 			tmp_str = _da2strentry(dargs, devallocp);
810 			if (tmp_str == NULL) {
811 				freedaent(devallocp);
812 				enddaent();
813 				return (2);
814 			}
815 			/* retaining devalloc entry: tmp_str->se_str */
816 			tmp_str->se_next = NULL;
817 			if (*head_devallocp == NULL) {
818 				*head_devallocp = tail_str = tmp_str;
819 			} else {
820 				tail_str->se_next = tmp_str;
821 				tail_str = tmp_str;
822 			}
823 		}
824 		freedaent(devallocp);
825 	}
826 	enddaent();
827 
828 dmap_only:
829 	if (dargs->optflag & DA_ALLOC_ONLY)
830 		return (rc);
831 
832 	/* build device_maps */
833 	rc = 0;
834 	setdmapent();
835 	while ((devmapp = getdmapent()) != NULL) {
836 		rc = dm_match(devmapp, dargs);
837 		if (rc && dargs->optflag & DA_ADD &&
838 		    !(dargs->optflag & DA_FORCE)) {
839 			/*
840 			 * During DA_ADD, we keep an existing entry unless
841 			 * we have DA_FORCE set to override that entry.
842 			 */
843 			dargs->optflag |= DA_NO_OVERRIDE;
844 			rc = 0;
845 		}
846 		if (rc == 0) {
847 			tmp_str = _dmap2strentry(devmapp);
848 			if (tmp_str == NULL) {
849 				freedmapent(devmapp);
850 				enddmapent();
851 				return (2);
852 			}
853 			/* retaining devmap entry: tmp_str->se_str */
854 			tmp_str->se_next = NULL;
855 			if (*head_devmapp == NULL) {
856 				*head_devmapp = tail_str = tmp_str;
857 			} else {
858 				tail_str->se_next = tmp_str;
859 				tail_str = tmp_str;
860 			}
861 		}
862 		freedmapent(devmapp);
863 	}
864 	enddmapent();
865 
866 	return (rc);
867 }
868 
869 /*
870  * _write_defattrs
871  *	writes current entries to devalloc_defaults.
872  */
873 static void
874 _write_defattrs(FILE *fp, strentry_t *head_defent)
875 {
876 	strentry_t *tmp_str;
877 
878 	for (tmp_str = head_defent; tmp_str != NULL;
879 	    tmp_str = tmp_str->se_next) {
880 		(void) fputs(tmp_str->se_str, fp);
881 		(void) fputs("\n", fp);
882 	}
883 
884 }
885 
886 /*
887  * _write_device_allocate -
888  *	writes current entries in the list to device_allocate.
889  *	frees the strings
890  */
891 static void
892 _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
893 {
894 	int		is_on = -1;
895 	strentry_t	*tmp_str, *old_str;
896 	struct stat	dastat;
897 
898 	(void) fseek(dafp, (off_t)0, SEEK_SET);
899 
900 	/*
901 	 * if the devalloc on/off string existed before,
902 	 * put it back before anything else.
903 	 * we need to check for the string only if the file
904 	 * exists.
905 	 */
906 	if (stat(odevalloc, &dastat) == 0) {
907 		is_on = da_is_on();
908 		if (is_on == 0)
909 			(void) fputs(DA_OFF_STR, dafp);
910 		else if (is_on == 1)
911 			(void) fputs(DA_ON_STR, dafp);
912 	}
913 	tmp_str = head_devallocp;
914 	while (tmp_str) {
915 		(void) fputs(tmp_str->se_str, dafp);
916 		(void) fputs("\n", dafp);
917 		old_str = tmp_str;
918 		tmp_str = tmp_str->se_next;
919 		free(old_str);
920 	}
921 }
922 
923 /*
924  * _write_device_maps -
925  *	writes current entries in the list to device_maps.
926  *	and frees the strings
927  */
928 static void
929 _write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
930 {
931 	strentry_t	*tmp_str, *old_str;
932 
933 	(void) fseek(dmfp, (off_t)0, SEEK_SET);
934 
935 	tmp_str = head_devmapp;
936 	while (tmp_str) {
937 		(void) fputs(tmp_str->se_str, dmfp);
938 		(void) fputs("\n", dmfp);
939 		old_str = tmp_str;
940 		tmp_str = tmp_str->se_next;
941 		free(old_str);
942 	}
943 }
944 
945 /*
946  * _write_new_defattrs
947  *	writes the new entry to devalloc_defaults.
948  *	returns 0 on success, -1 on error.
949  */
950 static int
951 _write_new_defattrs(FILE *fp, da_args *dargs)
952 {
953 	int		count;
954 	char		*tok = NULL, *tokp = NULL;
955 	char		*lasts;
956 	devinfo_t	*devinfo = dargs->devinfo;
957 
958 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
959 		return (-1);
960 	if (!devinfo->devopts)
961 		return (0);
962 	(void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
963 	    KV_TOKEN_DELIMIT);
964 	if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) {
965 		(void) strcpy(tokp, devinfo->devopts);
966 		if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
967 			(void) fprintf(fp, "%s", tok);
968 			count = 1;
969 		}
970 		while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
971 			if (count)
972 				(void) fprintf(fp, "%s", KV_DELIMITER);
973 			(void) fprintf(fp, "%s", tok);
974 			count++;
975 		}
976 	} else {
977 		(void) fprintf(fp, "%s", devinfo->devopts);
978 	}
979 
980 	return (0);
981 }
982 
983 /*
984  * _write_new_entry -
985  *	writes the new devalloc_t to device_allocate or the new devmap_t to
986  *	device_maps.
987  *	returns 0 on success, -1 on error.
988  */
989 static int
990 _write_new_entry(FILE *fp, da_args *dargs, int flag)
991 {
992 	int		count;
993 	char		*tok = NULL, *tokp = NULL;
994 	char		*lasts;
995 	devinfo_t	*devinfo = dargs->devinfo;
996 
997 	if (flag & DA_MAPS_ONLY)
998 		goto dmap_only;
999 
1000 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
1001 		return (-1);
1002 
1003 	(void) fprintf(fp, "%s%s\\\n\t",
1004 	    (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
1005 	(void) fprintf(fp, "%s%s\\\n\t",
1006 	    (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
1007 	if (devinfo->devopts == NULL) {
1008 		(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
1009 		    KV_DELIMITER);
1010 	} else {
1011 		if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1))
1012 		    != NULL) {
1013 			(void) strcpy(tokp, devinfo->devopts);
1014 			if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
1015 			    NULL) {
1016 				(void) fprintf(fp, "%s", tok);
1017 				count = 1;
1018 			}
1019 			while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
1020 			    &lasts)) != NULL) {
1021 				if (count)
1022 					(void) fprintf(fp, "%s",
1023 					    KV_TOKEN_DELIMIT "\\\n\t");
1024 				(void) fprintf(fp, "%s", tok);
1025 				count++;
1026 			}
1027 			if (count)
1028 				(void) fprintf(fp, "%s",
1029 				    KV_DELIMITER "\\\n\t");
1030 		} else {
1031 			(void) fprintf(fp, "%s%s", devinfo->devopts,
1032 			    KV_DELIMITER "\\\n\t");
1033 		}
1034 	}
1035 	(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
1036 	(void) fprintf(fp, "%s%s\\\n\t",
1037 	    (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
1038 	    KV_DELIMITER);
1039 	(void) fprintf(fp, "%s\n",
1040 	    (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
1041 
1042 dmap_only:
1043 	if (flag & DA_ALLOC_ONLY)
1044 		return (0);
1045 
1046 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
1047 		return (-1);
1048 
1049 	(void) fprintf(fp, "%s%s\\\n",
1050 	    (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
1051 	(void) fprintf(fp, "\t%s%s\\\n",
1052 	    (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
1053 	(void) fprintf(fp, "\t%s\n",
1054 	    (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
1055 
1056 	return (0);
1057 }
1058 
1059 /*
1060  * _da_lock_devdb -
1061  *	locks the database files; lock can be either broken explicitly by
1062  *	closing the fd of the lock file, or it expires automatically at process
1063  *	termination.
1064  * 	returns fd of the lock file or -1 on error.
1065  */
1066 int
1067 _da_lock_devdb(char *rootdir)
1068 {
1069 	int		lockfd = -1;
1070 	int		ret;
1071 	int		count = 0;
1072 	int		retry = 10;
1073 	int		retry_sleep;
1074 	uint_t		seed;
1075 	char		*lockfile;
1076 	char		path[MAXPATHLEN];
1077 	int		size = sizeof (path);
1078 
1079 	if (rootdir == NULL) {
1080 		lockfile = DA_DB_LOCK;
1081 	} else {
1082 		path[0] = '\0';
1083 		if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
1084 			return (-1);
1085 		lockfile = path;
1086 	}
1087 
1088 	if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
1089 		/* cannot open lock file */
1090 		return (-1);
1091 
1092 	(void) fchown(lockfd, DA_UID, DA_GID);
1093 
1094 	if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
1095 		/* cannot position lock file */
1096 		(void) close(lockfd);
1097 		return (-1);
1098 	}
1099 	errno = 0;
1100 	while (retry > 0) {
1101 		count++;
1102 		seed = (uint_t)gethrtime();
1103 		ret = lockf(lockfd, F_TLOCK, 0);
1104 		if (ret == 0) {
1105 			(void) utime(lockfile, NULL);
1106 			return (lockfd);
1107 		}
1108 		if ((errno != EACCES) && (errno != EAGAIN)) {
1109 			/* cannot set lock */
1110 			(void) close(lockfd);
1111 			return (-1);
1112 		}
1113 		retry--;
1114 		retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
1115 		(void) sleep(retry_sleep);
1116 		errno = 0;
1117 	}
1118 
1119 	return (-1);
1120 }
1121 
1122 /*
1123  * da_open_devdb -
1124  *	opens one or both database files - device_allocate, device_maps - in
1125  *	the specified mode.
1126  *	locks the database files; lock is either broken explicitly by the
1127  *	caller by closing the lock file fd, or it expires automatically at
1128  *	process termination.
1129  *	writes the file pointer of opened file in the input args - dafp, dmfp.
1130  *	returns fd of the lock file on success, -2 if database file does not
1131  *	exist, -1 on other errors.
1132  */
1133 int
1134 da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
1135 {
1136 	int	oflag = 0;
1137 	int	fda = -1;
1138 	int	fdm = -1;
1139 	int	lockfd = -1;
1140 	char	*fname;
1141 	char	*fmode;
1142 	char	path[MAXPATHLEN];
1143 	FILE	*devfile;
1144 
1145 	if ((dafp == NULL) && (dmfp == NULL))
1146 		return (-1);
1147 
1148 	if (flag & DA_RDWR) {
1149 		oflag = DA_RDWR;
1150 		fmode = "r+F";
1151 	} else if (flag & DA_RDONLY) {
1152 		oflag = DA_RDONLY;
1153 		fmode = "rF";
1154 	}
1155 
1156 	if ((lockfd = _da_lock_devdb(rootdir)) == -1)
1157 		return (-1);
1158 
1159 	if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
1160 		goto dmap_only;
1161 
1162 	path[0] = '\0';
1163 
1164 	/*
1165 	 * open the device allocation file
1166 	 */
1167 	if (rootdir == NULL) {
1168 		fname = DEVALLOC;
1169 	} else {
1170 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
1171 		    DEVALLOC) >= sizeof (path)) {
1172 			if (lockfd != -1)
1173 				(void) close(lockfd);
1174 			return (-1);
1175 		}
1176 		fname = path;
1177 	}
1178 	if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
1179 		if (lockfd != -1)
1180 			(void) close(lockfd);
1181 		return ((errno == ENOENT) ? -2 : -1);
1182 	}
1183 	if ((devfile = fdopen(fda, fmode)) == NULL) {
1184 		(void) close(fda);
1185 		if (lockfd != -1)
1186 			(void) close(lockfd);
1187 		return (-1);
1188 	}
1189 	*dafp = devfile;
1190 	(void) fchmod(fda, DA_DBMODE);
1191 
1192 	if ((flag & DA_ALLOC_ONLY))
1193 		goto out;
1194 
1195 dmap_only:
1196 	path[0] = '\0';
1197 	/*
1198 	 * open the device map file
1199 	 */
1200 	if (rootdir == NULL) {
1201 		fname = DEVMAP;
1202 	} else {
1203 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
1204 		    DEVMAP) >= sizeof (path)) {
1205 			(void) close(fda);
1206 			if (lockfd != -1)
1207 				(void) close(lockfd);
1208 			return (-1);
1209 		}
1210 		fname = path;
1211 	}
1212 
1213 	if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
1214 		if (lockfd != -1)
1215 			(void) close(lockfd);
1216 		return ((errno == ENOENT) ? -2 : -1);
1217 	}
1218 
1219 	if ((devfile = fdopen(fdm, fmode)) == NULL) {
1220 		(void) close(fdm);
1221 		(void) close(fda);
1222 		if (lockfd != -1)
1223 			(void) close(lockfd);
1224 		return (-1);
1225 	}
1226 	*dmfp = devfile;
1227 	(void) fchmod(fdm, DA_DBMODE);
1228 
1229 out:
1230 	return (lockfd);
1231 }
1232 
1233 /*
1234  * _record_on_off -
1235  *	adds either DA_ON_STR or DA_OFF_STR to device_allocate
1236  *	returns 0 on success, -1 on error.
1237  */
1238 static int
1239 _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
1240 {
1241 	int		dafd;
1242 	int		nsize;
1243 	int		nitems = 1;
1244 	int		actionlen;
1245 	int		str_found = 0;
1246 	int		len = 0, nlen = 0, plen = 0;
1247 	char		*ptr = NULL;
1248 	char		*actionstr;
1249 	char		*nbuf = NULL;
1250 	char		line[MAX_CANON];
1251 	struct stat	dastat;
1252 
1253 	if (dargs->optflag & DA_ON)
1254 		actionstr = DA_ON_STR;
1255 	else
1256 		actionstr = DA_OFF_STR;
1257 	actionlen = strlen(actionstr);
1258 	dafd = fileno(dafp);
1259 	if (fstat(dafd, &dastat) == -1)
1260 		return (-1);
1261 
1262 	/* check the old device_allocate for on/off string */
1263 	ptr = fgets(line, MAX_CANON, dafp);
1264 	if (ptr != NULL) {
1265 		if ((strcmp(line, DA_ON_STR) == 0) ||
1266 		    (strcmp(line, DA_OFF_STR) == 0)) {
1267 			str_found = 1;
1268 			nsize = dastat.st_size;
1269 		}
1270 	}
1271 	if (!ptr || !str_found) {
1272 		/*
1273 		 * the file never had either the on or the off string;
1274 		 * make room for it.
1275 		 */
1276 		str_found = 0;
1277 		nsize = dastat.st_size + actionlen + 1;
1278 	}
1279 	if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
1280 		return (-1);
1281 	nbuf[0] = '\0';
1282 	/* put the on/off string */
1283 	(void) strcpy(nbuf, actionstr);
1284 	nlen = strlen(nbuf);
1285 	plen = nlen;
1286 	if (ptr && !str_found) {
1287 		/* now put the first line that we read in fgets */
1288 		nlen = plen + strlen(line) + 1;
1289 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1290 		if (len >= nsize) {
1291 			free(nbuf);
1292 			return (-1);
1293 		}
1294 		plen += len;
1295 	}
1296 
1297 	/* now get the rest of the old file */
1298 	while (fgets(line, MAX_CANON, dafp) != NULL) {
1299 		nlen = plen + strlen(line) + 1;
1300 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1301 		if (len >= nsize) {
1302 			free(nbuf);
1303 			return (-1);
1304 		}
1305 		plen += len;
1306 	}
1307 	len = strlen(nbuf) + 1;
1308 	if (len < nsize)
1309 		nbuf[len] = '\n';
1310 
1311 	/* write the on/off str + the old device_allocate to the temp file */
1312 	if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
1313 		free(nbuf);
1314 		return (-1);
1315 	}
1316 
1317 	free(nbuf);
1318 
1319 	return (0);
1320 }
1321 
1322 /*
1323  * da_update_defattrs -
1324  *	writes default attributes to devalloc_defaults
1325  *	returns 0 on success, -1 on error.
1326  */
1327 int
1328 da_update_defattrs(da_args *dargs)
1329 {
1330 	int		rc = 0, lockfd = 0, tmpfd = 0;
1331 	char		*defpath = DEFATTRS;
1332 	char		*tmpdefpath = TMPATTRS;
1333 	FILE		*tmpfp = NULL;
1334 	struct stat	dstat;
1335 	strentry_t	*head_defent = NULL;
1336 
1337 	if (dargs == NULL)
1338 		return (0);
1339 	if ((lockfd = _da_lock_devdb(NULL)) == -1)
1340 		return (-1);
1341 	if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1342 		(void) close(lockfd);
1343 		return (-1);
1344 	}
1345 	(void) fchown(tmpfd, DA_UID, DA_GID);
1346 	if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
1347 		(void) close(tmpfd);
1348 		(void) unlink(tmpdefpath);
1349 		(void) close(lockfd);
1350 		return (-1);
1351 	}
1352 	/*
1353 	 * examine all entries, remove an old one if required, check
1354 	 * if a new one needs to be added.
1355 	 */
1356 	if (stat(defpath, &dstat) == 0) {
1357 		if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
1358 			if (rc == 1) {
1359 				(void) close(tmpfd);
1360 				(void) unlink(tmpdefpath);
1361 				(void) close(lockfd);
1362 				return (rc);
1363 			}
1364 		}
1365 	}
1366 	/*
1367 	 * write back any existing entries.
1368 	 */
1369 	_write_defattrs(tmpfp, head_defent);
1370 
1371 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1372 		/* add new entries */
1373 		rc = _write_new_defattrs(tmpfp, dargs);
1374 		(void) fclose(tmpfp);
1375 	} else {
1376 		(void) fclose(tmpfp);
1377 	}
1378 	if (rename(tmpdefpath, defpath) != 0) {
1379 		rc = -1;
1380 		(void) unlink(tmpdefpath);
1381 	}
1382 	(void) close(lockfd);
1383 
1384 	return (rc);
1385 }
1386 
1387 /*
1388  * da_update_device -
1389  *	Writes existing entries and the SINGLE change requested by da_args,
1390  *      to device_allocate and device_maps.
1391  * 	Returns 0 on success, -1 on error.
1392  */
1393 int
1394 da_update_device(da_args *dargs)
1395 {
1396 	int		rc;
1397 	int		tafd = -1, tmfd = -1;
1398 	int		lockfd = -1;
1399 	char		*rootdir = NULL;
1400 	char		*apathp = NULL, *mpathp = NULL;
1401 	char		*dapathp = NULL, *dmpathp = NULL;
1402 	char		apath[MAXPATHLEN], mpath[MAXPATHLEN];
1403 	char		dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
1404 	FILE		*tafp = NULL, *tmfp = NULL, *dafp = NULL;
1405 	struct stat	dastat;
1406 	devinfo_t	*devinfo;
1407 	strentry_t	*head_devmapp = NULL;
1408 	strentry_t	*head_devallocp = NULL;
1409 
1410 	if (dargs == NULL)
1411 		return (0);
1412 
1413 	rootdir = dargs->rootdir;
1414 	devinfo = dargs->devinfo;
1415 
1416 	/*
1417 	 * adding/removing entries should be done in both
1418 	 * device_allocate and device_maps. updates can be
1419 	 * done in both or either of the files.
1420 	 */
1421 	if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
1422 		if (dargs->optflag & DA_ALLOC_ONLY ||
1423 		    dargs->optflag & DA_MAPS_ONLY)
1424 			return (0);
1425 	}
1426 
1427 	/*
1428 	 * name, type and list are required fields for adding a new
1429 	 * device.
1430 	 */
1431 	if ((dargs->optflag & DA_ADD) &&
1432 	    ((devinfo->devname == NULL) ||
1433 	    (devinfo->devtype == NULL) ||
1434 	    (devinfo->devlist == NULL))) {
1435 		return (-1);
1436 	}
1437 
1438 	if (rootdir != NULL) {
1439 		if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
1440 		    TMPALLOC) >= sizeof (apath))
1441 			return (-1);
1442 		apathp = apath;
1443 		if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
1444 		    DEVALLOC) >= sizeof (dapath))
1445 			return (-1);
1446 		dapathp = dapath;
1447 		if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1448 			if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
1449 			    TMPMAP) >= sizeof (mpath))
1450 				return (-1);
1451 			mpathp = mpath;
1452 			if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
1453 			    DEVMAP) >= sizeof (dmpath))
1454 				return (-1);
1455 			dmpathp = dmpath;
1456 		}
1457 	} else {
1458 		apathp = TMPALLOC;
1459 		dapathp = DEVALLOC;
1460 		mpathp = TMPMAP;
1461 		dmpathp = DEVMAP;
1462 	}
1463 
1464 	if (dargs->optflag & DA_MAPS_ONLY)
1465 		goto dmap_only;
1466 
1467 	/*
1468 	 * Check if we are here just to record on/off status of
1469 	 * device_allocation.
1470 	 */
1471 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
1472 		lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
1473 		    DA_RDONLY|DA_ALLOC_ONLY);
1474 	else
1475 		lockfd = _da_lock_devdb(rootdir);
1476 	if (lockfd == -1)
1477 		return (-1);
1478 
1479 	if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1480 		(void) close(lockfd);
1481 		(void) fclose(dafp);
1482 		return (-1);
1483 	}
1484 	(void) fchown(tafd, DA_UID, DA_GID);
1485 	if ((tafp = fdopen(tafd, "r+")) == NULL) {
1486 		(void) close(tafd);
1487 		(void) unlink(apathp);
1488 		(void) fclose(dafp);
1489 		(void) close(lockfd);
1490 		return (-1);
1491 	}
1492 
1493 	/*
1494 	 * We don't need to parse the file if we are here just to record
1495 	 * on/off status of device_allocation.
1496 	 */
1497 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
1498 		if (_record_on_off(dargs, tafp, dafp) == -1) {
1499 			(void) close(tafd);
1500 			(void) unlink(apathp);
1501 			(void) fclose(dafp);
1502 			(void) close(lockfd);
1503 			return (-1);
1504 		}
1505 		(void) fclose(dafp);
1506 		goto out;
1507 	}
1508 
1509 	/*
1510 	 * If reacting to a hotplug, read the file entries,
1511 	 * figure out what dname (tname + a new number) goes to the
1512 	 * device being added/removed, and create a good head_devallocp and
1513 	 * head_devmapp with everything good still in it (_rebuild_lists)
1514 	 *
1515 	 * Else examine all the entries, remove an old one if it is
1516 	 * a duplicate with a device being added, returning the
1517 	 * remaining list (_build_lists.)
1518 	 *
1519 	 * We need to do this only if the file exists already.
1520 	 *
1521 	 * Once we have built these lists, we need to free the strings
1522 	 * in the head_* arrays before returning.
1523 	 */
1524 	if (stat(dapathp, &dastat) == 0) {
1525 		/* for device allocation, the /etc files are the "master" */
1526 		if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
1527 		    (!(dargs->optflag & DA_FORCE)))
1528 			rc = _rebuild_lists(dargs, &head_devallocp,
1529 			    &head_devmapp);
1530 		else
1531 			rc = _build_lists(dargs, &head_devallocp,
1532 			    &head_devmapp);
1533 
1534 		if (rc != 0 && rc != 1) {
1535 			(void) close(tafd);
1536 			(void) unlink(apathp);
1537 			(void) close(lockfd);
1538 			return (-1);
1539 		}
1540 	} else
1541 		rc = 0;
1542 
1543 	if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
1544 		(void) close(tafd);
1545 		(void) unlink(apathp);
1546 		(void) close(lockfd);
1547 		return (0);
1548 	}
1549 	/*
1550 	 * TODO: clean up the workings of DA_UPDATE.
1551 	 * Due to da_match looking at fields that are missing
1552 	 * in dargs for DA_UPDATE, the da_match call returns no match,
1553 	 * but due to the way _da2str combines the devalloc_t info with
1554 	 * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
1555 	 *
1556 	 * This would not scale if any type of update was ever needed
1557 	 * from the daemon.
1558 	 */
1559 
1560 	/*
1561 	 * Write out devallocp along with the devalloc on/off string.
1562 	 */
1563 	_write_device_allocate(dapathp, tafp, head_devallocp);
1564 
1565 	if (dargs->optflag & DA_ALLOC_ONLY)
1566 		goto out;
1567 
1568 dmap_only:
1569 	if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1570 		(void) close(tafd);
1571 		(void) unlink(apathp);
1572 		(void) close(lockfd);
1573 		return (-1);
1574 	}
1575 	(void) fchown(tmfd, DA_UID, DA_GID);
1576 	if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
1577 		(void) close(tafd);
1578 		(void) unlink(apathp);
1579 		(void) close(tmfd);
1580 		(void) unlink(mpathp);
1581 		(void) close(lockfd);
1582 		return (-1);
1583 	}
1584 
1585 	/*
1586 	 * Write back any non-removed pre-existing entries.
1587 	 */
1588 	if (head_devmapp != NULL)
1589 		_write_device_maps(tmfp, head_devmapp);
1590 
1591 out:
1592 	/*
1593 	 * Add any new entries here.
1594 	 */
1595 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1596 		/* add any new entries */
1597 		rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
1598 		(void) fclose(tafp);
1599 
1600 		if (rc == 0)
1601 			rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
1602 		(void) fclose(tmfp);
1603 	} else {
1604 		if (tafp)
1605 			(void) fclose(tafp);
1606 		if (tmfp)
1607 			(void) fclose(tmfp);
1608 	}
1609 
1610 	rc = 0;
1611 	if (!(dargs->optflag & DA_MAPS_ONLY)) {
1612 		if (rename(apathp, dapathp) != 0) {
1613 			rc = -1;
1614 			(void) unlink(apathp);
1615 		}
1616 	}
1617 	if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1618 		if (rename(mpathp, dmpathp) != 0) {
1619 			rc = -1;
1620 			(void) unlink(mpathp);
1621 		}
1622 	}
1623 
1624 	(void) close(lockfd);
1625 
1626 	return (rc);
1627 }
1628 
1629 /*
1630  * da_add_list -
1631  *	adds new /dev link name to the linked list of devices.
1632  *	returns 0 if link added successfully, -1 on error.
1633  */
1634 int
1635 da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
1636 {
1637 	int		instance;
1638 	int		nlen, plen;
1639 	int		new_entry = 0;
1640 	char		*dtype, *dexec, *tname, *kval;
1641 	char		*minstr = NULL, *maxstr = NULL;
1642 	char		dname[DA_MAXNAME + 1];
1643 	kva_t		*kva;
1644 	deventry_t	*dentry = NULL, *nentry = NULL, *pentry = NULL;
1645 	da_defs_t	*da_defs;
1646 
1647 	if (dlist == NULL || link == NULL)
1648 		return (-1);
1649 
1650 	dname[0] = '\0';
1651 	if (flag & DA_AUDIO) {
1652 		dentry = dlist->audio;
1653 		tname = DA_AUDIO_NAME;
1654 		dtype = DA_AUDIO_TYPE;
1655 		dexec = DA_DEFAULT_AUDIO_CLEAN;
1656 	} else if (flag & DA_CD) {
1657 		dentry = dlist->cd;
1658 		tname = DA_CD_NAME;
1659 		dtype = DA_CD_TYPE;
1660 		dexec = DA_DEFAULT_DISK_CLEAN;
1661 	} else if (flag & DA_FLOPPY) {
1662 		dentry = dlist->floppy;
1663 		tname = DA_FLOPPY_NAME;
1664 		dtype = DA_FLOPPY_TYPE;
1665 		dexec = DA_DEFAULT_DISK_CLEAN;
1666 	} else if (flag & DA_TAPE) {
1667 		dentry = dlist->tape;
1668 		tname = DA_TAPE_NAME;
1669 		dtype = DA_TAPE_TYPE;
1670 		dexec = DA_DEFAULT_TAPE_CLEAN;
1671 	} else if (flag & DA_RMDISK) {
1672 		dentry = dlist->rmdisk;
1673 		tname = DA_RMDISK_NAME;
1674 		dtype = DA_RMDISK_TYPE;
1675 		dexec = DA_DEFAULT_DISK_CLEAN;
1676 	} else {
1677 		return (-1);
1678 	}
1679 
1680 	for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
1681 		pentry = nentry;
1682 		(void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
1683 		if (nentry->devinfo.instance == new_instance)
1684 			/*
1685 			 * Add the new link name to the list of links
1686 			 * that the device 'dname' has.
1687 			 */
1688 			break;
1689 	}
1690 
1691 	if (nentry == NULL) {
1692 		/*
1693 		 * Either this is the first entry ever, or no matching entry
1694 		 * was found. Create a new one and add to the list.
1695 		 */
1696 		if (dentry == NULL)		/* first entry ever */
1697 			instance = 0;
1698 		else				/* no matching entry */
1699 			instance++;
1700 		(void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
1701 		if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
1702 		    NULL)
1703 			return (-1);
1704 		if (pentry != NULL)
1705 			pentry->next = nentry;
1706 		new_entry = 1;
1707 		nentry->devinfo.devname = strdup(dname);
1708 		nentry->devinfo.devtype = dtype;
1709 		nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
1710 		nentry->devinfo.devexec = dexec;
1711 		nentry->devinfo.instance = new_instance;
1712 		/*
1713 		 * Look for default label range, authorizations and cleaning
1714 		 * program in devalloc_defaults. If label range is not
1715 		 * specified in devalloc_defaults, assume it to be admin_low
1716 		 * to admin_high.
1717 		 */
1718 		minstr = DA_DEFAULT_MIN;
1719 		maxstr = DA_DEFAULT_MAX;
1720 		setdadefent();
1721 		if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
1722 			kva = da_defs->devopts;
1723 			if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
1724 				minstr = strdup(kval);
1725 			if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
1726 				maxstr = strdup(kval);
1727 			if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
1728 				nentry->devinfo.devauths = strdup(kval);
1729 			if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
1730 				nentry->devinfo.devexec = strdup(kval);
1731 			freedadefent(da_defs);
1732 		}
1733 		enddadefent();
1734 		kval = NULL;
1735 		nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
1736 		    strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
1737 		    strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
1738 		    + 1;			/* +1 for terminator */
1739 		if (kval = (char *)malloc(nlen))
1740 			(void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
1741 			    DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
1742 			    DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
1743 		nentry->devinfo.devopts = kval;
1744 
1745 		nentry->devinfo.devlist = NULL;
1746 		nentry->next = NULL;
1747 	}
1748 
1749 	nlen = strlen(link) + 1;		/* +1 terminator */
1750 	if (nentry->devinfo.devlist) {
1751 		plen = strlen(nentry->devinfo.devlist);
1752 		nlen = nlen + plen + 1;	/* +1 for blank to separate entries */
1753 	} else {
1754 		plen = 0;
1755 	}
1756 
1757 	if ((nentry->devinfo.devlist =
1758 	    (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
1759 		if (new_entry) {
1760 			free(nentry->devinfo.devname);
1761 			free(nentry);
1762 			if (pentry != NULL)
1763 				pentry->next = NULL;
1764 		}
1765 		return (-1);
1766 	}
1767 
1768 	if (plen == 0)
1769 		(void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
1770 	else
1771 		(void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
1772 		    " %s", link);
1773 
1774 	if (pentry == NULL) {
1775 		/*
1776 		 * This is the first entry of this device type.
1777 		 */
1778 		if (flag & DA_AUDIO)
1779 			dlist->audio = nentry;
1780 		else if (flag & DA_CD)
1781 			dlist->cd = nentry;
1782 		else if (flag & DA_FLOPPY)
1783 			dlist->floppy = nentry;
1784 		else if (flag & DA_TAPE)
1785 			dlist->tape = nentry;
1786 		else if (flag & DA_RMDISK)
1787 			dlist->rmdisk = nentry;
1788 	}
1789 
1790 	return (0);
1791 }
1792 
1793 /*
1794  * da_remove_list -
1795  *	removes a /dev link name from the linked list of devices.
1796  *	returns type of device if link for that device removed
1797  *	successfully, else returns -1 on error.
1798  *	if all links for a device are removed, stores that device
1799  *	name in devname.
1800  */
1801 int
1802 da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
1803 {
1804 	int		flag;
1805 	int		remove_dev = 0;
1806 	int		nlen, plen, slen;
1807 	char		*lasts, *lname, *oldlist;
1808 	struct stat	rmstat;
1809 	deventry_t	*dentry, *current, *prev;
1810 
1811 	if (type != NULL)
1812 		flag = type;
1813 	else if (link == NULL)
1814 		return (-1);
1815 	else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
1816 		flag = DA_AUDIO;
1817 	else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
1818 	    strstr(link, "sr") || strstr(link, "rsr"))
1819 		flag = DA_CD;
1820 	else if (strstr(link, "fd") || strstr(link, "rfd") ||
1821 	    strstr(link, "diskette") || strstr(link, "rdiskette"))
1822 		flag = DA_FLOPPY;
1823 	else if (strstr(link, DA_TAPE_NAME))
1824 		flag = DA_TAPE;
1825 	else
1826 		flag = DA_RMDISK;
1827 
1828 	switch (type) {
1829 	case DA_AUDIO:
1830 		dentry = dlist->audio;
1831 		break;
1832 	case DA_CD:
1833 		dentry = dlist->cd;
1834 		break;
1835 	case DA_FLOPPY:
1836 		dentry = dlist->floppy;
1837 		break;
1838 	case DA_TAPE:
1839 		dentry = dlist->tape;
1840 		break;
1841 	case DA_RMDISK:
1842 		dentry = dlist->rmdisk;
1843 		break;
1844 	default:
1845 		return (-1);
1846 	}
1847 
1848 	if ((type != NULL) && (link == NULL)) {
1849 		for (current = dentry, prev = dentry; current != NULL;
1850 		    current = current->next) {
1851 			oldlist = strdup(current->devinfo.devlist);
1852 			for (lname = strtok_r(oldlist, " ", &lasts);
1853 			    lname != NULL;
1854 			    lname = strtok_r(NULL, " ", &lasts)) {
1855 				if (stat(lname, &rmstat) != 0) {
1856 					remove_dev = 1;
1857 					goto remove_dev;
1858 				}
1859 			}
1860 			prev = current;
1861 		}
1862 		return (-1);
1863 	}
1864 
1865 	for (current = dentry, prev = dentry; current != NULL;
1866 	    current = current->next) {
1867 		plen = strlen(current->devinfo.devlist);
1868 		nlen = strlen(link);
1869 		if (plen == nlen) {
1870 			if (strcmp(current->devinfo.devlist, link) == 0) {
1871 				/* last name in the list */
1872 				remove_dev = 1;
1873 				break;
1874 			}
1875 		}
1876 		if (strstr(current->devinfo.devlist, link)) {
1877 			nlen = plen - nlen + 1;
1878 			oldlist = strdup(current->devinfo.devlist);
1879 			if ((current->devinfo.devlist =
1880 			    (char *)realloc(current->devinfo.devlist,
1881 			    nlen)) == NULL) {
1882 				free(oldlist);
1883 				return (-1);
1884 			}
1885 			current->devinfo.devlist[0] = '\0';
1886 			nlen = plen = slen = 0;
1887 			for (lname = strtok_r(oldlist, " ", &lasts);
1888 			    lname != NULL;
1889 			    lname = strtok_r(NULL, " ", &lasts)) {
1890 				if (strcmp(lname, link) == 0)
1891 					continue;
1892 				nlen = strlen(lname) + plen + 1;
1893 				if (plen == 0) {
1894 					slen =
1895 					    snprintf(current->devinfo.devlist,
1896 					    nlen, "%s", lname);
1897 				} else {
1898 					slen =
1899 					    snprintf(current->devinfo.devlist +
1900 					    plen, nlen - plen, " %s", lname);
1901 				}
1902 				plen = plen + slen + 1;
1903 			}
1904 			free(oldlist);
1905 			break;
1906 		}
1907 		prev = current;
1908 	}
1909 
1910 remove_dev:
1911 	if (remove_dev == 1) {
1912 		(void) strlcpy(devname, current->devinfo.devname, size);
1913 		free(current->devinfo.devname);
1914 		free(current->devinfo.devlist);
1915 		current->devinfo.devname = current->devinfo.devlist = NULL;
1916 		prev->next = current->next;
1917 		free(current);
1918 		current = NULL;
1919 	}
1920 	if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
1921 		if (prev->next) {
1922 			/*
1923 			 * what we removed above was the first entry
1924 			 * in the list. make the next entry to be the
1925 			 * first.
1926 			 */
1927 			current = prev->next;
1928 		} else {
1929 			/*
1930 			 * the matching entry was the only entry in the list
1931 			 * for this type.
1932 			 */
1933 			current = NULL;
1934 		}
1935 		if (flag & DA_AUDIO)
1936 			dlist->audio = current;
1937 		else if (flag & DA_CD)
1938 			dlist->cd = current;
1939 		else if (flag & DA_FLOPPY)
1940 			dlist->floppy = current;
1941 		else if (flag & DA_TAPE)
1942 			dlist->tape = current;
1943 		else if (flag & DA_RMDISK)
1944 			dlist->rmdisk = current;
1945 	}
1946 
1947 	return (flag);
1948 }
1949 
1950 /*
1951  * da_rm_list_entry -
1952  *
1953  *	The adding of devnames to a devlist and the removal of a
1954  *	device are not symmetrical -- hot_cleanup gives a /devices
1955  *	name which is used to remove the dentry whose links all point to
1956  *	that /devices entry.
1957  *
1958  *	The link argument is present if available to make debugging
1959  *	easier.
1960  *
1961  *	da_rm_list_entry removes an entry from the linked list of devices.
1962  *
1963  *	Returns 1 if the devname was removed successfully,
1964  *	0 if not found, -1 for error.
1965  */
1966 /*ARGSUSED*/
1967 int
1968 da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
1969 {
1970 	int		retval = 0;
1971 	deventry_t	**dentry, *current, *prev;
1972 
1973 	switch (type) {
1974 	case DA_AUDIO:
1975 		dentry = &(dlist->audio);
1976 		break;
1977 	case DA_CD:
1978 		dentry = &(dlist->cd);
1979 		break;
1980 	case DA_FLOPPY:
1981 		dentry = &(dlist->floppy);
1982 		break;
1983 	case DA_TAPE:
1984 		dentry = &(dlist->tape);
1985 		break;
1986 	case DA_RMDISK:
1987 		dentry = &(dlist->rmdisk);
1988 		break;
1989 	default:
1990 		return (-1);
1991 	}
1992 
1993 	/* Presumably in daemon mode, no need to remove entry, list is empty */
1994 	if (*dentry == (deventry_t *)NULL)
1995 		return (0);
1996 
1997 	prev = NULL;
1998 	for (current = *dentry; current != NULL;
1999 	    prev = current, current = current->next) {
2000 		if (strcmp(devname, current->devinfo.devname))
2001 			continue;
2002 		retval = 1;
2003 		break;
2004 	}
2005 	if (retval == 0)
2006 		return (0);
2007 	free(current->devinfo.devname);
2008 	if (current->devinfo.devlist != NULL)
2009 		free(current->devinfo.devlist);
2010 	if (current->devinfo.devopts != NULL)
2011 		free(current->devinfo.devopts);
2012 
2013 	if (prev == NULL)
2014 		*dentry = current->next;
2015 	else
2016 		prev->next = current->next;
2017 
2018 	free(current);
2019 	return (retval);
2020 }
2021 
2022 /*
2023  * da_is_on -
2024  *	checks if device allocation feature is turned on.
2025  *	returns 1 if on, 0 if off, -1 if status string not
2026  *	found in device_allocate.
2027  */
2028 int
2029 da_is_on()
2030 {
2031 	return (getdaon());
2032 }
2033 
2034 /*
2035  * da_print_device -
2036  *	debug routine to print device entries.
2037  */
2038 void
2039 da_print_device(int flag, devlist_t *devlist)
2040 {
2041 	deventry_t	*entry, *dentry;
2042 	devinfo_t	*devinfo;
2043 
2044 	if (flag & DA_AUDIO)
2045 		dentry = devlist->audio;
2046 	else if (flag & DA_CD)
2047 		dentry = devlist->cd;
2048 	else if (flag & DA_FLOPPY)
2049 		dentry = devlist->floppy;
2050 	else if (flag & DA_TAPE)
2051 		dentry = devlist->tape;
2052 	else if (flag & DA_RMDISK)
2053 		dentry = devlist->rmdisk;
2054 	else
2055 		return;
2056 
2057 	for (entry = dentry; entry != NULL; entry = entry->next) {
2058 		devinfo = &(entry->devinfo);
2059 		(void) fprintf(stdout, "name: %s\n", devinfo->devname);
2060 		(void) fprintf(stdout, "type: %s\n", devinfo->devtype);
2061 		(void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
2062 		(void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
2063 		(void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
2064 	}
2065 }
2066