Linux: read generic sysfs batteries

Not all batteries entries in /sys/class/power_supply start with either
BAT or AC, but might have device specific names, e.g. CMB1.
Detect the types of those entries and parse them accordingly.

Closes: #881
Fixes: 3e70de64 ("Code clean up for reading battery info")
This commit is contained in:
Christian Göttsche 2021-12-08 20:27:54 +01:00 committed by BenBE
parent 63fafb4844
commit 1e94b92226
1 changed files with 48 additions and 15 deletions

View File

@ -70,6 +70,10 @@ in the source distribution for its full text.
#include "LibSensors.h" #include "LibSensors.h"
#endif #endif
#ifndef O_PATH
#define O_PATH 010000000 // declare for ancient glibc versions
#endif
#ifdef HAVE_LIBCAP #ifdef HAVE_LIBCAP
enum CapMode { enum CapMode {
@ -725,18 +729,47 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
uint64_t totalFull = 0; uint64_t totalFull = 0;
uint64_t totalRemain = 0; uint64_t totalRemain = 0;
struct dirent* dirEntry = NULL; const struct dirent* dirEntry;
while ((dirEntry = readdir(dir))) { while ((dirEntry = readdir(dir))) {
const char* entryName = dirEntry->d_name; const char* entryName = dirEntry->d_name;
if (String_startsWith(entryName, "BAT")) { #ifdef HAVE_OPENAT
char buffer[1024] = {0}; int entryFd = openat(dirfd(dir), entryName, O_DIRECTORY | O_PATH);
char filePath[256]; if (entryFd < 0)
xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName); continue;
#else
char entryFd[4096];
xSnprintf(entryFd, sizeof(entryFd), SYS_POWERSUPPLY_DIR "/%s", entryName);
#endif
ssize_t r = xReadfile(filePath, buffer, sizeof(buffer)); enum { AC, BAT } type;
if (String_startsWith(entryName, "BAT")) {
type = BAT;
} else if (String_startsWith(entryName, "AC")) {
type = AC;
} else {
char buffer[32];
ssize_t ret = xReadfileat(entryFd, "type", buffer, sizeof(buffer));
if (ret <= 0)
goto next;
/* drop optional trailing newlines */
for (char* buf = &buffer[(size_t)ret - 1]; *buf == '\n'; buf--)
*buf = '\0';
if (String_eq(buffer, "Battery"))
type = BAT;
else if (String_eq(buffer, "Mains"))
type = AC;
else
goto next;
}
if (type == BAT) {
char buffer[1024];
ssize_t r = xReadfileat(entryFd, "uevent", buffer, sizeof(buffer));
if (r < 0) if (r < 0)
continue; goto next;
bool full = false; bool full = false;
bool now = false; bool now = false;
@ -778,18 +811,15 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
if (!now && full && !isnan(capacityLevel)) if (!now && full && !isnan(capacityLevel))
totalRemain += capacityLevel * fullCharge; totalRemain += capacityLevel * fullCharge;
} else if (String_startsWith(entryName, "AC")) { } else if (type == AC) {
char buffer[2] = {0};
if (*isOnAC != AC_ERROR) if (*isOnAC != AC_ERROR)
continue; goto next;
char filePath[256]; char buffer[2];
xSnprintf(filePath, sizeof(filePath), SYS_POWERSUPPLY_DIR "/%s/online", entryName); ssize_t r = xReadfileat(entryFd, "online", buffer, sizeof(buffer));
ssize_t r = xReadfile(filePath, buffer, sizeof(buffer));
if (r < 1) { if (r < 1) {
*isOnAC = AC_ERROR; *isOnAC = AC_ERROR;
continue; goto next;
} }
if (buffer[0] == '0') if (buffer[0] == '0')
@ -797,6 +827,9 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
else if (buffer[0] == '1') else if (buffer[0] == '1')
*isOnAC = AC_PRESENT; *isOnAC = AC_PRESENT;
} }
next:
Compat_openatArgClose(entryFd);
} }
closedir(dir); closedir(dir);