diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libdevid/deviceid.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libdevid/deviceid.c')
-rw-r--r-- | usr/src/lib/libdevid/deviceid.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/usr/src/lib/libdevid/deviceid.c b/usr/src/lib/libdevid/deviceid.c new file mode 100644 index 0000000000..840c32c23c --- /dev/null +++ b/usr/src/lib/libdevid/deviceid.c @@ -0,0 +1,378 @@ +/* + * 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. + * + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <ftw.h> +#include <string.h> +#include <thread.h> +#include <synch.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/modctl.h> +#include <strings.h> + +#include <libdevinfo.h> +#include "libdevid.h" + +/* + * Get Device Id from an open file descriptor + */ +int +devid_get(int fd, ddi_devid_t *devidp) +{ + int len = 0; + dev_t dev; + struct stat statb; + ddi_devid_t mydevid; + + if (fstat(fd, &statb) != 0) + return (-1); + + /* If not char or block device, then error */ + if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode)) + return (-1); + + /* Get the device id size */ + dev = statb.st_rdev; + if (modctl(MODSIZEOF_DEVID, dev, &len) != 0) + return (-1); + + /* Allocate space to return device id */ + if ((mydevid = (ddi_devid_t)malloc(len)) == NULL) + return (-1); + + /* Get the device id */ + if (modctl(MODGETDEVID, dev, len, mydevid) != 0) { + free(mydevid); + return (-1); + } + + /* Return the device id copy */ + *devidp = mydevid; + return (0); +} + +/* + * Get the minor name + */ +int +devid_get_minor_name(int fd, char **minor_namep) +{ + int len = 0; + dev_t dev; + int spectype; + char *myminor_name; + struct stat statb; + + if (fstat(fd, &statb) != 0) + return (-1); + + /* If not a char or block device, then return an error */ + if (!S_ISCHR(statb.st_mode) && !S_ISBLK(statb.st_mode)) + return (-1); + + spectype = statb.st_mode & S_IFMT; + dev = statb.st_rdev; + + /* Get the minor name size */ + if (modctl(MODSIZEOF_MINORNAME, dev, spectype, &len) != 0) + return (-1); + + /* Allocate space for the minor name */ + if ((myminor_name = (char *)malloc(len)) == NULL) + return (-1); + + /* Get the minor name */ + if (modctl(MODGETMINORNAME, dev, spectype, len, myminor_name) != 0) { + free(myminor_name); + return (-1); + } + + /* return the minor name copy */ + *minor_namep = myminor_name; + return (0); +} + +/* list element of devid_nmlist_t information */ +struct nmlist { + struct nmlist *nl_next; + char *nl_devname; + dev_t nl_dev; +}; + +/* add list element to end of nmlist headed by *nlhp */ +struct nmlist * +nmlist_add(struct nmlist **nlhp, char *path) +{ + struct stat statb; + dev_t dev; + struct nmlist *nl; + + /* stat and get the devt for char or block */ + if ((stat(path, &statb) == 0) && + (S_ISCHR(statb.st_mode) || S_ISBLK(statb.st_mode))) + dev = statb.st_rdev; + else + dev = NODEV; + + /* find the end of the list */ + for (; (nl = *nlhp) != NULL; nlhp = &nl->nl_next) + ; + + /* allocate and initialize new entry */ + if ((nl = malloc(sizeof (*nl))) == NULL) + return (NULL); + + if ((nl->nl_devname = strdup(path)) == NULL) { + free(nl); + return (NULL); + } + nl->nl_next = NULL; + nl->nl_dev = dev; + + /* link new entry at end */ + *nlhp = nl; + return (nl); +} + +/* information needed by devlink_callback to call nmlist_add */ +struct devlink_cbinfo { + struct nmlist **cbi_nlhp; + char *cbi_search_path; + int cbi_error; +}; + +/* di_devlink callback to add a /dev entry to nmlist */ +static int +devlink_callback(di_devlink_t dl, void *arg) +{ + struct devlink_cbinfo *cbip = (struct devlink_cbinfo *)arg; + char *devpath = (char *)di_devlink_path(dl); + + if (strncmp(devpath, cbip->cbi_search_path, + strlen(cbip->cbi_search_path)) == 0) { + if (nmlist_add(cbip->cbi_nlhp, devpath) == NULL) { + cbip->cbi_error = 1; + return (DI_WALK_TERMINATE); + } + } + return (DI_WALK_CONTINUE); +} + +/* + * Resolve /dev names to DI_PRIMARY_LINK, DI_SECONDARY_LINK, or both. + * The default is to resolve to just the DI_PRIMARY_LINK. + */ +int devid_deviceid_to_nmlist_link = DI_PRIMARY_LINK; + +/* + * Options for the devid_deviceid_to_nmlist implementation: + * + * DEVICEID_NMLIST_SLINK - reduce overhead by reuse the previous + * di_devlink_init. + */ +#define DEVICEID_NMLIST_SLINK 1 +int devid_deviceid_to_nmlist_flg = 0; +static di_devlink_handle_t devid_deviceid_to_nmlist_dlh = NULL; /* SLINK */ + +#define DEVICEID_NMLIST_NRETRY 10 + +/* + * Convert the specified devid/minor_name into a devid_nmlist_t array + * with names that resolve into /devices or /dev depending on search_path. + * + * The man page indicates that: + * + * This function traverses the file tree, starting at search_path. + * + * This is not true, we reverse engineer the paths relative to + * the specified search path to avoid attaching all devices. + */ +int +devid_deviceid_to_nmlist( + char *search_path, + ddi_devid_t devid, + char *minor_name, + devid_nmlist_t **retlist) +{ + char *cp; + int dev; + char *paths = NULL; + char *path; + int lens; + di_devlink_handle_t dlh = NULL; + int ret = -1; + struct devlink_cbinfo cbi; + struct nmlist *nlh = NULL; + struct nmlist *nl; + devid_nmlist_t *rl; + int nret; + int nagain = 0; + int err = 0; + + *retlist = NULL; + + /* verify valid search path starts with "/devices" or "/dev" */ + if ((strcmp(search_path, "/devices") == 0) || + (strncmp(search_path, "/devices/", 9) == 0)) + dev = 0; + else if ((strcmp(search_path, "/dev") == 0) || + (strncmp(search_path, "/dev/", 5) == 0)) + dev = 1; + else { + errno = EINVAL; + return (-1); + } + + + /* translate devid/minor_name to /devices paths */ +again: if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, NULL) != 0) + goto out; + if ((paths = (char *)malloc(lens)) == NULL) + goto out; + if (modctl(MODDEVID2PATHS, devid, minor_name, 0, &lens, paths) != 0) { + if ((errno == EAGAIN) && (nagain++ < DEVICEID_NMLIST_NRETRY)) { + free(paths); + paths = NULL; + goto again; + } + goto out; + } + + /* + * initialize for /devices path to /dev path translation. To reduce + * overhead we reuse the last snapshot if DEVICEID_NMLIST_SLINK is set. + */ + if (dev) { + dlh = devid_deviceid_to_nmlist_dlh; + if (dlh && + !(devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK)) { + (void) di_devlink_fini(&dlh); + dlh = devid_deviceid_to_nmlist_dlh = NULL; + } + if ((dlh == NULL) && + ((dlh = di_devlink_init(NULL, 0)) == NULL)) + goto out; + } + + /* + * iterate over all the devtspectype resolutions of the devid and + * convert them into the appropriate path form and add items to return + * to the nmlist list; + */ + for (path = paths; *path; path += strlen(path) + 1) { + if (dev) { + /* add /dev entries */ + cbi.cbi_nlhp = &nlh; + cbi.cbi_search_path = search_path; + cbi.cbi_error = 0; + + (void) di_devlink_walk(dlh, NULL, path, + devid_deviceid_to_nmlist_link, + (void *)&cbi, devlink_callback); + if (cbi.cbi_error) + goto out; + } else { + /* add /devices entry */ + cp = malloc(strlen("/devices") + strlen(path) + 1); + (void) strcpy(cp, "/devices"); + (void) strcat(cp, path); + if (strncmp(cp, search_path, + strlen(search_path)) == 0) { + if (nmlist_add(&nlh, cp) == NULL) { + free(cp); + goto out; + } + } + free(cp); + } + } + + /* convert from nmlist to retlist array */ + for (nl = nlh, nret = 0; nl; nl = nl->nl_next) + nret++; + if (nret == 0) { + err = ENODEV; + goto out; + } + if ((*retlist = calloc(nret + 1, sizeof (devid_nmlist_t))) == NULL) { + err = ENOMEM; + goto out; + } + for (nl = nlh, rl = *retlist; nl; nl = nl->nl_next, rl++) { + rl->devname = nl->nl_devname; + rl->dev = nl->nl_dev; + } + rl->devname = NULL; + rl->dev = NODEV; + + ret = 0; + +out: + while ((nl = nlh) != NULL) { /* free the nmlist */ + nlh = nl->nl_next; + free(nl); + } + if (paths) + free(paths); + if (dlh) { + if ((ret == 0) && + (devid_deviceid_to_nmlist_flg & DEVICEID_NMLIST_SLINK)) + devid_deviceid_to_nmlist_dlh = dlh; + else + (void) di_devlink_fini(&dlh); + } + if (ret && *retlist) + free(*retlist); + if (ret && err != 0) + errno = err; + return (ret); +} + +/* + * Free Device Id Name List + */ +void +devid_free_nmlist(devid_nmlist_t *list) +{ + devid_nmlist_t *p = list; + + if (list == NULL) + return; + + /* Free all the device names */ + while (p->devname != NULL) { + free(p->devname); + p++; + } + + /* Free the array */ + free(list); +} |