chiark / gitweb /
build-sys: add one more Makefile symlink
[elogind.git] / src / udev / udev-builtin-path_id.c
index 0659967c686818970a33d22f8f4eefc25fdbdd85..b6749aab76fc5713826483859db1a3fbd54eccab 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "udev.h"
 
-static int path_prepend(char **path, const char *fmt, ...)
-{
+_printf_(2,3)
+static int path_prepend(char **path, const char *fmt, ...) {
         va_list va;
         char *pre;
         int err = 0;
@@ -64,19 +64,17 @@ out:
 ** 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)
-{
+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);
+                return path_prepend(path, "lun-%lu", 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);
+        return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
 }
 
-static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys)
-{
+static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
         struct udev_device *parent = dev;
 
         while (parent != NULL) {
@@ -91,8 +89,7 @@ static struct udev_device *skip_subsystem(struct udev_device *dev, const char *s
         return dev;
 }
 
-static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
         struct udev *udev  = udev_device_get_udev(parent);
         struct udev_device *targetdev;
         struct udev_device *fcdev = NULL;
@@ -121,8 +118,7 @@ out:
         return parent;
 }
 
-static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) {
         struct udev *udev  = udev_device_get_udev(parent);
         struct udev_device *targetdev;
         struct udev_device *target_parent;
@@ -158,8 +154,101 @@ out:
         return parent;
 }
 
-static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path)
+static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
 {
+        struct udev *udev  = udev_device_get_udev(parent);
+        struct udev_device *targetdev;
+        struct udev_device *target_parent;
+        struct udev_device *port;
+        struct udev_device *expander;
+        struct udev_device *target_sasdev = NULL;
+        struct udev_device *expander_sasdev = NULL;
+        struct udev_device *port_sasdev = NULL;
+        const char *sas_address = NULL;
+        const char *phy_id;
+        const char *phy_count;
+        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;
+
+        /* Get sas device */
+        target_sasdev = udev_device_new_from_subsystem_sysname(udev,
+                          "sas_device", udev_device_get_sysname(target_parent));
+        if (target_sasdev == NULL)
+                return NULL;
+
+        /* The next parent is sas port */
+        port = udev_device_get_parent(target_parent);
+        if (port == NULL) {
+                parent = NULL;
+                goto out;
+        }
+
+        /* Get port device */
+        port_sasdev = udev_device_new_from_subsystem_sysname(udev,
+                          "sas_port", udev_device_get_sysname(port));
+
+        phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys");
+        if (phy_count == NULL) {
+               parent = NULL;
+               goto out;
+        }
+
+        /* Check if we are simple disk */
+        if (strncmp(phy_count, "1", 2) != 0) {
+                 parent = handle_scsi_sas_wide_port(parent, path);
+                 goto out;
+        }
+
+        /* Get connected phy */
+        phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier");
+        if (phy_id == NULL) {
+                parent = NULL;
+                goto out;
+        }
+
+        /* The port's parent is either hba or expander */
+        expander = udev_device_get_parent(port);
+        if (expander == NULL) {
+                parent = NULL;
+                goto out;
+        }
+
+        /* Get expander device */
+        expander_sasdev = udev_device_new_from_subsystem_sysname(udev,
+                          "sas_device", udev_device_get_sysname(expander));
+        if (expander_sasdev != NULL) {
+                 /* Get expander's address */
+                 sas_address = udev_device_get_sysattr_value(expander_sasdev,
+                                                    "sas_address");
+                 if (sas_address == NULL) {
+                        parent = NULL;
+                        goto out;
+                 }
+        }
+
+        format_lun_number(parent, &lun);
+        if (sas_address)
+                 path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun);
+        else
+                 path_prepend(path, "sas-phy%s-%s", phy_id, lun);
+
+        if (lun)
+                free(lun);
+out:
+        udev_device_unref(target_sasdev);
+        udev_device_unref(expander_sasdev);
+        udev_device_unref(port_sasdev);
+        return parent;
+}
+
+static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
         struct udev *udev  = udev_device_get_udev(parent);
         struct udev_device *transportdev;
         struct udev_device *sessiondev = NULL;
@@ -217,8 +306,7 @@ out:
         return parent;
 }
 
-static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
         struct udev_device *hostdev;
         int host, bus, target, lun;
         const char *name;
@@ -338,8 +426,7 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
         return parent;
 }
 
-static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) {
         const char *devtype;
         const char *name;
         const char *id;
@@ -353,6 +440,7 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
         if (id != NULL) {
                 parent = skip_subsystem(parent, "scsi");
                 path_prepend(path, "ieee1394-0x%s", id);
+                *supported_parent = true;
                 goto out;
         }
 
@@ -361,16 +449,19 @@ static struct udev_device *handle_scsi(struct udev_device *parent, char **path)
 
         if (strstr(name, "/rport-") != NULL) {
                 parent = handle_scsi_fibre_channel(parent, path);
+                *supported_parent = true;
                 goto out;
         }
 
         if (strstr(name, "/end_device-") != NULL) {
                 parent = handle_scsi_sas(parent, path);
+                *supported_parent = true;
                 goto out;
         }
 
         if (strstr(name, "/session") != NULL) {
                 parent = handle_scsi_iscsi(parent, path);
+                *supported_parent = true;
                 goto out;
         }
 
@@ -400,8 +491,7 @@ out:
         return parent;
 }
 
