]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/udev-builtin-path_id.c
Merge pull request #9560 from mbiebl/uaccess-dev-kvm
[thirdparty/systemd.git] / src / udev / udev-builtin-path_id.c
1 /* SPDX-License-Identifier: GPL-2.0+ */
2 /*
3 * compose persistent device path
4 *
5 * Logic based on Hannes Reinecke's shell script.
6 */
7
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #include "alloc-util.h"
19 #include "dirent-util.h"
20 #include "fd-util.h"
21 #include "string-util.h"
22 #include "sysexits.h"
23 #include "udev.h"
24 #include "udev-util.h"
25
26 _printf_(2,3)
27 static void path_prepend(char **path, const char *fmt, ...) {
28 va_list va;
29 _cleanup_free_ char *pre = NULL;
30 int r;
31
32 va_start(va, fmt);
33 r = vasprintf(&pre, fmt, va);
34 va_end(va);
35 if (r < 0) {
36 log_oom();
37 exit(EX_OSERR);
38 }
39
40 if (*path) {
41 char *new;
42
43 new = strjoin(pre, "-", *path);
44 if (!new) {
45 log_oom();
46 exit(EX_OSERR);
47 }
48
49 free_and_replace(*path, new);
50 } else
51 *path = TAKE_PTR(pre);
52 }
53
54 /*
55 ** Linux only supports 32 bit luns.
56 ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
57 */
58 static void format_lun_number(struct udev_device *dev, char **path) {
59 unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
60
61 if (lun < 256)
62 /* address method 0, peripheral device addressing with bus id of zero */
63 path_prepend(path, "lun-%lu", lun);
64 else
65 /* handle all other lun addressing methods by using a variant of the original lun format */
66 path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
67 }
68
69 static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
70 struct udev_device *parent = dev;
71
72 assert(dev);
73 assert(subsys);
74
75 while (parent) {
76 const char *subsystem;
77
78 subsystem = udev_device_get_subsystem(parent);
79 if (!streq_ptr(subsystem, subsys))
80 break;
81
82 dev = parent;
83 parent = udev_device_get_parent(parent);
84 }
85
86 return dev;
87 }
88
89 static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
90 struct udev *udev;
91 struct udev_device *targetdev;
92 _cleanup_(udev_device_unrefp) struct udev_device *fcdev = NULL;
93 const char *port;
94 _cleanup_free_ char *lun = NULL;
95
96 assert(parent);
97 assert(path);
98
99 udev = udev_device_get_udev(parent);
100
101 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
102 if (!targetdev)
103 return NULL;
104
105 fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev));
106 if (!fcdev)
107 return NULL;
108
109 port = udev_device_get_sysattr_value(fcdev, "port_name");
110 if (!port)
111 return NULL;
112
113 format_lun_number(parent, &lun);
114 path_prepend(path, "fc-%s-%s", port, lun);
115 return parent;
116 }
117
118 static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) {
119 struct udev *udev;
120 struct udev_device *targetdev, *target_parent;
121 _cleanup_(udev_device_unrefp) struct udev_device *sasdev = NULL;
122 const char *sas_address;
123 _cleanup_free_ char *lun = NULL;
124
125 assert(parent);
126 assert(path);
127
128 udev = udev_device_get_udev(parent);
129
130 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
131 if (!targetdev)
132 return NULL;
133
134 target_parent = udev_device_get_parent(targetdev);
135 if (!target_parent)
136 return NULL;
137
138 sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device",
139 udev_device_get_sysname(target_parent));
140 if (!sasdev)
141 return NULL;
142
143 sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
144 if (!sas_address)
145 return NULL;
146
147 format_lun_number(parent, &lun);
148 path_prepend(path, "sas-%s-%s", sas_address, lun);
149 return parent;
150 }
151
152 static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
153 {
154 struct udev *udev;
155 struct udev_device *targetdev, *target_parent, *port, *expander;
156 _cleanup_(udev_device_unrefp) struct udev_device
157 *target_sasdev = NULL, *expander_sasdev = NULL, *port_sasdev = NULL;
158 const char *sas_address = NULL;
159 const char *phy_id;
160 const char *phy_count;
161 _cleanup_free_ char *lun = NULL;
162
163 assert(parent);
164 assert(path);
165
166 udev = udev_device_get_udev(parent);
167
168 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
169 if (!targetdev)
170 return NULL;
171
172 target_parent = udev_device_get_parent(targetdev);
173 if (!target_parent)
174 return NULL;
175
176 /* Get sas device */
177 target_sasdev = udev_device_new_from_subsystem_sysname(
178 udev, "sas_device", udev_device_get_sysname(target_parent));
179 if (!target_sasdev)
180 return NULL;
181
182 /* The next parent is sas port */
183 port = udev_device_get_parent(target_parent);
184 if (!port)
185 return NULL;
186
187 /* Get port device */
188 port_sasdev = udev_device_new_from_subsystem_sysname(
189 udev, "sas_port", udev_device_get_sysname(port));
190
191 phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys");
192 if (!phy_count)
193 return NULL;
194
195 /* Check if we are simple disk */
196 if (strncmp(phy_count, "1", 2) != 0)
197 return handle_scsi_sas_wide_port(parent, path);
198
199 /* Get connected phy */
200 phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier");
201 if (!phy_id)
202 return NULL;
203
204 /* The port's parent is either hba or expander */
205 expander = udev_device_get_parent(port);
206 if (!expander)
207 return NULL;
208
209 /* Get expander device */
210 expander_sasdev = udev_device_new_from_subsystem_sysname(
211 udev, "sas_device", udev_device_get_sysname(expander));
212 if (expander_sasdev) {
213 /* Get expander's address */
214 sas_address = udev_device_get_sysattr_value(expander_sasdev,
215 "sas_address");
216 if (!sas_address)
217 return NULL;
218 }
219
220 format_lun_number(parent, &lun);
221 if (sas_address)
222 path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun);
223 else
224 path_prepend(path, "sas-phy%s-%s", phy_id, lun);
225
226 return parent;
227 }
228
229 static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
230 struct udev *udev;
231 struct udev_device *transportdev;
232 _cleanup_(udev_device_unrefp) struct udev_device
233 *sessiondev = NULL, *conndev = NULL;
234 const char *target, *connname, *addr, *port;
235 _cleanup_free_ char *lun = NULL;
236
237 assert(parent);
238 assert(path);
239
240 udev = udev_device_get_udev(parent);
241
242 /* find iscsi session */
243 transportdev = parent;
244 for (;;) {
245 transportdev = udev_device_get_parent(transportdev);
246 if (!transportdev)
247 return NULL;
248 if (startswith(udev_device_get_sysname(transportdev), "session"))
249 break;
250 }
251
252 /* find iscsi session device */
253 sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
254 if (!sessiondev)
255 return NULL;
256
257 target = udev_device_get_sysattr_value(sessiondev, "targetname");
258 if (!target)
259 return NULL;
260
261 connname = strjoina("connection", udev_device_get_sysnum(transportdev), ":0");
262 conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname);
263 if (!conndev)
264 return NULL;
265
266 addr = udev_device_get_sysattr_value(conndev, "persistent_address");
267 port = udev_device_get_sysattr_value(conndev, "persistent_port");
268 if (!addr || !port)
269 return NULL;
270
271 format_lun_number(parent, &lun);
272 path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
273 return parent;
274 }
275
276 static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) {
277 struct udev *udev;
278 struct udev_device *targetdev, *target_parent;
279 _cleanup_(udev_device_unrefp) struct udev_device *atadev = NULL;
280 const char *port_no;
281
282 assert(parent);
283 assert(path);
284
285 udev = udev_device_get_udev(parent);
286
287 targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
288 if (!targetdev)
289 return NULL;
290
291 target_parent = udev_device_get_parent(targetdev);
292 if (!target_parent)
293 return NULL;
294
295 atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent));
296 if (!atadev)
297 return NULL;
298
299 port_no = udev_device_get_sysattr_value(atadev, "port_no");
300 if (!port_no)
301 return NULL;
302
303 path_prepend(path, "ata-%s", port_no);
304 return parent;
305 }
306
307 static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
308 struct udev_device *hostdev;
309 int host, bus, target, lun;
310 const char *name, *base, *pos;
311 _cleanup_closedir_ DIR *dir = NULL;
312 struct dirent *dent;
313 int basenum = -1;
314
315 assert(parent);
316 assert(path);
317
318 hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
319 if (!hostdev)
320 return NULL;
321
322 name = udev_device_get_sysname(parent);
323 if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4)
324 return NULL;
325
326 /*
327 * Rebase host offset to get the local relative number
328 *
329 * Note: This is by definition racy, unreliable and too simple.
330 * Please do not copy this model anywhere. It's just a left-over
331 * from the time we had no idea how things should look like in
332 * the end.
333 *
334 * Making assumptions about a global in-kernel counter and use
335 * that to calculate a local offset is a very broken concept. It
336 * can only work as long as things are in strict order.
337 *
338 * The kernel needs to export the instance/port number of a
339 * controller directly, without the need for rebase magic like
340 * this. Manual driver unbind/bind, parallel hotplug/unplug will
341 * get into the way of this "I hope it works" logic.
342 */
343
344 base = udev_device_get_syspath(hostdev);
345 pos = strrchr(base, '/');
346 if (!pos)
347 return NULL;
348
349 base = strndupa(base, pos - base);
350 dir = opendir(base);
351 if (!dir)
352 return NULL;
353
354 FOREACH_DIRENT_ALL(dent, dir, break) {
355 char *rest;
356 int i;
357
358 if (dent->d_name[0] == '.')
359 continue;
360 if (!IN_SET(dent->d_type, DT_DIR, DT_LNK))
361 continue;
362 if (!startswith(dent->d_name, "host"))
363 continue;
364 i = strtoul(&dent->d_name[4], &rest, 10);
365 if (rest[0] != '\0')
366 continue;
367 /*
368 * find the smallest number; the host really needs to export its
369 * own instance number per parent device; relying on the global host
370 * enumeration and plainly rebasing the numbers sounds unreliable
371 */
372 if (basenum == -1 || i < basenum)
373 basenum = i;
374 }
375 if (basenum == -1)
376 return hostdev;
377 host -= basenum;
378
379 path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun);
380 return hostdev;
381 }
382
383 static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path, size_t guid_str_len) {
384 struct udev_device *hostdev;
385 struct udev_device *vmbusdev;
386 const char *guid_str;
387 _cleanup_free_ char *lun = NULL;
388 char guid[39];
389 size_t i, k;
390
391 assert(parent);
392 assert(path);
393 assert(guid_str_len < sizeof(guid));
394
395 hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host");
396 if (!hostdev)
397 return NULL;
398
399 vmbusdev = udev_device_get_parent(hostdev);
400 if (!vmbusdev)
401 return NULL;
402
403 guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id");
404 if (!guid_str)
405 return NULL;
406
407 if (strlen(guid_str) < guid_str_len || guid_str[0] != '{' || guid_str[guid_str_len-1] != '}')
408 return NULL;
409
410 for (i = 1, k = 0; i < guid_str_len-1; i++) {
411 if (guid_str[i] == '-')
412 continue;
413 guid[k++] = guid_str[i];
414 }
415 guid[k] = '\0';
416
417 format_lun_number(parent, &lun);
418 path_prepend(path, "vmbus-%s-%s", guid, lun);
419 return parent;
420 }
421
422 static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) {
423 const char *devtype, *id, *name;
424
425 devtype = udev_device_get_devtype(parent);
426 if (!streq_ptr(devtype, "scsi_device"))
427 return parent;
428
429 /* firewire */
430 id = udev_device_get_sysattr_value(parent, "ieee1394_id");
431 if (id) {
432 path_prepend(path, "ieee1394-0x%s", id);
433 *supported_parent = true;
434 return skip_subsystem(parent, "scsi");
435 }
436
437 /* scsi sysfs does not have a "subsystem" for the transport */
438 name = udev_device_get_syspath(parent);
439
440 if (strstr(name, "/rport-")) {
441 *supported_parent = true;
442 return handle_scsi_fibre_channel(parent, path);
443 }
444
445 if (strstr(name, "/end_device-")) {
446 *supported_parent = true;
447 return handle_scsi_sas(parent, path);
448 }
449
450 if (strstr(name, "/session")) {
451 *supported_parent = true;
452 return handle_scsi_iscsi(parent, path);
453 }
454
455 if (strstr(name, "/ata"))
456 return handle_scsi_ata(parent, path);
457
458 if (strstr(name, "/vmbus_"))
459 return handle_scsi_hyperv(parent, path, 37);
460 else if (strstr(name, "/VMBUS"))
461 return handle_scsi_hyperv(parent, path, 38);
462
463 return handle_scsi_default(parent, path);
464 }
465
466 static struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
467 const char *str;
468 unsigned int controller, disk;
469
470 str = udev_device_get_sysname(parent);
471 if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2)
472 return NULL;
473
474 path_prepend(path, "cciss-disk%u", disk);
475 return skip_subsystem(parent, "cciss");
476 }
477
478 static void handle_scsi_tape(struct udev_device *dev, char **path) {
479 const char *name;
480
481 /* must be the last device in the syspath */
482 if (*path)
483 return;
484
485 name = udev_device_get_sysname(dev);
486 if (startswith(name, "nst") && strchr("lma", name[3]))
487 path_prepend(path, "nst%c", name[3]);
488 else if (startswith(name, "st") && strchr("lma", name[2]))
489 path_prepend(path, "st%c", name[2]);
490 }
491
492 static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
493 const char *devtype, *str, *port;
494
495 devtype = udev_device_get_devtype(parent);
496 if (!devtype)
497 return parent;
498 if (!STR_IN_SET(devtype, "usb_interface", "usb_device"))
499 return parent;
500
501 str = udev_device_get_sysname(parent);
502 port = strchr(str, '-');
503 if (!port)
504 return parent;
505 port++;
506
507 path_prepend(path, "usb-0:%s", port);
508 return skip_subsystem(parent, "usb");
509 }
510
511 static struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
512 const char *sysname;
513 unsigned int core;
514
515 sysname = udev_device_get_sysname(parent);
516 if (sscanf(sysname, "bcma%*u:%u", &core) != 1)
517 return NULL;
518
519 path_prepend(path, "bcma-%u", core);
520 return parent;
521 }
522
523 /* Handle devices of AP bus in System z platform. */
524 static struct udev_device *handle_ap(struct udev_device *parent, char **path) {
525 const char *type, *func;
526
527 assert(parent);
528 assert(path);
529
530 type = udev_device_get_sysattr_value(parent, "type");
531 func = udev_device_get_sysattr_value(parent, "ap_functions");
532
533 if (type && func)
534 path_prepend(path, "ap-%s-%s", type, func);
535 else
536 path_prepend(path, "ap-%s", udev_device_get_sysname(parent));
537
538 return skip_subsystem(parent, "ap");
539 }
540
541 static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
542 struct udev_device *parent;
543 _cleanup_free_ char *path = NULL;
544 bool supported_transport = false;
545 bool supported_parent = false;
546
547 assert(dev);
548
549 /* walk up the chain of devices and compose path */
550 parent = dev;
551 while (parent) {
552 const char *subsys;
553
554 subsys = udev_device_get_subsystem(parent);
555 if (!subsys) {
556 ;
557 } else if (streq(subsys, "scsi_tape")) {
558 handle_scsi_tape(parent, &path);
559 } else if (streq(subsys, "scsi")) {
560 parent = handle_scsi(parent, &path, &supported_parent);
561 supported_transport = true;
562 } else if (streq(subsys, "cciss")) {
563 parent = handle_cciss(parent, &path);
564 supported_transport = true;
565 } else if (streq(subsys, "usb")) {
566 parent = handle_usb(parent, &path);
567 supported_transport = true;
568 } else if (streq(subsys, "bcma")) {
569 parent = handle_bcma(parent, &path);
570 supported_transport = true;
571 } else if (streq(subsys, "serio")) {
572 path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
573 parent = skip_subsystem(parent, "serio");
574 } else if (streq(subsys, "pci")) {
575 path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
576 parent = skip_subsystem(parent, "pci");
577 supported_parent = true;
578 } else if (streq(subsys, "platform")) {
579 path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
580 parent = skip_subsystem(parent, "platform");
581 supported_transport = true;
582 supported_parent = true;
583 } else if (streq(subsys, "acpi")) {
584 path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
585 parent = skip_subsystem(parent, "acpi");
586 supported_parent = true;
587 } else if (streq(subsys, "xen")) {
588 path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
589 parent = skip_subsystem(parent, "xen");
590 supported_parent = true;
591 } else if (streq(subsys, "virtio")) {
592 parent = skip_subsystem(parent, "virtio");
593 supported_transport = true;
594 } else if (streq(subsys, "scm")) {
595 path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
596 parent = skip_subsystem(parent, "scm");
597 supported_transport = true;
598 supported_parent = true;
599 } else if (streq(subsys, "ccw")) {
600 path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent));
601 parent = skip_subsystem(parent, "ccw");
602 supported_transport = true;
603 supported_parent = true;
604 } else if (streq(subsys, "ccwgroup")) {
605 path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent));
606 parent = skip_subsystem(parent, "ccwgroup");
607 supported_transport = true;
608 supported_parent = true;
609 } else if (streq(subsys, "ap")) {
610 parent = handle_ap(parent, &path);
611 supported_transport = true;
612 supported_parent = true;
613 } else if (streq(subsys, "iucv")) {
614 path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent));
615 parent = skip_subsystem(parent, "iucv");
616 supported_transport = true;
617 supported_parent = true;
618 } else if (streq(subsys, "nvme")) {
619 const char *nsid = udev_device_get_sysattr_value(dev, "nsid");
620
621 if (nsid) {
622 path_prepend(&path, "nvme-%s", nsid);
623 parent = skip_subsystem(parent, "nvme");
624 supported_parent = true;
625 supported_transport = true;
626 }
627 }
628
629 if (parent)
630 parent = udev_device_get_parent(parent);
631 }
632
633 if (!path)
634 return EXIT_FAILURE;
635
636 /*
637 * Do not return devices with an unknown parent device type. They
638 * might produce conflicting IDs if the parent does not provide a
639 * unique and predictable name.
640 */
641 if (!supported_parent)
642 return EXIT_FAILURE;
643
644 /*
645 * Do not return block devices without a well-known transport. Some
646 * devices do not expose their buses and do not provide a unique
647 * and predictable name that way.
648 */
649 if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport)
650 return EXIT_FAILURE;
651
652 {
653 char tag[UTIL_NAME_SIZE];
654 size_t i;
655 const char *p;
656
657 /* compose valid udev tag name */
658 for (p = path, i = 0; *p; p++) {
659 if ((*p >= '0' && *p <= '9') ||
660 (*p >= 'A' && *p <= 'Z') ||
661 (*p >= 'a' && *p <= 'z') ||
662 *p == '-') {
663 tag[i++] = *p;
664 continue;
665 }
666
667 /* skip all leading '_' */
668 if (i == 0)
669 continue;
670
671 /* avoid second '_' */
672 if (tag[i-1] == '_')
673 continue;
674
675 tag[i++] = '_';
676 }
677 /* strip trailing '_' */
678 while (i > 0 && tag[i-1] == '_')
679 i--;
680 tag[i] = '\0';
681
682 udev_builtin_add_property(dev, test, "ID_PATH", path);
683 udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
684 }
685
686 return EXIT_SUCCESS;
687 }
688
689 const struct udev_builtin udev_builtin_path_id = {
690 .name = "path_id",
691 .cmd = builtin_path_id,
692 .help = "Compose persistent device path",
693 .run_once = true,
694 };