]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
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
07630cea 33#include "string-util.h"
d7867b31
KS
34#include "udev.h"
35
9091e686 36_printf_(2,3)
9ec6e95b 37static int path_prepend(char **path, const char *fmt, ...) {
912541b0
KS
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 }
d7867b31 60out:
912541b0 61 return err;
d7867b31
KS
62}
63
64/*
65** Linux only supports 32 bit luns.
66** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
67*/
9ec6e95b 68static int format_lun_number(struct udev_device *dev, char **path) {
912541b0 69 unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
d7867b31 70
912541b0
KS
71 /* address method 0, peripheral device addressing with bus id of zero */
72 if (lun < 256)
d9de321f 73 return path_prepend(path, "lun-%lu", lun);
912541b0 74 /* handle all other lun addressing methods by using a variant of the original lun format */
d9de321f 75 return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
d7867b31
KS
76}
77
9ec6e95b 78static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) {
912541b0
KS
79 struct udev_device *parent = dev;
80
3b64e4d4
TG
81 assert(dev);
82 assert(subsys);
83
912541b0
KS
84 while (parent != NULL) {
85 const char *subsystem;
86
87 subsystem = udev_device_get_subsystem(parent);
090be865 88 if (subsystem == NULL || !streq(subsystem, subsys))
912541b0
KS
89 break;
90 dev = parent;
91 parent = udev_device_get_parent(parent);
92 }
93 return dev;
d7867b31
KS
94}
95
9ec6e95b 96static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) {
912541b0
KS
97 struct udev *udev = udev_device_get_udev(parent);
98 struct udev_device *targetdev;
99 struct udev_device *fcdev = NULL;
100 const char *port;
33b40551 101 char *lun = NULL;
912541b0 102
3b64e4d4
TG
103 assert(parent);
104 assert(path);
105
912541b0
KS
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);
dc4ebc07 121 free(lun);
d7867b31 122out:
912541b0
KS
123 udev_device_unref(fcdev);
124 return parent;
d7867b31
KS
125}
126
66bba0e7 127static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) {
912541b0
KS
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
3b64e4d4
TG
135 assert(parent);
136 assert(path);
137
912541b0
KS
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);
dc4ebc07 159 free(lun);
d7867b31 160out:
912541b0
KS
161 udev_device_unref(sasdev);
162 return parent;
d7867b31
KS
163}
164
66bba0e7
ML
165static 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
3b64e4d4
TG
180 assert(parent);
181 assert(path);
182
66bba0e7
ML
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
dc4ebc07 253 free(lun);
66bba0e7
ML
254out:
255 udev_device_unref(target_sasdev);
256 udev_device_unref(expander_sasdev);
257 udev_device_unref(port_sasdev);
258 return parent;
259}
260
9ec6e95b 261static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) {
912541b0
KS
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
3b64e4d4
TG
272 assert(parent);
273 assert(path);
274
912541b0
KS
275 /* find iscsi session */
276 transportdev = parent;
277 for (;;) {
278 transportdev = udev_device_get_parent(transportdev);
279 if (transportdev == NULL)
280 return NULL;
33502ffe 281 if (startswith(udev_device_get_sysname(transportdev), "session"))
912541b0
KS
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);
dc4ebc07 314 free(lun);
d7867b31 315out:
912541b0
KS
316 udev_device_unref(sessiondev);
317 udev_device_unref(conndev);
318 return parent;
d7867b31
KS
319}
320
ba86822d
DM
321static 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);
349out:
350 udev_device_unref(atadev);
351 return parent;
352}
353
9ec6e95b 354static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) {
912541b0
KS
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
3b64e4d4
TG
364 assert(parent);
365 assert(path);
366
912541b0
KS
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
1c7dfbf2
KS
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 */
912541b0
KS
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;
33502ffe 415 if (!startswith(dent->d_name, "host"))
912541b0
KS
416 continue;
417 i = strtoul(&dent->d_name[4], &rest, 10);
418 if (rest[0] != '\0')
419 continue;
746b5152
KS
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 */
912541b0
KS
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);
d7867b31 436out:
912541b0
KS
437 free(base);
438 return hostdev;
d7867b31
KS
439}
440
a24d03b8
HR
441static 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
3b64e4d4
TG
449 assert(parent);
450 assert(path);
451
a24d03b8
HR
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
cc821d02 480static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) {
912541b0
KS
481 const char *devtype;
482 const char *name;
483 const char *id;
484
485 devtype = udev_device_get_devtype(parent);
090be865 486 if (devtype == NULL || !streq(devtype, "scsi_device"))
912541b0
KS
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);
cc821d02 494 *supported_parent = true;
912541b0
KS
495 goto out;
496 }
497
59d86149 498 /* scsi sysfs does not have a "subsystem" for the transport */
912541b0
KS
499 name = udev_device_get_syspath(parent);
500
501 if (strstr(name, "/rport-") != NULL) {
502 parent = handle_scsi_fibre_channel(parent, path);
cc821d02 503 *supported_parent = true;
912541b0
KS
504 goto out;
505 }
506
507 if (strstr(name, "/end_device-") != NULL) {
508 parent = handle_scsi_sas(parent, path);
cc821d02 509 *supported_parent = true;
912541b0
KS
510 goto out;
511 }
512
513 if (strstr(name, "/session") != NULL) {
514 parent = handle_scsi_iscsi(parent, path);
cc821d02 515 *supported_parent = true;
912541b0
KS
516 goto out;
517 }
518
481dcf7c 519 if (strstr(name, "/ata") != NULL) {
ba86822d 520 parent = handle_scsi_ata(parent, path);
481dcf7c
KS
521 goto out;
522 }
523
a24d03b8
HR
524 if (strstr(name, "/vmbus_") != NULL) {
525 parent = handle_scsi_hyperv(parent, path);
526 goto out;
527 }
528
912541b0 529 parent = handle_scsi_default(parent, path);
d7867b31 530out:
912541b0 531 return parent;
d7867b31
KS
532}
533
9ec6e95b 534static struct udev_device *handle_cciss(struct udev_device *parent, char **path) {
68acb21d
HR
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
9ec6e95b 547static void handle_scsi_tape(struct udev_device *dev, char **path) {
912541b0 548 const char *name;
d7867b31 549
912541b0
KS
550 /* must be the last device in the syspath */
551 if (*path != NULL)
552 return;
d7867b31 553
912541b0 554 name = udev_device_get_sysname(dev);
33502ffe 555 if (startswith(name, "nst") && strchr("lma", name[3]) != NULL)
912541b0 556 path_prepend(path, "nst%c", name[3]);
33502ffe 557 else if (startswith(name, "st") && strchr("lma", name[2]) != NULL)
912541b0 558 path_prepend(path, "st%c", name[2]);
d7867b31
KS
559}
560
9ec6e95b 561static struct udev_device *handle_usb(struct udev_device *parent, char **path) {
912541b0
KS
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;
090be865 569 if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device"))
912541b0
KS
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;
d7867b31
KS
581}
582
9ec6e95b 583static struct udev_device *handle_bcma(struct udev_device *parent, char **path) {
89f17d4f
TG
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
9ec6e95b 595static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) {
912541b0
KS
596 struct udev_device *scsi_dev;
597
3b64e4d4
TG
598 assert(parent);
599 assert(dev);
600 assert(path);
601
912541b0
KS
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));
d7867b31 618out:
912541b0
KS
619 parent = skip_subsystem(parent, "ccw");
620 return parent;
d7867b31
KS
621}
622
9ec6e95b 623static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) {
912541b0
KS
624 struct udev_device *parent;
625 char *path = NULL;
e98bbfd2
KS
626 bool supported_transport = false;
627 bool supported_parent = false;
912541b0 628
3b64e4d4
TG
629 assert(dev);
630
912541b0
KS
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 ;
090be865 646 } else if (streq(subsys, "scsi_tape")) {
912541b0 647 handle_scsi_tape(parent, &path);
090be865 648 } else if (streq(subsys, "scsi")) {
cc821d02 649 parent = handle_scsi(parent, &path, &supported_parent);
e98bbfd2 650 supported_transport = true;
090be865 651 } else if (streq(subsys, "cciss")) {
68acb21d 652 parent = handle_cciss(parent, &path);
e98bbfd2 653 supported_transport = true;
090be865 654 } else if (streq(subsys, "usb")) {
912541b0 655 parent = handle_usb(parent, &path);
e98bbfd2 656 supported_transport = true;
89f17d4f
TG
657 } else if (streq(subsys, "bcma")) {
658 parent = handle_bcma(parent, &path);
e98bbfd2 659 supported_transport = true;
090be865 660 } else if (streq(subsys, "serio")) {
912541b0
KS
661 path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent));
662 parent = skip_subsystem(parent, "serio");
090be865 663 } else if (streq(subsys, "pci")) {
912541b0
KS
664 path_prepend(&path, "pci-%s", udev_device_get_sysname(parent));
665 parent = skip_subsystem(parent, "pci");
e98bbfd2 666 supported_parent = true;
090be865 667 } else if (streq(subsys, "platform")) {
912541b0
KS
668 path_prepend(&path, "platform-%s", udev_device_get_sysname(parent));
669 parent = skip_subsystem(parent, "platform");
e98bbfd2
KS
670 supported_transport = true;
671 supported_parent = true;
090be865 672 } else if (streq(subsys, "acpi")) {
912541b0
KS
673 path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent));
674 parent = skip_subsystem(parent, "acpi");
e98bbfd2 675 supported_parent = true;
090be865 676 } else if (streq(subsys, "xen")) {
912541b0
KS
677 path_prepend(&path, "xen-%s", udev_device_get_sysname(parent));
678 parent = skip_subsystem(parent, "xen");
e98bbfd2 679 supported_parent = true;
090be865 680 } else if (streq(subsys, "scm")) {
4ecc1318
SO
681 path_prepend(&path, "scm-%s", udev_device_get_sysname(parent));
682 parent = skip_subsystem(parent, "scm");
e98bbfd2
KS
683 supported_transport = true;
684 supported_parent = true;
912541b0
KS
685 }
686
28541a3d
TG
687 if (parent)
688 parent = udev_device_get_parent(parent);
912541b0 689 }
7fdd367e
KS
690
691 /*
a42cdff1
KS
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.
7fdd367e 695 */
97b11eed
DH
696 if (!supported_parent)
697 path = mfree(path);
e98bbfd2
KS
698
699 /*
a42cdff1
KS
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.
e98bbfd2 703 */
97b11eed
DH
704 if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport)
705 path = mfree(path);
7fdd367e 706
d7867b31 707out:
912541b0
KS
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;
d7867b31
KS
744}
745
746const struct udev_builtin udev_builtin_path_id = {
912541b0
KS
747 .name = "path_id",
748 .cmd = builtin_path_id,
5ac0162c 749 .help = "Compose persistent device path",
912541b0 750 .run_once = true,
d7867b31 751};