From 88a198af3c20730f638a60aa699f4d5aa1650512 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Mon, 7 Oct 2024 10:48:55 +0200 Subject: [PATCH] powerd: use nlsysevent if possible instead of depending on devd and its socket, try to use nlsysevent instead. This makes powerd independant from devd. Approved by: des Reviewed by: des Differential Revission: https://reviews.freebsd.org/D46972 --- usr.sbin/powerd/powerd.c | 113 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 7 deletions(-) diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c index 8517b4f2bf0b..7be24e0c01ad 100644 --- a/usr.sbin/powerd/powerd.c +++ b/usr.sbin/powerd/powerd.c @@ -29,17 +29,26 @@ #include #include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include + #include #include #include #include #include +#include #include #include #include @@ -88,7 +97,8 @@ static int read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq); static int set_freq(int freq); static void acline_init(void); -static void acline_read(void); +static void acline_read(int rfds); +static bool netlink_init(void); static int devd_init(void); static void devd_close(void); static void handle_sigs(int sig); @@ -117,6 +127,7 @@ typedef enum { #ifdef USE_APM ac_apm, #endif + ac_acpi_netlink, } acline_mode_t; static acline_mode_t acline_mode; static acline_mode_t acline_mode_user = ac_none; @@ -124,6 +135,8 @@ static acline_mode_t acline_mode_user = ac_none; static int apm_fd = -1; #endif static int devd_pipe = -1; +static bool try_netlink = true; +static struct snl_state ss; #define DEVD_RETRY_INTERVAL 60 /* seconds */ static struct timeval tried_devd; @@ -325,9 +338,48 @@ acline_init(void) } } +struct nlevent { + const char *name; + const char *subsystem; + const char *type; + const char *data; +}; +#define _OUT(_field) offsetof(struct nlevent, _field) +static struct snl_attr_parser ap_nlevent_get[] = { + { .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string }, + { .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string }, +}; +#undef _OUT + +SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get); + static void -acline_read(void) +acline_read(int rfds) { + if (acline_mode == ac_acpi_netlink) { + struct nlmsghdr *hdr; + struct nlevent ne; + char *ptr; + int notify; + + if (rfds == 0) + return; + hdr = snl_read_message(&ss); + if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) { + memset(&ne, 0, sizeof(ne)); + if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne)) + return; + if (strcmp(ne.subsystem, "ACAD") != 0) + return; + if ((ptr = strstr(ne.data, "notify=")) != NULL && + sscanf(ptr, "notify=%x", ¬ify) == 1) + acline_status = (notify ? SRC_AC : SRC_BATTERY); + } + return; + + } if (acline_mode == ac_acpi_devd) { char buf[DEVCTL_MAXBUF], *ptr; ssize_t rlen; @@ -383,10 +435,20 @@ acline_read(void) #else if (acline_mode == ac_sysctl && (acline_mode_user == ac_none || - acline_mode_user == ac_acpi_devd)) { + acline_mode_user == ac_acpi_devd || + acline_mode_user == ac_acpi_netlink)) { #endif struct timeval now; + if (acline_mode_user != ac_acpi_devd && try_netlink) { + try_netlink = false; /* only try once */ + if (netlink_init()) { + if (vflag) + warnx("using netlink for AC line status"); + acline_mode = ac_acpi_netlink; + } + return; + } gettimeofday(&now, NULL); if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) { if (devd_init() >= 0) { @@ -399,6 +461,38 @@ acline_read(void) } } +bool +netlink_init(void) +{ + struct _getfamily_attrs attrs; + + if (modfind("nlsysevent") < 0) + kldload("nlsysevent"); + if (modfind("nlsysevent") < 0) + return (false); + + if (!snl_init(&ss, NETLINK_GENERIC)) + return (false); + + if (!snl_get_genl_family_info(&ss, "nlsysevent", &attrs)) + return (false); + + for (unsigned int i = 0; i < attrs.mcast_groups.num_groups; i++) { + if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name, + "ACPI") == 0) { + if (setsockopt(ss.fd, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, + &attrs.mcast_groups.groups[i]->mcast_grp_id, + sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id)) + == -1) { + warnx("Cannot subscribe to \"ACPI\""); + return (false); + } + } + } + return (true); +} + static int devd_init(void) { @@ -460,6 +554,8 @@ parse_acline_mode(char *arg, int ch) else if (strcmp(arg, "apm") == 0) acline_mode_user = ac_apm; #endif + else if (strcmp(arg, "netlink") == 0) + acline_mode_user = ac_acpi_netlink; else errx(1, "bad option: -%c %s", (char)ch, optarg); } @@ -485,7 +581,7 @@ main(int argc, char * argv[]) { struct timeval timeout; fd_set fdset; - int nfds; + int nfds, rfds; struct pidfh *pfh = NULL; const char *pidfile = NULL; int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load; @@ -638,7 +734,7 @@ main(int argc, char * argv[]) * If we are in adaptive mode and the current frequency is outside the * user-defined range, adjust it to be within the user-defined range. */ - acline_read(); + acline_read(0); if (acline_status > SRC_UNKNOWN) errx(1, "invalid AC line status %d", acline_status); if ((acline_status == SRC_AC && @@ -683,6 +779,9 @@ main(int argc, char * argv[]) if (devd_pipe >= 0) { FD_SET(devd_pipe, &fdset); nfds = devd_pipe + 1; + } else if (acline_mode == ac_acpi_netlink) { + FD_SET(ss.fd, &fdset); + nfds = ss.fd + 1; } else { nfds = 0; } @@ -694,7 +793,7 @@ main(int argc, char * argv[]) to = poll_ival * 4; timeout.tv_sec = to / 1000000; timeout.tv_usec = to % 1000000; - select(nfds, &fdset, NULL, &fdset, &timeout); + rfds = select(nfds, &fdset, NULL, &fdset, &timeout); /* If the user requested we quit, print some statistics. */ if (exit_requested) { @@ -706,7 +805,7 @@ main(int argc, char * argv[]) } /* Read the current AC status and record the mode. */ - acline_read(); + acline_read(rfds); switch (acline_status) { case SRC_AC: mode = mode_ac;