chiark / gitweb /
path_id: add ID_PATH_TAG= to be used in udev tags
[elogind.git] / extras / path_id / path_id.c
index 4b57dec0d31caf3ba7b4f560e5fc2eca582137f8..98f0e400890b3edaae46c8c1614f319e56fa4fbc 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * compose persisistent device path
+ * compose persistent device path
  *
  * Copyright (C) 2009 Kay Sievers <kay.sievers@vrfy.org>
  *
+ * Logic based on Hannes Reinecke's shell script.
+ *
  * 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
@@ -31,7 +33,7 @@
 #include "libudev.h"
 #include "libudev-private.h"
 
-int debug;
+static int debug;
 
 static void log_fn(struct udev *udev, int priority,
                   const char *file, int line, const char *fn,
@@ -48,29 +50,44 @@ static void log_fn(struct udev *udev, int priority,
 static int path_prepend(char **path, const char *fmt, ...)
 {
        va_list va;
-       char *old;
        char *pre;
-       int err;
-
-       old = *path;
+       int err = 0;
 
        va_start(va, fmt);
        err = vasprintf(&pre, fmt, va);
        va_end(va);
        if (err < 0)
-               return err;
+               goto out;
 
-       if (old != NULL) {
-               err = asprintf(path, "%s-%s", pre, old);
-               if (err < 0)
-                       return err;
+       if (*path != NULL) {
+               char *new;
+
+               err = asprintf(&new, "%s-%s", pre, *path);
                free(pre);
+               if (err < 0)
+                       goto out;
+               free(*path);
+               *path = new;
        } else {
                *path = pre;
        }
+out:
+       return err;
+}
 
-       free(old);
-       return 0;
+/*
+** Linux only supports 32 bit luns.
+** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
+*/
+static int format_lun_number(struct udev_device *dev, char **path)
+{
+       unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
+
+       /* address method 0, peripheral device addressing with bus id of zero */
+       if (lun < 256)
+               return path_prepend(path, "lun-%d", lun);
+       /* handle all other lun addressing methods by using a variant of the original lun format */
+       return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff);
 }
 
 static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys)
@@ -95,7 +112,7 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
        struct udev_device *targetdev;
        struct udev_device *fcdev = NULL;
        const char *port;
-       unsigned int lun;
+       char *lun = NULL;;
 
        targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
        if (targetdev == NULL)
@@ -110,8 +127,10 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
                goto out;
        }
 
-       lun = strtoul(udev_device_get_sysnum(parent), NULL, 10);
-       path_prepend(path, "fc-%s:0x%04x%04x00000000", port, lun & 0xffff, (lun >> 16) & 0xffff);
+       format_lun_number(parent, &lun);
+       path_prepend(path, "fc-%s-%s", port, lun);
+       if (lun)
+               free(lun);
 out:
        udev_device_unref(fcdev);
        return parent;
@@ -119,7 +138,39 @@ out:
 
 static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
 {
-       return NULL;
+       struct udev *udev  = udev_device_get_udev(parent);
+       struct udev_device *targetdev;
+       struct udev_device *target_parent;
+       struct udev_device *sasdev;
+       const char *sas_address;
+       char *lun = NULL;
+
+       targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
+       if (targetdev == NULL)
+               return NULL;
+
+       target_parent = udev_device_get_parent(targetdev);
+       if (target_parent == NULL)
+               return NULL;
+
+       sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", 
+                               udev_device_get_sysname(target_parent));
+       if (sasdev == NULL)
+               return NULL;
+
+       sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
+       if (sas_address == NULL) {
+               parent = NULL;
+               goto out;
+       }
+
+       format_lun_number(parent, &lun);
+       path_prepend(path, "sas-%s-%s", sas_address, lun);
+       if (lun)
+               free(lun);
+out:
+       udev_device_unref(sasdev);
+       return parent;
 }
 
 static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path)
@@ -132,18 +183,17 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
        struct udev_device *conndev = NULL;
        const char *addr;
        const char *port;
+       char *lun = NULL;
 
        /* find iscsi session */
        transportdev = parent;
-       while (1) {
+       for (;;) {
                transportdev = udev_device_get_parent(transportdev);
                if (transportdev == NULL)
                        return NULL;
                if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0)
                        break;
        }
-       if (transportdev == NULL)
-               return NULL;
 
        /* find iscsi session device */
        sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
