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