145916cd2Sjpk /*
245916cd2Sjpk * CDDL HEADER START
345916cd2Sjpk *
445916cd2Sjpk * The contents of this file are subject to the terms of the
545916cd2Sjpk * Common Development and Distribution License (the "License").
645916cd2Sjpk * You may not use this file except in compliance with the License.
745916cd2Sjpk *
845916cd2Sjpk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
945916cd2Sjpk * or http://www.opensolaris.org/os/licensing.
1045916cd2Sjpk * See the License for the specific language governing permissions
1145916cd2Sjpk * and limitations under the License.
1245916cd2Sjpk *
1345916cd2Sjpk * When distributing Covered Code, include this CDDL HEADER in each
1445916cd2Sjpk * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1545916cd2Sjpk * If applicable, add the following below this CDDL HEADER, with the
1645916cd2Sjpk * fields enclosed by brackets "[]" replaced with your own identifying
1745916cd2Sjpk * information: Portions Copyright [yyyy] [name of copyright owner]
1845916cd2Sjpk *
1945916cd2Sjpk * CDDL HEADER END
2045916cd2Sjpk */
2145916cd2Sjpk
2245916cd2Sjpk /*
23*399f0677SJan Parcel * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
2445916cd2Sjpk */
2545916cd2Sjpk
2645916cd2Sjpk #include <stdlib.h>
2745916cd2Sjpk #include <ctype.h>
2845916cd2Sjpk #include <unistd.h>
2945916cd2Sjpk #include <limits.h>
3045916cd2Sjpk #include <fcntl.h>
3145916cd2Sjpk #include <sys/types.h>
3245916cd2Sjpk #include <sys/stat.h>
3345916cd2Sjpk #include <utime.h>
3445916cd2Sjpk #include <synch.h>
3545916cd2Sjpk #include <strings.h>
3645916cd2Sjpk #include <string.h>
3745916cd2Sjpk #include <libintl.h>
3845916cd2Sjpk #include <errno.h>
3945916cd2Sjpk #include <auth_list.h>
407e3e5701SJan Parcel #include <syslog.h>
4145916cd2Sjpk #include <bsm/devices.h>
4245916cd2Sjpk #include <bsm/devalloc.h>
43*399f0677SJan Parcel #include <tsol/label.h>
4445916cd2Sjpk
4545916cd2Sjpk #define DA_DEFS "/etc/security/tsol/devalloc_defaults"
4645916cd2Sjpk
4745916cd2Sjpk extern int _readbufline(char *, int, char *, int, int *);
4845916cd2Sjpk extern char *strtok_r(char *, const char *, char **);
4945916cd2Sjpk extern char *_strtok_escape(char *, char *, char **);
5045916cd2Sjpk extern int getdaon(void);
5145916cd2Sjpk extern int da_matchname(devalloc_t *, char *);
5245916cd2Sjpk extern int da_match(devalloc_t *, da_args *);
5345916cd2Sjpk extern int dmap_matchname(devmap_t *, char *);
5445916cd2Sjpk extern int dm_match(devmap_t *, da_args *);
557e3e5701SJan Parcel extern int dmap_matchtype(devmap_t *dmap, char *type);
567e3e5701SJan Parcel extern int dmap_matchdev(devmap_t *dmap, char *dev);
577e3e5701SJan Parcel extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
587e3e5701SJan Parcel extern char *dmap_physname(devmap_t *dmap);
5945916cd2Sjpk
6045916cd2Sjpk /*
6145916cd2Sjpk * The following structure is for recording old entries to be retained.
6245916cd2Sjpk * We read the entries from the database into a linked list in memory,
6345916cd2Sjpk * then turn around and write them out again.
6445916cd2Sjpk */
6545916cd2Sjpk typedef struct strentry {
6645916cd2Sjpk struct strentry *se_next;
6745916cd2Sjpk char se_str[4096 + 1];
6845916cd2Sjpk } strentry_t;
6945916cd2Sjpk
7045916cd2Sjpk /*
7145916cd2Sjpk * da_check_longindevperm -
7245916cd2Sjpk * reads /etc/logindevperm and checks if specified device is in the file.
7345916cd2Sjpk * returns 1 if specified device found in /etc/logindevperm, else returns 0
7445916cd2Sjpk */
7545916cd2Sjpk int
da_check_logindevperm(char * devname)7645916cd2Sjpk da_check_logindevperm(char *devname)
7745916cd2Sjpk {
7845916cd2Sjpk int ret = 0;
7945916cd2Sjpk int fd = -1;
8045916cd2Sjpk int nlen, plen, slen, lineno, fsize;
8145916cd2Sjpk char line[MAX_CANON];
8245916cd2Sjpk char *field_delims = " \t\n";
8345916cd2Sjpk char *fbuf = NULL;
8445916cd2Sjpk char *ptr, *device;
8545916cd2Sjpk char *lasts = NULL;
8645916cd2Sjpk FILE *fp;
8745916cd2Sjpk struct stat f_stat;
8845916cd2Sjpk
8945916cd2Sjpk /*
9045916cd2Sjpk * check if /etc/logindevperm exists and get its size
9145916cd2Sjpk */
9245916cd2Sjpk if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1)
9345916cd2Sjpk return (0);
9445916cd2Sjpk if (fstat(fd, &f_stat) != 0) {
9545916cd2Sjpk (void) close(fd);
9645916cd2Sjpk return (0);
9745916cd2Sjpk }
9845916cd2Sjpk fsize = f_stat.st_size;
9945916cd2Sjpk if ((fbuf = (char *)malloc(fsize)) == NULL) {
10045916cd2Sjpk (void) close(fd);
10145916cd2Sjpk return (0);
10245916cd2Sjpk }
103004388ebScasper if ((fp = fdopen(fd, "rF")) == NULL) {
10445916cd2Sjpk free(fbuf);
10545916cd2Sjpk (void) close(fd);
10645916cd2Sjpk return (0);
10745916cd2Sjpk }
10845916cd2Sjpk
10945916cd2Sjpk /*
11045916cd2Sjpk * read and parse /etc/logindevperm
11145916cd2Sjpk */
11245916cd2Sjpk plen = nlen = lineno = 0;
11345916cd2Sjpk while (fgets(line, MAX_CANON, fp) != NULL) {
11445916cd2Sjpk lineno++;
11545916cd2Sjpk if ((ptr = strchr(line, '#')) != NULL)
11645916cd2Sjpk *ptr = '\0'; /* handle comments */
11745916cd2Sjpk if (strtok_r(line, field_delims, &lasts) == NULL)
11845916cd2Sjpk continue; /* ignore blank lines */
11945916cd2Sjpk if (strtok_r(NULL, field_delims, &lasts) == NULL)
12045916cd2Sjpk /* invalid entry */
12145916cd2Sjpk continue;
12245916cd2Sjpk if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
12345916cd2Sjpk /* empty device list */
12445916cd2Sjpk continue;
12545916cd2Sjpk nlen = strlen(ptr) + 1; /* +1 terminator */
12645916cd2Sjpk nlen += (plen + 1);
12745916cd2Sjpk if (plen == 0)
12845916cd2Sjpk slen = snprintf(fbuf, nlen, "%s", ptr);
12945916cd2Sjpk else
13045916cd2Sjpk slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr);
13145916cd2Sjpk if (slen >= fsize) {
13245916cd2Sjpk fbuf[0] = '\0';
13345916cd2Sjpk (void) fclose(fp);
13445916cd2Sjpk return (slen);
13545916cd2Sjpk }
13645916cd2Sjpk plen += slen;
13745916cd2Sjpk }
13845916cd2Sjpk (void) fclose(fp);
13945916cd2Sjpk
14045916cd2Sjpk /*
14145916cd2Sjpk * check if devname exists in /etc/logindevperm
14245916cd2Sjpk */
14345916cd2Sjpk device = strtok_r(fbuf, ":", &lasts);
14445916cd2Sjpk while (device != NULL) {
14545916cd2Sjpk /*
14645916cd2Sjpk * device and devname may be one of these types -
14745916cd2Sjpk * /dev/xx
14845916cd2Sjpk * /dev/xx*
14945916cd2Sjpk * /dev/dir/xx
15045916cd2Sjpk * /dev/dir/xx*
15145916cd2Sjpk * /dev/dir/"*"
15245916cd2Sjpk */
15345916cd2Sjpk if (strcmp(device, devname) == 0) {
15445916cd2Sjpk /* /dev/xx, /dev/dir/xx */
15545916cd2Sjpk free(fbuf);
15645916cd2Sjpk return (1);
15745916cd2Sjpk }
15845916cd2Sjpk if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
15945916cd2Sjpk /* all wildcard types */
16045916cd2Sjpk *ptr = '\0';
16145916cd2Sjpk if (strncmp(device, devname, strlen(device)) == 0) {
16245916cd2Sjpk free(fbuf);
16345916cd2Sjpk return (1);
16445916cd2Sjpk }
16545916cd2Sjpk }
16645916cd2Sjpk device = strtok_r(NULL, ":", &lasts);
16745916cd2Sjpk }
16845916cd2Sjpk
16945916cd2Sjpk return (ret);
17045916cd2Sjpk }
17145916cd2Sjpk
17245916cd2Sjpk /*
17345916cd2Sjpk * _da_read_file -
17445916cd2Sjpk * establishes readers/writer lock on fname; reads in the file if its
17545916cd2Sjpk * contents changed since the last time we read it.
17645916cd2Sjpk * returns size of buffer read, or -1 on failure.
17745916cd2Sjpk */
17845916cd2Sjpk int
_da_read_file(char * fname,char ** fbuf,time_t * ftime,rwlock_t * flock,int flag)17945916cd2Sjpk _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock,
18045916cd2Sjpk int flag)
18145916cd2Sjpk {
18245916cd2Sjpk int fd = -1;
18345916cd2Sjpk int fsize = 0;
18445916cd2Sjpk time_t newtime;
18545916cd2Sjpk struct stat f_stat;
18645916cd2Sjpk
18745916cd2Sjpk if (flag & DA_FORCE)
18845916cd2Sjpk *ftime = 0;
18945916cd2Sjpk
19045916cd2Sjpk /* check the size and the time stamp on the file */
19145916cd2Sjpk if (rw_rdlock(flock) != 0)
19245916cd2Sjpk return (-1);
19345916cd2Sjpk if (stat(fname, &f_stat) != 0) {
19445916cd2Sjpk (void) rw_unlock(flock);
19545916cd2Sjpk return (-1);
19645916cd2Sjpk }
19745916cd2Sjpk fsize = f_stat.st_size;
19845916cd2Sjpk newtime = f_stat.st_mtime;
19945916cd2Sjpk (void) rw_unlock(flock);
20045916cd2Sjpk
20145916cd2Sjpk while (newtime > *ftime) {
20245916cd2Sjpk /*
20345916cd2Sjpk * file has been modified since we last read it; or this
20445916cd2Sjpk * is a forced read.
20545916cd2Sjpk * read file into the buffer with rw lock.
20645916cd2Sjpk */
20745916cd2Sjpk if (rw_wrlock(flock) != 0)
20845916cd2Sjpk return (-1);
209004388ebScasper if ((fd = open(fname, O_RDONLY)) == -1) {
21045916cd2Sjpk (void) rw_unlock(flock);
21145916cd2Sjpk return (-1);
21245916cd2Sjpk }
21345916cd2Sjpk if (*fbuf != NULL) {
21445916cd2Sjpk free(*fbuf);
21545916cd2Sjpk *fbuf = NULL;
21645916cd2Sjpk }
21745916cd2Sjpk if ((*fbuf = malloc(fsize)) == NULL) {
21845916cd2Sjpk (void) rw_unlock(flock);
21945916cd2Sjpk (void) close(fd);
22045916cd2Sjpk return (-1);
22145916cd2Sjpk }
22245916cd2Sjpk if (read(fd, *fbuf, fsize) < fsize) {
22345916cd2Sjpk free(*fbuf);
22445916cd2Sjpk (void) rw_unlock(flock);
22545916cd2Sjpk (void) close(fd);
22645916cd2Sjpk return (-1);
22745916cd2Sjpk }
22845916cd2Sjpk (void) rw_unlock(flock);
22945916cd2Sjpk /*
23045916cd2Sjpk * verify that the file did not change just after we read it.
23145916cd2Sjpk */
23245916cd2Sjpk if (rw_rdlock(flock) != 0) {
23345916cd2Sjpk free(*fbuf);
23445916cd2Sjpk (void) close(fd);
23545916cd2Sjpk return (-1);
23645916cd2Sjpk }
23745916cd2Sjpk if (stat(fname, &f_stat) != 0) {
23845916cd2Sjpk free(*fbuf);
23945916cd2Sjpk (void) rw_unlock(flock);
24045916cd2Sjpk (void) close(fd);
24145916cd2Sjpk return (-1);
24245916cd2Sjpk }
24345916cd2Sjpk fsize = f_stat.st_size;
24445916cd2Sjpk newtime = f_stat.st_mtime;
24545916cd2Sjpk (void) rw_unlock(flock);
24645916cd2Sjpk (void) close(fd);
24745916cd2Sjpk *ftime = newtime;
24845916cd2Sjpk }
24945916cd2Sjpk
25045916cd2Sjpk return (fsize);
25145916cd2Sjpk }
25245916cd2Sjpk
25345916cd2Sjpk /*
25445916cd2Sjpk * _update_zonename -
25545916cd2Sjpk * add/remove current zone's name to the given devalloc_t.
25645916cd2Sjpk */
25745916cd2Sjpk void
_update_zonename(da_args * dargs,devalloc_t * dap)25845916cd2Sjpk _update_zonename(da_args *dargs, devalloc_t *dap)
25945916cd2Sjpk {
26045916cd2Sjpk int i, j;
26145916cd2Sjpk int oldsize, newsize;
26245916cd2Sjpk int has_zonename = 0;
26345916cd2Sjpk char *zonename;
26445916cd2Sjpk kva_t *newkva, *oldkva;
26545916cd2Sjpk kv_t *newdata, *olddata;
26645916cd2Sjpk devinfo_t *devinfo;
26745916cd2Sjpk
26845916cd2Sjpk devinfo = dargs->devinfo;
26945916cd2Sjpk oldkva = dap->da_devopts;
27045916cd2Sjpk if (oldkva == NULL) {
27145916cd2Sjpk if (dargs->optflag & DA_REMOVE_ZONE)
27245916cd2Sjpk return;
27345916cd2Sjpk if (dargs->optflag & DA_ADD_ZONE) {
27445916cd2Sjpk newkva = _str2kva(devinfo->devopts, KV_ASSIGN,
27545916cd2Sjpk KV_TOKEN_DELIMIT);
27645916cd2Sjpk if (newkva != NULL)
27745916cd2Sjpk dap->da_devopts = newkva;
27845916cd2Sjpk return;
27945916cd2Sjpk }
28045916cd2Sjpk }
28145916cd2Sjpk newsize = oldsize = oldkva->length;
28245916cd2Sjpk if (kva_match(oldkva, DAOPT_ZONE))
28345916cd2Sjpk has_zonename = 1;
28445916cd2Sjpk if (dargs->optflag & DA_ADD_ZONE) {
28545916cd2Sjpk if ((zonename = index(devinfo->devopts, '=')) == NULL)
28645916cd2Sjpk return;
28745916cd2Sjpk zonename++;
28845916cd2Sjpk if (has_zonename) {
28945916cd2Sjpk (void) _insert2kva(oldkva, DAOPT_ZONE, zonename);
29045916cd2Sjpk return;
29145916cd2Sjpk }
29245916cd2Sjpk newsize += 1;
29345916cd2Sjpk } else if (dargs->optflag & DA_REMOVE_ZONE) {
29445916cd2Sjpk if (has_zonename) {
29545916cd2Sjpk newsize -= 1;
29645916cd2Sjpk if (newsize == 0) {
29745916cd2Sjpk /*
29845916cd2Sjpk * If zone name was the only key/value pair,
29945916cd2Sjpk * put 'reserved' in the empty slot.
30045916cd2Sjpk */
30145916cd2Sjpk _kva_free(oldkva);
30245916cd2Sjpk dap->da_devopts = NULL;
30345916cd2Sjpk return;
30445916cd2Sjpk }
30545916cd2Sjpk } else {
30645916cd2Sjpk return;
30745916cd2Sjpk }
30845916cd2Sjpk }
30945916cd2Sjpk newkva = _new_kva(newsize);
31045916cd2Sjpk newkva->length = 0;
31145916cd2Sjpk newdata = newkva->data;
31245916cd2Sjpk olddata = oldkva->data;
31345916cd2Sjpk for (i = 0, j = 0; i < oldsize; i++) {
31445916cd2Sjpk if ((dargs->optflag & DA_REMOVE_ZONE) &&
31545916cd2Sjpk (strcmp(olddata[i].key, DAOPT_ZONE) == 0))
31645916cd2Sjpk continue;
31745916cd2Sjpk newdata[j].key = strdup(olddata[i].key);
31845916cd2Sjpk newdata[j].value = strdup(olddata[i].value);
31945916cd2Sjpk newkva->length++;
32045916cd2Sjpk j++;
32145916cd2Sjpk }
32245916cd2Sjpk if (dargs->optflag & DA_ADD_ZONE) {
32345916cd2Sjpk newdata[j].key = strdup(DAOPT_ZONE);
32445916cd2Sjpk newdata[j].value = strdup(zonename);
32545916cd2Sjpk newkva->length++;
32645916cd2Sjpk }
32745916cd2Sjpk _kva_free(oldkva);
32845916cd2Sjpk dap->da_devopts = newkva;
32945916cd2Sjpk }
33045916cd2Sjpk
33145916cd2Sjpk /*
33245916cd2Sjpk * _dmap2str -
33345916cd2Sjpk * converts a device_map entry into a printable string
33445916cd2Sjpk * returns 0 on success, -1 on error.
33545916cd2Sjpk */
33645916cd2Sjpk /*ARGSUSED*/
33745916cd2Sjpk static int
_dmap2str(devmap_t * dmp,char * buf,int size,const char * sep)3387e3e5701SJan Parcel _dmap2str(devmap_t *dmp, char *buf, int size, const char *sep)
33945916cd2Sjpk {
34045916cd2Sjpk int length;
34145916cd2Sjpk
34245916cd2Sjpk length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep);
34345916cd2Sjpk if (length >= size)
34445916cd2Sjpk return (-1);
34545916cd2Sjpk length += snprintf(buf + length, size - length, "%s%s",
34645916cd2Sjpk dmp->dmap_devtype, sep);
34745916cd2Sjpk if (length >= size)
34845916cd2Sjpk return (-1);
34945916cd2Sjpk length += snprintf(buf + length, size - length, "%s\n",
35045916cd2Sjpk dmp->dmap_devlist);
35145916cd2Sjpk if (length >= size)
35245916cd2Sjpk return (-1);
35345916cd2Sjpk return (0);
35445916cd2Sjpk }
35545916cd2Sjpk
35645916cd2Sjpk /*
35745916cd2Sjpk * _dmap2strentry -
35845916cd2Sjpk * calls dmap2str to break given devmap_t into printable entry.
35945916cd2Sjpk * returns pointer to decoded entry, NULL on error.
36045916cd2Sjpk */
36145916cd2Sjpk static strentry_t *
_dmap2strentry(devmap_t * devmapp)3627e3e5701SJan Parcel _dmap2strentry(devmap_t *devmapp)
36345916cd2Sjpk {
36445916cd2Sjpk strentry_t *sep;
36545916cd2Sjpk
36645916cd2Sjpk if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
36745916cd2Sjpk return (NULL);
3687e3e5701SJan Parcel if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str),
36945916cd2Sjpk KV_TOKEN_DELIMIT"\\\n\t") != 0) {
37045916cd2Sjpk free(sep);
37145916cd2Sjpk return (NULL);
37245916cd2Sjpk }
37345916cd2Sjpk return (sep);
37445916cd2Sjpk }
37545916cd2Sjpk
37645916cd2Sjpk /*
37745916cd2Sjpk * fix_optstr -
37845916cd2Sjpk * removes trailing ':' from buf.
37945916cd2Sjpk */
38045916cd2Sjpk void
fix_optstr(char * buf)38145916cd2Sjpk fix_optstr(char *buf)
38245916cd2Sjpk {
38345916cd2Sjpk char *p = NULL;
38445916cd2Sjpk
38545916cd2Sjpk if (p = rindex(buf, ':'))
38645916cd2Sjpk *p = ';';
38745916cd2Sjpk }
38845916cd2Sjpk
38945916cd2Sjpk /*
39045916cd2Sjpk * _da2str -
39145916cd2Sjpk * converts a device_allocate entry into a printable string
39245916cd2Sjpk * returns 0 on success, -1 on error.
39345916cd2Sjpk */
39445916cd2Sjpk static int
_da2str(da_args * dargs,devalloc_t * dap,char * buf,int size,const char * sep,const char * osep)39545916cd2Sjpk _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep,
39645916cd2Sjpk const char *osep)
39745916cd2Sjpk {
39845916cd2Sjpk int length;
39945916cd2Sjpk int matching_entry = 0;
40045916cd2Sjpk char **dnames;
40145916cd2Sjpk
40245916cd2Sjpk if (dargs->optflag & DA_UPDATE &&
40345916cd2Sjpk (dargs->optflag & DA_ADD_ZONE ||
40445916cd2Sjpk dargs->optflag & DA_REMOVE_ZONE) &&
40545916cd2Sjpk dargs->devnames) {
40645916cd2Sjpk for (dnames = dargs->devnames; *dnames != NULL; dnames++) {
40745916cd2Sjpk if (da_matchname(dap, *dnames)) {
40845916cd2Sjpk matching_entry = 1;
40945916cd2Sjpk break;
41045916cd2Sjpk }
41145916cd2Sjpk }
41245916cd2Sjpk }
41345916cd2Sjpk length = snprintf(buf, size, "%s%s", dap->da_devname, sep);
41445916cd2Sjpk if (length >= size)
41545916cd2Sjpk return (-1);
41645916cd2Sjpk length += snprintf(buf + length, size - length, "%s%s",
41745916cd2Sjpk dap->da_devtype, sep);
41845916cd2Sjpk if (length >= size)
41945916cd2Sjpk return (-1);
42045916cd2Sjpk if (matching_entry)
42145916cd2Sjpk _update_zonename(dargs, dap);
42245916cd2Sjpk if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) &&
42345916cd2Sjpk (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) {
42445916cd2Sjpk length += snprintf(buf + length, size - length, "%s%s",
42545916cd2Sjpk DA_RESERVED, sep);
42645916cd2Sjpk } else {
42745916cd2Sjpk if (_kva2str(dap->da_devopts, buf + length, size - length,
42845916cd2Sjpk KV_ASSIGN, (char *)osep) != 0)
42945916cd2Sjpk return (-1);
43045916cd2Sjpk length = strlen(buf);
43145916cd2Sjpk }
43245916cd2Sjpk if (dap->da_devopts)
43345916cd2Sjpk fix_optstr(buf);
43445916cd2Sjpk if (length >= size)
43545916cd2Sjpk return (-1);
43645916cd2Sjpk length += snprintf(buf + length, size - length, "%s%s",
43745916cd2Sjpk DA_RESERVED, sep);
43845916cd2Sjpk if (length >= size)
43945916cd2Sjpk return (-1);
44045916cd2Sjpk length += snprintf(buf + length, size - length, "%s%s",
44145916cd2Sjpk dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep);
44245916cd2Sjpk if (length >= size)
44345916cd2Sjpk return (-1);
44445916cd2Sjpk length += snprintf(buf + length, size - length, "%s\n",
44545916cd2Sjpk dap->da_devexec ? dap->da_devexec : "");
44645916cd2Sjpk if (length >= size)
44745916cd2Sjpk return (-1);
44845916cd2Sjpk
44945916cd2Sjpk return (0);
45045916cd2Sjpk }
45145916cd2Sjpk
45245916cd2Sjpk /*
45345916cd2Sjpk * _da2strentry -
45445916cd2Sjpk * calls da2str to break given devalloc_t into printable entry.
45545916cd2Sjpk * returns pointer to decoded entry, NULL on error.
45645916cd2Sjpk */
45745916cd2Sjpk static strentry_t *
_da2strentry(da_args * dargs,devalloc_t * dap)45845916cd2Sjpk _da2strentry(da_args *dargs, devalloc_t *dap)
45945916cd2Sjpk {
46045916cd2Sjpk strentry_t *sep;
46145916cd2Sjpk
46245916cd2Sjpk if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
46345916cd2Sjpk return (NULL);
46445916cd2Sjpk if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str),
46545916cd2Sjpk KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) {
46645916cd2Sjpk free(sep);
46745916cd2Sjpk return (NULL);
46845916cd2Sjpk }
46945916cd2Sjpk return (sep);
47045916cd2Sjpk }
47145916cd2Sjpk
47245916cd2Sjpk /*
47345916cd2Sjpk * _def2str
47445916cd2Sjpk * converts da_defs_t into a printable string.
47545916cd2Sjpk * returns 0 on success, -1 on error.
47645916cd2Sjpk */
47745916cd2Sjpk static int
_def2str(da_defs_t * da_defs,char * buf,int size,const char * sep)47845916cd2Sjpk _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep)
47945916cd2Sjpk {
48045916cd2Sjpk int length;
48145916cd2Sjpk
48245916cd2Sjpk length = snprintf(buf, size, "%s%s", da_defs->devtype, sep);
48345916cd2Sjpk if (length >= size)
48445916cd2Sjpk return (-1);
48545916cd2Sjpk if (da_defs->devopts) {
48645916cd2Sjpk if (_kva2str(da_defs->devopts, buf + length, size - length,
48745916cd2Sjpk KV_ASSIGN, KV_DELIMITER) != 0)
48845916cd2Sjpk return (-1);
48945916cd2Sjpk length = strlen(buf);
49045916cd2Sjpk }
49145916cd2Sjpk if (length >= size)
49245916cd2Sjpk return (-1);
49345916cd2Sjpk
49445916cd2Sjpk return (0);
49545916cd2Sjpk }
49645916cd2Sjpk
49745916cd2Sjpk /*
49845916cd2Sjpk * _def2strentry
49945916cd2Sjpk * calls _def2str to break given da_defs_t into printable entry.
50045916cd2Sjpk * returns pointer decoded entry, NULL on error.
50145916cd2Sjpk */
50245916cd2Sjpk static strentry_t *
_def2strentry(da_defs_t * da_defs)50345916cd2Sjpk _def2strentry(da_defs_t *da_defs)
50445916cd2Sjpk {
50545916cd2Sjpk strentry_t *sep;
50645916cd2Sjpk
50745916cd2Sjpk if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
50845916cd2Sjpk return (NULL);
50945916cd2Sjpk if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str),
51045916cd2Sjpk KV_TOKEN_DELIMIT) != 0) {
51145916cd2Sjpk free(sep);
51245916cd2Sjpk return (NULL);
51345916cd2Sjpk }
51445916cd2Sjpk
51545916cd2Sjpk return (sep);
51645916cd2Sjpk }
51745916cd2Sjpk
51845916cd2Sjpk /*
51945916cd2Sjpk * _build_defattrs
52045916cd2Sjpk * cycles through all defattr entries, stores them in memory. removes
52145916cd2Sjpk * entries with the given search_key (device type).
52245916cd2Sjpk * returns 0 if given entry not found, 1 if given entry removed, 2 on
52345916cd2Sjpk * error.
52445916cd2Sjpk */
52545916cd2Sjpk static int
_build_defattrs(da_args * dargs,strentry_t ** head_defent)52645916cd2Sjpk _build_defattrs(da_args *dargs, strentry_t **head_defent)
52745916cd2Sjpk {
52845916cd2Sjpk int rc = 0;
52945916cd2Sjpk da_defs_t *da_defs;
53045916cd2Sjpk strentry_t *tail_str, *tmp_str;
53145916cd2Sjpk
53245916cd2Sjpk setdadefent();
53345916cd2Sjpk while ((da_defs = getdadefent()) != NULL) {
53445916cd2Sjpk rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype));
53545916cd2Sjpk if (rc && dargs->optflag & DA_ADD &&
53645916cd2Sjpk !(dargs->optflag & DA_FORCE)) {
53745916cd2Sjpk /*
53845916cd2Sjpk * During DA_ADD, we keep an existing entry unless
53945916cd2Sjpk * we have DA_FORCE set to override that entry.
54045916cd2Sjpk */
54145916cd2Sjpk dargs->optflag |= DA_NO_OVERRIDE;
54245916cd2Sjpk rc = 0;
54345916cd2Sjpk }
54445916cd2Sjpk if (rc == 0) {
54545916cd2Sjpk tmp_str = _def2strentry(da_defs);
54645916cd2Sjpk if (tmp_str == NULL) {
54745916cd2Sjpk freedadefent(da_defs);
54845916cd2Sjpk enddadefent();
54945916cd2Sjpk return (2);
55045916cd2Sjpk }
55145916cd2Sjpk /* retaining defattr entry: tmp_str->se_str */
55245916cd2Sjpk tmp_str->se_next = NULL;
55345916cd2Sjpk if (*head_defent == NULL) {
55445916cd2Sjpk *head_defent = tail_str = tmp_str;
55545916cd2Sjpk } else {
55645916cd2Sjpk tail_str->se_next = tmp_str;
55745916cd2Sjpk tail_str = tmp_str;
55845916cd2Sjpk }
55945916cd2Sjpk }
56045916cd2Sjpk freedadefent(da_defs);
56145916cd2Sjpk }
56245916cd2Sjpk enddadefent();
56345916cd2Sjpk
56445916cd2Sjpk return (rc);
56545916cd2Sjpk }
56645916cd2Sjpk
56745916cd2Sjpk /*
568*399f0677SJan Parcel * We have to handle the "standard" types in devlist differently than
569*399f0677SJan Parcel * other devices, which are not covered by our auto-naming conventions.
570*399f0677SJan Parcel *
571*399f0677SJan Parcel * buf must be a buffer of size DA_MAX_NAME + 1
572*399f0677SJan Parcel */
573*399f0677SJan Parcel int
da_std_type(da_args * dargs,char * namebuf)574*399f0677SJan Parcel da_std_type(da_args *dargs, char *namebuf)
575*399f0677SJan Parcel {
576*399f0677SJan Parcel char *type = dargs->devinfo->devtype;
577*399f0677SJan Parcel int system_labeled;
578*399f0677SJan Parcel
579*399f0677SJan Parcel system_labeled = is_system_labeled();
580*399f0677SJan Parcel
581*399f0677SJan Parcel /* check safely for sizes */
582*399f0677SJan Parcel if (strcmp(DA_AUDIO_TYPE, type) == 0) {
583*399f0677SJan Parcel (void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME);
584*399f0677SJan Parcel return (1);
585*399f0677SJan Parcel }
586*399f0677SJan Parcel if (strcmp(DA_CD_TYPE, type) == 0) {
587*399f0677SJan Parcel if (system_labeled)
588*399f0677SJan Parcel (void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME);
589*399f0677SJan Parcel else
590*399f0677SJan Parcel (void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME);
591*399f0677SJan Parcel return (1);
592*399f0677SJan Parcel }
593*399f0677SJan Parcel if (strcmp(DA_FLOPPY_TYPE, type) == 0) {
594*399f0677SJan Parcel if (system_labeled)
595*399f0677SJan Parcel (void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME);
596*399f0677SJan Parcel else
597*399f0677SJan Parcel (void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME);
598*399f0677SJan Parcel return (1);
599*399f0677SJan Parcel }
600*399f0677SJan Parcel if (strcmp(DA_TAPE_TYPE, type) == 0) {
601*399f0677SJan Parcel if (system_labeled)
602*399f0677SJan Parcel (void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME);
603*399f0677SJan Parcel else
604*399f0677SJan Parcel (void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME);
605*399f0677SJan Parcel return (1);
606*399f0677SJan Parcel }
607*399f0677SJan Parcel if (strcmp(DA_RMDISK_TYPE, type) == 0) {
608*399f0677SJan Parcel (void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME);
609*399f0677SJan Parcel return (1);
610*399f0677SJan Parcel }
611*399f0677SJan Parcel namebuf[0] = '\0';
612*399f0677SJan Parcel return (0);
613*399f0677SJan Parcel }
614*399f0677SJan Parcel
615*399f0677SJan Parcel /*
616*399f0677SJan Parcel * allocatable: returns
617*399f0677SJan Parcel * -1 if no auths field,
618*399f0677SJan Parcel * 0 if not allocatable (marked '*')
619*399f0677SJan Parcel * 1 if not marked '*'
620*399f0677SJan Parcel */
621*399f0677SJan Parcel static int
allocatable(da_args * dargs)622*399f0677SJan Parcel allocatable(da_args *dargs)
623*399f0677SJan Parcel {
624*399f0677SJan Parcel
625*399f0677SJan Parcel if (!dargs->devinfo->devauths)
626*399f0677SJan Parcel return (-1);
627*399f0677SJan Parcel if (strcmp("*", dargs->devinfo->devauths) == 0)
628*399f0677SJan Parcel return (0);
629*399f0677SJan Parcel return (1);
630*399f0677SJan Parcel }
631*399f0677SJan Parcel
632*399f0677SJan Parcel /*
6337e3e5701SJan Parcel * _rebuild_lists -
6347e3e5701SJan Parcel *
6357e3e5701SJan Parcel * If dargs->optflag & DA_EVENT, does not assume the dargs list is
6367e3e5701SJan Parcel * complete or completely believable, since devfsadm caches
6377e3e5701SJan Parcel * ONLY what it has been exposed to via syseventd.
6387e3e5701SJan Parcel *
6397e3e5701SJan Parcel * Cycles through all the entries in the /etc files, stores them
640*399f0677SJan Parcel * in memory, takes note of device->dname numbers (e.g. rmdisk0,
6417e3e5701SJan Parcel * rmdisk12)
6427e3e5701SJan Parcel *
6437e3e5701SJan Parcel * Cycles through again, adds dargs entry
6447e3e5701SJan Parcel * with the name tname%d (lowest unused number for the device type)
6457e3e5701SJan Parcel * to the list of things for the caller to write out to a file,
6467e3e5701SJan Parcel * IFF it is a new entry.
6477e3e5701SJan Parcel *
648*399f0677SJan Parcel * It is an error for it to already be there, if it is allocatable.
6497e3e5701SJan Parcel *
6507e3e5701SJan Parcel * Add:
6517e3e5701SJan Parcel * Returns 0 if successful and 2 on error.
6527e3e5701SJan Parcel * Remove:
6537e3e5701SJan Parcel * Returns 0 if not found, 1 if found, 2 on error.
6547e3e5701SJan Parcel */
6557e3e5701SJan Parcel static int
_rebuild_lists(da_args * dargs,strentry_t ** head_devallocp,strentry_t ** head_devmapp)6567e3e5701SJan Parcel _rebuild_lists(da_args *dargs, strentry_t **head_devallocp,
6577e3e5701SJan Parcel strentry_t **head_devmapp)
6587e3e5701SJan Parcel {
6597e3e5701SJan Parcel int rc = 0;
6607e3e5701SJan Parcel devalloc_t *devallocp;
6617e3e5701SJan Parcel devmap_t *devmapp;
6627e3e5701SJan Parcel strentry_t *tail_str;
6637e3e5701SJan Parcel strentry_t *tmp_str;
6647e3e5701SJan Parcel uint64_t tmp_bitmap = 0;
665*399f0677SJan Parcel uint_t tmp = 0;
6667e3e5701SJan Parcel char *realname;
667*399f0677SJan Parcel int suffix;
6687e3e5701SJan Parcel int found = 0;
669*399f0677SJan Parcel int stdtype = 1;
670*399f0677SJan Parcel int is_allocatable = 1;
671*399f0677SJan Parcel char new_devname[DA_MAXNAME + 1];
672*399f0677SJan Parcel char defname[DA_MAXNAME + 1]; /* default name for type */
673*399f0677SJan Parcel char errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
6747e3e5701SJan Parcel
6757e3e5701SJan Parcel if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
6767e3e5701SJan Parcel return (2);
6777e3e5701SJan Parcel
6787e3e5701SJan Parcel if (dargs->optflag & DA_FORCE)
6797e3e5701SJan Parcel return (2);
6807e3e5701SJan Parcel
681*399f0677SJan Parcel if (dargs->optflag & DA_ADD) {
682*399f0677SJan Parcel stdtype = da_std_type(dargs, defname);
683*399f0677SJan Parcel is_allocatable = allocatable(dargs);
684*399f0677SJan Parcel }
685*399f0677SJan Parcel
6867e3e5701SJan Parcel /* read both files, maps first so we can compare actual devices */
6877e3e5701SJan Parcel
6887e3e5701SJan Parcel /* build device_maps */
6897e3e5701SJan Parcel setdmapent();
6907e3e5701SJan Parcel while ((devmapp = getdmapent()) != NULL) {
691*399f0677SJan Parcel suffix = DA_MAX_DEVNO + 1;
6927e3e5701SJan Parcel if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
6937e3e5701SJan Parcel == 1) {
6947e3e5701SJan Parcel if (dargs->optflag & DA_REMOVE) {
6957e3e5701SJan Parcel if ((devmapp->dmap_devarray == NULL) ||
6967e3e5701SJan Parcel (devmapp->dmap_devarray[0] == NULL)) {
6977e3e5701SJan Parcel freedmapent(devmapp);
6987e3e5701SJan Parcel enddmapent();
6997e3e5701SJan Parcel return (2);
7007e3e5701SJan Parcel }
7017e3e5701SJan Parcel realname = dmap_physname(devmapp);
7027e3e5701SJan Parcel if (realname == NULL) {
7037e3e5701SJan Parcel freedmapent(devmapp);
7047e3e5701SJan Parcel enddmapent();
7057e3e5701SJan Parcel return (2);
7067e3e5701SJan Parcel }
7077e3e5701SJan Parcel if (strstr(realname, dargs->devinfo->devlist)
7087e3e5701SJan Parcel != NULL) {
709*399f0677SJan Parcel /* if need to free and safe to free */
710*399f0677SJan Parcel if (dargs->devinfo->devname != NULL &&
711*399f0677SJan Parcel (dargs->optflag & DA_EVENT) != 0)
7127e3e5701SJan Parcel free(dargs->devinfo->devname);
7137e3e5701SJan Parcel dargs->devinfo->devname =
7147e3e5701SJan Parcel strdup(devmapp->dmap_devname);
7157e3e5701SJan Parcel found = 1;
7167e3e5701SJan Parcel freedmapent(devmapp);
7177e3e5701SJan Parcel continue; /* don't retain */
7187e3e5701SJan Parcel }
7197e3e5701SJan Parcel } else if (dargs->optflag & DA_ADD) {
7207e3e5701SJan Parcel /*
7217e3e5701SJan Parcel * Need to know which suffixes are in use
7227e3e5701SJan Parcel */
7237e3e5701SJan Parcel rc = (dmap_exact_dev(devmapp,
7247e3e5701SJan Parcel dargs->devinfo->devlist, &suffix));
7257e3e5701SJan Parcel
7267e3e5701SJan Parcel if (rc == 0) {
7277e3e5701SJan Parcel /*
7287e3e5701SJan Parcel * Same type, different device. Record
729*399f0677SJan Parcel * device suffix already in use, if
730*399f0677SJan Parcel * applicable.
7317e3e5701SJan Parcel */
732*399f0677SJan Parcel if ((suffix < DA_MAX_DEVNO &&
733*399f0677SJan Parcel suffix != -1) && stdtype)
734*399f0677SJan Parcel tmp_bitmap |=
735*399f0677SJan Parcel (uint64_t)(1LL << suffix);
736*399f0677SJan Parcel } else if ((rc == 1) && !is_allocatable) {
737*399f0677SJan Parcel rc = 0;
7387e3e5701SJan Parcel } else {
7397e3e5701SJan Parcel /*
740*399f0677SJan Parcel * Match allocatable on add is an error
7417e3e5701SJan Parcel * or mapping attempt returned error
7427e3e5701SJan Parcel */
743*399f0677SJan Parcel (void) snprintf(errmsg, sizeof (errmsg),
744*399f0677SJan Parcel "Cannot add %s on node %s",
745*399f0677SJan Parcel dargs->devinfo->devtype,
746*399f0677SJan Parcel devmapp->dmap_devname);
747*399f0677SJan Parcel syslog(LOG_ERR, "%s", errmsg);
7487e3e5701SJan Parcel freedmapent(devmapp);
7497e3e5701SJan Parcel enddmapent();
7507e3e5701SJan Parcel return (2);
7517e3e5701SJan Parcel }
7527e3e5701SJan Parcel } else
7537e3e5701SJan Parcel /* add other transaction types as needed */
7547e3e5701SJan Parcel return (2);
755*399f0677SJan Parcel } else if ((dargs->optflag & DA_ADD) &&
756*399f0677SJan Parcel (stdtype || is_allocatable) &&
757*399f0677SJan Parcel dmap_exact_dev(devmapp, dargs->devinfo->devlist,
758*399f0677SJan Parcel &suffix)) {
759*399f0677SJan Parcel /*
760*399f0677SJan Parcel * no dups w/o DA_FORCE, even if type differs,
761*399f0677SJan Parcel * if there is a chance this operation is
762*399f0677SJan Parcel * machine-driven. The 5 "standard types"
763*399f0677SJan Parcel * can be machine-driven adds, and tend to
764*399f0677SJan Parcel * be allocatable.
765*399f0677SJan Parcel */
766*399f0677SJan Parcel (void) snprintf(errmsg, sizeof (errmsg),
767*399f0677SJan Parcel "Cannot add %s on node %s type %s",
768*399f0677SJan Parcel dargs->devinfo->devtype,
769*399f0677SJan Parcel devmapp->dmap_devname,
770*399f0677SJan Parcel devmapp->dmap_devtype);
771*399f0677SJan Parcel syslog(LOG_ERR, "%s", errmsg);
772*399f0677SJan Parcel freedmapent(devmapp);
773*399f0677SJan Parcel enddmapent();
774*399f0677SJan Parcel return (2);
775*399f0677SJan Parcel }
7767e3e5701SJan Parcel
7777e3e5701SJan Parcel tmp_str = _dmap2strentry(devmapp);
7787e3e5701SJan Parcel if (tmp_str == NULL) {
7797e3e5701SJan Parcel freedmapent(devmapp);
7807e3e5701SJan Parcel enddmapent();
7817e3e5701SJan Parcel return (2);
7827e3e5701SJan Parcel }
7837e3e5701SJan Parcel /* retaining devmap entry: tmp_str->se_str */
7847e3e5701SJan Parcel tmp_str->se_next = NULL;
7857e3e5701SJan Parcel if (*head_devmapp == NULL) {
7867e3e5701SJan Parcel *head_devmapp = tail_str = tmp_str;
7877e3e5701SJan Parcel } else {
7887e3e5701SJan Parcel tail_str->se_next = tmp_str;
7897e3e5701SJan Parcel tail_str = tmp_str;
7907e3e5701SJan Parcel }
7917e3e5701SJan Parcel freedmapent(devmapp);
7927e3e5701SJan Parcel }
7937e3e5701SJan Parcel enddmapent();
7947e3e5701SJan Parcel
7957e3e5701SJan Parcel /*
7967e3e5701SJan Parcel * No need to rewrite the files if the item to be removed is not
7977e3e5701SJan Parcel * in the files -- wait for another call on another darg.
7987e3e5701SJan Parcel */
7997e3e5701SJan Parcel if ((dargs->optflag & DA_REMOVE) && !found)
8007e3e5701SJan Parcel return (0);
8017e3e5701SJan Parcel
8027e3e5701SJan Parcel
8037e3e5701SJan Parcel if (dargs->optflag & DA_ADD) {
804*399f0677SJan Parcel int len;
8057e3e5701SJan Parcel /*
806*399f0677SJan Parcel * If we got here from an event, or from devfsadm,
807*399f0677SJan Parcel * we know the stored devname is a useless guess,
808*399f0677SJan Parcel * since the files had not been read when the name
809*399f0677SJan Parcel * was chosen, and we don't keep them anywhere else
810*399f0677SJan Parcel * that is sufficiently definitive.
8117e3e5701SJan Parcel */
8127e3e5701SJan Parcel
8137e3e5701SJan Parcel for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
8147e3e5701SJan Parcel if (!(tmp_bitmap & (1LL << tmp)))
8157e3e5701SJan Parcel break;
8167e3e5701SJan Parcel /* Future: support more than 64 hotplug devices per type? */
8177e3e5701SJan Parcel if (tmp > DA_MAX_DEVNO)
8187e3e5701SJan Parcel return (2);
8197e3e5701SJan Parcel
820*399f0677SJan Parcel /*
821*399f0677SJan Parcel * Let the caller choose the name unless BOTH the name and
822*399f0677SJan Parcel * device type one of: cdrom, floppy, audio, rmdisk, or tape.
823*399f0677SJan Parcel * (or sr, fd for unlabeled)
824*399f0677SJan Parcel */
825*399f0677SJan Parcel len = strlen(defname);
826*399f0677SJan Parcel if (stdtype &&
827*399f0677SJan Parcel (strncmp(dargs->devinfo->devname, defname, len) == 0)) {
8287e3e5701SJan Parcel (void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
829*399f0677SJan Parcel defname, tmp);
830*399f0677SJan Parcel /* if need to free and safe to free */
831*399f0677SJan Parcel if (dargs->devinfo->devname != NULL &&
832*399f0677SJan Parcel (dargs->optflag & DA_EVENT) != 0)
8337e3e5701SJan Parcel free(dargs->devinfo->devname);
8347e3e5701SJan Parcel dargs->devinfo->devname = strdup(new_devname);
8357e3e5701SJan Parcel }
836*399f0677SJan Parcel }
8377e3e5701SJan Parcel
8387e3e5701SJan Parcel /*
8397e3e5701SJan Parcel * Now adjust devalloc list to match devmaps
8407e3e5701SJan Parcel * Note we now have the correct devname for da_match to use.
8417e3e5701SJan Parcel */
8427e3e5701SJan Parcel setdaent();
8437e3e5701SJan Parcel while ((devallocp = getdaent()) != NULL) {
8447e3e5701SJan Parcel rc = da_match(devallocp, dargs);
8457e3e5701SJan Parcel if (rc == 1) {
8467e3e5701SJan Parcel if (dargs->optflag & DA_ADD) {
8477e3e5701SJan Parcel /* logging is on if DA_EVENT is set */
8487e3e5701SJan Parcel if (dargs->optflag & DA_EVENT) {
8497e3e5701SJan Parcel (void) snprintf(errmsg, sizeof (errmsg),
8507e3e5701SJan Parcel "%s and %s out of sync,"
8517e3e5701SJan Parcel "%s only in %s.",
8527e3e5701SJan Parcel DEVALLOC, DEVMAP,
8537e3e5701SJan Parcel devallocp->da_devname, DEVALLOC);
8547e3e5701SJan Parcel syslog(LOG_ERR, "%s", errmsg);
8557e3e5701SJan Parcel }
8567e3e5701SJan Parcel freedaent(devallocp);
8577e3e5701SJan Parcel enddaent();
8587e3e5701SJan Parcel return (2);
8597e3e5701SJan Parcel } else if (dargs->optflag & DA_REMOVE) {
8607e3e5701SJan Parcel /* make list w/o this entry */
8617e3e5701SJan Parcel freedaent(devallocp);
8627e3e5701SJan Parcel continue;
8637e3e5701SJan Parcel }
8647e3e5701SJan Parcel }
8657e3e5701SJan Parcel tmp_str = _da2strentry(dargs, devallocp);
8667e3e5701SJan Parcel if (tmp_str == NULL) {
8677e3e5701SJan Parcel freedaent(devallocp);
8687e3e5701SJan Parcel enddaent();
8697e3e5701SJan Parcel return (2);
8707e3e5701SJan Parcel }
8717e3e5701SJan Parcel /* retaining devalloc entry: tmp_str->se_str */
8727e3e5701SJan Parcel tmp_str->se_next = NULL;
8737e3e5701SJan Parcel if (*head_devallocp == NULL) {
8747e3e5701SJan Parcel *head_devallocp = tail_str = tmp_str;
8757e3e5701SJan Parcel } else {
8767e3e5701SJan Parcel tail_str->se_next = tmp_str;
8777e3e5701SJan Parcel tail_str = tmp_str;
8787e3e5701SJan Parcel }
8797e3e5701SJan Parcel freedaent(devallocp);
8807e3e5701SJan Parcel }
8817e3e5701SJan Parcel enddaent();
8827e3e5701SJan Parcel
8837e3e5701SJan Parcel /* the caller needs to know if a remove needs to rewrite files */
8847e3e5701SJan Parcel if (dargs->optflag & DA_REMOVE)
8857e3e5701SJan Parcel return (1); /* 0 and 2 cases returned earlier */
8867e3e5701SJan Parcel
8877e3e5701SJan Parcel return (0); /* Successful DA_ADD */
8887e3e5701SJan Parcel }
889*399f0677SJan Parcel
8907e3e5701SJan Parcel /*
89145916cd2Sjpk * _build_lists -
8927e3e5701SJan Parcel * Cycles through all the entries, stores them in memory. removes entries
89345916cd2Sjpk * with the given search_key (device name or type).
89445916cd2Sjpk * returns 0 if given entry not found, 1 if given entry removed, 2 on
89545916cd2Sjpk * error.
89645916cd2Sjpk */
89745916cd2Sjpk static int
_build_lists(da_args * dargs,strentry_t ** head_devallocp,strentry_t ** head_devmapp)89845916cd2Sjpk _build_lists(da_args *dargs, strentry_t **head_devallocp,
89945916cd2Sjpk strentry_t **head_devmapp)
90045916cd2Sjpk {
90145916cd2Sjpk int rc = 0;
902*399f0677SJan Parcel int found = 0;
90345916cd2Sjpk devalloc_t *devallocp;
90445916cd2Sjpk devmap_t *devmapp;
90545916cd2Sjpk strentry_t *tail_str;
90645916cd2Sjpk strentry_t *tmp_str;
90745916cd2Sjpk
90845916cd2Sjpk if (dargs->optflag & DA_MAPS_ONLY)
90945916cd2Sjpk goto dmap_only;
91045916cd2Sjpk
91145916cd2Sjpk /* build device_allocate */
91245916cd2Sjpk setdaent();
91345916cd2Sjpk while ((devallocp = getdaent()) != NULL) {
91445916cd2Sjpk rc = da_match(devallocp, dargs);
915*399f0677SJan Parcel /* if in _build_lists and DA_ADD is set, so is DA_FORCE */
91645916cd2Sjpk if (rc == 0) {
91745916cd2Sjpk tmp_str = _da2strentry(dargs, devallocp);
91845916cd2Sjpk if (tmp_str == NULL) {
91945916cd2Sjpk freedaent(devallocp);
92045916cd2Sjpk enddaent();
92145916cd2Sjpk return (2);
92245916cd2Sjpk }
92345916cd2Sjpk /* retaining devalloc entry: tmp_str->se_str */
92445916cd2Sjpk tmp_str->se_next = NULL;
92545916cd2Sjpk if (*head_devallocp == NULL) {
92645916cd2Sjpk *head_devallocp = tail_str = tmp_str;
92745916cd2Sjpk } else {
92845916cd2Sjpk tail_str->se_next = tmp_str;
92945916cd2Sjpk tail_str = tmp_str;
93045916cd2Sjpk }
931*399f0677SJan Parcel } else if (rc == 1)
932*399f0677SJan Parcel found = 1;
933*399f0677SJan Parcel
93445916cd2Sjpk freedaent(devallocp);
93545916cd2Sjpk }
93645916cd2Sjpk enddaent();
93745916cd2Sjpk
93845916cd2Sjpk dmap_only:
93945916cd2Sjpk if (dargs->optflag & DA_ALLOC_ONLY)
94045916cd2Sjpk return (rc);
94145916cd2Sjpk
94245916cd2Sjpk /* build device_maps */
94345916cd2Sjpk rc = 0;
94445916cd2Sjpk setdmapent();
94545916cd2Sjpk while ((devmapp = getdmapent()) != NULL) {
94645916cd2Sjpk rc = dm_match(devmapp, dargs);
94745916cd2Sjpk if (rc == 0) {
9487e3e5701SJan Parcel tmp_str = _dmap2strentry(devmapp);
94945916cd2Sjpk if (tmp_str == NULL) {
95045916cd2Sjpk freedmapent(devmapp);
95145916cd2Sjpk enddmapent();
95245916cd2Sjpk return (2);
95345916cd2Sjpk }
95445916cd2Sjpk /* retaining devmap entry: tmp_str->se_str */
95545916cd2Sjpk tmp_str->se_next = NULL;
95645916cd2Sjpk if (*head_devmapp == NULL) {
95745916cd2Sjpk *head_devmapp = tail_str = tmp_str;
95845916cd2Sjpk } else {
95945916cd2Sjpk tail_str->se_next = tmp_str;
96045916cd2Sjpk tail_str = tmp_str;
96145916cd2Sjpk }
96245916cd2Sjpk }
96345916cd2Sjpk freedmapent(devmapp);
96445916cd2Sjpk }
96545916cd2Sjpk enddmapent();
96645916cd2Sjpk
967*399f0677SJan Parcel /* later code cleanup may cause the use of "found" in other cases */
968*399f0677SJan Parcel if (dargs->optflag & DA_REMOVE)
969*399f0677SJan Parcel return (found);
97045916cd2Sjpk return (rc);
97145916cd2Sjpk }
97245916cd2Sjpk
97345916cd2Sjpk /*
97445916cd2Sjpk * _write_defattrs
97545916cd2Sjpk * writes current entries to devalloc_defaults.
97645916cd2Sjpk */
97745916cd2Sjpk static void
_write_defattrs(FILE * fp,strentry_t * head_defent)97845916cd2Sjpk _write_defattrs(FILE *fp, strentry_t *head_defent)
97945916cd2Sjpk {
98045916cd2Sjpk strentry_t *tmp_str;
98145916cd2Sjpk
98245916cd2Sjpk for (tmp_str = head_defent; tmp_str != NULL;
98345916cd2Sjpk tmp_str = tmp_str->se_next) {
98445916cd2Sjpk (void) fputs(tmp_str->se_str, fp);
98545916cd2Sjpk (void) fputs("\n", fp);
98645916cd2Sjpk }
98745916cd2Sjpk
98845916cd2Sjpk }
98945916cd2Sjpk
99045916cd2Sjpk /*
99145916cd2Sjpk * _write_device_allocate -
99245916cd2Sjpk * writes current entries in the list to device_allocate.
9937e3e5701SJan Parcel * frees the strings
99445916cd2Sjpk */
99545916cd2Sjpk static void
_write_device_allocate(char * odevalloc,FILE * dafp,strentry_t * head_devallocp)99645916cd2Sjpk _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
99745916cd2Sjpk {
99845916cd2Sjpk int is_on = -1;
9997e3e5701SJan Parcel strentry_t *tmp_str, *old_str;
100045916cd2Sjpk struct stat dastat;
100145916cd2Sjpk
100245916cd2Sjpk (void) fseek(dafp, (off_t)0, SEEK_SET);
100345916cd2Sjpk
100445916cd2Sjpk /*
100545916cd2Sjpk * if the devalloc on/off string existed before,
100645916cd2Sjpk * put it back before anything else.
100745916cd2Sjpk * we need to check for the string only if the file
100845916cd2Sjpk * exists.
100945916cd2Sjpk */
101045916cd2Sjpk if (stat(odevalloc, &dastat) == 0) {
101145916cd2Sjpk is_on = da_is_on();
101245916cd2Sjpk if (is_on == 0)
101345916cd2Sjpk (void) fputs(DA_OFF_STR, dafp);
101445916cd2Sjpk else if (is_on == 1)
101545916cd2Sjpk (void) fputs(DA_ON_STR, dafp);
101645916cd2Sjpk }
101745916cd2Sjpk tmp_str = head_devallocp;
101845916cd2Sjpk while (tmp_str) {
101945916cd2Sjpk (void) fputs(tmp_str->se_str, dafp);
102045916cd2Sjpk (void) fputs("\n", dafp);
10217e3e5701SJan Parcel old_str = tmp_str;
102245916cd2Sjpk tmp_str = tmp_str->se_next;
10237e3e5701SJan Parcel free(old_str);
102445916cd2Sjpk }
102545916cd2Sjpk }
102645916cd2Sjpk
102745916cd2Sjpk /*
102845916cd2Sjpk * _write_device_maps -
102945916cd2Sjpk * writes current entries in the list to device_maps.
10307e3e5701SJan Parcel * and frees the strings
103145916cd2Sjpk */
103245916cd2Sjpk static void
_write_device_maps(FILE * dmfp,strentry_t * head_devmapp)103345916cd2Sjpk _write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
103445916cd2Sjpk {
10357e3e5701SJan Parcel strentry_t *tmp_str, *old_str;
103645916cd2Sjpk
103745916cd2Sjpk (void) fseek(dmfp, (off_t)0, SEEK_SET);
103845916cd2Sjpk
103945916cd2Sjpk tmp_str = head_devmapp;
104045916cd2Sjpk while (tmp_str) {
104145916cd2Sjpk (void) fputs(tmp_str->se_str, dmfp);
104245916cd2Sjpk (void) fputs("\n", dmfp);
10437e3e5701SJan Parcel old_str = tmp_str;
104445916cd2Sjpk tmp_str = tmp_str->se_next;
10457e3e5701SJan Parcel free(old_str);
104645916cd2Sjpk }
104745916cd2Sjpk }
104845916cd2Sjpk
104945916cd2Sjpk /*
105045916cd2Sjpk * _write_new_defattrs
105145916cd2Sjpk * writes the new entry to devalloc_defaults.
105245916cd2Sjpk * returns 0 on success, -1 on error.
105345916cd2Sjpk */
105445916cd2Sjpk static int
_write_new_defattrs(FILE * fp,da_args * dargs)105545916cd2Sjpk _write_new_defattrs(FILE *fp, da_args *dargs)
105645916cd2Sjpk {
105745916cd2Sjpk int count;
105845916cd2Sjpk char *tok = NULL, *tokp = NULL;
105945916cd2Sjpk char *lasts;
106045916cd2Sjpk devinfo_t *devinfo = dargs->devinfo;
106145916cd2Sjpk
106245916cd2Sjpk if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
106345916cd2Sjpk return (-1);
106445916cd2Sjpk if (!devinfo->devopts)
106545916cd2Sjpk return (0);
106645916cd2Sjpk (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
106745916cd2Sjpk KV_TOKEN_DELIMIT);
10687e3e5701SJan Parcel if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) {
106945916cd2Sjpk (void) strcpy(tokp, devinfo->devopts);
107045916cd2Sjpk if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
107145916cd2Sjpk (void) fprintf(fp, "%s", tok);
107245916cd2Sjpk count = 1;
107345916cd2Sjpk }
107445916cd2Sjpk while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
107545916cd2Sjpk if (count)
107645916cd2Sjpk (void) fprintf(fp, "%s", KV_DELIMITER);
107745916cd2Sjpk (void) fprintf(fp, "%s", tok);
107845916cd2Sjpk count++;
107945916cd2Sjpk }
108045916cd2Sjpk } else {
108145916cd2Sjpk (void) fprintf(fp, "%s", devinfo->devopts);
108245916cd2Sjpk }
108345916cd2Sjpk
108445916cd2Sjpk return (0);
108545916cd2Sjpk }
108645916cd2Sjpk
108745916cd2Sjpk /*
108845916cd2Sjpk * _write_new_entry -
108945916cd2Sjpk * writes the new devalloc_t to device_allocate or the new devmap_t to
109045916cd2Sjpk * device_maps.
109145916cd2Sjpk * returns 0 on success, -1 on error.
109245916cd2Sjpk */
109345916cd2Sjpk static int
_write_new_entry(FILE * fp,da_args * dargs,int flag)109445916cd2Sjpk _write_new_entry(FILE *fp, da_args *dargs, int flag)
109545916cd2Sjpk {
109645916cd2Sjpk int count;
109745916cd2Sjpk char *tok = NULL, *tokp = NULL;
109845916cd2Sjpk char *lasts;
109945916cd2Sjpk devinfo_t *devinfo = dargs->devinfo;
110045916cd2Sjpk
110145916cd2Sjpk if (flag & DA_MAPS_ONLY)
110245916cd2Sjpk goto dmap_only;
110345916cd2Sjpk
110445916cd2Sjpk if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
110545916cd2Sjpk return (-1);
110645916cd2Sjpk
110745916cd2Sjpk (void) fprintf(fp, "%s%s\\\n\t",
110845916cd2Sjpk (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
110945916cd2Sjpk (void) fprintf(fp, "%s%s\\\n\t",
111045916cd2Sjpk (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
111145916cd2Sjpk if (devinfo->devopts == NULL) {
111245916cd2Sjpk (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
111345916cd2Sjpk KV_DELIMITER);
111445916cd2Sjpk } else {
11157e3e5701SJan Parcel if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1))
11167e3e5701SJan Parcel != NULL) {
111745916cd2Sjpk (void) strcpy(tokp, devinfo->devopts);
111845916cd2Sjpk if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
111945916cd2Sjpk NULL) {
112045916cd2Sjpk (void) fprintf(fp, "%s", tok);
112145916cd2Sjpk count = 1;
112245916cd2Sjpk }
112345916cd2Sjpk while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
112445916cd2Sjpk &lasts)) != NULL) {
112545916cd2Sjpk if (count)
112645916cd2Sjpk (void) fprintf(fp, "%s",
112745916cd2Sjpk KV_TOKEN_DELIMIT "\\\n\t");
112845916cd2Sjpk (void) fprintf(fp, "%s", tok);
112945916cd2Sjpk count++;
113045916cd2Sjpk }
113145916cd2Sjpk if (count)
113245916cd2Sjpk (void) fprintf(fp, "%s",
113345916cd2Sjpk KV_DELIMITER "\\\n\t");
113445916cd2Sjpk } else {
113545916cd2Sjpk (void) fprintf(fp, "%s%s", devinfo->devopts,
113645916cd2Sjpk KV_DELIMITER "\\\n\t");
113745916cd2Sjpk }
113845916cd2Sjpk }
113945916cd2Sjpk (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
114045916cd2Sjpk (void) fprintf(fp, "%s%s\\\n\t",
114145916cd2Sjpk (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
114245916cd2Sjpk KV_DELIMITER);
114345916cd2Sjpk (void) fprintf(fp, "%s\n",
114445916cd2Sjpk (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
114545916cd2Sjpk
114645916cd2Sjpk dmap_only:
114745916cd2Sjpk if (flag & DA_ALLOC_ONLY)
114845916cd2Sjpk return (0);
114945916cd2Sjpk
115045916cd2Sjpk if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
115145916cd2Sjpk return (-1);
115245916cd2Sjpk
115345916cd2Sjpk (void) fprintf(fp, "%s%s\\\n",
115445916cd2Sjpk (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
115545916cd2Sjpk (void) fprintf(fp, "\t%s%s\\\n",
115645916cd2Sjpk (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
115745916cd2Sjpk (void) fprintf(fp, "\t%s\n",
115845916cd2Sjpk (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
115945916cd2Sjpk
116045916cd2Sjpk return (0);
116145916cd2Sjpk }
116245916cd2Sjpk
116345916cd2Sjpk /*
116445916cd2Sjpk * _da_lock_devdb -
116545916cd2Sjpk * locks the database files; lock can be either broken explicitly by
116645916cd2Sjpk * closing the fd of the lock file, or it expires automatically at process
116745916cd2Sjpk * termination.
116845916cd2Sjpk * returns fd of the lock file or -1 on error.
116945916cd2Sjpk */
117045916cd2Sjpk int
_da_lock_devdb(char * rootdir)117145916cd2Sjpk _da_lock_devdb(char *rootdir)
117245916cd2Sjpk {
117345916cd2Sjpk int lockfd = -1;
117410ddde3aSaj int ret;
117510ddde3aSaj int count = 0;
117610ddde3aSaj int retry = 10;
117710ddde3aSaj int retry_sleep;
117810ddde3aSaj uint_t seed;
117945916cd2Sjpk char *lockfile;
118045916cd2Sjpk char path[MAXPATHLEN];
118145916cd2Sjpk int size = sizeof (path);
118245916cd2Sjpk
118345916cd2Sjpk if (rootdir == NULL) {
118445916cd2Sjpk lockfile = DA_DB_LOCK;
118545916cd2Sjpk } else {
118645916cd2Sjpk path[0] = '\0';
118745916cd2Sjpk if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
118845916cd2Sjpk return (-1);
118945916cd2Sjpk lockfile = path;
119045916cd2Sjpk }
119145916cd2Sjpk
119245916cd2Sjpk if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
119345916cd2Sjpk /* cannot open lock file */
119445916cd2Sjpk return (-1);
119545916cd2Sjpk
119645916cd2Sjpk (void) fchown(lockfd, DA_UID, DA_GID);
119745916cd2Sjpk
119845916cd2Sjpk if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
119945916cd2Sjpk /* cannot position lock file */
120045916cd2Sjpk (void) close(lockfd);
120145916cd2Sjpk return (-1);
120245916cd2Sjpk }
120310ddde3aSaj errno = 0;
120410ddde3aSaj while (retry > 0) {
120510ddde3aSaj count++;
120610ddde3aSaj seed = (uint_t)gethrtime();
120710ddde3aSaj ret = lockf(lockfd, F_TLOCK, 0);
120810ddde3aSaj if (ret == 0) {
120910ddde3aSaj (void) utime(lockfile, NULL);
121010ddde3aSaj return (lockfd);
121110ddde3aSaj }
121210ddde3aSaj if ((errno != EACCES) && (errno != EAGAIN)) {
121345916cd2Sjpk /* cannot set lock */
121445916cd2Sjpk (void) close(lockfd);
121545916cd2Sjpk return (-1);
121645916cd2Sjpk }
121710ddde3aSaj retry--;
121810ddde3aSaj retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
121910ddde3aSaj (void) sleep(retry_sleep);
122010ddde3aSaj errno = 0;
122110ddde3aSaj }
122245916cd2Sjpk
122310ddde3aSaj return (-1);
122445916cd2Sjpk }
122545916cd2Sjpk
122645916cd2Sjpk /*
122745916cd2Sjpk * da_open_devdb -
122845916cd2Sjpk * opens one or both database files - device_allocate, device_maps - in
122945916cd2Sjpk * the specified mode.
123045916cd2Sjpk * locks the database files; lock is either broken explicitly by the
123145916cd2Sjpk * caller by closing the lock file fd, or it expires automatically at
123245916cd2Sjpk * process termination.
123345916cd2Sjpk * writes the file pointer of opened file in the input args - dafp, dmfp.
123445916cd2Sjpk * returns fd of the lock file on success, -2 if database file does not
123545916cd2Sjpk * exist, -1 on other errors.
123645916cd2Sjpk */
123745916cd2Sjpk int
da_open_devdb(char * rootdir,FILE ** dafp,FILE ** dmfp,int flag)123845916cd2Sjpk da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
123945916cd2Sjpk {
124045916cd2Sjpk int oflag = 0;
124145916cd2Sjpk int fda = -1;
124245916cd2Sjpk int fdm = -1;
124345916cd2Sjpk int lockfd = -1;
124445916cd2Sjpk char *fname;
124545916cd2Sjpk char *fmode;
124645916cd2Sjpk char path[MAXPATHLEN];
124745916cd2Sjpk FILE *devfile;
124845916cd2Sjpk
124945916cd2Sjpk if ((dafp == NULL) && (dmfp == NULL))
125045916cd2Sjpk return (-1);
125145916cd2Sjpk
125245916cd2Sjpk if (flag & DA_RDWR) {
125345916cd2Sjpk oflag = DA_RDWR;
1254004388ebScasper fmode = "r+F";
125545916cd2Sjpk } else if (flag & DA_RDONLY) {
125645916cd2Sjpk oflag = DA_RDONLY;
1257004388ebScasper fmode = "rF";
125845916cd2Sjpk }
125945916cd2Sjpk
126045916cd2Sjpk if ((lockfd = _da_lock_devdb(rootdir)) == -1)
126145916cd2Sjpk return (-1);
126245916cd2Sjpk
126345916cd2Sjpk if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
126445916cd2Sjpk goto dmap_only;
126545916cd2Sjpk
126645916cd2Sjpk path[0] = '\0';
126745916cd2Sjpk
126845916cd2Sjpk /*
126945916cd2Sjpk * open the device allocation file
127045916cd2Sjpk */
127145916cd2Sjpk if (rootdir == NULL) {
127245916cd2Sjpk fname = DEVALLOC;
127345916cd2Sjpk } else {
127445916cd2Sjpk if (snprintf(path, sizeof (path), "%s%s", rootdir,
127545916cd2Sjpk DEVALLOC) >= sizeof (path)) {
127645916cd2Sjpk if (lockfd != -1)
127745916cd2Sjpk (void) close(lockfd);
127845916cd2Sjpk return (-1);
127945916cd2Sjpk }
128045916cd2Sjpk fname = path;
128145916cd2Sjpk }
128245916cd2Sjpk if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
128345916cd2Sjpk if (lockfd != -1)
128445916cd2Sjpk (void) close(lockfd);
128545916cd2Sjpk return ((errno == ENOENT) ? -2 : -1);
128645916cd2Sjpk }
128745916cd2Sjpk if ((devfile = fdopen(fda, fmode)) == NULL) {
128845916cd2Sjpk (void) close(fda);
128945916cd2Sjpk if (lockfd != -1)
129045916cd2Sjpk (void) close(lockfd);
129145916cd2Sjpk return (-1);
129245916cd2Sjpk }
129345916cd2Sjpk *dafp = devfile;
129445916cd2Sjpk (void) fchmod(fda, DA_DBMODE);
129545916cd2Sjpk
129645916cd2Sjpk if ((flag & DA_ALLOC_ONLY))
129745916cd2Sjpk goto out;
129845916cd2Sjpk
129945916cd2Sjpk dmap_only:
130045916cd2Sjpk path[0] = '\0';
130145916cd2Sjpk /*
130245916cd2Sjpk * open the device map file
130345916cd2Sjpk */
130445916cd2Sjpk if (rootdir == NULL) {
130545916cd2Sjpk fname = DEVMAP;
130645916cd2Sjpk } else {
130745916cd2Sjpk if (snprintf(path, sizeof (path), "%s%s", rootdir,
130845916cd2Sjpk DEVMAP) >= sizeof (path)) {
130945916cd2Sjpk (void) close(fda);
131045916cd2Sjpk if (lockfd != -1)
131145916cd2Sjpk (void) close(lockfd);
131245916cd2Sjpk return (-1);
131345916cd2Sjpk }
131445916cd2Sjpk fname = path;
131545916cd2Sjpk }
131645916cd2Sjpk
131745916cd2Sjpk if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
131845916cd2Sjpk if (lockfd != -1)
131945916cd2Sjpk (void) close(lockfd);
132045916cd2Sjpk return ((errno == ENOENT) ? -2 : -1);
132145916cd2Sjpk }
132245916cd2Sjpk
132345916cd2Sjpk if ((devfile = fdopen(fdm, fmode)) == NULL) {
132445916cd2Sjpk (void) close(fdm);
132545916cd2Sjpk (void) close(fda);
132645916cd2Sjpk if (lockfd != -1)
132745916cd2Sjpk (void) close(lockfd);
132845916cd2Sjpk return (-1);
132945916cd2Sjpk }
133045916cd2Sjpk *dmfp = devfile;
133145916cd2Sjpk (void) fchmod(fdm, DA_DBMODE);
133245916cd2Sjpk
133345916cd2Sjpk out:
133445916cd2Sjpk return (lockfd);
133545916cd2Sjpk }
133645916cd2Sjpk
133745916cd2Sjpk /*
133845916cd2Sjpk * _record_on_off -
133945916cd2Sjpk * adds either DA_ON_STR or DA_OFF_STR to device_allocate
134045916cd2Sjpk * returns 0 on success, -1 on error.
134145916cd2Sjpk */
134245916cd2Sjpk static int
_record_on_off(da_args * dargs,FILE * tafp,FILE * dafp)134345916cd2Sjpk _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
134445916cd2Sjpk {
134545916cd2Sjpk int dafd;
134645916cd2Sjpk int nsize;
134745916cd2Sjpk int nitems = 1;
134845916cd2Sjpk int actionlen;
134945916cd2Sjpk int str_found = 0;
135045916cd2Sjpk int len = 0, nlen = 0, plen = 0;
135145916cd2Sjpk char *ptr = NULL;
135245916cd2Sjpk char *actionstr;
135345916cd2Sjpk char *nbuf = NULL;
135445916cd2Sjpk char line[MAX_CANON];
135545916cd2Sjpk struct stat dastat;
135645916cd2Sjpk
135745916cd2Sjpk if (dargs->optflag & DA_ON)
135845916cd2Sjpk actionstr = DA_ON_STR;
135945916cd2Sjpk else
136045916cd2Sjpk actionstr = DA_OFF_STR;
136145916cd2Sjpk actionlen = strlen(actionstr);
136245916cd2Sjpk dafd = fileno(dafp);
136345916cd2Sjpk if (fstat(dafd, &dastat) == -1)
136445916cd2Sjpk return (-1);
136545916cd2Sjpk
136645916cd2Sjpk /* check the old device_allocate for on/off string */
136745916cd2Sjpk ptr = fgets(line, MAX_CANON, dafp);
136845916cd2Sjpk if (ptr != NULL) {
136945916cd2Sjpk if ((strcmp(line, DA_ON_STR) == 0) ||
137045916cd2Sjpk (strcmp(line, DA_OFF_STR) == 0)) {
137145916cd2Sjpk str_found = 1;
137245916cd2Sjpk nsize = dastat.st_size;
137345916cd2Sjpk }
137445916cd2Sjpk }
137545916cd2Sjpk if (!ptr || !str_found) {
137645916cd2Sjpk /*
137745916cd2Sjpk * the file never had either the on or the off string;
137845916cd2Sjpk * make room for it.
137945916cd2Sjpk */
138045916cd2Sjpk str_found = 0;
138145916cd2Sjpk nsize = dastat.st_size + actionlen + 1;
138245916cd2Sjpk }
13837e3e5701SJan Parcel if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
138445916cd2Sjpk return (-1);
138545916cd2Sjpk nbuf[0] = '\0';
138645916cd2Sjpk /* put the on/off string */
138745916cd2Sjpk (void) strcpy(nbuf, actionstr);
138845916cd2Sjpk nlen = strlen(nbuf);
138945916cd2Sjpk plen = nlen;
139045916cd2Sjpk if (ptr && !str_found) {
139145916cd2Sjpk /* now put the first line that we read in fgets */
139245916cd2Sjpk nlen = plen + strlen(line) + 1;
139345916cd2Sjpk len = snprintf(nbuf + plen, nlen - plen, "%s", line);
139445916cd2Sjpk if (len >= nsize) {
139545916cd2Sjpk free(nbuf);
139645916cd2Sjpk return (-1);
139745916cd2Sjpk }
139845916cd2Sjpk plen += len;
139945916cd2Sjpk }
140045916cd2Sjpk
140145916cd2Sjpk /* now get the rest of the old file */
140245916cd2Sjpk while (fgets(line, MAX_CANON, dafp) != NULL) {
140345916cd2Sjpk nlen = plen + strlen(line) + 1;
140445916cd2Sjpk len = snprintf(nbuf + plen, nlen - plen, "%s", line);
140545916cd2Sjpk if (len >= nsize) {
140645916cd2Sjpk free(nbuf);
140745916cd2Sjpk return (-1);
140845916cd2Sjpk }
140945916cd2Sjpk plen += len;
141045916cd2Sjpk }
141145916cd2Sjpk len = strlen(nbuf) + 1;
141245916cd2Sjpk if (len < nsize)
141345916cd2Sjpk nbuf[len] = '\n';
141445916cd2Sjpk
141545916cd2Sjpk /* write the on/off str + the old device_allocate to the temp file */
141645916cd2Sjpk if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
141745916cd2Sjpk free(nbuf);
141845916cd2Sjpk return (-1);
141945916cd2Sjpk }
142045916cd2Sjpk
142145916cd2Sjpk free(nbuf);
142245916cd2Sjpk
142345916cd2Sjpk return (0);
142445916cd2Sjpk }
142545916cd2Sjpk
142645916cd2Sjpk /*
142745916cd2Sjpk * da_update_defattrs -
142845916cd2Sjpk * writes default attributes to devalloc_defaults
142945916cd2Sjpk * returns 0 on success, -1 on error.
143045916cd2Sjpk */
143145916cd2Sjpk int
da_update_defattrs(da_args * dargs)143245916cd2Sjpk da_update_defattrs(da_args *dargs)
143345916cd2Sjpk {
143445916cd2Sjpk int rc = 0, lockfd = 0, tmpfd = 0;
143545916cd2Sjpk char *defpath = DEFATTRS;
143645916cd2Sjpk char *tmpdefpath = TMPATTRS;
143745916cd2Sjpk FILE *tmpfp = NULL;
143845916cd2Sjpk struct stat dstat;
143945916cd2Sjpk strentry_t *head_defent = NULL;
144045916cd2Sjpk
144145916cd2Sjpk if (dargs == NULL)
144245916cd2Sjpk return (0);
144345916cd2Sjpk if ((lockfd = _da_lock_devdb(NULL)) == -1)
144445916cd2Sjpk return (-1);
144545916cd2Sjpk if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
144645916cd2Sjpk (void) close(lockfd);
144745916cd2Sjpk return (-1);
144845916cd2Sjpk }
144945916cd2Sjpk (void) fchown(tmpfd, DA_UID, DA_GID);
145045916cd2Sjpk if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
145145916cd2Sjpk (void) close(tmpfd);
145245916cd2Sjpk (void) unlink(tmpdefpath);
145345916cd2Sjpk (void) close(lockfd);
145445916cd2Sjpk return (-1);
145545916cd2Sjpk }
145645916cd2Sjpk /*
145745916cd2Sjpk * examine all entries, remove an old one if required, check
145845916cd2Sjpk * if a new one needs to be added.
145945916cd2Sjpk */
146045916cd2Sjpk if (stat(defpath, &dstat) == 0) {
146145916cd2Sjpk if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
146245916cd2Sjpk if (rc == 1) {
146345916cd2Sjpk (void) close(tmpfd);
146445916cd2Sjpk (void) unlink(tmpdefpath);
146545916cd2Sjpk (void) close(lockfd);
146645916cd2Sjpk return (rc);
146745916cd2Sjpk }
146845916cd2Sjpk }
146945916cd2Sjpk }
147045916cd2Sjpk /*
147145916cd2Sjpk * write back any existing entries.
147245916cd2Sjpk */
147345916cd2Sjpk _write_defattrs(tmpfp, head_defent);
147445916cd2Sjpk
147545916cd2Sjpk if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
147645916cd2Sjpk /* add new entries */
147745916cd2Sjpk rc = _write_new_defattrs(tmpfp, dargs);
147845916cd2Sjpk (void) fclose(tmpfp);
147945916cd2Sjpk } else {
148045916cd2Sjpk (void) fclose(tmpfp);
148145916cd2Sjpk }
148245916cd2Sjpk if (rename(tmpdefpath, defpath) != 0) {
148345916cd2Sjpk rc = -1;
148445916cd2Sjpk (void) unlink(tmpdefpath);
148545916cd2Sjpk }
148645916cd2Sjpk (void) close(lockfd);
148745916cd2Sjpk
148845916cd2Sjpk return (rc);
148945916cd2Sjpk }
149045916cd2Sjpk
149145916cd2Sjpk /*
149245916cd2Sjpk * da_update_device -
14937e3e5701SJan Parcel * Writes existing entries and the SINGLE change requested by da_args,
14947e3e5701SJan Parcel * to device_allocate and device_maps.
14957e3e5701SJan Parcel * Returns 0 on success, -1 on error.
149645916cd2Sjpk */
149745916cd2Sjpk int
da_update_device(da_args * dargs)149845916cd2Sjpk da_update_device(da_args *dargs)
149945916cd2Sjpk {
150045916cd2Sjpk int rc;
150145916cd2Sjpk int tafd = -1, tmfd = -1;
150245916cd2Sjpk int lockfd = -1;
150345916cd2Sjpk char *rootdir = NULL;
150410ddde3aSaj char *apathp = NULL, *mpathp = NULL;
150510ddde3aSaj char *dapathp = NULL, *dmpathp = NULL;
150610ddde3aSaj char apath[MAXPATHLEN], mpath[MAXPATHLEN];
150710ddde3aSaj char dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
150845916cd2Sjpk FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL;
150945916cd2Sjpk struct stat dastat;
151045916cd2Sjpk devinfo_t *devinfo;
151145916cd2Sjpk strentry_t *head_devmapp = NULL;
151245916cd2Sjpk strentry_t *head_devallocp = NULL;
151345916cd2Sjpk
151445916cd2Sjpk if (dargs == NULL)
151545916cd2Sjpk return (0);
151645916cd2Sjpk
151745916cd2Sjpk rootdir = dargs->rootdir;
151845916cd2Sjpk devinfo = dargs->devinfo;
151945916cd2Sjpk
152045916cd2Sjpk /*
152145916cd2Sjpk * adding/removing entries should be done in both
152245916cd2Sjpk * device_allocate and device_maps. updates can be
152345916cd2Sjpk * done in both or either of the files.
152445916cd2Sjpk */
152545916cd2Sjpk if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
152645916cd2Sjpk if (dargs->optflag & DA_ALLOC_ONLY ||
152745916cd2Sjpk dargs->optflag & DA_MAPS_ONLY)
152845916cd2Sjpk return (0);
152945916cd2Sjpk }
153045916cd2Sjpk
153145916cd2Sjpk /*
153245916cd2Sjpk * name, type and list are required fields for adding a new
153345916cd2Sjpk * device.
153445916cd2Sjpk */
153545916cd2Sjpk if ((dargs->optflag & DA_ADD) &&
153645916cd2Sjpk ((devinfo->devname == NULL) ||
153745916cd2Sjpk (devinfo->devtype == NULL) ||
153845916cd2Sjpk (devinfo->devlist == NULL))) {
153945916cd2Sjpk return (-1);
154045916cd2Sjpk }
154145916cd2Sjpk
154245916cd2Sjpk if (rootdir != NULL) {
154345916cd2Sjpk if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
154445916cd2Sjpk TMPALLOC) >= sizeof (apath))
154545916cd2Sjpk return (-1);
154645916cd2Sjpk apathp = apath;
154745916cd2Sjpk if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
154845916cd2Sjpk DEVALLOC) >= sizeof (dapath))
154945916cd2Sjpk return (-1);
155045916cd2Sjpk dapathp = dapath;
155145916cd2Sjpk if (!(dargs->optflag & DA_ALLOC_ONLY)) {
155245916cd2Sjpk if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
155345916cd2Sjpk TMPMAP) >= sizeof (mpath))
155445916cd2Sjpk return (-1);
155545916cd2Sjpk mpathp = mpath;
155645916cd2Sjpk if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
155745916cd2Sjpk DEVMAP) >= sizeof (dmpath))
155845916cd2Sjpk return (-1);
155945916cd2Sjpk dmpathp = dmpath;
156045916cd2Sjpk }
156145916cd2Sjpk } else {
156245916cd2Sjpk apathp = TMPALLOC;
156345916cd2Sjpk dapathp = DEVALLOC;
156445916cd2Sjpk mpathp = TMPMAP;
156545916cd2Sjpk dmpathp = DEVMAP;
156645916cd2Sjpk }
156745916cd2Sjpk
156845916cd2Sjpk if (dargs->optflag & DA_MAPS_ONLY)
156945916cd2Sjpk goto dmap_only;
157045916cd2Sjpk
157145916cd2Sjpk /*
157245916cd2Sjpk * Check if we are here just to record on/off status of
157345916cd2Sjpk * device_allocation.
157445916cd2Sjpk */
157545916cd2Sjpk if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
157645916cd2Sjpk lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
157745916cd2Sjpk DA_RDONLY|DA_ALLOC_ONLY);
157845916cd2Sjpk else
157945916cd2Sjpk lockfd = _da_lock_devdb(rootdir);
158045916cd2Sjpk if (lockfd == -1)
158145916cd2Sjpk return (-1);
158245916cd2Sjpk
158345916cd2Sjpk if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
158445916cd2Sjpk (void) close(lockfd);
158545916cd2Sjpk (void) fclose(dafp);
158645916cd2Sjpk return (-1);
158745916cd2Sjpk }
158845916cd2Sjpk (void) fchown(tafd, DA_UID, DA_GID);
158945916cd2Sjpk if ((tafp = fdopen(tafd, "r+")) == NULL) {
159045916cd2Sjpk (void) close(tafd);
159145916cd2Sjpk (void) unlink(apathp);
159245916cd2Sjpk (void) fclose(dafp);
159345916cd2Sjpk (void) close(lockfd);
159445916cd2Sjpk return (-1);
159545916cd2Sjpk }
159645916cd2Sjpk
159745916cd2Sjpk /*
159845916cd2Sjpk * We don't need to parse the file if we are here just to record
159945916cd2Sjpk * on/off status of device_allocation.
160045916cd2Sjpk */
160145916cd2Sjpk if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
160245916cd2Sjpk if (_record_on_off(dargs, tafp, dafp) == -1) {
160345916cd2Sjpk (void) close(tafd);
160445916cd2Sjpk (void) unlink(apathp);
160545916cd2Sjpk (void) fclose(dafp);
160645916cd2Sjpk (void) close(lockfd);
160745916cd2Sjpk return (-1);
160845916cd2Sjpk }
160945916cd2Sjpk (void) fclose(dafp);
161045916cd2Sjpk goto out;
161145916cd2Sjpk }
161245916cd2Sjpk
161345916cd2Sjpk /*
16147e3e5701SJan Parcel * If reacting to a hotplug, read the file entries,
16157e3e5701SJan Parcel * figure out what dname (tname + a new number) goes to the
16167e3e5701SJan Parcel * device being added/removed, and create a good head_devallocp and
16177e3e5701SJan Parcel * head_devmapp with everything good still in it (_rebuild_lists)
16187e3e5701SJan Parcel *
16197e3e5701SJan Parcel * Else examine all the entries, remove an old one if it is
16207e3e5701SJan Parcel * a duplicate with a device being added, returning the
16217e3e5701SJan Parcel * remaining list (_build_lists.)
16227e3e5701SJan Parcel *
16237e3e5701SJan Parcel * We need to do this only if the file exists already.
16247e3e5701SJan Parcel *
16257e3e5701SJan Parcel * Once we have built these lists, we need to free the strings
16267e3e5701SJan Parcel * in the head_* arrays before returning.
162745916cd2Sjpk */
162845916cd2Sjpk if (stat(dapathp, &dastat) == 0) {
16297e3e5701SJan Parcel /* for device allocation, the /etc files are the "master" */
16307e3e5701SJan Parcel if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
16317e3e5701SJan Parcel (!(dargs->optflag & DA_FORCE)))
16327e3e5701SJan Parcel rc = _rebuild_lists(dargs, &head_devallocp,
16337e3e5701SJan Parcel &head_devmapp);
16347e3e5701SJan Parcel else
16357e3e5701SJan Parcel rc = _build_lists(dargs, &head_devallocp,
16367e3e5701SJan Parcel &head_devmapp);
16377e3e5701SJan Parcel
16387e3e5701SJan Parcel if (rc != 0 && rc != 1) {
163945916cd2Sjpk (void) close(tafd);
164045916cd2Sjpk (void) unlink(apathp);
164145916cd2Sjpk (void) close(lockfd);
16427e3e5701SJan Parcel return (-1);
164345916cd2Sjpk }
16447e3e5701SJan Parcel } else
16457e3e5701SJan Parcel rc = 0;
16467e3e5701SJan Parcel
16477e3e5701SJan Parcel if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
16487e3e5701SJan Parcel (void) close(tafd);
16497e3e5701SJan Parcel (void) unlink(apathp);
16507e3e5701SJan Parcel (void) close(lockfd);
16517e3e5701SJan Parcel return (0);
165245916cd2Sjpk }
16537e3e5701SJan Parcel /*
16547e3e5701SJan Parcel * TODO: clean up the workings of DA_UPDATE.
16557e3e5701SJan Parcel * Due to da_match looking at fields that are missing
16567e3e5701SJan Parcel * in dargs for DA_UPDATE, the da_match call returns no match,
16577e3e5701SJan Parcel * but due to the way _da2str combines the devalloc_t info with
16587e3e5701SJan Parcel * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
16597e3e5701SJan Parcel *
16607e3e5701SJan Parcel * This would not scale if any type of update was ever needed
16617e3e5701SJan Parcel * from the daemon.
16627e3e5701SJan Parcel */
166345916cd2Sjpk
166445916cd2Sjpk /*
16657e3e5701SJan Parcel * Write out devallocp along with the devalloc on/off string.
166645916cd2Sjpk */
166745916cd2Sjpk _write_device_allocate(dapathp, tafp, head_devallocp);
166845916cd2Sjpk
166945916cd2Sjpk if (dargs->optflag & DA_ALLOC_ONLY)
167045916cd2Sjpk goto out;
167145916cd2Sjpk
167245916cd2Sjpk dmap_only:
167345916cd2Sjpk if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
167445916cd2Sjpk (void) close(tafd);
167545916cd2Sjpk (void) unlink(apathp);
167645916cd2Sjpk (void) close(lockfd);
167745916cd2Sjpk return (-1);
167845916cd2Sjpk }
167945916cd2Sjpk (void) fchown(tmfd, DA_UID, DA_GID);
168045916cd2Sjpk if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
168145916cd2Sjpk (void) close(tafd);
168245916cd2Sjpk (void) unlink(apathp);
168345916cd2Sjpk (void) close(tmfd);
168445916cd2Sjpk (void) unlink(mpathp);
168545916cd2Sjpk (void) close(lockfd);
168645916cd2Sjpk return (-1);
168745916cd2Sjpk }
168845916cd2Sjpk
16897e3e5701SJan Parcel /*
16907e3e5701SJan Parcel * Write back any non-removed pre-existing entries.
16917e3e5701SJan Parcel */
169245916cd2Sjpk if (head_devmapp != NULL)
169345916cd2Sjpk _write_device_maps(tmfp, head_devmapp);
169445916cd2Sjpk
169545916cd2Sjpk out:
16967e3e5701SJan Parcel /*
16977e3e5701SJan Parcel * Add any new entries here.
16987e3e5701SJan Parcel */
169945916cd2Sjpk if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
170045916cd2Sjpk /* add any new entries */
170145916cd2Sjpk rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
170245916cd2Sjpk (void) fclose(tafp);
170345916cd2Sjpk
170445916cd2Sjpk if (rc == 0)
170545916cd2Sjpk rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
170645916cd2Sjpk (void) fclose(tmfp);
170745916cd2Sjpk } else {
170845916cd2Sjpk if (tafp)
170945916cd2Sjpk (void) fclose(tafp);
171045916cd2Sjpk if (tmfp)
171145916cd2Sjpk (void) fclose(tmfp);
171245916cd2Sjpk }
171345916cd2Sjpk
171445916cd2Sjpk rc = 0;
171545916cd2Sjpk if (!(dargs->optflag & DA_MAPS_ONLY)) {
171645916cd2Sjpk if (rename(apathp, dapathp) != 0) {
171745916cd2Sjpk rc = -1;
171845916cd2Sjpk (void) unlink(apathp);
171945916cd2Sjpk }
172045916cd2Sjpk }
172145916cd2Sjpk if (!(dargs->optflag & DA_ALLOC_ONLY)) {
172245916cd2Sjpk if (rename(mpathp, dmpathp) != 0) {
172345916cd2Sjpk rc = -1;
172445916cd2Sjpk (void) unlink(mpathp);
172545916cd2Sjpk }
172645916cd2Sjpk }
172745916cd2Sjpk
172845916cd2Sjpk (void) close(lockfd);
172945916cd2Sjpk
173045916cd2Sjpk return (rc);
173145916cd2Sjpk }
173245916cd2Sjpk
173345916cd2Sjpk /*
173445916cd2Sjpk * da_add_list -
173545916cd2Sjpk * adds new /dev link name to the linked list of devices.
173645916cd2Sjpk * returns 0 if link added successfully, -1 on error.
173745916cd2Sjpk */
173845916cd2Sjpk int
da_add_list(devlist_t * dlist,char * link,int new_instance,int flag)173945916cd2Sjpk da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
174045916cd2Sjpk {
174145916cd2Sjpk int instance;
174245916cd2Sjpk int nlen, plen;
174345916cd2Sjpk int new_entry = 0;
174445916cd2Sjpk char *dtype, *dexec, *tname, *kval;
174545916cd2Sjpk char *minstr = NULL, *maxstr = NULL;
17467e3e5701SJan Parcel char dname[DA_MAXNAME + 1];
174745916cd2Sjpk kva_t *kva;
174845916cd2Sjpk deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL;
174945916cd2Sjpk da_defs_t *da_defs;
175045916cd2Sjpk
175145916cd2Sjpk if (dlist == NULL || link == NULL)
175245916cd2Sjpk return (-1);
175345916cd2Sjpk
175445916cd2Sjpk dname[0] = '\0';
175545916cd2Sjpk if (flag & DA_AUDIO) {
175645916cd2Sjpk dentry = dlist->audio;
175745916cd2Sjpk tname = DA_AUDIO_NAME;
175845916cd2Sjpk dtype = DA_AUDIO_TYPE;
175945916cd2Sjpk dexec = DA_DEFAULT_AUDIO_CLEAN;
176045916cd2Sjpk } else if (flag & DA_CD) {
176145916cd2Sjpk dentry = dlist->cd;
176245916cd2Sjpk tname = DA_CD_NAME;
176345916cd2Sjpk dtype = DA_CD_TYPE;
176445916cd2Sjpk dexec = DA_DEFAULT_DISK_CLEAN;
176545916cd2Sjpk } else if (flag & DA_FLOPPY) {
176645916cd2Sjpk dentry = dlist->floppy;
176745916cd2Sjpk tname = DA_FLOPPY_NAME;
176845916cd2Sjpk dtype = DA_FLOPPY_TYPE;
176945916cd2Sjpk dexec = DA_DEFAULT_DISK_CLEAN;
177045916cd2Sjpk } else if (flag & DA_TAPE) {
177145916cd2Sjpk dentry = dlist->tape;
177245916cd2Sjpk tname = DA_TAPE_NAME;
177345916cd2Sjpk dtype = DA_TAPE_TYPE;
177445916cd2Sjpk dexec = DA_DEFAULT_TAPE_CLEAN;
177545916cd2Sjpk } else if (flag & DA_RMDISK) {
177645916cd2Sjpk dentry = dlist->rmdisk;
177745916cd2Sjpk tname = DA_RMDISK_NAME;
177845916cd2Sjpk dtype = DA_RMDISK_TYPE;
177945916cd2Sjpk dexec = DA_DEFAULT_DISK_CLEAN;
178045916cd2Sjpk } else {
178145916cd2Sjpk return (-1);
178245916cd2Sjpk }
178345916cd2Sjpk
178445916cd2Sjpk for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
178545916cd2Sjpk pentry = nentry;
178645916cd2Sjpk (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
178745916cd2Sjpk if (nentry->devinfo.instance == new_instance)
178845916cd2Sjpk /*
178945916cd2Sjpk * Add the new link name to the list of links
179045916cd2Sjpk * that the device 'dname' has.
179145916cd2Sjpk */
179245916cd2Sjpk break;
179345916cd2Sjpk }
179445916cd2Sjpk
179545916cd2Sjpk if (nentry == NULL) {
179645916cd2Sjpk /*
179745916cd2Sjpk * Either this is the first entry ever, or no matching entry
179845916cd2Sjpk * was found. Create a new one and add to the list.
179945916cd2Sjpk */
180045916cd2Sjpk if (dentry == NULL) /* first entry ever */
180145916cd2Sjpk instance = 0;
180245916cd2Sjpk else /* no matching entry */
180345916cd2Sjpk instance++;
180445916cd2Sjpk (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
180545916cd2Sjpk if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
180645916cd2Sjpk NULL)
180745916cd2Sjpk return (-1);
180845916cd2Sjpk if (pentry != NULL)
180945916cd2Sjpk pentry->next = nentry;
181045916cd2Sjpk new_entry = 1;
181145916cd2Sjpk nentry->devinfo.devname = strdup(dname);
181245916cd2Sjpk nentry->devinfo.devtype = dtype;
181345916cd2Sjpk nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
181445916cd2Sjpk nentry->devinfo.devexec = dexec;
181545916cd2Sjpk nentry->devinfo.instance = new_instance;
181645916cd2Sjpk /*
181745916cd2Sjpk * Look for default label range, authorizations and cleaning
181845916cd2Sjpk * program in devalloc_defaults. If label range is not
181945916cd2Sjpk * specified in devalloc_defaults, assume it to be admin_low
182045916cd2Sjpk * to admin_high.
182145916cd2Sjpk */
182245916cd2Sjpk minstr = DA_DEFAULT_MIN;
182345916cd2Sjpk maxstr = DA_DEFAULT_MAX;
182445916cd2Sjpk setdadefent();
182545916cd2Sjpk if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
182645916cd2Sjpk kva = da_defs->devopts;
182745916cd2Sjpk if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
182845916cd2Sjpk minstr = strdup(kval);
182945916cd2Sjpk if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
183045916cd2Sjpk maxstr = strdup(kval);
183145916cd2Sjpk if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
183245916cd2Sjpk nentry->devinfo.devauths = strdup(kval);
183345916cd2Sjpk if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
183445916cd2Sjpk nentry->devinfo.devexec = strdup(kval);
183545916cd2Sjpk freedadefent(da_defs);
183645916cd2Sjpk }
183745916cd2Sjpk enddadefent();
183845916cd2Sjpk kval = NULL;
183945916cd2Sjpk nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
184045916cd2Sjpk strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
184145916cd2Sjpk strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
184245916cd2Sjpk + 1; /* +1 for terminator */
184345916cd2Sjpk if (kval = (char *)malloc(nlen))
184445916cd2Sjpk (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
184545916cd2Sjpk DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
184645916cd2Sjpk DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
184745916cd2Sjpk nentry->devinfo.devopts = kval;
184845916cd2Sjpk
184945916cd2Sjpk nentry->devinfo.devlist = NULL;
185045916cd2Sjpk nentry->next = NULL;
185145916cd2Sjpk }
185245916cd2Sjpk
185345916cd2Sjpk nlen = strlen(link) + 1; /* +1 terminator */
185445916cd2Sjpk if (nentry->devinfo.devlist) {
185545916cd2Sjpk plen = strlen(nentry->devinfo.devlist);
185645916cd2Sjpk nlen = nlen + plen + 1; /* +1 for blank to separate entries */
185745916cd2Sjpk } else {
185845916cd2Sjpk plen = 0;
185945916cd2Sjpk }
186045916cd2Sjpk
186145916cd2Sjpk if ((nentry->devinfo.devlist =
186245916cd2Sjpk (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
186345916cd2Sjpk if (new_entry) {
186445916cd2Sjpk free(nentry->devinfo.devname);
186545916cd2Sjpk free(nentry);
186645916cd2Sjpk if (pentry != NULL)
186745916cd2Sjpk pentry->next = NULL;
186845916cd2Sjpk }
186945916cd2Sjpk return (-1);
187045916cd2Sjpk }
187145916cd2Sjpk
187245916cd2Sjpk if (plen == 0)
187345916cd2Sjpk (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
187445916cd2Sjpk else
187545916cd2Sjpk (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
187645916cd2Sjpk " %s", link);
187745916cd2Sjpk
187845916cd2Sjpk if (pentry == NULL) {
187945916cd2Sjpk /*
188045916cd2Sjpk * This is the first entry of this device type.
188145916cd2Sjpk */
188245916cd2Sjpk if (flag & DA_AUDIO)
188345916cd2Sjpk dlist->audio = nentry;
188445916cd2Sjpk else if (flag & DA_CD)
188545916cd2Sjpk dlist->cd = nentry;
188645916cd2Sjpk else if (flag & DA_FLOPPY)
188745916cd2Sjpk dlist->floppy = nentry;
188845916cd2Sjpk else if (flag & DA_TAPE)
188945916cd2Sjpk dlist->tape = nentry;
189045916cd2Sjpk else if (flag & DA_RMDISK)
189145916cd2Sjpk dlist->rmdisk = nentry;
189245916cd2Sjpk }
189345916cd2Sjpk
189445916cd2Sjpk return (0);
189545916cd2Sjpk }
189645916cd2Sjpk
189745916cd2Sjpk /*
189845916cd2Sjpk * da_remove_list -
189945916cd2Sjpk * removes a /dev link name from the linked list of devices.
190045916cd2Sjpk * returns type of device if link for that device removed
190145916cd2Sjpk * successfully, else returns -1 on error.
190245916cd2Sjpk * if all links for a device are removed, stores that device
190345916cd2Sjpk * name in devname.
190445916cd2Sjpk */
190545916cd2Sjpk int
da_remove_list(devlist_t * dlist,char * link,int type,char * devname,int size)190645916cd2Sjpk da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
190745916cd2Sjpk {
190845916cd2Sjpk int flag;
190945916cd2Sjpk int remove_dev = 0;
191045916cd2Sjpk int nlen, plen, slen;
191145916cd2Sjpk char *lasts, *lname, *oldlist;
191245916cd2Sjpk struct stat rmstat;
191345916cd2Sjpk deventry_t *dentry, *current, *prev;
191445916cd2Sjpk
191545916cd2Sjpk if (type != NULL)
191645916cd2Sjpk flag = type;
191745916cd2Sjpk else if (link == NULL)
191845916cd2Sjpk return (-1);
191945916cd2Sjpk else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
192045916cd2Sjpk flag = DA_AUDIO;
192145916cd2Sjpk else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
192245916cd2Sjpk strstr(link, "sr") || strstr(link, "rsr"))
192345916cd2Sjpk flag = DA_CD;
192445916cd2Sjpk else if (strstr(link, "fd") || strstr(link, "rfd") ||
192545916cd2Sjpk strstr(link, "diskette") || strstr(link, "rdiskette"))
192645916cd2Sjpk flag = DA_FLOPPY;
192745916cd2Sjpk else if (strstr(link, DA_TAPE_NAME))
192845916cd2Sjpk flag = DA_TAPE;
192945916cd2Sjpk else
193045916cd2Sjpk flag = DA_RMDISK;
193145916cd2Sjpk
193245916cd2Sjpk switch (type) {
193345916cd2Sjpk case DA_AUDIO:
193445916cd2Sjpk dentry = dlist->audio;
193545916cd2Sjpk break;
193645916cd2Sjpk case DA_CD:
193745916cd2Sjpk dentry = dlist->cd;
193845916cd2Sjpk break;
193945916cd2Sjpk case DA_FLOPPY:
194045916cd2Sjpk dentry = dlist->floppy;
194145916cd2Sjpk break;
194245916cd2Sjpk case DA_TAPE:
194345916cd2Sjpk dentry = dlist->tape;
194445916cd2Sjpk break;
194545916cd2Sjpk case DA_RMDISK:
194645916cd2Sjpk dentry = dlist->rmdisk;
194745916cd2Sjpk break;
194845916cd2Sjpk default:
194945916cd2Sjpk return (-1);
195045916cd2Sjpk }
195145916cd2Sjpk
195245916cd2Sjpk if ((type != NULL) && (link == NULL)) {
195345916cd2Sjpk for (current = dentry, prev = dentry; current != NULL;
195445916cd2Sjpk current = current->next) {
195545916cd2Sjpk oldlist = strdup(current->devinfo.devlist);
195645916cd2Sjpk for (lname = strtok_r(oldlist, " ", &lasts);
195745916cd2Sjpk lname != NULL;
195845916cd2Sjpk lname = strtok_r(NULL, " ", &lasts)) {
195945916cd2Sjpk if (stat(lname, &rmstat) != 0) {
196045916cd2Sjpk remove_dev = 1;
196145916cd2Sjpk goto remove_dev;
196245916cd2Sjpk }
196345916cd2Sjpk }
196445916cd2Sjpk prev = current;
196545916cd2Sjpk }
196645916cd2Sjpk return (-1);
196745916cd2Sjpk }
196845916cd2Sjpk
196945916cd2Sjpk for (current = dentry, prev = dentry; current != NULL;
197045916cd2Sjpk current = current->next) {
197145916cd2Sjpk plen = strlen(current->devinfo.devlist);
197245916cd2Sjpk nlen = strlen(link);
197345916cd2Sjpk if (plen == nlen) {
197445916cd2Sjpk if (strcmp(current->devinfo.devlist, link) == 0) {
197545916cd2Sjpk /* last name in the list */
197645916cd2Sjpk remove_dev = 1;
197745916cd2Sjpk break;
197845916cd2Sjpk }
197945916cd2Sjpk }
198045916cd2Sjpk if (strstr(current->devinfo.devlist, link)) {
198145916cd2Sjpk nlen = plen - nlen + 1;
198245916cd2Sjpk oldlist = strdup(current->devinfo.devlist);
198345916cd2Sjpk if ((current->devinfo.devlist =
198445916cd2Sjpk (char *)realloc(current->devinfo.devlist,
198545916cd2Sjpk nlen)) == NULL) {
198645916cd2Sjpk free(oldlist);
198745916cd2Sjpk return (-1);
198845916cd2Sjpk }
198945916cd2Sjpk current->devinfo.devlist[0] = '\0';
199045916cd2Sjpk nlen = plen = slen = 0;
199145916cd2Sjpk for (lname = strtok_r(oldlist, " ", &lasts);
199245916cd2Sjpk lname != NULL;
199345916cd2Sjpk lname = strtok_r(NULL, " ", &lasts)) {
199445916cd2Sjpk if (strcmp(lname, link) == 0)
199545916cd2Sjpk continue;
199645916cd2Sjpk nlen = strlen(lname) + plen + 1;
199745916cd2Sjpk if (plen == 0) {
199845916cd2Sjpk slen =
199945916cd2Sjpk snprintf(current->devinfo.devlist,
200045916cd2Sjpk nlen, "%s", lname);
200145916cd2Sjpk } else {
200245916cd2Sjpk slen =
200345916cd2Sjpk snprintf(current->devinfo.devlist +
200410ddde3aSaj plen, nlen - plen, " %s", lname);
200545916cd2Sjpk }
200645916cd2Sjpk plen = plen + slen + 1;
200745916cd2Sjpk }
200845916cd2Sjpk free(oldlist);
200945916cd2Sjpk break;
201045916cd2Sjpk }
201145916cd2Sjpk prev = current;
201245916cd2Sjpk }
201345916cd2Sjpk
201445916cd2Sjpk remove_dev:
201545916cd2Sjpk if (remove_dev == 1) {
201645916cd2Sjpk (void) strlcpy(devname, current->devinfo.devname, size);
201745916cd2Sjpk free(current->devinfo.devname);
201845916cd2Sjpk free(current->devinfo.devlist);
201945916cd2Sjpk current->devinfo.devname = current->devinfo.devlist = NULL;
202045916cd2Sjpk prev->next = current->next;
202145916cd2Sjpk free(current);
202245916cd2Sjpk current = NULL;
202345916cd2Sjpk }
202445916cd2Sjpk if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
202545916cd2Sjpk if (prev->next) {
202645916cd2Sjpk /*
202745916cd2Sjpk * what we removed above was the first entry
202845916cd2Sjpk * in the list. make the next entry to be the
202945916cd2Sjpk * first.
203045916cd2Sjpk */
203145916cd2Sjpk current = prev->next;
203245916cd2Sjpk } else {
203345916cd2Sjpk /*
203445916cd2Sjpk * the matching entry was the only entry in the list
203545916cd2Sjpk * for this type.
203645916cd2Sjpk */
203745916cd2Sjpk current = NULL;
203845916cd2Sjpk }
203945916cd2Sjpk if (flag & DA_AUDIO)
204045916cd2Sjpk dlist->audio = current;
204145916cd2Sjpk else if (flag & DA_CD)
204245916cd2Sjpk dlist->cd = current;
204345916cd2Sjpk else if (flag & DA_FLOPPY)
204445916cd2Sjpk dlist->floppy = current;
204545916cd2Sjpk else if (flag & DA_TAPE)
204645916cd2Sjpk dlist->tape = current;
204745916cd2Sjpk else if (flag & DA_RMDISK)
204845916cd2Sjpk dlist->rmdisk = current;
204945916cd2Sjpk }
205045916cd2Sjpk
205145916cd2Sjpk return (flag);
205245916cd2Sjpk }
205345916cd2Sjpk
205445916cd2Sjpk /*
20557e3e5701SJan Parcel * da_rm_list_entry -
20567e3e5701SJan Parcel *
20577e3e5701SJan Parcel * The adding of devnames to a devlist and the removal of a
20587e3e5701SJan Parcel * device are not symmetrical -- hot_cleanup gives a /devices
20597e3e5701SJan Parcel * name which is used to remove the dentry whose links all point to
20607e3e5701SJan Parcel * that /devices entry.
20617e3e5701SJan Parcel *
20627e3e5701SJan Parcel * The link argument is present if available to make debugging
20637e3e5701SJan Parcel * easier.
20647e3e5701SJan Parcel *
20657e3e5701SJan Parcel * da_rm_list_entry removes an entry from the linked list of devices.
20667e3e5701SJan Parcel *
20677e3e5701SJan Parcel * Returns 1 if the devname was removed successfully,
20687e3e5701SJan Parcel * 0 if not found, -1 for error.
20697e3e5701SJan Parcel */
20707e3e5701SJan Parcel /*ARGSUSED*/
20717e3e5701SJan Parcel int
da_rm_list_entry(devlist_t * dlist,char * link,int type,char * devname)20727e3e5701SJan Parcel da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
20737e3e5701SJan Parcel {
20747e3e5701SJan Parcel int retval = 0;
20757e3e5701SJan Parcel deventry_t **dentry, *current, *prev;
20767e3e5701SJan Parcel
20777e3e5701SJan Parcel switch (type) {
20787e3e5701SJan Parcel case DA_AUDIO:
20797e3e5701SJan Parcel dentry = &(dlist->audio);
20807e3e5701SJan Parcel break;
20817e3e5701SJan Parcel case DA_CD:
20827e3e5701SJan Parcel dentry = &(dlist->cd);
20837e3e5701SJan Parcel break;
20847e3e5701SJan Parcel case DA_FLOPPY:
20857e3e5701SJan Parcel dentry = &(dlist->floppy);
20867e3e5701SJan Parcel break;
20877e3e5701SJan Parcel case DA_TAPE:
20887e3e5701SJan Parcel dentry = &(dlist->tape);
20897e3e5701SJan Parcel break;
20907e3e5701SJan Parcel case DA_RMDISK:
20917e3e5701SJan Parcel dentry = &(dlist->rmdisk);
20927e3e5701SJan Parcel break;
20937e3e5701SJan Parcel default:
20947e3e5701SJan Parcel return (-1);
20957e3e5701SJan Parcel }
20967e3e5701SJan Parcel
20977e3e5701SJan Parcel /* Presumably in daemon mode, no need to remove entry, list is empty */
20987e3e5701SJan Parcel if (*dentry == (deventry_t *)NULL)
20997e3e5701SJan Parcel return (0);
21007e3e5701SJan Parcel
21017e3e5701SJan Parcel prev = NULL;
21027e3e5701SJan Parcel for (current = *dentry; current != NULL;
21037e3e5701SJan Parcel prev = current, current = current->next) {
21047e3e5701SJan Parcel if (strcmp(devname, current->devinfo.devname))
21057e3e5701SJan Parcel continue;
21067e3e5701SJan Parcel retval = 1;
21077e3e5701SJan Parcel break;
21087e3e5701SJan Parcel }
21097e3e5701SJan Parcel if (retval == 0)
21107e3e5701SJan Parcel return (0);
21117e3e5701SJan Parcel free(current->devinfo.devname);
21127e3e5701SJan Parcel if (current->devinfo.devlist != NULL)
21137e3e5701SJan Parcel free(current->devinfo.devlist);
21147e3e5701SJan Parcel if (current->devinfo.devopts != NULL)
21157e3e5701SJan Parcel free(current->devinfo.devopts);
21167e3e5701SJan Parcel
21177e3e5701SJan Parcel if (prev == NULL)
21187e3e5701SJan Parcel *dentry = current->next;
21197e3e5701SJan Parcel else
21207e3e5701SJan Parcel prev->next = current->next;
21217e3e5701SJan Parcel
21227e3e5701SJan Parcel free(current);
21237e3e5701SJan Parcel return (retval);
21247e3e5701SJan Parcel }
21257e3e5701SJan Parcel
21267e3e5701SJan Parcel /*
212745916cd2Sjpk * da_is_on -
212845916cd2Sjpk * checks if device allocation feature is turned on.
212945916cd2Sjpk * returns 1 if on, 0 if off, -1 if status string not
213045916cd2Sjpk * found in device_allocate.
213145916cd2Sjpk */
213245916cd2Sjpk int
da_is_on()213345916cd2Sjpk da_is_on()
213445916cd2Sjpk {
213545916cd2Sjpk return (getdaon());
213645916cd2Sjpk }
213745916cd2Sjpk
213845916cd2Sjpk /*
213945916cd2Sjpk * da_print_device -
214045916cd2Sjpk * debug routine to print device entries.
214145916cd2Sjpk */
214245916cd2Sjpk void
da_print_device(int flag,devlist_t * devlist)214345916cd2Sjpk da_print_device(int flag, devlist_t *devlist)
214445916cd2Sjpk {
214545916cd2Sjpk deventry_t *entry, *dentry;
214645916cd2Sjpk devinfo_t *devinfo;
214745916cd2Sjpk
214845916cd2Sjpk if (flag & DA_AUDIO)
214945916cd2Sjpk dentry = devlist->audio;
215045916cd2Sjpk else if (flag & DA_CD)
215145916cd2Sjpk dentry = devlist->cd;
215245916cd2Sjpk else if (flag & DA_FLOPPY)
215345916cd2Sjpk dentry = devlist->floppy;
215445916cd2Sjpk else if (flag & DA_TAPE)
215545916cd2Sjpk dentry = devlist->tape;
215645916cd2Sjpk else if (flag & DA_RMDISK)
215745916cd2Sjpk dentry = devlist->rmdisk;
215845916cd2Sjpk else
215945916cd2Sjpk return;
216045916cd2Sjpk
216145916cd2Sjpk for (entry = dentry; entry != NULL; entry = entry->next) {
216245916cd2Sjpk devinfo = &(entry->devinfo);
216345916cd2Sjpk (void) fprintf(stdout, "name: %s\n", devinfo->devname);
216445916cd2Sjpk (void) fprintf(stdout, "type: %s\n", devinfo->devtype);
216545916cd2Sjpk (void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
216645916cd2Sjpk (void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
216745916cd2Sjpk (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
216845916cd2Sjpk }
216945916cd2Sjpk }
2170