blob: 7bce8abea917bb14c9522492cf266209f67c9112 [file] [log] [blame]
Denis Vlasenkod16950d2008-11-29 09:05:50 +00001/* vi: set sw=4 ts=4: */
2/*
3 * simple ACPI events listener
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenkod16950d2008-11-29 09:05:50 +00008 */
Denys Vlasenkodd898c92016-11-23 11:46:32 +01009//config:config ACPID
Denys Vlasenkob097a842018-12-28 03:20:17 +010010//config: bool "acpid (9 kb)"
Denys Vlasenkodd898c92016-11-23 11:46:32 +010011//config: default y
Samuel Thibault77216c32022-10-16 02:04:59 +020012//config: select PLATFORM_LINUX
Denys Vlasenkodd898c92016-11-23 11:46:32 +010013//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020014//config: acpid listens to ACPI events coming either in textual form from
15//config: /proc/acpi/event (though it is marked deprecated it is still widely
16//config: used and _is_ a standard) or in binary form from specified evdevs
17//config: (just use /dev/input/event*).
Denys Vlasenkodd898c92016-11-23 11:46:32 +010018//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020019//config: It parses the event to retrieve ACTION and a possible PARAMETER.
20//config: It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
21//config: (if the resulting path is a directory) or directly as an executable.
Denys Vlasenkodd898c92016-11-23 11:46:32 +010022//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020023//config: N.B. acpid relies on run-parts so have the latter installed.
Denys Vlasenkodd898c92016-11-23 11:46:32 +010024//config:
25//config:config FEATURE_ACPID_COMPAT
26//config: bool "Accept and ignore redundant options"
27//config: default y
28//config: depends on ACPID
29//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020030//config: Accept and ignore compatibility options -g -m -s -S -v.
Denys Vlasenkodd898c92016-11-23 11:46:32 +010031
32//applet:IF_ACPID(APPLET(acpid, BB_DIR_SBIN, BB_SUID_DROP))
33
34//kbuild:lib-$(CONFIG_ACPID) += acpid.o
Pere Orga5bc8c002011-04-11 03:29:49 +020035
36//usage:#define acpid_trivial_usage
Denys Vlasenko982fdaf2012-01-09 05:01:25 +010037//usage: "[-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]"
Pere Orga5bc8c002011-04-11 03:29:49 +020038//usage:#define acpid_full_usage "\n\n"
39//usage: "Listen to ACPI events and spawn specific helpers on event arrival\n"
Denys Vlasenko982fdaf2012-01-09 05:01:25 +010040//usage: "\n -d Log to stderr, not log file (implies -f)"
Pere Orga5bc8c002011-04-11 03:29:49 +020041//usage: "\n -f Run in foreground"
Denys Vlasenko982fdaf2012-01-09 05:01:25 +010042//usage: "\n -c DIR Config directory [/etc/acpi]"
43//usage: "\n -e FILE /proc event file [/proc/acpi/event]"
Pere Orga5bc8c002011-04-11 03:29:49 +020044//usage: "\n -l FILE Log file [/var/log/acpid.log]"
Norbert Lange79bd7c32020-02-14 22:16:11 +010045//usage: IF_FEATURE_PIDFILE(
46//usage: "\n -p FILE Pid file [" CONFIG_PID_FILE_PATH "/acpid.pid]"
47//usage: )
Pere Orga5bc8c002011-04-11 03:29:49 +020048//usage: "\n -a FILE Action file [/etc/acpid.conf]"
49//usage: "\n -M FILE Map file [/etc/acpi.map]"
50//usage: IF_FEATURE_ACPID_COMPAT(
51//usage: "\n\nAccept and ignore compatibility options -g -m -s -S -v"
52//usage: )
53//usage:
54//usage:#define acpid_example_usage
55//usage: "Without -e option, acpid uses all /dev/input/event* files\n"
56//usage: "# acpid\n"
57//usage: "# acpid -l /var/log/my-acpi-log\n"
58//usage: "# acpid -e /proc/acpi/event\n"
59
Denis Vlasenkod16950d2008-11-29 09:05:50 +000060#include "libbb.h"
Souf Ouedccb7a432010-09-26 12:40:05 +020061#include <syslog.h>
Denis Vlasenkod16950d2008-11-29 09:05:50 +000062#include <linux/input.h>
Denys Vlasenko6af732b2010-07-12 06:20:11 +020063
Denys Vlasenko14bd16a2011-07-08 08:49:40 +020064#ifndef EV_SW
65# define EV_SW 0x05
66#endif
67#ifndef EV_KEY
68# define EV_KEY 0x01
69#endif
70#ifndef SW_LID
71# define SW_LID 0x00
72#endif
73#ifndef SW_RFKILL_ALL
74# define SW_RFKILL_ALL 0x03
75#endif
76#ifndef KEY_POWER
77# define KEY_POWER 116 /* SC System Power Down */
78#endif
79#ifndef KEY_SLEEP
80# define KEY_SLEEP 142 /* SC System Sleep */
81#endif
82
Souf Ouedccb7a432010-09-26 12:40:05 +020083enum {
84 OPT_c = (1 << 0),
85 OPT_d = (1 << 1),
86 OPT_e = (1 << 2),
87 OPT_f = (1 << 3),
88 OPT_l = (1 << 4),
Denys Vlasenko26777aa2010-11-22 23:49:10 +010089 OPT_a = (1 << 5),
90 OPT_M = (1 << 6),
91 OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
Souf Ouedccb7a432010-09-26 12:40:05 +020092};
93
94struct acpi_event {
95 const char *s_type;
96 uint16_t n_type;
97 const char *s_code;
98 uint16_t n_code;
99 uint32_t value;
100 const char *desc;
101};
102
Denys Vlasenko965b7952020-11-30 13:03:03 +0100103static const struct acpi_event f_evt_tab[] ALIGN_PTR = {
Souf Ouedccb7a432010-09-26 12:40:05 +0200104 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
105 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
Eric Martin5345b8d2012-07-07 19:06:50 +0200106 { "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" },
Souf Ouedccb7a432010-09-26 12:40:05 +0200107};
108
109struct acpi_action {
110 const char *key;
111 const char *action;
112};
113
Denys Vlasenko965b7952020-11-30 13:03:03 +0100114static const struct acpi_action f_act_tab[] ALIGN_PTR = {
Souf Ouedccb7a432010-09-26 12:40:05 +0200115 { "PWRF", "PWRF/00000080" },
116 { "LID0", "LID/00000080" },
117};
118
119struct globals {
120 struct acpi_action *act_tab;
121 int n_act;
122 struct acpi_event *evt_tab;
123 int n_evt;
124} FIX_ALIASING;
125#define G (*ptr_to_globals)
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200126#define act_tab (G.act_tab)
Souf Ouedccb7a432010-09-26 12:40:05 +0200127#define n_act (G.n_act )
128#define evt_tab (G.evt_tab)
129#define n_evt (G.n_evt )
130#define INIT_G() do { \
131 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
132} while (0)
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000133
134/*
135 * acpid listens to ACPI events coming either in textual form
136 * from /proc/acpi/event (though it is marked deprecated,
137 * it is still widely used and _is_ a standard) or in binary form
138 * from specified evdevs (just use /dev/input/event*).
139 * It parses the event to retrieve ACTION and a possible PARAMETER.
140 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
141 * (if the resulting path is a directory) or directly.
142 * If the resulting path does not exist it logs it via perror
143 * and continues listening.
144 */
145
146static void process_event(const char *event)
147{
148 struct stat st;
149 char *handler = xasprintf("./%s", event);
150 const char *args[] = { "run-parts", handler, NULL };
151
Serj Kalichev1eafd442015-02-23 15:26:47 +0100152 // log the event
James Byrne69374872019-07-02 11:35:03 +0200153 bb_simple_error_msg(event);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000154
155 // spawn handler
156 // N.B. run-parts would require scripts to have #!/bin/sh
157 // handler is directory? -> use run-parts
158 // handler is file? -> run it directly
159 if (0 == stat(event, &st))
160 spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
161 else
162 bb_simple_perror_msg(event);
Souf Ouedccb7a432010-09-26 12:40:05 +0200163
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000164 free(handler);
165}
166
Souf Ouedccb7a432010-09-26 12:40:05 +0200167static const char *find_action(struct input_event *ev, const char *buf)
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000168{
Souf Ouedccb7a432010-09-26 12:40:05 +0200169 const char *action = NULL;
170 int i;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000171
Souf Ouedccb7a432010-09-26 12:40:05 +0200172 // map event
173 for (i = 0; i < n_evt; i++) {
174 if (ev) {
175 if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
176 action = evt_tab[i].desc;
177 break;
178 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000179 }
180
Souf Ouedccb7a432010-09-26 12:40:05 +0200181 if (buf) {
Denys Vlasenko8dff01d2015-03-12 17:48:34 +0100182 if (is_prefixed_with(evt_tab[i].desc, buf)) {
Souf Ouedccb7a432010-09-26 12:40:05 +0200183 action = evt_tab[i].desc;
184 break;
185 }
186 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000187 }
188
Souf Ouedccb7a432010-09-26 12:40:05 +0200189 // get action
190 if (action) {
191 for (i = 0; i < n_act; i++) {
192 if (strstr(action, act_tab[i].key)) {
193 action = act_tab[i].action;
194 break;
195 }
196 }
197 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000198
Souf Ouedccb7a432010-09-26 12:40:05 +0200199 return action;
200}
201
202static void parse_conf_file(const char *filename)
203{
204 parser_t *parser;
205 char *tokens[2];
206
207 parser = config_open2(filename, fopen_for_read);
208
209 if (parser) {
210 while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
211 act_tab = xrealloc_vector(act_tab, 1, n_act);
212 act_tab[n_act].key = xstrdup(tokens[0]);
213 act_tab[n_act].action = xstrdup(tokens[1]);
214 n_act++;
215 }
216 config_close(parser);
217 } else {
218 act_tab = (void*)f_act_tab;
219 n_act = ARRAY_SIZE(f_act_tab);
220 }
221}
222
223static void parse_map_file(const char *filename)
224{
225 parser_t *parser;
226 char *tokens[6];
227
228 parser = config_open2(filename, fopen_for_read);
229
230 if (parser) {
231 while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
232 evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
233 evt_tab[n_evt].s_type = xstrdup(tokens[0]);
234 evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
235 evt_tab[n_evt].s_code = xstrdup(tokens[2]);
236 evt_tab[n_evt].n_code = xatou16(tokens[3]);
237 evt_tab[n_evt].value = xatoi_positive(tokens[4]);
238 evt_tab[n_evt].desc = xstrdup(tokens[5]);
239 n_evt++;
240 }
241 config_close(parser);
242 } else {
243 evt_tab = (void*)f_evt_tab;
244 n_evt = ARRAY_SIZE(f_evt_tab);
245 }
246}
247
248/*
249 * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
250 */
251
252int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
253int acpid_main(int argc UNUSED_PARAM, char **argv)
254{
Souf Ouedccb7a432010-09-26 12:40:05 +0200255 int nfd;
256 int opts;
257 struct pollfd *pfd;
258 const char *opt_dir = "/etc/acpi";
259 const char *opt_input = "/dev/input/event";
260 const char *opt_logfile = "/var/log/acpid.log";
261 const char *opt_action = "/etc/acpid.conf";
262 const char *opt_map = "/etc/acpi.map";
263#if ENABLE_FEATURE_PIDFILE
Anthony G. Basile12677ac2012-12-10 14:49:39 -0500264 const char *opt_pidfile = CONFIG_PID_FILE_PATH "/acpid.pid";
Souf Ouedccb7a432010-09-26 12:40:05 +0200265#endif
266
267 INIT_G();
268
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200269 opts = getopt32(argv, "^"
270 "c:de:fl:a:M:"
271 IF_FEATURE_PIDFILE("p:")
272 IF_FEATURE_ACPID_COMPAT("g:m:s:S:v")
273 "\0"
274 "df:e--e",
Denys Vlasenko26777aa2010-11-22 23:49:10 +0100275 &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
276 IF_FEATURE_PIDFILE(, &opt_pidfile)
Souf Ouedccb7a432010-09-26 12:40:05 +0200277 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
278 );
279
280 if (!(opts & OPT_f)) {
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100281 /* No -f "Foreground", we go to background */
Souf Ouedccb7a432010-09-26 12:40:05 +0200282 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
283 }
284
285 if (!(opts & OPT_d)) {
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100286 /* No -d "Debug", we log to log file.
287 * This includes any output from children.
288 */
Serj Kalichev1eafd442015-02-23 15:26:47 +0100289 xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_APPEND), STDOUT_FILENO);
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100290 xdup2(STDOUT_FILENO, STDERR_FILENO);
291 /* Also, acpid's messages (but not children) will go to syslog too */
Souf Ouedccb7a432010-09-26 12:40:05 +0200292 openlog(applet_name, LOG_PID, LOG_DAEMON);
293 logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
Souf Ouedccb7a432010-09-26 12:40:05 +0200294 }
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100295 /* else: -d "Debug", log is not redirected */
Souf Ouedccb7a432010-09-26 12:40:05 +0200296
297 parse_conf_file(opt_action);
298 parse_map_file(opt_map);
299
300 xchdir(opt_dir);
301
Denys Vlasenkobbf1e3c2012-02-05 15:08:08 +0100302 /* We spawn children but don't wait for them. Prevent zombies: */
Souf Ouedccb7a432010-09-26 12:40:05 +0200303 bb_signals((1 << SIGCHLD), SIG_IGN);
Denys Vlasenkobbf1e3c2012-02-05 15:08:08 +0100304 // If you enable this, (1) explain why, (2)
305 // make sure while(poll) loop below is still interruptible
306 // by SIGTERM et al:
307 //bb_signals(BB_FATAL_SIGS, record_signo);
Souf Ouedccb7a432010-09-26 12:40:05 +0200308
309 pfd = NULL;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000310 nfd = 0;
Souf Ouedccb7a432010-09-26 12:40:05 +0200311 while (1) {
312 int fd;
313 char *dev_event;
314
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100315 dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
Souf Ouedccb7a432010-09-26 12:40:05 +0200316 fd = open(dev_event, O_RDONLY | O_NONBLOCK);
317 if (fd < 0) {
318 if (nfd == 0)
319 bb_simple_perror_msg_and_die(dev_event);
320 break;
321 }
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100322 free(dev_event);
Souf Ouedccb7a432010-09-26 12:40:05 +0200323 pfd = xrealloc_vector(pfd, 1, nfd);
324 pfd[nfd].fd = fd;
325 pfd[nfd].events = POLLIN;
326 nfd++;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000327 }
328
Souf Ouedccb7a432010-09-26 12:40:05 +0200329 write_pidfile(opt_pidfile);
330
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100331 while (safe_poll(pfd, nfd, -1) > 0) {
Souf Ouedccb7a432010-09-26 12:40:05 +0200332 int i;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000333 for (i = 0; i < nfd; i++) {
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100334 const char *event;
Souf Ouedccb7a432010-09-26 12:40:05 +0200335
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100336 if (!(pfd[i].revents & POLLIN)) {
337 if (pfd[i].revents == 0)
338 continue; /* this fd has nothing */
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000339
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100340 /* Likely POLLERR, POLLHUP, POLLNVAL.
341 * Do not listen on this fd anymore.
342 */
343 close(pfd[i].fd);
344 nfd--;
345 for (; i < nfd; i++)
346 pfd[i].fd = pfd[i + 1].fd;
347 break; /* do poll() again */
348 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000349
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100350 event = NULL;
Souf Ouedccb7a432010-09-26 12:40:05 +0200351 if (option_mask32 & OPT_e) {
352 char *buf;
353 int len;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000354
Denys Vlasenko80c5b682011-05-08 21:21:10 +0200355 buf = xmalloc_reads(pfd[i].fd, NULL);
Souf Ouedccb7a432010-09-26 12:40:05 +0200356 /* buf = "button/power PWRB 00000080 00000000" */
357 len = strlen(buf) - 9;
358 if (len >= 0)
359 buf[len] = '\0';
360 event = find_action(NULL, buf);
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100361 free(buf);
Souf Ouedccb7a432010-09-26 12:40:05 +0200362 } else {
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100363 struct input_event ev;
364
Souf Ouedccb7a432010-09-26 12:40:05 +0200365 if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
366 continue;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000367
Souf Ouedccb7a432010-09-26 12:40:05 +0200368 if (ev.value != 1 && ev.value != 0)
369 continue;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000370
Souf Ouedccb7a432010-09-26 12:40:05 +0200371 event = find_action(&ev, NULL);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000372 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000373 if (!event)
374 continue;
Denys Vlasenkobbf1e3c2012-02-05 15:08:08 +0100375 /* spawn event handler */
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000376 process_event(event);
377 }
378 }
379
380 if (ENABLE_FEATURE_CLEAN_UP) {
Denys Vlasenko982fdaf2012-01-09 05:01:25 +0100381 while (nfd--)
382 close(pfd[nfd].fd);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000383 free(pfd);
384 }
Souf Ouedccb7a432010-09-26 12:40:05 +0200385 remove_pidfile(opt_pidfile);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000386
387 return EXIT_SUCCESS;
388}