xref: /titanic_52/usr/src/lib/libbsm/common/devalloc.c (revision ad4023c40b055806dce2bde9ee9e87e5016b5135)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <utime.h>
37 #include <synch.h>
38 #include <strings.h>
39 #include <string.h>
40 #include <libintl.h>
41 #include <errno.h>
42 #include <auth_list.h>
43 #include <bsm/devices.h>
44 #include <bsm/devalloc.h>
45 
46 #define	DA_DEFS	"/etc/security/tsol/devalloc_defaults"
47 
48 extern int _readbufline(char *, int, char *, int, int *);
49 extern char *strtok_r(char *, const char *, char **);
50 extern char *_strtok_escape(char *, char *, char **);
51 extern int getdaon(void);
52 extern int da_matchname(devalloc_t *, char *);
53 extern int da_match(devalloc_t *, da_args *);
54 extern int dmap_matchname(devmap_t *, char *);
55 extern int dm_match(devmap_t *, da_args *);
56 
57 /*
58  * The following structure is for recording old entries to be retained.
59  * We read the entries from the database into a linked list in memory,
60  * then turn around and write them out again.
61  */
62 typedef struct strentry {
63 	struct strentry	*se_next;
64 	char		se_str[4096 + 1];
65 } strentry_t;
66 
67 /*
68  * da_check_longindevperm -
69  *	reads /etc/logindevperm and checks if specified device is in the file.
70  *	returns 1 if specified device found in /etc/logindevperm, else returns 0
71  */
72 int
73 da_check_logindevperm(char *devname)
74 {
75 	int		ret = 0;
76 	int		fd = -1;
77 	int		nlen, plen, slen, lineno, fsize;
78 	char		line[MAX_CANON];
79 	char		*field_delims = " \t\n";
80 	char		*fbuf = NULL;
81 	char		*ptr, *device;
82 	char		*lasts = NULL;
83 	FILE		*fp;
84 	struct stat	f_stat;
85 
86 	/*
87 	 * check if /etc/logindevperm exists and get its size
88 	 */
89 	if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1)
90 		return (0);
91 	if (fstat(fd, &f_stat) != 0) {
92 		(void) close(fd);
93 		return (0);
94 	}
95 	fsize = f_stat.st_size;
96 	if ((fbuf = (char *)malloc(fsize)) == NULL) {
97 		(void) close(fd);
98 		return (0);
99 	}
100 	if ((fp = fdopen(fd, "r")) == NULL) {
101 		free(fbuf);
102 		(void) close(fd);
103 		return (0);
104 	}
105 
106 	/*
107 	 * read and parse /etc/logindevperm
108 	 */
109 	plen = nlen = lineno = 0;
110 	while (fgets(line, MAX_CANON, fp) != NULL) {
111 		lineno++;
112 		if ((ptr = strchr(line, '#')) != NULL)
113 			*ptr = '\0';	/* handle comments */
114 		if (strtok_r(line, field_delims, &lasts) == NULL)
115 			continue;	/* ignore blank lines */
116 		if (strtok_r(NULL, field_delims, &lasts) == NULL)
117 			/* invalid entry */
118 			continue;
119 		if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
120 			/* empty device list */
121 			continue;
122 		nlen = strlen(ptr) + 1;		/* +1 terminator */
123 		nlen += (plen + 1);
124 		if (plen == 0)
125 			slen = snprintf(fbuf, nlen, "%s", ptr);
126 		else
127 			slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr);
128 		if (slen >= fsize) {
129 			fbuf[0] = '\0';
130 			(void) fclose(fp);
131 			return (slen);
132 		}
133 		plen += slen;
134 	}
135 	(void) fclose(fp);
136 
137 	/*
138 	 * check if devname exists in /etc/logindevperm
139 	 */
140 	device = strtok_r(fbuf, ":", &lasts);
141 	while (device != NULL) {
142 		/*
143 		 * device and devname may be one of these types -
144 		 *    /dev/xx
145 		 *    /dev/xx*
146 		 *    /dev/dir/xx
147 		 *    /dev/dir/xx*
148 		 *    /dev/dir/"*"
149 		 */
150 		if (strcmp(device, devname) == 0) {
151 			/* /dev/xx, /dev/dir/xx */
152 			free(fbuf);
153 			return (1);
154 		}
155 		if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
156 			/* all wildcard types */
157 			*ptr = '\0';
158 			if (strncmp(device, devname, strlen(device)) == 0) {
159 				free(fbuf);
160 				return (1);
161 			}
162 		}
163 		device = strtok_r(NULL, ":", &lasts);
164 	}
165 
166 	return (ret);
167 }
168 
169 /*
170  * _da_read_file -
171  *	establishes readers/writer lock on fname; reads in the file if its
172  *	contents changed since the last time we read it.
173  *	returns size of buffer read, or -1 on failure.
174  */
175 int
176 _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock,
177     int flag)
178 {
179 	int		fd = -1;
180 	int		fsize = 0;
181 	time_t		newtime;
182 	FILE		*fp = NULL;
183 	struct stat	f_stat;
184 
185 	if (flag & DA_FORCE)
186 		*ftime = 0;
187 
188 	/* check the size and the time stamp on the file */
189 	if (rw_rdlock(flock) != 0)
190 		return (-1);
191 	if (stat(fname, &f_stat) != 0) {
192 		(void) rw_unlock(flock);
193 		return (-1);
194 	}
195 	fsize = f_stat.st_size;
196 	newtime = f_stat.st_mtime;
197 	(void) rw_unlock(flock);
198 
199 	while (newtime > *ftime) {
200 		/*
201 		 * file has been modified since we last read it; or this
202 		 * is a forced read.
203 		 * read file into the buffer with rw lock.
204 		 */
205 		if (rw_wrlock(flock) != 0)
206 			return (-1);
207 		if ((fp = fopen(fname, "r")) == NULL) {
208 			(void) rw_unlock(flock);
209 			return (-1);
210 		}
211 		fd = fileno(fp);
212 		if (*fbuf != NULL) {
213 			free(*fbuf);
214 			*fbuf = NULL;
215 		}
216 		if ((*fbuf = malloc(fsize)) == NULL) {
217 			(void) rw_unlock(flock);
218 			(void) close(fd);
219 			return (-1);
220 		}
221 		if (read(fd, *fbuf, fsize) < fsize) {
222 			free(*fbuf);
223 			(void) rw_unlock(flock);
224 			(void) close(fd);
225 			return (-1);
226 		}
227 		(void) rw_unlock(flock);
228 		/*
229 		 * verify that the file did not change just after we read it.
230 		 */
231 		if (rw_rdlock(flock) != 0) {
232 			free(*fbuf);
233 			(void) close(fd);
234 			return (-1);
235 		}
236 		if (stat(fname, &f_stat) != 0) {
237 			free(*fbuf);
238 			(void) rw_unlock(flock);
239 			(void) close(fd);
240 			return (-1);
241 		}
242 		fsize = f_stat.st_size;
243 		newtime = f_stat.st_mtime;
244 		(void) rw_unlock(flock);
245 		(void) close(fd);
246 		*ftime = newtime;
247 	}
248 
249 	return (fsize);
250 }
251 
252 /*
253  * _update_zonename -
254  *	add/remove current zone's name to the given devalloc_t.
255  */
256 void
257 _update_zonename(da_args *dargs, devalloc_t *dap)
258 {
259 	int		i, j;
260 	int		oldsize, newsize;
261 	int		has_zonename = 0;
262 	char		*zonename;
263 	kva_t		*newkva, *oldkva;
264 	kv_t		*newdata, *olddata;
265 	devinfo_t	*devinfo;
266 
267 	devinfo = dargs->devinfo;
268 	oldkva = dap->da_devopts;
269 	if (oldkva == NULL) {
270 		if (dargs->optflag & DA_REMOVE_ZONE)
271 			return;
272 		if (dargs->optflag & DA_ADD_ZONE) {
273 			newkva = _str2kva(devinfo->devopts, KV_ASSIGN,
274 			    KV_TOKEN_DELIMIT);
275 			if (newkva != NULL)
276 				dap->da_devopts = newkva;
277 			return;
278 		}
279 	}
280 	newsize = oldsize = oldkva->length;
281 	if (kva_match(oldkva, DAOPT_ZONE))
282 		has_zonename = 1;
283 	if (dargs->optflag & DA_ADD_ZONE) {
284 		if ((zonename = index(devinfo->devopts, '=')) == NULL)
285 			return;
286 		zonename++;
287 		if (has_zonename) {
288 			(void) _insert2kva(oldkva, DAOPT_ZONE, zonename);
289 			return;
290 		}
291 		newsize += 1;
292 	} else if (dargs->optflag & DA_REMOVE_ZONE) {
293 		if (has_zonename) {
294 			newsize -= 1;
295 			if (newsize == 0) {
296 				/*
297 				 * If zone name was the only key/value pair,
298 				 * put 'reserved' in the empty slot.
299 				 */
300 				_kva_free(oldkva);
301 				dap->da_devopts = NULL;
302 				return;
303 			}
304 		} else {
305 			return;
306 		}
307 	}
308 	newkva = _new_kva(newsize);
309 	newkva->length = 0;
310 	newdata = newkva->data;
311 	olddata = oldkva->data;
312 	for (i = 0, j = 0; i < oldsize; i++) {
313 		if ((dargs->optflag & DA_REMOVE_ZONE) &&
314 		    (strcmp(olddata[i].key, DAOPT_ZONE) == 0))
315 			continue;
316 		newdata[j].key = strdup(olddata[i].key);
317 		newdata[j].value = strdup(olddata[i].value);
318 		newkva->length++;
319 		j++;
320 	}
321 	if (dargs->optflag & DA_ADD_ZONE) {
322 		newdata[j].key = strdup(DAOPT_ZONE);
323 		newdata[j].value = strdup(zonename);
324 		newkva->length++;
325 	}
326 	_kva_free(oldkva);
327 	dap->da_devopts = newkva;
328 }
329 
330 /*
331  * _dmap2str -
332  *	converts a device_map entry into a printable string
333  *	returns 0 on success, -1 on error.
334  */
335 /*ARGSUSED*/
336 static int
337 _dmap2str(da_args *dargs, devmap_t *dmp, char *buf, int size, const char *sep)
338 {
339 	int	length;
340 
341 	length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep);
342 	if (length >= size)
343 		return (-1);
344 	length += snprintf(buf + length, size - length, "%s%s",
345 	    dmp->dmap_devtype, sep);
346 	if (length >= size)
347 		return (-1);
348 	length += snprintf(buf + length, size - length, "%s\n",
349 	    dmp->dmap_devlist);
350 	if (length >= size)
351 		return (-1);
352 	return (0);
353 }
354 
355 /*
356  * _dmap2strentry -
357  *	calls dmap2str to break given devmap_t into printable entry.
358  *	returns pointer to decoded entry, NULL on error.
359  */
360 static strentry_t *
361 _dmap2strentry(da_args *dargs, devmap_t *devmapp)
362 {
363 	strentry_t	*sep;
364 
365 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
366 		return (NULL);
367 	if (_dmap2str(dargs, devmapp, sep->se_str, sizeof (sep->se_str),
368 	    KV_TOKEN_DELIMIT"\\\n\t") != 0) {
369 		free(sep);
370 		return (NULL);
371 	}
372 	return (sep);
373 }
374 
375 /*
376  * fix_optstr -
377  * 	removes trailing ':' from buf.
378  */
379 void
380 fix_optstr(char *buf)
381 {
382 	char	*p = NULL;
383 
384 	if (p = rindex(buf, ':'))
385 		*p = ';';
386 }
387 
388 /*
389  * _da2str -
390  *	converts a device_allocate entry into a printable string
391  *	returns 0 on success, -1 on error.
392  */
393 static int
394 _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep,
395     const char *osep)
396 {
397 	int	length;
398 	int	matching_entry = 0;
399 	char	**dnames;
400 
401 	if (dargs->optflag & DA_UPDATE &&
402 	    (dargs->optflag & DA_ADD_ZONE ||
403 	    dargs->optflag & DA_REMOVE_ZONE) &&
404 	    dargs->devnames) {
405 		for (dnames = dargs->devnames; *dnames != NULL; dnames++) {
406 			if (da_matchname(dap, *dnames)) {
407 				matching_entry = 1;
408 				break;
409 			}
410 		}
411 	}
412 	length = snprintf(buf, size, "%s%s", dap->da_devname, sep);
413 	if (length >= size)
414 		return (-1);
415 	length += snprintf(buf + length, size - length, "%s%s",
416 	    dap->da_devtype, sep);
417 	if (length >= size)
418 		return (-1);
419 	if (matching_entry)
420 		_update_zonename(dargs, dap);
421 	if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) &&
422 	    (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) {
423 		length += snprintf(buf + length, size - length, "%s%s",
424 		    DA_RESERVED, sep);
425 	} else {
426 		if (_kva2str(dap->da_devopts, buf + length, size - length,
427 		    KV_ASSIGN, (char *)osep) != 0)
428 			return (-1);
429 		length = strlen(buf);
430 	}
431 	if (dap->da_devopts)
432 		fix_optstr(buf);
433 	if (length >= size)
434 		return (-1);
435 	length += snprintf(buf + length, size - length, "%s%s",
436 	    DA_RESERVED, sep);
437 	if (length >= size)
438 		return (-1);
439 	length += snprintf(buf + length, size - length, "%s%s",
440 	    dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep);
441 	if (length >= size)
442 		return (-1);
443 	length += snprintf(buf + length, size - length, "%s\n",
444 	    dap->da_devexec ? dap->da_devexec : "");
445 	if (length >= size)
446 		return (-1);
447 
448 	return (0);
449 }
450 
451 /*
452  * _da2strentry -
453  *	calls da2str to break given devalloc_t into printable entry.
454  *	returns pointer to decoded entry, NULL on error.
455  */
456 static strentry_t *
457 _da2strentry(da_args *dargs, devalloc_t *dap)
458 {
459 	strentry_t	*sep;
460 
461 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
462 		return (NULL);
463 	if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str),
464 	    KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) {
465 		free(sep);
466 		return (NULL);
467 	}
468 	return (sep);
469 }
470 
471 /*
472  * _def2str
473  *	converts da_defs_t into a printable string.
474  *	returns 0 on success, -1 on error.
475  */
476 static int
477 _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep)
478 {
479 	int length;
480 
481 	length = snprintf(buf, size, "%s%s", da_defs->devtype, sep);
482 	if (length >= size)
483 		return (-1);
484 	if (da_defs->devopts) {
485 		if (_kva2str(da_defs->devopts, buf + length, size - length,
486 		    KV_ASSIGN, KV_DELIMITER) != 0)
487 			return (-1);
488 		length = strlen(buf);
489 	}
490 	if (length >= size)
491 		return (-1);
492 
493 	return (0);
494 }
495 
496 /*
497  * _def2strentry
498  *	calls _def2str to break given da_defs_t into printable entry.
499  *	returns pointer decoded entry, NULL on error.
500  */
501 static strentry_t *
502 _def2strentry(da_defs_t *da_defs)
503 {
504 	strentry_t	*sep;
505 
506 	if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
507 		return (NULL);
508 	if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str),
509 	    KV_TOKEN_DELIMIT) != 0) {
510 		free(sep);
511 		return (NULL);
512 	}
513 
514 	return (sep);
515 }
516 
517 /*
518  * _build_defattrs
519  *	cycles through all defattr entries, stores them in memory. removes
520  *	entries with the given search_key (device type).
521  *	returns 0 if given entry not found, 1 if given entry removed, 2 on
522  *	error.
523  */
524 static int
525 _build_defattrs(da_args *dargs, strentry_t **head_defent)
526 {
527 	int		rc = 0;
528 	da_defs_t	*da_defs;
529 	strentry_t	*tail_str, *tmp_str;
530 
531 	setdadefent();
532 	while ((da_defs = getdadefent()) != NULL) {
533 		rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype));
534 		if (rc && dargs->optflag & DA_ADD &&
535 		    !(dargs->optflag & DA_FORCE)) {
536 			/*
537 			 * During DA_ADD, we keep an existing entry unless
538 			 * we have DA_FORCE set to override that entry.
539 			 */
540 			dargs->optflag |= DA_NO_OVERRIDE;
541 			rc = 0;
542 		}
543 		if (rc == 0) {
544 			tmp_str = _def2strentry(da_defs);
545 			if (tmp_str == NULL) {
546 				freedadefent(da_defs);
547 				enddadefent();
548 				return (2);
549 			}
550 			/* retaining defattr entry: tmp_str->se_str */
551 			tmp_str->se_next = NULL;
552 			if (*head_defent == NULL) {
553 				*head_defent = tail_str = tmp_str;
554 			} else {
555 				tail_str->se_next = tmp_str;
556 				tail_str = tmp_str;
557 			}
558 		}
559 		freedadefent(da_defs);
560 	}
561 	enddadefent();
562 
563 	return (rc);
564 }
565 
566 /*
567  * _build_lists -
568  *	cycles through all the entries, stores them in memory. removes entries
569  *	with the given search_key (device name or type).
570  *	returns 0 if given entry not found, 1 if given entry removed, 2 on
571  *	error.
572  */
573 static int
574 _build_lists(da_args *dargs, strentry_t **head_devallocp,
575     strentry_t **head_devmapp)
576 {
577 	int		rc = 0;
578 	devalloc_t	*devallocp;
579 	devmap_t	*devmapp;
580 	strentry_t	*tail_str;
581 	strentry_t	*tmp_str;
582 
583 	if (dargs->optflag & DA_MAPS_ONLY)
584 		goto dmap_only;
585 
586 	/* build device_allocate */
587 	setdaent();
588 	while ((devallocp = getdaent()) != NULL) {
589 		rc = da_match(devallocp, dargs);
590 		if (rc && dargs->optflag & DA_ADD &&
591 		    !(dargs->optflag & DA_FORCE)) {
592 			/*
593 			 * During DA_ADD, we keep an existing entry unless
594 			 * we have DA_FORCE set to override that entry.
595 			 */
596 			dargs->optflag |= DA_NO_OVERRIDE;
597 			rc = 0;
598 		}
599 		if (rc == 0) {
600 			tmp_str = _da2strentry(dargs, devallocp);
601 			if (tmp_str == NULL) {
602 				freedaent(devallocp);
603 				enddaent();
604 				return (2);
605 			}
606 			/* retaining devalloc entry: tmp_str->se_str */
607 			tmp_str->se_next = NULL;
608 			if (*head_devallocp == NULL) {
609 				*head_devallocp = tail_str = tmp_str;
610 			} else {
611 				tail_str->se_next = tmp_str;
612 				tail_str = tmp_str;
613 			}
614 		}
615 		freedaent(devallocp);
616 	}
617 	enddaent();
618 
619 dmap_only:
620 	if (dargs->optflag & DA_ALLOC_ONLY)
621 		return (rc);
622 
623 	/* build device_maps */
624 	rc = 0;
625 	setdmapent();
626 	while ((devmapp = getdmapent()) != NULL) {
627 		rc = dm_match(devmapp, dargs);
628 		if (rc && dargs->optflag & DA_ADD &&
629 		    !(dargs->optflag & DA_FORCE)) {
630 			/*
631 			 * During DA_ADD, we keep an existing entry unless
632 			 * we have DA_FORCE set to override that entry.
633 			 */
634 			dargs->optflag |= DA_NO_OVERRIDE;
635 			rc = 0;
636 		}
637 		if (rc == 0) {
638 			tmp_str = _dmap2strentry(dargs, devmapp);
639 			if (tmp_str == NULL) {
640 				freedmapent(devmapp);
641 				enddmapent();
642 				return (2);
643 			}
644 			/* retaining devmap entry: tmp_str->se_str */
645 			tmp_str->se_next = NULL;
646 			if (*head_devmapp == NULL) {
647 				*head_devmapp = tail_str = tmp_str;
648 			} else {
649 				tail_str->se_next = tmp_str;
650 				tail_str = tmp_str;
651 			}
652 		}
653 		freedmapent(devmapp);
654 	}
655 	enddmapent();
656 
657 	return (rc);
658 }
659 
660 /*
661  * _write_defattrs
662  *	writes current entries to devalloc_defaults.
663  */
664 static void
665 _write_defattrs(FILE *fp, strentry_t *head_defent)
666 {
667 	strentry_t *tmp_str;
668 
669 	for (tmp_str = head_defent; tmp_str != NULL;
670 	    tmp_str = tmp_str->se_next) {
671 		(void) fputs(tmp_str->se_str, fp);
672 		(void) fputs("\n", fp);
673 	}
674 
675 }
676 
677 /*
678  * _write_device_allocate -
679  *	writes current entries in the list to device_allocate.
680  */
681 static void
682 _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
683 {
684 	int		is_on = -1;
685 	strentry_t	*tmp_str;
686 	struct stat	dastat;
687 
688 	(void) fseek(dafp, (off_t)0, SEEK_SET);
689 
690 	/*
691 	 * if the devalloc on/off string existed before,
692 	 * put it back before anything else.
693 	 * we need to check for the string only if the file
694 	 * exists.
695 	 */
696 	if (stat(odevalloc, &dastat) == 0) {
697 		is_on = da_is_on();
698 		if (is_on == 0)
699 			(void) fputs(DA_OFF_STR, dafp);
700 		else if (is_on == 1)
701 			(void) fputs(DA_ON_STR, dafp);
702 	}
703 	tmp_str = head_devallocp;
704 	while (tmp_str) {
705 		(void) fputs(tmp_str->se_str, dafp);
706 		(void) fputs("\n", dafp);
707 		tmp_str = tmp_str->se_next;
708 	}
709 }
710 
711 /*
712  * _write_device_maps -
713  *	writes current entries in the list to device_maps.
714  */
715 static void
716 _write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
717 {
718 	strentry_t	*tmp_str;
719 
720 	(void) fseek(dmfp, (off_t)0, SEEK_SET);
721 
722 	tmp_str = head_devmapp;
723 	while (tmp_str) {
724 		(void) fputs(tmp_str->se_str, dmfp);
725 		(void) fputs("\n", dmfp);
726 		tmp_str = tmp_str->se_next;
727 	}
728 }
729 
730 /*
731  * _write_new_defattrs
732  *	writes the new entry to devalloc_defaults.
733  *	returns 0 on success, -1 on error.
734  */
735 static int
736 _write_new_defattrs(FILE *fp, da_args *dargs)
737 {
738 	int		count;
739 	char		*tok = NULL, *tokp = NULL;
740 	char		*lasts;
741 	devinfo_t	*devinfo = dargs->devinfo;
742 
743 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
744 		return (-1);
745 	if (!devinfo->devopts)
746 		return (0);
747 	(void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
748 	    KV_TOKEN_DELIMIT);
749 	if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
750 		(void) strcpy(tokp, devinfo->devopts);
751 		if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
752 			(void) fprintf(fp, "%s", tok);
753 			count = 1;
754 		}
755 		while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
756 			if (count)
757 				(void) fprintf(fp, "%s", KV_DELIMITER);
758 			(void) fprintf(fp, "%s", tok);
759 			count++;
760 		}
761 	} else {
762 		(void) fprintf(fp, "%s", devinfo->devopts);
763 	}
764 
765 	return (0);
766 }
767 
768 /*
769  * _write_new_entry -
770  *	writes the new devalloc_t to device_allocate or the new devmap_t to
771  *	device_maps.
772  *	returns 0 on success, -1 on error.
773  */
774 static int
775 _write_new_entry(FILE *fp, da_args *dargs, int flag)
776 {
777 	int		count;
778 	char		*tok = NULL, *tokp = NULL;
779 	char		*lasts;
780 	devinfo_t	*devinfo = dargs->devinfo;
781 
782 	if (flag & DA_MAPS_ONLY)
783 		goto dmap_only;
784 
785 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
786 		return (-1);
787 
788 	(void) fprintf(fp, "%s%s\\\n\t",
789 	    (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
790 	(void) fprintf(fp, "%s%s\\\n\t",
791 	    (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
792 	if (devinfo->devopts == NULL) {
793 		(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
794 		    KV_DELIMITER);
795 	} else {
796 		if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
797 			(void) strcpy(tokp, devinfo->devopts);
798 			if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
799 			    NULL) {
800 				(void) fprintf(fp, "%s", tok);
801 				count = 1;
802 			}
803 			while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
804 			    &lasts)) != NULL) {
805 				if (count)
806 					(void) fprintf(fp, "%s",
807 					    KV_TOKEN_DELIMIT "\\\n\t");
808 				(void) fprintf(fp, "%s", tok);
809 				count++;
810 			}
811 			if (count)
812 				(void) fprintf(fp, "%s",
813 				    KV_DELIMITER "\\\n\t");
814 		} else {
815 			(void) fprintf(fp, "%s%s", devinfo->devopts,
816 			    KV_DELIMITER "\\\n\t");
817 		}
818 	}
819 	(void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
820 	(void) fprintf(fp, "%s%s\\\n\t",
821 	    (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
822 	    KV_DELIMITER);
823 	(void) fprintf(fp, "%s\n",
824 	    (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
825 
826 dmap_only:
827 	if (flag & DA_ALLOC_ONLY)
828 		return (0);
829 
830 	if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
831 		return (-1);
832 
833 	(void) fprintf(fp, "%s%s\\\n",
834 	    (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
835 	(void) fprintf(fp, "\t%s%s\\\n",
836 	    (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
837 	(void) fprintf(fp, "\t%s\n",
838 	    (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
839 
840 	return (0);
841 }
842 
843 /*
844  * _da_lock_devdb -
845  *	locks the database files; lock can be either broken explicitly by
846  *	closing the fd of the lock file, or it expires automatically at process
847  *	termination.
848  * 	returns fd of the lock file or -1 on error.
849  */
850 int
851 _da_lock_devdb(char *rootdir)
852 {
853 	int		lockfd = -1;
854 	char		*lockfile;
855 	char		path[MAXPATHLEN];
856 	int		size = sizeof (path);
857 
858 	if (rootdir == NULL) {
859 		lockfile = DA_DB_LOCK;
860 	} else {
861 		path[0] = '\0';
862 		if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
863 			return (-1);
864 		lockfile = path;
865 	}
866 
867 	if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
868 		/* cannot open lock file */
869 		return (-1);
870 
871 	(void) fchown(lockfd, DA_UID, DA_GID);
872 
873 	if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
874 		/* cannot position lock file */
875 		(void) close(lockfd);
876 		return (-1);
877 	}
878 	if (lockf(lockfd, F_TLOCK, 0) == -1) {
879 		/* cannot set lock */
880 		(void) close(lockfd);
881 		return (-1);
882 	}
883 	(void) utime(lockfile, NULL);
884 
885 	return (lockfd);
886 }
887 
888 /*
889  * da_open_devdb -
890  *	opens one or both database files - device_allocate, device_maps - in
891  *	the specified mode.
892  *	locks the database files; lock is either broken explicitly by the
893  *	caller by closing the lock file fd, or it expires automatically at
894  *	process termination.
895  *	writes the file pointer of opened file in the input args - dafp, dmfp.
896  *	returns fd of the lock file on success, -2 if database file does not
897  *	exist, -1 on other errors.
898  */
899 int
900 da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
901 {
902 	int	oflag = 0;
903 	int	fda = -1;
904 	int	fdm = -1;
905 	int	lockfd = -1;
906 	char	*fname;
907 	char	*fmode;
908 	char	path[MAXPATHLEN];
909 	FILE	*devfile;
910 
911 	if ((dafp == NULL) && (dmfp == NULL))
912 		return (-1);
913 
914 	if (flag & DA_RDWR) {
915 		oflag = DA_RDWR;
916 		fmode = "r+";
917 	} else if (flag & DA_RDONLY) {
918 		oflag = DA_RDONLY;
919 		fmode = "r";
920 	}
921 
922 	if ((lockfd = _da_lock_devdb(rootdir)) == -1)
923 		return (-1);
924 
925 	if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
926 		goto dmap_only;
927 
928 	path[0] = '\0';
929 
930 	/*
931 	 * open the device allocation file
932 	 */
933 	if (rootdir == NULL) {
934 		fname = DEVALLOC;
935 	} else {
936 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
937 		    DEVALLOC) >= sizeof (path)) {
938 			if (lockfd != -1)
939 				(void) close(lockfd);
940 			return (-1);
941 		}
942 		fname = path;
943 	}
944 	if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
945 		if (lockfd != -1)
946 			(void) close(lockfd);
947 		return ((errno == ENOENT) ? -2 : -1);
948 	}
949 	if ((devfile = fdopen(fda, fmode)) == NULL) {
950 		(void) close(fda);
951 		if (lockfd != -1)
952 			(void) close(lockfd);
953 		return (-1);
954 	}
955 	*dafp = devfile;
956 	(void) fchmod(fda, DA_DBMODE);
957 
958 	if ((flag & DA_ALLOC_ONLY))
959 		goto out;
960 
961 dmap_only:
962 	path[0] = '\0';
963 	/*
964 	 * open the device map file
965 	 */
966 	if (rootdir == NULL) {
967 		fname = DEVMAP;
968 	} else {
969 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
970 		    DEVMAP) >= sizeof (path)) {
971 			(void) close(fda);
972 			if (lockfd != -1)
973 				(void) close(lockfd);
974 			return (-1);
975 		}
976 		fname = path;
977 	}
978 
979 	if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
980 		if (lockfd != -1)
981 			(void) close(lockfd);
982 		return ((errno == ENOENT) ? -2 : -1);
983 	}
984 
985 	if ((devfile = fdopen(fdm, fmode)) == NULL) {
986 		(void) close(fdm);
987 		(void) close(fda);
988 		if (lockfd != -1)
989 			(void) close(lockfd);
990 		return (-1);
991 	}
992 	*dmfp = devfile;
993 	(void) fchmod(fdm, DA_DBMODE);
994 
995 out:
996 	return (lockfd);
997 }
998 
999 /*
1000  * _record_on_off -
1001  *	adds either DA_ON_STR or DA_OFF_STR to device_allocate
1002  *	returns 0 on success, -1 on error.
1003  */
1004 static int
1005 _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
1006 {
1007 	int		dafd;
1008 	int		nsize;
1009 	int		nitems = 1;
1010 	int		actionlen;
1011 	int		str_found = 0;
1012 	int		len = 0, nlen = 0, plen = 0;
1013 	char		*ptr = NULL;
1014 	char		*actionstr;
1015 	char		*nbuf = NULL;
1016 	char		line[MAX_CANON];
1017 	struct stat	dastat;
1018 
1019 	if (dargs->optflag & DA_ON)
1020 		actionstr = DA_ON_STR;
1021 	else
1022 		actionstr = DA_OFF_STR;
1023 	actionlen = strlen(actionstr);
1024 	dafd = fileno(dafp);
1025 	if (fstat(dafd, &dastat) == -1)
1026 		return (-1);
1027 
1028 	/* check the old device_allocate for on/off string */
1029 	ptr = fgets(line, MAX_CANON, dafp);
1030 	if (ptr != NULL) {
1031 		if ((strcmp(line, DA_ON_STR) == 0) ||
1032 		    (strcmp(line, DA_OFF_STR) == 0)) {
1033 			str_found = 1;
1034 			nsize = dastat.st_size;
1035 		}
1036 	}
1037 	if (!ptr || !str_found) {
1038 		/*
1039 		 * the file never had either the on or the off string;
1040 		 * make room for it.
1041 		 */
1042 		str_found = 0;
1043 		nsize = dastat.st_size + actionlen + 1;
1044 	}
1045 	if ((nbuf = (char *)malloc(nsize)) == NULL)
1046 		return (-1);
1047 	nbuf[0] = '\0';
1048 	/* put the on/off string */
1049 	(void) strcpy(nbuf, actionstr);
1050 	nlen = strlen(nbuf);
1051 	plen = nlen;
1052 	if (ptr && !str_found) {
1053 		/* now put the first line that we read in fgets */
1054 		nlen = plen + strlen(line) + 1;
1055 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1056 		if (len >= nsize) {
1057 			free(nbuf);
1058 			return (-1);
1059 		}
1060 		plen += len;
1061 	}
1062 
1063 	/* now get the rest of the old file */
1064 	while (fgets(line, MAX_CANON, dafp) != NULL) {
1065 		nlen = plen + strlen(line) + 1;
1066 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1067 		if (len >= nsize) {
1068 			free(nbuf);
1069 			return (-1);
1070 		}
1071 		plen += len;
1072 	}
1073 	len = strlen(nbuf) + 1;
1074 	if (len < nsize)
1075 		nbuf[len] = '\n';
1076 
1077 	/* write the on/off str + the old device_allocate to the temp file */
1078 	if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
1079 		free(nbuf);
1080 		return (-1);
1081 	}
1082 
1083 	free(nbuf);
1084 
1085 	return (0);
1086 }
1087 
1088 /*
1089  * da_update_defattrs -
1090  *	writes default attributes to devalloc_defaults
1091  *	returns 0 on success, -1 on error.
1092  */
1093 int
1094 da_update_defattrs(da_args *dargs)
1095 {
1096 	int		rc = 0, lockfd = 0, tmpfd = 0;
1097 	char		*defpath = DEFATTRS;
1098 	char		*tmpdefpath = TMPATTRS;
1099 	FILE		*tmpfp = NULL;
1100 	struct stat	dstat;
1101 	strentry_t	*head_defent = NULL;
1102 
1103 	if (dargs == NULL)
1104 		return (0);
1105 	if ((lockfd = _da_lock_devdb(NULL)) == -1)
1106 		return (-1);
1107 	if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1108 		(void) close(lockfd);
1109 		return (-1);
1110 	}
1111 	(void) fchown(tmpfd, DA_UID, DA_GID);
1112 	if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
1113 		(void) close(tmpfd);
1114 		(void) unlink(tmpdefpath);
1115 		(void) close(lockfd);
1116 		return (-1);
1117 	}
1118 	/*
1119 	 * examine all entries, remove an old one if required, check
1120 	 * if a new one needs to be added.
1121 	 */
1122 	if (stat(defpath, &dstat) == 0) {
1123 		if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
1124 			if (rc == 1) {
1125 				(void) close(tmpfd);
1126 				(void) unlink(tmpdefpath);
1127 				(void) close(lockfd);
1128 				return (rc);
1129 			}
1130 		}
1131 	}
1132 	/*
1133 	 * write back any existing entries.
1134 	 */
1135 	_write_defattrs(tmpfp, head_defent);
1136 
1137 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1138 		/* add new entries */
1139 		rc = _write_new_defattrs(tmpfp, dargs);
1140 		(void) fclose(tmpfp);
1141 	} else {
1142 		(void) fclose(tmpfp);
1143 	}
1144 	if (rename(tmpdefpath, defpath) != 0) {
1145 		rc = -1;
1146 		(void) unlink(tmpdefpath);
1147 	}
1148 	(void) close(lockfd);
1149 
1150 	return (rc);
1151 }
1152 
1153 /*
1154  * da_update_device -
1155  *	writes devices entries to device_allocate and device_maps.
1156  * 	returns 0 on success, -1 on error.
1157  */
1158 int
1159 da_update_device(da_args *dargs)
1160 {
1161 	int		rc;
1162 	int		tafd = -1, tmfd = -1;
1163 	int		lockfd = -1;
1164 	char		*rootdir = NULL;
1165 	char		*apathp = NULL, *mpathp = NULL, *dapathp = NULL,
1166 			*dmpathp = NULL;
1167 	char		apath[MAXPATHLEN], mpath[MAXPATHLEN],
1168 			dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
1169 	FILE		*tafp = NULL, *tmfp = NULL, *dafp = NULL;
1170 	struct stat	dastat;
1171 	devinfo_t	*devinfo;
1172 	strentry_t	*head_devmapp = NULL;
1173 	strentry_t	*head_devallocp = NULL;
1174 
1175 	if (dargs == NULL)
1176 		return (0);
1177 
1178 	rootdir = dargs->rootdir;
1179 	devinfo = dargs->devinfo;
1180 
1181 	/*
1182 	 * adding/removing entries should be done in both
1183 	 * device_allocate and device_maps. updates can be
1184 	 * done in both or either of the files.
1185 	 */
1186 	if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
1187 		if (dargs->optflag & DA_ALLOC_ONLY ||
1188 		    dargs->optflag & DA_MAPS_ONLY)
1189 			return (0);
1190 	}
1191 
1192 	/*
1193 	 * name, type and list are required fields for adding a new
1194 	 * device.
1195 	 */
1196 	if ((dargs->optflag & DA_ADD) &&
1197 	    ((devinfo->devname == NULL) ||
1198 	    (devinfo->devtype == NULL) ||
1199 	    (devinfo->devlist == NULL))) {
1200 		return (-1);
1201 	}
1202 
1203 	if (rootdir != NULL) {
1204 		if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
1205 		    TMPALLOC) >= sizeof (apath))
1206 			return (-1);
1207 		apathp = apath;
1208 		if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
1209 		    DEVALLOC) >= sizeof (dapath))
1210 			return (-1);
1211 		dapathp = dapath;
1212 		if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1213 			if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
1214 			    TMPMAP) >= sizeof (mpath))
1215 				return (-1);
1216 			mpathp = mpath;
1217 			if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
1218 			    DEVMAP) >= sizeof (dmpath))
1219 				return (-1);
1220 			dmpathp = dmpath;
1221 		}
1222 	} else {
1223 		apathp = TMPALLOC;
1224 		dapathp = DEVALLOC;
1225 		mpathp = TMPMAP;
1226 		dmpathp = DEVMAP;
1227 	}
1228 
1229 	if (dargs->optflag & DA_MAPS_ONLY)
1230 		goto dmap_only;
1231 
1232 	/*
1233 	 * Check if we are here just to record on/off status of
1234 	 * device_allocation.
1235 	 */
1236 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
1237 		lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
1238 		    DA_RDONLY|DA_ALLOC_ONLY);
1239 	else
1240 		lockfd = _da_lock_devdb(rootdir);
1241 	if (lockfd == -1)
1242 		return (-1);
1243 
1244 	if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1245 		(void) close(lockfd);
1246 		(void) fclose(dafp);
1247 		return (-1);
1248 	}
1249 	(void) fchown(tafd, DA_UID, DA_GID);
1250 	if ((tafp = fdopen(tafd, "r+")) == NULL) {
1251 		(void) close(tafd);
1252 		(void) unlink(apathp);
1253 		(void) fclose(dafp);
1254 		(void) close(lockfd);
1255 		return (-1);
1256 	}
1257 
1258 	/*
1259 	 * We don't need to parse the file if we are here just to record
1260 	 * on/off status of device_allocation.
1261 	 */
1262 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
1263 		if (_record_on_off(dargs, tafp, dafp) == -1) {
1264 			(void) close(tafd);
1265 			(void) unlink(apathp);
1266 			(void) fclose(dafp);
1267 			(void) close(lockfd);
1268 			return (-1);
1269 		}
1270 		(void) fclose(dafp);
1271 		goto out;
1272 	}
1273 
1274 	/*
1275 	 * examine all the entries, remove an old one if forced to,
1276 	 * and check that they are suitable for updating.
1277 	 *  we need to do this only if the file exists already.
1278 	 */
1279 	if (stat(dapathp, &dastat) == 0) {
1280 		if ((rc = _build_lists(dargs, &head_devallocp,
1281 		    &head_devmapp)) != 0) {
1282 			if (rc != 1) {
1283 				(void) close(tafd);
1284 				(void) unlink(apathp);
1285 				(void) close(lockfd);
1286 				return (rc);
1287 			}
1288 		}
1289 	}
1290 
1291 	/*
1292 	 * write back any existing devalloc entries, along with
1293 	 * the devalloc on/off string.
1294 	 */
1295 	_write_device_allocate(dapathp, tafp, head_devallocp);
1296 
1297 	if (dargs->optflag & DA_ALLOC_ONLY)
1298 		goto out;
1299 
1300 dmap_only:
1301 	if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1302 		(void) close(tafd);
1303 		(void) unlink(apathp);
1304 		(void) close(lockfd);
1305 		return (-1);
1306 	}
1307 	(void) fchown(tmfd, DA_UID, DA_GID);
1308 	if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
1309 		(void) close(tafd);
1310 		(void) unlink(apathp);
1311 		(void) close(tmfd);
1312 		(void) unlink(mpathp);
1313 		(void) close(lockfd);
1314 		return (-1);
1315 	}
1316 
1317 	/* write back any existing devmap entries */
1318 	if (head_devmapp != NULL)
1319 		_write_device_maps(tmfp, head_devmapp);
1320 
1321 out:
1322 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1323 		/* add any new entries */
1324 		rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
1325 		(void) fclose(tafp);
1326 
1327 		if (rc == 0)
1328 			rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
1329 		(void) fclose(tmfp);
1330 	} else {
1331 		if (tafp)
1332 			(void) fclose(tafp);
1333 		if (tmfp)
1334 			(void) fclose(tmfp);
1335 	}
1336 
1337 	rc = 0;
1338 	if (!(dargs->optflag & DA_MAPS_ONLY)) {
1339 		if (rename(apathp, dapathp) != 0) {
1340 			rc = -1;
1341 			(void) unlink(apathp);
1342 		}
1343 	}
1344 	if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1345 		if (rename(mpathp, dmpathp) != 0) {
1346 			rc = -1;
1347 			(void) unlink(mpathp);
1348 		}
1349 	}
1350 
1351 	(void) close(lockfd);
1352 
1353 	return (rc);
1354 }
1355 
1356 /*
1357  * da_add_list -
1358  *	adds new /dev link name to the linked list of devices.
1359  *	returns 0 if link added successfully, -1 on error.
1360  */
1361 int
1362 da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
1363 {
1364 	int		instance;
1365 	int		nlen, plen;
1366 	int		new_entry = 0;
1367 	char		*dtype, *dexec, *tname, *kval;
1368 	char		*minstr = NULL, *maxstr = NULL;
1369 	char		dname[DA_MAXNAME];
1370 	kva_t		*kva;
1371 	deventry_t	*dentry = NULL, *nentry = NULL, *pentry = NULL;
1372 	da_defs_t	*da_defs;
1373 
1374 	if (dlist == NULL || link == NULL)
1375 		return (-1);
1376 
1377 	dname[0] = '\0';
1378 	if (flag & DA_AUDIO) {
1379 		dentry = dlist->audio;
1380 		tname = DA_AUDIO_NAME;
1381 		dtype = DA_AUDIO_TYPE;
1382 		dexec = DA_DEFAULT_AUDIO_CLEAN;
1383 	} else if (flag & DA_CD) {
1384 		dentry = dlist->cd;
1385 		tname = DA_CD_NAME;
1386 		dtype = DA_CD_TYPE;
1387 		dexec = DA_DEFAULT_DISK_CLEAN;
1388 	} else if (flag & DA_FLOPPY) {
1389 		dentry = dlist->floppy;
1390 		tname = DA_FLOPPY_NAME;
1391 		dtype = DA_FLOPPY_TYPE;
1392 		dexec = DA_DEFAULT_DISK_CLEAN;
1393 	} else if (flag & DA_TAPE) {
1394 		dentry = dlist->tape;
1395 		tname = DA_TAPE_NAME;
1396 		dtype = DA_TAPE_TYPE;
1397 		dexec = DA_DEFAULT_TAPE_CLEAN;
1398 	} else if (flag & DA_RMDISK) {
1399 		dentry = dlist->rmdisk;
1400 		tname = DA_RMDISK_NAME;
1401 		dtype = DA_RMDISK_TYPE;
1402 		dexec = DA_DEFAULT_DISK_CLEAN;
1403 	} else {
1404 		return (-1);
1405 	}
1406 
1407 	for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
1408 		pentry = nentry;
1409 		(void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
1410 		if (nentry->devinfo.instance == new_instance)
1411 			/*
1412 			 * Add the new link name to the list of links
1413 			 * that the device 'dname' has.
1414 			 */
1415 			break;
1416 	}
1417 
1418 	if (nentry == NULL) {
1419 		/*
1420 		 * Either this is the first entry ever, or no matching entry
1421 		 * was found. Create a new one and add to the list.
1422 		 */
1423 		if (dentry == NULL)		/* first entry ever */
1424 			instance = 0;
1425 		else				/* no matching entry */
1426 			instance++;
1427 		(void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
1428 		if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
1429 		    NULL)
1430 			return (-1);
1431 		if (pentry != NULL)
1432 			pentry->next = nentry;
1433 		new_entry = 1;
1434 		nentry->devinfo.devname = strdup(dname);
1435 		nentry->devinfo.devtype = dtype;
1436 		nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
1437 		nentry->devinfo.devexec = dexec;
1438 		nentry->devinfo.instance = new_instance;
1439 		/*
1440 		 * Look for default label range, authorizations and cleaning
1441 		 * program in devalloc_defaults. If label range is not
1442 		 * specified in devalloc_defaults, assume it to be admin_low
1443 		 * to admin_high.
1444 		 */
1445 		minstr = DA_DEFAULT_MIN;
1446 		maxstr = DA_DEFAULT_MAX;
1447 		setdadefent();
1448 		if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
1449 			kva = da_defs->devopts;
1450 			if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
1451 				minstr = strdup(kval);
1452 			if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
1453 				maxstr = strdup(kval);
1454 			if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
1455 				nentry->devinfo.devauths = strdup(kval);
1456 			if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
1457 				nentry->devinfo.devexec = strdup(kval);
1458 			freedadefent(da_defs);
1459 		}
1460 		enddadefent();
1461 		kval = NULL;
1462 		nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
1463 		    strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
1464 		    strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
1465 		    + 1;			/* +1 for terminator */
1466 		if (kval = (char *)malloc(nlen))
1467 			(void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
1468 			    DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
1469 			    DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
1470 		nentry->devinfo.devopts = kval;
1471 
1472 		nentry->devinfo.devlist = NULL;
1473 		nentry->next = NULL;
1474 	}
1475 
1476 	nlen = strlen(link) + 1;		/* +1 terminator */
1477 	if (nentry->devinfo.devlist) {
1478 		plen = strlen(nentry->devinfo.devlist);
1479 		nlen = nlen + plen + 1;	/* +1 for blank to separate entries */
1480 	} else {
1481 		plen = 0;
1482 	}
1483 
1484 	if ((nentry->devinfo.devlist =
1485 	    (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
1486 		if (new_entry) {
1487 			nentry->devinfo.devname = NULL;
1488 			free(nentry->devinfo.devname);
1489 			nentry = NULL;
1490 			free(nentry);
1491 			if (pentry != NULL)
1492 				pentry->next = NULL;
1493 		}
1494 		return (-1);
1495 	}
1496 
1497 	if (plen == 0)
1498 		(void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
1499 	else
1500 		(void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
1501 		    " %s", link);
1502 
1503 	if (pentry == NULL) {
1504 		/*
1505 		 * This is the first entry of this device type.
1506 		 */
1507 		if (flag & DA_AUDIO)
1508 			dlist->audio = nentry;
1509 		else if (flag & DA_CD)
1510 			dlist->cd = nentry;
1511 		else if (flag & DA_FLOPPY)
1512 			dlist->floppy = nentry;
1513 		else if (flag & DA_TAPE)
1514 			dlist->tape = nentry;
1515 		else if (flag & DA_RMDISK)
1516 			dlist->rmdisk = nentry;
1517 	}
1518 
1519 	return (0);
1520 }
1521 
1522 /*
1523  * da_remove_list -
1524  *	removes a /dev link name from the linked list of devices.
1525  *	returns type of device if link for that device removed
1526  *	successfully, else returns -1 on error.
1527  *	if all links for a device are removed, stores that device
1528  *	name in devname.
1529  */
1530 int
1531 da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
1532 {
1533 	int		flag;
1534 	int		remove_dev = 0;
1535 	int		nlen, plen, slen;
1536 	char		*lasts, *lname, *oldlist;
1537 	struct stat	rmstat;
1538 	deventry_t	*dentry, *current, *prev;
1539 
1540 	if (type != NULL)
1541 		flag = type;
1542 	else if (link == NULL)
1543 		return (-1);
1544 	else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
1545 		flag = DA_AUDIO;
1546 	else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
1547 	    strstr(link, "sr") || strstr(link, "rsr"))
1548 		flag = DA_CD;
1549 	else if (strstr(link, "fd") || strstr(link, "rfd") ||
1550 	    strstr(link, "diskette") || strstr(link, "rdiskette"))
1551 		flag = DA_FLOPPY;
1552 	else if (strstr(link, DA_TAPE_NAME))
1553 		flag = DA_TAPE;
1554 	else
1555 		flag = DA_RMDISK;
1556 
1557 	switch (type) {
1558 	case DA_AUDIO:
1559 		dentry = dlist->audio;
1560 		break;
1561 	case DA_CD:
1562 		dentry = dlist->cd;
1563 		break;
1564 	case DA_FLOPPY:
1565 		dentry = dlist->floppy;
1566 		break;
1567 	case DA_TAPE:
1568 		dentry = dlist->tape;
1569 		break;
1570 	case DA_RMDISK:
1571 		dentry = dlist->rmdisk;
1572 		break;
1573 	default:
1574 		return (-1);
1575 	}
1576 
1577 	if ((type != NULL) && (link == NULL)) {
1578 		for (current = dentry, prev = dentry; current != NULL;
1579 		    current = current->next) {
1580 			oldlist = strdup(current->devinfo.devlist);
1581 			for (lname = strtok_r(oldlist, " ", &lasts);
1582 			    lname != NULL;
1583 			    lname = strtok_r(NULL, " ", &lasts)) {
1584 				if (stat(lname, &rmstat) != 0) {
1585 					remove_dev = 1;
1586 					goto remove_dev;
1587 				}
1588 			}
1589 			prev = current;
1590 		}
1591 		return (-1);
1592 	}
1593 
1594 	for (current = dentry, prev = dentry; current != NULL;
1595 	    current = current->next) {
1596 		plen = strlen(current->devinfo.devlist);
1597 		nlen = strlen(link);
1598 		if (plen == nlen) {
1599 			if (strcmp(current->devinfo.devlist, link) == 0) {
1600 				/* last name in the list */
1601 				remove_dev = 1;
1602 				break;
1603 			}
1604 		}
1605 		if (strstr(current->devinfo.devlist, link)) {
1606 			nlen = plen - nlen + 1;
1607 			oldlist = strdup(current->devinfo.devlist);
1608 			if ((current->devinfo.devlist =
1609 			    (char *)realloc(current->devinfo.devlist,
1610 			    nlen)) == NULL) {
1611 				free(oldlist);
1612 				return (-1);
1613 			}
1614 			current->devinfo.devlist[0] = '\0';
1615 			nlen = plen = slen = 0;
1616 			for (lname = strtok_r(oldlist, " ", &lasts);
1617 			    lname != NULL;
1618 			    lname = strtok_r(NULL, " ", &lasts)) {
1619 				if (strcmp(lname, link) == 0)
1620 					continue;
1621 				nlen = strlen(lname) + plen + 1;
1622 				if (plen == 0) {
1623 					slen =
1624 					    snprintf(current->devinfo.devlist,
1625 						nlen, "%s", lname);
1626 				} else {
1627 					slen =
1628 					    snprintf(current->devinfo.devlist +
1629 						plen, nlen - plen, " %s",
1630 						lname);
1631 				}
1632 				plen = plen + slen + 1;
1633 			}
1634 			free(oldlist);
1635 			break;
1636 		}
1637 		prev = current;
1638 	}
1639 
1640 remove_dev:
1641 	if (remove_dev == 1) {
1642 		(void) strlcpy(devname, current->devinfo.devname, size);
1643 		free(current->devinfo.devname);
1644 		free(current->devinfo.devlist);
1645 		current->devinfo.devname = current->devinfo.devlist = NULL;
1646 		prev->next = current->next;
1647 		free(current);
1648 		current = NULL;
1649 	}
1650 	if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
1651 		if (prev->next) {
1652 			/*
1653 			 * what we removed above was the first entry
1654 			 * in the list. make the next entry to be the
1655 			 * first.
1656 			 */
1657 			current = prev->next;
1658 		} else {
1659 			/*
1660 			 * the matching entry was the only entry in the list
1661 			 * for this type.
1662 			 */
1663 			current = NULL;
1664 		}
1665 		if (flag & DA_AUDIO)
1666 			dlist->audio = current;
1667 		else if (flag & DA_CD)
1668 			dlist->cd = current;
1669 		else if (flag & DA_FLOPPY)
1670 			dlist->floppy = current;
1671 		else if (flag & DA_TAPE)
1672 			dlist->tape = current;
1673 		else if (flag & DA_RMDISK)
1674 			dlist->rmdisk = current;
1675 	}
1676 
1677 	return (flag);
1678 }
1679 
1680 /*
1681  * da_is_on -
1682  *	checks if device allocation feature is turned on.
1683  *	returns 1 if on, 0 if off, -1 if status string not
1684  *	found in device_allocate.
1685  */
1686 int
1687 da_is_on()
1688 {
1689 	return (getdaon());
1690 }
1691 
1692 /*
1693  * da_print_device -
1694  *	debug routine to print device entries.
1695  */
1696 void
1697 da_print_device(int flag, devlist_t *devlist)
1698 {
1699 	deventry_t	*entry, *dentry;
1700 	devinfo_t	*devinfo;
1701 
1702 	if (flag & DA_AUDIO)
1703 		dentry = devlist->audio;
1704 	else if (flag & DA_CD)
1705 		dentry = devlist->cd;
1706 	else if (flag & DA_FLOPPY)
1707 		dentry = devlist->floppy;
1708 	else if (flag & DA_TAPE)
1709 		dentry = devlist->tape;
1710 	else if (flag & DA_RMDISK)
1711 		dentry = devlist->rmdisk;
1712 	else
1713 		return;
1714 
1715 	for (entry = dentry; entry != NULL; entry = entry->next) {
1716 		devinfo = &(entry->devinfo);
1717 		(void) fprintf(stdout, "name: %s\n", devinfo->devname);
1718 		(void) fprintf(stdout, "type: %s\n", devinfo->devtype);
1719 		(void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
1720 		(void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
1721 		(void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
1722 	}
1723 }
1724