summaryrefslogtreecommitdiff
path: root/usr/src/lib/libbsm/common/devalloc.c
diff options
context:
space:
mode:
authorjpk <none@none>2006-03-24 12:29:20 -0800
committerjpk <none@none>2006-03-24 12:29:20 -0800
commit45916cd2fec6e79bca5dee0421bd39e3c2910d1e (patch)
tree6b3ea6982435d47edc8972c72c62f9d111e8bb10 /usr/src/lib/libbsm/common/devalloc.c
parent2c9565cfcd87a2045c2e4b76f31ac4e978903589 (diff)
downloadillumos-gate-45916cd2fec6e79bca5dee0421bd39e3c2910d1e.tar.gz
PSARC/2002/762 Layered Trusted Solaris
PSARC/2005/060 TSNET: Trusted Networking with Security Labels PSARC/2005/259 Layered Trusted Solaris Label Interfaces PSARC/2005/573 Solaris Trusted Extensions for Printing PSARC/2005/691 Trusted Extensions for Device Allocation PSARC/2005/723 Solaris Trusted Extensions Filesystem Labeling PSARC/2006/009 Labeled Auditing PSARC/2006/155 Trusted Extensions RBAC Changes PSARC/2006/191 is_system_labeled 6293271 Zone processes should use zone_kcred instead of kcred 6394554 integrate Solaris Trusted Extensions --HG-- rename : usr/src/cmd/dminfo/Makefile => deleted_files/usr/src/cmd/dminfo/Makefile rename : usr/src/cmd/dminfo/dminfo.c => usr/src/cmd/allocate/dminfo.c
Diffstat (limited to 'usr/src/lib/libbsm/common/devalloc.c')
-rw-r--r--usr/src/lib/libbsm/common/devalloc.c1723
1 files changed, 1723 insertions, 0 deletions
diff --git a/usr/src/lib/libbsm/common/devalloc.c b/usr/src/lib/libbsm/common/devalloc.c
new file mode 100644
index 0000000000..4541191349
--- /dev/null
+++ b/usr/src/lib/libbsm/common/devalloc.c
@@ -0,0 +1,1723 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <synch.h>
+#include <strings.h>
+#include <string.h>
+#include <libintl.h>
+#include <errno.h>
+#include <auth_list.h>
+#include <bsm/devices.h>
+#include <bsm/devalloc.h>
+
+#define DA_DEFS "/etc/security/tsol/devalloc_defaults"
+
+extern int _readbufline(char *, int, char *, int, int *);
+extern char *strtok_r(char *, const char *, char **);
+extern char *_strtok_escape(char *, char *, char **);
+extern int getdaon(void);
+extern int da_matchname(devalloc_t *, char *);
+extern int da_match(devalloc_t *, da_args *);
+extern int dmap_matchname(devmap_t *, char *);
+extern int dm_match(devmap_t *, da_args *);
+
+/*
+ * The following structure is for recording old entries to be retained.
+ * We read the entries from the database into a linked list in memory,
+ * then turn around and write them out again.
+ */
+typedef struct strentry {
+ struct strentry *se_next;
+ char se_str[4096 + 1];
+} strentry_t;
+
+/*
+ * da_check_longindevperm -
+ * reads /etc/logindevperm and checks if specified device is in the file.
+ * returns 1 if specified device found in /etc/logindevperm, else returns 0
+ */
+int
+da_check_logindevperm(char *devname)
+{
+ int ret = 0;
+ int fd = -1;
+ int nlen, plen, slen, lineno, fsize;
+ char line[MAX_CANON];
+ char *field_delims = " \t\n";
+ char *fbuf = NULL;
+ char *ptr, *device;
+ char *lasts = NULL;
+ FILE *fp;
+ struct stat f_stat;
+
+ /*
+ * check if /etc/logindevperm exists and get its size
+ */
+ if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1)
+ return (0);
+ if (fstat(fd, &f_stat) != 0) {
+ (void) close(fd);
+ return (0);
+ }
+ fsize = f_stat.st_size;
+ if ((fbuf = (char *)malloc(fsize)) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ free(fbuf);
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * read and parse /etc/logindevperm
+ */
+ plen = nlen = lineno = 0;
+ while (fgets(line, MAX_CANON, fp) != NULL) {
+ lineno++;
+ if ((ptr = strchr(line, '#')) != NULL)
+ *ptr = '\0'; /* handle comments */
+ if (strtok_r(line, field_delims, &lasts) == NULL)
+ continue; /* ignore blank lines */
+ if (strtok_r(NULL, field_delims, &lasts) == NULL)
+ /* invalid entry */
+ continue;
+ if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
+ /* empty device list */
+ continue;
+ nlen = strlen(ptr) + 1; /* +1 terminator */
+ nlen += (plen + 1);
+ if (plen == 0)
+ slen = snprintf(fbuf, nlen, "%s", ptr);
+ else
+ slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr);
+ if (slen >= fsize) {
+ fbuf[0] = '\0';
+ (void) fclose(fp);
+ return (slen);
+ }
+ plen += slen;
+ }
+ (void) fclose(fp);
+
+ /*
+ * check if devname exists in /etc/logindevperm
+ */
+ device = strtok_r(fbuf, ":", &lasts);
+ while (device != NULL) {
+ /*
+ * device and devname may be one of these types -
+ * /dev/xx
+ * /dev/xx*
+ * /dev/dir/xx
+ * /dev/dir/xx*
+ * /dev/dir/"*"
+ */
+ if (strcmp(device, devname) == 0) {
+ /* /dev/xx, /dev/dir/xx */
+ free(fbuf);
+ return (1);
+ }
+ if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
+ /* all wildcard types */
+ *ptr = '\0';
+ if (strncmp(device, devname, strlen(device)) == 0) {
+ free(fbuf);
+ return (1);
+ }
+ }
+ device = strtok_r(NULL, ":", &lasts);
+ }
+
+ return (ret);
+}
+
+/*
+ * _da_read_file -
+ * establishes readers/writer lock on fname; reads in the file if its
+ * contents changed since the last time we read it.
+ * returns size of buffer read, or -1 on failure.
+ */
+int
+_da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock,
+ int flag)
+{
+ int fd = -1;
+ int fsize = 0;
+ time_t newtime;
+ FILE *fp = NULL;
+ struct stat f_stat;
+
+ if (flag & DA_FORCE)
+ *ftime = 0;
+
+ /* check the size and the time stamp on the file */
+ if (rw_rdlock(flock) != 0)
+ return (-1);
+ if (stat(fname, &f_stat) != 0) {
+ (void) rw_unlock(flock);
+ return (-1);
+ }
+ fsize = f_stat.st_size;
+ newtime = f_stat.st_mtime;
+ (void) rw_unlock(flock);
+
+ while (newtime > *ftime) {
+ /*
+ * file has been modified since we last read it; or this
+ * is a forced read.
+ * read file into the buffer with rw lock.
+ */
+ if (rw_wrlock(flock) != 0)
+ return (-1);
+ if ((fp = fopen(fname, "r")) == NULL) {
+ (void) rw_unlock(flock);
+ return (-1);
+ }
+ fd = fileno(fp);
+ if (*fbuf != NULL) {
+ free(*fbuf);
+ *fbuf = NULL;
+ }
+ if ((*fbuf = malloc(fsize)) == NULL) {
+ (void) rw_unlock(flock);
+ (void) close(fd);
+ return (-1);
+ }
+ if (read(fd, *fbuf, fsize) < fsize) {
+ free(*fbuf);
+ (void) rw_unlock(flock);
+ (void) close(fd);
+ return (-1);
+ }
+ (void) rw_unlock(flock);
+ /*
+ * verify that the file did not change just after we read it.
+ */
+ if (rw_rdlock(flock) != 0) {
+ free(*fbuf);
+ (void) close(fd);
+ return (-1);
+ }
+ if (stat(fname, &f_stat) != 0) {
+ free(*fbuf);
+ (void) rw_unlock(flock);
+ (void) close(fd);
+ return (-1);
+ }
+ fsize = f_stat.st_size;
+ newtime = f_stat.st_mtime;
+ (void) rw_unlock(flock);
+ (void) close(fd);
+ *ftime = newtime;
+ }
+
+ return (fsize);
+}
+
+/*
+ * _update_zonename -
+ * add/remove current zone's name to the given devalloc_t.
+ */
+void
+_update_zonename(da_args *dargs, devalloc_t *dap)
+{
+ int i, j;
+ int oldsize, newsize;
+ int has_zonename = 0;
+ char *zonename;
+ kva_t *newkva, *oldkva;
+ kv_t *newdata, *olddata;
+ devinfo_t *devinfo;
+
+ devinfo = dargs->devinfo;
+ oldkva = dap->da_devopts;
+ if (oldkva == NULL) {
+ if (dargs->optflag & DA_REMOVE_ZONE)
+ return;
+ if (dargs->optflag & DA_ADD_ZONE) {
+ newkva = _str2kva(devinfo->devopts, KV_ASSIGN,
+ KV_TOKEN_DELIMIT);
+ if (newkva != NULL)
+ dap->da_devopts = newkva;
+ return;
+ }
+ }
+ newsize = oldsize = oldkva->length;
+ if (kva_match(oldkva, DAOPT_ZONE))
+ has_zonename = 1;
+ if (dargs->optflag & DA_ADD_ZONE) {
+ if ((zonename = index(devinfo->devopts, '=')) == NULL)
+ return;
+ zonename++;
+ if (has_zonename) {
+ (void) _insert2kva(oldkva, DAOPT_ZONE, zonename);
+ return;
+ }
+ newsize += 1;
+ } else if (dargs->optflag & DA_REMOVE_ZONE) {
+ if (has_zonename) {
+ newsize -= 1;
+ if (newsize == 0) {
+ /*
+ * If zone name was the only key/value pair,
+ * put 'reserved' in the empty slot.
+ */
+ _kva_free(oldkva);
+ dap->da_devopts = NULL;
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ newkva = _new_kva(newsize);
+ newkva->length = 0;
+ newdata = newkva->data;
+ olddata = oldkva->data;
+ for (i = 0, j = 0; i < oldsize; i++) {
+ if ((dargs->optflag & DA_REMOVE_ZONE) &&
+ (strcmp(olddata[i].key, DAOPT_ZONE) == 0))
+ continue;
+ newdata[j].key = strdup(olddata[i].key);
+ newdata[j].value = strdup(olddata[i].value);
+ newkva->length++;
+ j++;
+ }
+ if (dargs->optflag & DA_ADD_ZONE) {
+ newdata[j].key = strdup(DAOPT_ZONE);
+ newdata[j].value = strdup(zonename);
+ newkva->length++;
+ }
+ _kva_free(oldkva);
+ dap->da_devopts = newkva;
+}
+
+/*
+ * _dmap2str -
+ * converts a device_map entry into a printable string
+ * returns 0 on success, -1 on error.
+ */
+/*ARGSUSED*/
+static int
+_dmap2str(da_args *dargs, devmap_t *dmp, char *buf, int size, const char *sep)
+{
+ int length;
+
+ length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s%s",
+ dmp->dmap_devtype, sep);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s\n",
+ dmp->dmap_devlist);
+ if (length >= size)
+ return (-1);
+ return (0);
+}
+
+/*
+ * _dmap2strentry -
+ * calls dmap2str to break given devmap_t into printable entry.
+ * returns pointer to decoded entry, NULL on error.
+ */
+static strentry_t *
+_dmap2strentry(da_args *dargs, devmap_t *devmapp)
+{
+ strentry_t *sep;
+
+ if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
+ return (NULL);
+ if (_dmap2str(dargs, devmapp, sep->se_str, sizeof (sep->se_str),
+ KV_TOKEN_DELIMIT"\\\n\t") != 0) {
+ free(sep);
+ return (NULL);
+ }
+ return (sep);
+}
+
+/*
+ * fix_optstr -
+ * removes trailing ':' from buf.
+ */
+void
+fix_optstr(char *buf)
+{
+ char *p = NULL;
+
+ if (p = rindex(buf, ':'))
+ *p = ';';
+}
+
+/*
+ * _da2str -
+ * converts a device_allocate entry into a printable string
+ * returns 0 on success, -1 on error.
+ */
+static int
+_da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep,
+ const char *osep)
+{
+ int length;
+ int matching_entry = 0;
+ char **dnames;
+
+ if (dargs->optflag & DA_UPDATE &&
+ (dargs->optflag & DA_ADD_ZONE ||
+ dargs->optflag & DA_REMOVE_ZONE) &&
+ dargs->devnames) {
+ for (dnames = dargs->devnames; *dnames != NULL; dnames++) {
+ if (da_matchname(dap, *dnames)) {
+ matching_entry = 1;
+ break;
+ }
+ }
+ }
+ length = snprintf(buf, size, "%s%s", dap->da_devname, sep);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s%s",
+ dap->da_devtype, sep);
+ if (length >= size)
+ return (-1);
+ if (matching_entry)
+ _update_zonename(dargs, dap);
+ if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) &&
+ (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) {
+ length += snprintf(buf + length, size - length, "%s%s",
+ DA_RESERVED, sep);
+ } else {
+ if (_kva2str(dap->da_devopts, buf + length, size - length,
+ KV_ASSIGN, (char *)osep) != 0)
+ return (-1);
+ length = strlen(buf);
+ }
+ if (dap->da_devopts)
+ fix_optstr(buf);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s%s",
+ DA_RESERVED, sep);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s%s",
+ dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep);
+ if (length >= size)
+ return (-1);
+ length += snprintf(buf + length, size - length, "%s\n",
+ dap->da_devexec ? dap->da_devexec : "");
+ if (length >= size)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * _da2strentry -
+ * calls da2str to break given devalloc_t into printable entry.
+ * returns pointer to decoded entry, NULL on error.
+ */
+static strentry_t *
+_da2strentry(da_args *dargs, devalloc_t *dap)
+{
+ strentry_t *sep;
+
+ if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
+ return (NULL);
+ if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str),
+ KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) {
+ free(sep);
+ return (NULL);
+ }
+ return (sep);
+}
+
+/*
+ * _def2str
+ * converts da_defs_t into a printable string.
+ * returns 0 on success, -1 on error.
+ */
+static int
+_def2str(da_defs_t *da_defs, char *buf, int size, const char *sep)
+{
+ int length;
+
+ length = snprintf(buf, size, "%s%s", da_defs->devtype, sep);
+ if (length >= size)
+ return (-1);
+ if (da_defs->devopts) {
+ if (_kva2str(da_defs->devopts, buf + length, size - length,
+ KV_ASSIGN, KV_DELIMITER) != 0)
+ return (-1);
+ length = strlen(buf);
+ }
+ if (length >= size)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * _def2strentry
+ * calls _def2str to break given da_defs_t into printable entry.
+ * returns pointer decoded entry, NULL on error.
+ */
+static strentry_t *
+_def2strentry(da_defs_t *da_defs)
+{
+ strentry_t *sep;
+
+ if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
+ return (NULL);
+ if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str),
+ KV_TOKEN_DELIMIT) != 0) {
+ free(sep);
+ return (NULL);
+ }
+
+ return (sep);
+}
+
+/*
+ * _build_defattrs
+ * cycles through all defattr entries, stores them in memory. removes
+ * entries with the given search_key (device type).
+ * returns 0 if given entry not found, 1 if given entry removed, 2 on
+ * error.
+ */
+static int
+_build_defattrs(da_args *dargs, strentry_t **head_defent)
+{
+ int rc = 0;
+ da_defs_t *da_defs;
+ strentry_t *tail_str, *tmp_str;
+
+ setdadefent();
+ while ((da_defs = getdadefent()) != NULL) {
+ rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype));
+ if (rc && dargs->optflag & DA_ADD &&
+ !(dargs->optflag & DA_FORCE)) {
+ /*
+ * During DA_ADD, we keep an existing entry unless
+ * we have DA_FORCE set to override that entry.
+ */
+ dargs->optflag |= DA_NO_OVERRIDE;
+ rc = 0;
+ }
+ if (rc == 0) {
+ tmp_str = _def2strentry(da_defs);
+ if (tmp_str == NULL) {
+ freedadefent(da_defs);
+ enddadefent();
+ return (2);
+ }
+ /* retaining defattr entry: tmp_str->se_str */
+ tmp_str->se_next = NULL;
+ if (*head_defent == NULL) {
+ *head_defent = tail_str = tmp_str;
+ } else {
+ tail_str->se_next = tmp_str;
+ tail_str = tmp_str;
+ }
+ }
+ freedadefent(da_defs);
+ }
+ enddadefent();
+
+ return (rc);
+}
+
+/*
+ * _build_lists -
+ * cycles through all the entries, stores them in memory. removes entries
+ * with the given search_key (device name or type).
+ * returns 0 if given entry not found, 1 if given entry removed, 2 on
+ * error.
+ */
+static int
+_build_lists(da_args *dargs, strentry_t **head_devallocp,
+ strentry_t **head_devmapp)
+{
+ int rc = 0;
+ devalloc_t *devallocp;
+ devmap_t *devmapp;
+ strentry_t *tail_str;
+ strentry_t *tmp_str;
+
+ if (dargs->optflag & DA_MAPS_ONLY)
+ goto dmap_only;
+
+ /* build device_allocate */
+ setdaent();
+ while ((devallocp = getdaent()) != NULL) {
+ rc = da_match(devallocp, dargs);
+ if (rc && dargs->optflag & DA_ADD &&
+ !(dargs->optflag & DA_FORCE)) {
+ /*
+ * During DA_ADD, we keep an existing entry unless
+ * we have DA_FORCE set to override that entry.
+ */
+ dargs->optflag |= DA_NO_OVERRIDE;
+ rc = 0;
+ }
+ if (rc == 0) {
+ tmp_str = _da2strentry(dargs, devallocp);
+ if (tmp_str == NULL) {
+ freedaent(devallocp);
+ enddaent();
+ return (2);
+ }
+ /* retaining devalloc entry: tmp_str->se_str */
+ tmp_str->se_next = NULL;
+ if (*head_devallocp == NULL) {
+ *head_devallocp = tail_str = tmp_str;
+ } else {
+ tail_str->se_next = tmp_str;
+ tail_str = tmp_str;
+ }
+ }
+ freedaent(devallocp);
+ }
+ enddaent();
+
+dmap_only:
+ if (dargs->optflag & DA_ALLOC_ONLY)
+ return (rc);
+
+ /* build device_maps */
+ rc = 0;
+ setdmapent();
+ while ((devmapp = getdmapent()) != NULL) {
+ rc = dm_match(devmapp, dargs);
+ if (rc && dargs->optflag & DA_ADD &&
+ !(dargs->optflag & DA_FORCE)) {
+ /*
+ * During DA_ADD, we keep an existing entry unless
+ * we have DA_FORCE set to override that entry.
+ */
+ dargs->optflag |= DA_NO_OVERRIDE;
+ rc = 0;
+ }
+ if (rc == 0) {
+ tmp_str = _dmap2strentry(dargs, devmapp);
+ if (tmp_str == NULL) {
+ freedmapent(devmapp);
+ enddmapent();
+ return (2);
+ }
+ /* retaining devmap entry: tmp_str->se_str */
+ tmp_str->se_next = NULL;
+ if (*head_devmapp == NULL) {
+ *head_devmapp = tail_str = tmp_str;
+ } else {
+ tail_str->se_next = tmp_str;
+ tail_str = tmp_str;
+ }
+ }
+ freedmapent(devmapp);
+ }
+ enddmapent();
+
+ return (rc);
+}
+
+/*
+ * _write_defattrs
+ * writes current entries to devalloc_defaults.
+ */
+static void
+_write_defattrs(FILE *fp, strentry_t *head_defent)
+{
+ strentry_t *tmp_str;
+
+ for (tmp_str = head_defent; tmp_str != NULL;
+ tmp_str = tmp_str->se_next) {
+ (void) fputs(tmp_str->se_str, fp);
+ (void) fputs("\n", fp);
+ }
+
+}
+
+/*
+ * _write_device_allocate -
+ * writes current entries in the list to device_allocate.
+ */
+static void
+_write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
+{
+ int is_on = -1;
+ strentry_t *tmp_str;
+ struct stat dastat;
+
+ (void) fseek(dafp, (off_t)0, SEEK_SET);
+
+ /*
+ * if the devalloc on/off string existed before,
+ * put it back before anything else.
+ * we need to check for the string only if the file
+ * exists.
+ */
+ if (stat(odevalloc, &dastat) == 0) {
+ is_on = da_is_on();
+ if (is_on == 0)
+ (void) fputs(DA_OFF_STR, dafp);
+ else if (is_on == 1)
+ (void) fputs(DA_ON_STR, dafp);
+ }
+ tmp_str = head_devallocp;
+ while (tmp_str) {
+ (void) fputs(tmp_str->se_str, dafp);
+ (void) fputs("\n", dafp);
+ tmp_str = tmp_str->se_next;
+ }
+}
+
+/*
+ * _write_device_maps -
+ * writes current entries in the list to device_maps.
+ */
+static void
+_write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
+{
+ strentry_t *tmp_str;
+
+ (void) fseek(dmfp, (off_t)0, SEEK_SET);
+
+ tmp_str = head_devmapp;
+ while (tmp_str) {
+ (void) fputs(tmp_str->se_str, dmfp);
+ (void) fputs("\n", dmfp);
+ tmp_str = tmp_str->se_next;
+ }
+}
+
+/*
+ * _write_new_defattrs
+ * writes the new entry to devalloc_defaults.
+ * returns 0 on success, -1 on error.
+ */
+static int
+_write_new_defattrs(FILE *fp, da_args *dargs)
+{
+ int count;
+ char *tok = NULL, *tokp = NULL;
+ char *lasts;
+ devinfo_t *devinfo = dargs->devinfo;
+
+ if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
+ return (-1);
+ if (!devinfo->devopts)
+ return (0);
+ (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
+ KV_TOKEN_DELIMIT);
+ if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
+ (void) strcpy(tokp, devinfo->devopts);
+ if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
+ (void) fprintf(fp, "%s", tok);
+ count = 1;
+ }
+ while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
+ if (count)
+ (void) fprintf(fp, "%s", KV_DELIMITER);
+ (void) fprintf(fp, "%s", tok);
+ count++;
+ }
+ } else {
+ (void) fprintf(fp, "%s", devinfo->devopts);
+ }
+
+ return (0);
+}
+
+/*
+ * _write_new_entry -
+ * writes the new devalloc_t to device_allocate or the new devmap_t to
+ * device_maps.
+ * returns 0 on success, -1 on error.
+ */
+static int
+_write_new_entry(FILE *fp, da_args *dargs, int flag)
+{
+ int count;
+ char *tok = NULL, *tokp = NULL;
+ char *lasts;
+ devinfo_t *devinfo = dargs->devinfo;
+
+ if (flag & DA_MAPS_ONLY)
+ goto dmap_only;
+
+ if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
+ return (-1);
+
+ (void) fprintf(fp, "%s%s\\\n\t",
+ (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
+ (void) fprintf(fp, "%s%s\\\n\t",
+ (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
+ if (devinfo->devopts == NULL) {
+ (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
+ KV_DELIMITER);
+ } else {
+ if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) {
+ (void) strcpy(tokp, devinfo->devopts);
+ if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
+ NULL) {
+ (void) fprintf(fp, "%s", tok);
+ count = 1;
+ }
+ while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
+ &lasts)) != NULL) {
+ if (count)
+ (void) fprintf(fp, "%s",
+ KV_TOKEN_DELIMIT "\\\n\t");
+ (void) fprintf(fp, "%s", tok);
+ count++;
+ }
+ if (count)
+ (void) fprintf(fp, "%s",
+ KV_DELIMITER "\\\n\t");
+ } else {
+ (void) fprintf(fp, "%s%s", devinfo->devopts,
+ KV_DELIMITER "\\\n\t");
+ }
+ }
+ (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
+ (void) fprintf(fp, "%s%s\\\n\t",
+ (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
+ KV_DELIMITER);
+ (void) fprintf(fp, "%s\n",
+ (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
+
+dmap_only:
+ if (flag & DA_ALLOC_ONLY)
+ return (0);
+
+ if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
+ return (-1);
+
+ (void) fprintf(fp, "%s%s\\\n",
+ (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
+ (void) fprintf(fp, "\t%s%s\\\n",
+ (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
+ (void) fprintf(fp, "\t%s\n",
+ (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
+
+ return (0);
+}
+
+/*
+ * _da_lock_devdb -
+ * locks the database files; lock can be either broken explicitly by
+ * closing the fd of the lock file, or it expires automatically at process
+ * termination.
+ * returns fd of the lock file or -1 on error.
+ */
+int
+_da_lock_devdb(char *rootdir)
+{
+ int lockfd = -1;
+ char *lockfile;
+ char path[MAXPATHLEN];
+ int size = sizeof (path);
+
+ if (rootdir == NULL) {
+ lockfile = DA_DB_LOCK;
+ } else {
+ path[0] = '\0';
+ if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
+ return (-1);
+ lockfile = path;
+ }
+
+ if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
+ /* cannot open lock file */
+ return (-1);
+
+ (void) fchown(lockfd, DA_UID, DA_GID);
+
+ if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
+ /* cannot position lock file */
+ (void) close(lockfd);
+ return (-1);
+ }
+ if (lockf(lockfd, F_TLOCK, 0) == -1) {
+ /* cannot set lock */
+ (void) close(lockfd);
+ return (-1);
+ }
+ (void) utime(lockfile, NULL);
+
+ return (lockfd);
+}
+
+/*
+ * da_open_devdb -
+ * opens one or both database files - device_allocate, device_maps - in
+ * the specified mode.
+ * locks the database files; lock is either broken explicitly by the
+ * caller by closing the lock file fd, or it expires automatically at
+ * process termination.
+ * writes the file pointer of opened file in the input args - dafp, dmfp.
+ * returns fd of the lock file on success, -2 if database file does not
+ * exist, -1 on other errors.
+ */
+int
+da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
+{
+ int oflag = 0;
+ int fda = -1;
+ int fdm = -1;
+ int lockfd = -1;
+ char *fname;
+ char *fmode;
+ char path[MAXPATHLEN];
+ FILE *devfile;
+
+ if ((dafp == NULL) && (dmfp == NULL))
+ return (-1);
+
+ if (flag & DA_RDWR) {
+ oflag = DA_RDWR;
+ fmode = "r+";
+ } else if (flag & DA_RDONLY) {
+ oflag = DA_RDONLY;
+ fmode = "r";
+ }
+
+ if ((lockfd = _da_lock_devdb(rootdir)) == -1)
+ return (-1);
+
+ if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
+ goto dmap_only;
+
+ path[0] = '\0';
+
+ /*
+ * open the device allocation file
+ */
+ if (rootdir == NULL) {
+ fname = DEVALLOC;
+ } else {
+ if (snprintf(path, sizeof (path), "%s%s", rootdir,
+ DEVALLOC) >= sizeof (path)) {
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return (-1);
+ }
+ fname = path;
+ }
+ if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return ((errno == ENOENT) ? -2 : -1);
+ }
+ if ((devfile = fdopen(fda, fmode)) == NULL) {
+ (void) close(fda);
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return (-1);
+ }
+ *dafp = devfile;
+ (void) fchmod(fda, DA_DBMODE);
+
+ if ((flag & DA_ALLOC_ONLY))
+ goto out;
+
+dmap_only:
+ path[0] = '\0';
+ /*
+ * open the device map file
+ */
+ if (rootdir == NULL) {
+ fname = DEVMAP;
+ } else {
+ if (snprintf(path, sizeof (path), "%s%s", rootdir,
+ DEVMAP) >= sizeof (path)) {
+ (void) close(fda);
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return (-1);
+ }
+ fname = path;
+ }
+
+ if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return ((errno == ENOENT) ? -2 : -1);
+ }
+
+ if ((devfile = fdopen(fdm, fmode)) == NULL) {
+ (void) close(fdm);
+ (void) close(fda);
+ if (lockfd != -1)
+ (void) close(lockfd);
+ return (-1);
+ }
+ *dmfp = devfile;
+ (void) fchmod(fdm, DA_DBMODE);
+
+out:
+ return (lockfd);
+}
+
+/*
+ * _record_on_off -
+ * adds either DA_ON_STR or DA_OFF_STR to device_allocate
+ * returns 0 on success, -1 on error.
+ */
+static int
+_record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
+{
+ int dafd;
+ int nsize;
+ int nitems = 1;
+ int actionlen;
+ int str_found = 0;
+ int len = 0, nlen = 0, plen = 0;
+ char *ptr = NULL;
+ char *actionstr;
+ char *nbuf = NULL;
+ char line[MAX_CANON];
+ struct stat dastat;
+
+ if (dargs->optflag & DA_ON)
+ actionstr = DA_ON_STR;
+ else
+ actionstr = DA_OFF_STR;
+ actionlen = strlen(actionstr);
+ dafd = fileno(dafp);
+ if (fstat(dafd, &dastat) == -1)
+ return (-1);
+
+ /* check the old device_allocate for on/off string */
+ ptr = fgets(line, MAX_CANON, dafp);
+ if (ptr != NULL) {
+ if ((strcmp(line, DA_ON_STR) == 0) ||
+ (strcmp(line, DA_OFF_STR) == 0)) {
+ str_found = 1;
+ nsize = dastat.st_size;
+ }
+ }
+ if (!ptr || !str_found) {
+ /*
+ * the file never had either the on or the off string;
+ * make room for it.
+ */
+ str_found = 0;
+ nsize = dastat.st_size + actionlen + 1;
+ }
+ if ((nbuf = (char *)malloc(nsize)) == NULL)
+ return (-1);
+ nbuf[0] = '\0';
+ /* put the on/off string */
+ (void) strcpy(nbuf, actionstr);
+ nlen = strlen(nbuf);
+ plen = nlen;
+ if (ptr && !str_found) {
+ /* now put the first line that we read in fgets */
+ nlen = plen + strlen(line) + 1;
+ len = snprintf(nbuf + plen, nlen - plen, "%s", line);
+ if (len >= nsize) {
+ free(nbuf);
+ return (-1);
+ }
+ plen += len;
+ }
+
+ /* now get the rest of the old file */
+ while (fgets(line, MAX_CANON, dafp) != NULL) {
+ nlen = plen + strlen(line) + 1;
+ len = snprintf(nbuf + plen, nlen - plen, "%s", line);
+ if (len >= nsize) {
+ free(nbuf);
+ return (-1);
+ }
+ plen += len;
+ }
+ len = strlen(nbuf) + 1;
+ if (len < nsize)
+ nbuf[len] = '\n';
+
+ /* write the on/off str + the old device_allocate to the temp file */
+ if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
+ free(nbuf);
+ return (-1);
+ }
+
+ free(nbuf);
+
+ return (0);
+}
+
+/*
+ * da_update_defattrs -
+ * writes default attributes to devalloc_defaults
+ * returns 0 on success, -1 on error.
+ */
+int
+da_update_defattrs(da_args *dargs)
+{
+ int rc = 0, lockfd = 0, tmpfd = 0;
+ char *defpath = DEFATTRS;
+ char *tmpdefpath = TMPATTRS;
+ FILE *tmpfp = NULL;
+ struct stat dstat;
+ strentry_t *head_defent = NULL;
+
+ if (dargs == NULL)
+ return (0);
+ if ((lockfd = _da_lock_devdb(NULL)) == -1)
+ return (-1);
+ if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
+ (void) close(lockfd);
+ return (-1);
+ }
+ (void) fchown(tmpfd, DA_UID, DA_GID);
+ if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
+ (void) close(tmpfd);
+ (void) unlink(tmpdefpath);
+ (void) close(lockfd);
+ return (-1);
+ }
+ /*
+ * examine all entries, remove an old one if required, check
+ * if a new one needs to be added.
+ */
+ if (stat(defpath, &dstat) == 0) {
+ if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
+ if (rc == 1) {
+ (void) close(tmpfd);
+ (void) unlink(tmpdefpath);
+ (void) close(lockfd);
+ return (rc);
+ }
+ }
+ }
+ /*
+ * write back any existing entries.
+ */
+ _write_defattrs(tmpfp, head_defent);
+
+ if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
+ /* add new entries */
+ rc = _write_new_defattrs(tmpfp, dargs);
+ (void) fclose(tmpfp);
+ } else {
+ (void) fclose(tmpfp);
+ }
+ if (rename(tmpdefpath, defpath) != 0) {
+ rc = -1;
+ (void) unlink(tmpdefpath);
+ }
+ (void) close(lockfd);
+
+ return (rc);
+}
+
+/*
+ * da_update_device -
+ * writes devices entries to device_allocate and device_maps.
+ * returns 0 on success, -1 on error.
+ */
+int
+da_update_device(da_args *dargs)
+{
+ int rc;
+ int tafd = -1, tmfd = -1;
+ int lockfd = -1;
+ char *rootdir = NULL;
+ char *apathp = NULL, *mpathp = NULL, *dapathp = NULL,
+ *dmpathp = NULL;
+ char apath[MAXPATHLEN], mpath[MAXPATHLEN],
+ dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
+ FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL;
+ struct stat dastat;
+ devinfo_t *devinfo;
+ strentry_t *head_devmapp = NULL;
+ strentry_t *head_devallocp = NULL;
+
+ if (dargs == NULL)
+ return (0);
+
+ rootdir = dargs->rootdir;
+ devinfo = dargs->devinfo;
+
+ /*
+ * adding/removing entries should be done in both
+ * device_allocate and device_maps. updates can be
+ * done in both or either of the files.
+ */
+ if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
+ if (dargs->optflag & DA_ALLOC_ONLY ||
+ dargs->optflag & DA_MAPS_ONLY)
+ return (0);
+ }
+
+ /*
+ * name, type and list are required fields for adding a new
+ * device.
+ */
+ if ((dargs->optflag & DA_ADD) &&
+ ((devinfo->devname == NULL) ||
+ (devinfo->devtype == NULL) ||
+ (devinfo->devlist == NULL))) {
+ return (-1);
+ }
+
+ if (rootdir != NULL) {
+ if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
+ TMPALLOC) >= sizeof (apath))
+ return (-1);
+ apathp = apath;
+ if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
+ DEVALLOC) >= sizeof (dapath))
+ return (-1);
+ dapathp = dapath;
+ if (!(dargs->optflag & DA_ALLOC_ONLY)) {
+ if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
+ TMPMAP) >= sizeof (mpath))
+ return (-1);
+ mpathp = mpath;
+ if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
+ DEVMAP) >= sizeof (dmpath))
+ return (-1);
+ dmpathp = dmpath;
+ }
+ } else {
+ apathp = TMPALLOC;
+ dapathp = DEVALLOC;
+ mpathp = TMPMAP;
+ dmpathp = DEVMAP;
+ }
+
+ if (dargs->optflag & DA_MAPS_ONLY)
+ goto dmap_only;
+
+ /*
+ * Check if we are here just to record on/off status of
+ * device_allocation.
+ */
+ if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
+ lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
+ DA_RDONLY|DA_ALLOC_ONLY);
+ else
+ lockfd = _da_lock_devdb(rootdir);
+ if (lockfd == -1)
+ return (-1);
+
+ if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
+ (void) close(lockfd);
+ (void) fclose(dafp);
+ return (-1);
+ }
+ (void) fchown(tafd, DA_UID, DA_GID);
+ if ((tafp = fdopen(tafd, "r+")) == NULL) {
+ (void) close(tafd);
+ (void) unlink(apathp);
+ (void) fclose(dafp);
+ (void) close(lockfd);
+ return (-1);
+ }
+
+ /*
+ * We don't need to parse the file if we are here just to record
+ * on/off status of device_allocation.
+ */
+ if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
+ if (_record_on_off(dargs, tafp, dafp) == -1) {
+ (void) close(tafd);
+ (void) unlink(apathp);
+ (void) fclose(dafp);
+ (void) close(lockfd);
+ return (-1);
+ }
+ (void) fclose(dafp);
+ goto out;
+ }
+
+ /*
+ * examine all the entries, remove an old one if forced to,
+ * and check that they are suitable for updating.
+ * we need to do this only if the file exists already.
+ */
+ if (stat(dapathp, &dastat) == 0) {
+ if ((rc = _build_lists(dargs, &head_devallocp,
+ &head_devmapp)) != 0) {
+ if (rc != 1) {
+ (void) close(tafd);
+ (void) unlink(apathp);
+ (void) close(lockfd);
+ return (rc);
+ }
+ }
+ }
+
+ /*
+ * write back any existing devalloc entries, along with
+ * the devalloc on/off string.
+ */
+ _write_device_allocate(dapathp, tafp, head_devallocp);
+
+ if (dargs->optflag & DA_ALLOC_ONLY)
+ goto out;
+
+dmap_only:
+ if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
+ (void) close(tafd);
+ (void) unlink(apathp);
+ (void) close(lockfd);
+ return (-1);
+ }
+ (void) fchown(tmfd, DA_UID, DA_GID);
+ if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
+ (void) close(tafd);
+ (void) unlink(apathp);
+ (void) close(tmfd);
+ (void) unlink(mpathp);
+ (void) close(lockfd);
+ return (-1);
+ }
+
+ /* write back any existing devmap entries */
+ if (head_devmapp != NULL)
+ _write_device_maps(tmfp, head_devmapp);
+
+out:
+ if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
+ /* add any new entries */
+ rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
+ (void) fclose(tafp);
+
+ if (rc == 0)
+ rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
+ (void) fclose(tmfp);
+ } else {
+ if (tafp)
+ (void) fclose(tafp);
+ if (tmfp)
+ (void) fclose(tmfp);
+ }
+
+ rc = 0;
+ if (!(dargs->optflag & DA_MAPS_ONLY)) {
+ if (rename(apathp, dapathp) != 0) {
+ rc = -1;
+ (void) unlink(apathp);
+ }
+ }
+ if (!(dargs->optflag & DA_ALLOC_ONLY)) {
+ if (rename(mpathp, dmpathp) != 0) {
+ rc = -1;
+ (void) unlink(mpathp);
+ }
+ }
+
+ (void) close(lockfd);
+
+ return (rc);
+}
+
+/*
+ * da_add_list -
+ * adds new /dev link name to the linked list of devices.
+ * returns 0 if link added successfully, -1 on error.
+ */
+int
+da_add_list(devlist_t *dlist, char *link, int new_instance, int flag)
+{
+ int instance;
+ int nlen, plen;
+ int new_entry = 0;
+ char *dtype, *dexec, *tname, *kval;
+ char *minstr = NULL, *maxstr = NULL;
+ char dname[DA_MAXNAME];
+ kva_t *kva;
+ deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL;
+ da_defs_t *da_defs;
+
+ if (dlist == NULL || link == NULL)
+ return (-1);
+
+ dname[0] = '\0';
+ if (flag & DA_AUDIO) {
+ dentry = dlist->audio;
+ tname = DA_AUDIO_NAME;
+ dtype = DA_AUDIO_TYPE;
+ dexec = DA_DEFAULT_AUDIO_CLEAN;
+ } else if (flag & DA_CD) {
+ dentry = dlist->cd;
+ tname = DA_CD_NAME;
+ dtype = DA_CD_TYPE;
+ dexec = DA_DEFAULT_DISK_CLEAN;
+ } else if (flag & DA_FLOPPY) {
+ dentry = dlist->floppy;
+ tname = DA_FLOPPY_NAME;
+ dtype = DA_FLOPPY_TYPE;
+ dexec = DA_DEFAULT_DISK_CLEAN;
+ } else if (flag & DA_TAPE) {
+ dentry = dlist->tape;
+ tname = DA_TAPE_NAME;
+ dtype = DA_TAPE_TYPE;
+ dexec = DA_DEFAULT_TAPE_CLEAN;
+ } else if (flag & DA_RMDISK) {
+ dentry = dlist->rmdisk;
+ tname = DA_RMDISK_NAME;
+ dtype = DA_RMDISK_TYPE;
+ dexec = DA_DEFAULT_DISK_CLEAN;
+ } else {
+ return (-1);
+ }
+
+ for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
+ pentry = nentry;
+ (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
+ if (nentry->devinfo.instance == new_instance)
+ /*
+ * Add the new link name to the list of links
+ * that the device 'dname' has.
+ */
+ break;
+ }
+
+ if (nentry == NULL) {
+ /*
+ * Either this is the first entry ever, or no matching entry
+ * was found. Create a new one and add to the list.
+ */
+ if (dentry == NULL) /* first entry ever */
+ instance = 0;
+ else /* no matching entry */
+ instance++;
+ (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
+ if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) ==
+ NULL)
+ return (-1);
+ if (pentry != NULL)
+ pentry->next = nentry;
+ new_entry = 1;
+ nentry->devinfo.devname = strdup(dname);
+ nentry->devinfo.devtype = dtype;
+ nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
+ nentry->devinfo.devexec = dexec;
+ nentry->devinfo.instance = new_instance;
+ /*
+ * Look for default label range, authorizations and cleaning
+ * program in devalloc_defaults. If label range is not
+ * specified in devalloc_defaults, assume it to be admin_low
+ * to admin_high.
+ */
+ minstr = DA_DEFAULT_MIN;
+ maxstr = DA_DEFAULT_MAX;
+ setdadefent();
+ if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
+ kva = da_defs->devopts;
+ if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL)
+ minstr = strdup(kval);
+ if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL)
+ maxstr = strdup(kval);
+ if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
+ nentry->devinfo.devauths = strdup(kval);
+ if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
+ nentry->devinfo.devexec = strdup(kval);
+ freedadefent(da_defs);
+ }
+ enddadefent();
+ kval = NULL;
+ nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
+ strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
+ strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
+ + 1; /* +1 for terminator */
+ if (kval = (char *)malloc(nlen))
+ (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
+ DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
+ DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
+ nentry->devinfo.devopts = kval;
+
+ nentry->devinfo.devlist = NULL;
+ nentry->next = NULL;
+ }
+
+ nlen = strlen(link) + 1; /* +1 terminator */
+ if (nentry->devinfo.devlist) {
+ plen = strlen(nentry->devinfo.devlist);
+ nlen = nlen + plen + 1; /* +1 for blank to separate entries */
+ } else {
+ plen = 0;
+ }
+
+ if ((nentry->devinfo.devlist =
+ (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
+ if (new_entry) {
+ nentry->devinfo.devname = NULL;
+ free(nentry->devinfo.devname);
+ nentry = NULL;
+ free(nentry);
+ if (pentry != NULL)
+ pentry->next = NULL;
+ }
+ return (-1);
+ }
+
+ if (plen == 0)
+ (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
+ else
+ (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
+ " %s", link);
+
+ if (pentry == NULL) {
+ /*
+ * This is the first entry of this device type.
+ */
+ if (flag & DA_AUDIO)
+ dlist->audio = nentry;
+ else if (flag & DA_CD)
+ dlist->cd = nentry;
+ else if (flag & DA_FLOPPY)
+ dlist->floppy = nentry;
+ else if (flag & DA_TAPE)
+ dlist->tape = nentry;
+ else if (flag & DA_RMDISK)
+ dlist->rmdisk = nentry;
+ }
+
+ return (0);
+}
+
+/*
+ * da_remove_list -
+ * removes a /dev link name from the linked list of devices.
+ * returns type of device if link for that device removed
+ * successfully, else returns -1 on error.
+ * if all links for a device are removed, stores that device
+ * name in devname.
+ */
+int
+da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size)
+{
+ int flag;
+ int remove_dev = 0;
+ int nlen, plen, slen;
+ char *lasts, *lname, *oldlist;
+ struct stat rmstat;
+ deventry_t *dentry, *current, *prev;
+
+ if (type != NULL)
+ flag = type;
+ else if (link == NULL)
+ return (-1);
+ else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
+ flag = DA_AUDIO;
+ else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
+ strstr(link, "sr") || strstr(link, "rsr"))
+ flag = DA_CD;
+ else if (strstr(link, "fd") || strstr(link, "rfd") ||
+ strstr(link, "diskette") || strstr(link, "rdiskette"))
+ flag = DA_FLOPPY;
+ else if (strstr(link, DA_TAPE_NAME))
+ flag = DA_TAPE;
+ else
+ flag = DA_RMDISK;
+
+ switch (type) {
+ case DA_AUDIO:
+ dentry = dlist->audio;
+ break;
+ case DA_CD:
+ dentry = dlist->cd;
+ break;
+ case DA_FLOPPY:
+ dentry = dlist->floppy;
+ break;
+ case DA_TAPE:
+ dentry = dlist->tape;
+ break;
+ case DA_RMDISK:
+ dentry = dlist->rmdisk;
+ break;
+ default:
+ return (-1);
+ }
+
+ if ((type != NULL) && (link == NULL)) {
+ for (current = dentry, prev = dentry; current != NULL;
+ current = current->next) {
+ oldlist = strdup(current->devinfo.devlist);
+ for (lname = strtok_r(oldlist, " ", &lasts);
+ lname != NULL;
+ lname = strtok_r(NULL, " ", &lasts)) {
+ if (stat(lname, &rmstat) != 0) {
+ remove_dev = 1;
+ goto remove_dev;
+ }
+ }
+ prev = current;
+ }
+ return (-1);
+ }
+
+ for (current = dentry, prev = dentry; current != NULL;
+ current = current->next) {
+ plen = strlen(current->devinfo.devlist);
+ nlen = strlen(link);
+ if (plen == nlen) {
+ if (strcmp(current->devinfo.devlist, link) == 0) {
+ /* last name in the list */
+ remove_dev = 1;
+ break;
+ }
+ }
+ if (strstr(current->devinfo.devlist, link)) {
+ nlen = plen - nlen + 1;
+ oldlist = strdup(current->devinfo.devlist);
+ if ((current->devinfo.devlist =
+ (char *)realloc(current->devinfo.devlist,
+ nlen)) == NULL) {
+ free(oldlist);
+ return (-1);
+ }
+ current->devinfo.devlist[0] = '\0';
+ nlen = plen = slen = 0;
+ for (lname = strtok_r(oldlist, " ", &lasts);
+ lname != NULL;
+ lname = strtok_r(NULL, " ", &lasts)) {
+ if (strcmp(lname, link) == 0)
+ continue;
+ nlen = strlen(lname) + plen + 1;
+ if (plen == 0) {
+ slen =
+ snprintf(current->devinfo.devlist,
+ nlen, "%s", lname);
+ } else {
+ slen =
+ snprintf(current->devinfo.devlist +
+ plen, nlen - plen, " %s",
+ lname);
+ }
+ plen = plen + slen + 1;
+ }
+ free(oldlist);
+ break;
+ }
+ prev = current;
+ }
+
+remove_dev:
+ if (remove_dev == 1) {
+ (void) strlcpy(devname, current->devinfo.devname, size);
+ free(current->devinfo.devname);
+ free(current->devinfo.devlist);
+ current->devinfo.devname = current->devinfo.devlist = NULL;
+ prev->next = current->next;
+ free(current);
+ current = NULL;
+ }
+ if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
+ if (prev->next) {
+ /*
+ * what we removed above was the first entry
+ * in the list. make the next entry to be the
+ * first.
+ */
+ current = prev->next;
+ } else {
+ /*
+ * the matching entry was the only entry in the list
+ * for this type.
+ */
+ current = NULL;
+ }
+ if (flag & DA_AUDIO)
+ dlist->audio = current;
+ else if (flag & DA_CD)
+ dlist->cd = current;
+ else if (flag & DA_FLOPPY)
+ dlist->floppy = current;
+ else if (flag & DA_TAPE)
+ dlist->tape = current;
+ else if (flag & DA_RMDISK)
+ dlist->rmdisk = current;
+ }
+
+ return (flag);
+}
+
+/*
+ * da_is_on -
+ * checks if device allocation feature is turned on.
+ * returns 1 if on, 0 if off, -1 if status string not
+ * found in device_allocate.
+ */
+int
+da_is_on()
+{
+ return (getdaon());
+}
+
+/*
+ * da_print_device -
+ * debug routine to print device entries.
+ */
+void
+da_print_device(int flag, devlist_t *devlist)
+{
+ deventry_t *entry, *dentry;
+ devinfo_t *devinfo;
+
+ if (flag & DA_AUDIO)
+ dentry = devlist->audio;
+ else if (flag & DA_CD)
+ dentry = devlist->cd;
+ else if (flag & DA_FLOPPY)
+ dentry = devlist->floppy;
+ else if (flag & DA_TAPE)
+ dentry = devlist->tape;
+ else if (flag & DA_RMDISK)
+ dentry = devlist->rmdisk;
+ else
+ return;
+
+ for (entry = dentry; entry != NULL; entry = entry->next) {
+ devinfo = &(entry->devinfo);
+ (void) fprintf(stdout, "name: %s\n", devinfo->devname);
+ (void) fprintf(stdout, "type: %s\n", devinfo->devtype);
+ (void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
+ (void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
+ (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
+ }
+}