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