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 (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <stdlib.h>
27#include <ctype.h>
28#include <unistd.h>
29#include <limits.h>
30#include <fcntl.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <utime.h>
34#include <synch.h>
35#include <strings.h>
36#include <string.h>
37#include <libintl.h>
38#include <errno.h>
39#include <auth_list.h>
40#include <syslog.h>
41#include <bsm/devices.h>
42#include <bsm/devalloc.h>
43#include <tsol/label.h>
44
45#define	DA_DEFS	"/etc/security/tsol/devalloc_defaults"
46
47extern int _readbufline(char *, int, char *, int, int *);
48extern char *strtok_r(char *, const char *, char **);
49extern char *_strtok_escape(char *, char *, char **);
50extern int getdaon(void);
51extern int da_matchname(devalloc_t *, char *);
52extern int da_match(devalloc_t *, da_args *);
53extern int dmap_matchname(devmap_t *, char *);
54extern int dm_match(devmap_t *, da_args *);
55extern int dmap_matchtype(devmap_t *dmap, char *type);
56extern int dmap_matchdev(devmap_t *dmap, char *dev);
57extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
58extern 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 */
65typedef 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 */
75int
76da_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 */
178int
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 */
257void
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*/
337static 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 */
361static 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 */
380void
381fix_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 */
394static 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 */
457static 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 */
477static 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 */
502static 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 */
525static 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 * We have to handle the "standard" types in devlist differently than
569 * other devices, which are not covered by our auto-naming conventions.
570 *
571 * buf must be a buffer of size DA_MAX_NAME + 1
572 */
573int
574da_std_type(da_args *dargs, char *namebuf)
575{
576	char *type = dargs->devinfo->devtype;
577	int system_labeled;
578
579	system_labeled = is_system_labeled();
580
581	/* check safely for sizes */
582	if (strcmp(DA_AUDIO_TYPE, type) == 0) {
583		(void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME);
584		return (1);
585	}
586	if (strcmp(DA_CD_TYPE, type) == 0) {
587		if (system_labeled)
588			(void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME);
589		else
590			(void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME);
591		return (1);
592	}
593	if (strcmp(DA_FLOPPY_TYPE, type) == 0) {
594		if (system_labeled)
595			(void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME);
596		else
597			(void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME);
598		return (1);
599	}
600	if (strcmp(DA_TAPE_TYPE, type) == 0) {
601		if (system_labeled)
602			(void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME);
603		else
604			(void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME);
605		return (1);
606	}
607	if (strcmp(DA_RMDISK_TYPE, type) == 0) {
608		(void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME);
609		return (1);
610	}
611	namebuf[0] = '\0';
612	return (0);
613}
614
615/*
616 * allocatable: returns
617 * -1 if no auths field,
618 * 0 if not allocatable (marked '*')
619 * 1 if not marked '*'
620 */
621static int
622allocatable(da_args *dargs)
623{
624
625	if (!dargs->devinfo->devauths)
626		return (-1);
627	if (strcmp("*", dargs->devinfo->devauths) == 0)
628		return (0);
629	return (1);
630}
631
632/*
633 * _rebuild_lists -
634 *
635 *	If dargs->optflag & DA_EVENT, does not assume the dargs list is
636 *	complete or completely believable, since devfsadm caches
637 *	ONLY what it has been exposed to via syseventd.
638 *
639 *	Cycles through all the entries in the /etc files, stores them
640 *	in memory, takes note of device->dname numbers (e.g. rmdisk0,
641 *	rmdisk12)
642 *
643 *	Cycles through again, adds dargs entry
644 *	with the name tname%d (lowest unused number for the device type)
645 *	to the list of things for the caller to write out to a file,
646 *	IFF it is a new entry.
647 *
648 *	It is an error for it to already be there, if it is allocatable.
649 *
650 *	Add:
651 *	    Returns 0 if successful and 2 on error.
652 *	Remove:
653 *	    Returns 0 if not found, 1 if found,  2 on error.
654 */
655static int
656_rebuild_lists(da_args *dargs, strentry_t **head_devallocp,
657    strentry_t **head_devmapp)
658{
659	int		rc = 0;
660	devalloc_t	*devallocp;
661	devmap_t	*devmapp;
662	strentry_t	*tail_str;
663	strentry_t	*tmp_str;
664	uint64_t	tmp_bitmap = 0;
665	uint_t		tmp = 0;
666	char		*realname;
667	int		suffix;
668	int		found = 0;
669	int		stdtype = 1;
670	int		is_allocatable = 1;
671	char		new_devname[DA_MAXNAME + 1];
672	char		defname[DA_MAXNAME + 1]; /* default name for type */
673	char		errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
674
675	if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
676		return (2);
677
678	if (dargs->optflag & DA_FORCE)
679		return (2);
680
681	if (dargs->optflag & DA_ADD) {
682		stdtype = da_std_type(dargs, defname);
683		is_allocatable = allocatable(dargs);
684	}
685
686	/* read both files, maps first so we can compare actual devices */
687
688	/* build device_maps */
689	setdmapent();
690	while ((devmapp = getdmapent()) != NULL) {
691		suffix = DA_MAX_DEVNO + 1;
692		if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
693		    == 1) {
694			if (dargs->optflag & DA_REMOVE) {
695				if ((devmapp->dmap_devarray == NULL) ||
696				    (devmapp->dmap_devarray[0] == NULL)) {
697					freedmapent(devmapp);
698					enddmapent();
699					return (2);
700				}
701				realname = dmap_physname(devmapp);
702				if (realname == NULL) {
703					freedmapent(devmapp);
704					enddmapent();
705					return (2);
706				}
707				if (strstr(realname, dargs->devinfo->devlist)
708				    != NULL) {
709					/* if need to free and safe to free */
710					if (dargs->devinfo->devname != NULL &&
711					    (dargs->optflag & DA_EVENT) != 0)
712						free(dargs->devinfo->devname);
713					dargs->devinfo->devname =
714					    strdup(devmapp->dmap_devname);
715					found = 1;
716					freedmapent(devmapp);
717					continue; /* don't retain */
718				}
719			} else if (dargs->optflag & DA_ADD) {
720				/*
721				 * Need to know which suffixes are in use
722				 */
723				rc = (dmap_exact_dev(devmapp,
724				    dargs->devinfo->devlist, &suffix));
725
726				if (rc == 0) {
727					/*
728					 * Same type, different device.  Record
729					 * device suffix already in use, if
730					 * applicable.
731					 */
732					if ((suffix < DA_MAX_DEVNO &&
733					    suffix != -1) && stdtype)
734						tmp_bitmap |=
735						    (uint64_t)(1LL << suffix);
736				} else if ((rc == 1) && !is_allocatable) {
737					rc = 0;
738				} else {
739					/*
740					 * Match allocatable on add is an error
741					 * or mapping attempt returned error
742					 */
743					(void) snprintf(errmsg, sizeof (errmsg),
744					    "Cannot add %s on node %s",
745					    dargs->devinfo->devtype,
746					    devmapp->dmap_devname);
747					syslog(LOG_ERR, "%s", errmsg);
748					freedmapent(devmapp);
749					enddmapent();
750					return (2);
751				}
752			} else
753				/* add other transaction types as needed */
754				return (2);
755		} else if ((dargs->optflag & DA_ADD) &&
756		    (stdtype || is_allocatable) &&
757		    dmap_exact_dev(devmapp, dargs->devinfo->devlist,
758		    &suffix)) {
759			/*
760			 * no dups w/o DA_FORCE, even if type differs,
761			 * if there is a chance this operation is
762			 * machine-driven.  The 5 "standard types"
763			 * can be machine-driven adds, and tend to
764			 * be allocatable.
765			 */
766			(void) snprintf(errmsg, sizeof (errmsg),
767			    "Cannot add %s on node %s type %s",
768			    dargs->devinfo->devtype,
769			    devmapp->dmap_devname,
770			    devmapp->dmap_devtype);
771			syslog(LOG_ERR, "%s", errmsg);
772			freedmapent(devmapp);
773			enddmapent();
774			return (2);
775		}
776
777		tmp_str = _dmap2strentry(devmapp);
778		if (tmp_str == NULL) {
779			freedmapent(devmapp);
780			enddmapent();
781			return (2);
782		}
783		/* retaining devmap entry: tmp_str->se_str */
784		tmp_str->se_next = NULL;
785		if (*head_devmapp == NULL) {
786			*head_devmapp = tail_str = tmp_str;
787		} else {
788			tail_str->se_next = tmp_str;
789			tail_str = tmp_str;
790		}
791		freedmapent(devmapp);
792	}
793	enddmapent();
794
795	/*
796	 * No need to rewrite the files if the item to be removed is not
797	 * in the files -- wait for another call on another darg.
798	 */
799	if ((dargs->optflag & DA_REMOVE) && !found)
800		return (0);
801
802
803	if (dargs->optflag & DA_ADD) {
804		int len;
805		/*
806		 * If we got here from an event, or from devfsadm,
807		 * we know the stored devname is a useless guess,
808		 * since the files had not been read when the name
809		 * was chosen, and we don't keep them anywhere else
810		 * that is sufficiently definitive.
811		 */
812
813		for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
814			if (!(tmp_bitmap & (1LL << tmp)))
815				break;
816		/* Future: support more than 64 hotplug devices per type? */
817		if (tmp > DA_MAX_DEVNO)
818			return (2);
819
820		/*
821		 * Let the caller choose the name unless BOTH the name and
822		 * device type one of: cdrom, floppy, audio, rmdisk, or tape.
823		 * (or sr, fd for unlabeled)
824		 */
825		len = strlen(defname);
826		if (stdtype &&
827		    (strncmp(dargs->devinfo->devname, defname, len) == 0)) {
828			(void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
829			    defname, tmp);
830			/* if need to free and safe to free */
831			if (dargs->devinfo->devname != NULL &&
832			    (dargs->optflag & DA_EVENT) != 0)
833				free(dargs->devinfo->devname);
834			dargs->devinfo->devname = strdup(new_devname);
835		}
836	}
837
838	/*
839	 * Now adjust devalloc list to match devmaps
840	 * Note we now have the correct devname for da_match to use.
841	 */
842	setdaent();
843	while ((devallocp = getdaent()) != NULL) {
844		rc = da_match(devallocp, dargs);
845		if (rc == 1) {
846			if (dargs->optflag & DA_ADD) {
847				/* logging is on if DA_EVENT is set */
848				if (dargs->optflag & DA_EVENT) {
849					(void) snprintf(errmsg, sizeof (errmsg),
850					    "%s and %s out of sync,"
851					    "%s only in %s.",
852					    DEVALLOC, DEVMAP,
853					    devallocp->da_devname, DEVALLOC);
854					syslog(LOG_ERR, "%s", errmsg);
855				}
856				freedaent(devallocp);
857				enddaent();
858				return (2);
859			} else if (dargs->optflag & DA_REMOVE) {
860				/* make list w/o this entry */
861				freedaent(devallocp);
862				continue;
863			}
864		}
865		tmp_str = _da2strentry(dargs, devallocp);
866		if (tmp_str == NULL) {
867			freedaent(devallocp);
868			enddaent();
869			return (2);
870		}
871		/* retaining devalloc entry: tmp_str->se_str */
872		tmp_str->se_next = NULL;
873		if (*head_devallocp == NULL) {
874			*head_devallocp = tail_str = tmp_str;
875		} else {
876			tail_str->se_next = tmp_str;
877			tail_str = tmp_str;
878		}
879		freedaent(devallocp);
880	}
881	enddaent();
882
883	/* the caller needs to know if a remove needs to rewrite files */
884	if (dargs->optflag & DA_REMOVE)
885		return (1);  /* 0 and 2 cases returned earlier */
886
887	return (0);  /* Successful DA_ADD */
888}
889
890/*
891 * _build_lists -
892 *	Cycles through all the entries, stores them in memory. removes entries
893 *	with the given search_key (device name or type).
894 *	returns 0 if given entry not found, 1 if given entry removed, 2 on
895 *	error.
896 */
897static int
898_build_lists(da_args *dargs, strentry_t **head_devallocp,
899    strentry_t **head_devmapp)
900{
901	int		rc = 0;
902	int		found = 0;
903	devalloc_t	*devallocp;
904	devmap_t	*devmapp;
905	strentry_t	*tail_str;
906	strentry_t	*tmp_str;
907
908	if (dargs->optflag & DA_MAPS_ONLY)
909		goto dmap_only;
910
911	/* build device_allocate */
912	setdaent();
913	while ((devallocp = getdaent()) != NULL) {
914		rc = da_match(devallocp, dargs);
915		/* if in _build_lists and DA_ADD is set, so is DA_FORCE */
916		if (rc == 0) {
917			tmp_str = _da2strentry(dargs, devallocp);
918			if (tmp_str == NULL) {
919				freedaent(devallocp);
920				enddaent();
921				return (2);
922			}
923			/* retaining devalloc entry: tmp_str->se_str */
924			tmp_str->se_next = NULL;
925			if (*head_devallocp == NULL) {
926				*head_devallocp = tail_str = tmp_str;
927			} else {
928				tail_str->se_next = tmp_str;
929				tail_str = tmp_str;
930			}
931		} else if (rc == 1)
932			found = 1;
933
934		freedaent(devallocp);
935	}
936	enddaent();
937
938dmap_only:
939	if (dargs->optflag & DA_ALLOC_ONLY)
940		return (rc);
941
942	/* build device_maps */
943	rc = 0;
944	setdmapent();
945	while ((devmapp = getdmapent()) != NULL) {
946		rc = dm_match(devmapp, dargs);
947		if (rc == 0) {
948			tmp_str = _dmap2strentry(devmapp);
949			if (tmp_str == NULL) {
950				freedmapent(devmapp);
951				enddmapent();
952				return (2);
953			}
954			/* retaining devmap entry: tmp_str->se_str */
955			tmp_str->se_next = NULL;
956			if (*head_devmapp == NULL) {
957				*head_devmapp = tail_str = tmp_str;
958			} else {
959				tail_str->se_next = tmp_str;
960				tail_str = tmp_str;
961			}
962		}
963		freedmapent(devmapp);
964	}
965	enddmapent();
966
967	/* later code cleanup may cause the use of "found" in other cases */
968	if (dargs->optflag & DA_REMOVE)
969		return (found);
970	return (rc);
971}
972
973/*
974 * _write_defattrs
975 *	writes current entries to devalloc_defaults.
976 */
977static void
978_write_defattrs(FILE *fp, strentry_t *head_defent)
979{
980	strentry_t *tmp_str;
981
982	for (tmp_str = head_defent; tmp_str != NULL;
983	    tmp_str = tmp_str->se_next) {
984		(void) fputs(tmp_str->se_str, fp);
985		(void) fputs("\n", fp);
986	}
987
988}
989
990/*
991 * _write_device_allocate -
992 *	writes current entries in the list to device_allocate.
993 *	frees the strings
994 */
995static void
996_write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
997{
998	int		is_on = -1;
999	strentry_t	*tmp_str, *old_str;
1000	struct stat	dastat;
1001
1002	(void) fseek(dafp, (off_t)0, SEEK_SET);
1003
1004	/*
1005	 * if the devalloc on/off string existed before,
1006	 * put it back before anything else.
1007	 * we need to check for the string only if the file
1008	 * exists.
1009	 */
1010	if (stat(odevalloc, &dastat) == 0) {
1011		is_on = da_is_on();
1012		if (is_on == 0)
1013			(void) fputs(DA_OFF_STR, dafp);
1014		else if (is_on == 1)
1015			(void) fputs(DA_ON_STR, dafp);
1016	}
1017	tmp_str = head_devallocp;
1018	while (tmp_str) {
1019		(void) fputs(tmp_str->se_str, dafp);
1020		(void) fputs("\n", dafp);
1021		old_str = tmp_str;
1022		tmp_str = tmp_str->se_next;
1023		free(old_str);
1024	}
1025}
1026
1027/*
1028 * _write_device_maps -
1029 *	writes current entries in the list to device_maps.
1030 *	and frees the strings
1031 */
1032static void
1033_write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
1034{
1035	strentry_t	*tmp_str, *old_str;
1036
1037	(void) fseek(dmfp, (off_t)0, SEEK_SET);
1038
1039	tmp_str = head_devmapp;
1040	while (tmp_str) {
1041		(void) fputs(tmp_str->se_str, dmfp);
1042		(void) fputs("\n", dmfp);
1043		old_str = tmp_str;
1044		tmp_str = tmp_str->se_next;
1045		free(old_str);
1046	}
1047}
1048
1049/*
1050 * _write_new_defattrs
1051 *	writes the new entry to devalloc_defaults.
1052 *	returns 0 on success, -1 on error.
1053 */
1054static int
1055_write_new_defattrs(FILE *fp, da_args *dargs)
1056{
1057	int		count;
1058	char		*tok = NULL, *tokp = NULL;
1059	char		*lasts;
1060	devinfo_t	*devinfo = dargs->devinfo;
1061
1062	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
1063		return (-1);
1064	if (!devinfo->devopts)
1065		return (0);
1066	(void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
1067	    KV_TOKEN_DELIMIT);
1068	if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) {
1069		(void) strcpy(tokp, devinfo->devopts);
1070		if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
1071			(void) fprintf(fp, "%s", tok);
1072			count = 1;
1073		}
1074		while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
1075			if (count)
1076				(void) fprintf(fp, "%s", KV_DELIMITER);
1077			(void) fprintf(fp, "%s", tok);
1078			count++;
1079		}
1080	} else {
1081		(void) fprintf(fp, "%s", devinfo->devopts);
1082	}
1083
1084	return (0);
1085}
1086
1087/*
1088 * _write_new_entry -
1089 *	writes the new devalloc_t to device_allocate or the new devmap_t to
1090 *	device_maps.
1091 *	returns 0 on success, -1 on error.
1092 */
1093static int
1094_write_new_entry(FILE *fp, da_args *dargs, int flag)
1095{
1096	int		count;
1097	char		*tok = NULL, *tokp = NULL;
1098	char		*lasts;
1099	devinfo_t	*devinfo = dargs->devinfo;
1100
1101	if (flag & DA_MAPS_ONLY)
1102		goto dmap_only;
1103
1104	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
1105		return (-1);
1106
1107	(void) fprintf(fp, "%s%s\\\n\t",
1108	    (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
1109	(void) fprintf(fp, "%s%s\\\n\t",
1110	    (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
1111	if (devinfo->devopts == NULL) {
1112		(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
1113		    KV_DELIMITER);
1114	} else {
1115		if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1))
1116		    != NULL) {
1117			(void) strcpy(tokp, devinfo->devopts);
1118			if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
1119			    NULL) {
1120				(void) fprintf(fp, "%s", tok);
1121				count = 1;
1122			}
1123			while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
1124			    &lasts)) != NULL) {
1125				if (count)
1126					(void) fprintf(fp, "%s",
1127					    KV_TOKEN_DELIMIT "\\\n\t");
1128				(void) fprintf(fp, "%s", tok);
1129				count++;
1130			}
1131			if (count)
1132				(void) fprintf(fp, "%s",
1133				    KV_DELIMITER "\\\n\t");
1134		} else {
1135			(void) fprintf(fp, "%s%s", devinfo->devopts,
1136			    KV_DELIMITER "\\\n\t");
1137		}
1138	}
1139	(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
1140	(void) fprintf(fp, "%s%s\\\n\t",
1141	    (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
1142	    KV_DELIMITER);
1143	(void) fprintf(fp, "%s\n",
1144	    (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
1145
1146dmap_only:
1147	if (flag & DA_ALLOC_ONLY)
1148		return (0);
1149
1150	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
1151		return (-1);
1152
1153	(void) fprintf(fp, "%s%s\\\n",
1154	    (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
1155	(void) fprintf(fp, "\t%s%s\\\n",
1156	    (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
1157	(void) fprintf(fp, "\t%s\n",
1158	    (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
1159
1160	return (0);
1161}
1162
1163/*
1164 * _da_lock_devdb -
1165 *	locks the database files; lock can be either broken explicitly by
1166 *	closing the fd of the lock file, or it expires automatically at process
1167 *	termination.
1168 * 	returns fd of the lock file or -1 on error.
1169 */
1170int
1171_da_lock_devdb(char *rootdir)
1172{
1173	int		lockfd = -1;
1174	int		ret;
1175	int		count = 0;
1176	int		retry = 10;
1177	int		retry_sleep;
1178	uint_t		seed;
1179	char		*lockfile;
1180	char		path[MAXPATHLEN];
1181	int		size = sizeof (path);
1182
1183	if (rootdir == NULL) {
1184		lockfile = DA_DB_LOCK;
1185	} else {
1186		path[0] = '\0';
1187		if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
1188			return (-1);
1189		lockfile = path;
1190	}
1191
1192	if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
1193		/* cannot open lock file */
1194		return (-1);
1195
1196	(void) fchown(lockfd, DA_UID, DA_GID);
1197
1198	if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
1199		/* cannot position lock file */
1200		(void) close(lockfd);
1201		return (-1);
1202	}
1203	errno = 0;
1204	while (retry > 0) {
1205		count++;
1206		seed = (uint_t)gethrtime();
1207		ret = lockf(lockfd, F_TLOCK, 0);
1208		if (ret == 0) {
1209			(void) utime(lockfile, NULL);
1210			return (lockfd);
1211		}
1212		if ((errno != EACCES) && (errno != EAGAIN)) {
1213			/* cannot set lock */
1214			(void) close(lockfd);
1215			return (-1);
1216		}
1217		retry--;
1218		retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
1219		(void) sleep(retry_sleep);
1220		errno = 0;
1221	}
1222
1223	return (-1);
1224}
1225
1226/*
1227 * da_open_devdb -
1228 *	opens one or both database files - device_allocate, device_maps - in
1229 *	the specified mode.
1230 *	locks the database files; lock is either broken explicitly by the
1231 *	caller by closing the lock file fd, or it expires automatically at
1232 *	process termination.
1233 *	writes the file pointer of opened file in the input args - dafp, dmfp.
1234 *	returns fd of the lock file on success, -2 if database file does not
1235 *	exist, -1 on other errors.
1236 */
1237int
1238da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
1239{
1240	int	oflag = 0;
1241	int	fda = -1;
1242	int	fdm = -1;
1243	int	lockfd = -1;
1244	char	*fname;
1245	char	*fmode;
1246	char	path[MAXPATHLEN];
1247	FILE	*devfile;
1248
1249	if ((dafp == NULL) && (dmfp == NULL))
1250		return (-1);
1251
1252	if (flag & DA_RDWR) {
1253		oflag = DA_RDWR;
1254		fmode = "r+F";
1255	} else if (flag & DA_RDONLY) {
1256		oflag = DA_RDONLY;
1257		fmode = "rF";
1258	}
1259
1260	if ((lockfd = _da_lock_devdb(rootdir)) == -1)
1261		return (-1);
1262
1263	if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
1264		goto dmap_only;
1265
1266	path[0] = '\0';
1267
1268	/*
1269	 * open the device allocation file
1270	 */
1271	if (rootdir == NULL) {
1272		fname = DEVALLOC;
1273	} else {
1274		if (snprintf(path, sizeof (path), "%s%s", rootdir,
1275		    DEVALLOC) >= sizeof (path)) {
1276			if (lockfd != -1)
1277				(void) close(lockfd);
1278			return (-1);
1279		}
1280		fname = path;
1281	}
1282	if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
1283		if (lockfd != -1)
1284			(void) close(lockfd);
1285		return ((errno == ENOENT) ? -2 : -1);
1286	}
1287	if ((devfile = fdopen(fda, fmode)) == NULL) {
1288		(void) close(fda);
1289		if (lockfd != -1)
1290			(void) close(lockfd);
1291		return (-1);
1292	}
1293	*dafp = devfile;
1294	(void) fchmod(fda, DA_DBMODE);
1295
1296	if ((flag & DA_ALLOC_ONLY))
1297		goto out;
1298
1299dmap_only:
1300	path[0] = '\0';
1301	/*
1302	 * open the device map file
1303	 */
1304	if (rootdir == NULL) {
1305		fname = DEVMAP;
1306	} else {
1307		if (snprintf(path, sizeof (path), "%s%s", rootdir,
1308		    DEVMAP) >= sizeof (path)) {
1309			(void) close(fda);
1310			if (lockfd != -1)
1311				(void) close(lockfd);
1312			return (-1);
1313		}
1314		fname = path;
1315	}
1316
1317	if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
1318		if (lockfd != -1)
1319			(void) close(lockfd);
1320		return ((errno == ENOENT) ? -2 : -1);
1321	}
1322
1323	if ((devfile = fdopen(fdm, fmode)) == NULL) {
1324		(void) close(fdm);
1325		(void) close(fda);
1326		if (lockfd != -1)
1327			(void) close(lockfd);
1328		return (-1);
1329	}
1330	*dmfp = devfile;
1331	(void) fchmod(fdm, DA_DBMODE);
1332
1333out:
1334	return (lockfd);
1335}
1336
1337/*
1338 * _record_on_off -
1339 *	adds either DA_ON_STR or DA_OFF_STR to device_allocate
1340 *	returns 0 on success, -1 on error.
1341 */
1342static int
1343_record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
1344{
1345	int		dafd;
1346	int		nsize;
1347	int		nitems = 1;
1348	int		actionlen;
1349	int		str_found = 0;
1350	int		len = 0, nlen = 0, plen = 0;
1351	char		*ptr = NULL;
1352	char		*actionstr;
1353	char		*nbuf = NULL;
1354	char		line[MAX_CANON];
1355	struct stat	dastat;
1356
1357	if (dargs->optflag & DA_ON)
1358		actionstr = DA_ON_STR;
1359	else
1360		actionstr = DA_OFF_STR;
1361	actionlen = strlen(actionstr);
1362	dafd = fileno(dafp);
1363	if (fstat(dafd, &dastat) == -1)
1364		return (-1);
1365
1366	/* check the old device_allocate for on/off string */
1367	ptr = fgets(line, MAX_CANON, dafp);
1368	if (ptr != NULL) {
1369		if ((strcmp(line, DA_ON_STR) == 0) ||
1370		    (strcmp(line, DA_OFF_STR) == 0)) {
1371			str_found = 1;
1372			nsize = dastat.st_size;
1373		}
1374	}
1375	if (!ptr || !str_found) {
1376		/*
1377		 * the file never had either the on or the off string;
1378		 * make room for it.
1379		 */
1380		str_found = 0;
1381		nsize = dastat.st_size + actionlen + 1;
1382	}
1383	if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
1384		return (-1);
1385	nbuf[0] = '\0';
1386	/* put the on/off string */
1387	(void) strcpy(nbuf, actionstr);
1388	nlen = strlen(nbuf);
1389	plen = nlen;
1390	if (ptr && !str_found) {
1391		/* now put the first line that we read in fgets */
1392		nlen = plen + strlen(line) + 1;
1393		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1394		if (len >= nsize) {
1395			free(nbuf);
1396			return (-1);
1397		}
1398		plen += len;
1399	}
1400
1401	/* now get the rest of the old file */
1402	while (fgets(line, MAX_CANON, dafp) != NULL) {
1403		nlen = plen + strlen(line) + 1;
1404		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1405		if (len >= nsize) {
1406			free(nbuf);
1407			return (-1);
1408		}
1409		plen += len;
1410	}
1411	len = strlen(nbuf) + 1;
1412	if (len < nsize)
1413		nbuf[len] = '\n';
1414
1415	/* write the on/off str + the old device_allocate to the temp file */
1416	if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
1417		free(nbuf);
1418		return (-1);
1419	}
1420
1421	free(nbuf);
1422
1423	return (0);
1424}
1425
1426/*
1427 * da_update_defattrs -
1428 *	writes default attributes to devalloc_defaults
1429 *	returns 0 on success, -1 on error.
1430 */
1431int
1432da_update_defattrs(da_args *dargs)
1433{
1434	int		rc = 0, lockfd = 0, tmpfd = 0;
1435	char		*defpath = DEFATTRS;
1436	char		*tmpdefpath = TMPATTRS;
1437	FILE		*tmpfp = NULL;
1438	struct stat	dstat;
1439	strentry_t	*head_defent = NULL;
1440
1441	if (dargs == NULL)
1442		return (0);
1443	if ((lockfd = _da_lock_devdb(NULL)) == -1)
1444		return (-1);
1445	if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1446		(void) close(lockfd);
1447		return (-1);
1448	}
1449	(void) fchown(tmpfd, DA_UID, DA_GID);
1450	if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
1451		(void) close(tmpfd);
1452		(void) unlink(tmpdefpath);
1453		(void) close(lockfd);
1454		return (-1);
1455	}
1456	/*
1457	 * examine all entries, remove an old one if required, check
1458	 * if a new one needs to be added.
1459	 */
1460	if (stat(defpath, &dstat) == 0) {
1461		if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
1462			if (rc == 1) {
1463				(void) close(tmpfd);
1464				(void) unlink(tmpdefpath);
1465				(void) close(lockfd);
1466				return (rc);
1467			}
1468		}
1469	}
1470	/*
1471	 * write back any existing entries.
1472	 */
1473	_write_defattrs(tmpfp, head_defent);
1474
1475	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1476		/* add new entries */
1477		rc = _write_new_defattrs(tmpfp, dargs);
1478		(void) fclose(tmpfp);
1479	} else {
1480		(void) fclose(tmpfp);
1481	}
1482	if (rename(tmpdefpath, defpath) != 0) {
1483		rc = -1;
1484		(void) unlink(tmpdefpath);
1485	}
1486	(void) close(lockfd);
1487
1488	return (rc);
1489}
1490
1491/*
1492 * da_update_device -
1493 *	Writes existing entries and the SINGLE change requested by da_args,
1494 *	to device_allocate and device_maps.
1495 *	Returns 0 on success, -1 on error.
1496 */
1497int
1498da_update_device(da_args *dargs)
1499{
1500	int		rc;
1501	int		tafd = -1, tmfd = -1;
1502	int		lockfd = -1;
1503	char		*rootdir = NULL;
1504	char		*apathp = NULL, *mpathp = NULL;
1505	char		*dapathp = NULL, *dmpathp = NULL;
1506	char		apath[MAXPATHLEN], mpath[MAXPATHLEN];
1507	char		dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
1508	FILE		*tafp = NULL, *tmfp = NULL, *dafp = NULL;
1509	struct stat	dastat;
1510	devinfo_t	*devinfo;
1511	strentry_t	*head_devmapp = NULL;
1512	strentry_t	*head_devallocp = NULL;
1513
1514	if (dargs == NULL)
1515		return (0);
1516
1517	rootdir = dargs->rootdir;
1518	devinfo = dargs->devinfo;
1519
1520	/*
1521	 * adding/removing entries should be done in both
1522	 * device_allocate and device_maps. updates can be
1523	 * done in both or either of the files.
1524	 */
1525	if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
1526		if (dargs->optflag & DA_ALLOC_ONLY ||
1527		    dargs->optflag & DA_MAPS_ONLY)
1528			return (0);
1529	}
1530
1531	/*
1532	 * name, type and list are required fields for adding a new
1533	 * device.
1534	 */
1535	if ((dargs->optflag & DA_ADD) &&
1536	    ((devinfo->devname == NULL) ||
1537	    (devinfo->devtype == NULL) ||
1538	    (devinfo->devlist == NULL))) {
1539		return (-1);
1540	}
1541
1542	if (rootdir != NULL) {
1543		if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
1544		    TMPALLOC) >= sizeof (apath))
1545			return (-1);
1546		apathp = apath;
1547		if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
1548		    DEVALLOC) >= sizeof (dapath))
1549			return (-1);
1550		dapathp = dapath;
1551		if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1552			if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
1553			    TMPMAP) >= sizeof (mpath))
1554				return (-1);
1555			mpathp = mpath;
1556			if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
1557			    DEVMAP) >= sizeof (dmpath))
1558				return (-1);
1559			dmpathp = dmpath;
1560		}
1561	} else {
1562		apathp = TMPALLOC;
1563		dapathp = DEVALLOC;
1564		mpathp = TMPMAP;
1565		dmpathp = DEVMAP;
1566	}
1567
1568	if (dargs->optflag & DA_MAPS_ONLY)
1569		goto dmap_only;
1570
1571	/*
1572	 * Check if we are here just to record on/off status of
1573	 * device_allocation.
1574	 */
1575	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
1576		lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
1577		    DA_RDONLY|DA_ALLOC_ONLY);
1578	else
1579		lockfd = _da_lock_devdb(rootdir);
1580	if (lockfd == -1)
1581		return (-1);
1582
1583	if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1584		(void) close(lockfd);
1585		(void) fclose(dafp);
1586		return (-1);
1587	}
1588	(void) fchown(tafd, DA_UID, DA_GID);
1589	if ((tafp = fdopen(tafd, "r+")) == NULL) {
1590		(void) close(tafd);
1591		(void) unlink(apathp);
1592		(void) fclose(dafp);
1593		(void) close(lockfd);
1594		return (-1);
1595	}
1596
1597	/*
1598	 * We don't need to parse the file if we are here just to record
1599	 * on/off status of device_allocation.
1600	 */
1601	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
1602		if (_record_on_off(dargs, tafp, dafp) == -1) {
1603			(void) close(tafd);
1604			(void) unlink(apathp);
1605			(void) fclose(dafp);
1606			(void) close(lockfd);
1607			return (-1);
1608		}
1609		(void) fclose(dafp);
1610		goto out;
1611	}
1612
1613	/*
1614	 * If reacting to a hotplug, read the file entries,
1615	 * figure out what dname (tname + a new number) goes to the
1616	 * device being added/removed, and create a good head_devallocp and
1617	 * head_devmapp with everything good still in it (_rebuild_lists)
1618	 *
1619	 * Else examine all the entries, remove an old one if it is
1620	 * a duplicate with a device being added, returning the
1621	 * remaining list (_build_lists.)
1622	 *
1623	 * We need to do this only if the file exists already.
1624	 *
1625	 * Once we have built these lists, we need to free the strings
1626	 * in the head_* arrays before returning.
1627	 */
1628	if (stat(dapathp, &dastat) == 0) {
1629		/* for device allocation, the /etc files are the "master" */
1630		if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
1631		    (!(dargs->optflag & DA_FORCE)))
1632			rc = _rebuild_lists(dargs, &head_devallocp,
1633			    &head_devmapp);
1634		else
1635			rc = _build_lists(dargs, &head_devallocp,
1636			    &head_devmapp);
1637
1638		if (rc != 0 && rc != 1) {
1639			(void) close(tafd);
1640			(void) unlink(apathp);
1641			(void) close(lockfd);
1642			return (-1);
1643		}
1644	} else
1645		rc = 0;
1646
1647	if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
1648		(void) close(tafd);
1649		(void) unlink(apathp);
1650		(void) close(lockfd);
1651		return (0);
1652	}
1653	/*
1654	 * TODO: clean up the workings of DA_UPDATE.
1655	 * Due to da_match looking at fields that are missing
1656	 * in dargs for DA_UPDATE, the da_match call returns no match,
1657	 * but due to the way _da2str combines the devalloc_t info with
1658	 * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
1659	 *
1660	 * This would not scale if any type of update was ever needed
1661	 * from the daemon.
1662	 */
1663
1664	/*
1665	 * Write out devallocp along with the devalloc on/off string.
1666	 */
1667	_write_device_allocate(dapathp, tafp, head_devallocp);
1668
1669	if (dargs->optflag & DA_ALLOC_ONLY)
1670		goto out;
1671
1672dmap_only:
1673	if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1674		(void) close(tafd);
1675		(void) unlink(apathp);
1676		(void) close(lockfd);
1677		return (-1);
1678	}
1679	(void) fchown(tmfd, DA_UID, DA_GID);
1680	if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
1681		(void) close(tafd);
1682		(void) unlink(apathp);
1683		(void) close(tmfd);
1684		(void) unlink(mpathp);
1685		(void) close(lockfd);
1686		return (-1);
1687	}
1688
1689	/*
1690	 * Write back any non-removed pre-existing entries.
1691	 */
1692	if (head_devmapp != NULL)
1693		_write_device_maps(tmfp, head_devmapp);
1694
1695out:
1696	/*
1697	 * Add any new entries here.
1698	 */
1699	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1700		/* add any new entries */
1701		rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
1702		(void) fclose(tafp);
1703
1704		if (rc == 0)
1705			rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
1706		(void) fclose(tmfp);
1707	} else {
1708		if (tafp)
1709			(void) fclose(tafp);
1710		if (tmfp)
1711			(void) fclose(tmfp);
1712	}
1713
1714	rc = 0;
1715	if (!(dargs->optflag & DA_MAPS_ONLY)) {
1716		if (rename(apathp, dapathp) != 0) {
1717			rc = -1;
1718			(void) unlink(apathp);
1719		}
1720	}
1721	if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1722		if (rename(mpathp, dmpathp) != 0) {
1723			rc = -1;
1724			(void) unlink(mpathp);
1725		}
1726	}
1727
1728	(void) close(lockfd);
1729
1730	return (rc);
1731}
1732
1733/*
1734 * da_add_list -
1735 *	adds new /dev link name to the linked list of devices.
1736 *	returns 0 if link added successfully, -1 on error.
1737 */
1738int
1739da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
1740{
1741	int		instance;
1742	int		nlen, plen;
1743	int		new_entry = 0;
1744	char		*dtype, *dexec, *tname, *kval;
1745	char		*minstr = NULL, *maxstr = NULL;
1746	char		dname[DA_MAXNAME + 1];
1747	kva_t		*kva;
1748	deventry_t	*dentry = NULL, *nentry = NULL, *pentry = NULL;
1749	da_defs_t	*da_defs;
1750
1751	if (dlist == NULL || link == NULL)
1752		return (-1);
1753
1754	dname[0] = '\0';
1755	if (flag & DA_AUDIO) {
1756		dentry = dlist->audio;
1757		tname = DA_AUDIO_NAME;
1758		dtype = DA_AUDIO_TYPE;
1759		dexec = DA_DEFAULT_AUDIO_CLEAN;
1760	} else if (flag & DA_CD) {
1761		dentry = dlist->cd;
1762		tname = DA_CD_NAME;
1763		dtype = DA_CD_TYPE;
1764		dexec = DA_DEFAULT_DISK_CLEAN;
1765	} else if (flag & DA_FLOPPY) {
1766		dentry = dlist->floppy;
1767		tname = DA_FLOPPY_NAME;
1768		dtype = DA_FLOPPY_TYPE;
1769		dexec = DA_DEFAULT_DISK_CLEAN;
1770	} else if (flag & DA_TAPE) {
1771		dentry = dlist->tape;
1772		tname = DA_TAPE_NAME;
1773		dtype = DA_TAPE_TYPE;
1774		dexec = DA_DEFAULT_TAPE_CLEAN;
1775	} else if (flag & DA_RMDISK) {
1776		dentry = dlist->rmdisk;
1777		tname = DA_RMDISK_NAME;
1778		dtype = DA_RMDISK_TYPE;
1779		dexec = DA_DEFAULT_DISK_CLEAN;
1780	} else {
1781		return (-1);
1782	}
1783
1784	for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
1785		pentry = nentry;
1786		(void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
1787		if (nentry->devinfo.instance == new_instance)
1788			/*
1789			 * Add the new link name to the list of links
1790			 * that the device 'dname' has.
1791			 */
1792			break;
1793	}
1794
1795	if (nentry == NULL) {
1796		/*
1797		 * Either this is the first entry ever, or no matching entry
1798		 * was found. Create a new one and add to the list.
1799		 */
1800		if (dentry == NULL)		/* first entry ever */
1801			instance = 0;
1802		else				/* no matching entry */
1803			instance++;
1804		(void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
1805		if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
1806		    NULL)
1807			return (-1);
1808		if (pentry != NULL)
1809			pentry->next = nentry;
1810		new_entry = 1;
1811		nentry->devinfo.devname = strdup(dname);
1812		nentry->devinfo.devtype = dtype;
1813		nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
1814		nentry->devinfo.devexec = dexec;
1815		nentry->devinfo.instance = new_instance;
1816		/*
1817		 * Look for default label range, authorizations and cleaning
1818		 * program in devalloc_defaults. If label range is not
1819		 * specified in devalloc_defaults, assume it to be admin_low
1820		 * to admin_high.
1821		 */
1822		minstr = DA_DEFAULT_MIN;
1823		maxstr = DA_DEFAULT_MAX;
1824		setdadefent();
1825		if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
1826			kva = da_defs->devopts;
1827			if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
1828				minstr = strdup(kval);
1829			if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
1830				maxstr = strdup(kval);
1831			if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
1832				nentry->devinfo.devauths = strdup(kval);
1833			if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
1834				nentry->devinfo.devexec = strdup(kval);
1835			freedadefent(da_defs);
1836		}
1837		enddadefent();
1838		kval = NULL;
1839		nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
1840		    strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
1841		    strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
1842		    + 1;			/* +1 for terminator */
1843		if (kval = (char *)malloc(nlen))
1844			(void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
1845			    DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
1846			    DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
1847		nentry->devinfo.devopts = kval;
1848
1849		nentry->devinfo.devlist = NULL;
1850		nentry->next = NULL;
1851	}
1852
1853	nlen = strlen(link) + 1;		/* +1 terminator */
1854	if (nentry->devinfo.devlist) {
1855		plen = strlen(nentry->devinfo.devlist);
1856		nlen = nlen + plen + 1;	/* +1 for blank to separate entries */
1857	} else {
1858		plen = 0;
1859	}
1860
1861	if ((nentry->devinfo.devlist =
1862	    (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
1863		if (new_entry) {
1864			free(nentry->devinfo.devname);
1865			free(nentry);
1866			if (pentry != NULL)
1867				pentry->next = NULL;
1868		}
1869		return (-1);
1870	}
1871
1872	if (plen == 0)
1873		(void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
1874	else
1875		(void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
1876		    " %s", link);
1877
1878	if (pentry == NULL) {
1879		/*
1880		 * This is the first entry of this device type.
1881		 */
1882		if (flag & DA_AUDIO)
1883			dlist->audio = nentry;
1884		else if (flag & DA_CD)
1885			dlist->cd = nentry;
1886		else if (flag & DA_FLOPPY)
1887			dlist->floppy = nentry;
1888		else if (flag & DA_TAPE)
1889			dlist->tape = nentry;
1890		else if (flag & DA_RMDISK)
1891			dlist->rmdisk = nentry;
1892	}
1893
1894	return (0);
1895}
1896
1897/*
1898 * da_remove_list -
1899 *	removes a /dev link name from the linked list of devices.
1900 *	returns type of device if link for that device removed
1901 *	successfully, else returns -1 on error.
1902 *	if all links for a device are removed, stores that device
1903 *	name in devname.
1904 */
1905int
1906da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
1907{
1908	int		flag;
1909	int		remove_dev = 0;
1910	int		nlen, plen, slen;
1911	char		*lasts, *lname, *oldlist;
1912	struct stat	rmstat;
1913	deventry_t	*dentry, *current, *prev;
1914
1915	if (type != 0)
1916		flag = type;
1917	else if (link == NULL)
1918		return (-1);
1919	else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
1920		flag = DA_AUDIO;
1921	else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
1922	    strstr(link, "sr") || strstr(link, "rsr"))
1923		flag = DA_CD;
1924	else if (strstr(link, "fd") || strstr(link, "rfd") ||
1925	    strstr(link, "diskette") || strstr(link, "rdiskette"))
1926		flag = DA_FLOPPY;
1927	else if (strstr(link, DA_TAPE_NAME))
1928		flag = DA_TAPE;
1929	else
1930		flag = DA_RMDISK;
1931
1932	switch (type) {
1933	case DA_AUDIO:
1934		dentry = dlist->audio;
1935		break;
1936	case DA_CD:
1937		dentry = dlist->cd;
1938		break;
1939	case DA_FLOPPY:
1940		dentry = dlist->floppy;
1941		break;
1942	case DA_TAPE:
1943		dentry = dlist->tape;
1944		break;
1945	case DA_RMDISK:
1946		dentry = dlist->rmdisk;
1947		break;
1948	default:
1949		return (-1);
1950	}
1951
1952	if ((type != 0) && (link == NULL)) {
1953		for (current = dentry, prev = dentry; current != NULL;
1954		    current = current->next) {
1955			oldlist = strdup(current->devinfo.devlist);
1956			for (lname = strtok_r(oldlist, " ", &lasts);
1957			    lname != NULL;
1958			    lname = strtok_r(NULL, " ", &lasts)) {
1959				if (stat(lname, &rmstat) != 0) {
1960					remove_dev = 1;
1961					goto remove_dev;
1962				}
1963			}
1964			prev = current;
1965		}
1966		return (-1);
1967	}
1968
1969	for (current = dentry, prev = dentry; current != NULL;
1970	    current = current->next) {
1971		plen = strlen(current->devinfo.devlist);
1972		nlen = strlen(link);
1973		if (plen == nlen) {
1974			if (strcmp(current->devinfo.devlist, link) == 0) {
1975				/* last name in the list */
1976				remove_dev = 1;
1977				break;
1978			}
1979		}
1980		if (strstr(current->devinfo.devlist, link)) {
1981			nlen = plen - nlen + 1;
1982			oldlist = strdup(current->devinfo.devlist);
1983			if ((current->devinfo.devlist =
1984			    (char *)realloc(current->devinfo.devlist,
1985			    nlen)) == NULL) {
1986				free(oldlist);
1987				return (-1);
1988			}
1989			current->devinfo.devlist[0] = '\0';
1990			nlen = plen = slen = 0;
1991			for (lname = strtok_r(oldlist, " ", &lasts);
1992			    lname != NULL;
1993			    lname = strtok_r(NULL, " ", &lasts)) {
1994				if (strcmp(lname, link) == 0)
1995					continue;
1996				nlen = strlen(lname) + plen + 1;
1997				if (plen == 0) {
1998					slen =
1999					    snprintf(current->devinfo.devlist,
2000					    nlen, "%s", lname);
2001				} else {
2002					slen =
2003					    snprintf(current->devinfo.devlist +
2004					    plen, nlen - plen, " %s", lname);
2005				}
2006				plen = plen + slen + 1;
2007			}
2008			free(oldlist);
2009			break;
2010		}
2011		prev = current;
2012	}
2013
2014remove_dev:
2015	if (remove_dev == 1) {
2016		(void) strlcpy(devname, current->devinfo.devname, size);
2017		free(current->devinfo.devname);
2018		free(current->devinfo.devlist);
2019		current->devinfo.devname = current->devinfo.devlist = NULL;
2020		prev->next = current->next;
2021		free(current);
2022		current = NULL;
2023	}
2024	if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
2025		if (prev->next) {
2026			/*
2027			 * what we removed above was the first entry
2028			 * in the list. make the next entry to be the
2029			 * first.
2030			 */
2031			current = prev->next;
2032		} else {
2033			/*
2034			 * the matching entry was the only entry in the list
2035			 * for this type.
2036			 */
2037			current = NULL;
2038		}
2039		if (flag & DA_AUDIO)
2040			dlist->audio = current;
2041		else if (flag & DA_CD)
2042			dlist->cd = current;
2043		else if (flag & DA_FLOPPY)
2044			dlist->floppy = current;
2045		else if (flag & DA_TAPE)
2046			dlist->tape = current;
2047		else if (flag & DA_RMDISK)
2048			dlist->rmdisk = current;
2049	}
2050
2051	return (flag);
2052}
2053
2054/*
2055 * da_rm_list_entry -
2056 *
2057 *	The adding of devnames to a devlist and the removal of a
2058 *	device are not symmetrical -- hot_cleanup gives a /devices
2059 *	name which is used to remove the dentry whose links all point to
2060 *	that /devices entry.
2061 *
2062 *	The link argument is present if available to make debugging
2063 *	easier.
2064 *
2065 *	da_rm_list_entry removes an entry from the linked list of devices.
2066 *
2067 *	Returns 1 if the devname was removed successfully,
2068 *	0 if not found, -1 for error.
2069 */
2070/*ARGSUSED*/
2071int
2072da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
2073{
2074	int		retval = 0;
2075	deventry_t	**dentry, *current, *prev;
2076
2077	switch (type) {
2078	case DA_AUDIO:
2079		dentry = &(dlist->audio);
2080		break;
2081	case DA_CD:
2082		dentry = &(dlist->cd);
2083		break;
2084	case DA_FLOPPY:
2085		dentry = &(dlist->floppy);
2086		break;
2087	case DA_TAPE:
2088		dentry = &(dlist->tape);
2089		break;
2090	case DA_RMDISK:
2091		dentry = &(dlist->rmdisk);
2092		break;
2093	default:
2094		return (-1);
2095	}
2096
2097	/* Presumably in daemon mode, no need to remove entry, list is empty */
2098	if (*dentry == (deventry_t *)NULL)
2099		return (0);
2100
2101	prev = NULL;
2102	for (current = *dentry; current != NULL;
2103	    prev = current, current = current->next) {
2104		if (strcmp(devname, current->devinfo.devname))
2105			continue;
2106		retval = 1;
2107		break;
2108	}
2109	if (retval == 0)
2110		return (0);
2111	free(current->devinfo.devname);
2112	if (current->devinfo.devlist != NULL)
2113		free(current->devinfo.devlist);
2114	if (current->devinfo.devopts != NULL)
2115		free(current->devinfo.devopts);
2116
2117	if (prev == NULL)
2118		*dentry = current->next;
2119	else
2120		prev->next = current->next;
2121
2122	free(current);
2123	return (retval);
2124}
2125
2126/*
2127 * da_is_on -
2128 *	checks if device allocation feature is turned on.
2129 *	returns 1 if on, 0 if off, -1 if status string not
2130 *	found in device_allocate.
2131 */
2132int
2133da_is_on()
2134{
2135	return (getdaon());
2136}
2137
2138/*
2139 * da_print_device -
2140 *	debug routine to print device entries.
2141 */
2142void
2143da_print_device(int flag, devlist_t *devlist)
2144{
2145	deventry_t	*entry, *dentry;
2146	devinfo_t	*devinfo;
2147
2148	if (flag & DA_AUDIO)
2149		dentry = devlist->audio;
2150	else if (flag & DA_CD)
2151		dentry = devlist->cd;
2152	else if (flag & DA_FLOPPY)
2153		dentry = devlist->floppy;
2154	else if (flag & DA_TAPE)
2155		dentry = devlist->tape;
2156	else if (flag & DA_RMDISK)
2157		dentry = devlist->rmdisk;
2158	else
2159		return;
2160
2161	for (entry = dentry; entry != NULL; entry = entry->next) {
2162		devinfo = &(entry->devinfo);
2163		(void) fprintf(stdout, "name: %s\n", devinfo->devname);
2164		(void) fprintf(stdout, "type: %s\n", devinfo->devtype);
2165		(void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
2166		(void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
2167		(void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
2168	}
2169}
2170