]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
b237a168 | 2 | |
5953d8b9 | 3 | #include <ctype.h> |
69a283c5 | 4 | #include <time.h> |
030a0d79 | 5 | #include <unistd.h> |
b237a168 | 6 | |
69a283c5 DDM |
7 | #include "sd-event.h" |
8 | #include "sd-id128.h" | |
9 | ||
152d0efa | 10 | #include "alloc-util.h" |
393fcaf7 | 11 | #include "device-nodes.h" |
a1130022 | 12 | #include "device-private.h" |
f822c5d5 | 13 | #include "device-util.h" |
acfc2a1d | 14 | #include "errno-util.h" |
69a283c5 | 15 | #include "hashmap.h" |
b237a168 | 16 | #include "log.h" |
030a0d79 | 17 | #include "path-util.h" |
f92c5bb1 | 18 | #include "stat-util.h" |
b237a168 | 19 | #include "string-util.h" |
e548ca38 | 20 | #include "strv.h" |
b237a168 | 21 | #include "udev-util.h" |
aea3253e | 22 | #include "utf8.h" |
b237a168 | 23 | |
09dd8e77 DT |
24 | int udev_parse_config_full(const ConfigTableItem config_table[]) { |
25 | int r; | |
26 | ||
27 | assert(config_table); | |
a14e7af1 | 28 | |
6378f257 | 29 | r = config_parse_standard_file_with_dropins( |
e5abff37 | 30 | "udev/udev.conf", |
07f5e35f | 31 | /* sections = */ NULL, |
6378f257 | 32 | config_item_table_lookup, config_table, |
07f5e35f DT |
33 | CONFIG_PARSE_WARN, |
34 | /* userdata = */ NULL); | |
04a1ee58 YW |
35 | if (r == -ENOENT) |
36 | return 0; | |
09dd8e77 DT |
37 | return r; |
38 | } | |
39 | ||
40 | int udev_parse_config(void) { | |
41 | int r, log_val = -1; | |
42 | const ConfigTableItem config_table[] = { | |
90670111 YW |
43 | { NULL, "udev_log", config_parse_log_level, 0, &log_val }, |
44 | { NULL, "children_max", NULL, 0, NULL }, | |
45 | { NULL, "exec_delay", NULL, 0, NULL }, | |
46 | { NULL, "event_timeout", NULL, 0, NULL }, | |
47 | { NULL, "resolve_names", NULL, 0, NULL }, | |
48 | { NULL, "timeout_signal", NULL, 0, NULL }, | |
09dd8e77 DT |
49 | {} |
50 | }; | |
51 | ||
52 | r = udev_parse_config_full(config_table); | |
04a1ee58 YW |
53 | if (r < 0) |
54 | return r; | |
e2099267 | 55 | |
07f5e35f DT |
56 | if (log_val >= 0) |
57 | log_set_max_level(log_val); | |
a14e7af1 | 58 | |
b237a168 ZJS |
59 | return 0; |
60 | } | |
ed435031 ZJS |
61 | |
62 | struct DeviceMonitorData { | |
63 | const char *sysname; | |
030a0d79 | 64 | const char *devlink; |
ed435031 ZJS |
65 | sd_device *device; |
66 | }; | |
67 | ||
ce5eef65 LB |
68 | static void device_monitor_data_free(struct DeviceMonitorData *d) { |
69 | assert(d); | |
70 | ||
71 | sd_device_unref(d->device); | |
72 | } | |
73 | ||
ed435031 | 74 | static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { |
99534007 | 75 | struct DeviceMonitorData *data = ASSERT_PTR(userdata); |
ed435031 ZJS |
76 | const char *sysname; |
77 | ||
78 | assert(device); | |
030a0d79 | 79 | assert(data->sysname || data->devlink); |
ed435031 ZJS |
80 | assert(!data->device); |
81 | ||
e13d96ca LP |
82 | /* Ignore REMOVE events here. We are waiting for initialization after all, not de-initialization. We |
83 | * might see a REMOVE event from an earlier use of the device (devices by the same name are recycled | |
84 | * by the kernel after all), which we should not get confused by. After all we cannot distinguish use | |
85 | * cycles of the devices, as the udev queue is entirely asynchronous. | |
86 | * | |
87 | * If we see a REMOVE event here for the use cycle we actually care about then we won't notice of | |
88 | * course, but that should be OK, given the timeout logic used on the wait loop: this will be noticed | |
89 | * by means of -ETIMEDOUT. Thus we won't notice immediately, but eventually, and that should be | |
90 | * sufficient for an error path that should regularly not happen. | |
91 | * | |
92 | * (And yes, we only need to special case REMOVE. It's the only "negative" event type, where a device | |
93 | * ceases to exist. All other event types are "positive": the device exists and is registered in the | |
94 | * udev database, thus whenever we see the event, we can consider it initialized.) */ | |
a1130022 | 95 | if (device_for_action(device, SD_DEVICE_REMOVE)) |
e13d96ca LP |
96 | return 0; |
97 | ||
030a0d79 LB |
98 | if (data->sysname && sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) |
99 | goto found; | |
100 | ||
101 | if (data->devlink) { | |
102 | const char *devlink; | |
103 | ||
a1af8372 DDM |
104 | FOREACH_DEVICE_DEVLINK(device, link) |
105 | if (path_equal(link, data->devlink)) | |
030a0d79 LB |
106 | goto found; |
107 | ||
108 | if (sd_device_get_devname(device, &devlink) >= 0 && path_equal(devlink, data->devlink)) | |
109 | goto found; | |
ed435031 ZJS |
110 | } |
111 | ||
112 | return 0; | |
030a0d79 LB |
113 | |
114 | found: | |
115 | data->device = sd_device_ref(device); | |
116 | return sd_event_exit(sd_device_monitor_get_event(monitor), 0); | |
ed435031 ZJS |
117 | } |
118 | ||
030a0d79 LB |
119 | static int device_wait_for_initialization_internal( |
120 | sd_device *_device, | |
121 | const char *devlink, | |
122 | const char *subsystem, | |
4f89ce0c | 123 | usec_t timeout_usec, |
030a0d79 | 124 | sd_device **ret) { |
9e3d9067 | 125 | |
ed435031 ZJS |
126 | _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; |
127 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; | |
030a0d79 LB |
128 | /* Ensure that if !_device && devlink, device gets unrefd on errors since it will be new */ |
129 | _cleanup_(sd_device_unrefp) sd_device *device = sd_device_ref(_device); | |
ce5eef65 | 130 | _cleanup_(device_monitor_data_free) struct DeviceMonitorData data = { |
030a0d79 LB |
131 | .devlink = devlink, |
132 | }; | |
ed435031 ZJS |
133 | int r; |
134 | ||
030a0d79 | 135 | assert(device || (subsystem && devlink)); |
ed435031 | 136 | |
030a0d79 LB |
137 | /* Devlink might already exist, if it does get the device to use the sysname filtering */ |
138 | if (!device && devlink) { | |
f81b3e90 YW |
139 | r = sd_device_new_from_devname(&device, devlink); |
140 | if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r)) | |
141 | return log_error_errno(r, "Failed to create sd-device object from %s: %m", devlink); | |
ed435031 ZJS |
142 | } |
143 | ||
030a0d79 | 144 | if (device) { |
54e61725 | 145 | if (device_is_processed(device) > 0) { |
030a0d79 LB |
146 | if (ret) |
147 | *ret = sd_device_ref(device); | |
148 | return 0; | |
149 | } | |
150 | /* We need either the sysname or the devlink for filtering */ | |
151 | assert_se(sd_device_get_sysname(device, &data.sysname) >= 0 || devlink); | |
152 | } | |
ed435031 ZJS |
153 | |
154 | /* Wait until the device is initialized, so that we can get access to the ID_PATH property */ | |
155 | ||
fc40bfa7 | 156 | r = sd_event_new(&event); |
ed435031 ZJS |
157 | if (r < 0) |
158 | return log_error_errno(r, "Failed to get default event: %m"); | |
159 | ||
160 | r = sd_device_monitor_new(&monitor); | |
161 | if (r < 0) | |
162 | return log_error_errno(r, "Failed to acquire monitor: %m"); | |
163 | ||
030a0d79 | 164 | if (device && !subsystem) { |
f822c5d5 YW |
165 | r = sd_device_get_subsystem(device, &subsystem); |
166 | if (r < 0 && r != -ENOENT) | |
167 | return log_device_error_errno(device, r, "Failed to get subsystem: %m"); | |
168 | } | |
169 | ||
170 | if (subsystem) { | |
171 | r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL); | |
172 | if (r < 0) | |
173 | return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem); | |
174 | } | |
ed435031 | 175 | |
17bf3c55 YW |
176 | _cleanup_free_ char *desc = NULL; |
177 | const char *sysname = NULL; | |
178 | if (device) | |
179 | (void) sd_device_get_sysname(device, &sysname); | |
180 | ||
181 | desc = strjoin(sysname ?: subsystem, devlink ? ":" : ":initialization", devlink); | |
182 | if (desc) | |
183 | (void) sd_device_monitor_set_description(monitor, desc); | |
184 | ||
ed435031 ZJS |
185 | r = sd_device_monitor_attach_event(monitor, event); |
186 | if (r < 0) | |
187 | return log_error_errno(r, "Failed to attach event to device monitor: %m"); | |
188 | ||
189 | r = sd_device_monitor_start(monitor, device_monitor_handler, &data); | |
190 | if (r < 0) | |
191 | return log_error_errno(r, "Failed to start device monitor: %m"); | |
192 | ||
4f89ce0c YW |
193 | if (timeout_usec != USEC_INFINITY) { |
194 | r = sd_event_add_time_relative( | |
195 | event, NULL, | |
196 | CLOCK_MONOTONIC, timeout_usec, 0, | |
bac0bfc1 | 197 | NULL, INT_TO_PTR(-ETIMEDOUT)); |
1b47436e YW |
198 | if (r < 0) |
199 | return log_error_errno(r, "Failed to add timeout event source: %m"); | |
200 | } | |
201 | ||
4f89ce0c | 202 | /* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized yet. */ |
030a0d79 | 203 | if (!device && devlink) { |
f81b3e90 YW |
204 | r = sd_device_new_from_devname(&device, devlink); |
205 | if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r)) | |
206 | return log_error_errno(r, "Failed to create sd-device object from %s: %m", devlink); | |
030a0d79 | 207 | } |
54e61725 | 208 | if (device && device_is_processed(device) > 0) { |
ed435031 ZJS |
209 | if (ret) |
210 | *ret = sd_device_ref(device); | |
211 | return 0; | |
212 | } | |
213 | ||
214 | r = sd_event_loop(event); | |
215 | if (r < 0) | |
1b47436e | 216 | return log_error_errno(r, "Failed to wait for device to be initialized: %m"); |
ed435031 ZJS |
217 | |
218 | if (ret) | |
219 | *ret = TAKE_PTR(data.device); | |
220 | return 0; | |
221 | } | |
90ba130f | 222 | |
4f89ce0c YW |
223 | int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret) { |
224 | return device_wait_for_initialization_internal(device, NULL, subsystem, timeout_usec, ret); | |
030a0d79 LB |
225 | } |
226 | ||
4f89ce0c YW |
227 | int device_wait_for_devlink(const char *devlink, const char *subsystem, usec_t timeout_usec, sd_device **ret) { |
228 | return device_wait_for_initialization_internal(NULL, devlink, subsystem, timeout_usec, ret); | |
030a0d79 LB |
229 | } |
230 | ||
90ba130f YW |
231 | int device_is_renaming(sd_device *dev) { |
232 | int r; | |
233 | ||
234 | assert(dev); | |
235 | ||
1e7c8fe4 | 236 | r = device_get_property_bool(dev, "ID_RENAMING"); |
b9daaedb | 237 | if (r == -ENOENT) |
1e7c8fe4 | 238 | return false; /* defaults to false */ |
90ba130f | 239 | |
1e7c8fe4 | 240 | return r; |
90ba130f | 241 | } |
a707c65b | 242 | |
3df2b455 | 243 | int device_is_processed(sd_device *dev) { |
f30a47c4 YW |
244 | int r; |
245 | ||
246 | assert(dev); | |
247 | ||
3df2b455 YW |
248 | /* sd_device_get_is_initialized() only checks if the udev database file exists. However, even if the |
249 | * database file exist, systemd-udevd may be still processing the device, e.g. when the udev rules | |
250 | * for the device have RUN tokens. See issue #30056. Hence, to check if the device is really | |
251 | * processed by systemd-udevd, we also need to read ID_PROCESSING property. */ | |
252 | ||
253 | r = sd_device_get_is_initialized(dev); | |
254 | if (r <= 0) | |
255 | return r; | |
256 | ||
f30a47c4 YW |
257 | r = device_get_property_bool(dev, "ID_PROCESSING"); |
258 | if (r == -ENOENT) | |
3df2b455 YW |
259 | return true; /* If the property does not exist, then it means that the device is processed. */ |
260 | if (r < 0) | |
261 | return r; | |
f30a47c4 | 262 | |
3df2b455 | 263 | return !r; |
f30a47c4 YW |
264 | } |
265 | ||
a1130022 LP |
266 | bool device_for_action(sd_device *dev, sd_device_action_t a) { |
267 | sd_device_action_t b; | |
a707c65b YW |
268 | |
269 | assert(dev); | |
270 | ||
a1130022 | 271 | if (a < 0) |
a707c65b YW |
272 | return false; |
273 | ||
a1130022 LP |
274 | if (sd_device_get_action(dev, &b) < 0) |
275 | return false; | |
276 | ||
277 | return a == b; | |
a707c65b | 278 | } |
aea3253e | 279 | |
b2d9e58f | 280 | void log_device_uevent(sd_device *device, const char *str) { |
a1130022 | 281 | sd_device_action_t action = _SD_DEVICE_ACTION_INVALID; |
e9162760 | 282 | sd_id128_t event_id = SD_ID128_NULL; |
b2d9e58f YW |
283 | uint64_t seqnum = 0; |
284 | ||
285 | if (!DEBUG_LOGGING) | |
286 | return; | |
287 | ||
a1130022 LP |
288 | (void) sd_device_get_seqnum(device, &seqnum); |
289 | (void) sd_device_get_action(device, &action); | |
e9162760 YW |
290 | (void) sd_device_get_trigger_uuid(device, &event_id); |
291 | log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s%s%s)", | |
b2d9e58f | 292 | strempty(str), isempty(str) ? "" : " ", |
e9162760 YW |
293 | seqnum, strna(device_action_to_string(action)), |
294 | sd_id128_is_null(event_id) ? "" : ", UUID=", | |
b7416360 | 295 | sd_id128_is_null(event_id) ? "" : SD_ID128_TO_UUID_STRING(event_id)); |
b2d9e58f YW |
296 | } |
297 | ||
5953d8b9 YW |
298 | size_t udev_replace_whitespace(const char *str, char *to, size_t len) { |
299 | bool is_space = false; | |
300 | size_t i, j; | |
301 | ||
302 | assert(str); | |
303 | assert(to); | |
304 | ||
305 | /* Copy from 'str' to 'to', while removing all leading and trailing whitespace, and replacing | |
306 | * each run of consecutive whitespace with a single underscore. The chars from 'str' are copied | |
307 | * up to the \0 at the end of the string, or at most 'len' chars. This appends \0 to 'to', at | |
308 | * the end of the copied characters. | |
309 | * | |
310 | * If 'len' chars are copied into 'to', the final \0 is placed at len+1 (i.e. 'to[len] = \0'), | |
311 | * so the 'to' buffer must have at least len+1 chars available. | |
312 | * | |
313 | * Note this may be called with 'str' == 'to', i.e. to replace whitespace in-place in a buffer. | |
314 | * This function can handle that situation. | |
315 | * | |
316 | * Note that only 'len' characters are read from 'str'. */ | |
317 | ||
318 | i = strspn(str, WHITESPACE); | |
319 | ||
320 | for (j = 0; j < len && i < len && str[i] != '\0'; i++) { | |
321 | if (isspace(str[i])) { | |
322 | is_space = true; | |
323 | continue; | |
324 | } | |
325 | ||
326 | if (is_space) { | |
327 | if (j + 1 >= len) | |
328 | break; | |
329 | ||
330 | to[j++] = '_'; | |
331 | is_space = false; | |
332 | } | |
333 | to[j++] = str[i]; | |
334 | } | |
335 | ||
336 | to[j] = '\0'; | |
337 | return j; | |
338 | } | |
393fcaf7 YW |
339 | |
340 | size_t udev_replace_chars(char *str, const char *allow) { | |
341 | size_t i = 0, replaced = 0; | |
342 | ||
343 | assert(str); | |
344 | ||
345 | /* allow chars in allow list, plain ascii, hex-escaping and valid utf8. */ | |
346 | ||
347 | while (str[i] != '\0') { | |
348 | int len; | |
349 | ||
350 | if (allow_listed_char_for_devnode(str[i], allow)) { | |
351 | i++; | |
352 | continue; | |
353 | } | |
354 | ||
355 | /* accept hex encoding */ | |
356 | if (str[i] == '\\' && str[i+1] == 'x') { | |
357 | i += 2; | |
358 | continue; | |
359 | } | |
360 | ||
361 | /* accept valid utf8 */ | |
f5fbe71d | 362 | len = utf8_encoded_valid_unichar(str + i, SIZE_MAX); |
393fcaf7 YW |
363 | if (len > 1) { |
364 | i += len; | |
365 | continue; | |
366 | } | |
367 | ||
368 | /* if space is allowed, replace whitespace with ordinary space */ | |
369 | if (isspace(str[i]) && allow && strchr(allow, ' ')) { | |
370 | str[i] = ' '; | |
371 | i++; | |
372 | replaced++; | |
373 | continue; | |
374 | } | |
375 | ||
376 | /* everything else is replaced with '_' */ | |
377 | str[i] = '_'; | |
378 | i++; | |
379 | replaced++; | |
380 | } | |
381 | return replaced; | |
382 | } | |
1223227f | 383 | |
bee33d05 YW |
384 | int udev_queue_is_empty(void) { |
385 | return access("/run/udev/queue", F_OK) < 0 ? | |
386 | (errno == ENOENT ? true : -errno) : false; | |
387 | } | |
388 | ||
a3df6937 YW |
389 | static int cached_udev_availability = -1; |
390 | ||
391 | void reset_cached_udev_availability(void) { | |
392 | cached_udev_availability = -1; | |
393 | } | |
f92c5bb1 | 394 | |
a3df6937 | 395 | bool udev_available(void) { |
f92c5bb1 YW |
396 | /* The service systemd-udevd is started only when /sys is read write. |
397 | * See systemd-udevd.service: ConditionPathIsReadWrite=/sys | |
398 | * Also, our container interface (http://systemd.io/CONTAINER_INTERFACE/) states that /sys must | |
399 | * be mounted in read-only mode in containers. */ | |
400 | ||
a3df6937 YW |
401 | if (cached_udev_availability >= 0) |
402 | return cached_udev_availability; | |
f92c5bb1 | 403 | |
a3df6937 | 404 | return (cached_udev_availability = (path_is_read_only_fs("/sys/") <= 0)); |
f92c5bb1 | 405 | } |
3cc7a9fd LP |
406 | |
407 | int device_get_vendor_string(sd_device *device, const char **ret) { | |
408 | int r; | |
409 | ||
410 | assert(device); | |
411 | ||
412 | FOREACH_STRING(field, "ID_VENDOR_FROM_DATABASE", "ID_VENDOR") { | |
413 | r = sd_device_get_property_value(device, field, ret); | |
414 | if (r != -ENOENT) | |
415 | return r; | |
416 | } | |
417 | ||
418 | return -ENOENT; | |
419 | } | |
420 | ||
421 | int device_get_model_string(sd_device *device, const char **ret) { | |
422 | int r; | |
423 | ||
424 | assert(device); | |
425 | ||
426 | FOREACH_STRING(field, "ID_MODEL_FROM_DATABASE", "ID_MODEL") { | |
427 | r = sd_device_get_property_value(device, field, ret); | |
428 | if (r != -ENOENT) | |
429 | return r; | |
430 | } | |
431 | ||
432 | return -ENOENT; | |
433 | } | |
f20ae7db DT |
434 | |
435 | int device_get_property_value_with_fallback( | |
436 | sd_device *device, | |
437 | const char *prop, | |
438 | Hashmap *extra_props, | |
439 | const char **ret) { | |
440 | const char *value; | |
441 | int r; | |
442 | ||
443 | assert(device); | |
444 | assert(prop); | |
445 | assert(ret); | |
446 | ||
447 | r = sd_device_get_property_value(device, prop, &value); | |
448 | if (r < 0) { | |
449 | if (r != -ENOENT) | |
450 | return r; | |
451 | ||
452 | value = hashmap_get(extra_props, prop); | |
453 | if (!value) | |
454 | return -ENOENT; | |
455 | } | |
456 | ||
457 | *ret = value; | |
458 | ||
459 | return 1; | |
460 | } |