From 06795b02e386763ca919bb4cd03990b9036e2e0a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 5 Jan 2022 08:05:26 +0900 Subject: [PATCH 1/3] util: move on_ac_power() from util.c -> udev-util.c --- src/ac-power/ac-power.c | 2 +- src/basic/util.c | 60 ------------------------------------- src/basic/util.h | 2 -- src/shared/condition.c | 2 +- src/shared/udev-util.c | 62 +++++++++++++++++++++++++++++++++++++++ src/shared/udev-util.h | 2 ++ src/test/test-condition.c | 1 + 7 files changed, 67 insertions(+), 64 deletions(-) diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c index c4bfe7cb18..12379df344 100644 --- a/src/ac-power/ac-power.c +++ b/src/ac-power/ac-power.c @@ -3,7 +3,7 @@ #include #include "main-func.h" -#include "util.h" +#include "udev-util.h" static bool arg_verbose = false; diff --git a/src/basic/util.c b/src/basic/util.c index 3aecb22fc4..d7ef382737 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -6,7 +6,6 @@ #include "alloc-util.h" #include "build.h" -#include "dirent-util.h" #include "env-file.h" #include "env-util.h" #include "fd-util.h" @@ -115,65 +114,6 @@ void in_initrd_force(bool value) { saved_in_initrd = value; } -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - int r; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int device_fd = -1; - _cleanup_free_ char *contents = NULL; - unsigned v; - - device_fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC); - if (device_fd < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - r = read_virtual_file_at(device_fd, "type", SIZE_MAX, &contents, NULL); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - delete_trailing_chars(contents, NEWLINE); - - /* We assume every power source is AC, except for batteries. See - * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176 - * for defined power source types. Also see: - * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power */ - if (streq(contents, "Battery")) - continue; - - contents = mfree(contents); - - r = read_virtual_file_at(device_fd, "online", SIZE_MAX, &contents, NULL); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - delete_trailing_chars(contents, NEWLINE); - - r = safe_atou(contents, &v); - if (r < 0) - return r; - if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */ - found_online = true; - else - found_offline = true; - } - - return found_online || !found_offline; -} - int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; diff --git a/src/basic/util.h b/src/basic/util.h index f5434c9641..94804f28e3 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -20,8 +20,6 @@ int prot_from_flags(int flags) _const_; bool in_initrd(void); void in_initrd_force(bool value); -int on_ac_power(void); - /* Note: log2(0) == log2(1) == 0 here and below. */ #define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) diff --git a/src/shared/condition.c b/src/shared/condition.c index 44e26775db..68fbbf643a 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -50,9 +50,9 @@ #include "string-table.h" #include "string-util.h" #include "tomoyo-util.h" +#include "udev-util.h" #include "uid-alloc-range.h" #include "user-util.h" -#include "util.h" #include "virt.h" Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 27dfd11a6a..73ca886fb3 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -9,10 +10,12 @@ #include "device-nodes.h" #include "device-private.h" #include "device-util.h" +#include "dirent-util.h" #include "env-file.h" #include "errno-util.h" #include "escape.h" #include "fd-util.h" +#include "fileio.h" #include "log.h" #include "macro.h" #include "parse-util.h" @@ -579,3 +582,62 @@ int udev_queue_init(void) { return TAKE_FD(fd); } + +int on_ac_power(void) { + bool found_offline = false, found_online = false; + _cleanup_closedir_ DIR *d = NULL; + int r; + + d = opendir("/sys/class/power_supply"); + if (!d) + return errno == ENOENT ? true : -errno; + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_close_ int device_fd = -1; + _cleanup_free_ char *contents = NULL; + unsigned v; + + device_fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC); + if (device_fd < 0) { + if (IN_SET(errno, ENOENT, ENOTDIR)) + continue; + + return -errno; + } + + r = read_virtual_file_at(device_fd, "type", SIZE_MAX, &contents, NULL); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + delete_trailing_chars(contents, NEWLINE); + + /* We assume every power source is AC, except for batteries. See + * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176 + * for defined power source types. Also see: + * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power */ + if (streq(contents, "Battery")) + continue; + + contents = mfree(contents); + + r = read_virtual_file_at(device_fd, "online", SIZE_MAX, &contents, NULL); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + + delete_trailing_chars(contents, NEWLINE); + + r = safe_atou(contents, &v); + if (r < 0) + return r; + if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */ + found_online = true; + else + found_offline = true; + } + + return found_online || !found_offline; +} diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index 276686da80..8d21dc4364 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -53,6 +53,8 @@ int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, int udev_queue_is_empty(void); int udev_queue_init(void); +int on_ac_power(void); + #if HAVE_SYS_SDT_H /* Each trace point can have different number of additional arguments. Note that when the macro is used only diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 4b22784d17..c872a6c2b9 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -33,6 +33,7 @@ #include "strv.h" #include "tests.h" #include "tomoyo-util.h" +#include "udev-util.h" #include "uid-alloc-range.h" #include "user-util.h" #include "virt.h" From 01d4ad3bde9efbbc5856846fea328ee9bbcef87e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 5 Jan 2022 08:29:00 +0900 Subject: [PATCH 2/3] udev-util: re-implement on_ac_power() with sd-device --- src/shared/udev-util.c | 75 ++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 73ca886fb3..800493afe9 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -10,12 +9,10 @@ #include "device-nodes.h" #include "device-private.h" #include "device-util.h" -#include "dirent-util.h" #include "env-file.h" #include "errno-util.h" #include "escape.h" #include "fd-util.h" -#include "fileio.h" #include "log.h" #include "macro.h" #include "parse-util.h" @@ -584,60 +581,68 @@ int udev_queue_init(void) { } int on_ac_power(void) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; + sd_device *d; int r; - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int device_fd = -1; - _cleanup_free_ char *contents = NULL; + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "power_supply", true); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + const char *val; unsigned v; - device_fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC); - if (device_fd < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - r = read_virtual_file_at(device_fd, "type", SIZE_MAX, &contents, NULL); - if (r == -ENOENT) + r = sd_device_get_sysattr_value(d, "type", &val); + if (r < 0) { + log_device_debug_errno(d, r, "Failed to read 'type' sysfs attribute, ignoring: %m"); continue; - if (r < 0) - return r; - - delete_trailing_chars(contents, NEWLINE); + } /* We assume every power source is AC, except for batteries. See * https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176 * for defined power source types. Also see: * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power */ - if (streq(contents, "Battery")) + if (streq(val, "Battery")) { + log_device_debug(d, "The power supply is battery, ignoring."); continue; + } - contents = mfree(contents); - - r = read_virtual_file_at(device_fd, "online", SIZE_MAX, &contents, NULL); - if (r == -ENOENT) + r = sd_device_get_sysattr_value(d, "online", &val); + if (r < 0) { + log_device_debug_errno(d, r, "Failed to read 'online' sysfs attribute, ignoring: %m"); continue; - if (r < 0) - return r; + } - delete_trailing_chars(contents, NEWLINE); + r = safe_atou(val, &v); + if (r < 0) { + log_device_debug_errno(d, r, "Failed to parse 'online' attribute, ignoring: %m"); + continue; + } - r = safe_atou(contents, &v); - if (r < 0) - return r; if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */ found_online = true; else found_offline = true; + + log_device_debug(d, "The power supply is currently %s.", v > 0 ? "online" : "offline"); } + if (found_online) + log_debug("Found at least one online non-battery power supply, system is running on AC power."); + else if (!found_offline) + log_debug("Found no offline non-battery power supply, assuming system is running on AC power."); + else + log_debug("All non-battery power supplies are offline, assuming system is running with battery."); + return found_online || !found_offline; } From 795e86b4f1e8a1fd440f8c817621779c6aedbdb5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 5 Jan 2022 09:10:35 +0900 Subject: [PATCH 3/3] udev-util: ignore USB-C ports in power source mode when detecting system is running on AC power Fixes #21988. --- src/shared/udev-util.c | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 800493afe9..56c28773ce 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -580,6 +580,66 @@ int udev_queue_init(void) { return TAKE_FD(fd); } +static int device_is_power_sink(sd_device *device) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + bool found_source = false, found_sink = false; + sd_device *parent, *d; + int r; + + assert(device); + + /* USB-C power supply device has two power roles: source or sink. See, + * https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-typec */ + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "typec", true); + if (r < 0) + return r; + + r = sd_device_get_parent(device, &parent); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_parent(e, parent); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + const char *val; + + r = sd_device_get_sysattr_value(d, "power_role", &val); + if (r < 0) { + if (r != -ENOENT) + log_device_debug_errno(d, r, "Failed to read 'power_role' sysfs attribute, ignoring: %m"); + continue; + } + + if (strstr(val, "[source]")) { + found_source = true; + log_device_debug(d, "The USB type-C port is in power source mode."); + } else if (strstr(val, "[sink]")) { + found_sink = true; + log_device_debug(d, "The USB type-C port is in power sink mode."); + } + } + + if (found_sink) + log_device_debug(device, "The USB type-C device has at least one port in power sink mode."); + else if (!found_source) + log_device_debug(device, "The USB type-C device has no port in power source mode, assuming the device is in power sink mode."); + else + log_device_debug(device, "All USB type-C ports are in power source mode."); + + return found_sink || !found_source; +} + int on_ac_power(void) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; bool found_offline = false, found_online = false; @@ -617,6 +677,18 @@ int on_ac_power(void) { continue; } + /* Ignore USB-C power supply in source mode. See issue #21988. */ + if (streq(val, "USB")) { + r = device_is_power_sink(d); + if (r <= 0) { + if (r < 0) + log_device_debug_errno(d, r, "Failed to determine the current power role, ignoring: %m"); + else + log_device_debug(d, "USB power supply is in source mode, ignoring."); + continue; + } + } + r = sd_device_get_sysattr_value(d, "online", &val); if (r < 0) { log_device_debug_errno(d, r, "Failed to read 'online' sysfs attribute, ignoring: %m");