/*
* 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 (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#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 <syslog.h>
#include <bsm/devices.h>
#include <bsm/devalloc.h>
#include <tsol/label.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 *);
extern int dmap_matchtype(devmap_t *dmap, char *type);
extern int dmap_matchdev(devmap_t *dmap, char *dev);
extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
extern char *dmap_physname(devmap_t *dmap);
/*
* 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, "rF")) == 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;
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 ((fd = open(fname, O_RDONLY)) == -1) {
(void) rw_unlock(flock);
return (-1);
}
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(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(devmap_t *devmapp)
{
strentry_t *sep;
if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
return (NULL);
if (_dmap2str(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);
}
/*
* We have to handle the "standard" types in devlist differently than
* other devices, which are not covered by our auto-naming conventions.
*
* buf must be a buffer of size DA_MAX_NAME + 1
*/
int
da_std_type(da_args *dargs, char *namebuf)
{
char *type = dargs->devinfo->devtype;
int system_labeled;
system_labeled = is_system_labeled();
/* check safely for sizes */
if (strcmp(DA_AUDIO_TYPE, type) == 0) {
(void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME);
return (1);
}
if (strcmp(DA_CD_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_FLOPPY_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_TAPE_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_RMDISK_TYPE, type) == 0) {
(void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME);
return (1);
}
namebuf[0] = '\0';
return (0);
}
/*
* allocatable: returns
* -1 if no auths field,
* 0 if not allocatable (marked '*')
* 1 if not marked '*'
*/
static int
allocatable(da_args *dargs)
{
if (!dargs->devinfo->devauths)
return (-1);
if (strcmp("*", dargs->devinfo->devauths) == 0)
return (0);
return (1);
}
/*
* _rebuild_lists -
*
* If dargs->optflag & DA_EVENT, does not assume the dargs list is
* complete or completely believable, since devfsadm caches
* ONLY what it has been exposed to via syseventd.
*
* Cycles through all the entries in the /etc files, stores them
* in memory, takes note of device->dname numbers (e.g. rmdisk0,
* rmdisk12)
*
* Cycles through again, adds dargs entry
* with the name tname%d (lowest unused number for the device type)
* to the list of things for the caller to write out to a file,
* IFF it is a new entry.
*
* It is an error for it to already be there, if it is allocatable.
*
* Add:
* Returns 0 if successful and 2 on error.
* Remove:
* Returns 0 if not found, 1 if found, 2 on error.
*/
static int
_rebuild_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;
uint64_t tmp_bitmap = 0;
uint_t tmp = 0;
char *realname;
int suffix;
int found = 0;
int stdtype = 1;
int is_allocatable = 1;
char new_devname[DA_MAXNAME + 1];
char defname[DA_MAXNAME + 1]; /* default name for type */
char errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
return (2);
if (dargs->optflag & DA_FORCE)
return (2);
if (dargs->optflag & DA_ADD) {
stdtype = da_std_type(dargs, defname);
is_allocatable = allocatable(dargs);
}
/* read both files, maps first so we can compare actual devices */
/* build device_maps */
setdmapent();
while ((devmapp = getdmapent()) != NULL) {
suffix = DA_MAX_DEVNO + 1;
if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
== 1) {
if (dargs->optflag & DA_REMOVE) {
if ((devmapp->dmap_devarray == NULL) ||
(devmapp->dmap_devarray[0] == NULL)) {
freedmapent(devmapp);
enddmapent();
return (2);
}
realname = dmap_physname(devmapp);
if (realname == NULL) {
freedmapent(devmapp);
enddmapent();
return (2);
}
if (strstr(realname, dargs->devinfo->devlist)
!= NULL) {
/* if need to free and safe to free */
if (dargs->devinfo->devname != NULL &&
(dargs->optflag & DA_EVENT) != 0)
free(dargs->devinfo->devname);
dargs->devinfo->devname =
strdup(devmapp->dmap_devname);
found = 1;
freedmapent(devmapp);
continue; /* don't retain */
}
} else if (dargs->optflag & DA_ADD) {
/*
* Need to know which suffixes are in use
*/
rc = (dmap_exact_dev(devmapp,
dargs->devinfo->devlist, &suffix));
if (rc == 0) {
/*
* Same type, different device. Record
* device suffix already in use, if
* applicable.
*/
if ((suffix < DA_MAX_DEVNO &&
suffix != -1) && stdtype)
tmp_bitmap |=
(uint64_t)(1LL << suffix);
} else if ((rc == 1) && !is_allocatable) {
rc = 0;
} else {
/*
* Match allocatable on add is an error
* or mapping attempt returned error
*/
(void) snprintf(errmsg, sizeof (errmsg),
"Cannot add %s on node %s",
dargs->devinfo->devtype,
devmapp->dmap_devname);
syslog(LOG_ERR, "%s", errmsg);
freedmapent(devmapp);
enddmapent();
return (2);
}
} else
/* add other transaction types as needed */
return (2);
} else if ((dargs->optflag & DA_ADD) &&
(stdtype || is_allocatable) &&
dmap_exact_dev(devmapp, dargs->devinfo->devlist,
&suffix)) {
/*
* no dups w/o DA_FORCE, even if type differs,
* if there is a chance this operation is
* machine-driven. The 5 "standard types"
* can be machine-driven adds, and tend to
* be allocatable.
*/
(void) snprintf(errmsg, sizeof (errmsg),
"Cannot add %s on node %s type %s",
dargs->devinfo->devtype,
devmapp->dmap_devname,
devmapp->dmap_devtype);
syslog(LOG_ERR, "%s", errmsg);
freedmapent(devmapp);
enddmapent();
return (2);
}
tmp_str = _dmap2strentry(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();
/*
* No need to rewrite the files if the item to be removed is not
* in the files -- wait for another call on another darg.
*/
if ((dargs->optflag & DA_REMOVE) && !found)
return (0);
if (dargs->optflag & DA_ADD) {
int len;
/*
* If we got here from an event, or from devfsadm,
* we know the stored devname is a useless guess,
* since the files had not been read when the name
* was chosen, and we don't keep them anywhere else
* that is sufficiently definitive.
*/
for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
if (!(tmp_bitmap & (1LL << tmp)))
break;
/* Future: support more than 64 hotplug devices per type? */
if (tmp > DA_MAX_DEVNO)
return (2);
/*
* Let the caller choose the name unless BOTH the name and
* device type one of: cdrom, floppy, audio, rmdisk, or tape.
* (or sr, fd for unlabeled)
*/
len = strlen(defname);
if (stdtype &&
(strncmp(dargs->devinfo->devname, defname, len) == 0)) {
(void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
defname, tmp);
/* if need to free and safe to free */
if (dargs->devinfo->devname != NULL &&
(dargs->optflag & DA_EVENT) != 0)
free(dargs->devinfo->devname);
dargs->devinfo->devname = strdup(new_devname);
}
}
/*
* Now adjust devalloc list to match devmaps
* Note we now have the correct devname for da_match to use.
*/
setdaent();
while ((devallocp = getdaent()) != NULL) {
rc = da_match(devallocp, dargs);
if (rc == 1) {
if (dargs->optflag & DA_ADD) {
/* logging is on if DA_EVENT is set */
if (dargs->optflag & DA_EVENT) {
(void) snprintf(errmsg, sizeof (errmsg),
"%s and %s out of sync,"
"%s only in %s.",
DEVALLOC, DEVMAP,
devallocp->da_devname, DEVALLOC);
syslog(LOG_ERR, "%s", errmsg);
}
freedaent(devallocp);
enddaent();
return (2);
} else if (dargs->optflag & DA_REMOVE) {
/* make list w/o this entry */
freedaent(devallocp);
continue;
}
}
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();
/* the caller needs to know if a remove needs to rewrite files */
if (dargs->optflag & DA_REMOVE)
return (1); /* 0 and 2 cases returned earlier */
return (0); /* Successful DA_ADD */
}
/*
* _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;
int found = 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 in _build_lists and DA_ADD is set, so is DA_FORCE */
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;
}
} else if (rc == 1)
found = 1;
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 == 0) {
tmp_str = _dmap2strentry(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();
/* later code cleanup may cause the use of "found" in other cases */
if (dargs->optflag & DA_REMOVE)
return (found);
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.
* frees the strings
*/
static void
_write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
{
int is_on = -1;
strentry_t *tmp_str, *old_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);
old_str = tmp_str;
tmp_str = tmp_str->se_next;
free(old_str);
}
}
/*
* _write_device_maps -
* writes current entries in the list to device_maps.
* and frees the strings
*/
static void
_write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
{
strentry_t *tmp_str, *old_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);
old_str = tmp_str;
tmp_str = tmp_str->se_next;
free(old_str);
}
}
/*
* _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) +1)) != 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) + 1))
!= 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;
int ret;
int count = 0;
int retry = 10;
int retry_sleep;
uint_t seed;
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);
}
errno = 0;
while (retry > 0) {
count++;
seed = (uint_t)gethrtime();
ret = lockf(lockfd, F_TLOCK, 0);
if (ret == 0) {
(void) utime(lockfile, NULL);
return (lockfd);
}
if ((errno != EACCES) && (errno != EAGAIN)) {
/* cannot set lock */
(void) close(lockfd);
return (-1);
}
retry--;
retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
(void) sleep(retry_sleep);
errno = 0;
}
return (-1);
}
/*
* 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+F";
} else if (flag & DA_RDONLY) {
oflag = DA_RDONLY;
fmode = "rF";
}
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 + 1)) == 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 existing entries and the SINGLE change requested by da_args,
* 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;
char *dapathp = NULL, *dmpathp = NULL;
char apath[MAXPATHLEN], mpath[MAXPATHLEN];
char 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;
}
/*
* If reacting to a hotplug, read the file entries,
* figure out what dname (tname + a new number) goes to the
* device being added/removed, and create a good head_devallocp and
* head_devmapp with everything good still in it (_rebuild_lists)
*
* Else examine all the entries, remove an old one if it is
* a duplicate with a device being added, returning the
* remaining list (_build_lists.)
*
* We need to do this only if the file exists already.
*
* Once we have built these lists, we need to free the strings
* in the head_* arrays before returning.
*/
if (stat(dapathp, &dastat) == 0) {
/* for device allocation, the /etc files are the "master" */
if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
(!(dargs->optflag & DA_FORCE)))
rc = _rebuild_lists(dargs, &head_devallocp,
&head_devmapp);
else
rc = _build_lists(dargs, &head_devallocp,
&head_devmapp);
if (rc != 0 && rc != 1) {
(void) close(tafd);
(void) unlink(apathp);
(void) close(lockfd);
return (-1);
}
} else
rc = 0;
if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
(void) close(tafd);
(void) unlink(apathp);
(void) close(lockfd);
return (0);
}
/*
* TODO: clean up the workings of DA_UPDATE.
* Due to da_match looking at fields that are missing
* in dargs for DA_UPDATE, the da_match call returns no match,
* but due to the way _da2str combines the devalloc_t info with
* the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
*
* This would not scale if any type of update was ever needed
* from the daemon.
*/
/*
* Write out devallocp 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 non-removed pre-existing entries.
*/
if (head_devmapp != NULL)
_write_device_maps(tmfp, head_devmapp);
out:
/*
* Add any new entries here.
*/
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 + 1];
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) {
free(nentry->devinfo.devname);
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_rm_list_entry -
*
* The adding of devnames to a devlist and the removal of a
* device are not symmetrical -- hot_cleanup gives a /devices
* name which is used to remove the dentry whose links all point to
* that /devices entry.
*
* The link argument is present if available to make debugging
* easier.
*
* da_rm_list_entry removes an entry from the linked list of devices.
*
* Returns 1 if the devname was removed successfully,
* 0 if not found, -1 for error.
*/
/*ARGSUSED*/
int
da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
{
int retval = 0;
deventry_t **dentry, *current, *prev;
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);
}
/* Presumably in daemon mode, no need to remove entry, list is empty */
if (*dentry == (deventry_t *)NULL)
return (0);
prev = NULL;
for (current = *dentry; current != NULL;
prev = current, current = current->next) {
if (strcmp(devname, current->devinfo.devname))
continue;
retval = 1;
break;
}
if (retval == 0)
return (0);
free(current->devinfo.devname);
if (current->devinfo.devlist != NULL)
free(current->devinfo.devlist);
if (current->devinfo.devopts != NULL)
free(current->devinfo.devopts);
if (prev == NULL)
*dentry = current->next;
else
prev->next = current->next;
free(current);
return (retval);
}
/*
* 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);
}
}