From: Kay Sievers Date: Sat, 6 Jun 2009 14:07:06 +0000 (+0200) Subject: path_id: implement in C using libudev X-Git-Tag: 174~1011 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=185ea6a76d3f3a9e9708239d2302bd98e00460fa path_id: implement in C using libudev --- diff --git a/TODO b/TODO index bedccdb63..dfbb18d0c 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ + o kill path_id.sh (add fc, sas, iscsi to C version) o add tests for kernel provided DEVNAME logic o drop modprobe floppy alias (SUSE), it will be in the module (2.6.30) o remove MMC rules, they got a modalias now (2.6.30) diff --git a/extras/path_id/.gitignore b/extras/path_id/.gitignore new file mode 100644 index 000000000..6fd2f8976 --- /dev/null +++ b/extras/path_id/.gitignore @@ -0,0 +1 @@ +path_id diff --git a/extras/path_id/Makefile.am b/extras/path_id/Makefile.am index f48929039..2e936234f 100644 --- a/extras/path_id/Makefile.am +++ b/extras/path_id/Makefile.am @@ -2,7 +2,19 @@ include $(top_srcdir)/Makefile.am.inc udevhomedir = $(udev_prefix)/lib/udev dist_udevhome_SCRIPTS = \ + path_id.sh + +udevhome_PROGRAMS = \ path_id +path_id_SOURCES = \ + path_id.c \ + ../../udev/lib/libudev.h \ + ../../udev/lib/libudev.c \ + ../../udev/lib/libudev-device.c \ + ../../udev/lib/libudev-enumerate.c \ + ../../udev/lib/libudev-list.c \ + ../../udev/lib/libudev-util.c + dist_man_MANS = \ path_id.8 diff --git a/extras/path_id/path_id.c b/extras/path_id/path_id.c new file mode 100644 index 000000000..872f0c932 --- /dev/null +++ b/extras/path_id/path_id.c @@ -0,0 +1,365 @@ +/* + * compose persisistent device path + * + * Copyright (C) 2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include <../../udev/udev.h> + +int debug; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn != NULL ? fn : file); + vfprintf(stderr, format, args); + } else { + vsyslog(priority, format, args); + } +} + +static int path_prepend(char **path, const char *fmt, ...) +{ + va_list va; + char *old; + char *pre; + int err; + + old = *path; + + va_start(va, fmt); + err = vasprintf(&pre, fmt, va); + va_end(va); + if (err < 0) + return err; + + if (old != NULL) { + err = asprintf(path, "%s-%s", pre, old); + if (err < 0) + return err; + free(pre); + } else { + *path = pre; + } + + free(old); + return 0; +} + +static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) +{ + struct udev_device *parent = dev; + + while (parent != NULL) { + const char *subsystem; + + subsystem = udev_device_get_subsystem(parent); + if (subsystem == NULL || strcmp(subsystem, subsys) != 0) + break; + dev = parent; + parent = udev_device_get_parent(parent); + } + return dev; +} + +/* find smallest number of instances of / */ +static int base_number(const char *syspath, const char *name) +{ + char *base; + char *pos; + DIR *dir; + struct dirent *dent; + size_t len; + int number = -1; + + base = strdup(syspath); + if (base == NULL) + goto out; + + pos = strrchr(base, '/'); + if (pos == NULL) + goto out; + pos[0] = '\0'; + + len = strlen(name); + dir = opendir(base); + if (dir == NULL) + goto out; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *rest; + int i; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) + continue; + if (strncmp(dent->d_name, name, len) != 0) + continue; + i = strtoul(&dent->d_name[len], &rest, 10); + if (rest[0] != '\0') + continue; + if (number == -1 || i < number) + number = i; + } + closedir(dir); +out: + free(base); + return number; +} + +static struct udev_device *handle_scsi(struct udev_device *dev, char **path) +{ + const char *devtype; + struct udev_device *hostdev; + const char *name; + int host, bus, target, lun; + int base; + + devtype = udev_device_get_devtype(dev); + if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) + return dev; + + hostdev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_host"); + if (hostdev == NULL) + return dev; + + name = udev_device_get_sysname(dev); + if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) + return dev; + + /* rebase host offset to get the local relative number */ + base = base_number(udev_device_get_syspath(hostdev), "host"); + if (base < 0) + return dev; + host -= base; + + path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); + dev = skip_subsystem(dev, "scsi"); + return dev; +} + +static void handle_scsi_tape(struct udev_device *dev, char **suffix) +{ + const char *name; + + name = udev_device_get_sysname(dev); + if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) + asprintf(suffix, "nst%c", name[3]); + else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) + asprintf(suffix, "st%c", name[2]); +} + +static struct udev_device *handle_usb(struct udev_device *dev, char **path) +{ + const char *devtype; + const char *str; + const char *port; + + devtype = udev_device_get_devtype(dev); + if (devtype == NULL || strcmp(devtype, "usb_interface") != 0) + return dev; + + str = udev_device_get_sysname(dev); + port = strchr(str, '-'); + if (port == NULL) + return dev; + port++; + + dev = skip_subsystem(dev, "usb"); + path_prepend(path, "usb-0:%s", port); + return dev; +} + +static struct udev_device *handle_firewire(struct udev_device *parent, struct udev_device *dev, char **path) +{ + struct udev_device *scsi_dev; + + scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (scsi_dev != NULL) { + const char *id; + + id = udev_device_get_sysattr_value(scsi_dev, "ieee1394_id"); + if (id != NULL) + path_prepend(path, "ieee1394-0x%s", id); + } + + parent = skip_subsystem(parent, "firewire"); + return parent; +} + +static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) +{ + struct udev_device *scsi_dev; + + scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (scsi_dev != NULL) { + const char *wwpn; + const char *lun; + const char *hba_id; + + hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); + wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); + lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); + if (hba_id != NULL && lun != NULL && wwpn != NULL) { + path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); + goto out; + } + } + + path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); +out: + parent = skip_subsystem(parent, "ccw"); + return parent; +} + +int main(int argc, char **argv) +{ + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + struct udev *udev; + struct udev_device *dev; + struct udev_device *parent; + char syspath[UTIL_PATH_SIZE]; + const char *devpath; + char *path; + char *path_suffix; + int rc = 1; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + logging_init("usb_id"); + udev_set_log_fn(udev, log_fn); + + while (1) { + int option; + + option = getopt_long(argc, argv, "dh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + debug = 1; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + printf("Usage: path_id [--debug] [--help] \n" + " --debug print debug information\n" + " --help print this help text\n\n"); + default: + rc = 1; + goto exit; + } + } + + devpath = argv[optind]; + if (devpath == NULL) { + fprintf(stderr, "No device specified\n"); + rc = 2; + goto exit; + } + + util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL); + dev = udev_device_new_from_syspath(udev, syspath); + if (dev == NULL) { + fprintf(stderr, "unable to access '%s'\n", devpath); + rc = 3; + goto exit; + } + + path = NULL; + path_suffix = NULL; + + parent = dev; + while (parent != NULL) { + const char *subsys; + + subsys = udev_device_get_subsystem(parent); + + if (subsys == NULL) { + ; + } else if (strcmp(subsys, "scsi_tape") == 0) { + handle_scsi_tape(parent, &path_suffix); + } else if (strcmp(subsys, "scsi") == 0) { + parent = handle_scsi(parent, &path); + } else if (strcmp(subsys, "fc_transport") == 0) { + ; //handle_fc(); + } else if (strcmp(subsys, "sas_end_device") == 0) { + ; //handle_sas(); + } else if (strcmp(subsys, "iscsi_session") == 0) { + ; //handle_iscsi() + } else if (strcmp(subsys, "ccw") == 0) { + handle_ccw(parent, dev, &path); + } else if (strcmp(subsys, "cciss") == 0) { + ; //handle_cciss(); + } else if (strcmp(subsys, "usb") == 0) { + parent = handle_usb(parent, &path); + } else if (strcmp(subsys, "serio") == 0) { + path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); + parent = skip_subsystem(parent, "serio"); + } else if (strcmp(subsys, "firewire") == 0 || strcmp(subsys, "ieee1394") == 0) { + parent = handle_firewire(parent, dev, &path); + } else if (strcmp(subsys, "pci") == 0) { + path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "pci"); + } else if (strcmp(subsys, "platform") == 0) { + path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "platform"); + } else if (strcmp(subsys, "xen") == 0) { + path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "xen"); + } + + parent = udev_device_get_parent(parent); + } + + if (path != NULL) { + if (path_suffix != NULL) { + printf("ID_PATH=%s%s\n", path, path_suffix); + free(path_suffix); + } else { + printf("ID_PATH=%s\n", path); + } + free(path); + rc = 0; + } + + udev_device_unref(dev); +exit: + udev_unref(udev); + logging_close(); + return rc; +} diff --git a/extras/path_id/path_id b/extras/path_id/path_id.sh similarity index 100% rename from extras/path_id/path_id rename to extras/path_id/path_id.sh diff --git a/rules/rules.d/60-persistent-storage-tape.rules b/rules/rules.d/60-persistent-storage-tape.rules index 30a011560..968528d84 100644 --- a/rules/rules.d/60-persistent-storage-tape.rules +++ b/rules/rules.d/60-persistent-storage-tape.rules @@ -17,7 +17,7 @@ KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$en KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" # by-path (parent device path) -KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{program}="path_id %p" +KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{program}="path_id.sh %p" KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" diff --git a/rules/rules.d/60-persistent-storage.rules b/rules/rules.d/60-persistent-storage.rules index e47cf2157..b39ea0332 100644 --- a/rules/rules.d/60-persistent-storage.rules +++ b/rules/rules.d/60-persistent-storage.rules @@ -47,7 +47,9 @@ KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}= KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" # by-path (parent device path) -ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{program}="path_id %p" +# old shell script for fc, sas, iscsi, s390, ide +ENV{DEVTYPE}=="disk", DEVPATH=="*/css0/*|*/rport-[0-9]*|*/end_device-*|*/session[0-9]*|*/ide[0-9]*", IMPORT{program}="path_id.sh %p" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="", DEVPATH!="*/virtual/*", IMPORT{program}="path_id %p" ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"