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