xref: /illumos-gate/usr/src/lib/libbsm/common/devalloc.c (revision 2e0fe3efe5f9d579d4e44b3532d8e342c68b40ca)
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 
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  * 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  */
573 int
574 da_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  */
621 static int
622 allocatable(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  */
655 static 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  */
897 static 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 
938 dmap_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  */
977 static 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  */
995 static 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  */
1032 static 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  */
1054 static 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  */
1093 static 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 
1146 dmap_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  */
1170 int
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  */
1237 int
1238 da_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 
1299 dmap_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 
1333 out:
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  */
1342 static 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  */
1431 int
1432 da_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  */
1497 int
1498 da_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 
1672 dmap_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 
1695 out:
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  */
1738 int
1739 da_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  */
1905 int
1906 da_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 != NULL)
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 != NULL) && (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 
2014 remove_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*/
2071 int
2072 da_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  */
2132 int
2133 da_is_on()
2134 {
2135 	return (getdaon());
2136 }
2137 
2138 /*
2139  * da_print_device -
2140  *	debug routine to print device entries.
2141  */
2142 void
2143 da_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