diff options
Diffstat (limited to 'usr/src/lib/libdevinfo/devinfo_devname.c')
-rw-r--r-- | usr/src/lib/libdevinfo/devinfo_devname.c | 701 |
1 files changed, 701 insertions, 0 deletions
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); +} |