2 * compose persistent device path
4 * Copyright (C) 2009-2011 Kay Sievers <kay@vrfy.org>
6 * Logic based on Hannes Reinecke's shell script.
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 #include "alloc-util.h"
34 #include "string-util.h"
38 static int path_prepend(char **path
, const char *fmt
, ...) {
44 err
= vasprintf(&pre
, fmt
, va
);
52 err
= asprintf(&new, "%s-%s", pre
, *path
);
66 ** Linux only supports 32 bit luns.
67 ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
69 static int format_lun_number(struct udev_device
*dev
, char **path
) {
70 unsigned long lun
= strtoul(udev_device_get_sysnum(dev
), NULL
, 10);
72 /* address method 0, peripheral device addressing with bus id of zero */
74 return path_prepend(path
, "lun-%lu", lun
);
75 /* handle all other lun addressing methods by using a variant of the original lun format */
76 return path_prepend(path
, "lun-0x%04lx%04lx00000000", lun
& 0xffff, (lun
>> 16) & 0xffff);
79 static struct udev_device
*skip_subsystem(struct udev_device
*dev
, const char *subsys
) {
80 struct udev_device
*parent
= dev
;
85 while (parent
!= NULL
) {
86 const char *subsystem
;
88 subsystem
= udev_device_get_subsystem(parent
);
89 if (subsystem
== NULL
|| !streq(subsystem
, subsys
))
92 parent
= udev_device_get_parent(parent
);
97 static struct udev_device
*handle_scsi_fibre_channel(struct udev_device
*parent
, char **path
) {
98 struct udev
*udev
= udev_device_get_udev(parent
);
99 struct udev_device
*targetdev
;
100 struct udev_device
*fcdev
= NULL
;
107 targetdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target");
108 if (targetdev
== NULL
)
111 fcdev
= udev_device_new_from_subsystem_sysname(udev
, "fc_transport", udev_device_get_sysname(targetdev
));
114 port
= udev_device_get_sysattr_value(fcdev
, "port_name");
120 format_lun_number(parent
, &lun
);
121 path_prepend(path
, "fc-%s-%s", port
, lun
);
124 udev_device_unref(fcdev
);
128 static struct udev_device
*handle_scsi_sas_wide_port(struct udev_device
*parent
, char **path
) {
129 struct udev
*udev
= udev_device_get_udev(parent
);
130 struct udev_device
*targetdev
;
131 struct udev_device
*target_parent
;
132 struct udev_device
*sasdev
;
133 const char *sas_address
;
139 targetdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target");
140 if (targetdev
== NULL
)
143 target_parent
= udev_device_get_parent(targetdev
);
144 if (target_parent
== NULL
)
147 sasdev
= udev_device_new_from_subsystem_sysname(udev
, "sas_device",
148 udev_device_get_sysname(target_parent
));
152 sas_address
= udev_device_get_sysattr_value(sasdev
, "sas_address");
153 if (sas_address
== NULL
) {
158 format_lun_number(parent
, &lun
);
159 path_prepend(path
, "sas-%s-%s", sas_address
, lun
);
162 udev_device_unref(sasdev
);
166 static struct udev_device
*handle_scsi_sas(struct udev_device
*parent
, char **path
)
168 struct udev
*udev
= udev_device_get_udev(parent
);
169 struct udev_device
*targetdev
;
170 struct udev_device
*target_parent
;
171 struct udev_device
*port
;
172 struct udev_device
*expander
;
173 struct udev_device
*target_sasdev
= NULL
;
174 struct udev_device
*expander_sasdev
= NULL
;
175 struct udev_device
*port_sasdev
= NULL
;
176 const char *sas_address
= NULL
;
178 const char *phy_count
;
184 targetdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_target");
185 if (targetdev
== NULL
)
188 target_parent
= udev_device_get_parent(targetdev
);
189 if (target_parent
== NULL
)
193 target_sasdev
= udev_device_new_from_subsystem_sysname(udev
,
194 "sas_device", udev_device_get_sysname(target_parent
));
195 if (target_sasdev
== NULL
)
198 /* The next parent is sas port */
199 port
= udev_device_get_parent(target_parent
);
205 /* Get port device */
206 port_sasdev
= udev_device_new_from_subsystem_sysname(udev
,
207 "sas_port", udev_device_get_sysname(port
));
209 phy_count
= udev_device_get_sysattr_value(port_sasdev
, "num_phys");
210 if (phy_count
== NULL
) {
215 /* Check if we are simple disk */
216 if (strncmp(phy_count
, "1", 2) != 0) {
217 parent
= handle_scsi_sas_wide_port(parent
, path
);
221 /* Get connected phy */
222 phy_id
= udev_device_get_sysattr_value(target_sasdev
, "phy_identifier");
223 if (phy_id
== NULL
) {
228 /* The port's parent is either hba or expander */
229 expander
= udev_device_get_parent(port
);
230 if (expander
== NULL
) {
235 /* Get expander device */
236 expander_sasdev
= udev_device_new_from_subsystem_sysname(udev
,
237 "sas_device", udev_device_get_sysname(expander
));
238 if (expander_sasdev
!= NULL
) {
239 /* Get expander's address */
240 sas_address
= udev_device_get_sysattr_value(expander_sasdev
,
242 if (sas_address
== NULL
) {
248 format_lun_number(parent
, &lun
);
250 path_prepend(path
, "sas-exp%s-phy%s-%s", sas_address
, phy_id
, lun
);
252 path_prepend(path
, "sas-phy%s-%s", phy_id
, lun
);
256 udev_device_unref(target_sasdev
);
257 udev_device_unref(expander_sasdev
);
258 udev_device_unref(port_sasdev
);
262 static struct udev_device
*handle_scsi_iscsi(struct udev_device
*parent
, char **path
) {
263 struct udev
*udev
= udev_device_get_udev(parent
);
264 struct udev_device
*transportdev
;
265 struct udev_device
*sessiondev
= NULL
;
268 struct udev_device
*conndev
= NULL
;
276 /* find iscsi session */
277 transportdev
= parent
;
279 transportdev
= udev_device_get_parent(transportdev
);
280 if (transportdev
== NULL
)
282 if (startswith(udev_device_get_sysname(transportdev
), "session"))
286 /* find iscsi session device */
287 sessiondev
= udev_device_new_from_subsystem_sysname(udev
, "iscsi_session", udev_device_get_sysname(transportdev
));
288 if (sessiondev
== NULL
)
290 target
= udev_device_get_sysattr_value(sessiondev
, "targetname");
291 if (target
== NULL
) {
296 if (asprintf(&connname
, "connection%s:0", udev_device_get_sysnum(transportdev
)) < 0) {
300 conndev
= udev_device_new_from_subsystem_sysname(udev
, "iscsi_connection", connname
);
302 if (conndev
== NULL
) {
306 addr
= udev_device_get_sysattr_value(conndev
, "persistent_address");
307 port
= udev_device_get_sysattr_value(conndev
, "persistent_port");
308 if (addr
== NULL
|| port
== NULL
) {
313 format_lun_number(parent
, &lun
);
314 path_prepend(path
, "ip-%s:%s-iscsi-%s-%s", addr
, port
, target
, lun
);
317 udev_device_unref(sessiondev
);
318 udev_device_unref(conndev
);
322 static struct udev_device
*handle_scsi_ata(struct udev_device
*parent
, char **path
) {
323 struct udev
*udev
= udev_device_get_udev(parent
);
324 struct udev_device
*targetdev
;
325 struct udev_device
*target_parent
;
326 struct udev_device
*atadev
;
332 targetdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host");
336 target_parent
= udev_device_get_parent(targetdev
);
340 atadev
= udev_device_new_from_subsystem_sysname(udev
, "ata_port", udev_device_get_sysname(target_parent
));
344 port_no
= udev_device_get_sysattr_value(atadev
, "port_no");
349 path_prepend(path
, "ata-%s", port_no
);
351 udev_device_unref(atadev
);
355 static struct udev_device
*handle_scsi_default(struct udev_device
*parent
, char **path
) {
356 struct udev_device
*hostdev
;
357 int host
, bus
, target
, lun
;
368 hostdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host");
372 name
= udev_device_get_sysname(parent
);
373 if (sscanf(name
, "%d:%d:%d:%d", &host
, &bus
, &target
, &lun
) != 4)
377 * Rebase host offset to get the local relative number
379 * Note: This is by definition racy, unreliable and too simple.
380 * Please do not copy this model anywhere. It's just a left-over
381 * from the time we had no idea how things should look like in
384 * Making assumptions about a global in-kernel counter and use
385 * that to calculate a local offset is a very broken concept. It
386 * can only work as long as things are in strict order.
388 * The kernel needs to export the instance/port number of a
389 * controller directly, without the need for rebase magic like
390 * this. Manual driver unbind/bind, parallel hotplug/unplug will
391 * get into the way of this "I hope it works" logic.
394 base
= strdup(udev_device_get_syspath(hostdev
));
397 pos
= strrchr(base
, '/');
408 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
412 if (dent
->d_name
[0] == '.')
414 if (dent
->d_type
!= DT_DIR
&& dent
->d_type
!= DT_LNK
)
416 if (!startswith(dent
->d_name
, "host"))
418 i
= strtoul(&dent
->d_name
[4], &rest
, 10);
422 * find the smallest number; the host really needs to export its
423 * own instance number per parent device; relying on the global host
424 * enumeration and plainly rebasing the numbers sounds unreliable
426 if (basenum
== -1 || i
< basenum
)
436 path_prepend(path
, "scsi-%u:%u:%u:%u", host
, bus
, target
, lun
);
442 static struct udev_device
*handle_scsi_hyperv(struct udev_device
*parent
, char **path
) {
443 struct udev_device
*hostdev
;
444 struct udev_device
*vmbusdev
;
445 const char *guid_str
;
453 hostdev
= udev_device_get_parent_with_subsystem_devtype(parent
, "scsi", "scsi_host");
457 vmbusdev
= udev_device_get_parent(hostdev
);
461 guid_str
= udev_device_get_sysattr_value(vmbusdev
, "device_id");
465 if (strlen(guid_str
) < 37 || guid_str
[0] != '{' || guid_str
[36] != '}')
468 for (i
= 1, k
= 0; i
< 36; i
++) {
469 if (guid_str
[i
] == '-')
471 guid
[k
++] = guid_str
[i
];
475 format_lun_number(parent
, &lun
);
476 path_prepend(path
, "vmbus-%s-%s", guid
, lun
);
481 static struct udev_device
*handle_scsi(struct udev_device
*parent
, char **path
, bool *supported_parent
) {
486 devtype
= udev_device_get_devtype(parent
);
487 if (devtype
== NULL
|| !streq(devtype
, "scsi_device"))
491 id
= udev_device_get_sysattr_value(parent
, "ieee1394_id");
493 parent
= skip_subsystem(parent
, "scsi");
494 path_prepend(path
, "ieee1394-0x%s", id
);
495 *supported_parent
= true;
499 /* scsi sysfs does not have a "subsystem" for the transport */
500 name
= udev_device_get_syspath(parent
);
502 if (strstr(name
, "/rport-") != NULL
) {
503 parent
= handle_scsi_fibre_channel(parent
, path
);
504 *supported_parent
= true;
508 if (strstr(name
, "/end_device-") != NULL
) {
509 parent
= handle_scsi_sas(parent
, path
);
510 *supported_parent
= true;
514 if (strstr(name
, "/session") != NULL
) {
515 parent
= handle_scsi_iscsi(parent
, path
);
516 *supported_parent
= true;
520 if (strstr(name
, "/ata") != NULL
) {
521 parent
= handle_scsi_ata(parent
, path
);
525 if (strstr(name
, "/vmbus_") != NULL
) {
526 parent
= handle_scsi_hyperv(parent
, path
);
530 parent
= handle_scsi_default(parent
, path
);
535 static struct udev_device
*handle_cciss(struct udev_device
*parent
, char **path
) {
537 unsigned int controller
, disk
;
539 str
= udev_device_get_sysname(parent
);
540 if (sscanf(str
, "c%ud%u%*s", &controller
, &disk
) != 2)
543 path_prepend(path
, "cciss-disk%u", disk
);
544 parent
= skip_subsystem(parent
, "cciss");
548 static void handle_scsi_tape(struct udev_device
*dev
, char **path
) {
551 /* must be the last device in the syspath */
555 name
= udev_device_get_sysname(dev
);
556 if (startswith(name
, "nst") && strchr("lma", name
[3]) != NULL
)
557 path_prepend(path
, "nst%c", name
[3]);
558 else if (startswith(name
, "st") && strchr("lma", name
[2]) != NULL
)
559 path_prepend(path
, "st%c", name
[2]);
562 static struct udev_device
*handle_usb(struct udev_device
*parent
, char **path
) {
567 devtype
= udev_device_get_devtype(parent
);
570 if (!streq(devtype
, "usb_interface") && !streq(devtype
, "usb_device"))
573 str
= udev_device_get_sysname(parent
);
574 port
= strchr(str
, '-');
579 parent
= skip_subsystem(parent
, "usb");
580 path_prepend(path
, "usb-0:%s", port
);
584 static struct udev_device
*handle_bcma(struct udev_device
*parent
, char **path
) {
588 sysname
= udev_device_get_sysname(parent
);
589 if (sscanf(sysname
, "bcma%*u:%u", &core
) != 1)
592 path_prepend(path
, "bcma-%u", core
);
596 /* Handle devices of AP bus in System z platform. */
597 static struct udev_device
*handle_ap(struct udev_device
*parent
, char **path
) {
598 const char *type
, *func
;
603 type
= udev_device_get_sysattr_value(parent
, "type");
604 func
= udev_device_get_sysattr_value(parent
, "ap_functions");
606 if (type
!= NULL
&& func
!= NULL
) {
607 path_prepend(path
, "ap-%s-%s", type
, func
);
610 path_prepend(path
, "ap-%s", udev_device_get_sysname(parent
));
612 parent
= skip_subsystem(parent
, "ap");
616 static int builtin_path_id(struct udev_device
*dev
, int argc
, char *argv
[], bool test
) {
617 struct udev_device
*parent
;
619 bool supported_transport
= false;
620 bool supported_parent
= false;
624 /* walk up the chain of devices and compose path */
626 while (parent
!= NULL
) {
629 subsys
= udev_device_get_subsystem(parent
);
630 if (subsys
== NULL
) {
632 } else if (streq(subsys
, "scsi_tape")) {
633 handle_scsi_tape(parent
, &path
);
634 } else if (streq(subsys
, "scsi")) {
635 parent
= handle_scsi(parent
, &path
, &supported_parent
);
636 supported_transport
= true;
637 } else if (streq(subsys
, "cciss")) {
638 parent
= handle_cciss(parent
, &path
);
639 supported_transport
= true;
640 } else if (streq(subsys
, "usb")) {
641 parent
= handle_usb(parent
, &path
);
642 supported_transport
= true;
643 } else if (streq(subsys
, "bcma")) {
644 parent
= handle_bcma(parent
, &path
);
645 supported_transport
= true;
646 } else if (streq(subsys
, "serio")) {
647 path_prepend(&path
, "serio-%s", udev_device_get_sysnum(parent
));
648 parent
= skip_subsystem(parent
, "serio");
649 } else if (streq(subsys
, "pci")) {
650 path_prepend(&path
, "pci-%s", udev_device_get_sysname(parent
));
651 parent
= skip_subsystem(parent
, "pci");
652 supported_parent
= true;
653 } else if (streq(subsys
, "platform")) {
654 path_prepend(&path
, "platform-%s", udev_device_get_sysname(parent
));
655 parent
= skip_subsystem(parent
, "platform");
656 supported_transport
= true;
657 supported_parent
= true;
658 } else if (streq(subsys
, "acpi")) {
659 path_prepend(&path
, "acpi-%s", udev_device_get_sysname(parent
));
660 parent
= skip_subsystem(parent
, "acpi");
661 supported_parent
= true;
662 } else if (streq(subsys
, "xen")) {
663 path_prepend(&path
, "xen-%s", udev_device_get_sysname(parent
));
664 parent
= skip_subsystem(parent
, "xen");
665 supported_parent
= true;
666 } else if (streq(subsys
, "virtio")) {
667 while (parent
&& streq_ptr("virtio", udev_device_get_subsystem(parent
)))
668 parent
= udev_device_get_parent(parent
);
669 path_prepend(&path
, "virtio-pci-%s", udev_device_get_sysname(parent
));
670 supported_transport
= true;
671 supported_parent
= true;
672 } else if (streq(subsys
, "scm")) {
673 path_prepend(&path
, "scm-%s", udev_device_get_sysname(parent
));
674 parent
= skip_subsystem(parent
, "scm");
675 supported_transport
= true;
676 supported_parent
= true;
677 } else if (streq(subsys
, "ccw")) {
678 path_prepend(&path
, "ccw-%s", udev_device_get_sysname(parent
));
679 parent
= skip_subsystem(parent
, "ccw");
680 supported_transport
= true;
681 supported_parent
= true;
682 } else if (streq(subsys
, "ccwgroup")) {
683 path_prepend(&path
, "ccwgroup-%s", udev_device_get_sysname(parent
));
684 parent
= skip_subsystem(parent
, "ccwgroup");
685 supported_transport
= true;
686 supported_parent
= true;
687 } else if (streq(subsys
, "ap")) {
688 parent
= handle_ap(parent
, &path
);
689 supported_transport
= true;
690 supported_parent
= true;
691 } else if (streq(subsys
, "iucv")) {
692 path_prepend(&path
, "iucv-%s", udev_device_get_sysname(parent
));
693 parent
= skip_subsystem(parent
, "iucv");
694 supported_transport
= true;
695 supported_parent
= true;
699 parent
= udev_device_get_parent(parent
);
703 * Do not return devices with an unknown parent device type. They
704 * might produce conflicting IDs if the parent does not provide a
705 * unique and predictable name.
707 if (!supported_parent
)
711 * Do not return block devices without a well-known transport. Some
712 * devices do not expose their buses and do not provide a unique
713 * and predictable name that way.
715 if (streq(udev_device_get_subsystem(dev
), "block") && !supported_transport
)
719 char tag
[UTIL_NAME_SIZE
];
723 /* compose valid udev tag name */
724 for (p
= path
, i
= 0; *p
; p
++) {
725 if ((*p
>= '0' && *p
<= '9') ||
726 (*p
>= 'A' && *p
<= 'Z') ||
727 (*p
>= 'a' && *p
<= 'z') ||
733 /* skip all leading '_' */
737 /* avoid second '_' */
743 /* strip trailing '_' */
744 while (i
> 0 && tag
[i
-1] == '_')
748 udev_builtin_add_property(dev
, test
, "ID_PATH", path
);
749 udev_builtin_add_property(dev
, test
, "ID_PATH_TAG", tag
);
756 const struct udev_builtin udev_builtin_path_id
= {
758 .cmd
= builtin_path_id
,
759 .help
= "Compose persistent device path",