]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/udev-util.c
Merge pull request #24515 from yuwata/dissect-timeout
[thirdparty/systemd.git] / src / shared / udev-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <sys/inotify.h>
6 #include <unistd.h>
7
8 #include "alloc-util.h"
9 #include "device-nodes.h"
10 #include "device-private.h"
11 #include "device-util.h"
12 #include "env-file.h"
13 #include "errno-util.h"
14 #include "escape.h"
15 #include "fd-util.h"
16 #include "id128-util.h"
17 #include "log.h"
18 #include "macro.h"
19 #include "parse-util.h"
20 #include "path-util.h"
21 #include "signal-util.h"
22 #include "socket-util.h"
23 #include "stat-util.h"
24 #include "string-table.h"
25 #include "string-util.h"
26 #include "strxcpyx.h"
27 #include "udev-util.h"
28 #include "utf8.h"
29
30 static const char* const resolve_name_timing_table[_RESOLVE_NAME_TIMING_MAX] = {
31 [RESOLVE_NAME_NEVER] = "never",
32 [RESOLVE_NAME_LATE] = "late",
33 [RESOLVE_NAME_EARLY] = "early",
34 };
35
36 DEFINE_STRING_TABLE_LOOKUP(resolve_name_timing, ResolveNameTiming);
37
38 int udev_parse_config_full(
39 unsigned *ret_children_max,
40 usec_t *ret_exec_delay_usec,
41 usec_t *ret_event_timeout_usec,
42 ResolveNameTiming *ret_resolve_name_timing,
43 int *ret_timeout_signal) {
44
45 _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL, *timeout_signal = NULL;
46 int r;
47
48 r = parse_env_file(NULL, "/etc/udev/udev.conf",
49 "udev_log", &log_val,
50 "children_max", &children_max,
51 "exec_delay", &exec_delay,
52 "event_timeout", &event_timeout,
53 "resolve_names", &resolve_names,
54 "timeout_signal", &timeout_signal);
55 if (r == -ENOENT)
56 return 0;
57 if (r < 0)
58 return r;
59
60 if (log_val) {
61 const char *log;
62 size_t n;
63
64 /* unquote */
65 n = strlen(log_val);
66 if (n >= 2 &&
67 ((log_val[0] == '"' && log_val[n-1] == '"') ||
68 (log_val[0] == '\'' && log_val[n-1] == '\''))) {
69 log_val[n - 1] = '\0';
70 log = log_val + 1;
71 } else
72 log = log_val;
73
74 /* we set the udev log level here explicitly, this is supposed
75 * to regulate the code in libudev/ and udev/. */
76 r = log_set_max_level_from_string(log);
77 if (r < 0)
78 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
79 "failed to set udev log level '%s', ignoring: %m", log);
80 }
81
82 if (ret_children_max && children_max) {
83 r = safe_atou(children_max, ret_children_max);
84 if (r < 0)
85 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
86 "failed to parse children_max=%s, ignoring: %m", children_max);
87 }
88
89 if (ret_exec_delay_usec && exec_delay) {
90 r = parse_sec(exec_delay, ret_exec_delay_usec);
91 if (r < 0)
92 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
93 "failed to parse exec_delay=%s, ignoring: %m", exec_delay);
94 }
95
96 if (ret_event_timeout_usec && event_timeout) {
97 r = parse_sec(event_timeout, ret_event_timeout_usec);
98 if (r < 0)
99 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
100 "failed to parse event_timeout=%s, ignoring: %m", event_timeout);
101 }
102
103 if (ret_resolve_name_timing && resolve_names) {
104 ResolveNameTiming t;
105
106 t = resolve_name_timing_from_string(resolve_names);
107 if (t < 0)
108 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
109 "failed to parse resolve_names=%s, ignoring.", resolve_names);
110 else
111 *ret_resolve_name_timing = t;
112 }
113
114 if (ret_timeout_signal && timeout_signal) {
115 r = signal_from_string(timeout_signal);
116 if (r < 0)
117 log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
118 "failed to parse timeout_signal=%s, ignoring: %m", timeout_signal);
119 else
120 *ret_timeout_signal = r;
121 }
122
123 return 0;
124 }
125
126 struct DeviceMonitorData {
127 const char *sysname;
128 const char *devlink;
129 sd_device *device;
130 };
131
132 static void device_monitor_data_free(struct DeviceMonitorData *d) {
133 assert(d);
134
135 sd_device_unref(d->device);
136 }
137
138 static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
139 struct DeviceMonitorData *data = userdata;
140 const char *sysname;
141
142 assert(device);
143 assert(data);
144 assert(data->sysname || data->devlink);
145 assert(!data->device);
146
147 /* Ignore REMOVE events here. We are waiting for initialization after all, not de-initialization. We
148 * might see a REMOVE event from an earlier use of the device (devices by the same name are recycled
149 * by the kernel after all), which we should not get confused by. After all we cannot distinguish use
150 * cycles of the devices, as the udev queue is entirely asynchronous.
151 *
152 * If we see a REMOVE event here for the use cycle we actually care about then we won't notice of
153 * course, but that should be OK, given the timeout logic used on the wait loop: this will be noticed
154 * by means of -ETIMEDOUT. Thus we won't notice immediately, but eventually, and that should be
155 * sufficient for an error path that should regularly not happen.
156 *
157 * (And yes, we only need to special case REMOVE. It's the only "negative" event type, where a device
158 * ceases to exist. All other event types are "positive": the device exists and is registered in the
159 * udev database, thus whenever we see the event, we can consider it initialized.) */
160 if (device_for_action(device, SD_DEVICE_REMOVE))
161 return 0;
162
163 if (data->sysname && sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname))
164 goto found;
165
166 if (data->devlink) {
167 const char *devlink;
168
169 FOREACH_DEVICE_DEVLINK(device, devlink)
170 if (path_equal(devlink, data->devlink))
171 goto found;
172
173 if (sd_device_get_devname(device, &devlink) >= 0 && path_equal(devlink, data->devlink))
174 goto found;
175 }
176
177 return 0;
178
179 found:
180 data->device = sd_device_ref(device);
181 return sd_event_exit(sd_device_monitor_get_event(monitor), 0);
182 }
183
184 static int device_wait_for_initialization_internal(
185 sd_device *_device,
186 const char *devlink,
187 const char *subsystem,
188 usec_t timeout_usec,
189 sd_device **ret) {
190
191 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
192 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
193 /* Ensure that if !_device && devlink, device gets unrefd on errors since it will be new */
194 _cleanup_(sd_device_unrefp) sd_device *device = sd_device_ref(_device);
195 _cleanup_(device_monitor_data_free) struct DeviceMonitorData data = {
196 .devlink = devlink,
197 };
198 int r;
199
200 assert(device || (subsystem && devlink));
201
202 /* Devlink might already exist, if it does get the device to use the sysname filtering */
203 if (!device && devlink) {
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);
207 }
208
209 if (device) {
210 if (sd_device_get_is_initialized(device) > 0) {
211 if (ret)
212 *ret = sd_device_ref(device);
213 return 0;
214 }
215 /* We need either the sysname or the devlink for filtering */
216 assert_se(sd_device_get_sysname(device, &data.sysname) >= 0 || devlink);
217 }
218
219 /* Wait until the device is initialized, so that we can get access to the ID_PATH property */
220
221 r = sd_event_new(&event);
222 if (r < 0)
223 return log_error_errno(r, "Failed to get default event: %m");
224
225 r = sd_device_monitor_new(&monitor);
226 if (r < 0)
227 return log_error_errno(r, "Failed to acquire monitor: %m");
228
229 if (device && !subsystem) {
230 r = sd_device_get_subsystem(device, &subsystem);
231 if (r < 0 && r != -ENOENT)
232 return log_device_error_errno(device, r, "Failed to get subsystem: %m");
233 }
234
235 if (subsystem) {
236 r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL);
237 if (r < 0)
238 return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem);
239 }
240
241 r = sd_device_monitor_attach_event(monitor, event);
242 if (r < 0)
243 return log_error_errno(r, "Failed to attach event to device monitor: %m");
244
245 r = sd_device_monitor_start(monitor, device_monitor_handler, &data);
246 if (r < 0)
247 return log_error_errno(r, "Failed to start device monitor: %m");
248
249 if (timeout_usec != USEC_INFINITY) {
250 r = sd_event_add_time_relative(
251 event, NULL,
252 CLOCK_MONOTONIC, timeout_usec, 0,
253 NULL, INT_TO_PTR(-ETIMEDOUT));
254 if (r < 0)
255 return log_error_errno(r, "Failed to add timeout event source: %m");
256 }
257
258 /* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized yet. */
259 if (!device && devlink) {
260 r = sd_device_new_from_devname(&device, devlink);
261 if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
262 return log_error_errno(r, "Failed to create sd-device object from %s: %m", devlink);
263 }
264 if (device && sd_device_get_is_initialized(device) > 0) {
265 if (ret)
266 *ret = sd_device_ref(device);
267 return 0;
268 }
269
270 r = sd_event_loop(event);
271 if (r < 0)
272 return log_error_errno(r, "Failed to wait for device to be initialized: %m");
273
274 if (ret)
275 *ret = TAKE_PTR(data.device);
276 return 0;
277 }
278
279 int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret) {
280 return device_wait_for_initialization_internal(device, NULL, subsystem, timeout_usec, ret);
281 }
282
283 int device_wait_for_devlink(const char *devlink, const char *subsystem, usec_t timeout_usec, sd_device **ret) {
284 return device_wait_for_initialization_internal(NULL, devlink, subsystem, timeout_usec, ret);
285 }
286
287 int device_is_renaming(sd_device *dev) {
288 int r;
289
290 assert(dev);
291
292 r = sd_device_get_property_value(dev, "ID_RENAMING", NULL);
293 if (r == -ENOENT)
294 return false;
295 if (r < 0)
296 return r;
297
298 return true;
299 }
300
301 bool device_for_action(sd_device *dev, sd_device_action_t a) {
302 sd_device_action_t b;
303
304 assert(dev);
305
306 if (a < 0)
307 return false;
308
309 if (sd_device_get_action(dev, &b) < 0)
310 return false;
311
312 return a == b;
313 }
314
315 void log_device_uevent(sd_device *device, const char *str) {
316 sd_device_action_t action = _SD_DEVICE_ACTION_INVALID;
317 sd_id128_t event_id = SD_ID128_NULL;
318 uint64_t seqnum = 0;
319
320 if (!DEBUG_LOGGING)
321 return;
322
323 (void) sd_device_get_seqnum(device, &seqnum);
324 (void) sd_device_get_action(device, &action);
325 (void) sd_device_get_trigger_uuid(device, &event_id);
326 log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s%s%s)",
327 strempty(str), isempty(str) ? "" : " ",
328 seqnum, strna(device_action_to_string(action)),
329 sd_id128_is_null(event_id) ? "" : ", UUID=",
330 sd_id128_is_null(event_id) ? "" : SD_ID128_TO_UUID_STRING(event_id));
331 }
332
333 int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
334 char *i, *j;
335 bool is_escaped;
336
337 /* value must be double quotated */
338 is_escaped = str[0] == 'e';
339 str += is_escaped;
340 if (str[0] != '"')
341 return -EINVAL;
342 str++;
343
344 if (!is_escaped) {
345 /* unescape double quotation '\"'->'"' */
346 for (i = j = str; *i != '"'; i++, j++) {
347 if (*i == '\0')
348 return -EINVAL;
349 if (i[0] == '\\' && i[1] == '"')
350 i++;
351 *j = *i;
352 }
353 j[0] = '\0';
354 } else {
355 _cleanup_free_ char *unescaped = NULL;
356 ssize_t l;
357
358 /* find the end position of value */
359 for (i = str; *i != '"'; i++) {
360 if (i[0] == '\\')
361 i++;
362 if (*i == '\0')
363 return -EINVAL;
364 }
365 i[0] = '\0';
366
367 l = cunescape_length(str, i - str, 0, &unescaped);
368 if (l < 0)
369 return l;
370
371 assert(l <= i - str);
372 memcpy(str, unescaped, l + 1);
373 }
374
375 *ret_value = str;
376 *ret_endpos = i + 1;
377 return 0;
378 }
379
380 size_t udev_replace_whitespace(const char *str, char *to, size_t len) {
381 bool is_space = false;
382 size_t i, j;
383
384 assert(str);
385 assert(to);
386
387 /* Copy from 'str' to 'to', while removing all leading and trailing whitespace, and replacing
388 * each run of consecutive whitespace with a single underscore. The chars from 'str' are copied
389 * up to the \0 at the end of the string, or at most 'len' chars. This appends \0 to 'to', at
390 * the end of the copied characters.
391 *
392 * If 'len' chars are copied into 'to', the final \0 is placed at len+1 (i.e. 'to[len] = \0'),
393 * so the 'to' buffer must have at least len+1 chars available.
394 *
395 * Note this may be called with 'str' == 'to', i.e. to replace whitespace in-place in a buffer.
396 * This function can handle that situation.
397 *
398 * Note that only 'len' characters are read from 'str'. */
399
400 i = strspn(str, WHITESPACE);
401
402 for (j = 0; j < len && i < len && str[i] != '\0'; i++) {
403 if (isspace(str[i])) {
404 is_space = true;
405 continue;
406 }
407
408 if (is_space) {
409 if (j + 1 >= len)
410 break;
411
412 to[j++] = '_';
413 is_space = false;
414 }
415 to[j++] = str[i];
416 }
417
418 to[j] = '\0';
419 return j;
420 }
421
422 size_t udev_replace_ifname(char *str) {
423 size_t replaced = 0;
424
425 assert(str);
426
427 /* See ifname_valid_full(). */
428
429 for (char *p = str; *p != '\0'; p++)
430 if (!ifname_valid_char(*p)) {
431 *p = '_';
432 replaced++;
433 }
434
435 return replaced;
436 }
437
438 size_t udev_replace_chars(char *str, const char *allow) {
439 size_t i = 0, replaced = 0;
440
441 assert(str);
442
443 /* allow chars in allow list, plain ascii, hex-escaping and valid utf8. */
444
445 while (str[i] != '\0') {
446 int len;
447
448 if (allow_listed_char_for_devnode(str[i], allow)) {
449 i++;
450 continue;
451 }
452
453 /* accept hex encoding */
454 if (str[i] == '\\' && str[i+1] == 'x') {
455 i += 2;
456 continue;
457 }
458
459 /* accept valid utf8 */
460 len = utf8_encoded_valid_unichar(str + i, SIZE_MAX);
461 if (len > 1) {
462 i += len;
463 continue;
464 }
465
466 /* if space is allowed, replace whitespace with ordinary space */
467 if (isspace(str[i]) && allow && strchr(allow, ' ')) {
468 str[i] = ' ';
469 i++;
470 replaced++;
471 continue;
472 }
473
474 /* everything else is replaced with '_' */
475 str[i] = '_';
476 i++;
477 replaced++;
478 }
479 return replaced;
480 }
481
482 int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) {
483 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
484 _cleanup_free_ char *temp = NULL;
485 char *subsys, *sysname, *attr;
486 const char *val;
487 int r;
488
489 assert(string);
490 assert(result);
491
492 /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
493
494 if (string[0] != '[')
495 return -EINVAL;
496
497 temp = strdup(string);
498 if (!temp)
499 return -ENOMEM;
500
501 subsys = &temp[1];
502
503 sysname = strchr(subsys, '/');
504 if (!sysname)
505 return -EINVAL;
506 sysname[0] = '\0';
507 sysname = &sysname[1];
508
509 attr = strchr(sysname, ']');
510 if (!attr)
511 return -EINVAL;
512 attr[0] = '\0';
513 attr = &attr[1];
514 if (attr[0] == '/')
515 attr = &attr[1];
516 if (attr[0] == '\0')
517 attr = NULL;
518
519 if (read_value && !attr)
520 return -EINVAL;
521
522 r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname);
523 if (r < 0)
524 return r;
525
526 if (read_value) {
527 r = sd_device_get_sysattr_value(dev, attr, &val);
528 if (r < 0 && !ERRNO_IS_PRIVILEGE(r) && r != -ENOENT)
529 return r;
530 if (r >= 0)
531 strscpy(result, maxsize, val);
532 else
533 result[0] = '\0';
534 log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result);
535 } else {
536 r = sd_device_get_syspath(dev, &val);
537 if (r < 0)
538 return r;
539
540 strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL);
541 log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result);
542 }
543 return 0;
544 }
545
546 bool devpath_conflict(const char *a, const char *b) {
547 /* This returns true when two paths are equivalent, or one is a child of another. */
548
549 if (!a || !b)
550 return false;
551
552 for (; *a != '\0' && *b != '\0'; a++, b++)
553 if (*a != *b)
554 return false;
555
556 return *a == '/' || *b == '/' || *a == *b;
557 }
558
559 int udev_queue_is_empty(void) {
560 return access("/run/udev/queue", F_OK) < 0 ?
561 (errno == ENOENT ? true : -errno) : false;
562 }
563
564 int udev_queue_init(void) {
565 _cleanup_close_ int fd = -1;
566
567 fd = inotify_init1(IN_CLOEXEC);
568 if (fd < 0)
569 return -errno;
570
571 if (inotify_add_watch(fd, "/run/udev" , IN_DELETE) < 0)
572 return -errno;
573
574 return TAKE_FD(fd);
575 }
576
577 static int device_is_power_sink(sd_device *device) {
578 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
579 bool found_source = false, found_sink = false;
580 sd_device *parent, *d;
581 int r;
582
583 assert(device);
584
585 /* USB-C power supply device has two power roles: source or sink. See,
586 * https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-typec */
587
588 r = sd_device_enumerator_new(&e);
589 if (r < 0)
590 return r;
591
592 r = sd_device_enumerator_allow_uninitialized(e);
593 if (r < 0)
594 return r;
595
596 r = sd_device_enumerator_add_match_subsystem(e, "typec", true);
597 if (r < 0)
598 return r;
599
600 r = sd_device_get_parent(device, &parent);
601 if (r < 0)
602 return r;
603
604 r = sd_device_enumerator_add_match_parent(e, parent);
605 if (r < 0)
606 return r;
607
608 FOREACH_DEVICE(e, d) {
609 const char *val;
610
611 r = sd_device_get_sysattr_value(d, "power_role", &val);
612 if (r < 0) {
613 if (r != -ENOENT)
614 log_device_debug_errno(d, r, "Failed to read 'power_role' sysfs attribute, ignoring: %m");
615 continue;
616 }
617
618 if (strstr(val, "[source]")) {
619 found_source = true;
620 log_device_debug(d, "The USB type-C port is in power source mode.");
621 } else if (strstr(val, "[sink]")) {
622 found_sink = true;
623 log_device_debug(d, "The USB type-C port is in power sink mode.");
624 }
625 }
626
627 if (found_sink)
628 log_device_debug(device, "The USB type-C device has at least one port in power sink mode.");
629 else if (!found_source)
630 log_device_debug(device, "The USB type-C device has no port in power source mode, assuming the device is in power sink mode.");
631 else
632 log_device_debug(device, "All USB type-C ports are in power source mode.");
633
634 return found_sink || !found_source;
635 }
636
637 int on_ac_power(void) {
638 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
639 bool found_ac_online = false, found_battery = false;
640 sd_device *d;
641 int r;
642
643 r = sd_device_enumerator_new(&e);
644 if (r < 0)
645 return r;
646
647 r = sd_device_enumerator_allow_uninitialized(e);
648 if (r < 0)
649 return r;
650
651 r = sd_device_enumerator_add_match_subsystem(e, "power_supply", true);
652 if (r < 0)
653 return r;
654
655 FOREACH_DEVICE(e, d) {
656 /* See
657 * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
658 * for defined power source types. Also see:
659 * https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
660
661 const char *val;
662 r = sd_device_get_sysattr_value(d, "type", &val);
663 if (r < 0) {
664 log_device_debug_errno(d, r, "Failed to read 'type' sysfs attribute, ignoring device: %m");
665 continue;
666 }
667
668 /* Ignore USB-C power supply in source mode. See issue #21988. */
669 if (streq(val, "USB")) {
670 r = device_is_power_sink(d);
671 if (r <= 0) {
672 if (r < 0)
673 log_device_debug_errno(d, r, "Failed to determine the current power role, ignoring device: %m");
674 else
675 log_device_debug(d, "USB power supply is in source mode, ignoring device.");
676 continue;
677 }
678 }
679
680 if (streq(val, "Battery")) {
681 r = sd_device_get_sysattr_value(d, "scope", &val);
682 if (r < 0) {
683 if (r != -ENOENT)
684 log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
685 } else if (streq(val, "Device")) {
686 log_device_debug(d, "The power supply is a device battery, ignoring device.");
687 continue;
688 }
689
690 found_battery = true;
691 log_device_debug(d, "The power supply is battery.");
692 continue;
693 }
694
695 r = device_get_sysattr_unsigned(d, "online", NULL);
696 if (r < 0) {
697 log_device_debug_errno(d, r, "Failed to query 'online' sysfs attribute, ignoring device: %m");
698 continue;
699 } else if (r > 0) /* At least 1 and 2 are defined as different types of 'online' */
700 found_ac_online = true;
701
702 log_device_debug(d, "The power supply is currently %s.", r > 0 ? "online" : "offline");
703 }
704
705 if (found_ac_online) {
706 log_debug("Found at least one online non-battery power supply, system is running on AC.");
707 return true;
708 } else if (found_battery) {
709 log_debug("Found battery and no online power sources, assuming system is running from battery.");
710 return false;
711 } else {
712 log_debug("No power supply reported online and no battery, assuming system is running on AC.");
713 return true;
714 }
715 }
716
717 bool udev_available(void) {
718 static int cache = -1;
719
720 /* The service systemd-udevd is started only when /sys is read write.
721 * See systemd-udevd.service: ConditionPathIsReadWrite=/sys
722 * Also, our container interface (http://systemd.io/CONTAINER_INTERFACE/) states that /sys must
723 * be mounted in read-only mode in containers. */
724
725 if (cache >= 0)
726 return cache;
727
728 return (cache = (path_is_read_only_fs("/sys/") <= 0));
729 }