]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/udev/udev-builtin-path_id.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / udev / udev-builtin-path_id.c
index 073f05aac31c733752cbccc1d3435750d971b93d..2c40988c7aaa7830746812fb3a5273abc2e89d1f 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <string.h>
 #include <ctype.h>
-#include <fcntl.h>
-#include <errno.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
+#include "string-util.h"
 #include "udev.h"
 
 _printf_(2,3)
@@ -77,6 +78,9 @@ static int format_lun_number(struct udev_device *dev, char **path) {
 static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
         struct udev_device *parent = dev;
 
+        assert(dev);
+        assert(subsys);
+
         while (parent != NULL) {
                 const char *subsystem;
 
@@ -96,6 +100,9 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
         const char *port;
         char *lun = NULL;
 
+        assert(parent);
+        assert(path);
+
         targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
         if (targetdev == NULL)
                 return NULL;
@@ -111,14 +118,13 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
 
         format_lun_number(parent, &lun);
         path_prepend(path, "fc-%s-%s", port, lun);
-        if (lun)
-                free(lun);
+        free(lun);
 out:
         udev_device_unref(fcdev);
         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;
@@ -126,6 +132,9 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
         const char *sas_address;
         char *lun = NULL;
 
+        assert(parent);
+        assert(path);
+
         targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
         if (targetdev == NULL)
                 return NULL;
@@ -147,13 +156,108 @@ static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **pa
 
         format_lun_number(parent, &lun);
         path_prepend(path, "sas-%s-%s", sas_address, lun);
-        if (lun)
-                free(lun);
+        free(lun);
 out:
         udev_device_unref(sasdev);
         return parent;
 }
 
+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;
+
+        assert(parent);
+        assert(path);
+
+        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);
+
+        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;
@@ -165,6 +269,9 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
         const char *port;
         char *lun = NULL;
 
+        assert(parent);
+        assert(path);
+
         /* find iscsi session */
         transportdev = parent;
         for (;;) {
@@ -204,14 +311,46 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
 
         format_lun_number(parent, &lun);
         path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
-        if (lun)
-                free(lun);
+        free(lun);
 out:
         udev_device_unref(sessiondev);
         udev_device_unref(conndev);
         return parent;
 }
 
+static struct udev_device *handle_scsi_ata(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 *atadev;
+        const char *port_no;
+
+        assert(parent);
+        assert(path);
+
+        targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
+        if (!targetdev)
+                return NULL;
+
+        target_parent = udev_device_get_parent(targetdev);
+        if (!target_parent)
+                return NULL;
+
+        atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent));
+        if (!atadev)
+                return NULL;
+
+        port_no = udev_device_get_sysattr_value(atadev, "port_no");
+        if (!port_no) {
+               parent = NULL;
+               goto out;
+        }
+        path_prepend(path, "ata-%s", port_no);
+out:
+        udev_device_unref(atadev);
+        return parent;
+}
+
 static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
         struct udev_device *hostdev;
         int host, bus, target, lun;
@@ -222,6 +361,9 @@ static struct udev_device *handle_scsi_default(struct udev_device *parent, char
         struct dirent *dent;
         int basenum;
 
+        assert(parent);
+        assert(path);
+
         hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
         if (hostdev == NULL)
                 return NULL;
@@ -304,6 +446,9 @@ static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char *
         char guid[38];
         size_t i, k;
 
+        assert(parent);
+        assert(path);
+
         hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
         if (!hostdev)
                 return NULL;
@@ -332,7 +477,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;
@@ -346,6 +491,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;
         }
 
@@ -354,32 +500,24 @@ 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;
         }
 
-        /*
-         * We do not support the ATA transport class, it uses global counters
-         * to name the ata devices which numbers spread across multiple
-         * controllers.
-         *
-         * The real link numbers are not exported. Also, possible chains of ports
-         * behind port multipliers cannot be composed that way.
-         *
-         * Until all that is solved at the kernel level, there are no by-path/
-         * links for ATA devices.
-         */
         if (strstr(name, "/ata") != NULL) {
-                parent = NULL;
+                parent = handle_scsi_ata(parent, path);
                 goto out;
         }
 
@@ -457,6 +595,10 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path)
 static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
         struct udev_device *scsi_dev;
 
+        assert(parent);
+        assert(dev);
+        assert(path);
+
         scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
         if (scsi_dev != NULL) {
                 const char *wwpn;
@@ -484,6 +626,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
         bool supported_transport = false;
         bool supported_parent = false;
 
+        assert(dev);
+
         /* S390 ccw bus */
         parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
         if (parent != NULL) {
@@ -502,7 +646,7 @@ 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);
+                        parent = handle_scsi(parent, &path, &supported_parent);
                         supported_transport = true;
                 } else if (streq(subsys, "cciss")) {
                         parent = handle_cciss(parent, &path);
@@ -540,28 +684,25 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool
                         supported_parent = true;
                 }
 
-                parent = udev_device_get_parent(parent);
+                if (parent)
+                        parent = udev_device_get_parent(parent);
         }
 
         /*
-         * Do return devices with have an unknown type of parent device, they
-         * might produce conflicting IDs below multiple independent parent
-         * devices.
+         * 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;
-        }
+        if (!supported_parent)
+                path = mfree(path);
 
         /*
-         * Do not return a have-only a single-parent block devices, some
-         * have entire hidden buses behind it, and not create predictable
-         * IDs that way.
+         * 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 (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) {
-                free(path);
-                path = NULL;
-        }
+        if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport)
+                path = mfree(path);
 
 out:
         if (path != NULL) {
@@ -605,6 +746,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,
 };