mirror of
https://github.com/systemd/systemd.git
synced 2025-03-08 08:58:27 +03:00
Merge pull request #27924 from poettering/low-battery-tool
ac-power: expose low battery state via systemd-ac-power
This commit is contained in:
commit
7dc3c94245
@ -50,6 +50,14 @@
|
||||
<listitem><para>Show result as text instead of just returning success or failure.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--low</option></term>
|
||||
|
||||
<listitem><para>Instead of showing AC power state, show low battery state. In this case will return
|
||||
zero if all batteries are currently discharging and below 5% of maximum charge. Returns non-zero
|
||||
otherwise.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
@ -2,18 +2,24 @@
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "battery-util.h"
|
||||
#include "build.h"
|
||||
#include "main-func.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
static bool arg_verbose = false;
|
||||
|
||||
static enum {
|
||||
ACTION_AC_POWER,
|
||||
ACTION_LOW,
|
||||
} arg_action = ACTION_AC_POWER;
|
||||
|
||||
static void help(void) {
|
||||
printf("%s\n\n"
|
||||
"Report whether we are connected to an external power source.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" -v --verbose Show state as text\n",
|
||||
" -v --verbose Show state as text\n"
|
||||
" --low Checks if battery is discharing and low\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
@ -21,12 +27,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_LOW,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "low", no_argument, NULL, ARG_LOW },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -50,6 +58,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_verbose = true;
|
||||
break;
|
||||
|
||||
case ARG_LOW:
|
||||
arg_action = ACTION_LOW;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -78,9 +90,15 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = on_ac_power();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read AC status: %m");
|
||||
if (arg_action == ACTION_AC_POWER) {
|
||||
r = on_ac_power();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read AC status: %m");
|
||||
} else {
|
||||
r = battery_is_discharging_and_low();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read battery discharging + low status: %m");
|
||||
}
|
||||
|
||||
if (arg_verbose)
|
||||
puts(yes_no(r));
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "battery-util.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-locator.h"
|
||||
#include "bus-util.h"
|
||||
|
279
src/shared/battery-util.c
Normal file
279
src/shared/battery-util.c
Normal file
@ -0,0 +1,279 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "string-util.h"
|
||||
#include "battery-util.h"
|
||||
|
||||
#define BATTERY_LOW_CAPACITY_LEVEL 5
|
||||
|
||||
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://docs.kernel.org/admin-guide/abi-testing.html#abi-file-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;
|
||||
}
|
||||
|
||||
static bool battery_is_discharging(sd_device *d) {
|
||||
const char *val;
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
|
||||
r = sd_device_get_sysattr_value(d, "scope", &val);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
|
||||
} else if (streq(val, "Device")) {
|
||||
log_device_debug(d, "The power supply is a device battery, ignoring device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
r = device_get_sysattr_bool(d, "present");
|
||||
if (r < 0)
|
||||
log_device_debug_errno(d, r, "Failed to read 'present' sysfs attribute, assuming the battery is present: %m");
|
||||
else if (r == 0) {
|
||||
log_device_debug(d, "The battery is not present, ignoring the power supply.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Possible values: "Unknown", "Charging", "Discharging", "Not charging", "Full" */
|
||||
r = sd_device_get_sysattr_value(d, "status", &val);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(d, r, "Failed to read 'status' sysfs attribute, assuming the battery is discharging: %m");
|
||||
return true;
|
||||
}
|
||||
if (!streq(val, "Discharging")) {
|
||||
log_device_debug(d, "The battery status is '%s', assuming the battery is not used as a power source of this machine.", val);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int on_ac_power(void) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
bool found_ac_online = false, found_discharging_battery = false;
|
||||
sd_device *d;
|
||||
int r;
|
||||
|
||||
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, "power_supply", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
FOREACH_DEVICE(e, d) {
|
||||
/* See
|
||||
* https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
|
||||
* for defined power source types. Also see:
|
||||
* https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
|
||||
|
||||
const char *val;
|
||||
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 device: %m");
|
||||
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 device: %m");
|
||||
else
|
||||
log_device_debug(d, "USB power supply is in source mode, ignoring device.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (streq(val, "Battery")) {
|
||||
if (battery_is_discharging(d)) {
|
||||
found_discharging_battery = true;
|
||||
log_device_debug(d, "The power supply is a battery and currently discharging.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
r = device_get_sysattr_unsigned(d, "online", NULL);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(d, r, "Failed to query 'online' sysfs attribute, ignoring device: %m");
|
||||
continue;
|
||||
} else if (r > 0) /* At least 1 and 2 are defined as different types of 'online' */
|
||||
found_ac_online = true;
|
||||
|
||||
log_device_debug(d, "The power supply is currently %s.", r > 0 ? "online" : "offline");
|
||||
}
|
||||
|
||||
if (found_ac_online) {
|
||||
log_debug("Found at least one online non-battery power supply, system is running on AC.");
|
||||
return true;
|
||||
} else if (found_discharging_battery) {
|
||||
log_debug("Found at least one discharging battery and no online power sources, assuming system is running from battery.");
|
||||
return false;
|
||||
} else {
|
||||
log_debug("No power supply reported online and no discharging battery found, assuming system is running on AC.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the list of batteries */
|
||||
int battery_enumerator_new(sd_device_enumerator **ret) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = sd_device_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_subsystem(e, "power_supply", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_allow_uninitialized(e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "type", "Battery", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "present", "1", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "scope", "Device", /* match = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Battery percentage capacity fetched from capacity file and if in range 0-100 then returned */
|
||||
int battery_read_capacity_percentage(sd_device *dev) {
|
||||
int battery_capacity, r;
|
||||
|
||||
assert(dev);
|
||||
|
||||
r = device_get_sysattr_int(dev, "capacity", &battery_capacity);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(dev, r, "Failed to read/parse POWER_SUPPLY_CAPACITY: %m");
|
||||
|
||||
if (battery_capacity < 0 || battery_capacity > 100)
|
||||
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery capacity");
|
||||
|
||||
return battery_capacity;
|
||||
}
|
||||
|
||||
/* If a battery whose percentage capacity is <= 5% exists, and we're not on AC power, return success */
|
||||
int battery_is_discharging_and_low(void) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
bool unsure = false, found_low = false;
|
||||
sd_device *dev;
|
||||
int r;
|
||||
|
||||
/* We have not used battery capacity_level since value is set to full
|
||||
* or Normal in case ACPI is not working properly. In case of no battery
|
||||
* 0 will be returned and system will be suspended for 1st cycle then hibernated */
|
||||
|
||||
r = on_ac_power();
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check if the system is running on AC, assuming it is not: %m");
|
||||
if (r > 0)
|
||||
return false;
|
||||
|
||||
r = battery_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
|
||||
|
||||
FOREACH_DEVICE(e, dev) {
|
||||
int level;
|
||||
|
||||
level = battery_read_capacity_percentage(dev);
|
||||
if (level < 0) {
|
||||
unsure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (level > BATTERY_LOW_CAPACITY_LEVEL) /* Found a charged battery */
|
||||
return false;
|
||||
|
||||
found_low = true;
|
||||
}
|
||||
|
||||
/* If we found a battery whose state we couldn't read, don't assume we are in low battery state */
|
||||
if (unsure)
|
||||
return false;
|
||||
|
||||
/* Found no charged battery, but did find low batteries */
|
||||
if (found_low)
|
||||
return true;
|
||||
|
||||
/* Found neither charged nor low batteries? let's return that we aren't on low battery state */
|
||||
return false;
|
||||
}
|
11
src/shared/battery-util.h
Normal file
11
src/shared/battery-util.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
int on_ac_power(void);
|
||||
|
||||
int battery_is_discharging_and_low(void);
|
||||
|
||||
int battery_enumerator_new(sd_device_enumerator **ret);
|
||||
int battery_read_capacity_percentage(sd_device *dev);
|
@ -18,6 +18,7 @@
|
||||
#include "apparmor-util.h"
|
||||
#include "architecture.h"
|
||||
#include "audit-util.h"
|
||||
#include "battery-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "cgroup-util.h"
|
||||
@ -56,7 +57,6 @@
|
||||
#include "string-util.h"
|
||||
#include "tomoyo-util.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "uid-alloc-range.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
|
@ -6,6 +6,7 @@ shared_sources = files(
|
||||
'ask-password-api.c',
|
||||
'barrier.c',
|
||||
'base-filesystem.c',
|
||||
'battery-util.c',
|
||||
'binfmt-util.c',
|
||||
'bitmap.c',
|
||||
'blockdev-util.c',
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "battery-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "conf-parser.h"
|
||||
@ -34,17 +35,15 @@
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "path-util.h"
|
||||
#include "sleep-config.h"
|
||||
#include "siphash24.h"
|
||||
#include "sleep-config.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
#define BATTERY_LOW_CAPACITY_LEVEL 5
|
||||
#define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour"
|
||||
#define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63)
|
||||
#define SYS_ENTRY_RAW_FILE_TYPE1 "/sys/firmware/dmi/entries/1-0/raw"
|
||||
@ -128,41 +127,6 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the list of batteries */
|
||||
static int battery_enumerator_new(sd_device_enumerator **ret) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = sd_device_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_subsystem(e, "power_supply", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_allow_uninitialized(e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "type", "Battery", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "present", "1", /* match = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_enumerator_add_match_sysattr(e, "scope", "Device", /* match = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_capacity_by_name(Hashmap *capacities_by_name, const char *name) {
|
||||
void *p;
|
||||
|
||||
@ -176,49 +140,6 @@ int get_capacity_by_name(Hashmap *capacities_by_name, const char *name) {
|
||||
return PTR_TO_CAPACITY(p);
|
||||
}
|
||||
|
||||
/* Battery percentage capacity fetched from capacity file and if in range 0-100 then returned */
|
||||
static int read_battery_capacity_percentage(sd_device *dev) {
|
||||
int battery_capacity, r;
|
||||
|
||||
assert(dev);
|
||||
|
||||
r = device_get_sysattr_int(dev, "capacity", &battery_capacity);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(dev, r, "Failed to read/parse POWER_SUPPLY_CAPACITY: %m");
|
||||
|
||||
if (battery_capacity < 0 || battery_capacity > 100)
|
||||
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery capacity");
|
||||
|
||||
return battery_capacity;
|
||||
}
|
||||
|
||||
/* If a battery whose percentage capacity is <= 5% exists, and we're not on AC power, return success */
|
||||
int battery_is_discharging_and_low(void) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
sd_device *dev;
|
||||
int r;
|
||||
|
||||
/* We have not used battery capacity_level since value is set to full
|
||||
* or Normal in case ACPI is not working properly. In case of no battery
|
||||
* 0 will be returned and system will be suspended for 1st cycle then hibernated */
|
||||
|
||||
r = on_ac_power();
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check if the system is running on AC, assuming it is not: %m");
|
||||
if (r > 0)
|
||||
return false;
|
||||
|
||||
r = battery_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
|
||||
|
||||
FOREACH_DEVICE(e, dev)
|
||||
if (read_battery_capacity_percentage(dev) > BATTERY_LOW_CAPACITY_LEVEL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Store current capacity of each battery before suspension and timestamp */
|
||||
int fetch_batteries_capacity_by_name(Hashmap **ret) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
@ -241,7 +162,7 @@ int fetch_batteries_capacity_by_name(Hashmap **ret) {
|
||||
const char *battery_name;
|
||||
int battery_capacity;
|
||||
|
||||
battery_capacity = r = read_battery_capacity_percentage(dev);
|
||||
battery_capacity = r = battery_read_capacity_percentage(dev);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
|
@ -60,7 +60,6 @@ int find_hibernate_location(HibernateLocation **ret_hibernate_location);
|
||||
int can_sleep(SleepOperation operation);
|
||||
int can_sleep_disk(char **types);
|
||||
int can_sleep_state(char **types);
|
||||
int battery_is_discharging_and_low(void);
|
||||
int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret);
|
||||
int fetch_batteries_capacity_by_name(Hashmap **ret_current_capacity);
|
||||
int get_capacity_by_name(Hashmap *capacities_by_name, const char *name);
|
||||
|
@ -591,176 +591,6 @@ 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://docs.kernel.org/admin-guide/abi-testing.html#abi-file-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;
|
||||
}
|
||||
|
||||
static bool battery_is_discharging(sd_device *d) {
|
||||
const char *val;
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
|
||||
r = sd_device_get_sysattr_value(d, "scope", &val);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
|
||||
} else if (streq(val, "Device")) {
|
||||
log_device_debug(d, "The power supply is a device battery, ignoring device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
r = device_get_sysattr_bool(d, "present");
|
||||
if (r < 0)
|
||||
log_device_debug_errno(d, r, "Failed to read 'present' sysfs attribute, assuming the battery is present: %m");
|
||||
else if (r == 0) {
|
||||
log_device_debug(d, "The battery is not present, ignoring the power supply.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Possible values: "Unknown", "Charging", "Discharging", "Not charging", "Full" */
|
||||
r = sd_device_get_sysattr_value(d, "status", &val);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(d, r, "Failed to read 'status' sysfs attribute, assuming the battery is discharging: %m");
|
||||
return true;
|
||||
}
|
||||
if (!streq(val, "Discharging")) {
|
||||
log_device_debug(d, "The battery status is '%s', assuming the battery is not used as a power source of this machine.", val);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int on_ac_power(void) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
bool found_ac_online = false, found_discharging_battery = false;
|
||||
sd_device *d;
|
||||
int r;
|
||||
|
||||
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, "power_supply", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
FOREACH_DEVICE(e, d) {
|
||||
/* See
|
||||
* https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
|
||||
* for defined power source types. Also see:
|
||||
* https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
|
||||
|
||||
const char *val;
|
||||
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 device: %m");
|
||||
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 device: %m");
|
||||
else
|
||||
log_device_debug(d, "USB power supply is in source mode, ignoring device.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (streq(val, "Battery")) {
|
||||
if (battery_is_discharging(d)) {
|
||||
found_discharging_battery = true;
|
||||
log_device_debug(d, "The power supply is a battery and currently discharging.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
r = device_get_sysattr_unsigned(d, "online", NULL);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(d, r, "Failed to query 'online' sysfs attribute, ignoring device: %m");
|
||||
continue;
|
||||
} else if (r > 0) /* At least 1 and 2 are defined as different types of 'online' */
|
||||
found_ac_online = true;
|
||||
|
||||
log_device_debug(d, "The power supply is currently %s.", r > 0 ? "online" : "offline");
|
||||
}
|
||||
|
||||
if (found_ac_online) {
|
||||
log_debug("Found at least one online non-battery power supply, system is running on AC.");
|
||||
return true;
|
||||
} else if (found_discharging_battery) {
|
||||
log_debug("Found at least one discharging battery and no online power sources, assuming system is running from battery.");
|
||||
return false;
|
||||
} else {
|
||||
log_debug("No power supply reported online and no discharging battery found, assuming system is running on AC.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool udev_available(void) {
|
||||
static int cache = -1;
|
||||
|
||||
|
@ -55,8 +55,6 @@ bool devpath_conflict(const char *a, const char *b);
|
||||
int udev_queue_is_empty(void);
|
||||
int udev_queue_init(void);
|
||||
|
||||
int on_ac_power(void);
|
||||
|
||||
bool udev_available(void);
|
||||
|
||||
#if HAVE_SYS_SDT_H
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "sd-bus.h"
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "battery-util.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "build.h"
|
||||
#include "bus-error.h"
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "apparmor-util.h"
|
||||
#include "architecture.h"
|
||||
#include "audit-util.h"
|
||||
#include "battery-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "condition.h"
|
||||
#include "cpu-set-util.h"
|
||||
@ -39,7 +40,6 @@
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "tomoyo-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "uid-alloc-range.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user