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