udev-builtin-path_id.c revision e98bbfd2074e2b1079b7059341eac25741baf319
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * compose persistent device path
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * Copyright (C) 2009-2011 Kay Sievers <kay@vrfy.org>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * Logic based on Hannes Reinecke's shell script.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * This program is free software: you can redistribute it and/or modify
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering * it under the terms of the GNU General Public License as published by
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering * the Free Software Foundation, either version 2 of the License, or
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * (at your option) any later version.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * This program is distributed in the hope that it will be useful,
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * but WITHOUT ANY WARRANTY; without even the implied warranty of
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering * GNU General Public License for more details.
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering * You should have received a copy of the GNU General Public License
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * along with this program. If not, see <http://www.gnu.org/licenses/>.
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic int path_prepend(char **path, const char *fmt, ...) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering err = asprintf(&new, "%s-%s", pre, *path);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering** Linux only supports 32 bit luns.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic int format_lun_number(struct udev_device *dev, char **path) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering /* address method 0, peripheral device addressing with bus id of zero */
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return path_prepend(path, "lun-%lu", lun);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering /* handle all other lun addressing methods by using a variant of the original lun format */
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering subsystem = udev_device_get_subsystem(parent);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (subsystem == NULL || !streq(subsystem, subsys))
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering struct udev *udev = udev_device_get_udev(parent);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev));
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering port = udev_device_get_sysattr_value(fcdev, "port_name");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering path_prepend(path, "fc-%s-%s", port, lun);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering struct udev *udev = udev_device_get_udev(parent);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering target_parent = udev_device_get_parent(targetdev);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device",
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poettering path_prepend(path, "sas-%s-%s", sas_address, lun);
72f1d5a2880d103dc1c1746f5c02e192e054705eLennart Poetteringstatic struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering struct udev *udev = udev_device_get_udev(parent);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* find iscsi session */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering transportdev = udev_device_get_parent(transportdev);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (startswith(udev_device_get_sysname(transportdev), "session"))
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* find iscsi session device */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering target = udev_device_get_sysattr_value(sessiondev, "targetname");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering addr = udev_device_get_sysattr_value(conndev, "persistent_address");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering port = udev_device_get_sysattr_value(conndev, "persistent_port");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poetteringstatic struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * Rebase host offset to get the local relative number
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * Note: This is by definition racy, unreliable and too simple.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * Please do not copy this model anywhere. It's just a left-over
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * from the time we had no idea how things should look like in
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * Making assumptions about a global in-kernel counter and use
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering * that to calculate a local offset is a very broken concept. It
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * can only work as long as things are in strict order.
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * The kernel needs to export the instance/port number of a
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * controller directly, without the need for rebase magic like
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * this. Manual driver unbind/bind, parallel hotplug/unplug will
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * get into the way of this "I hope it works" logic.
29abad107f8610e73b2fc091216040b579c75453Zbigniew Jędrzejewski-Szmek base = strdup(udev_device_get_syspath(hostdev));
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (dent->d_type != DT_DIR && dent->d_type != DT_LNK)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering i = strtoul(&dent->d_name[4], &rest, 10);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * find the smallest number; the host really needs to export its
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * own instance number per parent device; relying on the global host
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * enumeration and plainly rebasing the numbers sounds unreliable
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poettering path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun);
ee55db41442ad8055f5a84a339b1e0e22bc037c4Lennart Poetteringstatic struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering vmbusdev = udev_device_get_parent(hostdev);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}')
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering path_prepend(path, "vmbus-%s-%s", guid, lun);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic struct udev_device *handle_scsi(struct udev_device *parent, char **path) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering devtype = udev_device_get_devtype(parent);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (devtype == NULL || !streq(devtype, "scsi_device"))
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering /* firewire */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering id = udev_device_get_sysattr_value(parent, "ieee1394_id");
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering /* scsi sysfs does not have a "subsystem" for the transport */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering parent = handle_scsi_fibre_channel(parent, path);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (strstr(name, "/end_device-") != NULL) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering parent = handle_scsi_iscsi(parent, path);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * We do not support the ATA transport class, it uses global counters
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * to name the ata devices which numbers spread across multiple
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * controllers.
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * The real link numbers are not exported. Also, possible chains of ports
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * behind port multipliers cannot be composed that way.
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * Until all that is solved at the kernel level, there are no by-path/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering * links for ATA devices.
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering parent = handle_scsi_hyperv(parent, path);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering parent = handle_scsi_default(parent, path);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering path_prepend(path, "cciss-disk%u", disk);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering parent = skip_subsystem(parent, "cciss");
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic void handle_scsi_tape(struct udev_device *dev, char **path) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* must be the last device in the syspath */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (startswith(name, "nst") && strchr("lma", name[3]) != NULL)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering else if (startswith(name, "st") && strchr("lma", name[2]) != NULL)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic struct udev_device *handle_usb(struct udev_device *parent, char **path) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering devtype = udev_device_get_devtype(parent);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device"))
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poetteringstatic struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering sysname = udev_device_get_sysname(parent);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poetteringstatic struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device");
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id");
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn");
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun");
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering if (hba_id != NULL && lun != NULL && wwpn != NULL) {
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun);
8b38f3cc3eb73adf9536cb73d0f319e60d42ea0cLennart Poettering path_prepend(path, "ccw-%s", udev_device_get_sysname(parent));
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poetteringstatic int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering /* S390 ccw bus */
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering /* walk up the chain of devices and compose path */
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering subsys = udev_device_get_subsystem(parent);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering parent = skip_subsystem(parent, "serio");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering parent = skip_subsystem(parent, "platform");
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * Do return devices with have an unknown type of parent device, they
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * might produce conflicting IDs below multiple independent parent
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * Do not return a have-only a single-parent block devices, some
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * have entire hidden buses behind it, and not create predictable
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * IDs that way.
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering const char *p;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* compose valid udev tag name */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering for (p = path, i = 0; *p; p++) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* skip all leading '_' */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* avoid second '_' */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* strip trailing '_' */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering udev_builtin_add_property(dev, test, "ID_PATH", path);
3ed08c446cfaaae2b234fdfeb0c34ab6b4748c3eLennart Poettering udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringconst struct udev_builtin udev_builtin_path_id = {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering .help = "compose persistent device path",