/*
* 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) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <auth_attr.h>
#include <auth_list.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <bsm/devices.h>
#include <sys/acl.h>
#include <tsol/label.h>
#include <syslog.h>
#include <limits.h>
#include <user_attr.h>
#include <secdb.h>
#include <sys/mkdev.h>
#include <sys/acl.h>
#include <sys/file.h>
#include <sys/procfs.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <utime.h>
#include <libgen.h>
#include <zone.h>
#include <nss_dbdefs.h>
#include <bsm/devalloc.h>
#include <libdevinfo.h>
#include "allocate.h"
extern void print_error(int, char *);
#if defined(DEBUG) || defined(lint)
#define dprintf(s, a) (void) fprintf(stderr, s, a)
#define dperror(s) perror(s)
#else /* !DEBUG */
#define dprintf(s, a) 0
#define dperror(s) 0
#endif /* DEBUG */
#define DEV_ERRORED(sbuf) (((sbuf).st_mode & ~S_IFMT) == ALLOC_ERR_MODE)
#define DEV_ALLOCATED(sbuf) ((sbuf).st_uid != DA_UID || \
!(((sbuf).st_mode & ~S_IFMT) == DEALLOC_MODE || \
DEV_ERRORED(sbuf)))
#define ALLOC_CLEAN "-A"
#define DEALLOC_CLEAN "-D"
#define DAC_DIR "/etc/security/dev"
#define DEVICE_AUTH_SEPARATOR ","
#define LOCALDEVICE "/dev/console"
#define PROCFS "/proc/"
#define SFF_NO_ERROR 0x1
#define ALLOC_BY_NONE -1
#define CHECK_DRANGE 1
#define CHECK_URANGE 2
#define CHECK_ZLABEL 3
extern void audit_allocate_list(char *);
extern void audit_allocate_device(char *);
extern int system_labeled;
extern char *newenv[];
struct state_file {
int sf_flags;
char sf_path[MAXPATHLEN];
};
struct file_info {
struct stat fi_stat;
char *fi_message;
};
struct zone_path {
int count;
char **path;
};
struct dev_names {
char **dnames;
};
static int _dev_file_name(struct state_file *, devmap_t *);
static int lock_dev(char *, struct stat *);
static int _check_label(devalloc_t *, char *, uid_t, int);
static int create_znode(char *, struct zone_path *, devmap_t *);
static int remove_znode(char *, devmap_t *);
static int update_device(char **, char *, int);
/*
* checks if the invoking user is local to the device
*/
/*ARGSUSED*/
int
_is_local(uid_t uid)
{
struct stat statbuf;
if (stat(LOCALDEVICE, &statbuf) == 0 &&
statbuf.st_uid == uid)
return (1);
return (0);
}
/*
* Checks if the user with the specified uid has the specified authorization
*/
int
_is_authorized(char *auths, uid_t uid)
{
char *dcp, *authlist, *lasts;
char pw_buf[NSS_BUFLEN_PASSWD];
struct passwd pw_ent;
/*
* first, the easy cases
*/
if (strcmp(auths, "@") == 0)
return (1);
if (strcmp(auths, "*") == 0)
return (ALLOC_BY_NONE);
if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
return (0);
if (strpbrk(auths, DEVICE_AUTH_SEPARATOR) == NULL)
return (chkauthattr(auths, pw_ent.pw_name));
authlist = strdup(auths);
if (authlist == NULL)
return (0);
for (dcp = authlist;
(dcp = strtok_r(dcp, DEVICE_AUTH_SEPARATOR, &lasts)) != NULL;
dcp = NULL) {
if (chkauthattr(dcp, pw_ent.pw_name))
break;
}
free(authlist);
return (dcp != NULL);
}
/*
* Checks if the specified user has authorization for the device
*/
int
_is_dev_authorized(devalloc_t *da, uid_t uid)
{
int ares;
char *auth_list, *dcp, *subauth = NULL;
auth_list = da->da_devauth;
if (auth_list == NULL)
return (0);
dcp = strpbrk(auth_list, KV_TOKEN_DELIMIT);
if (dcp == NULL)
return (_is_authorized(auth_list, uid));
if (_is_local(uid)) {
/* the local authorization is before the separator */
ares = dcp - auth_list;
subauth = malloc(ares + 1);
if (subauth == NULL)
return (0);
(void) strlcpy(subauth, auth_list, (ares + 1));
auth_list = subauth;
} else
auth_list = dcp + 1;
ares = _is_authorized(auth_list, uid);
if (subauth != NULL)
free(subauth);
return (ares);
}
int
check_devs(devmap_t *dm)
{
int status = 0;
char **file;
if (dm->dmap_devarray == NULL)
return (NODMAPERR);
for (file = dm->dmap_devarray; *file != NULL; file++) {
if ((status = access(*file, F_OK)) == -1) {
dprintf("Unable to access file %s\n", *file);
break;
}
}
return (status);
}
int
print_da_defs(da_defs_t *da_defs)
{
char optbuf[BUFSIZ];
char *p = NULL;
if (da_defs->devopts == NULL) {
dprintf("No default attributes for %s\n", da_defs->devtype);
return (DEFATTRSERR);
}
(void) printf("dev_type=%s\n", da_defs->devtype);
if (_kva2str(da_defs->devopts, optbuf, sizeof (optbuf), KV_ASSIGN,
KV_TOKEN_DELIMIT) == 0) {
if (p = rindex(optbuf, ':'))
*p = '\0';
(void) printf("\t%s\n", optbuf);
}
return (0);
}
void
print_dev_attrs(int optflag, devalloc_t *da, devmap_t *dm,
struct file_info *fip)
{
char *p = NULL;
char optbuf[BUFSIZ];
(void) printf("device=%s%s", dm->dmap_devname, KV_DELIMITER);
(void) printf("type=%s%s", dm->dmap_devtype, KV_DELIMITER);
(void) printf("auths=%s%s",
(da->da_devauth ? da->da_devauth : ""), KV_DELIMITER);
(void) printf("clean=%s%s",
(da->da_devexec ? da->da_devexec : ""), KV_DELIMITER);
if (da->da_devopts != NULL) {
if (_kva2str(da->da_devopts, optbuf, sizeof (optbuf),
KV_ASSIGN, KV_TOKEN_DELIMIT) == 0) {
if (p = rindex(optbuf, ':'))
*p = '\0';
(void) printf("%s", optbuf);
}
}
(void) printf("%s", KV_DELIMITER);
if (optflag & WINDOWING) {
if ((fip->fi_message != NULL) &&
(strcmp(fip->fi_message, DAOPT_CLASS) == 0))
(void) printf("owner=/FREE%s", KV_DELIMITER);
else if (DEV_ERRORED(fip->fi_stat))
(void) printf("owner=/ERROR%s", KV_DELIMITER);
else if (!DEV_ALLOCATED(fip->fi_stat))
(void) printf("owner=/FREE%s", KV_DELIMITER);
else
(void) printf("owner=%u%s", fip->fi_stat.st_uid,
KV_DELIMITER);
}
(void) printf("files=%s", dm->dmap_devlist);
(void) printf("\n");
}
void
print_dev(devmap_t *dm)
{
char **file;
(void) printf(gettext("device: %s "), dm->dmap_devname);
(void) printf(gettext("type: %s "), dm->dmap_devtype);
(void) printf(gettext("files:"));
file = dm->dmap_devarray;
if (file != NULL) {
for (; *file != NULL; file++)
(void) printf(" %s", *file);
}
(void) printf("\n");
}
/* ARGSUSED */
int
_list_device(int optflag, uid_t uid, devalloc_t *da, char *zonename)
{
int bytes = 0;
int error = 0;
int is_authorized = 0;
char *fname = NULL;
char file_name[MAXPATHLEN];
devmap_t *dm;
struct file_info fi;
struct state_file sf;
fi.fi_message = NULL;
setdmapent();
if ((dm = getdmapnam(da->da_devname)) == NULL) {
enddmapent();
dprintf("Unable to find %s in the maps database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
if ((optflag & CLASS) &&
(!(optflag & (LISTALL | LISTFREE | LISTALLOC)))) {
fi.fi_message = DAOPT_CLASS;
if (optflag & LISTATTRS)
print_dev_attrs(optflag, da, dm, &fi);
else
print_dev(dm);
goto out;
}
if (system_labeled) {
if ((error = _dev_file_name(&sf, dm)) != 0) {
freedmapent(dm);
dprintf("Unable to find %s device files\n",
da->da_devname);
error = NODMAPERR;
goto out;
}
fname = sf.sf_path;
} else {
bytes = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
fname = file_name;
}
if (stat(fname, &fi.fi_stat) != 0) {
dprintf("Unable to stat %s\n", fname);
dperror("Error:");
error = DACACCERR;
goto out;
}
is_authorized = _is_dev_authorized(da, uid);
if (optflag & LISTFREE) { /* list_devices -n */
/*
* list all free devices
*/
if (DEV_ALLOCATED(fi.fi_stat)) {
error = PREALLOCERR;
goto out;
}
if (system_labeled) {
/*
* for this free device, check if -
* 1. user has authorization to allocate
* 2. the zone label is within the label range of the
* device
*/
if (is_authorized == ALLOC_BY_NONE) {
error = DAUTHERR;
goto out;
} else if (is_authorized == 0) {
error = UAUTHERR;
goto out;
}
if (_check_label(da, zonename, uid,
CHECK_DRANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
} else if (optflag & LISTALLOC) { /* list_devices -u */
/*
* list all allocated devices
*/
if (!DEV_ALLOCATED(fi.fi_stat)) {
error = DEVNALLOCERR;
goto out;
}
if (fi.fi_stat.st_uid != uid) {
error = DEVSTATEERR;
goto out;
}
if (system_labeled) {
/*
* check if the zone label equals the label at which
* the device is allocated.
*/
if (_check_label(da, zonename, uid,
CHECK_ZLABEL) != 0) {
error = LABELRNGERR;
goto out;
}
}
} else if (optflag & LISTALL) { /* list_devices -l */
/*
* list all devices - free and allocated - available
*/
if (DEV_ALLOCATED(fi.fi_stat)) {
if (optflag & WINDOWING &&
(is_authorized == ALLOC_BY_NONE)) {
/*
* don't complain if we're here for the GUI.
*/
error = 0;
} else if (fi.fi_stat.st_uid != uid) {
if (!(optflag & WINDOWING)) {
error = ALLOCUERR;
goto out;
}
}
if (system_labeled && !(optflag & WINDOWING)) {
/*
* if we're not displaying in the GUI,
* check if the zone label equals the label
* at which the device is allocated.
*/
if (_check_label(da, zonename, uid,
CHECK_ZLABEL) != 0) {
error = LABELRNGERR;
goto out;
}
}
} else if (system_labeled && !(optflag & WINDOWING)) {
/*
* if we're not displaying in the GUI,
* for this free device, check if -
* 1. user has authorization to allocate
* 2. the zone label is within the label range of the
* device
*/
if (is_authorized == ALLOC_BY_NONE) {
error = DAUTHERR;
goto out;
} else if (is_authorized == 0) {
error = UAUTHERR;
goto out;
}
if (_check_label(da, zonename, uid,
CHECK_DRANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
}
if (system_labeled && DEV_ERRORED(fi.fi_stat) && !(optflag & LISTALL)) {
error = DEVSTATEERR;
goto out;
}
if (check_devs(dm) == -1) {
error = DSPMISSERR;
goto out;
}
if (optflag & LISTATTRS)
print_dev_attrs(optflag, da, dm, &fi);
else
print_dev(dm);
error = 0;
out:
freedmapent(dm);
return (error);
}
/* ARGSUSED */
int
list_devices(int optflag, uid_t uid, char *device, char *zonename)
{
int error = 0;
char *class = NULL;
da_defs_t *da_defs;
devalloc_t *da;
if (system_labeled && optflag & WINDOWING && !(optflag & LISTATTRS)) {
/*
* Private interface for GUI.
*/
(void) puts(DA_DB_LOCK);
return (0);
}
if (optflag & USERID) {
/*
* we need device.revoke to list someone else's devices
*/
if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
return (UAUTHERR);
}
if (system_labeled) {
if (!(optflag & USERID) &&
!_is_authorized(DEFAULT_DEV_ALLOC_AUTH, uid))
/*
* we need device.allocate to list our devices
*/
return (UAUTHERR);
if (optflag & LISTDEFS) {
/*
* list default attrs from devalloc_defaults
*/
setdadefent();
if (device) {
/*
* list default attrs for this device type
*/
da_defs = getdadeftype(device);
if (da_defs == NULL) {
enddadefent();
dprintf("No default attributes for "
"%s\n", device);
return (DEFATTRSERR);
}
error = print_da_defs(da_defs);
freedadefent(da_defs);
} else {
/*
* list everything in devalloc_defaults
*/
while ((da_defs = getdadefent()) != NULL) {
(void) print_da_defs(da_defs);
freedadefent(da_defs);
}
}
enddadefent();
return (error);
}
}
/*
* Lock the database to make sure no body writes to it while we are
* reading.
*/
(void) lock_dev(NULL, NULL);
setdaent();
if (device) {
if (optflag & CLASS) {
/*
* list all devices of this class.
*/
while ((da = getdaent()) != NULL) {
class = kva_match(da->da_devopts, DAOPT_CLASS);
if (class && (strcmp(class, device) == 0)) {
(void) _list_device(optflag, uid, da,
zonename);
}
freedaent(da);
}
} else {
/*
* list this device
*/
if ((da = getdanam(device)) == NULL) {
enddaent();
return (NODAERR);
}
error = _list_device(optflag, uid, da, zonename);
freedaent(da);
}
} else {
/*
* list all devices
*/
while ((da = getdaent()) != NULL) {
(void) _list_device(optflag, uid, da, zonename);
freedaent(da);
}
}
enddaent();
return (error);
}
/*
* Set the DAC characteristics of the file.
* This uses a fancy chmod() by setting a minimal ACL which sets the mode
* and discards any existing ACL.
*/
int
_newdac(char *file, uid_t owner, gid_t group, o_mode_t mode)
{
int err = 0;
if (mode == ALLOC_MODE) {
if (chown(file, owner, group) == -1) {
dperror("newdac: unable to chown");
err = CHOWNERR;
}
} else do {
if (chown(file, owner, group) == -1) {
dperror("newdac: unable to chown");
err = CHOWNERR;
}
} while (fdetach(file) == 0);
if (err)
return (err);
if (strncmp(file, "/dev/", strlen("/dev/")) != 0) {
/*
* This could be a SunRay device that is in /tmp.
*/
if (chmod(file, mode) == -1) {
dperror("newdac: unable to chmod");
err = SETACLERR;
}
} else {
err = acl_strip(file, owner, group, (mode_t)mode);
}
if (err != 0) {
dperror("newdac: unable to setacl");
err = SETACLERR;
}
return (err);
}
/*
* lock_dev -
* locks a section of DA_DB_LOCK.
* returns lock fd if successful, else -1 on error.
*/
static int
lock_dev(char *file, struct stat *statbuf)
{
static int lockfd = -1;
int ret;
int count = 0;
int retry = 10;
off_t size = 0;
off_t offset;
char *lockfile;
if (system_labeled)
lockfile = DA_DB_LOCK;
else
lockfile = file;
if (statbuf) {
offset = statbuf->st_rdev;
dprintf("locking %s\n", file);
} else {
offset = 0;
dprintf("locking %s\n", lockfile);
}
if ((lockfd == -1) &&
(lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1) {
dperror("lock_dev: cannot open lock file");
return (-1);
}
if (system_labeled) {
(void) _newdac(lockfile, DA_UID, DA_GID, 0600);
if (lseek(lockfd, offset, SEEK_SET) == -1) {
dperror("lock_dev: cannot position lock file");
return (-1);
}
size = 1;
}
errno = 0;
while (retry) {
count++;
ret = lockf(lockfd, F_TLOCK, size);
if (ret == 0)
return (lockfd);
if ((errno != EACCES) && (errno != EAGAIN)) {
dperror("lock_dev: cannot set lock");
return (-1);
}
retry--;
(void) sleep(count);
errno = 0;
}
return (-1);
}
int
mk_alloc(devmap_t *list, uid_t uid, struct zone_path *zpath)
{
int i;
int error = 0;
char **file;
gid_t gid = getgid();
mode_t mode = ALLOC_MODE;
file = list->dmap_devarray;
if (file == NULL)
return (NODMAPERR);
for (; *file != NULL; file++) {
dprintf("Allocating %s\n", *file);
if ((error = _newdac(*file, uid, gid, mode)) != 0) {
(void) _newdac(*file, ALLOC_ERRID, DA_GID,
ALLOC_ERR_MODE);
break;
}
}
if (system_labeled && zpath->count && (error == 0)) {
/*
* mark as allocated any new device nodes that we
* created in local zone
*/
for (i = 0; i < zpath->count; i++) {
dprintf("Allocating %s\n", zpath->path[i]);
if ((error = _newdac(zpath->path[i], uid, gid,
mode)) != 0) {
(void) _newdac(zpath->path[i], ALLOC_ERRID,
DA_GID, ALLOC_ERR_MODE);
break;
}
}
}
return (error);
}
/*
* mk_revoke() is used instead of system("/usr/sbin/fuser -k file")
* because "/usr/sbin/fuser -k file" kills all processes
* working with the file, even "vold" (bug #4095152).
*/
int
mk_revoke(int optflag, char *file)
{
int r = 0, p[2], fp, lock;
int fuserpid;
char buf[MAXPATHLEN];
FILE *ptr;
pid_t c_pid;
prpsinfo_t info;
(void) strcpy(buf, PROCFS);
/*
* vfork() and execl() just to make the same output
* as before fixing of bug #4095152.
* The problem is that the "fuser" command prints
* one part of output into stderr and another into stdout,
* but user sees them mixed. Of course, better to change "fuser"
* or to intercept and not to print its output.
*/
if (!(optflag & SILENT)) {
c_pid = vfork();
if (c_pid == -1)
return (-1);
if (c_pid == 0) {
dprintf("first exec fuser %s\n", file);
(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
dperror("first exec fuser");
_exit(1);
}
(void) waitpid(c_pid, &lock, 0);
dprintf("exit status %x\n", lock);
if (WEXITSTATUS(lock) != 0)
return (-1);
}
dprintf("first continuing c_pid=%d\n", (int)c_pid);
if (pipe(p)) {
dperror("pipe");
return (-1);
}
/* vfork() and execl() to catch output and to process it */
c_pid = vfork();
if (c_pid == -1) {
dperror("second vfork");
return (-1);
}
dprintf("second continuing c_pid=%d\n", (int)c_pid);
if (c_pid == 0) {
(void) close(p[0]);
(void) close(1);
(void) fcntl(p[1], F_DUPFD, 1);
(void) close(p[1]);
(void) close(2);
dprintf("second exec fuser %s\n", file);
(void) execl("/usr/sbin/fuser", "fuser", file, NULL);
dperror("second exec fuser");
_exit(1);
}
(void) close(p[1]);
if ((ptr = fdopen(p[0], "r")) != NULL) {
while (!feof(ptr)) {
if (fscanf(ptr, "%d", &fuserpid) > 0) {
(void) sprintf(buf + strlen(PROCFS), "%d",
fuserpid);
if ((fp = open(buf, O_RDONLY)) == -1) {
dperror(buf);
continue;
}
if (ioctl(fp, PIOCPSINFO,
(char *)&info) == -1) {
dprintf("%d psinfo failed", fuserpid);
dperror("");
(void) close(fp);
continue;
}
(void) close(fp);
if (strcmp(info.pr_fname, "vold") == NULL) {
dprintf("%d matched vold name\n",
fuserpid);
continue;
}
if (strcmp(info.pr_fname, "deallocate")
== NULL) {
dprintf("%d matched deallocate name\n",
fuserpid);
continue;
}
dprintf("killing %s", info.pr_fname);
dprintf("(%d)\n", fuserpid);
if ((r =
kill((pid_t)fuserpid, SIGKILL)) == -1) {
dprintf("kill %d", fuserpid);
dperror("");
break;
}
}
}
} else {
dperror("fdopen(p[0], r)");
r = -1;
}
(void) fclose(ptr);
return (r);
}
int
mk_unalloc(int optflag, devmap_t *list)
{
int error = 0;
int status;
char **file;
audit_allocate_list(list->dmap_devlist);
file = list->dmap_devarray;
if (file == NULL)
return (NODMAPERR);
for (; *file != NULL; file++) {
dprintf("Deallocating %s\n", *file);
if (mk_revoke(optflag, *file) < 0) {
dprintf("mk_unalloc: unable to revoke %s\n", *file);
dperror("");
error = CNTFRCERR;
}
status = _newdac(*file, DA_UID, DA_GID, DEALLOC_MODE);
if (error == 0)
error = status;
}
return (error);
}
int
mk_error(devmap_t *list)
{
int status = 0;
char **file;
audit_allocate_list(list->dmap_devlist);
file = list->dmap_devarray;
if (file == NULL)
return (NODMAPERR);
for (; *file != NULL; file++) {
dprintf("Putting %s in error state\n", *file);
status = _newdac(*file, ALLOC_ERRID, DA_GID, ALLOC_ERR_MODE);
}
return (status);
}
int
exec_clean(int optflag, char *devname, char *path, uid_t uid, char *zonename,
char *clean_arg)
{
int c;
int status = 0, exit_status;
char *mode, *cmd, *wdwcmd, *zoneroot;
char *devzone = zonename;
char wdwpath[PATH_MAX];
char zonepath[MAXPATHLEN];
char title[100];
char pw_buf[NSS_BUFLEN_PASSWD];
struct passwd pw_ent;
zonepath[0] = '\0';
if (system_labeled) {
if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
if (strcmp(clean_arg, ALLOC_CLEAN) == 0) {
return (-1);
} else if (optflag & FORCE) {
(void) strcpy(zonepath, "/");
devzone = GLOBAL_ZONENAME;
} else {
dprintf("unable to get label for %s zone\n",
zonename);
return (-1);
}
} else {
(void) strcpy(zonepath, zoneroot);
free(zoneroot);
}
}
if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL)
return (-1);
if (optflag & FORCE_ALL)
mode = "-I";
else if (optflag & FORCE)
mode = "-f";
else
mode = "-s";
if (path == NULL)
return (0);
if ((cmd = strrchr(path, '/')) == NULL)
cmd = path;
else
cmd++; /* skip leading '/' */
c = vfork();
switch (c) {
case -1:
return (-1);
case 0:
(void) setuid(0);
if (system_labeled && (optflag & WINDOWING)) {
/* First try .windowing version of script */
(void) strncpy(wdwpath, path, PATH_MAX);
(void) strncat(wdwpath, ".windowing", PATH_MAX);
if ((wdwcmd = strrchr(wdwpath, '/')) == NULL)
wdwcmd = wdwpath;
(void) execl(wdwpath, wdwcmd, mode, devname, clean_arg,
pw_ent.pw_name, devzone, zonepath, NULL);
/* If that failed, run regular version via dtterm */
(void) snprintf(title, sizeof (title),
"Device %s for %s",
strcmp(clean_arg, ALLOC_CLEAN) == 0 ?
"allocation" : "deallocation", devname);
(void) execl("/usr/dt/bin/dtterm", "dtterm",
"-title", title, "-geometry", "x10+100+400",
"-e", "/etc/security/lib/wdwwrapper",
path, mode, devname, clean_arg, pw_ent.pw_name,
devzone, zonepath, NULL);
/*
* And if that failed, continue on to try
* running regular version directly.
*/
}
dprintf("clean script: %s, ", path);
dprintf("cmd=%s, ", cmd);
dprintf("mode=%s, ", mode);
if (system_labeled) {
dprintf("devname=%s ", devname);
dprintf("zonename=%s ", devzone);
dprintf("zonepath=%s ", zonepath);
dprintf("username=%s\n", pw_ent.pw_name);
(void) execl(path, cmd, mode, devname, clean_arg,
pw_ent.pw_name, devzone, zonepath, NULL);
} else {
dprintf("devname=%s\n", devname);
(void) execle(path, cmd, mode, devname, NULL, newenv);
}
dprintf("Unable to execute clean up script %s\n", path);
dperror("");
exit(CNTDEXECERR);
default:
(void) waitpid(c, &status, 0);
dprintf("Child %d", c);
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
dprintf(" exited, status: %d\n", exit_status);
return (exit_status);
} else if (WIFSIGNALED(status)) {
dprintf(" killed, signal %d\n", WTERMSIG(status));
} else {
dprintf(": exit status %d\n", status);
}
return (-1);
}
}
int
_deallocate_dev(int optflag, devalloc_t *da, devmap_t *dm_in, uid_t uid,
char *zonename, int *lock_fd)
{
int bytes = 0;
int error = 0;
int is_authorized = 0;
uid_t nuid;
char *fname = NULL;
char file_name[MAXPATHLEN];
char *devzone = NULL;
devmap_t *dm = NULL, *dm_new = NULL;
struct stat stat_buf;
struct state_file sf;
if (dm_in == NULL) {
setdmapent();
if ((dm_new = getdmapnam(da->da_devname)) == NULL) {
enddmapent();
dprintf("Unable to find %s in device map database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
dm = dm_new;
} else {
dm = dm_in;
}
if (system_labeled) {
if (_dev_file_name(&sf, dm) != 0) {
if (dm_new)
freedmapent(dm_new);
dprintf("Unable to find %s device files\n",
da->da_devname);
error = NODMAPERR;
goto out;
}
fname = sf.sf_path;
} else {
bytes = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
fname = file_name;
}
audit_allocate_device(fname);
if (stat(fname, &stat_buf) != 0) {
dprintf("Unable to stat %s\n", fname);
error = DACACCERR;
goto out;
}
is_authorized = _is_dev_authorized(da, uid);
if (is_authorized == ALLOC_BY_NONE) {
dprintf("Not deallocating %s, not allocatable\n",
da->da_devname);
goto out;
}
if (!(optflag & (FORCE | FORCE_ALL)) && !is_authorized) {
dprintf("User %d is unauthorized to deallocate\n", (int)uid);
error = UAUTHERR;
goto out;
}
if (system_labeled) {
/*
* unless we're here to deallocate by force, check if the
* label at which the device is currently allocated is
* within the user label range.
*/
if (!(optflag & FORCE) &&
_check_label(da, zonename, uid, CHECK_URANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
if (!(optflag & FORCE) && stat_buf.st_uid != uid &&
DEV_ALLOCATED(stat_buf)) {
error = ALLOCUERR;
goto out;
}
if (!DEV_ALLOCATED(stat_buf)) {
if (DEV_ERRORED(stat_buf)) {
if (!(optflag & FORCE)) {
error = DEVSTATEERR;
goto out;
}
} else {
error = DEVNALLOCERR;
goto out;
}
}
/* All checks passed, time to lock and deallocate */
if ((*lock_fd = lock_dev(fname, &stat_buf)) == -1) {
error = DEVLKERR;
goto out;
}
if (system_labeled) {
devzone = kva_match(da->da_devopts, DAOPT_ZONE);
if (devzone == NULL) {
devzone = GLOBAL_ZONENAME;
} else if (strcmp(devzone, GLOBAL_ZONENAME) != 0) {
if ((remove_znode(devzone, dm) != 0) &&
!(optflag & FORCE)) {
error = ZONEERR;
goto out;
}
}
}
if ((error = mk_unalloc(optflag, dm)) != 0) {
if (!(optflag & FORCE))
goto out;
}
if (system_labeled == 0) {
if ((error = _newdac(fname, DA_UID, DA_GID,
DEALLOC_MODE)) != 0) {
(void) _newdac(file_name, DA_UID, DA_GID,
ALLOC_ERR_MODE);
goto out;
}
}
/*
* if we are deallocating device owned by someone else,
* pass the owner's uid to the cleaning script.
*/
nuid = (stat_buf.st_uid == uid) ? uid : stat_buf.st_uid;
error = exec_clean(optflag, da->da_devname, da->da_devexec, nuid,
devzone, DEALLOC_CLEAN);
if (error != 0) {
if (!(optflag & (FORCE | FORCE_ALL))) {
error = CLEANERR;
(void) mk_error(dm);
} else {
error = 0;
}
}
out:
if (dm_new)
freedmapent(dm_new);
return (error);
}
int
_allocate_dev(int optflag, uid_t uid, devalloc_t *da, char *zonename,
int *lock_fd)
{
int i;
int bytes = 0;
int error = 0;
int is_authorized = 0;
int dealloc_optflag = 0;
char *fname = NULL;
char file_name[MAXPATHLEN];
devmap_t *dm;
struct stat stat_buf;
struct state_file sf;
struct zone_path zpath;
zpath.count = 0;
zpath.path = NULL;
setdmapent();
if ((dm = getdmapnam(da->da_devname)) == NULL) {
enddmapent();
dprintf("Unable to find %s in device map database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
if (system_labeled) {
if (_dev_file_name(&sf, dm) != 0) {
freedmapent(dm);
dprintf("Unable to find %s device files\n",
da->da_devname);
error = NODMAPERR;
goto out;
}
fname = sf.sf_path;
} else {
bytes = snprintf(file_name, MAXPATHLEN, "%s/%s", DAC_DIR,
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
fname = file_name;
}
(void) audit_allocate_device(fname);
if (stat(fname, &stat_buf) != 0) {
dprintf("Unable to stat %s\n", fname);
dperror("Error:");
error = DACACCERR;
goto out;
}
if (DEV_ERRORED(stat_buf)) {
error = DEVSTATEERR;
goto out;
}
is_authorized = _is_dev_authorized(da, uid);
if (is_authorized == ALLOC_BY_NONE) {
dprintf("Device %s is not allocatable\n", da->da_devname);
error = UAUTHERR;
goto out;
} else if (!is_authorized && !(optflag & USERNAME)) {
dprintf("User %d is unauthorized to allocate\n", (int)uid);
error = UAUTHERR;
goto out;
}
if (system_labeled) {
/*
* check if label of the zone to which the device is being
* allocated is within the device label range.
*/
if (_check_label(da, zonename, uid, CHECK_DRANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
if (check_devs(dm) == -1) {
error = DSPMISSERR;
goto out;
}
if (DEV_ALLOCATED(stat_buf)) {
if (optflag & FORCE) {
if (optflag & SILENT)
dealloc_optflag = FORCE|SILENT;
else
dealloc_optflag = FORCE;
if (_deallocate_dev(dealloc_optflag, da, dm, uid,
zonename, lock_fd)) {
dprintf("Couldn't force deallocate device %s\n",
da->da_devname);
error = CNTFRCERR;
goto out;
}
} else if (stat_buf.st_uid == uid) {
error = PREALLOCERR;
goto out;
} else {
error = ALLOCUERR;
goto out;
}
}
/* All checks passed, time to lock and allocate */
if ((*lock_fd = lock_dev(fname, &stat_buf)) == -1) {
error = DEVLKERR;
goto out;
}
if (system_labeled) {
/*
* Run the cleaning program; it also mounts allocated
* device if required.
*/
error = exec_clean(optflag, da->da_devname, da->da_devexec, uid,
zonename, ALLOC_CLEAN);
if (error != DEVCLEAN_OK) {
switch (error) {
case DEVCLEAN_ERROR:
case DEVCLEAN_SYSERR:
dprintf("allocate: "
"Error in device clean program %s\n",
da->da_devexec);
error = CLEANERR;
(void) mk_error(dm);
goto out;
case DEVCLEAN_BADMOUNT:
dprintf("allocate: Failed to mount device %s\n",
da->da_devexec);
goto out;
case DEVCLEAN_MOUNTOK:
break;
default:
error = 0;
goto out;
}
}
/*
* If not mounted, create zonelinks, if this is not the
* global zone.
*/
if ((strcmp(zonename, GLOBAL_ZONENAME) != 0) &&
(error != DEVCLEAN_MOUNTOK)) {
if (create_znode(zonename, &zpath, dm) != 0) {
error = ZONEERR;
goto out;
}
}
}
(void) audit_allocate_list(dm->dmap_devlist);
if ((error = mk_alloc(dm, uid, &zpath)) != 0) {
(void) mk_unalloc(optflag, dm);
goto out;
}
if (system_labeled == 0) {
if ((error = _newdac(file_name, uid, getgid(),
ALLOC_MODE)) != 0) {
(void) _newdac(file_name, DA_UID, DA_GID,
ALLOC_ERR_MODE);
goto out;
}
}
error = 0;
out:
if (zpath.count) {
for (i = 0; i < zpath.count; i++)
free(zpath.path[i]);
free(zpath.path);
}
freedmapent(dm);
return (error);
}
void
_store_devnames(int *count, struct dev_names *dnms, char *zonename,
devalloc_t *da, int flag)
{
int i;
dnms->dnames = (char **)realloc(dnms->dnames,
(*count + 1) * sizeof (char *));
if (da) {
dnms->dnames[*count] = strdup(da->da_devname);
(*count)++;
} else {
dnms->dnames[*count] = NULL;
if (flag == DA_ADD_ZONE)
(void) update_device(dnms->dnames, zonename,
DA_ADD_ZONE);
else if (flag == DA_REMOVE_ZONE)
(void) update_device(dnms->dnames, NULL,
DA_REMOVE_ZONE);
for (i = 0; i < *count; i++)
free(dnms->dnames[i]);
free(dnms->dnames);
}
}
int
allocate(int optflag, uid_t uid, char *device, char *zonename)
{
int count = 0;
int error = 0;
int lock_fd = -1;
devalloc_t *da;
struct dev_names dnms;
if (optflag & (FORCE | USERID | USERNAME)) {
if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
return (UAUTHERR);
}
dnms.dnames = NULL;
setdaent();
if (optflag & TYPE) {
/*
* allocate devices of this type
*/
while ((da = getdatype(device)) != NULL) {
if (system_labeled &&
da_check_logindevperm(da->da_devname)) {
freedaent(da);
continue;
}
dprintf("trying to allocate %s\n", da->da_devname);
error = _allocate_dev(optflag, uid, da, zonename,
&lock_fd);
if (system_labeled && (error == 0)) {
/*
* we need to record in device_allocate the
* label (zone name) at which this device is
* being allocated. store this device entry.
*/
_store_devnames(&count, &dnms, zonename, da, 0);
}
freedaent(da);
error = 0;
}
} else {
/*
* allocate this device
*/
if ((da = getdanam(device)) == NULL) {
enddaent();
return (NODAERR);
}
if (system_labeled && da_check_logindevperm(device)) {
freedaent(da);
return (LOGINDEVPERMERR);
}
dprintf("trying to allocate %s\n", da->da_devname);
error = _allocate_dev(optflag, uid, da, zonename, &lock_fd);
/*
* we need to record in device_allocate the label (zone name)
* at which this device is being allocated. store this device
* entry.
*/
if (system_labeled && (error == 0))
_store_devnames(&count, &dnms, zonename, da, 0);
freedaent(da);
if (error == DEVCLEAN_BADMOUNT)
error = 0;
}
enddaent();
if (lock_fd != -1)
(void) close(lock_fd);
/*
* add to device_allocate labels (zone names) for the devices we
* allocated.
*/
if (dnms.dnames)
_store_devnames(&count, &dnms, zonename, NULL, DA_ADD_ZONE);
return (error);
}
/* ARGSUSED */
int
deallocate(int optflag, uid_t uid, char *device, char *zonename)
{
int count = 0;
int error = 0;
int lock_fd = -1;
char *class = NULL;
devalloc_t *da;
struct dev_names dnms;
if (optflag & (FORCE | FORCE_ALL)) {
if (!_is_authorized(DEVICE_REVOKE_AUTH, getuid()))
return (UAUTHERR);
}
if (optflag & FORCE_ALL)
optflag |= FORCE;
dnms.dnames = NULL;
setdaent();
if (optflag & FORCE_ALL) {
/*
* deallocate all devices
*/
while ((da = getdaent()) != NULL) {
if (system_labeled &&
da_check_logindevperm(da->da_devname)) {
freedaent(da);
continue;
}
dprintf("trying to deallocate %s\n", da->da_devname);
error = _deallocate_dev(optflag, da, NULL, uid,
zonename, &lock_fd);
if (system_labeled && (error == 0)) {
/*
* we need to remove this device's allocation
* label (zone name) from device_allocate.
* store this device name.
*/
_store_devnames(&count, &dnms, zonename, da, 0);
}
freedaent(da);
error = 0;
}
} else if (system_labeled && (optflag & TYPE)) {
/*
* deallocate all devices of this type
*/
while ((da = getdatype(device)) != NULL) {
if (da_check_logindevperm(da->da_devname)) {
freedaent(da);
continue;
}
dprintf("trying to deallocate %s\n", da->da_devname);
error = _deallocate_dev(optflag, da, NULL, uid,
zonename, &lock_fd);
if (error == 0) {
/*
* we need to remove this device's allocation
* label (zone name) from device_allocate.
* store this device name.
*/
_store_devnames(&count, &dnms, zonename, da, 0);
}
freedaent(da);
error = 0;
}
} else if (system_labeled && (optflag & CLASS)) {
/*
* deallocate all devices of this class (for sunray)
*/
while ((da = getdaent()) != NULL) {
class = kva_match(da->da_devopts, DAOPT_CLASS);
if (class && (strcmp(class, device) == 0)) {
dprintf("trying to deallocate %s\n",
da->da_devname);
error = _deallocate_dev(optflag, da, NULL, uid,
zonename, &lock_fd);
if (error == 0) {
/*
* we need to remove this device's
* allocation label (zone name) from
* device_allocate. store this device
* name.
*/
_store_devnames(&count, &dnms, zonename,
da, 0);
}
error = 0;
}
freedaent(da);
}
} else if (!(optflag & TYPE)) {
/*
* deallocate this device
*/
if ((da = getdanam(device)) == NULL) {
enddaent();
return (NODAERR);
}
if (system_labeled && da_check_logindevperm(da->da_devname)) {
freedaent(da);
return (LOGINDEVPERMERR);
}
dprintf("trying to deallocate %s\n", da->da_devname);
error = _deallocate_dev(optflag, da, NULL, uid, zonename,
&lock_fd);
if (system_labeled && (error == 0)) {
/*
* we need to remove this device's allocation label
* (zone name) from device_allocate. store this
* device name.
*/
_store_devnames(&count, &dnms, zonename, da, 0);
}
freedaent(da);
if (error == DEVCLEAN_BADMOUNT)
error = 0;
}
enddaent();
if (lock_fd != -1)
(void) close(lock_fd);
/*
* remove from device_allocate labels (zone names) for the devices we
* deallocated.
*/
if (dnms.dnames)
_store_devnames(&count, &dnms, zonename, NULL, DA_REMOVE_ZONE);
return (error);
}
static int
_dev_file_name(struct state_file *sfp, devmap_t *dm)
{
sfp->sf_flags = 0;
/* if devlist is generated, never leave device in error state */
if (dm->dmap_devlist[0] == '`')
sfp->sf_flags |= SFF_NO_ERROR;
if (dm->dmap_devarray == NULL ||
dm->dmap_devarray[0] == NULL)
return (NODMAPERR);
(void) strncpy(sfp->sf_path, dm->dmap_devarray[0],
sizeof (sfp->sf_path));
sfp->sf_path[sizeof (sfp->sf_path) - 1] = '\0';
if (sfp->sf_path[0] == '\0') {
dprintf("dev_file_name: no device list for %s\n",
dm->dmap_devname);
return (NODMAPERR);
}
return (0);
}
/*
* _check_label -
* checks the device label range against zone label, which is also
* user's current label.
* returns 0 if in range, -1 for all other conditions.
*
*/
static int
_check_label(devalloc_t *da, char *zonename, uid_t uid, int flag)
{
int err;
int in_range = 0;
char *alloczone, *lstr;
char pw_buf[NSS_BUFLEN_PASSWD];
blrange_t *range;
m_label_t *zlabel;
struct passwd pw_ent;
if ((da == NULL) || (zonename == NULL))
return (-1);
if ((zlabel = getzonelabelbyname(zonename)) == NULL) {
dprintf("unable to get label for %s zone\n", zonename);
return (-1);
}
if (flag == CHECK_DRANGE) {
blrange_t drange;
drange.lower_bound = blabel_alloc();
lstr = kva_match(da->da_devopts, DAOPT_MINLABEL);
if (lstr == NULL) {
bsllow(drange.lower_bound);
} else if (stobsl(lstr, drange.lower_bound, NO_CORRECTION,
&err) == 0) {
dprintf("bad min_label for device %s\n",
da->da_devname);
free(zlabel);
blabel_free(drange.lower_bound);
return (-1);
}
drange.upper_bound = blabel_alloc();
lstr = kva_match(da->da_devopts, DAOPT_MAXLABEL);
if (lstr == NULL) {
bslhigh(drange.upper_bound);
} else if (stobsl(lstr, drange.upper_bound, NO_CORRECTION,
&err) == 0) {
dprintf("bad max_label for device %s\n",
da->da_devname);
free(zlabel);
blabel_free(drange.lower_bound);
blabel_free(drange.upper_bound);
return (-1);
}
if (blinrange(zlabel, &drange) == 0) {
char *zlbl = NULL, *min = NULL, *max = NULL;
(void) bsltos(zlabel, &zlbl, 0, 0);
(void) bsltos(drange.lower_bound, &min, 0, 0);
(void) bsltos(drange.upper_bound, &max, 0, 0);
dprintf("%s zone label ", zonename);
dprintf("%s outside device label range: ", zlbl);
dprintf("min - %s, ", min);
dprintf("max - %s\n", max);
free(zlabel);
blabel_free(drange.lower_bound);
blabel_free(drange.upper_bound);
return (-1);
}
} else if (flag == CHECK_URANGE) {
if (getpwuid_r(uid, &pw_ent, pw_buf, sizeof (pw_buf)) == NULL) {
dprintf("Unable to get passwd entry for userid %d\n",
(int)uid);
free(zlabel);
return (-1);
}
if ((range = getuserrange(pw_ent.pw_name)) == NULL) {
dprintf("Unable to get label range for userid %d\n",
(int)uid);
free(zlabel);
return (-1);
}
in_range = blinrange(zlabel, range);
free(zlabel);
blabel_free(range->lower_bound);
blabel_free(range->upper_bound);
free(range);
if (in_range == 0) {
dprintf("%s device label ", da->da_devname);
dprintf("out of user %d label range\n", (int)uid);
return (-1);
}
} else if (flag == CHECK_ZLABEL) {
alloczone = kva_match(da->da_devopts, DAOPT_ZONE);
if (alloczone == NULL) {
free(zlabel);
return (-1);
}
if (strcmp(zonename, alloczone) != 0) {
dprintf("%s zone is different than ", zonename);
dprintf("%s zone to which the device ", alloczone);
dprintf("%s is allocated\n", da->da_devname);
free(zlabel);
return (-1);
}
}
free(zlabel);
return (0);
}
int
create_znode(char *zonename, struct zone_path *zpath, devmap_t *list)
{
int size;
int len = 0;
int fcount = 0;
char *p, *tmpfile, *zoneroot;
char **file;
char zonepath[MAXPATHLEN];
di_prof_t prof = NULL;
file = list->dmap_devarray;
if (file == NULL)
return (NODMAPERR);
if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
dprintf("unable to get label for %s zone\n", zonename);
return (1);
}
(void) strcpy(zonepath, zoneroot);
free(zoneroot);
len = strlen(zonepath);
size = sizeof (zonepath);
(void) strlcat(zonepath, "/dev", size);
if (di_prof_init(zonepath, &prof)) {
dprintf("failed to initialize dev profile at %s\n", zonepath);
return (1);
}
zonepath[len] = '\0';
for (; *file != NULL; file++) {
/*
* First time initialization
*/
tmpfile = strdup(*file);
/*
* Most devices have pathnames starting in /dev
* but SunRay devices do not. In SRRS 3.1 they use /tmp.
*
* If the device pathname is not in /dev then create
* a symbolic link to it and put the device in /dev
*/
if (strncmp(tmpfile, "/dev/", strlen("/dev/")) != 0) {
char *linkdir;
char srclinkdir[MAXPATHLEN];
char dstlinkdir[MAXPATHLEN];
linkdir = strchr(tmpfile + 1, '/');
p = strchr(linkdir + 1, '/');
*p = '\0';
(void) strcpy(dstlinkdir, "/dev");
(void) strncat(dstlinkdir, linkdir, MAXPATHLEN);
(void) snprintf(srclinkdir, MAXPATHLEN, "%s/root%s",
zonepath, tmpfile);
(void) symlink(dstlinkdir, srclinkdir);
*p = '/';
(void) strncat(dstlinkdir, p, MAXPATHLEN);
free(tmpfile);
tmpfile = strdup(dstlinkdir);
}
if (di_prof_add_dev(prof, tmpfile)) {
dprintf("failed to add %s to profile\n", tmpfile);
di_prof_fini(prof);
return (1);
}
if (strlcat(zonepath, tmpfile, size) >= size) {
dprintf("Buffer overflow in create_znode for %s\n",
*file);
free(tmpfile);
di_prof_fini(prof);
return (1);
}
free(tmpfile);
fcount++;
if ((zpath->path = (char **)realloc(zpath->path,
(fcount * sizeof (char *)))) == NULL) {
di_prof_fini(prof);
return (1);
}
zpath->path[zpath->count] = strdup(zonepath);
zpath->count = fcount;
zonepath[len] = '\0';
}
if (di_prof_commit(prof))
dprintf("failed to add devices to zone %s\n", zonename);
di_prof_fini(prof);
return (0);
}
int
remove_znode(char *zonename, devmap_t *dm)
{
int len = 0;
char *zoneroot;
char **file;
char zonepath[MAXPATHLEN];
di_prof_t prof = NULL;
file = dm->dmap_devarray;
if (file == NULL)
return (NODMAPERR);
if ((zoneroot = getzonerootbyname(zonename)) == NULL) {
(void) snprintf(zonepath, MAXPATHLEN, "/zone/%s", zonename);
} else {
(void) strcpy(zonepath, zoneroot);
free(zoneroot);
}
/*
* To support SunRay we will just deal with the
* file in /dev, not the symlinks.
*/
(void) strncat(zonepath, "/dev", MAXPATHLEN);
len = strlen(zonepath);
if (di_prof_init(zonepath, &prof)) {
dprintf("failed to initialize dev profile at %s\n", zonepath);
return (1);
}
for (; *file != NULL; file++) {
char *devrelpath;
/*
* remove device node from zone.
*
* SunRay devices don't start with /dev
* so skip over first directory to make
* sure it is /dev. SunRay devices in zones
* will have a symlink into /dev but
* we don't ever delete it.
*/
devrelpath = strchr(*file + 1, '/');
if (di_prof_add_exclude(prof, devrelpath + 1)) {
dprintf("Failed exclude %s in dev profile\n", *file);
di_prof_fini(prof);
return (1);
}
zonepath[len] = '\0';
}
if (di_prof_commit(prof))
dprintf("failed to remove devices from zone %s\n", zonename);
di_prof_fini(prof);
return (0);
}
int
update_device(char **devnames, char *zonename, int flag)
{
int len, rc;
char *optstr = NULL;
da_args dargs;
devinfo_t devinfo;
dargs.optflag = flag;
dargs.optflag |= DA_UPDATE|DA_ALLOC_ONLY;
dargs.rootdir = NULL;
dargs.devnames = devnames;
devinfo.devname = devinfo.devtype = devinfo.devauths = devinfo.devexec =
devinfo.devlist = NULL;
if (dargs.optflag & DA_ADD_ZONE) {
len = strlen(DAOPT_ZONE) + strlen(zonename) + 3;
if ((optstr = (char *)malloc(len)) == NULL)
return (-1);
(void) snprintf(optstr, len, "%s%s%s", DAOPT_ZONE, KV_ASSIGN,
zonename);
devinfo.devopts = optstr;
}
dargs.devinfo = &devinfo;
rc = da_update_device(&dargs);
if (optstr)
free(optstr);
return (rc);
}