blob: 11a9f624a17511ad04691cca159242dda6411f05 [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 *
7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
8 */
9#include "libbb.h"
10
11#include <linux/input.h>
Denys Vlasenko6af732b2010-07-12 06:20:11 +020012#ifndef EV_SW
13# define EV_SW 0x05
Denis Vlasenkod16950d2008-11-29 09:05:50 +000014#endif
Denys Vlasenko6af732b2010-07-12 06:20:11 +020015#ifndef EV_KEY
16# define EV_KEY 0x01
17#endif
18#ifndef SW_LID
19# define SW_LID 0x00
20#endif
21#ifndef SW_RFKILL_ALL
22# define SW_RFKILL_ALL 0x03
23#endif
24#ifndef KEY_POWER
25# define KEY_POWER 116 /* SC System Power Down */
26#endif
27#ifndef KEY_SLEEP
28# define KEY_SLEEP 142 /* SC System Sleep */
29#endif
30
Denis Vlasenkod16950d2008-11-29 09:05:50 +000031
32/*
33 * acpid listens to ACPI events coming either in textual form
34 * from /proc/acpi/event (though it is marked deprecated,
35 * it is still widely used and _is_ a standard) or in binary form
36 * from specified evdevs (just use /dev/input/event*).
37 * It parses the event to retrieve ACTION and a possible PARAMETER.
38 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
39 * (if the resulting path is a directory) or directly.
40 * If the resulting path does not exist it logs it via perror
41 * and continues listening.
42 */
43
44static void process_event(const char *event)
45{
46 struct stat st;
47 char *handler = xasprintf("./%s", event);
48 const char *args[] = { "run-parts", handler, NULL };
49
50 // debug info
51 if (option_mask32 & 8) { // -d
52 bb_error_msg("%s", event);
53 }
54
55 // spawn handler
56 // N.B. run-parts would require scripts to have #!/bin/sh
57 // handler is directory? -> use run-parts
58 // handler is file? -> run it directly
59 if (0 == stat(event, &st))
60 spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
61 else
62 bb_simple_perror_msg(event);
63 free(handler);
64}
65
66/*
67 * acpid [-c conf_dir] [-l log_file] [-e proc_event_file] [evdev_event_file...]
68*/
69
70int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
71int acpid_main(int argc, char **argv)
72{
73 struct pollfd *pfd;
74 int i, nfd;
75 const char *opt_conf = "/etc/acpi";
76 const char *opt_input = "/proc/acpi/event";
77 const char *opt_logfile = "/var/log/acpid.log";
78
79 getopt32(argv, "c:e:l:d"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000080 IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
Denis Vlasenkod16950d2008-11-29 09:05:50 +000081 &opt_conf, &opt_input, &opt_logfile
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000082 IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL, NULL)
Denis Vlasenkod16950d2008-11-29 09:05:50 +000083 );
84
85 // daemonize unless -d given
86 if (!(option_mask32 & 8)) { // ! -d
87 bb_daemonize_or_rexec(0, argv);
88 close(2);
89 xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC);
90 }
91
92 argv += optind;
Denys Vlasenko2ec91ae2010-01-04 14:15:38 +010093 argc -= optind;
Denis Vlasenkod16950d2008-11-29 09:05:50 +000094
95 // goto configuration directory
96 xchdir(opt_conf);
97
Denis Vlasenko6a07d1f2009-04-18 11:35:16 +000098 // prevent zombies
99 signal(SIGCHLD, SIG_IGN);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000100
101 // no explicit evdev files given? -> use proc event interface
102 if (!*argv) {
103 // proc_event file is just a "config" :)
104 char *token[4];
105 parser_t *parser = config_open(opt_input);
106
107 // dispatch events
108 while (config_read(parser, token, 4, 4, "\0 ", PARSE_NORMAL)) {
109 char *event = xasprintf("%s/%s", token[1], token[2]);
110 process_event(event);
111 free(event);
112 }
113
114 if (ENABLE_FEATURE_CLEAN_UP)
115 config_close(parser);
116 return EXIT_SUCCESS;
117 }
118
119 // evdev files given, use evdev interface
120
121 // open event devices
Denys Vlasenko2ec91ae2010-01-04 14:15:38 +0100122 pfd = xzalloc(sizeof(*pfd) * argc);
Denis Vlasenkod16950d2008-11-29 09:05:50 +0000123 nfd = 0;
124 while (*argv) {
125 pfd[nfd].fd = open_or_warn(*argv++, O_RDONLY | O_NONBLOCK);
126 if (pfd[nfd].fd >= 0)
127 pfd[nfd++].events = POLLIN;
128 }
129
130 // dispatch events
131 while (/* !bb_got_signal && */ poll(pfd, nfd, -1) > 0) {
132 for (i = 0; i < nfd; i++) {
133 const char *event;
134 struct input_event ev;
135
136 if (!(pfd[i].revents & POLLIN))
137 continue;
138
139 if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
140 continue;
141//bb_info_msg("%d: %d %d %4d", i, ev.type, ev.code, ev.value);
142
143 // filter out unneeded events
144 if (ev.value != 1)
145 continue;
146
147 event = NULL;
148
149 // N.B. we will conform to /proc/acpi/event
150 // naming convention when assigning event names
151
152 // TODO: do we want other events?
153
154 // power and sleep buttons delivered as keys pressed
155 if (EV_KEY == ev.type) {
156 if (KEY_POWER == ev.code)
157 event = "PWRF/00000080";
158 else if (KEY_SLEEP == ev.code)
159 event = "SLPB/00000080";
160 }
161 // switches
162 else if (EV_SW == ev.type) {
163 if (SW_LID == ev.code)
164 event = "LID/00000080";
165 else if (SW_RFKILL_ALL == ev.code)
166 event = "RFKILL";
167 }
168 // filter out unneeded events
169 if (!event)
170 continue;
171
172 // spawn event handler
173 process_event(event);
174 }
175 }
176
177 if (ENABLE_FEATURE_CLEAN_UP) {
178 for (i = 0; i < nfd; i++)
179 close(pfd[i].fd);
180 free(pfd);
181 }
182
183 return EXIT_SUCCESS;
184}