source: MondoRescue/branches/3.3/mindi-busybox/util-linux/acpid.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * simple ACPI events listener
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9
10//usage:#define acpid_trivial_usage
11//usage: "[-df] [-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: "\n -d Log to stderr, not log file (implies -f)"
15//usage: "\n -f Run in foreground"
16//usage: "\n -c DIR Config directory [/etc/acpi]"
17//usage: "\n -e FILE /proc event file [/proc/acpi/event]"
18//usage: "\n -l FILE Log file [/var/log/acpid.log]"
19//usage: "\n -p FILE Pid file [/var/run/acpid.pid]"
20//usage: "\n -a FILE Action file [/etc/acpid.conf]"
21//usage: "\n -M FILE Map file [/etc/acpi.map]"
22//usage: IF_FEATURE_ACPID_COMPAT(
23//usage: "\n\nAccept and ignore compatibility options -g -m -s -S -v"
24//usage: )
25//usage:
26//usage:#define acpid_example_usage
27//usage: "Without -e option, acpid uses all /dev/input/event* files\n"
28//usage: "# acpid\n"
29//usage: "# acpid -l /var/log/my-acpi-log\n"
30//usage: "# acpid -e /proc/acpi/event\n"
31
32#include "libbb.h"
33#include <syslog.h>
34#include <linux/input.h>
35
36#ifndef EV_SW
37# define EV_SW 0x05
38#endif
39#ifndef EV_KEY
40# define EV_KEY 0x01
41#endif
42#ifndef SW_LID
43# define SW_LID 0x00
44#endif
45#ifndef SW_RFKILL_ALL
46# define SW_RFKILL_ALL 0x03
47#endif
48#ifndef KEY_POWER
49# define KEY_POWER 116 /* SC System Power Down */
50#endif
51#ifndef KEY_SLEEP
52# define KEY_SLEEP 142 /* SC System Sleep */
53#endif
54
55enum {
56 OPT_c = (1 << 0),
57 OPT_d = (1 << 1),
58 OPT_e = (1 << 2),
59 OPT_f = (1 << 3),
60 OPT_l = (1 << 4),
61 OPT_a = (1 << 5),
62 OPT_M = (1 << 6),
63 OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
64};
65
66struct acpi_event {
67 const char *s_type;
68 uint16_t n_type;
69 const char *s_code;
70 uint16_t n_code;
71 uint32_t value;
72 const char *desc;
73};
74
75static const struct acpi_event f_evt_tab[] = {
76 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
77 { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
78 { "EV_SW", 0x05, "SW_LID", 0x00, 1, "button/lid LID0 00000080" },
79};
80
81struct acpi_action {
82 const char *key;
83 const char *action;
84};
85
86static const struct acpi_action f_act_tab[] = {
87 { "PWRF", "PWRF/00000080" },
88 { "LID0", "LID/00000080" },
89};
90
91struct globals {
92 struct acpi_action *act_tab;
93 int n_act;
94 struct acpi_event *evt_tab;
95 int n_evt;
96} FIX_ALIASING;
97#define G (*ptr_to_globals)
98#define act_tab (G.act_tab)
99#define n_act (G.n_act )
100#define evt_tab (G.evt_tab)
101#define n_evt (G.n_evt )
102#define INIT_G() do { \
103 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
104} while (0)
105
106/*
107 * acpid listens to ACPI events coming either in textual form
108 * from /proc/acpi/event (though it is marked deprecated,
109 * it is still widely used and _is_ a standard) or in binary form
110 * from specified evdevs (just use /dev/input/event*).
111 * It parses the event to retrieve ACTION and a possible PARAMETER.
112 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
113 * (if the resulting path is a directory) or directly.
114 * If the resulting path does not exist it logs it via perror
115 * and continues listening.
116 */
117
118static void process_event(const char *event)
119{
120 struct stat st;
121 char *handler = xasprintf("./%s", event);
122 const char *args[] = { "run-parts", handler, NULL };
123
124 // log the event
125 bb_error_msg("%s", event);
126
127 // spawn handler
128 // N.B. run-parts would require scripts to have #!/bin/sh
129 // handler is directory? -> use run-parts
130 // handler is file? -> run it directly
131 if (0 == stat(event, &st))
132 spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
133 else
134 bb_simple_perror_msg(event);
135
136 free(handler);
137}
138
139static const char *find_action(struct input_event *ev, const char *buf)
140{
141 const char *action = NULL;
142 int i;
143
144 // map event
145 for (i = 0; i < n_evt; i++) {
146 if (ev) {
147 if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
148 action = evt_tab[i].desc;
149 break;
150 }
151 }
152
153 if (buf) {
154 if (is_prefixed_with(evt_tab[i].desc, buf)) {
155 action = evt_tab[i].desc;
156 break;
157 }
158 }
159 }
160
161 // get action
162 if (action) {
163 for (i = 0; i < n_act; i++) {
164 if (strstr(action, act_tab[i].key)) {
165 action = act_tab[i].action;
166 break;
167 }
168 }
169 }
170
171 return action;
172}
173
174static void parse_conf_file(const char *filename)
175{
176 parser_t *parser;
177 char *tokens[2];
178
179 parser = config_open2(filename, fopen_for_read);
180
181 if (parser) {
182 while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
183 act_tab = xrealloc_vector(act_tab, 1, n_act);
184 act_tab[n_act].key = xstrdup(tokens[0]);
185 act_tab[n_act].action = xstrdup(tokens[1]);
186 n_act++;
187 }
188 config_close(parser);
189 } else {
190 act_tab = (void*)f_act_tab;
191 n_act = ARRAY_SIZE(f_act_tab);
192 }
193}
194
195static void parse_map_file(const char *filename)
196{
197 parser_t *parser;
198 char *tokens[6];
199
200 parser = config_open2(filename, fopen_for_read);
201
202 if (parser) {
203 while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
204 evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
205 evt_tab[n_evt].s_type = xstrdup(tokens[0]);
206 evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
207 evt_tab[n_evt].s_code = xstrdup(tokens[2]);
208 evt_tab[n_evt].n_code = xatou16(tokens[3]);
209 evt_tab[n_evt].value = xatoi_positive(tokens[4]);
210 evt_tab[n_evt].desc = xstrdup(tokens[5]);
211 n_evt++;
212 }
213 config_close(parser);
214 } else {
215 evt_tab = (void*)f_evt_tab;
216 n_evt = ARRAY_SIZE(f_evt_tab);
217 }
218}
219
220/*
221 * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
222 */
223
224int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
225int acpid_main(int argc UNUSED_PARAM, char **argv)
226{
227 int nfd;
228 int opts;
229 struct pollfd *pfd;
230 const char *opt_dir = "/etc/acpi";
231 const char *opt_input = "/dev/input/event";
232 const char *opt_logfile = "/var/log/acpid.log";
233 const char *opt_action = "/etc/acpid.conf";
234 const char *opt_map = "/etc/acpi.map";
235#if ENABLE_FEATURE_PIDFILE
236 const char *opt_pidfile = CONFIG_PID_FILE_PATH "/acpid.pid";
237#endif
238
239 INIT_G();
240
241 opt_complementary = "df:e--e";
242 opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
243 &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
244 IF_FEATURE_PIDFILE(, &opt_pidfile)
245 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
246 );
247
248 if (!(opts & OPT_f)) {
249 /* No -f "Foreground", we go to background */
250 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
251 }
252
253 if (!(opts & OPT_d)) {
254 /* No -d "Debug", we log to log file.
255 * This includes any output from children.
256 */
257 xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_APPEND), STDOUT_FILENO);
258 xdup2(STDOUT_FILENO, STDERR_FILENO);
259 /* Also, acpid's messages (but not children) will go to syslog too */
260 openlog(applet_name, LOG_PID, LOG_DAEMON);
261 logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
262 }
263 /* else: -d "Debug", log is not redirected */
264
265 parse_conf_file(opt_action);
266 parse_map_file(opt_map);
267
268 xchdir(opt_dir);
269
270 /* We spawn children but don't wait for them. Prevent zombies: */
271 bb_signals((1 << SIGCHLD), SIG_IGN);
272 // If you enable this, (1) explain why, (2)
273 // make sure while(poll) loop below is still interruptible
274 // by SIGTERM et al:
275 //bb_signals(BB_FATAL_SIGS, record_signo);
276
277 pfd = NULL;
278 nfd = 0;
279 while (1) {
280 int fd;
281 char *dev_event;
282
283 dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
284 fd = open(dev_event, O_RDONLY | O_NONBLOCK);
285 if (fd < 0) {
286 if (nfd == 0)
287 bb_simple_perror_msg_and_die(dev_event);
288 break;
289 }
290 free(dev_event);
291 pfd = xrealloc_vector(pfd, 1, nfd);
292 pfd[nfd].fd = fd;
293 pfd[nfd].events = POLLIN;
294 nfd++;
295 }
296
297 write_pidfile(opt_pidfile);
298
299 while (safe_poll(pfd, nfd, -1) > 0) {
300 int i;
301 for (i = 0; i < nfd; i++) {
302 const char *event;
303
304 if (!(pfd[i].revents & POLLIN)) {
305 if (pfd[i].revents == 0)
306 continue; /* this fd has nothing */
307
308 /* Likely POLLERR, POLLHUP, POLLNVAL.
309 * Do not listen on this fd anymore.
310 */
311 close(pfd[i].fd);
312 nfd--;
313 for (; i < nfd; i++)
314 pfd[i].fd = pfd[i + 1].fd;
315 break; /* do poll() again */
316 }
317
318 event = NULL;
319 if (option_mask32 & OPT_e) {
320 char *buf;
321 int len;
322
323 buf = xmalloc_reads(pfd[i].fd, NULL);
324 /* buf = "button/power PWRB 00000080 00000000" */
325 len = strlen(buf) - 9;
326 if (len >= 0)
327 buf[len] = '\0';
328 event = find_action(NULL, buf);
329 free(buf);
330 } else {
331 struct input_event ev;
332
333 if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
334 continue;
335
336 if (ev.value != 1 && ev.value != 0)
337 continue;
338
339 event = find_action(&ev, NULL);
340 }
341 if (!event)
342 continue;
343 /* spawn event handler */
344 process_event(event);
345 }
346 }
347
348 if (ENABLE_FEATURE_CLEAN_UP) {
349 while (nfd--)
350 close(pfd[nfd].fd);
351 free(pfd);
352 }
353 remove_pidfile(opt_pidfile);
354
355 return EXIT_SUCCESS;
356}
Note: See TracBrowser for help on using the repository browser.