@@ -172,7 +222,10 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
                goto out;
        }
 
-       path_prepend(path, "ip-%s:%s-iscsi-%s-lun-%s", addr, port, target, udev_device_get_sysnum(parent));
+       format_lun_number(parent, &lun);
+       path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
+       if (lun)
+               free(lun);
 out:
        udev_device_unref(sessiondev);
        udev_device_unref(conndev);
@@ -284,15 +337,19 @@ out:
        return parent;
 }
 
-static void handle_scsi_tape(struct udev_device *dev, char **suffix)
+static void handle_scsi_tape(struct udev_device *dev, char **path)
 {
        const char *name;
 
+       /* must be the last device in the syspath */
+       if (*path != NULL)
+               return;
+
        name = udev_device_get_sysname(dev);
        if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL)
-               asprintf(suffix, "nst%c", name[3]);
+               path_prepend(path, "nst%c", name[3]);
        else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL)
-               asprintf(suffix, "st%c", name[2]);
+               path_prepend(path, "st%c", name[2]);
 }
 
 static struct udev_device *handle_usb(struct udev_device *parent, char **path)
@@ -302,7 +359,9 @@ static struct udev_device *handle_usb(struct udev_device *parent, char **path)
        const char *port;
 
        devtype = udev_device_get_devtype(parent);
-       if (devtype == NULL || strcmp(devtype, "usb_interface") != 0)
+       if (devtype == NULL)
+               return parent;
+       if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0)
                return parent;
 
        str = udev_device_get_sysname(parent);
@@ -358,9 +417,8 @@ int main(int argc, char **argv)
        struct udev_device *parent;
        char syspath[UTIL_PATH_SIZE];
        const char *devpath;
-       char *path;
-       char *path_suffix;
-       int rc = 1;
+       char *path = NULL;
+       int rc = EXIT_FAILURE;
 
        udev = udev_new();
        if (udev == NULL)
@@ -386,8 +444,6 @@ int main(int argc, char **argv)
                        printf("Usage: path_id [--debug] [--help] <devpath>\n"
                               "  --debug    print debug information\n"
                               "  --help      print this help text\n\n");
-               default:
-                       rc = 1;
                        goto exit;
                }
        }
@@ -407,9 +463,6 @@ int main(int argc, char **argv)
                goto exit;
        }
 
-       path = NULL;
-       path_suffix = NULL;
-
        /* S390 ccw bus */
        parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
        if (parent != NULL) {
@@ -427,7 +480,7 @@ int main(int argc, char **argv)
                if (subsys == NULL) {
                        ;
                } else if (strcmp(subsys, "scsi_tape") == 0) {
-                       handle_scsi_tape(parent, &path_suffix);
+                       handle_scsi_tape(parent, &path);
                } else if (strcmp(subsys, "scsi") == 0) {
                        parent = handle_scsi(parent, &path);
                } else if (strcmp(subsys, "cciss") == 0) {
@@ -446,20 +499,48 @@ int main(int argc, char **argv)
                } else if (strcmp(subsys, "xen") == 0) {
                        path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
                        parent = skip_subsystem(parent, "xen");
+               } else if (strcmp(subsys, "virtio") == 0) {
+                       path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent));
+                       parent = skip_subsystem(parent, "virtio");
                }
 
                parent = udev_device_get_parent(parent);
        }
 out:
        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);
+               char tag[UTIL_NAME_SIZE];
+               size_t i;
+               const char *p;
+
+               /* compose valid udev tag name */
+               for (p = path, i = 0; *p; p++) {
+                       if ((*p >= '0' && *p <= '9') ||
+                           (*p >= 'A' && *p <= 'Z') ||
+                           (*p >= 'a' && *p <= 'z') ||
+                           *p == '-') {
+                               tag[i++] = *p;
+                               continue;
+                       }
+
+                       /* skip all leading '_' */
+                       if (i == 0)
+                               continue;
+
+                       /* avoid second '_' */
+                       if (tag[i-1] == '_')
+                               continue;
+
+                       tag[i++] = '_';
                }
+               /* strip trailing '_' */
+               while (i > 0 && tag[i-1] == '_')
+                       i--;
+               tag[i] = '\0';
+
+               printf("ID_PATH=%s\n", path);
+               printf("ID_PATH_TAG=%s\n", tag);
                free(path);
-               rc = 0;
+               rc = EXIT_SUCCESS;
        }
 
        udev_device_unref(dev);