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