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