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