xref: /titanic_41/usr/src/lib/libbsm/common/devalloc.c (revision 63360950109af2ce85a962ca61f40b8782f11100)
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, "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 	char		*lockfile;
853 	char		path[MAXPATHLEN];
854 	int		size = sizeof (path);
855 
856 	if (rootdir == NULL) {
857 		lockfile = DA_DB_LOCK;
858 	} else {
859 		path[0] = '\0';
860 		if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
861 			return (-1);
862 		lockfile = path;
863 	}
864 
865 	if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
866 		/* cannot open lock file */
867 		return (-1);
868 
869 	(void) fchown(lockfd, DA_UID, DA_GID);
870 
871 	if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
872 		/* cannot position lock file */
873 		(void) close(lockfd);
874 		return (-1);
875 	}
876 	if (lockf(lockfd, F_TLOCK, 0) == -1) {
877 		/* cannot set lock */
878 		(void) close(lockfd);
879 		return (-1);
880 	}
881 	(void) utime(lockfile, NULL);
882 
883 	return (lockfd);
884 }
885 
886 /*
887  * da_open_devdb -
888  *	opens one or both database files - device_allocate, device_maps - in
889  *	the specified mode.
890  *	locks the database files; lock is either broken explicitly by the
891  *	caller by closing the lock file fd, or it expires automatically at
892  *	process termination.
893  *	writes the file pointer of opened file in the input args - dafp, dmfp.
894  *	returns fd of the lock file on success, -2 if database file does not
895  *	exist, -1 on other errors.
896  */
897 int
898 da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
899 {
900 	int	oflag = 0;
901 	int	fda = -1;
902 	int	fdm = -1;
903 	int	lockfd = -1;
904 	char	*fname;
905 	char	*fmode;
906 	char	path[MAXPATHLEN];
907 	FILE	*devfile;
908 
909 	if ((dafp == NULL) && (dmfp == NULL))
910 		return (-1);
911 
912 	if (flag & DA_RDWR) {
913 		oflag = DA_RDWR;
914 		fmode = "r+F";
915 	} else if (flag & DA_RDONLY) {
916 		oflag = DA_RDONLY;
917 		fmode = "rF";
918 	}
919 
920 	if ((lockfd = _da_lock_devdb(rootdir)) == -1)
921 		return (-1);
922 
923 	if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
924 		goto dmap_only;
925 
926 	path[0] = '\0';
927 
928 	/*
929 	 * open the device allocation file
930 	 */
931 	if (rootdir == NULL) {
932 		fname = DEVALLOC;
933 	} else {
934 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
935 		    DEVALLOC) >= sizeof (path)) {
936 			if (lockfd != -1)
937 				(void) close(lockfd);
938 			return (-1);
939 		}
940 		fname = path;
941 	}
942 	if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
943 		if (lockfd != -1)
944 			(void) close(lockfd);
945 		return ((errno == ENOENT) ? -2 : -1);
946 	}
947 	if ((devfile = fdopen(fda, fmode)) == NULL) {
948 		(void) close(fda);
949 		if (lockfd != -1)
950 			(void) close(lockfd);
951 		return (-1);
952 	}
953 	*dafp = devfile;
954 	(void) fchmod(fda, DA_DBMODE);
955 
956 	if ((flag & DA_ALLOC_ONLY))
957 		goto out;
958 
959 dmap_only:
960 	path[0] = '\0';
961 	/*
962 	 * open the device map file
963 	 */
964 	if (rootdir == NULL) {
965 		fname = DEVMAP;
966 	} else {
967 		if (snprintf(path, sizeof (path), "%s%s", rootdir,
968 		    DEVMAP) >= sizeof (path)) {
969 			(void) close(fda);
970 			if (lockfd != -1)
971 				(void) close(lockfd);
972 			return (-1);
973 		}
974 		fname = path;
975 	}
976 
977 	if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
978 		if (lockfd != -1)
979 			(void) close(lockfd);
980 		return ((errno == ENOENT) ? -2 : -1);
981 	}
982 
983 	if ((devfile = fdopen(fdm, fmode)) == NULL) {
984 		(void) close(fdm);
985 		(void) close(fda);
986 		if (lockfd != -1)
987 			(void) close(lockfd);
988 		return (-1);
989 	}
990 	*dmfp = devfile;
991 	(void) fchmod(fdm, DA_DBMODE);
992 
993 out:
994 	return (lockfd);
995 }
996 
997 /*
998  * _record_on_off -
999  *	adds either DA_ON_STR or DA_OFF_STR to device_allocate
1000  *	returns 0 on success, -1 on error.
1001  */
1002 static int
1003 _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
1004 {
1005 	int		dafd;
1006 	int		nsize;
1007 	int		nitems = 1;
1008 	int		actionlen;
1009 	int		str_found = 0;
1010 	int		len = 0, nlen = 0, plen = 0;
1011 	char		*ptr = NULL;
1012 	char		*actionstr;
1013 	char		*nbuf = NULL;
1014 	char		line[MAX_CANON];
1015 	struct stat	dastat;
1016 
1017 	if (dargs->optflag & DA_ON)
1018 		actionstr = DA_ON_STR;
1019 	else
1020 		actionstr = DA_OFF_STR;
1021 	actionlen = strlen(actionstr);
1022 	dafd = fileno(dafp);
1023 	if (fstat(dafd, &dastat) == -1)
1024 		return (-1);
1025 
1026 	/* check the old device_allocate for on/off string */
1027 	ptr = fgets(line, MAX_CANON, dafp);
1028 	if (ptr != NULL) {
1029 		if ((strcmp(line, DA_ON_STR) == 0) ||
1030 		    (strcmp(line, DA_OFF_STR) == 0)) {
1031 			str_found = 1;
1032 			nsize = dastat.st_size;
1033 		}
1034 	}
1035 	if (!ptr || !str_found) {
1036 		/*
1037 		 * the file never had either the on or the off string;
1038 		 * make room for it.
1039 		 */
1040 		str_found = 0;
1041 		nsize = dastat.st_size + actionlen + 1;
1042 	}
1043 	if ((nbuf = (char *)malloc(nsize)) == NULL)
1044 		return (-1);
1045 	nbuf[0] = '\0';
1046 	/* put the on/off string */
1047 	(void) strcpy(nbuf, actionstr);
1048 	nlen = strlen(nbuf);
1049 	plen = nlen;
1050 	if (ptr && !str_found) {
1051 		/* now put the first line that we read in fgets */
1052 		nlen = plen + strlen(line) + 1;
1053 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1054 		if (len >= nsize) {
1055 			free(nbuf);
1056 			return (-1);
1057 		}
1058 		plen += len;
1059 	}
1060 
1061 	/* now get the rest of the old file */
1062 	while (fgets(line, MAX_CANON, dafp) != NULL) {
1063 		nlen = plen + strlen(line) + 1;
1064 		len = snprintf(nbuf + plen, nlen - plen, "%s", line);
1065 		if (len >= nsize) {
1066 			free(nbuf);
1067 			return (-1);
1068 		}
1069 		plen += len;
1070 	}
1071 	len = strlen(nbuf) + 1;
1072 	if (len < nsize)
1073 		nbuf[len] = '\n';
1074 
1075 	/* write the on/off str + the old device_allocate to the temp file */
1076 	if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
1077 		free(nbuf);
1078 		return (-1);
1079 	}
1080 
1081 	free(nbuf);
1082 
1083 	return (0);
1084 }
1085 
1086 /*
1087  * da_update_defattrs -
1088  *	writes default attributes to devalloc_defaults
1089  *	returns 0 on success, -1 on error.
1090  */
1091 int
1092 da_update_defattrs(da_args *dargs)
1093 {
1094 	int		rc = 0, lockfd = 0, tmpfd = 0;
1095 	char		*defpath = DEFATTRS;
1096 	char		*tmpdefpath = TMPATTRS;
1097 	FILE		*tmpfp = NULL;
1098 	struct stat	dstat;
1099 	strentry_t	*head_defent = NULL;
1100 
1101 	if (dargs == NULL)
1102 		return (0);
1103 	if ((lockfd = _da_lock_devdb(NULL)) == -1)
1104 		return (-1);
1105 	if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1106 		(void) close(lockfd);
1107 		return (-1);
1108 	}
1109 	(void) fchown(tmpfd, DA_UID, DA_GID);
1110 	if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
1111 		(void) close(tmpfd);
1112 		(void) unlink(tmpdefpath);
1113 		(void) close(lockfd);
1114 		return (-1);
1115 	}
1116 	/*
1117 	 * examine all entries, remove an old one if required, check
1118 	 * if a new one needs to be added.
1119 	 */
1120 	if (stat(defpath, &dstat) == 0) {
1121 		if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
1122 			if (rc == 1) {
1123 				(void) close(tmpfd);
1124 				(void) unlink(tmpdefpath);
1125 				(void) close(lockfd);
1126 				return (rc);
1127 			}
1128 		}
1129 	}
1130 	/*
1131 	 * write back any existing entries.
1132 	 */
1133 	_write_defattrs(tmpfp, head_defent);
1134 
1135 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1136 		/* add new entries */
1137 		rc = _write_new_defattrs(tmpfp, dargs);
1138 		(void) fclose(tmpfp);
1139 	} else {
1140 		(void) fclose(tmpfp);
1141 	}
1142 	if (rename(tmpdefpath, defpath) != 0) {
1143 		rc = -1;
1144 		(void) unlink(tmpdefpath);
1145 	}
1146 	(void) close(lockfd);
1147 
1148 	return (rc);
1149 }
1150 
1151 /*
1152  * da_update_device -
1153  *	writes devices entries to device_allocate and device_maps.
1154  * 	returns 0 on success, -1 on error.
1155  */
1156 int
1157 da_update_device(da_args *dargs)
1158 {
1159 	int		rc;
1160 	int		tafd = -1, tmfd = -1;
1161 	int		lockfd = -1;
1162 	char		*rootdir = NULL;
1163 	char		*apathp = NULL, *mpathp = NULL, *dapathp = NULL,
1164 			*dmpathp = NULL;
1165 	char		apath[MAXPATHLEN], mpath[MAXPATHLEN],
1166 			dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
1167 	FILE		*tafp = NULL, *tmfp = NULL, *dafp = NULL;
1168 	struct stat	dastat;
1169 	devinfo_t	*devinfo;
1170 	strentry_t	*head_devmapp = NULL;
1171 	strentry_t	*head_devallocp = NULL;
1172 
1173 	if (dargs == NULL)
1174 		return (0);
1175 
1176 	rootdir = dargs->rootdir;
1177 	devinfo = dargs->devinfo;
1178 
1179 	/*
1180 	 * adding/removing entries should be done in both
1181 	 * device_allocate and device_maps. updates can be
1182 	 * done in both or either of the files.
1183 	 */
1184 	if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
1185 		if (dargs->optflag & DA_ALLOC_ONLY ||
1186 		    dargs->optflag & DA_MAPS_ONLY)
1187 			return (0);
1188 	}
1189 
1190 	/*
1191 	 * name, type and list are required fields for adding a new
1192 	 * device.
1193 	 */
1194 	if ((dargs->optflag & DA_ADD) &&
1195 	    ((devinfo->devname == NULL) ||
1196 	    (devinfo->devtype == NULL) ||
1197 	    (devinfo->devlist == NULL))) {
1198 		return (-1);
1199 	}
1200 
1201 	if (rootdir != NULL) {
1202 		if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
1203 		    TMPALLOC) >= sizeof (apath))
1204 			return (-1);
1205 		apathp = apath;
1206 		if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
1207 		    DEVALLOC) >= sizeof (dapath))
1208 			return (-1);
1209 		dapathp = dapath;
1210 		if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1211 			if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
1212 			    TMPMAP) >= sizeof (mpath))
1213 				return (-1);
1214 			mpathp = mpath;
1215 			if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
1216 			    DEVMAP) >= sizeof (dmpath))
1217 				return (-1);
1218 			dmpathp = dmpath;
1219 		}
1220 	} else {
1221 		apathp = TMPALLOC;
1222 		dapathp = DEVALLOC;
1223 		mpathp = TMPMAP;
1224 		dmpathp = DEVMAP;
1225 	}
1226 
1227 	if (dargs->optflag & DA_MAPS_ONLY)
1228 		goto dmap_only;
1229 
1230 	/*
1231 	 * Check if we are here just to record on/off status of
1232 	 * device_allocation.
1233 	 */
1234 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
1235 		lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
1236 		    DA_RDONLY|DA_ALLOC_ONLY);
1237 	else
1238 		lockfd = _da_lock_devdb(rootdir);
1239 	if (lockfd == -1)
1240 		return (-1);
1241 
1242 	if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1243 		(void) close(lockfd);
1244 		(void) fclose(dafp);
1245 		return (-1);
1246 	}
1247 	(void) fchown(tafd, DA_UID, DA_GID);
1248 	if ((tafp = fdopen(tafd, "r+")) == NULL) {
1249 		(void) close(tafd);
1250 		(void) unlink(apathp);
1251 		(void) fclose(dafp);
1252 		(void) close(lockfd);
1253 		return (-1);
1254 	}
1255 
1256 	/*
1257 	 * We don't need to parse the file if we are here just to record
1258 	 * on/off status of device_allocation.
1259 	 */
1260 	if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
1261 		if (_record_on_off(dargs, tafp, dafp) == -1) {
1262 			(void) close(tafd);
1263 			(void) unlink(apathp);
1264 			(void) fclose(dafp);
1265 			(void) close(lockfd);
1266 			return (-1);
1267 		}
1268 		(void) fclose(dafp);
1269 		goto out;
1270 	}
1271 
1272 	/*
1273 	 * examine all the entries, remove an old one if forced to,
1274 	 * and check that they are suitable for updating.
1275 	 *  we need to do this only if the file exists already.
1276 	 */
1277 	if (stat(dapathp, &dastat) == 0) {
1278 		if ((rc = _build_lists(dargs, &head_devallocp,
1279 		    &head_devmapp)) != 0) {
1280 			if (rc != 1) {
1281 				(void) close(tafd);
1282 				(void) unlink(apathp);
1283 				(void) close(lockfd);
1284 				return (rc);
1285 			}
1286 		}
1287 	}
1288 
1289 	/*
1290 	 * write back any existing devalloc entries, along with
1291 	 * the devalloc on/off string.
1292 	 */
1293 	_write_device_allocate(dapathp, tafp, head_devallocp);
1294 
1295 	if (dargs->optflag & DA_ALLOC_ONLY)
1296 		goto out;
1297 
1298 dmap_only:
1299 	if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
1300 		(void) close(tafd);
1301 		(void) unlink(apathp);
1302 		(void) close(lockfd);
1303 		return (-1);
1304 	}
1305 	(void) fchown(tmfd, DA_UID, DA_GID);
1306 	if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
1307 		(void) close(tafd);
1308 		(void) unlink(apathp);
1309 		(void) close(tmfd);
1310 		(void) unlink(mpathp);
1311 		(void) close(lockfd);
1312 		return (-1);
1313 	}
1314 
1315 	/* write back any existing devmap entries */
1316 	if (head_devmapp != NULL)
1317 		_write_device_maps(tmfp, head_devmapp);
1318 
1319 out:
1320 	if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
1321 		/* add any new entries */
1322 		rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
1323 		(void) fclose(tafp);
1324 
1325 		if (rc == 0)
1326 			rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
1327 		(void) fclose(tmfp);
1328 	} else {
1329 		if (tafp)
1330 			(void) fclose(tafp);
1331 		if (tmfp)
1332 			(void) fclose(tmfp);
1333 	}
1334 
1335 	rc = 0;
1336 	if (!(dargs->optflag & DA_MAPS_ONLY)) {
1337 		if (rename(apathp, dapathp) != 0) {
1338 			rc = -1;
1339 			(void) unlink(apathp);
1340 		}
1341 	}
1342 	if (!(dargs->optflag & DA_ALLOC_ONLY)) {
1343 		if (rename(mpathp, dmpathp) != 0) {
1344 			rc = -1;
1345 			(void) unlink(mpathp);
1346 		}
1347 	}
1348 
1349 	(void) close(lockfd);
1350 
1351 	return (rc);
1352 }
1353 
1354 /*
1355  * da_add_list -
1356  *	adds new /dev link name to the linked list of devices.
1357  *	returns 0 if link added successfully, -1 on error.
1358  */
1359 int
1360 da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
1361 {
1362 	int		instance;
1363 	int		nlen, plen;
1364 	int		new_entry = 0;
1365 	char		*dtype, *dexec, *tname, *kval;
1366 	char		*minstr = NULL, *maxstr = NULL;
1367 	char		dname[DA_MAXNAME];
1368 	kva_t		*kva;
1369 	deventry_t	*dentry = NULL, *nentry = NULL, *pentry = NULL;
1370 	da_defs_t	*da_defs;
1371 
1372 	if (dlist == NULL || link == NULL)
1373 		return (-1);
1374 
1375 	dname[0] = '\0';
1376 	if (flag & DA_AUDIO) {
1377 		dentry = dlist->audio;
1378 		tname = DA_AUDIO_NAME;
1379 		dtype = DA_AUDIO_TYPE;
1380 		dexec = DA_DEFAULT_AUDIO_CLEAN;
1381 	} else if (flag & DA_CD) {
1382 		dentry = dlist->cd;
1383 		tname = DA_CD_NAME;
1384 		dtype = DA_CD_TYPE;
1385 		dexec = DA_DEFAULT_DISK_CLEAN;
1386 	} else if (flag & DA_FLOPPY) {
1387 		dentry = dlist->floppy;
1388 		tname = DA_FLOPPY_NAME;
1389 		dtype = DA_FLOPPY_TYPE;
1390 		dexec = DA_DEFAULT_DISK_CLEAN;
1391 	} else if (flag & DA_TAPE) {
1392 		dentry = dlist->tape;
1393 		tname = DA_TAPE_NAME;
1394 		dtype = DA_TAPE_TYPE;
1395 		dexec = DA_DEFAULT_TAPE_CLEAN;
1396 	} else if (flag & DA_RMDISK) {
1397 		dentry = dlist->rmdisk;
1398 		tname = DA_RMDISK_NAME;
1399 		dtype = DA_RMDISK_TYPE;
1400 		dexec = DA_DEFAULT_DISK_CLEAN;
1401 	} else {
1402 		return (-1);
1403 	}
1404 
1405 	for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
1406 		pentry = nentry;
1407 		(void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
1408 		if (nentry->devinfo.instance == new_instance)
1409 			/*
1410 			 * Add the new link name to the list of links
1411 			 * that the device 'dname' has.
1412 			 */
1413 			break;
1414 	}
1415 
1416 	if (nentry == NULL) {
1417 		/*
1418 		 * Either this is the first entry ever, or no matching entry
1419 		 * was found. Create a new one and add to the list.
1420 		 */
1421 		if (dentry == NULL)		/* first entry ever */
1422 			instance = 0;
1423 		else				/* no matching entry */
1424 			instance++;
1425 		(void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
1426 		if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
1427 		    NULL)
1428 			return (-1);
1429 		if (pentry != NULL)
1430 			pentry->next = nentry;
1431 		new_entry = 1;
1432 		nentry->devinfo.devname = strdup(dname);
1433 		nentry->devinfo.devtype = dtype;
1434 		nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
1435 		nentry->devinfo.devexec = dexec;
1436 		nentry->devinfo.instance = new_instance;
1437 		/*
1438 		 * Look for default label range, authorizations and cleaning
1439 		 * program in devalloc_defaults. If label range is not
1440 		 * specified in devalloc_defaults, assume it to be admin_low
1441 		 * to admin_high.
1442 		 */
1443 		minstr = DA_DEFAULT_MIN;
1444 		maxstr = DA_DEFAULT_MAX;
1445 		setdadefent();
1446 		if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
1447 			kva = da_defs->devopts;
1448 			if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
1449 				minstr = strdup(kval);
1450 			if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
1451 				maxstr = strdup(kval);
1452 			if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
1453 				nentry->devinfo.devauths = strdup(kval);
1454 			if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
1455 				nentry->devinfo.devexec = strdup(kval);
1456 			freedadefent(da_defs);
1457 		}
1458 		enddadefent();
1459 		kval = NULL;
1460 		nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
1461 		    strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
1462 		    strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
1463 		    + 1;			/* +1 for terminator */
1464 		if (kval = (char *)malloc(nlen))
1465 			(void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
1466 			    DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
1467 			    DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
1468 		nentry->devinfo.devopts = kval;
1469 
1470 		nentry->devinfo.devlist = NULL;
1471 		nentry->next = NULL;
1472 	}
1473 
1474 	nlen = strlen(link) + 1;		/* +1 terminator */
1475 	if (nentry->devinfo.devlist) {
1476 		plen = strlen(nentry->devinfo.devlist);
1477 		nlen = nlen + plen + 1;	/* +1 for blank to separate entries */
1478 	} else {
1479 		plen = 0;
1480 	}
1481 
1482 	if ((nentry->devinfo.devlist =
1483 	    (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
1484 		if (new_entry) {
1485 			nentry->devinfo.devname = NULL;
1486 			free(nentry->devinfo.devname);
1487 			nentry = NULL;
1488 			free(nentry);
1489 			if (pentry != NULL)
1490 				pentry->next = NULL;
1491 		}
1492 		return (-1);
1493 	}
1494 
1495 	if (plen == 0)
1496 		(void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
1497 	else
1498 		(void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
1499 		    " %s", link);
1500 
1501 	if (pentry == NULL) {
1502 		/*
1503 		 * This is the first entry of this device type.
1504 		 */
1505 		if (flag & DA_AUDIO)
1506 			dlist->audio = nentry;
1507 		else if (flag & DA_CD)
1508 			dlist->cd = nentry;
1509 		else if (flag & DA_FLOPPY)
1510 			dlist->floppy = nentry;
1511 		else if (flag & DA_TAPE)
1512 			dlist->tape = nentry;
1513 		else if (flag & DA_RMDISK)
1514 			dlist->rmdisk = nentry;
1515 	}
1516 
1517 	return (0);
1518 }
1519 
1520 /*
1521  * da_remove_list -
1522  *	removes a /dev link name from the linked list of devices.
1523  *	returns type of device if link for that device removed
1524  *	successfully, else returns -1 on error.
1525  *	if all links for a device are removed, stores that device
1526  *	name in devname.
1527  */
1528 int
1529 da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
1530 {
1531 	int		flag;
1532 	int		remove_dev = 0;
1533 	int		nlen, plen, slen;
1534 	char		*lasts, *lname, *oldlist;
1535 	struct stat	rmstat;
1536 	deventry_t	*dentry, *current, *prev;
1537 
1538 	if (type != NULL)
1539 		flag = type;
1540 	else if (link == NULL)
1541 		return (-1);
1542 	else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
1543 		flag = DA_AUDIO;
1544 	else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
1545 	    strstr(link, "sr") || strstr(link, "rsr"))
1546 		flag = DA_CD;
1547 	else if (strstr(link, "fd") || strstr(link, "rfd") ||
1548 	    strstr(link, "diskette") || strstr(link, "rdiskette"))
1549 		flag = DA_FLOPPY;
1550 	else if (strstr(link, DA_TAPE_NAME))
1551 		flag = DA_TAPE;
1552 	else
1553 		flag = DA_RMDISK;
1554 
1555 	switch (type) {
1556 	case DA_AUDIO:
1557 		dentry = dlist->audio;
1558 		break;
1559 	case DA_CD:
1560 		dentry = dlist->cd;
1561 		break;
1562 	case DA_FLOPPY:
1563 		dentry = dlist->floppy;
1564 		break;
1565 	case DA_TAPE:
1566 		dentry = dlist->tape;
1567 		break;
1568 	case DA_RMDISK:
1569 		dentry = dlist->rmdisk;
1570 		break;
1571 	default:
1572 		return (-1);
1573 	}
1574 
1575 	if ((type != NULL) && (link == NULL)) {
1576 		for (current = dentry, prev = dentry; current != NULL;
1577 		    current = current->next) {
1578 			oldlist = strdup(current->devinfo.devlist);
1579 			for (lname = strtok_r(oldlist, " ", &lasts);
1580 			    lname != NULL;
1581 			    lname = strtok_r(NULL, " ", &lasts)) {
1582 				if (stat(lname, &rmstat) != 0) {
1583 					remove_dev = 1;
1584 					goto remove_dev;
1585 				}
1586 			}
1587 			prev = current;
1588 		}
1589 		return (-1);
1590 	}
1591 
1592 	for (current = dentry, prev = dentry; current != NULL;
1593 	    current = current->next) {
1594 		plen = strlen(current->devinfo.devlist);
1595 		nlen = strlen(link);
1596 		if (plen == nlen) {
1597 			if (strcmp(current->devinfo.devlist, link) == 0) {
1598 				/* last name in the list */
1599 				remove_dev = 1;
1600 				break;
1601 			}
1602 		}
1603 		if (strstr(current->devinfo.devlist, link)) {
1604 			nlen = plen - nlen + 1;
1605 			oldlist = strdup(current->devinfo.devlist);
1606 			if ((current->devinfo.devlist =
1607 			    (char *)realloc(current->devinfo.devlist,
1608 			    nlen)) == NULL) {
1609 				free(oldlist);
1610 				return (-1);
1611 			}
1612 			current->devinfo.devlist[0] = '\0';
1613 			nlen = plen = slen = 0;
1614 			for (lname = strtok_r(oldlist, " ", &lasts);
1615 			    lname != NULL;
1616 			    lname = strtok_r(NULL, " ", &lasts)) {
1617 				if (strcmp(lname, link) == 0)
1618 					continue;
1619 				nlen = strlen(lname) + plen + 1;
1620 				if (plen == 0) {
1621 					slen =
1622 					    snprintf(current->devinfo.devlist,
1623 						nlen, "%s", lname);
1624 				} else {
1625 					slen =
1626 					    snprintf(current->devinfo.devlist +
1627 						plen, nlen - plen, " %s",
1628 						lname);
1629 				}
1630 				plen = plen + slen + 1;
1631 			}
1632 			free(oldlist);
1633 			break;
1634 		}
1635 		prev = current;
1636 	}
1637 
1638 remove_dev:
1639 	if (remove_dev == 1) {
1640 		(void) strlcpy(devname, current->devinfo.devname, size);
1641 		free(current->devinfo.devname);
1642 		free(current->devinfo.devlist);
1643 		current->devinfo.devname = current->devinfo.devlist = NULL;
1644 		prev->next = current->next;
1645 		free(current);
1646 		current = NULL;
1647 	}
1648 	if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
1649 		if (prev->next) {
1650 			/*
1651 			 * what we removed above was the first entry
1652 			 * in the list. make the next entry to be the
1653 			 * first.
1654 			 */
1655 			current = prev->next;
1656 		} else {
1657 			/*
1658 			 * the matching entry was the only entry in the list
1659 			 * for this type.
1660 			 */
1661 			current = NULL;
1662 		}
1663 		if (flag & DA_AUDIO)
1664 			dlist->audio = current;
1665 		else if (flag & DA_CD)
1666 			dlist->cd = current;
1667 		else if (flag & DA_FLOPPY)
1668 			dlist->floppy = current;
1669 		else if (flag & DA_TAPE)
1670 			dlist->tape = current;
1671 		else if (flag & DA_RMDISK)
1672 			dlist->rmdisk = current;
1673 	}
1674 
1675 	return (flag);
1676 }
1677 
1678 /*
1679  * da_is_on -
1680  *	checks if device allocation feature is turned on.
1681  *	returns 1 if on, 0 if off, -1 if status string not
1682  *	found in device_allocate.
1683  */
1684 int
1685 da_is_on()
1686 {
1687 	return (getdaon());
1688 }
1689 
1690 /*
1691  * da_print_device -
1692  *	debug routine to print device entries.
1693  */
1694 void
1695 da_print_device(int flag, devlist_t *devlist)
1696 {
1697 	deventry_t	*entry, *dentry;
1698 	devinfo_t	*devinfo;
1699 
1700 	if (flag & DA_AUDIO)
1701 		dentry = devlist->audio;
1702 	else if (flag & DA_CD)
1703 		dentry = devlist->cd;
1704 	else if (flag & DA_FLOPPY)
1705 		dentry = devlist->floppy;
1706 	else if (flag & DA_TAPE)
1707 		dentry = devlist->tape;
1708 	else if (flag & DA_RMDISK)
1709 		dentry = devlist->rmdisk;
1710 	else
1711 		return;
1712 
1713 	for (entry = dentry; entry != NULL; entry = entry->next) {
1714 		devinfo = &(entry->devinfo);
1715 		(void) fprintf(stdout, "name: %s\n", devinfo->devname);
1716 		(void) fprintf(stdout, "type: %s\n", devinfo->devtype);
1717 		(void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
1718 		(void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
1719 		(void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
1720 	}
1721 }
1722