]>
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> | |
07630cea | 15 | #include <unistd.h> |
d7867b31 | 16 | |
b5efdb8a | 17 | #include "alloc-util.h" |
8fb3f009 | 18 | #include "dirent-util.h" |
d7d7daec | 19 | #include "fd-util.h" |
5ea78a39 | 20 | #include "libudev-util.h" |
07630cea | 21 | #include "string-util.h" |
07a26e42 | 22 | #include "strv.h" |
a6856129 | 23 | #include "sysexits.h" |
07a26e42 | 24 | #include "udev-builtin.h" |
d7867b31 | 25 | |
9091e686 | 26 | _printf_(2,3) |
a6856129 | 27 | static void path_prepend(char **path, const char *fmt, ...) { |
912541b0 | 28 | va_list va; |
d7d7daec ZJS |
29 | _cleanup_free_ char *pre = NULL; |
30 | int r; | |
912541b0 KS |
31 | |
32 | va_start(va, fmt); | |
d7d7daec | 33 | r = vasprintf(&pre, fmt, va); |
912541b0 | 34 | va_end(va); |
a6856129 ZJS |
35 | if (r < 0) { |
36 | log_oom(); | |
37 | exit(EX_OSERR); | |
38 | } | |
912541b0 | 39 | |
d7d7daec | 40 | if (*path) { |
912541b0 KS |
41 | char *new; |
42 | ||
d7d7daec | 43 | new = strjoin(pre, "-", *path); |
a6856129 ZJS |
44 | if (!new) { |
45 | log_oom(); | |
46 | exit(EX_OSERR); | |
47 | } | |
48 | ||
d7d7daec | 49 | free_and_replace(*path, new); |
ae2a15bc LP |
50 | } else |
51 | *path = TAKE_PTR(pre); | |
d7867b31 KS |
52 | } |
53 | ||
54 | /* | |
55 | ** Linux only supports 32 bit luns. | |
56 | ** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. | |
57 | */ | |
de675b17 YW |
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; | |
d7867b31 | 68 | |
de675b17 | 69 | lun = strtoul(sysnum, NULL, 10); |
912541b0 | 70 | if (lun < 256) |
a6856129 ZJS |
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); | |
de675b17 YW |
76 | |
77 | return 0; | |
d7867b31 KS |
78 | } |
79 | ||
de675b17 YW |
80 | static sd_device *skip_subsystem(sd_device *dev, const char *subsys) { |
81 | sd_device *parent; | |
912541b0 | 82 | |
3b64e4d4 TG |
83 | assert(dev); |
84 | assert(subsys); | |
85 | ||
de675b17 | 86 | for (parent = dev; ; ) { |
912541b0 KS |
87 | const char *subsystem; |
88 | ||
de675b17 YW |
89 | if (sd_device_get_subsystem(parent, &subsystem) < 0) |
90 | break; | |
91 | ||
92 | if (!streq(subsystem, subsys)) | |
912541b0 | 93 | break; |
d7d7daec | 94 | |
912541b0 | 95 | dev = parent; |
de675b17 YW |
96 | if (sd_device_get_parent(dev, &parent) < 0) |
97 | break; | |
912541b0 | 98 | } |
d7d7daec | 99 | |
912541b0 | 100 | return dev; |
d7867b31 KS |
101 | } |
102 | ||
de675b17 YW |
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; | |
d7d7daec | 107 | _cleanup_free_ char *lun = NULL; |
912541b0 | 108 | |
3b64e4d4 TG |
109 | assert(parent); |
110 | assert(path); | |
111 | ||
de675b17 | 112 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0) |
912541b0 | 113 | return NULL; |
de675b17 | 114 | if (sd_device_get_sysname(targetdev, &sysname) < 0) |
912541b0 | 115 | return NULL; |
de675b17 YW |
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) | |
d7d7daec | 119 | return NULL; |
912541b0 KS |
120 | |
121 | format_lun_number(parent, &lun); | |
122 | path_prepend(path, "fc-%s-%s", port, lun); | |
912541b0 | 123 | return parent; |
d7867b31 KS |
124 | } |
125 | ||
de675b17 YW |
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; | |
d7d7daec | 130 | _cleanup_free_ char *lun = NULL; |
912541b0 | 131 | |
3b64e4d4 TG |
132 | assert(parent); |
133 | assert(path); | |
134 | ||
de675b17 | 135 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0) |
912541b0 | 136 | return NULL; |
de675b17 | 137 | if (sd_device_get_parent(targetdev, &target_parent) < 0) |
912541b0 | 138 | return NULL; |
de675b17 | 139 | if (sd_device_get_sysname(target_parent, &sysname) < 0) |
912541b0 | 140 | return NULL; |
de675b17 YW |
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) | |
d7d7daec | 144 | return NULL; |
912541b0 KS |
145 | |
146 | format_lun_number(parent, &lun); | |
147 | path_prepend(path, "sas-%s-%s", sas_address, lun); | |
912541b0 | 148 | return parent; |
d7867b31 KS |
149 | } |
150 | ||
de675b17 YW |
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; | |
66bba0e7 ML |
154 | const char *sas_address = NULL; |
155 | const char *phy_id; | |
de675b17 | 156 | const char *phy_count, *sysname; |
d7d7daec | 157 | _cleanup_free_ char *lun = NULL; |
66bba0e7 | 158 | |
3b64e4d4 TG |
159 | assert(parent); |
160 | assert(path); | |
161 | ||
de675b17 | 162 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target", &targetdev) < 0) |
66bba0e7 | 163 | return NULL; |
de675b17 YW |
164 | if (sd_device_get_parent(targetdev, &target_parent) < 0) |
165 | return NULL; | |
166 | if (sd_device_get_sysname(target_parent, &sysname) < 0) | |
66bba0e7 | 167 | return NULL; |
66bba0e7 | 168 | /* Get sas device */ |
de675b17 | 169 | if (sd_device_new_from_subsystem_sysname(&target_sasdev, "sas_device", sysname) < 0) |
66bba0e7 | 170 | return NULL; |
66bba0e7 | 171 | /* The next parent is sas port */ |
de675b17 YW |
172 | if (sd_device_get_parent(target_parent, &port) < 0) |
173 | return NULL; | |
174 | if (sd_device_get_sysname(port, &sysname) < 0) | |
d7d7daec | 175 | return NULL; |
66bba0e7 | 176 | /* Get port device */ |
de675b17 YW |
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) | |
d7d7daec | 180 | return NULL; |
66bba0e7 ML |
181 | |
182 | /* Check if we are simple disk */ | |
d7d7daec ZJS |
183 | if (strncmp(phy_count, "1", 2) != 0) |
184 | return handle_scsi_sas_wide_port(parent, path); | |
66bba0e7 ML |
185 | |
186 | /* Get connected phy */ | |
de675b17 | 187 | if (sd_device_get_sysattr_value(target_sasdev, "phy_identifier", &phy_id) < 0) |
d7d7daec | 188 | return NULL; |
66bba0e7 ML |
189 | |
190 | /* The port's parent is either hba or expander */ | |
de675b17 | 191 | if (sd_device_get_parent(port, &expander) < 0) |
d7d7daec | 192 | return NULL; |
66bba0e7 | 193 | |
de675b17 YW |
194 | if (sd_device_get_sysname(expander, &sysname) < 0) |
195 | return NULL; | |
66bba0e7 | 196 | /* Get expander device */ |
de675b17 YW |
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; | |
66bba0e7 ML |
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 | ||
66bba0e7 ML |
209 | return parent; |
210 | } | |
211 | ||
de675b17 YW |
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; | |
d7d7daec ZJS |
215 | const char *target, *connname, *addr, *port; |
216 | _cleanup_free_ char *lun = NULL; | |
de675b17 | 217 | const char *sysname, *sysnum; |
912541b0 | 218 | |
3b64e4d4 TG |
219 | assert(parent); |
220 | assert(path); | |
221 | ||
912541b0 | 222 | /* find iscsi session */ |
de675b17 YW |
223 | for (transportdev = parent; ; ) { |
224 | ||
225 | if (sd_device_get_parent(transportdev, &transportdev) < 0) | |
912541b0 | 226 | return NULL; |
de675b17 YW |
227 | if (sd_device_get_sysname(transportdev, &sysname) < 0) |
228 | return NULL; | |
229 | if (startswith(sysname, "session")) | |
912541b0 KS |
230 | break; |
231 | } | |
232 | ||
233 | /* find iscsi session device */ | |
de675b17 | 234 | if (sd_device_new_from_subsystem_sysname(&sessiondev, "iscsi_session", sysname) < 0) |
912541b0 | 235 | return NULL; |
d7d7daec | 236 | |
de675b17 | 237 | if (sd_device_get_sysattr_value(sessiondev, "targetname", &target) < 0) |
d7d7daec | 238 | return NULL; |
912541b0 | 239 | |
de675b17 YW |
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) | |
d7d7daec ZJS |
244 | return NULL; |
245 | ||
de675b17 YW |
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) | |
d7d7daec | 249 | return NULL; |
912541b0 KS |
250 | |
251 | format_lun_number(parent, &lun); | |
252 | path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); | |
912541b0 | 253 | return parent; |
d7867b31 KS |
254 | } |
255 | ||
9b3278d9 | 256 | static sd_device *handle_scsi_ata(sd_device *parent, char **path, char **compat_path) { |
de675b17 YW |
257 | sd_device *targetdev, *target_parent; |
258 | _cleanup_(sd_device_unrefp) sd_device *atadev = NULL; | |
9b3278d9 LZ |
259 | const char *port_no, *sysname, *name; |
260 | unsigned host, bus, target, lun; | |
ba86822d DM |
261 | |
262 | assert(parent); | |
263 | assert(path); | |
264 | ||
9b3278d9 LZ |
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 | ||
de675b17 | 270 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &targetdev) < 0) |
ba86822d DM |
271 | return NULL; |
272 | ||
de675b17 | 273 | if (sd_device_get_parent(targetdev, &target_parent) < 0) |
ba86822d DM |
274 | return NULL; |
275 | ||
de675b17 YW |
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) | |
ba86822d DM |
279 | return NULL; |
280 | ||
de675b17 | 281 | if (sd_device_get_sysattr_value(atadev, "port_no", &port_no) < 0) |
d7d7daec ZJS |
282 | return NULL; |
283 | ||
9b3278d9 LZ |
284 | if (bus != 0) |
285 | /* Devices behind port multiplier have a bus != 0*/ | |
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 | ||
ba86822d DM |
295 | return parent; |
296 | } | |
297 | ||
de675b17 YW |
298 | static sd_device *handle_scsi_default(sd_device *parent, char **path) { |
299 | sd_device *hostdev; | |
912541b0 | 300 | int host, bus, target, lun; |
d7d7daec ZJS |
301 | const char *name, *base, *pos; |
302 | _cleanup_closedir_ DIR *dir = NULL; | |
912541b0 | 303 | struct dirent *dent; |
d7d7daec | 304 | int basenum = -1; |
912541b0 | 305 | |
3b64e4d4 TG |
306 | assert(parent); |
307 | assert(path); | |
308 | ||
de675b17 | 309 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &hostdev) < 0) |
912541b0 KS |
310 | return NULL; |
311 | ||
de675b17 YW |
312 | if (sd_device_get_sysname(parent, &name) < 0) |
313 | return NULL; | |
912541b0 KS |
314 | if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) |
315 | return NULL; | |
316 | ||
1c7dfbf2 KS |
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 | */ | |
d7d7daec | 334 | |
de675b17 YW |
335 | if (sd_device_get_syspath(hostdev, &base) < 0) |
336 | return NULL; | |
912541b0 | 337 | pos = strrchr(base, '/'); |
d7d7daec ZJS |
338 | if (!pos) |
339 | return NULL; | |
340 | ||
341 | base = strndupa(base, pos - base); | |
912541b0 | 342 | dir = opendir(base); |
d7d7daec ZJS |
343 | if (!dir) |
344 | return NULL; | |
345 | ||
8fb3f009 | 346 | FOREACH_DIRENT_ALL(dent, dir, break) { |
912541b0 KS |
347 | char *rest; |
348 | int i; | |
349 | ||
350 | if (dent->d_name[0] == '.') | |
351 | continue; | |
ec2ce0c5 | 352 | if (!IN_SET(dent->d_type, DT_DIR, DT_LNK)) |
912541b0 | 353 | continue; |
33502ffe | 354 | if (!startswith(dent->d_name, "host")) |
912541b0 KS |
355 | continue; |
356 | i = strtoul(&dent->d_name[4], &rest, 10); | |
357 | if (rest[0] != '\0') | |
358 | continue; | |
746b5152 KS |
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 | */ | |
912541b0 KS |
364 | if (basenum == -1 || i < basenum) |
365 | basenum = i; | |
366 | } | |
d7d7daec ZJS |
367 | if (basenum == -1) |
368 | return hostdev; | |
912541b0 KS |
369 | host -= basenum; |
370 | ||
371 | path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); | |
912541b0 | 372 | return hostdev; |
d7867b31 KS |
373 | } |
374 | ||
de675b17 YW |
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; | |
a24d03b8 | 378 | const char *guid_str; |
d7d7daec | 379 | _cleanup_free_ char *lun = NULL; |
cf3fabac | 380 | char guid[39]; |
a24d03b8 HR |
381 | size_t i, k; |
382 | ||
3b64e4d4 TG |
383 | assert(parent); |
384 | assert(path); | |
cf3fabac | 385 | assert(guid_str_len < sizeof(guid)); |
3b64e4d4 | 386 | |
de675b17 | 387 | if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &hostdev) < 0) |
a24d03b8 HR |
388 | return NULL; |
389 | ||
de675b17 | 390 | if (sd_device_get_parent(hostdev, &vmbusdev) < 0) |
a24d03b8 HR |
391 | return NULL; |
392 | ||
de675b17 | 393 | if (sd_device_get_sysattr_value(vmbusdev, "device_id", &guid_str) < 0) |
a24d03b8 HR |
394 | return NULL; |
395 | ||
cf3fabac | 396 | if (strlen(guid_str) < guid_str_len || guid_str[0] != '{' || guid_str[guid_str_len-1] != '}') |
a24d03b8 HR |
397 | return NULL; |
398 | ||
cf3fabac | 399 | for (i = 1, k = 0; i < guid_str_len-1; i++) { |
a24d03b8 HR |
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); | |
a24d03b8 HR |
408 | return parent; |
409 | } | |
410 | ||
9b3278d9 | 411 | static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) { |
d7d7daec | 412 | const char *devtype, *id, *name; |
912541b0 | 413 | |
de675b17 YW |
414 | if (sd_device_get_devtype(parent, &devtype) < 0 || |
415 | !streq(devtype, "scsi_device")) | |
912541b0 KS |
416 | return parent; |
417 | ||
418 | /* firewire */ | |
de675b17 | 419 | if (sd_device_get_sysattr_value(parent, "ieee1394_id", &id) >= 0) { |
912541b0 | 420 | path_prepend(path, "ieee1394-0x%s", id); |
cc821d02 | 421 | *supported_parent = true; |
d7d7daec | 422 | return skip_subsystem(parent, "scsi"); |
912541b0 KS |
423 | } |
424 | ||
59d86149 | 425 | /* scsi sysfs does not have a "subsystem" for the transport */ |
de675b17 YW |
426 | if (sd_device_get_syspath(parent, &name) < 0) |
427 | return NULL; | |
912541b0 | 428 | |
d7d7daec | 429 | if (strstr(name, "/rport-")) { |
cc821d02 | 430 | *supported_parent = true; |
d7d7daec | 431 | return handle_scsi_fibre_channel(parent, path); |
912541b0 KS |
432 | } |
433 | ||
d7d7daec | 434 | if (strstr(name, "/end_device-")) { |
cc821d02 | 435 | *supported_parent = true; |
d7d7daec | 436 | return handle_scsi_sas(parent, path); |
912541b0 KS |
437 | } |
438 | ||
d7d7daec | 439 | if (strstr(name, "/session")) { |
cc821d02 | 440 | *supported_parent = true; |
d7d7daec | 441 | return handle_scsi_iscsi(parent, path); |
912541b0 KS |
442 | } |
443 | ||
d7d7daec | 444 | if (strstr(name, "/ata")) |
9b3278d9 | 445 | return handle_scsi_ata(parent, path, compat_path); |
481dcf7c | 446 | |
d7d7daec | 447 | if (strstr(name, "/vmbus_")) |
cf3fabac LL |
448 | return handle_scsi_hyperv(parent, path, 37); |
449 | else if (strstr(name, "/VMBUS")) | |
450 | return handle_scsi_hyperv(parent, path, 38); | |
a24d03b8 | 451 | |
d7d7daec | 452 | return handle_scsi_default(parent, path); |
d7867b31 KS |
453 | } |
454 | ||
de675b17 | 455 | static sd_device *handle_cciss(sd_device *parent, char **path) { |
68acb21d | 456 | const char *str; |
14cb109d | 457 | unsigned controller, disk; |
68acb21d | 458 | |
de675b17 YW |
459 | if (sd_device_get_sysname(parent, &str) < 0) |
460 | return NULL; | |
68acb21d HR |
461 | if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) |
462 | return NULL; | |
463 | ||
464 | path_prepend(path, "cciss-disk%u", disk); | |
d7d7daec | 465 | return skip_subsystem(parent, "cciss"); |
68acb21d HR |
466 | } |
467 | ||
de675b17 | 468 | static void handle_scsi_tape(sd_device *dev, char **path) { |
912541b0 | 469 | const char *name; |
d7867b31 | 470 | |
912541b0 | 471 | /* must be the last device in the syspath */ |
d7d7daec | 472 | if (*path) |
912541b0 | 473 | return; |
d7867b31 | 474 | |
de675b17 YW |
475 | if (sd_device_get_sysname(dev, &name) < 0) |
476 | return; | |
477 | ||
d7d7daec | 478 | if (startswith(name, "nst") && strchr("lma", name[3])) |
912541b0 | 479 | path_prepend(path, "nst%c", name[3]); |
d7d7daec | 480 | else if (startswith(name, "st") && strchr("lma", name[2])) |
912541b0 | 481 | path_prepend(path, "st%c", name[2]); |
d7867b31 KS |
482 | } |
483 | ||
de675b17 | 484 | static sd_device *handle_usb(sd_device *parent, char **path) { |
d7d7daec | 485 | const char *devtype, *str, *port; |
912541b0 | 486 | |
de675b17 | 487 | if (sd_device_get_devtype(parent, &devtype) < 0) |
912541b0 | 488 | return parent; |
d7d7daec | 489 | if (!STR_IN_SET(devtype, "usb_interface", "usb_device")) |
912541b0 KS |
490 | return parent; |
491 | ||
de675b17 YW |
492 | if (sd_device_get_sysname(parent, &str) < 0) |
493 | return parent; | |
912541b0 | 494 | port = strchr(str, '-'); |
d7d7daec | 495 | if (!port) |
912541b0 KS |
496 | return parent; |
497 | port++; | |
498 | ||
912541b0 | 499 | path_prepend(path, "usb-0:%s", port); |
d7d7daec | 500 | return skip_subsystem(parent, "usb"); |
d7867b31 KS |
501 | } |
502 | ||
de675b17 | 503 | static sd_device *handle_bcma(sd_device *parent, char **path) { |
89f17d4f | 504 | const char *sysname; |
14cb109d | 505 | unsigned core; |
89f17d4f | 506 | |
de675b17 YW |
507 | if (sd_device_get_sysname(parent, &sysname) < 0) |
508 | return NULL; | |
89f17d4f TG |
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 | ||
e7eb5a8d | 516 | /* Handle devices of AP bus in System z platform. */ |
de675b17 | 517 | static sd_device *handle_ap(sd_device *parent, char **path) { |
e7eb5a8d | 518 | const char *type, *func; |
912541b0 | 519 | |
3b64e4d4 | 520 | assert(parent); |
3b64e4d4 TG |
521 | assert(path); |
522 | ||
de675b17 YW |
523 | if (sd_device_get_sysattr_value(parent, "type", &type) >= 0 && |
524 | sd_device_get_sysattr_value(parent, "ap_functions", &func) >= 0) | |
e7eb5a8d | 525 | path_prepend(path, "ap-%s-%s", type, func); |
de675b17 YW |
526 | else { |
527 | const char *sysname; | |
528 | ||
529 | if (sd_device_get_sysname(parent, &sysname) >= 0) | |
530 | path_prepend(path, "ap-%s", sysname); | |
531 | } | |
d7d7daec ZJS |
532 | |
533 | return skip_subsystem(parent, "ap"); | |
d7867b31 KS |
534 | } |
535 | ||
3fc2e9a2 | 536 | static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) { |
de675b17 | 537 | sd_device *parent; |
d7d7daec | 538 | _cleanup_free_ char *path = NULL; |
9b3278d9 | 539 | _cleanup_free_ char *compat_path = NULL; |
e98bbfd2 KS |
540 | bool supported_transport = false; |
541 | bool supported_parent = false; | |
de675b17 | 542 | const char *subsystem; |
912541b0 | 543 | |
3b64e4d4 TG |
544 | assert(dev); |
545 | ||
912541b0 KS |
546 | /* walk up the chain of devices and compose path */ |
547 | parent = dev; | |
d7d7daec | 548 | while (parent) { |
de675b17 | 549 | const char *subsys, *sysname; |
912541b0 | 550 | |
de675b17 YW |
551 | if (sd_device_get_subsystem(parent, &subsys) < 0 || |
552 | sd_device_get_sysname(parent, &sysname) < 0) { | |
912541b0 | 553 | ; |
090be865 | 554 | } else if (streq(subsys, "scsi_tape")) { |
912541b0 | 555 | handle_scsi_tape(parent, &path); |
090be865 | 556 | } else if (streq(subsys, "scsi")) { |
9b3278d9 | 557 | parent = handle_scsi(parent, &path, &compat_path, &supported_parent); |
e98bbfd2 | 558 | supported_transport = true; |
090be865 | 559 | } else if (streq(subsys, "cciss")) { |
68acb21d | 560 | parent = handle_cciss(parent, &path); |
e98bbfd2 | 561 | supported_transport = true; |
090be865 | 562 | } else if (streq(subsys, "usb")) { |
912541b0 | 563 | parent = handle_usb(parent, &path); |
e98bbfd2 | 564 | supported_transport = true; |
89f17d4f TG |
565 | } else if (streq(subsys, "bcma")) { |
566 | parent = handle_bcma(parent, &path); | |
e98bbfd2 | 567 | supported_transport = true; |
090be865 | 568 | } else if (streq(subsys, "serio")) { |
de675b17 YW |
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 | } | |
090be865 | 575 | } else if (streq(subsys, "pci")) { |
de675b17 | 576 | path_prepend(&path, "pci-%s", sysname); |
9b3278d9 LZ |
577 | if (compat_path) |
578 | path_prepend(&compat_path, "pci-%s", sysname); | |
912541b0 | 579 | parent = skip_subsystem(parent, "pci"); |
e98bbfd2 | 580 | supported_parent = true; |
090be865 | 581 | } else if (streq(subsys, "platform")) { |
de675b17 | 582 | path_prepend(&path, "platform-%s", sysname); |
9b3278d9 LZ |
583 | if (compat_path) |
584 | path_prepend(&compat_path, "platform-%s", sysname); | |
912541b0 | 585 | parent = skip_subsystem(parent, "platform"); |
e98bbfd2 KS |
586 | supported_transport = true; |
587 | supported_parent = true; | |
090be865 | 588 | } else if (streq(subsys, "acpi")) { |
de675b17 | 589 | path_prepend(&path, "acpi-%s", sysname); |
9b3278d9 LZ |
590 | if (compat_path) |
591 | path_prepend(&compat_path, "acpi-%s", sysname); | |
912541b0 | 592 | parent = skip_subsystem(parent, "acpi"); |
e98bbfd2 | 593 | supported_parent = true; |
090be865 | 594 | } else if (streq(subsys, "xen")) { |
de675b17 | 595 | path_prepend(&path, "xen-%s", sysname); |
9b3278d9 LZ |
596 | if (compat_path) |
597 | path_prepend(&compat_path, "xen-%s", sysname); | |
912541b0 | 598 | parent = skip_subsystem(parent, "xen"); |
e98bbfd2 | 599 | supported_parent = true; |
f073b1b3 | 600 | } else if (streq(subsys, "virtio")) { |
fb92fbb1 | 601 | parent = skip_subsystem(parent, "virtio"); |
f073b1b3 | 602 | supported_transport = true; |
090be865 | 603 | } else if (streq(subsys, "scm")) { |
de675b17 | 604 | path_prepend(&path, "scm-%s", sysname); |
9b3278d9 LZ |
605 | if (compat_path) |
606 | path_prepend(&compat_path, "scm-%s", sysname); | |
4ecc1318 | 607 | parent = skip_subsystem(parent, "scm"); |
e98bbfd2 KS |
608 | supported_transport = true; |
609 | supported_parent = true; | |
e7eb5a8d | 610 | } else if (streq(subsys, "ccw")) { |
de675b17 | 611 | path_prepend(&path, "ccw-%s", sysname); |
9b3278d9 LZ |
612 | if (compat_path) |
613 | path_prepend(&compat_path, "ccw-%s", sysname); | |
e7eb5a8d LYY |
614 | parent = skip_subsystem(parent, "ccw"); |
615 | supported_transport = true; | |
616 | supported_parent = true; | |
617 | } else if (streq(subsys, "ccwgroup")) { | |
de675b17 | 618 | path_prepend(&path, "ccwgroup-%s", sysname); |
9b3278d9 LZ |
619 | if (compat_path) |
620 | path_prepend(&compat_path, "ccwgroup-%s", sysname); | |
e7eb5a8d LYY |
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")) { | |
de675b17 | 629 | path_prepend(&path, "iucv-%s", sysname); |
9b3278d9 LZ |
630 | if (compat_path) |
631 | path_prepend(&compat_path, "iucv-%s", sysname); | |
e7eb5a8d LYY |
632 | parent = skip_subsystem(parent, "iucv"); |
633 | supported_transport = true; | |
634 | supported_parent = true; | |
b4c6f71b | 635 | } else if (streq(subsys, "nvme")) { |
de675b17 | 636 | const char *nsid; |
b4c6f71b | 637 | |
de675b17 | 638 | if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) { |
b4c6f71b | 639 | path_prepend(&path, "nvme-%s", nsid); |
9b3278d9 LZ |
640 | if (compat_path) |
641 | path_prepend(&compat_path, "nvme-%s", nsid); | |
b4c6f71b KB |
642 | parent = skip_subsystem(parent, "nvme"); |
643 | supported_parent = true; | |
644 | supported_transport = true; | |
645 | } | |
b46d8c1c MKB |
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 | } | |
912541b0 KS |
653 | } |
654 | ||
de675b17 YW |
655 | if (!parent) |
656 | break; | |
657 | if (sd_device_get_parent(parent, &parent) < 0) | |
658 | break; | |
912541b0 | 659 | } |
7fdd367e | 660 | |
d7d7daec | 661 | if (!path) |
d354690e | 662 | return -ENOENT; |
d7d7daec | 663 | |
7fdd367e | 664 | /* |
a42cdff1 KS |
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. | |
7fdd367e | 668 | */ |
97b11eed | 669 | if (!supported_parent) |
d354690e | 670 | return -ENOENT; |
e98bbfd2 KS |
671 | |
672 | /* | |
a42cdff1 KS |
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. | |
e98bbfd2 | 676 | */ |
de675b17 YW |
677 | if (sd_device_get_subsystem(dev, &subsystem) >= 0 && |
678 | streq(subsystem, "block") && | |
679 | !supported_transport) | |
d354690e | 680 | return -ENOENT; |
7fdd367e | 681 | |
d7d7daec | 682 | { |
912541b0 KS |
683 | char tag[UTIL_NAME_SIZE]; |
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 | ||
de675b17 YW |
712 | udev_builtin_add_property(dev, test, "ID_PATH", path); |
713 | udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); | |
912541b0 | 714 | } |
d7d7daec | 715 | |
9b3278d9 LZ |
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 | ||
d354690e | 724 | return 0; |
d7867b31 KS |
725 | } |
726 | ||
25de7aa7 | 727 | const UdevBuiltin udev_builtin_path_id = { |
912541b0 KS |
728 | .name = "path_id", |
729 | .cmd = builtin_path_id, | |
5ac0162c | 730 | .help = "Compose persistent device path", |
912541b0 | 731 | .run_once = true, |
d7867b31 | 732 | }; |