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