devalloc.c revision 1914:8a8c5f225b1b
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
48extern int _readbufline(char *, int, char *, int, int *);
49extern char *strtok_r(char *, const char *, char **);
50extern char *_strtok_escape(char *, char *, char **);
51extern int getdaon(void);
52extern int da_matchname(devalloc_t *, char *);
53extern int da_match(devalloc_t *, da_args *);
54extern int dmap_matchname(devmap_t *, char *);
55extern 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 */
62typedef 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 */
72int
73da_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 */
175int
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 */
254void
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*/
334static 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 */
358static 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 */
377void
378fix_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 */
391static 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 */
454static 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 */
474static 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 */
499static 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 */
522static 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 */
571static 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
617dmap_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 */
662static 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 */
679static 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 */
713static 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 */
733static 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 */
772static 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
824dmap_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 */
848int
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 */
897int
898da_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
959dmap_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
993out:
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 */
1002static 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 */
1091int
1092da_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 */
1156int
1157da_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
1298dmap_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
1319out:
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 */
1359int
1360da_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 */
1528int
1529da_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
1638remove_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 */
1684int
1685da_is_on()
1686{
1687	return (getdaon());
1688}
1689
1690/*
1691 * da_print_device -
1692 *	debug routine to print device entries.
1693 */
1694void
1695da_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