diff options
Diffstat (limited to 'usr/src/lib/libdevinfo')
-rw-r--r-- | usr/src/lib/libdevinfo/Makefile.com | 3 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devlink.c | 103 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devlink.h | 10 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devname.c | 701 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devperm.c | 152 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_finddev.c | 163 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/libdevinfo.h | 45 | ||||
-rw-r--r-- | usr/src/lib/libdevinfo/mapfile-vers | 15 |
8 files changed, 1101 insertions, 91 deletions
diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com index 048c28653f..adcea87c26 100644 --- a/usr/src/lib/libdevinfo/Makefile.com +++ b/usr/src/lib/libdevinfo/Makefile.com @@ -29,7 +29,8 @@ LIBRARY= libdevinfo.a VERS= .1 OBJECTS= devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \ - devinfo_devperm.o devfsmap.o devinfo_dli.o + devinfo_devperm.o devfsmap.o devinfo_devname.o \ + devinfo_finddev.o devinfo_dli.o include ../../Makefile.lib include ../../Makefile.rootfs diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.c b/usr/src/lib/libdevinfo/devinfo_devlink.c index 5417b88bc3..3bc5dadf6d 100644 --- a/usr/src/lib/libdevinfo/devinfo_devlink.c +++ b/usr/src/lib/libdevinfo/devinfo_devlink.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,12 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" +#include "libdevinfo.h" #include "devinfo_devlink.h" #undef DEBUG @@ -205,7 +205,7 @@ get_db_path( } #endif if (dir == NULL) { - dir = hdp->dev_dir; + dir = hdp->db_dir; } (void) snprintf(buf, blen, "%s/%s", dir, fname); @@ -246,6 +246,14 @@ open_db(struct di_devlink_handle *hdp, int flags) get_db_path(hdp, DB_TMP, path, sizeof (path)); } + /* + * Avoid triggering /dev reconfigure for read when not present + */ + if (IS_RDONLY(flags) && + (strncmp(path, "/dev/", 5) == 0) && !device_exists(path)) { + return (-1); + } + if ((fd = open(path, flg, DB_PERMS)) == -1) { return (-1); } @@ -298,12 +306,13 @@ open_db(struct di_devlink_handle *hdp, int flags) static struct di_devlink_handle * handle_alloc(const char *root_dir, uint_t flags) { - char dev_dir[PATH_MAX], path[PATH_MAX]; + char dev_dir[PATH_MAX], path[PATH_MAX], db_dir[PATH_MAX]; struct di_devlink_handle *hdp, proto = {0}; assert(flags == OPEN_RDWR || flags == OPEN_RDONLY); dev_dir[0] = '\0'; + db_dir[0] = '\0'; /* * NULL and the empty string are equivalent to "/" @@ -319,18 +328,24 @@ handle_alloc(const char *root_dir, uint_t flags) /*LINTED*/ assert(sizeof (dev_dir) >= PATH_MAX); #endif - if (realpath(root_dir, dev_dir) == NULL) { + if ((realpath(root_dir, dev_dir) == NULL) || + (realpath(root_dir, db_dir) == NULL)) { return (NULL); } } if (strcmp(dev_dir, "/") == 0) { - (void) strlcpy(dev_dir, DEV, sizeof (dev_dir)); + dev_dir[0] = 0; + db_dir[0] = 0; } else { - (void) strlcat(dev_dir, DEV, sizeof (dev_dir)); + (void) strlcpy(db_dir, dev_dir, sizeof (db_dir)); } + (void) strlcat(dev_dir, DEV, sizeof (dev_dir)); + (void) strlcat(db_dir, ETCDEV, sizeof (db_dir)); + proto.dev_dir = dev_dir; + proto.db_dir = db_dir; proto.flags = flags; proto.lock_fd = -1; @@ -366,6 +381,11 @@ handle_alloc(const char *root_dir, uint_t flags) goto error; } + if ((hdp->db_dir = strdup(proto.db_dir)) == NULL) { + free(hdp->dev_dir); + free(hdp); + goto error; + } return (hdp); @@ -1110,6 +1130,8 @@ link2minor(struct di_devlink_handle *hdp, cache_link_t *clp) cache_link_t *plp; const char *minor_path; char *cp, buf[PATH_MAX], link[PATH_MAX]; + char abspath[PATH_MAX]; + struct stat st; if (TYPE_PRI(attr2type(clp->attr))) { /* @@ -1127,7 +1149,7 @@ link2minor(struct di_devlink_handle *hdp, cache_link_t *clp) /* * If secondary, the primary link is derived from the secondary * link contents. Secondary link contents can have two formats: - * audio -> /dev/sound/0 + * audio -> /dev/sound/0 * fb0 -> fbs/afb0 */ @@ -1162,6 +1184,35 @@ follow_link: /*LINTED*/ assert(sizeof (buf) >= PATH_MAX); #endif + + /* + * A realpath attempt to lookup a dangling link can invoke implicit + * reconfig so verify there's an actual device behind the link first. + */ + if (lstat(link, &st) == -1) + return (NULL); + if (S_ISLNK(st.st_mode)) { + if (s_readlink(link, buf, sizeof (buf)) < 0) + return (NULL); + if (buf[0] != '/') { + char *p; + size_t n = sizeof (abspath); + if (strlcpy(abspath, link, n) >= n) + return (NULL); + p = strrchr(abspath, '/') + 1; + *p = 0; + n = sizeof (abspath) - strlen(p); + if (strlcpy(p, buf, n) >= n) + return (NULL); + } else { + if (strlcpy(abspath, buf, sizeof (abspath)) >= + sizeof (abspath)) + return (NULL); + } + if (!device_exists(abspath)) + return (NULL); + } + if (realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) { return (NULL); } @@ -2399,13 +2450,13 @@ do_recurse( recurse_t *rp, int *retp) { - DIR *dp; size_t len; const char *rel; struct stat sbuf; char cur[PATH_MAX], *cp; int i, rv = DI_WALK_CONTINUE; - struct dirent *entp; + finddevhdl_t handle; + char *d_name; if ((rel = rel_path(hdp, dir)) == NULL) @@ -2424,7 +2475,7 @@ do_recurse( (void) dprintf(DBG_STEP, "do_recurse: dir = %s\n", dir); - if ((dp = opendir(dir)) == NULL) + if (finddev_readdir(dir, &handle) != 0) return (DI_WALK_CONTINUE); (void) snprintf(cur, sizeof (cur), "%s/", dir); @@ -2432,14 +2483,12 @@ do_recurse( cp = cur + len; len = sizeof (cur) - len; - while ((entp = readdir(dp)) != NULL) { - - if (strcmp(entp->d_name, ".") == 0 || - strcmp(entp->d_name, "..") == 0) { - continue; - } + for (;;) { + if ((d_name = (char *)finddev_next(handle)) == NULL) + break; - (void) snprintf(cp, len, "%s", entp->d_name); + if (strlcpy(cp, d_name, len) >= len) + break; /* * Skip files we are not interested in. @@ -2475,7 +2524,7 @@ next_entry: break; } - (void) closedir(dp); + finddev_close(handle); return (rv); } @@ -3011,7 +3060,7 @@ exit_update_lock(struct di_devlink_handle *hdp) * returns 1 if contents is a minor node in /devices. * If mn_root is not NULL, mn_root is set to: * if contents is a /dev node, mn_root = contents - * OR + * OR * if contents is a /devices node, mn_root set to the '/' * following /devices. */ @@ -3203,9 +3252,17 @@ daemon_call(const char *root, struct dca_off *dcp) int fd, door_error; sigset_t oset, nset; char synch_door[PATH_MAX]; + struct statvfs svf; + char *prefix; + /* + * If readonly root, assume we are in install + */ + prefix = + (statvfs("/etc/dev", &svf) == 0 && (svf.f_flag & ST_RDONLY)) ? + "/tmp" : (char *)root; (void) snprintf(synch_door, sizeof (synch_door), - "%s/dev/%s", root, DEVFSADM_SYNCH_DOOR); + "%s/etc/dev/%s", prefix, DEVFSADM_SYNCH_DOOR); if ((fd = open(synch_door, O_RDONLY)) == -1) { dcp->dca_error = errno; diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.h b/usr/src/lib/libdevinfo/devinfo_devlink.h index 0bee7f4c39..d16bb7e52d 100644 --- a/usr/src/lib/libdevinfo/devinfo_devlink.h +++ b/usr/src/lib/libdevinfo/devinfo_devlink.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -19,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -56,6 +55,7 @@ extern "C" { #include <sys/wait.h> #include <door.h> #include <signal.h> +#include <sys/statvfs.h> struct db_link { uint32_t attr; /* primary or secondary */ @@ -143,6 +143,7 @@ struct db { struct di_devlink_handle { char *dev_dir; /* <root-dir>/dev */ + char *db_dir; /* <root-dir>/etc/dev */ uint_t flags; /* handle flags */ uint_t error; /* records errors encountered */ int lock_fd; /* lock file for updates */ @@ -199,6 +200,7 @@ typedef enum { #define DB_NIL 0 #define DEV "/dev" +#define ETCDEV "/etc/dev" #define DEVICES_SUFFIX "ices" #define HDR_LEN sizeof (struct db_hdr) diff --git a/usr/src/lib/libdevinfo/devinfo_devname.c b/usr/src/lib/libdevinfo/devinfo_devname.c new file mode 100644 index 0000000000..379c156627 --- /dev/null +++ b/usr/src/lib/libdevinfo/devinfo_devname.c @@ -0,0 +1,701 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stdarg.h> +#include <fcntl.h> +#include <stdlib.h> +#include <libnvpair.h> +#include <libdevinfo.h> +#include <syslog.h> +#include <sys/param.h> +#include <errno.h> +#include <assert.h> +#include <sys/systeminfo.h> +#include <sys/modctl.h> +#include <sys/fs/sdev_node.h> + + +#define LINEMAX 1024 +#define SPC " \t\n" +#define QUOTES "\'\"" + +/* + * This is for local file supports of DBNR configurations. + */ +static int di_devname_getmapent_files(char *, char *, nvlist_t **); +static int di_devname_get_mapinfo_files(char *, nvlist_t **); +static int parse_mapinfo_file(FILE *, nvlist_t **); +static FILE *open_local_map(char *); +static void unquote(char *, char *); + +static int msglog = 1; +typedef enum { + DBG_ERR = 1, + DBG_INFO, + DBG_STEP, + DBG_ALL +} debug_level_t; +static int devname_debug = 1; +static void dprintf(debug_level_t, const char *, ...); + +extern int isspace(int); + +/* exported interfaces */ +void di_devname_print_mapinfo(nvlist_t *); +int di_devname_get_mapinfo(char *, nvlist_t **); +int di_devname_get_mapent(char *, char *, nvlist_t **); +int di_devname_action_on_key(nvlist_t *, uint8_t, char *, void *); + +/* + * Returns 0 and the valid maplist, otherwise errno. + */ +int +di_devname_get_mapinfo_files(char *mapname, nvlist_t **maplist) +{ + FILE *fp; + int rval = 0; + nvlist_t *nvl = NULL; + + fp = open_local_map(mapname); + if (fp == NULL) { + dprintf(DBG_INFO, "di_devname_get_mapinfo_files: file %s does" + "not exist\n", mapname); + return (ENOENT); + } + + rval = parse_mapinfo_file(fp, &nvl); + if (rval == 0) { + *maplist = nvl; + } + (void) fclose(fp); + + return (rval); +} + +static FILE * +open_local_map(char *mapname) +{ + char filename[LINEMAX]; + + if (*mapname != '/') { + (void) snprintf(filename, sizeof (filename), "/etc/dev/%s", + mapname); + } else { + (void) snprintf(filename, sizeof (filename), "%s", mapname); + } + + return (fopen(filename, "r")); +} + +static void +unquote(char *str, char *qbuf) +{ + register int escaped, inquote, quoted; + register char *ip, *bp, *qp; + char buf[LINEMAX]; + + escaped = inquote = quoted = 0; + + for (ip = str, bp = buf, qp = qbuf; *ip; ip++) { + if (!escaped) { + if (*ip == '\\') { + escaped = 1; + quoted ++; + continue; + } else if (*ip == '"') { + inquote = !inquote; + quoted ++; + continue; + } + } + + *bp++ = *ip; + *qp++ = (inquote || escaped) ? '^' : ' '; + escaped = 0; + } + *bp = '\0'; + *qp = '\0'; + if (quoted) + (void) strcpy(str, buf); +} + +/* + * gets the qualified characters in *p into w, which has space allocated + * already + */ +static int +getword(char *w, char *wq, char **p, char **pq, char delim, int wordsz) +{ + char *tmp = w; + char *tmpq = wq; + int count = wordsz; + + if (wordsz <= 0) { + return (-1); + } + + while ((delim == ' ' ? isspace(**p) : **p == delim) && **pq == ' ') { + (*p)++; + (*pq)++; + } + + while (**p && + !((delim == ' ' ? isspace(**p) : **p == delim) && + **pq == ' ')) { + if (--count <= 0) { + *tmp = '\0'; + *tmpq = '\0'; + dprintf(DBG_INFO, "maximum word length %d exceeded\n", + wordsz); + return (-1); + } + *w++ = *(*p)++; + *wq++ = *(*pq)++; + } + *w = '\0'; + *wq = '\0'; + return (0); +} + +static int +parse_mapinfo_file(FILE *fp, nvlist_t **ret_nvlp) +{ + int error = 0; + nvlist_t *nvl = NULL, *attrs = NULL; + char line[LINEMAX], lineq[LINEMAX]; + char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; + char *lp, *lq; + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { + return (EFAULT); + } + + while (fgets(line, sizeof (line), fp)) { + char *name, *key, *val; + + lp = (char *)line; + lq = (char *)lineq; + unquote(lp, lq); + if ((getword(word, wordq, &lp, &lq, ' ', + sizeof (word)) == -1) || (word[0] == '\0')) + continue; + + if (word[0] == '#') + continue; + + name = strtok(line, SPC); + if (name == NULL) + continue; + + (void) dprintf(DBG_INFO, "get a line for %s\n", name); + key = strtok(NULL, "="); + if (key == NULL) { + (void) dprintf(DBG_INFO, "no attributes specified for " + "%s\n", name); + continue; + } + + attrs = NULL; + if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0) { + error = EFAULT; + goto fail1; + } + + while (key && *key) { + char *rest; + rest = strtok(NULL, "\n"); + if (rest == NULL) { + (void) dprintf(DBG_INFO, "no value for key " + "%s\n", key); + break; + } + if (rest[0] == ';') { + val = strdup("devname_null"); + rest++; + } else { + val = strtok(rest, ";"); + rest = strtok(NULL, ""); + } + (void) dprintf(DBG_INFO, "parse_map_info: one entry " + "key=%s val=%s\n", key, val); + if (nvlist_add_string(attrs, key, val) != 0) { + error = EFAULT; + goto fail; + } + + key = strtok(rest, "="); + } + (void) dprintf(DBG_INFO, "parse_map_info: add entry name=%s\n", + name); + if (nvlist_add_nvlist(nvl, name, attrs) != 0) { + error = EFAULT; + goto fail; + } + } + +done: + *ret_nvlp = nvl; + return (0); + +fail: + nvlist_free(attrs); +fail1: + nvlist_free(nvl); + return (error); +} + +void +di_devname_print_mapinfo(nvlist_t *nvl) +{ + char *name, *key, *val; + nvlist_t *attrs; + nvpair_t *nvp, *kvp; + + nvp = nvlist_next_nvpair(nvl, NULL); + while (nvp) { + name = nvpair_name(nvp); + (void) nvpair_value_nvlist(nvp, &attrs); + (void) printf("name = %s, binding attributes:\n", name); + kvp = nvlist_next_nvpair(attrs, NULL); + while (kvp) { + key = nvpair_name(kvp); + (void) nvpair_value_string(kvp, &val); + (void) printf("\t%s = %s\n", key, val); + kvp = nvlist_next_nvpair(attrs, kvp); + } + nvp = nvlist_next_nvpair(nvl, nvp); + } +} + +static int +action_mklink(char *target, char *source) +{ + (void) dprintf(DBG_INFO, "mklink for source %s target %s\n", + source, target); + return (symlink(source, target)); +} + +static struct actions { + char *key; + devname_spec_t spec; + int (*action)(char *, char *); +} actions[] = { + {"devices-path", DEVNAME_NS_PATH, action_mklink}, + {"dev-path", DEVNAME_NS_DEV, action_mklink}, + {NULL, DEVNAME_NS_NONE, NULL} +}; + +static int +action_on_key(uint_t cmd, char *dir_name, char *devname, nvpair_t *attr, + uint32_t *nsmapcount, char **devfsadm_link, devname_spec_t *devfsadm_spec) +{ + int i = 0; + int error = 0; + char *attrname, *attrval; + int len = 0; + char *path = NULL; + + attrname = nvpair_name(attr); + (void) nvpair_value_string(attr, &attrval); + (void) dprintf(DBG_INFO, "key = %s; value = %s\n", attrname, attrval); + + while (actions[i].key) { + if (strcmp(actions[i].key, attrname) == 0) { + switch (cmd) { + case DEVFSADMD_NS_READDIR: + len = strlen(dir_name) + strlen(devname) + 2; + path = malloc(len); + (void) snprintf(path, len, "%s/%s", dir_name, + devname); + error = actions[i].action(path, attrval); + free(path); + if (error) { + (void) dprintf(DBG_INFO, "action " + "failed %d\n", error); + return (error); + } else { + (*nsmapcount)++; + (void) dprintf(DBG_INFO, + "mapcount %d\n", *nsmapcount); + } + break; + case DEVFSADMD_NS_LOOKUP: + *devfsadm_link = strdup(attrval); + *devfsadm_spec = actions[i].spec; + break; + default: + break; + } + } + i++; + } + return (0); +} + +int +di_devname_action_on_key(nvlist_t *map, uint8_t cmd, char *dir_name, void *hdl) +{ + char *name = NULL; + nvpair_t *entry; + nvlist_t *attrs; + int32_t error = 0; + uint32_t ns_mapcount = 0; + char *devfsadm_link = NULL; + devname_spec_t devfsadm_spec = DEVNAME_NS_NONE; + sdev_door_res_t *resp; + + entry = nvlist_next_nvpair(map, NULL); + while (entry) { + nvpair_t *attr; + name = nvpair_name(entry); + (void) dprintf(DBG_INFO, "di_devname_action_on_key: name %s\n", + name); + (void) nvpair_value_nvlist(entry, &attrs); + + attr = nvlist_next_nvpair(attrs, NULL); + while (attr) { + error = action_on_key(cmd, dir_name, name, attr, + &ns_mapcount, &devfsadm_link, &devfsadm_spec); + + /* do not continue if encountered the first error */ + if (error) { + (void) dprintf(DBG_INFO, "error %d\n", error); + return ((int32_t)error); + } + attr = nvlist_next_nvpair(attrs, attr); + } + entry = nvlist_next_nvpair(map, entry); + } + + resp = (sdev_door_res_t *)hdl; + (void) dprintf(DBG_INFO, "cmd is %d\n", cmd); + switch (cmd) { + case DEVFSADMD_NS_READDIR: + resp->ns_rdr_hdl.ns_mapcount = (uint32_t)ns_mapcount; + (void) dprintf(DBG_INFO, "mapcount is %d\n", ns_mapcount); + break; + case DEVFSADMD_NS_LOOKUP: + if (devfsadm_link && devfsadm_spec != DEVNAME_NS_NONE) { + (void) dprintf(DBG_INFO, "devfsadm_link is %s\n", + devfsadm_link); + (void) snprintf(resp->ns_lkp_hdl.devfsadm_link, + strlen(devfsadm_link) + 1, "%s", devfsadm_link); + resp->ns_lkp_hdl.devfsadm_spec = devfsadm_spec; + } else { + (void) dprintf(DBG_INFO, "error out\n"); + return (1); + } + break; + default: + (void) dprintf(DBG_INFO, "error NOTSUP out\n"); + return (ENOTSUP); + } + + return (0); +} + + +static nvlist_t * +getent_mapinfo_file(FILE *fp, char *match) +{ + nvlist_t *nvl, *attrs; + char line[LINEMAX], lineq[LINEMAX]; + char word[MAXPATHLEN+1], wordq[MAXPATHLEN+1]; + int count = 0; + char *lp, *lq; + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + return (NULL); + + while (fgets(line, sizeof (line), fp)) { + char *name, *key, *val; + + if (line[0] == '#') + continue; + + dprintf(DBG_INFO, "getent_mapinfo_file: get a line %s\n", line); + lp = (char *)line; + lq = (char *)lineq; + unquote(lp, lq); + if ((getword(word, wordq, &lp, &lq, ' ', sizeof (word)) + == -1) || (word[0] == '\0')) + continue; + + name = strtok(line, SPC); + if (name == NULL) + continue; + + dprintf(DBG_INFO, "macthing with the key %s match %s\n", + name, match); + /* bypass the non-related entries */ + if (strcmp(name, match) != 0) + continue; + + /* get a matched entry */ + key = strtok(NULL, "="); + if (key == NULL) { + (void) dprintf(DBG_INFO, "no attributes specified " + "for %s\n", name); + goto fail1; + } + + attrs = NULL; + if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) != 0) + goto fail1; + while (key && *key) { + char *rest; + rest = strtok(NULL, "\n"); + if (rest == NULL) { + (void) dprintf(DBG_INFO, "no value for key " + "%s\n", key); + goto fail; + } + if (rest[0] == ';') { + val = strdup("devname_null"); + rest++; + } else { + val = strtok(rest, ";"); + rest = strtok(NULL, ""); + } + (void) dprintf(DBG_INFO, "found entry %s %s for %s\n", + key, val, name); + if (nvlist_add_string(attrs, key, val) != 0) + goto fail; + + key = strtok(rest, "="); + } + (void) dprintf(DBG_INFO, "adding nvlist for %s\n", name); + if (nvlist_add_nvlist(nvl, name, attrs) != 0) + goto fail; + count++; + break; + } + + if (count == 0) + goto fail1; + + return (nvl); + +fail: + nvlist_free(attrs); +fail1: + nvlist_free(nvl); + errno = EFAULT; + return (NULL); +} + +static int +di_devname_getmapent_files(char *key, char *mapname, nvlist_t **map) +{ + FILE *fp; + int rval = 0; + nvlist_t *nvl = NULL; + + fp = open_local_map(mapname); + if (fp == NULL) + return (1); + + nvl = getent_mapinfo_file(fp, key); + if (nvl != NULL) { + *map = nvl; + } else { + rval = errno; + } + (void) fclose(fp); + + return (rval); +} + +int +di_devname_get_mapent(char *key, char *mapname, nvlist_t **map) +{ + dprintf(DBG_INFO, "di_devname_get_mapent: called for %s in %s\n", + key, mapname); + + return (di_devname_getmapent_files(key, mapname, map)); + +} + +int +di_devname_get_mapinfo(char *mapname, nvlist_t **maps) +{ + dprintf(DBG_INFO, "di_devname_get_mapinfo: called for %s\n", mapname); + + return (di_devname_get_mapinfo_files(mapname, maps)); +} + +static void +debug_print(debug_level_t msglevel, const char *fmt, va_list ap) +{ + if (devname_debug < msglevel) + return; + + /* Print a distinctive label for error msgs */ + if (msglevel == DBG_ERR) { + (void) fprintf(stderr, "[ERROR]: "); + } + + if (msglog == TRUE) { + (void) vsyslog(LOG_NOTICE, fmt, ap); + } else { + (void) vfprintf(stderr, fmt, ap); + } +} + +/* ARGSUSED */ +/* PRINTFLIKE2 */ +static void +dprintf(debug_level_t msglevel, const char *fmt, ...) +{ + va_list ap; + + assert(msglevel > 0); + + if (!devname_debug) + return; + + va_start(ap, fmt); + debug_print(msglevel, fmt, ap); + va_end(ap); +} + + +/* + * Private interfaces for non-global /dev profile + */ + +/* + * Allocate opaque data structure for passing profile to the kernel for + * the given mount point. + * + * Note that this interface returns an empty, initialized, profile. + * It does not return what may have been previously committed. + */ +int +di_prof_init(const char *mountpt, di_prof_t *profp) +{ + nvlist_t *nvl; + + if (nvlist_alloc(&nvl, 0, 0)) + return (-1); + + if (nvlist_add_string(nvl, SDEV_NVNAME_MOUNTPT, mountpt)) { + nvlist_free(nvl); + return (-1); + } + + *profp = (di_prof_t)nvl; + return (0); +} + +/* + * Free space allocated by di_prof_init(). + */ +void +di_prof_fini(di_prof_t prof) +{ + nvlist_free((nvlist_t *)prof); +} + +/* + * Sends profile to the kernel. + */ +int +di_prof_commit(di_prof_t prof) +{ + char *buf = NULL; + size_t buflen = 0; + int rv; + + if (nvlist_pack((nvlist_t *)prof, &buf, &buflen, NV_ENCODE_NATIVE, 0)) + return (-1); + rv = modctl(MODDEVNAME, MODDEVNAME_PROFILE, buf, buflen); + free(buf); + return (rv); +} + +/* + * Add a device or directory to profile's include list. + * + * Note that there is no arbitration between conflicting + * include and exclude profile entries, most recent + * is the winner. + */ +int +di_prof_add_dev(di_prof_t prof, const char *dev) +{ + if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_INCLUDE, dev)) + return (-1); + return (0); +} + +/* + * Add a device or directory to profile's exclude list. + * This can effectively remove a previously committed device. + */ +int +di_prof_add_exclude(di_prof_t prof, const char *dev) +{ + if (nvlist_add_string((nvlist_t *)prof, SDEV_NVNAME_EXCLUDE, dev)) + return (-1); + return (0); +} + +/* + * Add a symlink to profile. + */ +int +di_prof_add_symlink(di_prof_t prof, const char *linkname, const char *target) +{ + nvlist_t *nvl = (nvlist_t *)prof; + char *syml[2]; + + syml[0] = (char *)linkname; /* 1st entry must be the symlink */ + syml[1] = (char *)target; /* 2nd entry must be the target */ + if (nvlist_add_string_array(nvl, SDEV_NVNAME_SYMLINK, syml, 2)) + return (-1); + return (0); +} + +/* + * Add a name mapping to profile. + */ +int +di_prof_add_map(di_prof_t prof, const char *source, const char *target) +{ + nvlist_t *nvl = (nvlist_t *)prof; + char *map[2]; + + map[0] = (char *)source; /* 1st entry must be the source */ + map[1] = (char *)target; /* 2nd entry must be the target */ + if (nvlist_add_string_array(nvl, SDEV_NVNAME_MAP, map, 2)) + return (-1); + return (0); +} diff --git a/usr/src/lib/libdevinfo/devinfo_devperm.c b/usr/src/lib/libdevinfo/devinfo_devperm.c index b4fb3fb217..32af4dbe11 100644 --- a/usr/src/lib/libdevinfo/devinfo_devperm.c +++ b/usr/src/lib/libdevinfo/devinfo_devperm.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -116,6 +115,7 @@ setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode, * doesn't support ACLs, therefore, we must assume that * there were no ACLs to remove in the first place. */ + err = 0; if (errno != ENOSYS) { err = -1; @@ -377,8 +377,29 @@ dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode, DIR *dirp; struct dirent *direntp; char errstring[MAX_LINELEN]; + char *p; + regex_t regex; + int alwaysmatch = 0; + char *match; + char *name, *newpath, *remainder_path; + finddevhdl_t handle; + int find_method; + + /* + * Determine to begin if the search needs to be performed via + * finddev, which returns only persisted names in /dev, or readdir, + * for paths other than /dev. This use of finddev avoids triggering + * potential implicit reconfig for names noted as managed by + * logindevperm but not present on the system. + */ + find_method = ((strcmp(path, "/dev") == 0) || + (strncmp(path, "/dev/", 5) == 0)) ? + FLAG_USE_FINDDEV : FLAG_USE_READDIR; /* path must be a valid name */ + if (find_method == FLAG_USE_FINDDEV && !device_exists(path)) { + return (-1); + } if (stat(path, &stat_buf) == -1) { /* * ENOENT errors are expected errors when there are @@ -411,76 +432,89 @@ dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode, } } - dirp = opendir(path); - if (dirp == NULL) { - return (0); + if (find_method == FLAG_USE_READDIR) { + dirp = opendir(path); + if (dirp == NULL) + return (0); } else { - char *p = strchr(left_to_do, '/'); - regex_t regex; - int alwaysmatch = 0; - char *match; - char *name, *newpath, *remainder_path; - - newpath = (char *)malloc(MAXPATHLEN); - if (newpath == NULL) { - return (-1); - } - match = (char *)calloc(MAXPATHLEN, 1); - if (match == NULL) { - free(newpath); - return (-1); - } + if (finddev_readdir(path, &handle) != 0) + return (0); + } - if (p) { - (void) strncpy(match, left_to_do, p - left_to_do); - } else { - (void) strcpy(match, left_to_do); - } + p = strchr(left_to_do, '/'); + alwaysmatch = 0; - if (strcmp(match, "*") == 0) { - alwaysmatch = 1; - } else { - if (regcomp(®ex, match, REG_EXTENDED) != 0) { - free(newpath); - free(match); - return (-1); - } + newpath = (char *)malloc(MAXPATHLEN); + if (newpath == NULL) { + return (-1); + } + match = (char *)calloc(MAXPATHLEN, 1); + if (match == NULL) { + free(newpath); + return (-1); + } + + if (p) { + (void) strncpy(match, left_to_do, p - left_to_do); + } else { + (void) strcpy(match, left_to_do); + } + + if (strcmp(match, "*") == 0) { + alwaysmatch = 1; + } else { + if (regcomp(®ex, match, REG_EXTENDED) != 0) { + free(newpath); + free(match); + return (-1); } + } - while ((direntp = readdir(dirp)) != NULL) { + for (;;) { + if (find_method == FLAG_USE_READDIR) { + if ((direntp = readdir(dirp)) == NULL) + break; name = direntp->d_name; if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) continue; + } else { + if ((name = (char *)finddev_next(handle)) == NULL) + break; + } - if (alwaysmatch || - regexec(®ex, name, 0, NULL, 0) == 0) { - if (strcmp(path, "/") == 0) { - (void) snprintf(newpath, - MAXPATHLEN, "%s%s", path, name); - } else { - (void) snprintf(newpath, - MAXPATHLEN, "%s/%s", path, name); - } + if (alwaysmatch || + regexec(®ex, name, 0, NULL, 0) == 0) { + if (strcmp(path, "/") == 0) { + (void) snprintf(newpath, + MAXPATHLEN, "%s%s", path, name); + } else { + (void) snprintf(newpath, + MAXPATHLEN, "%s/%s", path, name); + } - /* - * recurse but adjust what is still left to do - */ - remainder_path = (p ? - left_to_do + (p - left_to_do) + 1 : - &left_to_do[strlen(left_to_do)]); - if (dir_dev_acc(newpath, remainder_path, - uid, gid, mode, line, errmsg)) { - err = -1; - } + /* + * recurse but adjust what is still left to do + */ + remainder_path = (p ? + left_to_do + (p - left_to_do) + 1 : + &left_to_do[strlen(left_to_do)]); + if (dir_dev_acc(newpath, remainder_path, + uid, gid, mode, line, errmsg)) { + err = -1; } } + } + + if (find_method == FLAG_USE_READDIR) { (void) closedir(dirp); - free(newpath); - free(match); - if (!alwaysmatch) { - regfree(®ex); - } + } else { + finddev_close(handle); + } + free(newpath); + free(match); + if (!alwaysmatch) { + regfree(®ex); } return (err); diff --git a/usr/src/lib/libdevinfo/devinfo_finddev.c b/usr/src/lib/libdevinfo/devinfo_finddev.c new file mode 100644 index 0000000000..1b89afb7b3 --- /dev/null +++ b/usr/src/lib/libdevinfo/devinfo_finddev.c @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <thread.h> +#include <synch.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <dirent.h> +#include <regex.h> +#include <errno.h> +#include <stdarg.h> +#include <libdevinfo.h> +#include <sys/modctl.h> +#include <syslog.h> + +#include <assert.h> + + +struct finddevhdl { + int npaths; + int curpath; + char **paths; +}; + + +int +device_exists(const char *devname) +{ + int rv; + + rv = modctl(MODDEVEXISTS, devname, strlen(devname)); + return ((rv == 0) ? 1 : 0); +} + +int +finddev_readdir(const char *dir, finddevhdl_t *handlep) +{ + struct finddevhdl *handle; + int n; + int rv; + int64_t bufsiz; + char *pathlist; + char *p; + int len; + + *handlep = NULL; + handle = calloc(1, sizeof (struct finddevhdl)); + if (handle == NULL) + return (ENOMEM); + + handle->npaths = 0; + handle->curpath = 0; + handle->paths = NULL; + + rv = modctl(MODDEVREADDIR, dir, strlen(dir), NULL, &bufsiz); + if (rv != 0) { + free(handle); + return (rv); + } + + for (;;) { + assert(bufsiz != 0); + if ((pathlist = malloc(bufsiz)) == NULL) { + free(handle); + return (ENOMEM); + } + + rv = modctl(MODDEVREADDIR, dir, strlen(dir), + pathlist, &bufsiz); + if (rv == 0) { + for (n = 0, p = pathlist; + (len = strlen(p)) > 0; p += len+1) { + n++; + } + handle->npaths = n; + handle->paths = calloc(n, sizeof (char *)); + if (handle->paths == NULL) { + free(handle); + free(pathlist); + return (ENOMEM); + } + for (n = 0, p = pathlist; + (len = strlen(p)) > 0; p += len+1, n++) { + handle->paths[n] = strdup(p); + if (handle->paths[n] == NULL) { + finddev_close((finddevhdl_t)handle); + free(pathlist); + return (ENOMEM); + } + } + *handlep = (finddevhdl_t)handle; + free(pathlist); + return (0); + } + free(pathlist); + switch (errno) { + case EAGAIN: + break; + case ENOENT: + default: + free(handle); + return (errno); + } + } + /*NOTREACHED*/ +} + +void +finddev_close(finddevhdl_t arg) +{ + struct finddevhdl *handle = (struct finddevhdl *)arg; + int i; + + for (i = 0; i < handle->npaths; i++) { + if (handle->paths[i]) + free(handle->paths[i]); + } + free(handle->paths); + free(handle); +} + +const char * +finddev_next(finddevhdl_t arg) +{ + struct finddevhdl *handle = (struct finddevhdl *)arg; + const char *path = NULL; + + if (handle->curpath < handle->npaths) { + path = handle->paths[handle->curpath]; + handle->curpath++; + } + return (path); +} diff --git a/usr/src/lib/libdevinfo/libdevinfo.h b/usr/src/lib/libdevinfo/libdevinfo.h index 62fa27bcb2..0fac182582 100644 --- a/usr/src/lib/libdevinfo/libdevinfo.h +++ b/usr/src/lib/libdevinfo/libdevinfo.h @@ -28,7 +28,12 @@ #pragma ident "%Z%%M% %I% %E% SMI" +#ifdef __cplusplus +extern "C" { +#endif + #include <errno.h> +#include <libnvpair.h> #include <sys/param.h> #include <sys/sunddi.h> #include <sys/sunmdi.h> @@ -37,10 +42,6 @@ #include <sys/devinfo_impl.h> #include <limits.h> -#ifdef __cplusplus -extern "C" { -#endif - /* * flags for di_walk_node */ @@ -394,6 +395,42 @@ extern int di_dli_openr(char *); extern int di_dli_openw(char *); extern void di_dli_close(int); +/* + * Private interface for parsing devname binding info + */ +extern void di_devname_print_mapinfo(nvlist_t *); +extern int di_devname_get_mapinfo(char *, nvlist_t **); +extern int di_devname_get_mapent(char *, char *, nvlist_t **); +extern int di_devname_action_on_key(nvlist_t *, uint8_t, char *, void *); + +/* + * finddev - alternate readdir to discover only /dev persisted device names + */ +typedef struct __finddevhdl *finddevhdl_t; + +extern int device_exists(const char *); +extern int finddev_readdir(const char *, finddevhdl_t *); +extern void finddev_close(finddevhdl_t); +extern const char *finddev_next(finddevhdl_t); + +/* For interfaces implementing search either by readdir or finddev */ +#define FLAG_USE_READDIR 0 +#define FLAG_USE_FINDDEV 1 + + +/* + * Private interfaces for non-global /dev profile + */ +typedef struct __di_prof *di_prof_t; + +extern int di_prof_init(const char *mountpt, di_prof_t *); +extern void di_prof_fini(di_prof_t); +extern int di_prof_commit(di_prof_t); +extern int di_prof_add_dev(di_prof_t, const char *); +extern int di_prof_add_exclude(di_prof_t, const char *); +extern int di_prof_add_symlink(di_prof_t, const char *, const char *); +extern int di_prof_add_map(di_prof_t, const char *, const char *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libdevinfo/mapfile-vers b/usr/src/lib/libdevinfo/mapfile-vers index 6e84e1bee1..5a6310b03d 100644 --- a/usr/src/lib/libdevinfo/mapfile-vers +++ b/usr/src/lib/libdevinfo/mapfile-vers @@ -145,6 +145,10 @@ SUNWprivate_1.1 { di_devlink_walk; di_devperm_login; di_devperm_logout; + di_devname_get_mapinfo; + di_devname_get_mapent; + di_devname_action_on_key; + di_devname_print_mapinfo; di_driver_private_data; di_init_driver; di_init_impl; @@ -173,6 +177,13 @@ SUNWprivate_1.1 { di_path_state; di_phci_first_node; di_phci_next_node; + di_prof_add_dev; + di_prof_add_exclude; + di_prof_add_map; + di_prof_add_symlink; + di_prof_commit; + di_prof_init; + di_prof_fini; di_prop_drv_next; di_prop_global_next; di_prop_hw_next; @@ -184,6 +195,10 @@ SUNWprivate_1.1 { di_dli_openr; di_dli_openw; di_dli_close; + device_exists; + finddev_readdir; + finddev_close; + finddev_next; local: *; }; |