-static struct udev_device *handle_cciss(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
         const char *str;
         unsigned int controller, disk;
 
@@ -414,8 +504,7 @@ static struct udev_device *handle_cciss(struct udev_device *parent, char **path)
         return parent;
 }
 
-static void handle_scsi_tape(struct udev_device *dev, char **path)
-{
+static void handle_scsi_tape(struct udev_device *dev, char **path) {
         const char *name;
 
         /* must be the last device in the syspath */
@@ -429,8 +518,7 @@ static void handle_scsi_tape(struct udev_device *dev, char **path)
                 path_prepend(path, "st%c", name[2]);
 }
 
-static struct udev_device *handle_usb(struct udev_device *parent, char **path)
-{
+static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
         const char *devtype;
         const char *str;
         const char *port;
@@ -452,8 +540,19 @@ static struct udev_device *handle_usb(struct udev_device *parent, char **path)
         return parent;
 }
 
-static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path)
-{
+static struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
+        const char *sysname;
+        unsigned int core;
+
+        sysname = udev_device_get_sysname(parent);
+        if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
+                return NULL;
+
+        path_prepend(path, "bcma-%u", core);
+        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");
@@ -477,11 +576,11 @@ out:
         return parent;
 }
 
-static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test)
-{
+static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
         struct udev_device *parent;
         char *path = NULL;
-        bool some_transport = false;
+        bool supported_transport = false;
+        bool supported_parent = false;
 
         /* S390 ccw bus */
         parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
@@ -501,48 +600,63 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
                 } else if (streq(subsys, "scsi_tape")) {
                         handle_scsi_tape(parent, &path);
                 } else if (streq(subsys, "scsi")) {
-                        parent = handle_scsi(parent, &path);
-                        some_transport = true;
+                        parent = handle_scsi(parent, &path, &supported_parent);
+                        supported_transport = true;
                 } else if (streq(subsys, "cciss")) {
                         parent = handle_cciss(parent, &path);
-                        some_transport = true;
+                        supported_transport = true;
                 } else if (streq(subsys, "usb")) {
                         parent = handle_usb(parent, &path);
-                        some_transport = true;
+                        supported_transport = true;
+                } else if (streq(subsys, "bcma")) {
+                        parent = handle_bcma(parent, &path);
+                        supported_transport = true;
                 } else if (streq(subsys, "serio")) {
                         path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
                         parent = skip_subsystem(parent, "serio");
                 } else if (streq(subsys, "pci")) {
                         path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
                         parent = skip_subsystem(parent, "pci");
+                        supported_parent = true;
                 } else if (streq(subsys, "platform")) {
                         path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
                         parent = skip_subsystem(parent, "platform");
-                        some_transport = true;
+                        supported_transport = true;
+                        supported_parent = true;
                 } else if (streq(subsys, "acpi")) {
                         path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
                         parent = skip_subsystem(parent, "acpi");
+                        supported_parent = true;
                 } else if (streq(subsys, "xen")) {
                         path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
                         parent = skip_subsystem(parent, "xen");
-                } else if (streq(subsys, "virtio")) {
-                        path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent));
-                        parent = skip_subsystem(parent, "virtio");
+                        supported_parent = true;
                 } else if (streq(subsys, "scm")) {
                         path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
                         parent = skip_subsystem(parent, "scm");
-                        some_transport = true;
+                        supported_transport = true;
+                        supported_parent = true;
                 }
 
                 parent = udev_device_get_parent(parent);
         }
 
         /*
-         * Do not return a single-parent-device-only for block
-         * devices, they might have entire buses behind it which
-         * do not get unique IDs only by using the parent device.
+         * Do not return devices with an unknown parent device type. They
+         * might produce conflicting IDs if the parent does not provide a
+         * unique and predictable name.
+         */
+        if (!supported_parent) {
+                free(path);
+                path = NULL;
+        }
+
+        /*
+         * Do not return block devices without a well-known transport. Some
+         * devices do not expose their buses and do not provide a unique
+         * and predictable name that way.
          */
-        if (!some_transport && streq(udev_device_get_subsystem(dev), "block")) {
+        if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) {
                 free(path);
                 path = NULL;
         }
@@ -589,6 +703,6 @@ out:
 const struct udev_builtin udev_builtin_path_id = {
         .name = "path_id",
         .cmd = builtin_path_id,
-        .help = "compose persistent device path",
+        .help = "Compose persistent device path",
         .run_once = true,
 };