blob: c9eed2a7f4cd07b14309e5c21cb7c4fee9b5d14e [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 */
Pere Orga5bc8c002011-04-11 03:29:49 +02009
10//usage:#define acpid_trivial_usage
11//usage: "[-d] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]"
12//usage:#define acpid_full_usage "\n\n"
13//usage: "Listen to ACPI events and spawn specific helpers on event arrival\n"
14//usage: "\nOptions:"
15//usage: "\n -c DIR Config directory [/etc/acpi]"
16//usage: "\n -d Don't daemonize, (implies -f)"
17//usage: "\n -e FILE /proc event file [/proc/acpi/event]"
18//usage: "\n -f Run in foreground"
19//usage: "\n -l FILE Log file [/var/log/acpid.log]"
20//usage: "\n -p FILE Pid file [/var/run/acpid.pid]"
21//usage: "\n -a FILE Action file [/etc/acpid.conf]"
22//usage: "\n -M FILE Map file [/etc/acpi.map]"
23//usage: IF_FEATURE_ACPID_COMPAT(
24//usage: "\n\nAccept and ignore compatibility options -g -m -s -S -v"
25//usage: )
26//usage:
27//usage:#define acpid_example_usage
28//usage: "Without -e option, acpid uses all /dev/input/event* files\n"
29//usage: "# acpid\n"
30//usage: "# acpid -l /var/log/my-acpi-log\n"
31//usage: "# acpid -e /proc/acpi/event\n"
32
Denis Vlasenkod16950d2008-11-29 09:05:50 +000033#include "libbb.h"
Souf Ouedccb7a432010-09-26 12:40:05 +020034#include <syslog.h>
Denis Vlasenkod16950d2008-11-29 09:05:50 +000035#include <linux/input.h>
Denys Vlasenko6af732b2010-07-12 06:20:11 +020036
Souf Ouedccb7a432010-09-26 12:40:05 +020037enum {
38 OPT_c = (1 << 0),
39 OPT_d = (1 << 1),
40 OPT_e = (1 << 2),
41 OPT_f = (1 << 3),
42 OPT_l = (1 << 4),
Denys Vlasenko26777aa2010-11-22 23:49:10 +010043 OPT_a = (1 << 5),
44 OPT_M = (1 << 6),
45 OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
Souf Ouedccb7a432010-09-26 12:40:05 +020046};
47
48struct acpi_event {
49 const char *s_type;
50 uint16_t n_type;
51 const char *s_code;
52 uint16_t n_code;
53 uint32_t value;
54 const char *desc;
55};
56
57static const struct acpi_event f_evt_tab[] = {
58 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
59 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
60};
61
62struct acpi_action {
63 const char *key;
64 const char *action;
65};
66
67static const struct acpi_action f_act_tab[] = {
68 { "PWRF", "PWRF/00000080" },
69 { "LID0", "LID/00000080" },
70};
71
72struct globals {
73 struct acpi_action *act_tab;
74 int n_act;
75 struct acpi_event *evt_tab;
76 int n_evt;
77} FIX_ALIASING;
78#define G (*ptr_to_globals)
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020079#define act_tab (G.act_tab)
Souf Ouedccb7a432010-09-26 12:40:05 +020080#define n_act (G.n_act )
81#define evt_tab (G.evt_tab)
82#define n_evt (G.n_evt )
83#define INIT_G() do { \
84 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
85} while (0)
Denis Vlasenkod16950d2008-11-29 09:05:50 +000086
87/*
88 * acpid listens to ACPI events coming either in textual form
89 * from /proc/acpi/event (though it is marked deprecated,
90 * it is still widely used and _is_ a standard) or in binary form
91 * from specified evdevs (just use /dev/input/event*).
92 * It parses the event to retrieve ACTION and a possible PARAMETER.
93 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
94 * (if the resulting path is a directory) or directly.
95 * If the resulting path does not exist it logs it via perror
96 * and continues listening.
97 */
98
99static void process_event(const char *event)
100{
101 struct stat st;
102 char *handler = xasprintf("./%s", event);
103 const char *args[] = { "run-parts", handler, NULL };
104
105 // debug info
Souf Ouedccb7a432010-09-26 12:40:05 +0200106 if (option_mask32 & OPT_d) {
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000107 bb_error_msg("%s", event);
108 }
109
110 // spawn handler
111 // N.B. run-parts would require scripts to have #!/bin/sh
112 // handler is directory? -> use run-parts
113 // handler is file? -> run it directly
114 if (0 == stat(event, &st))
115 spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
116 else
117 bb_simple_perror_msg(event);
Souf Ouedccb7a432010-09-26 12:40:05 +0200118
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000119 free(handler);
120}
121
Souf Ouedccb7a432010-09-26 12:40:05 +0200122static const char *find_action(struct input_event *ev, const char *buf)
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000123{
Souf Ouedccb7a432010-09-26 12:40:05 +0200124 const char *action = NULL;
125 int i;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000126
Souf Ouedccb7a432010-09-26 12:40:05 +0200127 // map event
128 for (i = 0; i < n_evt; i++) {
129 if (ev) {
130 if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
131 action = evt_tab[i].desc;
132 break;
133 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000134 }
135
Souf Ouedccb7a432010-09-26 12:40:05 +0200136 if (buf) {
137 if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) {
138 action = evt_tab[i].desc;
139 break;
140 }
141 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000142 }
143
Souf Ouedccb7a432010-09-26 12:40:05 +0200144 // get action
145 if (action) {
146 for (i = 0; i < n_act; i++) {
147 if (strstr(action, act_tab[i].key)) {
148 action = act_tab[i].action;
149 break;
150 }
151 }
152 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000153
Souf Ouedccb7a432010-09-26 12:40:05 +0200154 return action;
155}
156
157static void parse_conf_file(const char *filename)
158{
159 parser_t *parser;
160 char *tokens[2];
161
162 parser = config_open2(filename, fopen_for_read);
163
164 if (parser) {
165 while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
166 act_tab = xrealloc_vector(act_tab, 1, n_act);
167 act_tab[n_act].key = xstrdup(tokens[0]);
168 act_tab[n_act].action = xstrdup(tokens[1]);
169 n_act++;
170 }
171 config_close(parser);
172 } else {
173 act_tab = (void*)f_act_tab;
174 n_act = ARRAY_SIZE(f_act_tab);
175 }
176}
177
178static void parse_map_file(const char *filename)
179{
180 parser_t *parser;
181 char *tokens[6];
182
183 parser = config_open2(filename, fopen_for_read);
184
185 if (parser) {
186 while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
187 evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
188 evt_tab[n_evt].s_type = xstrdup(tokens[0]);
189 evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
190 evt_tab[n_evt].s_code = xstrdup(tokens[2]);
191 evt_tab[n_evt].n_code = xatou16(tokens[3]);
192 evt_tab[n_evt].value = xatoi_positive(tokens[4]);
193 evt_tab[n_evt].desc = xstrdup(tokens[5]);
194 n_evt++;
195 }
196 config_close(parser);
197 } else {
198 evt_tab = (void*)f_evt_tab;
199 n_evt = ARRAY_SIZE(f_evt_tab);
200 }
201}
202
203/*
204 * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
205 */
206
207int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
208int acpid_main(int argc UNUSED_PARAM, char **argv)
209{
210 struct input_event ev;
211 int nfd;
212 int opts;
213 struct pollfd *pfd;
214 const char *opt_dir = "/etc/acpi";
215 const char *opt_input = "/dev/input/event";
216 const char *opt_logfile = "/var/log/acpid.log";
217 const char *opt_action = "/etc/acpid.conf";
218 const char *opt_map = "/etc/acpi.map";
219#if ENABLE_FEATURE_PIDFILE
220 const char *opt_pidfile = "/var/run/acpid.pid";
221#endif
222
223 INIT_G();
224
225 opt_complementary = "df:e--e";
Denys Vlasenko26777aa2010-11-22 23:49:10 +0100226 opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
227 &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
228 IF_FEATURE_PIDFILE(, &opt_pidfile)
Souf Ouedccb7a432010-09-26 12:40:05 +0200229 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
230 );
231
232 if (!(opts & OPT_f)) {
233 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
234 }
235
236 if (!(opts & OPT_d)) {
237 openlog(applet_name, LOG_PID, LOG_DAEMON);
238 logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
239 } else {
240 xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
241 }
242
243 parse_conf_file(opt_action);
244 parse_map_file(opt_map);
245
246 xchdir(opt_dir);
247
248 bb_signals((1 << SIGCHLD), SIG_IGN);
249 bb_signals(BB_FATAL_SIGS, record_signo);
250
251 pfd = NULL;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000252 nfd = 0;
Souf Ouedccb7a432010-09-26 12:40:05 +0200253 while (1) {
254 int fd;
255 char *dev_event;
256
257 dev_event = xasprintf((option_mask32 & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
258 fd = open(dev_event, O_RDONLY | O_NONBLOCK);
259 if (fd < 0) {
260 if (nfd == 0)
261 bb_simple_perror_msg_and_die(dev_event);
262 break;
263 }
264 pfd = xrealloc_vector(pfd, 1, nfd);
265 pfd[nfd].fd = fd;
266 pfd[nfd].events = POLLIN;
267 nfd++;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000268 }
269
Souf Ouedccb7a432010-09-26 12:40:05 +0200270 write_pidfile(opt_pidfile);
271
272 while (poll(pfd, nfd, -1) > 0) {
273 int i;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000274 for (i = 0; i < nfd; i++) {
Souf Ouedccb7a432010-09-26 12:40:05 +0200275 const char *event = NULL;
276
277 memset(&ev, 0, sizeof(ev));
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000278
279 if (!(pfd[i].revents & POLLIN))
280 continue;
281
Souf Ouedccb7a432010-09-26 12:40:05 +0200282 if (option_mask32 & OPT_e) {
283 char *buf;
284 int len;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000285
Souf Ouedccb7a432010-09-26 12:40:05 +0200286 buf = xmalloc_reads(pfd[i].fd, NULL, NULL);
287 /* buf = "button/power PWRB 00000080 00000000" */
288 len = strlen(buf) - 9;
289 if (len >= 0)
290 buf[len] = '\0';
291 event = find_action(NULL, buf);
292 } else {
293 if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
294 continue;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000295
Souf Ouedccb7a432010-09-26 12:40:05 +0200296 if (ev.value != 1 && ev.value != 0)
297 continue;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000298
Souf Ouedccb7a432010-09-26 12:40:05 +0200299 event = find_action(&ev, NULL);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000300 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000301 if (!event)
302 continue;
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000303 // spawn event handler
304 process_event(event);
305 }
306 }
307
308 if (ENABLE_FEATURE_CLEAN_UP) {
Souf Ouedccb7a432010-09-26 12:40:05 +0200309 while (nfd--) {
310 if (pfd[nfd].fd) {
311 close(pfd[nfd].fd);
312 }
313 }
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000314 free(pfd);
315 }
Souf Ouedccb7a432010-09-26 12:40:05 +0200316 remove_pidfile(opt_pidfile);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000317
318 return EXIT_SUCCESS;
319}