2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <ctype.h>
2N/A#include <unistd.h>
2N/A#include <limits.h>
2N/A#include <fcntl.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <utime.h>
2N/A#include <synch.h>
2N/A#include <strings.h>
2N/A#include <string.h>
2N/A#include <libintl.h>
2N/A#include <errno.h>
2N/A#include <auth_list.h>
2N/A#include <syslog.h>
2N/A#include <dev_alloc.h>
2N/A#include <secdb.h>
2N/A#include <devalloc_impl.h>
2N/A#include <tsol/label.h>
2N/A
2N/A#define DA_DEFS "/etc/security/tsol/devalloc_defaults"
2N/A
2N/Aextern int _readbufline(char *, int, char *, int, int *);
2N/Aextern char *strtok_r(char *, const char *, char **);
2N/Aextern int getdaon(void);
2N/Aextern int da_matchname(devalloc_t *, char *);
2N/Aextern int da_match(devalloc_t *, da_args *);
2N/Aextern int dmap_matchname(devmap_t *, char *);
2N/Aextern int dm_match(devmap_t *, da_args *);
2N/Aextern int dmap_matchtype(devmap_t *dmap, char *type);
2N/Aextern int dmap_matchdev(devmap_t *dmap, char *dev);
2N/Aextern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
2N/Aextern char *dmap_physname(devmap_t *dmap);
2N/A
2N/A/*
2N/A * The following structure is for recording old entries to be retained.
2N/A * We read the entries from the database into a linked list in memory,
2N/A * then turn around and write them out again.
2N/A */
2N/Atypedef struct strentry {
2N/A struct strentry *se_next;
2N/A char se_str[4096 + 1];
2N/A} strentry_t;
2N/A
2N/A/*
2N/A * da_check_link -
2N/A * check if the link should be included in device_maps
2N/A * return 1 if true, 0 if false.
2N/A */
2N/Aint
2N/Ada_check_link(const char *link)
2N/A{
2N/A if (strchr(link, ':') != NULL)
2N/A return (0);
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * da_check_longindevperm -
2N/A * reads /etc/logindevperm and checks if specified device is in the file.
2N/A * returns 1 if specified device found in /etc/logindevperm, else returns 0
2N/A */
2N/Aint
2N/Ada_check_logindevperm(char *devname)
2N/A{
2N/A int ret = 0;
2N/A int fd = -1;
2N/A int nlen, plen, slen, lineno, fsize;
2N/A char line[MAX_CANON];
2N/A char *field_delims = " \t\n";
2N/A char *fbuf = NULL;
2N/A char *ptr, *device;
2N/A char *lasts = NULL;
2N/A FILE *fp;
2N/A struct stat f_stat;
2N/A
2N/A /*
2N/A * check if /etc/logindevperm exists and get its size
2N/A */
2N/A if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1)
2N/A return (0);
2N/A if (fstat(fd, &f_stat) != 0) {
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A fsize = f_stat.st_size;
2N/A if ((fbuf = (char *)malloc(fsize)) == NULL) {
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A if ((fp = fdopen(fd, "rF")) == NULL) {
2N/A free(fbuf);
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * read and parse /etc/logindevperm
2N/A */
2N/A plen = nlen = lineno = 0;
2N/A while (fgets(line, MAX_CANON, fp) != NULL) {
2N/A lineno++;
2N/A if ((ptr = strchr(line, '#')) != NULL)
2N/A *ptr = '\0'; /* handle comments */
2N/A if (strtok_r(line, field_delims, &lasts) == NULL)
2N/A continue; /* ignore blank lines */
2N/A if (strtok_r(NULL, field_delims, &lasts) == NULL)
2N/A /* invalid entry */
2N/A continue;
2N/A if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
2N/A /* empty device list */
2N/A continue;
2N/A nlen = strlen(ptr) + 1; /* +1 terminator */
2N/A nlen += (plen + 1);
2N/A if (plen == 0)
2N/A slen = snprintf(fbuf, nlen, "%s", ptr);
2N/A else
2N/A slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr);
2N/A if (slen >= fsize) {
2N/A free(fbuf);
2N/A (void) fclose(fp);
2N/A return (slen);
2N/A }
2N/A plen += slen;
2N/A }
2N/A (void) fclose(fp);
2N/A
2N/A /*
2N/A * check if devname exists in /etc/logindevperm
2N/A */
2N/A device = strtok_r(fbuf, ":", &lasts);
2N/A while (device != NULL) {
2N/A /*
2N/A * device and devname may be one of these types -
2N/A * /dev/xx
2N/A * /dev/xx*
2N/A * /dev/dir/xx
2N/A * /dev/dir/xx*
2N/A * /dev/dir/"*"
2N/A */
2N/A if (strcmp(device, devname) == 0) {
2N/A /* /dev/xx, /dev/dir/xx */
2N/A free(fbuf);
2N/A return (1);
2N/A }
2N/A if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
2N/A /* all wildcard types */
2N/A *ptr = '\0';
2N/A if (strncmp(device, devname, strlen(device)) == 0) {
2N/A free(fbuf);
2N/A return (1);
2N/A }
2N/A }
2N/A device = strtok_r(NULL, ":", &lasts);
2N/A }
2N/A
2N/A free(fbuf);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * _da_read_file -
2N/A * establishes readers/writer lock on fname; reads in the file if its
2N/A * contents changed since the last time we read it.
2N/A * returns size of buffer read, or -1 on failure.
2N/A */
2N/Aint
2N/A_da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock,
2N/A int flag)
2N/A{
2N/A int fd = -1;
2N/A int fsize = 0;
2N/A time_t newtime;
2N/A struct stat f_stat;
2N/A
2N/A if (flag & DA_FORCE)
2N/A *ftime = 0;
2N/A
2N/A /* check the size and the time stamp on the file */
2N/A if (rw_rdlock(flock) != 0)
2N/A return (-1);
2N/A if (stat(fname, &f_stat) != 0) {
2N/A (void) rw_unlock(flock);
2N/A return (-1);
2N/A }
2N/A fsize = f_stat.st_size;
2N/A newtime = f_stat.st_mtime;
2N/A (void) rw_unlock(flock);
2N/A
2N/A while (newtime > *ftime) {
2N/A /*
2N/A * file has been modified since we last read it; or this
2N/A * is a forced read.
2N/A * read file into the buffer with rw lock.
2N/A */
2N/A if (rw_wrlock(flock) != 0)
2N/A return (-1);
2N/A if ((fd = open(fname, O_RDONLY)) == -1) {
2N/A (void) rw_unlock(flock);
2N/A return (-1);
2N/A }
2N/A if (*fbuf != NULL) {
2N/A free(*fbuf);
2N/A *fbuf = NULL;
2N/A }
2N/A if ((*fbuf = malloc(fsize)) == NULL) {
2N/A (void) rw_unlock(flock);
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A if (read(fd, *fbuf, fsize) < fsize) {
2N/A free(*fbuf);
2N/A (void) rw_unlock(flock);
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A (void) rw_unlock(flock);
2N/A /*
2N/A * verify that the file did not change just after we read it.
2N/A */
2N/A if (rw_rdlock(flock) != 0) {
2N/A free(*fbuf);
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A if (stat(fname, &f_stat) != 0) {
2N/A free(*fbuf);
2N/A (void) rw_unlock(flock);
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A fsize = f_stat.st_size;
2N/A newtime = f_stat.st_mtime;
2N/A (void) rw_unlock(flock);
2N/A (void) close(fd);
2N/A *ftime = newtime;
2N/A }
2N/A
2N/A return (fsize);
2N/A}
2N/A
2N/A/*
2N/A * _update_zonename -
2N/A * add/remove current zone's name to the given devalloc_t.
2N/A */
2N/Avoid
2N/A_update_zonename(da_args *dargs, devalloc_t *dap)
2N/A{
2N/A int i, j;
2N/A int oldsize, newsize;
2N/A int has_zonename = 0;
2N/A char *zonename;
2N/A kva_t *newkva, *oldkva;
2N/A kv_t *newdata, *olddata;
2N/A da_devinfo_t *devinfo;
2N/A
2N/A devinfo = dargs->devinfo;
2N/A oldkva = dap->da_devopts;
2N/A if (oldkva == NULL) {
2N/A if (dargs->optflag & DA_REMOVE_ZONE)
2N/A return;
2N/A if (dargs->optflag & DA_ADD_ZONE) {
2N/A newkva = _str2kva(devinfo->devopts, KV_ASSIGN,
2N/A KV_TOKEN_DELIMIT);
2N/A if (newkva != NULL)
2N/A dap->da_devopts = newkva;
2N/A return;
2N/A }
2N/A }
2N/A newsize = oldsize = oldkva->length;
2N/A if (kva_match(oldkva, DAOPT_ZONE))
2N/A has_zonename = 1;
2N/A if (dargs->optflag & DA_ADD_ZONE) {
2N/A if ((zonename = index(devinfo->devopts, '=')) == NULL)
2N/A return;
2N/A zonename++;
2N/A if (has_zonename) {
2N/A (void) _insert2kva(oldkva, DAOPT_ZONE, zonename);
2N/A return;
2N/A }
2N/A newsize += 1;
2N/A } else if (dargs->optflag & DA_REMOVE_ZONE) {
2N/A if (has_zonename) {
2N/A newsize -= 1;
2N/A if (newsize == 0) {
2N/A /*
2N/A * If zone name was the only key/value pair,
2N/A * put 'reserved' in the empty slot.
2N/A */
2N/A _kva_free(oldkva);
2N/A dap->da_devopts = NULL;
2N/A return;
2N/A }
2N/A } else {
2N/A return;
2N/A }
2N/A }
2N/A newkva = _new_kva(newsize);
2N/A newkva->length = 0;
2N/A newdata = newkva->data;
2N/A olddata = oldkva->data;
2N/A for (i = 0, j = 0; i < oldsize; i++) {
2N/A if ((dargs->optflag & DA_REMOVE_ZONE) &&
2N/A (strcmp(olddata[i].key, DAOPT_ZONE) == 0))
2N/A continue;
2N/A newdata[j].key = strdup(olddata[i].key);
2N/A newdata[j].value = strdup(olddata[i].value);
2N/A newkva->length++;
2N/A j++;
2N/A }
2N/A if (dargs->optflag & DA_ADD_ZONE) {
2N/A newdata[j].key = strdup(DAOPT_ZONE);
2N/A newdata[j].value = strdup(zonename);
2N/A newkva->length++;
2N/A }
2N/A _kva_free(oldkva);
2N/A dap->da_devopts = newkva;
2N/A}
2N/A
2N/A/*
2N/A * _dmap2str -
2N/A * converts a device_map entry into a printable string
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic int
2N/A_dmap2str(devmap_t *dmp, char *buf, int size, const char *sep)
2N/A{
2N/A int length;
2N/A
2N/A length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s%s",
2N/A dmp->dmap_devtype, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s\n",
2N/A dmp->dmap_devlist);
2N/A if (length >= size)
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * _dmap2strentry -
2N/A * calls dmap2str to break given devmap_t into printable entry.
2N/A * returns pointer to decoded entry, NULL on error.
2N/A */
2N/Astatic strentry_t *
2N/A_dmap2strentry(devmap_t *devmapp)
2N/A{
2N/A strentry_t *sep;
2N/A
2N/A if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
2N/A return (NULL);
2N/A if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str),
2N/A KV_TOKEN_DELIMIT"\\\n\t") != 0) {
2N/A free(sep);
2N/A return (NULL);
2N/A }
2N/A return (sep);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * _da2str -
2N/A * converts a device_allocate entry into a printable string
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Astatic int
2N/A_da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep,
2N/A const char *osep)
2N/A{
2N/A int length;
2N/A int matching_entry = 0;
2N/A char **dnames;
2N/A
2N/A if (dargs->optflag & DA_UPDATE &&
2N/A (dargs->optflag & DA_ADD_ZONE ||
2N/A dargs->optflag & DA_REMOVE_ZONE) &&
2N/A dargs->devnames) {
2N/A for (dnames = dargs->devnames; *dnames != NULL; dnames++) {
2N/A if (da_matchname(dap, *dnames)) {
2N/A matching_entry = 1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A length = snprintf(buf, size, "%s%s", dap->da_devname, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s%s",
2N/A dap->da_devtype, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A if (matching_entry)
2N/A _update_zonename(dargs, dap);
2N/A if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) &&
2N/A (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) {
2N/A length += snprintf(buf + length, size - length, "%s%s",
2N/A DA_RESERVED, sep);
2N/A } else {
2N/A if (_kva2str(dap->da_devopts, buf + length, size - length,
2N/A KV_ASSIGN, osep) != 0)
2N/A return (-1);
2N/A length = strlen(buf);
2N/A length = strlcat(buf, sep, size - length);
2N/A }
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s%s",
2N/A DA_RESERVED, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s%s",
2N/A dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A length += snprintf(buf + length, size - length, "%s\n",
2N/A dap->da_devexec ? dap->da_devexec : "");
2N/A if (length >= size)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * _da2strentry -
2N/A * calls da2str to break given devalloc_t into printable entry.
2N/A * returns pointer to decoded entry, NULL on error.
2N/A */
2N/Astatic strentry_t *
2N/A_da2strentry(da_args *dargs, devalloc_t *dap)
2N/A{
2N/A strentry_t *sep;
2N/A
2N/A if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
2N/A return (NULL);
2N/A if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str),
2N/A KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) {
2N/A free(sep);
2N/A return (NULL);
2N/A }
2N/A return (sep);
2N/A}
2N/A
2N/A/*
2N/A * _def2str
2N/A * converts da_defs_t into a printable string.
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Astatic int
2N/A_def2str(da_defs_t *da_defs, char *buf, int size, const char *sep)
2N/A{
2N/A int length;
2N/A
2N/A length = snprintf(buf, size, "%s%s", da_defs->devtype, sep);
2N/A if (length >= size)
2N/A return (-1);
2N/A if (da_defs->devopts) {
2N/A if (_kva2str(da_defs->devopts, buf + length, size - length,
2N/A KV_ASSIGN, KV_DELIMITER) != 0)
2N/A return (-1);
2N/A length = strlen(buf);
2N/A length = strlcat(buf, KV_DELIMITER, size - length);
2N/A }
2N/A if (length >= size)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * _def2strentry
2N/A * calls _def2str to break given da_defs_t into printable entry.
2N/A * returns pointer decoded entry, NULL on error.
2N/A */
2N/Astatic strentry_t *
2N/A_def2strentry(da_defs_t *da_defs)
2N/A{
2N/A strentry_t *sep;
2N/A
2N/A if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
2N/A return (NULL);
2N/A if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str),
2N/A KV_TOKEN_DELIMIT) != 0) {
2N/A free(sep);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (sep);
2N/A}
2N/A
2N/A/*
2N/A * _build_defattrs
2N/A * cycles through all defattr entries, stores them in memory. removes
2N/A * entries with the given search_key (device type).
2N/A * returns 0 if given entry not found, 1 if given entry removed, 2 on
2N/A * error.
2N/A */
2N/Astatic int
2N/A_build_defattrs(da_args *dargs, strentry_t **head_defent)
2N/A{
2N/A int rc = 0;
2N/A da_defs_t *da_defs;
2N/A strentry_t *tail_str, *tmp_str;
2N/A
2N/A setdadefent();
2N/A while ((da_defs = getdadefent()) != NULL) {
2N/A rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype));
2N/A if (rc && dargs->optflag & DA_ADD &&
2N/A !(dargs->optflag & DA_FORCE)) {
2N/A /*
2N/A * During DA_ADD, we keep an existing entry unless
2N/A * we have DA_FORCE set to override that entry.
2N/A */
2N/A dargs->optflag |= DA_NO_OVERRIDE;
2N/A rc = 0;
2N/A }
2N/A if (rc == 0) {
2N/A tmp_str = _def2strentry(da_defs);
2N/A if (tmp_str == NULL) {
2N/A freedadefent(da_defs);
2N/A enddadefent();
2N/A return (2);
2N/A }
2N/A /* retaining defattr entry: tmp_str->se_str */
2N/A tmp_str->se_next = NULL;
2N/A if (*head_defent == NULL) {
2N/A *head_defent = tail_str = tmp_str;
2N/A } else {
2N/A tail_str->se_next = tmp_str;
2N/A tail_str = tmp_str;
2N/A }
2N/A }
2N/A freedadefent(da_defs);
2N/A }
2N/A enddadefent();
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Utility routine to make it easy to make type-based decisions in
2N/A * a switch statement.
2N/A */
2N/A
2N/Aint
2N/Ada_str2flag(char *devtype)
2N/A{
2N/A
2N/A if (strcmp(DA_AUDIO_TYPE, devtype) == 0)
2N/A return (DA_AUDIO);
2N/A
2N/A if (strcmp(DA_CD_TYPE, devtype) == 0)
2N/A return (DA_CD);
2N/A
2N/A if (strcmp(DA_FLOPPY_TYPE, devtype) == 0)
2N/A return (DA_FLOPPY);
2N/A
2N/A if (strcmp(DA_TAPE_TYPE, devtype) == 0)
2N/A return (DA_TAPE);
2N/A
2N/A if (strcmp(DA_RMDISK_TYPE, devtype) == 0)
2N/A return (DA_RMDISK);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * We have to handle the "standard" types in devlist differently than
2N/A * other devices, which are not covered by our auto-naming conventions.
2N/A *
2N/A * buf must be a buffer of size DA_MAX_NAME + 1
2N/A */
2N/Aint
2N/Ada_std_type(da_args *dargs, char *namebuf)
2N/A{
2N/A int type;
2N/A int system_labeled;
2N/A
2N/A system_labeled = is_system_labeled();
2N/A type = da_str2flag(dargs->devinfo->devtype);
2N/A
2N/A /* check safely for sizes */
2N/A switch (type) {
2N/A case DA_AUDIO:
2N/A (void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME);
2N/A return (1);
2N/A
2N/A case DA_CD:
2N/A if (system_labeled)
2N/A (void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME);
2N/A else
2N/A (void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME);
2N/A return (1);
2N/A
2N/A case DA_FLOPPY:
2N/A if (system_labeled)
2N/A (void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME);
2N/A else
2N/A (void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME);
2N/A return (1);
2N/A
2N/A case DA_TAPE:
2N/A if (system_labeled)
2N/A (void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME);
2N/A else
2N/A (void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME);
2N/A return (1);
2N/A
2N/A case DA_RMDISK:
2N/A (void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME);
2N/A return (1);
2N/A }
2N/A namebuf[0] = '\0';
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * allocatable: returns
2N/A * -1 if no auths field,
2N/A * 0 if not allocatable (marked '*')
2N/A * 1 if not marked '*'
2N/A */
2N/Astatic int
2N/Aallocatable(da_args *dargs)
2N/A{
2N/A
2N/A if (!dargs->devinfo->devauths)
2N/A return (-1);
2N/A if (strcmp("*", dargs->devinfo->devauths) == 0)
2N/A return (0);
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * _rebuild_lists -
2N/A *
2N/A * If dargs->optflag & DA_EVENT, does not assume the dargs list is
2N/A * complete or completely believable, since devfsadm caches
2N/A * ONLY what it has been exposed to via syseventd.
2N/A *
2N/A * Cycles through all the entries in the /etc files, stores them
2N/A * in memory, takes note of device->dname numbers (e.g. rmdisk0,
2N/A * rmdisk12)
2N/A *
2N/A * Cycles through again, adds dargs entry
2N/A * with the name tname%d (lowest unused number for the device type)
2N/A * to the list of things for the caller to write out to a file,
2N/A * IFF it is a new entry.
2N/A *
2N/A * It is an error for it to already be there, if it is allocatable.
2N/A *
2N/A * Add:
2N/A * Returns 0 if successful and 2 on error.
2N/A * Remove:
2N/A * Returns 0 if not found, 1 if found, 2 on error.
2N/A */
2N/Astatic int
2N/A_rebuild_lists(da_args *dargs, strentry_t **head_devallocp,
2N/A strentry_t **head_devmapp)
2N/A{
2N/A int rc = 0;
2N/A devalloc_t *devallocp;
2N/A devmap_t *devmapp;
2N/A strentry_t *tail_str;
2N/A strentry_t *tmp_str;
2N/A uint64_t tmp_bitmap = 0;
2N/A uint_t tmp = 0;
2N/A int suffix; /* number at end of devname, i.e. rmdisk1 */
2N/A int found = 0;
2N/A int stdtype = 1;
2N/A int is_allocatable = 1;
2N/A char new_devname[DA_MAXNAME + 1];
2N/A char defname[DA_MAXNAME + 1]; /* default name for type */
2N/A char errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
2N/A
2N/A *head_devallocp = *head_devmapp = NULL;
2N/A
2N/A if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
2N/A return (2);
2N/A
2N/A if (dargs->optflag & DA_FORCE)
2N/A return (2);
2N/A
2N/A if (dargs->optflag & DA_ADD) {
2N/A stdtype = da_std_type(dargs, defname);
2N/A is_allocatable = allocatable(dargs);
2N/A }
2N/A
2N/A /* read both files, maps first so we can compare actual devices */
2N/A
2N/A /* build device_maps */
2N/A setdmapent();
2N/A while ((devmapp = getdmapent()) != NULL) {
2N/A suffix = DA_MAX_DEVNO + 1;
2N/A if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
2N/A == 1) {
2N/A if (dargs->optflag & DA_REMOVE) {
2N/A if ((devmapp->dmap_devarray == NULL) ||
2N/A (devmapp->dmap_devarray[0] == NULL)) {
2N/A freedmapent(devmapp);
2N/A enddmapent();
2N/A return (2);
2N/A }
2N/A /* no need to track suffix on remove */
2N/A rc = dmap_exact_dev(devmapp,
2N/A dargs->devinfo->devlist, (int *)NULL);
2N/A
2N/A if (rc == 1) {
2N/A /* if need to free and safe to free */
2N/A if (dargs->devinfo->devname != NULL &&
2N/A (dargs->optflag & DA_EVENT) != 0)
2N/A free(dargs->devinfo->devname);
2N/A dargs->devinfo->devname =
2N/A strdup(devmapp->dmap_devname);
2N/A found = 1;
2N/A freedmapent(devmapp);
2N/A continue; /* don't retain */
2N/A }
2N/A } else if (dargs->optflag & DA_ADD) {
2N/A /*
2N/A * Need to know which suffixes are in use
2N/A */
2N/A rc = (dmap_exact_dev(devmapp,
2N/A dargs->devinfo->devlist, &suffix));
2N/A
2N/A if (rc == 0) {
2N/A /*
2N/A * Same type, different device. Record
2N/A * device suffix already in use, if
2N/A * applicable.
2N/A */
2N/A if ((suffix <= DA_MAX_DEVNO &&
2N/A suffix != -1) && stdtype)
2N/A tmp_bitmap |=
2N/A (uint64_t)(1LL << suffix);
2N/A } else if ((rc == 1) && !is_allocatable) {
2N/A rc = 0;
2N/A } else {
2N/A int log_level = LOG_NOTICE;
2N/A
2N/A if ((dargs->optflag & DA_EVENT) == 0)
2N/A log_level = LOG_DEBUG;
2N/A /*
2N/A * Match allocatable on add is an error
2N/A * unless just looping through on a
2N/A * brainless loopy daemon.
2N/A * Or mapping attempt returned error.
2N/A */
2N/A (void) snprintf(errmsg, sizeof (errmsg),
2N/A "Cannot add %s on node %s",
2N/A dargs->devinfo->devtype,
2N/A devmapp->dmap_devname);
2N/A syslog(log_level, "%s", errmsg);
2N/A freedmapent(devmapp);
2N/A enddmapent();
2N/A return (2);
2N/A }
2N/A } else
2N/A /* add other transaction types as needed */
2N/A return (2);
2N/A } else if ((dargs->optflag & DA_ADD) &&
2N/A (stdtype || is_allocatable) &&
2N/A dmap_exact_dev(devmapp, dargs->devinfo->devlist,
2N/A &suffix)) {
2N/A /*
2N/A * no dups w/o DA_FORCE, even if type differs,
2N/A * if there is a chance this operation is
2N/A * machine-driven. The 5 "standard types"
2N/A * can be machine-driven adds, and tend to
2N/A * be allocatable.
2N/A */
2N/A int log_level = LOG_NOTICE;
2N/A /*
2N/A * Cut down on noise caused by busy daemon.
2N/A */
2N/A if (dargs->optflag & DA_EVENT)
2N/A log_level = LOG_DEBUG;
2N/A (void) snprintf(errmsg, sizeof (errmsg),
2N/A "Cannot add %s on node %s type %s",
2N/A dargs->devinfo->devtype,
2N/A devmapp->dmap_devname,
2N/A devmapp->dmap_devtype);
2N/A syslog(log_level, "%s", errmsg);
2N/A freedmapent(devmapp);
2N/A enddmapent();
2N/A return (2);
2N/A }
2N/A
2N/A tmp_str = _dmap2strentry(devmapp);
2N/A if (tmp_str == NULL) {
2N/A freedmapent(devmapp);
2N/A enddmapent();
2N/A return (2);
2N/A }
2N/A /* retaining devmap entry: tmp_str->se_str */
2N/A tmp_str->se_next = NULL;
2N/A if (*head_devmapp == NULL) {
2N/A *head_devmapp = tail_str = tmp_str;
2N/A } else {
2N/A tail_str->se_next = tmp_str;
2N/A tail_str = tmp_str;
2N/A }
2N/A freedmapent(devmapp);
2N/A }
2N/A enddmapent();
2N/A
2N/A /*
2N/A * No need to rewrite the files if the item to be removed is not
2N/A * in the files -- wait for another call on another darg.
2N/A */
2N/A if ((dargs->optflag & DA_REMOVE) && !found)
2N/A return (0);
2N/A
2N/A
2N/A if (dargs->optflag & DA_ADD) {
2N/A int len;
2N/A /*
2N/A * If we got here from an event, or from devfsadm,
2N/A * we know the passed-in devname is a useless guess,
2N/A * since the files had not been read when the name
2N/A * was chosen, and we don't keep them anywhere else
2N/A * that is sufficiently definitive.
2N/A *
2N/A * So, above, we've been setting bits in tmp_bitmap for each
2N/A * device of this type found as we loop through DEVMAPS_FILE,
2N/A * to make sure we don't change the entry for an existing
2N/A * allocatable device.
2N/A *
2N/A */
2N/A
2N/A for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
2N/A if (!(tmp_bitmap & (1LL << tmp)))
2N/A break;
2N/A /*
2N/A * Let the caller choose the name unless BOTH the name and
2N/A * device type one of: cdrom, floppy, audio, rmdisk, or tape.
2N/A * (or sr, fd for unlabeled)
2N/A *
2N/A * If the caller chooses a specialized name, then we
2N/A * trust the caller to keep track of the numbers
2N/A * (such as sraudio1, printer23) so we ignore the
2N/A * bitmap and therefore have no need for a limit on
2N/A * the number of devices.
2N/A */
2N/A len = strlen(defname);
2N/A if (stdtype &&
2N/A (strncmp(dargs->devinfo->devname, defname, len) == 0)) {
2N/A /*
2N/A * Future: support > 63 hotplug devices per type?
2N/A * DA_MAX_DEVNO was chosen as a handy number for
2N/A * a bitmap, and is much much higher than the number
2N/A * of hotpluggable devices we ever expect to see added
2N/A * by the devfsadmd daemon.
2N/A */
2N/A if (tmp > DA_MAX_DEVNO)
2N/A return (2);
2N/A (void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
2N/A defname, tmp);
2N/A /* if need to free and safe to free */
2N/A if (dargs->devinfo->devname != NULL &&
2N/A (dargs->optflag & DA_EVENT) != 0)
2N/A free(dargs->devinfo->devname);
2N/A dargs->devinfo->devname = strdup(new_devname);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Now adjust devalloc list to match devmaps
2N/A * Note we now have the correct devname for da_match to use.
2N/A */
2N/A setdaent();
2N/A while ((devallocp = getdaent()) != NULL) {
2N/A rc = da_match(devallocp, dargs);
2N/A if (rc == 1) {
2N/A if (dargs->optflag & DA_ADD) {
2N/A (void) snprintf(errmsg, sizeof (errmsg),
2N/A "%s and %s out of sync,"
2N/A "%s only in %s.",
2N/A DEVALLOC, DEVMAP,
2N/A devallocp->da_devname, DEVALLOC);
2N/A syslog(LOG_ERR, "%s", errmsg);
2N/A freedaent(devallocp);
2N/A enddaent();
2N/A return (2);
2N/A } else if (dargs->optflag & DA_REMOVE) {
2N/A /* make list w/o this entry */
2N/A freedaent(devallocp);
2N/A continue;
2N/A }
2N/A }
2N/A tmp_str = _da2strentry(dargs, devallocp);
2N/A if (tmp_str == NULL) {
2N/A freedaent(devallocp);
2N/A enddaent();
2N/A return (2);
2N/A }
2N/A /* retaining devalloc entry: tmp_str->se_str */
2N/A tmp_str->se_next = NULL;
2N/A if (*head_devallocp == NULL) {
2N/A *head_devallocp = tail_str = tmp_str;
2N/A } else {
2N/A tail_str->se_next = tmp_str;
2N/A tail_str = tmp_str;
2N/A }
2N/A freedaent(devallocp);
2N/A }
2N/A enddaent();
2N/A
2N/A /* the caller needs to know if a remove needs to rewrite files */
2N/A if (dargs->optflag & DA_REMOVE)
2N/A return (1); /* 0 and 2 cases returned earlier */
2N/A
2N/A return (0); /* Successful DA_ADD */
2N/A}
2N/A
2N/A/*
2N/A * _build_lists -
2N/A * Cycles through all the entries, stores them in memory. removes entries
2N/A * with the given search_key (device name or type).
2N/A * returns 0 if given entry not found, 1 if given entry removed, 2 on
2N/A * error.
2N/A */
2N/Astatic int
2N/A_build_lists(da_args *dargs, strentry_t **head_devallocp,
2N/A strentry_t **head_devmapp)
2N/A{
2N/A int rc = 0;
2N/A int found = 0;
2N/A devalloc_t *devallocp;
2N/A devmap_t *devmapp;
2N/A strentry_t *tail_str;
2N/A strentry_t *tmp_str;
2N/A
2N/A *head_devallocp = *head_devmapp = NULL;
2N/A
2N/A if (dargs->optflag & DA_MAPS_ONLY)
2N/A goto dmap_only;
2N/A
2N/A /* build device_allocate */
2N/A setdaent();
2N/A while ((devallocp = getdaent()) != NULL) {
2N/A rc = da_match(devallocp, dargs);
2N/A /* if in _build_lists and DA_ADD is set, so is DA_FORCE */
2N/A if (rc == 0) {
2N/A tmp_str = _da2strentry(dargs, devallocp);
2N/A if (tmp_str == NULL) {
2N/A freedaent(devallocp);
2N/A enddaent();
2N/A return (2);
2N/A }
2N/A /* retaining devalloc entry: tmp_str->se_str */
2N/A tmp_str->se_next = NULL;
2N/A if (*head_devallocp == NULL) {
2N/A *head_devallocp = tail_str = tmp_str;
2N/A } else {
2N/A tail_str->se_next = tmp_str;
2N/A tail_str = tmp_str;
2N/A }
2N/A } else if (rc == 1)
2N/A found = 1;
2N/A
2N/A freedaent(devallocp);
2N/A }
2N/A enddaent();
2N/A
2N/Admap_only:
2N/A if (dargs->optflag & DA_ALLOC_ONLY)
2N/A return (rc);
2N/A
2N/A /* build device_maps */
2N/A rc = 0;
2N/A setdmapent();
2N/A while ((devmapp = getdmapent()) != NULL) {
2N/A rc = dm_match(devmapp, dargs);
2N/A if (rc == 0) {
2N/A tmp_str = _dmap2strentry(devmapp);
2N/A if (tmp_str == NULL) {
2N/A freedmapent(devmapp);
2N/A enddmapent();
2N/A return (2);
2N/A }
2N/A /* retaining devmap entry: tmp_str->se_str */
2N/A tmp_str->se_next = NULL;
2N/A if (*head_devmapp == NULL) {
2N/A *head_devmapp = tail_str = tmp_str;
2N/A } else {
2N/A tail_str->se_next = tmp_str;
2N/A tail_str = tmp_str;
2N/A }
2N/A }
2N/A freedmapent(devmapp);
2N/A }
2N/A enddmapent();
2N/A
2N/A /* later code cleanup may cause the use of "found" in other cases */
2N/A if (dargs->optflag & DA_REMOVE)
2N/A return (found);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * _write_defattrs
2N/A * writes current entries to devalloc_defaults.
2N/A */
2N/Astatic void
2N/A_write_defattrs(FILE *fp, strentry_t *head_defent)
2N/A{
2N/A strentry_t *tmp_str;
2N/A
2N/A for (tmp_str = head_defent; tmp_str != NULL;
2N/A tmp_str = tmp_str->se_next) {
2N/A (void) fputs(tmp_str->se_str, fp);
2N/A (void) fputs("\n", fp);
2N/A }
2N/A
2N/A}
2N/A
2N/A/*
2N/A * _write_device_allocate -
2N/A * writes current entries in the list to device_allocate.
2N/A * frees the strings
2N/A */
2N/Astatic void
2N/A_write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
2N/A{
2N/A int is_on = -1;
2N/A strentry_t *tmp_str, *old_str;
2N/A struct stat dastat;
2N/A
2N/A (void) fseek(dafp, (off_t)0, SEEK_SET);
2N/A
2N/A /*
2N/A * if the devalloc on/off string existed before,
2N/A * put it back before anything else.
2N/A * we need to check for the string only if the file
2N/A * exists.
2N/A */
2N/A if (stat(odevalloc, &dastat) == 0) {
2N/A is_on = da_is_on();
2N/A if (is_on == 0)
2N/A (void) fputs(DA_OFF_STR, dafp);
2N/A else if (is_on == 1)
2N/A (void) fputs(DA_ON_STR, dafp);
2N/A }
2N/A tmp_str = head_devallocp;
2N/A while (tmp_str) {
2N/A (void) fputs(tmp_str->se_str, dafp);
2N/A (void) fputs("\n", dafp);
2N/A old_str = tmp_str;
2N/A tmp_str = tmp_str->se_next;
2N/A free(old_str);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * _write_device_maps -
2N/A * writes current entries in the list to device_maps.
2N/A * and frees the strings
2N/A */
2N/Astatic void
2N/A_write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
2N/A{
2N/A strentry_t *tmp_str, *old_str;
2N/A
2N/A (void) fseek(dmfp, (off_t)0, SEEK_SET);
2N/A
2N/A tmp_str = head_devmapp;
2N/A while (tmp_str) {
2N/A (void) fputs(tmp_str->se_str, dmfp);
2N/A (void) fputs("\n", dmfp);
2N/A old_str = tmp_str;
2N/A tmp_str = tmp_str->se_next;
2N/A free(old_str);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * _write_new_defattrs
2N/A * writes the new entry to devalloc_defaults.
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Astatic int
2N/A_write_new_defattrs(FILE *fp, da_args *dargs)
2N/A{
2N/A int count = 0;
2N/A char *tok = NULL, *tokp = NULL;
2N/A char *lasts;
2N/A da_devinfo_t *devinfo = dargs->devinfo;
2N/A
2N/A if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
2N/A return (-1);
2N/A if (!devinfo->devopts)
2N/A return (0);
2N/A (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""),
2N/A KV_TOKEN_DELIMIT);
2N/A if ((tokp = (char *)malloc(strlen(devinfo->devopts) +1)) != NULL) {
2N/A (void) strcpy(tokp, devinfo->devopts);
2N/A if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) {
2N/A (void) fprintf(fp, "%s", tok);
2N/A count = 1;
2N/A }
2N/A while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) {
2N/A if (count)
2N/A (void) fprintf(fp, "%s", KV_DELIMITER);
2N/A (void) fprintf(fp, "%s", tok);
2N/A count++;
2N/A }
2N/A } else {
2N/A (void) fprintf(fp, "%s", devinfo->devopts);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * _write_new_entry -
2N/A * writes the new devalloc_t to device_allocate or the new devmap_t to
2N/A * device_maps.
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Astatic int
2N/A_write_new_entry(FILE *fp, da_args *dargs, int flag)
2N/A{
2N/A int count = 0;
2N/A char *tok = NULL, *tokp = NULL;
2N/A char *lasts;
2N/A da_devinfo_t *devinfo = dargs->devinfo;
2N/A
2N/A if (flag & DA_MAPS_ONLY)
2N/A goto dmap_only;
2N/A
2N/A if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
2N/A return (-1);
2N/A
2N/A (void) fprintf(fp, "%s%s\\\n\t",
2N/A (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER);
2N/A (void) fprintf(fp, "%s%s\\\n\t",
2N/A (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER);
2N/A if (devinfo->devopts == NULL) {
2N/A (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED,
2N/A KV_DELIMITER);
2N/A } else {
2N/A if ((tokp = (char *)malloc(strlen(devinfo->devopts) + 1))
2N/A != NULL) {
2N/A (void) strcpy(tokp, devinfo->devopts);
2N/A if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) !=
2N/A NULL) {
2N/A (void) fprintf(fp, "%s", tok);
2N/A count = 1;
2N/A }
2N/A while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT,
2N/A &lasts)) != NULL) {
2N/A if (count)
2N/A (void) fprintf(fp, "%s",
2N/A KV_TOKEN_DELIMIT "\\\n\t");
2N/A (void) fprintf(fp, "%s", tok);
2N/A count++;
2N/A }
2N/A if (count)
2N/A (void) fprintf(fp, "%s",
2N/A KV_DELIMITER "\\\n\t");
2N/A } else {
2N/A (void) fprintf(fp, "%s%s", devinfo->devopts,
2N/A KV_DELIMITER "\\\n\t");
2N/A }
2N/A }
2N/A (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER);
2N/A (void) fprintf(fp, "%s%s\\\n\t",
2N/A (devinfo->devauths ? devinfo->devauths : DA_ANYUSER),
2N/A KV_DELIMITER);
2N/A (void) fprintf(fp, "%s\n",
2N/A (devinfo->devexec ? devinfo->devexec : KV_DELIMITER));
2N/A
2N/Admap_only:
2N/A if (flag & DA_ALLOC_ONLY)
2N/A return (0);
2N/A
2N/A if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1)
2N/A return (-1);
2N/A
2N/A (void) fprintf(fp, "%s%s\\\n",
2N/A (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT);
2N/A (void) fprintf(fp, "\t%s%s\\\n",
2N/A (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT);
2N/A (void) fprintf(fp, "\t%s\n",
2N/A (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * _da_lock_devdb -
2N/A * locks the database files; lock can be either broken explicitly by
2N/A * closing the fd of the lock file, or it expires automatically at process
2N/A * termination.
2N/A * returns fd of the lock file or -1 on error.
2N/A */
2N/Aint
2N/A_da_lock_devdb(char *rootdir)
2N/A{
2N/A int lockfd = -1;
2N/A int ret;
2N/A int count = 0;
2N/A int retry = 10;
2N/A int retry_sleep;
2N/A uint_t seed;
2N/A char *lockfile;
2N/A char path[MAXPATHLEN];
2N/A int size = sizeof (path);
2N/A
2N/A if (rootdir == NULL) {
2N/A lockfile = DA_DB_LOCK;
2N/A } else {
2N/A path[0] = '\0';
2N/A if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size)
2N/A return (-1);
2N/A lockfile = path;
2N/A }
2N/A
2N/A if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1)
2N/A /* cannot open lock file */
2N/A return (-1);
2N/A
2N/A (void) fchown(lockfd, DA_UID, DA_GID);
2N/A
2N/A if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
2N/A /* cannot position lock file */
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A errno = 0;
2N/A while (retry > 0) {
2N/A count++;
2N/A seed = (uint_t)gethrtime();
2N/A ret = lockf(lockfd, F_TLOCK, 0);
2N/A if (ret == 0) {
2N/A (void) utime(lockfile, NULL);
2N/A return (lockfd);
2N/A }
2N/A if ((errno != EACCES) && (errno != EAGAIN)) {
2N/A /* cannot set lock */
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A retry--;
2N/A retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
2N/A (void) sleep(retry_sleep);
2N/A errno = 0;
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * da_open_devdb -
2N/A * opens one or both database files - device_allocate, device_maps - in
2N/A * the specified mode.
2N/A * locks the database files; lock is either broken explicitly by the
2N/A * caller by closing the lock file fd, or it expires automatically at
2N/A * process termination.
2N/A * writes the file pointer of opened file in the input args - dafp, dmfp.
2N/A * returns fd of the lock file on success, -2 if database file does not
2N/A * exist, -1 on other errors.
2N/A */
2N/Aint
2N/Ada_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag)
2N/A{
2N/A int oflag = 0;
2N/A int fda = -1;
2N/A int fdm = -1;
2N/A int lockfd = -1;
2N/A char *fname;
2N/A char *fmode;
2N/A char path[MAXPATHLEN];
2N/A FILE *devfile;
2N/A
2N/A if ((dafp == NULL) && (dmfp == NULL))
2N/A return (-1);
2N/A
2N/A if (flag & DA_RDWR) {
2N/A oflag = DA_RDWR;
2N/A fmode = "r+F";
2N/A } else if (flag & DA_RDONLY) {
2N/A oflag = DA_RDONLY;
2N/A fmode = "rF";
2N/A }
2N/A
2N/A if ((lockfd = _da_lock_devdb(rootdir)) == -1)
2N/A return (-1);
2N/A
2N/A if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
2N/A goto dmap_only;
2N/A
2N/A path[0] = '\0';
2N/A
2N/A /*
2N/A * open the device allocation file
2N/A */
2N/A if (rootdir == NULL) {
2N/A fname = DEVALLOC;
2N/A } else {
2N/A if (snprintf(path, sizeof (path), "%s%s", rootdir,
2N/A DEVALLOC) >= sizeof (path)) {
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A fname = path;
2N/A }
2N/A if ((fda = open(fname, oflag, DA_DBMODE)) == -1) {
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return ((errno == ENOENT) ? -2 : -1);
2N/A }
2N/A if ((devfile = fdopen(fda, fmode)) == NULL) {
2N/A (void) close(fda);
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A *dafp = devfile;
2N/A (void) fchmod(fda, DA_DBMODE);
2N/A
2N/A if ((flag & DA_ALLOC_ONLY))
2N/A goto out;
2N/A
2N/Admap_only:
2N/A path[0] = '\0';
2N/A /*
2N/A * open the device map file
2N/A */
2N/A if (rootdir == NULL) {
2N/A fname = DEVMAP;
2N/A } else {
2N/A if (snprintf(path, sizeof (path), "%s%s", rootdir,
2N/A DEVMAP) >= sizeof (path)) {
2N/A (void) close(fda);
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A fname = path;
2N/A }
2N/A
2N/A if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) {
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return ((errno == ENOENT) ? -2 : -1);
2N/A }
2N/A
2N/A if ((devfile = fdopen(fdm, fmode)) == NULL) {
2N/A (void) close(fdm);
2N/A (void) close(fda);
2N/A if (lockfd != -1)
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A *dmfp = devfile;
2N/A (void) fchmod(fdm, DA_DBMODE);
2N/A
2N/Aout:
2N/A return (lockfd);
2N/A}
2N/A
2N/A/*
2N/A * _record_on_off -
2N/A * adds either DA_ON_STR or DA_OFF_STR to device_allocate
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Astatic int
2N/A_record_on_off(da_args *dargs, FILE *tafp, FILE *dafp)
2N/A{
2N/A int dafd;
2N/A int nsize;
2N/A int nitems = 1;
2N/A int actionlen;
2N/A int str_found = 0;
2N/A int len = 0, nlen = 0, plen = 0;
2N/A char *ptr = NULL;
2N/A char *actionstr;
2N/A char *nbuf = NULL;
2N/A char line[MAX_CANON];
2N/A struct stat dastat;
2N/A
2N/A if (dargs->optflag & DA_ON)
2N/A actionstr = DA_ON_STR;
2N/A else
2N/A actionstr = DA_OFF_STR;
2N/A actionlen = strlen(actionstr);
2N/A dafd = fileno(dafp);
2N/A if (fstat(dafd, &dastat) == -1)
2N/A return (-1);
2N/A
2N/A /* check the old device_allocate for on/off string */
2N/A ptr = fgets(line, MAX_CANON, dafp);
2N/A if (ptr != NULL) {
2N/A if ((strcmp(line, DA_ON_STR) == 0) ||
2N/A (strcmp(line, DA_OFF_STR) == 0)) {
2N/A str_found = 1;
2N/A nsize = dastat.st_size;
2N/A }
2N/A }
2N/A if (!ptr || !str_found) {
2N/A /*
2N/A * the file never had either the on or the off string;
2N/A * make room for it.
2N/A */
2N/A str_found = 0;
2N/A nsize = dastat.st_size + actionlen + 1;
2N/A }
2N/A if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
2N/A return (-1);
2N/A nbuf[0] = '\0';
2N/A /* put the on/off string */
2N/A (void) strcpy(nbuf, actionstr);
2N/A nlen = strlen(nbuf);
2N/A plen = nlen;
2N/A if (ptr && !str_found) {
2N/A /* now put the first line that we read in fgets */
2N/A nlen = plen + strlen(line) + 1;
2N/A len = snprintf(nbuf + plen, nlen - plen, "%s", line);
2N/A if (len >= nsize) {
2N/A free(nbuf);
2N/A return (-1);
2N/A }
2N/A plen += len;
2N/A }
2N/A
2N/A /* now get the rest of the old file */
2N/A while (fgets(line, MAX_CANON, dafp) != NULL) {
2N/A nlen = plen + strlen(line) + 1;
2N/A len = snprintf(nbuf + plen, nlen - plen, "%s", line);
2N/A if (len >= nsize) {
2N/A free(nbuf);
2N/A return (-1);
2N/A }
2N/A plen += len;
2N/A }
2N/A len = strlen(nbuf) + 1;
2N/A if (len < nsize)
2N/A nbuf[len] = '\n';
2N/A
2N/A /* write the on/off str + the old device_allocate to the temp file */
2N/A if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
2N/A free(nbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A free(nbuf);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * da_update_defattrs -
2N/A * writes default attributes to devalloc_defaults
2N/A * returns 0 on success, -1 on error.
2N/A */
2N/Aint
2N/Ada_update_defattrs(da_args *dargs)
2N/A{
2N/A int rc = 0, lockfd = 0, tmpfd = 0;
2N/A char *defpath = DEFATTRS;
2N/A char *tmpdefpath = TMPATTRS;
2N/A FILE *tmpfp = NULL;
2N/A struct stat dstat;
2N/A strentry_t *head_defent = NULL;
2N/A
2N/A if (dargs == NULL)
2N/A return (0);
2N/A if ((lockfd = _da_lock_devdb(NULL)) == -1)
2N/A return (-1);
2N/A if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A (void) fchown(tmpfd, DA_UID, DA_GID);
2N/A if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) {
2N/A (void) close(tmpfd);
2N/A (void) unlink(tmpdefpath);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A /*
2N/A * examine all entries, remove an old one if required, check
2N/A * if a new one needs to be added.
2N/A */
2N/A if (stat(defpath, &dstat) == 0) {
2N/A if ((rc = _build_defattrs(dargs, &head_defent)) != 0) {
2N/A if (rc == 1) {
2N/A (void) fclose(tmpfp);
2N/A (void) unlink(tmpdefpath);
2N/A (void) close(lockfd);
2N/A return (rc);
2N/A }
2N/A }
2N/A }
2N/A /*
2N/A * write back any existing entries.
2N/A */
2N/A _write_defattrs(tmpfp, head_defent);
2N/A
2N/A if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
2N/A /* add new entries */
2N/A rc = _write_new_defattrs(tmpfp, dargs);
2N/A (void) fclose(tmpfp);
2N/A } else {
2N/A (void) fclose(tmpfp);
2N/A }
2N/A if (rename(tmpdefpath, defpath) != 0) {
2N/A rc = -1;
2N/A (void) unlink(tmpdefpath);
2N/A }
2N/A (void) close(lockfd);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * da_update_device -
2N/A * Writes existing entries and the SINGLE change requested by da_args,
2N/A * to device_allocate and device_maps.
2N/A * Returns 0 on success, -1 on error.
2N/A */
2N/Aint
2N/Ada_update_device(da_args *dargs)
2N/A{
2N/A int rc;
2N/A int tafd = -1, tmfd = -1;
2N/A int lockfd = -1;
2N/A char *rootdir = NULL;
2N/A char *apathp = NULL, *mpathp = NULL;
2N/A char *dapathp = NULL, *dmpathp = NULL;
2N/A char apath[MAXPATHLEN], mpath[MAXPATHLEN];
2N/A char dapath[MAXPATHLEN], dmpath[MAXPATHLEN];
2N/A FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL;
2N/A struct stat dastat;
2N/A da_devinfo_t *devinfo;
2N/A strentry_t *head_devmapp = NULL;
2N/A strentry_t *head_devallocp = NULL;
2N/A
2N/A if (dargs == NULL)
2N/A return (0);
2N/A
2N/A rootdir = dargs->rootdir;
2N/A devinfo = dargs->devinfo;
2N/A
2N/A /*
2N/A * adding/removing entries should be done in both
2N/A * device_allocate and device_maps. updates can be
2N/A * done in both or either of the files.
2N/A */
2N/A if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
2N/A if (dargs->optflag & DA_ALLOC_ONLY ||
2N/A dargs->optflag & DA_MAPS_ONLY)
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * name, type and list are required fields for adding a new
2N/A * device.
2N/A */
2N/A if ((dargs->optflag & DA_ADD) &&
2N/A ((devinfo->devname == NULL) ||
2N/A (devinfo->devtype == NULL) ||
2N/A (devinfo->devlist == NULL))) {
2N/A return (-1);
2N/A }
2N/A
2N/A if (rootdir != NULL) {
2N/A if (snprintf(apath, sizeof (apath), "%s%s", rootdir,
2N/A TMPALLOC) >= sizeof (apath))
2N/A return (-1);
2N/A apathp = apath;
2N/A if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir,
2N/A DEVALLOC) >= sizeof (dapath))
2N/A return (-1);
2N/A dapathp = dapath;
2N/A if (!(dargs->optflag & DA_ALLOC_ONLY)) {
2N/A if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir,
2N/A TMPMAP) >= sizeof (mpath))
2N/A return (-1);
2N/A mpathp = mpath;
2N/A if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir,
2N/A DEVMAP) >= sizeof (dmpath))
2N/A return (-1);
2N/A dmpathp = dmpath;
2N/A }
2N/A } else {
2N/A apathp = TMPALLOC;
2N/A dapathp = DEVALLOC;
2N/A mpathp = TMPMAP;
2N/A dmpathp = DEVMAP;
2N/A }
2N/A
2N/A if (dargs->optflag & DA_MAPS_ONLY)
2N/A goto dmap_only;
2N/A
2N/A /*
2N/A * Check if we are here just to record on/off status of
2N/A * device_allocation.
2N/A */
2N/A if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF)
2N/A lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL,
2N/A DA_RDONLY|DA_ALLOC_ONLY);
2N/A else
2N/A lockfd = _da_lock_devdb(rootdir);
2N/A if (lockfd == -1)
2N/A return (-1);
2N/A
2N/A if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
2N/A (void) close(lockfd);
2N/A (void) fclose(dafp);
2N/A return (-1);
2N/A }
2N/A (void) fchown(tafd, DA_UID, DA_GID);
2N/A if ((tafp = fdopen(tafd, "r+")) == NULL) {
2N/A (void) close(tafd);
2N/A (void) unlink(apathp);
2N/A (void) fclose(dafp);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * We don't need to parse the file if we are here just to record
2N/A * on/off status of device_allocation.
2N/A */
2N/A if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) {
2N/A if (_record_on_off(dargs, tafp, dafp) == -1) {
2N/A (void) fclose(tafp);
2N/A (void) unlink(apathp);
2N/A (void) fclose(dafp);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A (void) fclose(dafp);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * If reacting to a hotplug, read the file entries,
2N/A * figure out what dname (tname + a new number) goes to the
2N/A * device being added/removed, and create a good head_devallocp and
2N/A * head_devmapp with everything good still in it (_rebuild_lists)
2N/A *
2N/A * Else examine all the entries, remove an old one if it is
2N/A * a duplicate with a device being added, returning the
2N/A * remaining list (_build_lists.)
2N/A *
2N/A * We need to do this only if the file exists already.
2N/A *
2N/A * Once we have built these lists, we need to free the strings
2N/A * in the head_* arrays before returning.
2N/A */
2N/A if (stat(dapathp, &dastat) == 0) {
2N/A /* for device allocation, the /etc files are the "master" */
2N/A if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
2N/A (!(dargs->optflag & DA_FORCE)))
2N/A rc = _rebuild_lists(dargs, &head_devallocp,
2N/A &head_devmapp);
2N/A else
2N/A rc = _build_lists(dargs, &head_devallocp,
2N/A &head_devmapp);
2N/A
2N/A if (rc != 0 && rc != 1) {
2N/A (void) fclose(tafp);
2N/A (void) unlink(apathp);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A } else
2N/A rc = 0;
2N/A
2N/A if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
2N/A (void) fclose(tafp);
2N/A (void) unlink(apathp);
2N/A (void) close(lockfd);
2N/A return (0);
2N/A }
2N/A /*
2N/A * TODO: clean up the workings of DA_UPDATE.
2N/A * Due to da_match looking at fields that are missing
2N/A * in dargs for DA_UPDATE, the da_match call returns no match,
2N/A * but due to the way _da2str combines the devalloc_t info with
2N/A * the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
2N/A *
2N/A * This would not scale if any type of update was ever needed
2N/A * from the daemon.
2N/A */
2N/A
2N/A /*
2N/A * Write out devallocp along with the devalloc on/off string.
2N/A */
2N/A _write_device_allocate(dapathp, tafp, head_devallocp);
2N/A
2N/A if (dargs->optflag & DA_ALLOC_ONLY)
2N/A goto out;
2N/A
2N/Admap_only:
2N/A if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) {
2N/A (void) close(tafd);
2N/A (void) unlink(apathp);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A (void) fchown(tmfd, DA_UID, DA_GID);
2N/A if ((tmfp = fdopen(tmfd, "r+")) == NULL) {
2N/A (void) close(tafd);
2N/A (void) unlink(apathp);
2N/A (void) close(tmfd);
2N/A (void) unlink(mpathp);
2N/A (void) close(lockfd);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Write back any non-removed pre-existing entries.
2N/A */
2N/A if (head_devmapp != NULL)
2N/A _write_device_maps(tmfp, head_devmapp);
2N/A
2N/Aout:
2N/A /*
2N/A * Add any new entries here.
2N/A */
2N/A if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
2N/A /* add any new entries */
2N/A rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY);
2N/A (void) fclose(tafp);
2N/A
2N/A if (rc == 0)
2N/A rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY);
2N/A (void) fclose(tmfp);
2N/A } else {
2N/A if (tafp)
2N/A (void) fclose(tafp);
2N/A if (tmfp)
2N/A (void) fclose(tmfp);
2N/A }
2N/A
2N/A rc = 0;
2N/A if (!(dargs->optflag & DA_MAPS_ONLY)) {
2N/A if (rename(apathp, dapathp) != 0) {
2N/A rc = -1;
2N/A (void) unlink(apathp);
2N/A }
2N/A }
2N/A if (!(dargs->optflag & DA_ALLOC_ONLY)) {
2N/A if (rename(mpathp, dmpathp) != 0) {
2N/A rc = -1;
2N/A (void) unlink(mpathp);
2N/A }
2N/A }
2N/A
2N/A (void) close(lockfd);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * da_add_list -
2N/A * adds new /dev link name to the linked list of devices.
2N/A * returns 0 if link added successfully, -1 on error.
2N/A */
2N/Aint
2N/Ada_add_list(da_devlist_t *dlist, char *link, int new_instance, int flag)
2N/A{
2N/A int instance;
2N/A int nlen, plen;
2N/A int new_entry = 0;
2N/A char *dtype, *dexec, *tname, *kval;
2N/A char *minstr = NULL, *maxstr = NULL;
2N/A int minstr_needs_free = 0, maxstr_needs_free = 0;
2N/A char dname[DA_MAXNAME + 1];
2N/A kva_t *kva;
2N/A da_deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL;
2N/A da_defs_t *da_defs;
2N/A
2N/A if (dlist == NULL || link == NULL)
2N/A return (-1);
2N/A
2N/A dname[0] = '\0';
2N/A if (flag & DA_AUDIO) {
2N/A dentry = dlist->audio;
2N/A tname = DA_AUDIO_NAME;
2N/A dtype = DA_AUDIO_TYPE;
2N/A dexec = DA_DEFAULT_AUDIO_CLEAN;
2N/A } else if (flag & DA_CD) {
2N/A dentry = dlist->cd;
2N/A tname = DA_CD_NAME;
2N/A dtype = DA_CD_TYPE;
2N/A dexec = DA_DEFAULT_DISK_CLEAN;
2N/A } else if (flag & DA_FLOPPY) {
2N/A dentry = dlist->floppy;
2N/A tname = DA_FLOPPY_NAME;
2N/A dtype = DA_FLOPPY_TYPE;
2N/A dexec = DA_DEFAULT_DISK_CLEAN;
2N/A } else if (flag & DA_TAPE) {
2N/A dentry = dlist->tape;
2N/A tname = DA_TAPE_NAME;
2N/A dtype = DA_TAPE_TYPE;
2N/A dexec = DA_DEFAULT_TAPE_CLEAN;
2N/A } else if (flag & DA_RMDISK) {
2N/A dentry = dlist->rmdisk;
2N/A tname = DA_RMDISK_NAME;
2N/A dtype = DA_RMDISK_TYPE;
2N/A dexec = DA_DEFAULT_DISK_CLEAN;
2N/A } else {
2N/A return (-1);
2N/A }
2N/A
2N/A for (nentry = dentry; nentry != NULL; nentry = nentry->next) {
2N/A pentry = nentry;
2N/A (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance);
2N/A if (nentry->devinfo.instance == new_instance)
2N/A /*
2N/A * Add the new link name to the list of links
2N/A * that the device 'dname' has.
2N/A */
2N/A break;
2N/A }
2N/A
2N/A if (nentry == NULL) {
2N/A /*
2N/A * Either this is the first entry ever, or no matching entry
2N/A * was found. Create a new one and add to the list.
2N/A */
2N/A if (dentry == NULL) /* first entry ever */
2N/A instance = 0;
2N/A else /* no matching entry */
2N/A instance++;
2N/A (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance);
2N/A if ((nentry = (da_deventry_t *)malloc(sizeof (da_deventry_t)))
2N/A == NULL)
2N/A return (-1);
2N/A if (pentry != NULL)
2N/A pentry->next = nentry;
2N/A new_entry = 1;
2N/A nentry->devinfo.devname = strdup(dname);
2N/A nentry->devinfo.devtype = dtype;
2N/A nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH;
2N/A nentry->devinfo.devexec = dexec;
2N/A nentry->devinfo.instance = new_instance;
2N/A /*
2N/A * Look for default label range, authorizations and cleaning
2N/A * program in devalloc_defaults. If label range is not
2N/A * specified in devalloc_defaults, assume it to be admin_low
2N/A * to admin_high.
2N/A */
2N/A minstr = DA_DEFAULT_MIN;
2N/A maxstr = DA_DEFAULT_MAX;
2N/A setdadefent();
2N/A if (da_defs = getdadeftype(nentry->devinfo.devtype)) {
2N/A kva = da_defs->devopts;
2N/A if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL) {
2N/A minstr = strdup(kval);
2N/A minstr_needs_free = 1;
2N/A }
2N/A if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL) {
2N/A maxstr = strdup(kval);
2N/A maxstr_needs_free = 1;
2N/A }
2N/A if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL)
2N/A nentry->devinfo.devauths = strdup(kval);
2N/A if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL)
2N/A nentry->devinfo.devexec = strdup(kval);
2N/A freedadefent(da_defs);
2N/A }
2N/A enddadefent();
2N/A kval = NULL;
2N/A nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) +
2N/A strlen(minstr) + strlen(KV_TOKEN_DELIMIT) +
2N/A strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr)
2N/A + 1; /* +1 for terminator */
2N/A if (kval = (char *)malloc(nlen))
2N/A (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s",
2N/A DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT,
2N/A DAOPT_MAXLABEL, KV_ASSIGN, maxstr);
2N/A nentry->devinfo.devopts = kval;
2N/A
2N/A nentry->devinfo.devlist = NULL;
2N/A nentry->next = NULL;
2N/A if (minstr_needs_free == 1)
2N/A free(minstr);
2N/A if (maxstr_needs_free == 1)
2N/A free(maxstr);
2N/A }
2N/A
2N/A nlen = strlen(link) + 1; /* +1 terminator */
2N/A if (nentry->devinfo.devlist) {
2N/A plen = strlen(nentry->devinfo.devlist);
2N/A nlen = nlen + plen + 1; /* +1 for blank to separate entries */
2N/A } else {
2N/A plen = 0;
2N/A }
2N/A
2N/A if ((nentry->devinfo.devlist =
2N/A (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
2N/A if (new_entry) {
2N/A free(nentry->devinfo.devname);
2N/A free(nentry);
2N/A if (pentry != NULL)
2N/A pentry->next = NULL;
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A if (plen == 0)
2N/A (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link);
2N/A else
2N/A (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen,
2N/A " %s", link);
2N/A
2N/A if (pentry == NULL) {
2N/A /*
2N/A * This is the first entry of this device type.
2N/A */
2N/A if (flag & DA_AUDIO)
2N/A dlist->audio = nentry;
2N/A else if (flag & DA_CD)
2N/A dlist->cd = nentry;
2N/A else if (flag & DA_FLOPPY)
2N/A dlist->floppy = nentry;
2N/A else if (flag & DA_TAPE)
2N/A dlist->tape = nentry;
2N/A else if (flag & DA_RMDISK)
2N/A dlist->rmdisk = nentry;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * da_remove_list -
2N/A * removes a /dev link name from the linked list of devices.
2N/A * returns type of device if link for that device removed
2N/A * successfully, else returns -1 on error.
2N/A * if all links for a device are removed, stores that device
2N/A * name in devname.
2N/A */
2N/Aint
2N/Ada_remove_list(da_devlist_t *dlist, char *link, int type, char *devname,
2N/A int size)
2N/A{
2N/A int flag;
2N/A int remove_dev = 0;
2N/A int nlen, plen, slen;
2N/A char *lasts, *lname, *oldlist;
2N/A struct stat rmstat;
2N/A da_deventry_t *dentry, *current, *prev;
2N/A
2N/A if (type != NULL)
2N/A flag = type;
2N/A else if (link == NULL)
2N/A return (-1);
2N/A else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME))
2N/A flag = DA_AUDIO;
2N/A else if (strstr(link, "dsk") || strstr(link, "rdsk") ||
2N/A strstr(link, "sr") || strstr(link, "rsr"))
2N/A flag = DA_CD;
2N/A else if (strstr(link, "fd") || strstr(link, "rfd") ||
2N/A strstr(link, "diskette") || strstr(link, "rdiskette"))
2N/A flag = DA_FLOPPY;
2N/A else if (strstr(link, DA_TAPE_NAME))
2N/A flag = DA_TAPE;
2N/A else
2N/A flag = DA_RMDISK;
2N/A
2N/A switch (type) {
2N/A case DA_AUDIO:
2N/A dentry = dlist->audio;
2N/A break;
2N/A case DA_CD:
2N/A dentry = dlist->cd;
2N/A break;
2N/A case DA_FLOPPY:
2N/A dentry = dlist->floppy;
2N/A break;
2N/A case DA_TAPE:
2N/A dentry = dlist->tape;
2N/A break;
2N/A case DA_RMDISK:
2N/A dentry = dlist->rmdisk;
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A
2N/A if ((type != NULL) && (link == NULL)) {
2N/A for (current = dentry, prev = dentry; current != NULL;
2N/A current = current->next) {
2N/A oldlist = strdup(current->devinfo.devlist);
2N/A for (lname = strtok_r(oldlist, " ", &lasts);
2N/A lname != NULL;
2N/A lname = strtok_r(NULL, " ", &lasts)) {
2N/A if (stat(lname, &rmstat) != 0) {
2N/A remove_dev = 1;
2N/A goto remove_dev;
2N/A }
2N/A }
2N/A prev = current;
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A for (current = dentry, prev = dentry; current != NULL;
2N/A current = current->next) {
2N/A plen = strlen(current->devinfo.devlist);
2N/A nlen = strlen(link);
2N/A if (plen == nlen) {
2N/A if (strcmp(current->devinfo.devlist, link) == 0) {
2N/A /* last name in the list */
2N/A remove_dev = 1;
2N/A break;
2N/A }
2N/A }
2N/A if (strstr(current->devinfo.devlist, link)) {
2N/A nlen = plen - nlen + 1;
2N/A oldlist = strdup(current->devinfo.devlist);
2N/A if ((current->devinfo.devlist =
2N/A (char *)realloc(current->devinfo.devlist,
2N/A nlen)) == NULL) {
2N/A free(oldlist);
2N/A return (-1);
2N/A }
2N/A current->devinfo.devlist[0] = '\0';
2N/A nlen = plen = slen = 0;
2N/A for (lname = strtok_r(oldlist, " ", &lasts);
2N/A lname != NULL;
2N/A lname = strtok_r(NULL, " ", &lasts)) {
2N/A if (strcmp(lname, link) == 0)
2N/A continue;
2N/A nlen = strlen(lname) + plen + 1;
2N/A if (plen == 0) {
2N/A slen =
2N/A snprintf(current->devinfo.devlist,
2N/A nlen, "%s", lname);
2N/A } else {
2N/A slen =
2N/A snprintf(current->devinfo.devlist +
2N/A plen, nlen - plen, " %s", lname);
2N/A }
2N/A plen = plen + slen + 1;
2N/A }
2N/A free(oldlist);
2N/A break;
2N/A }
2N/A prev = current;
2N/A }
2N/A
2N/Aremove_dev:
2N/A if (remove_dev == 1) {
2N/A (void) strlcpy(devname, current->devinfo.devname, size);
2N/A free(current->devinfo.devname);
2N/A free(current->devinfo.devlist);
2N/A current->devinfo.devname = current->devinfo.devlist = NULL;
2N/A prev->next = current->next;
2N/A free(current);
2N/A current = NULL;
2N/A }
2N/A if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) {
2N/A if (prev->next) {
2N/A /*
2N/A * what we removed above was the first entry
2N/A * in the list. make the next entry to be the
2N/A * first.
2N/A */
2N/A current = prev->next;
2N/A } else {
2N/A /*
2N/A * the matching entry was the only entry in the list
2N/A * for this type.
2N/A */
2N/A current = NULL;
2N/A }
2N/A if (flag & DA_AUDIO)
2N/A dlist->audio = current;
2N/A else if (flag & DA_CD)
2N/A dlist->cd = current;
2N/A else if (flag & DA_FLOPPY)
2N/A dlist->floppy = current;
2N/A else if (flag & DA_TAPE)
2N/A dlist->tape = current;
2N/A else if (flag & DA_RMDISK)
2N/A dlist->rmdisk = current;
2N/A }
2N/A
2N/A return (flag);
2N/A}
2N/A
2N/A/*
2N/A * da_rm_list_entry -
2N/A *
2N/A * The adding of devnames to a devlist and the removal of a
2N/A * device are not symmetrical -- hot_cleanup gives a /devices
2N/A * name which is used to remove the dentry whose links all point to
2N/A * that /devices entry.
2N/A *
2N/A * The link argument is present if available to make debugging
2N/A * easier.
2N/A *
2N/A * da_rm_list_entry removes an entry from the linked list of devices.
2N/A *
2N/A * Returns 1 if the devname was removed successfully,
2N/A * 0 if not found, -1 for error.
2N/A */
2N/A/*ARGSUSED*/
2N/Aint
2N/Ada_rm_list_entry(da_devlist_t *dlist, char *link, int type, char *devname)
2N/A{
2N/A int retval = 0;
2N/A da_deventry_t **dentry, *current, *prev;
2N/A
2N/A switch (type) {
2N/A case DA_AUDIO:
2N/A dentry = &(dlist->audio);
2N/A break;
2N/A case DA_CD:
2N/A dentry = &(dlist->cd);
2N/A break;
2N/A case DA_FLOPPY:
2N/A dentry = &(dlist->floppy);
2N/A break;
2N/A case DA_TAPE:
2N/A dentry = &(dlist->tape);
2N/A break;
2N/A case DA_RMDISK:
2N/A dentry = &(dlist->rmdisk);
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A
2N/A /* Presumably in daemon mode, no need to remove entry, list is empty */
2N/A if (*dentry == (da_deventry_t *)NULL)
2N/A return (0);
2N/A
2N/A prev = NULL;
2N/A for (current = *dentry; current != NULL;
2N/A prev = current, current = current->next) {
2N/A if (strcmp(devname, current->devinfo.devname))
2N/A continue;
2N/A retval = 1;
2N/A break;
2N/A }
2N/A if (retval == 0)
2N/A return (0);
2N/A free(current->devinfo.devname);
2N/A if (current->devinfo.devlist != NULL)
2N/A free(current->devinfo.devlist);
2N/A if (current->devinfo.devopts != NULL)
2N/A free(current->devinfo.devopts);
2N/A
2N/A if (prev == NULL)
2N/A *dentry = current->next;
2N/A else
2N/A prev->next = current->next;
2N/A
2N/A free(current);
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * da_is_on -
2N/A * checks if device allocation feature is turned on.
2N/A * returns 1 if on, 0 if off, -1 if status string not
2N/A * found in device_allocate.
2N/A */
2N/Aint
2N/Ada_is_on()
2N/A{
2N/A return (getdaon());
2N/A}
2N/A
2N/A/*
2N/A * da_print_device -
2N/A * debug routine to print device entries.
2N/A */
2N/Avoid
2N/Ada_print_device(int flag, da_devlist_t *devlist)
2N/A{
2N/A da_deventry_t *entry, *dentry;
2N/A da_devinfo_t *devinfo;
2N/A
2N/A if (flag & DA_AUDIO)
2N/A dentry = devlist->audio;
2N/A else if (flag & DA_CD)
2N/A dentry = devlist->cd;
2N/A else if (flag & DA_FLOPPY)
2N/A dentry = devlist->floppy;
2N/A else if (flag & DA_TAPE)
2N/A dentry = devlist->tape;
2N/A else if (flag & DA_RMDISK)
2N/A dentry = devlist->rmdisk;
2N/A else
2N/A return;
2N/A
2N/A for (entry = dentry; entry != NULL; entry = entry->next) {
2N/A devinfo = &(entry->devinfo);
2N/A (void) fprintf(stdout, "name: %s\n", devinfo->devname);
2N/A (void) fprintf(stdout, "type: %s\n", devinfo->devtype);
2N/A (void) fprintf(stdout, "auth: %s\n", devinfo->devauths);
2N/A (void) fprintf(stdout, "exec: %s\n", devinfo->devexec);
2N/A (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist);
2N/A }
2N/A}