Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 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 | * |
Denys Vlasenko | 0ef64bd | 2010-08-16 20:14:46 +0200 | [diff] [blame] | 7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 8 | */ |
| 9 | #include "libbb.h" |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 10 | #include <syslog.h> |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 11 | #include <linux/input.h> |
Denys Vlasenko | 6af732b | 2010-07-12 06:20:11 +0200 | [diff] [blame] | 12 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 13 | enum { |
| 14 | OPT_c = (1 << 0), |
| 15 | OPT_d = (1 << 1), |
| 16 | OPT_e = (1 << 2), |
| 17 | OPT_f = (1 << 3), |
| 18 | OPT_l = (1 << 4), |
| 19 | OPT_p = (1 << 5) * ENABLE_FEATURE_PIDFILE, |
| 20 | OPT_a = (1 << 6), |
| 21 | OPT_M = (1 << 7), |
| 22 | }; |
| 23 | |
| 24 | struct acpi_event { |
| 25 | const char *s_type; |
| 26 | uint16_t n_type; |
| 27 | const char *s_code; |
| 28 | uint16_t n_code; |
| 29 | uint32_t value; |
| 30 | const char *desc; |
| 31 | }; |
| 32 | |
| 33 | static const struct acpi_event f_evt_tab[] = { |
| 34 | { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" }, |
| 35 | { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" }, |
| 36 | }; |
| 37 | |
| 38 | struct acpi_action { |
| 39 | const char *key; |
| 40 | const char *action; |
| 41 | }; |
| 42 | |
| 43 | static const struct acpi_action f_act_tab[] = { |
| 44 | { "PWRF", "PWRF/00000080" }, |
| 45 | { "LID0", "LID/00000080" }, |
| 46 | }; |
| 47 | |
| 48 | struct globals { |
| 49 | struct acpi_action *act_tab; |
| 50 | int n_act; |
| 51 | struct acpi_event *evt_tab; |
| 52 | int n_evt; |
| 53 | } FIX_ALIASING; |
| 54 | #define G (*ptr_to_globals) |
Denys Vlasenko | e4dcba1 | 2010-10-28 18:57:19 +0200 | [diff] [blame] | 55 | #define act_tab (G.act_tab) |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 56 | #define n_act (G.n_act ) |
| 57 | #define evt_tab (G.evt_tab) |
| 58 | #define n_evt (G.n_evt ) |
| 59 | #define INIT_G() do { \ |
| 60 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
| 61 | } while (0) |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 62 | |
| 63 | /* |
| 64 | * acpid listens to ACPI events coming either in textual form |
| 65 | * from /proc/acpi/event (though it is marked deprecated, |
| 66 | * it is still widely used and _is_ a standard) or in binary form |
| 67 | * from specified evdevs (just use /dev/input/event*). |
| 68 | * It parses the event to retrieve ACTION and a possible PARAMETER. |
| 69 | * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts |
| 70 | * (if the resulting path is a directory) or directly. |
| 71 | * If the resulting path does not exist it logs it via perror |
| 72 | * and continues listening. |
| 73 | */ |
| 74 | |
| 75 | static void process_event(const char *event) |
| 76 | { |
| 77 | struct stat st; |
| 78 | char *handler = xasprintf("./%s", event); |
| 79 | const char *args[] = { "run-parts", handler, NULL }; |
| 80 | |
| 81 | // debug info |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 82 | if (option_mask32 & OPT_d) { |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 83 | bb_error_msg("%s", event); |
| 84 | } |
| 85 | |
| 86 | // spawn handler |
| 87 | // N.B. run-parts would require scripts to have #!/bin/sh |
| 88 | // handler is directory? -> use run-parts |
| 89 | // handler is file? -> run it directly |
| 90 | if (0 == stat(event, &st)) |
| 91 | spawn((char **)args + (0==(st.st_mode & S_IFDIR))); |
| 92 | else |
| 93 | bb_simple_perror_msg(event); |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 94 | |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 95 | free(handler); |
| 96 | } |
| 97 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 98 | static const char *find_action(struct input_event *ev, const char *buf) |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 99 | { |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 100 | const char *action = NULL; |
| 101 | int i; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 102 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 103 | // map event |
| 104 | for (i = 0; i < n_evt; i++) { |
| 105 | if (ev) { |
| 106 | if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) { |
| 107 | action = evt_tab[i].desc; |
| 108 | break; |
| 109 | } |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 110 | } |
| 111 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 112 | if (buf) { |
| 113 | if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) { |
| 114 | action = evt_tab[i].desc; |
| 115 | break; |
| 116 | } |
| 117 | } |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 118 | } |
| 119 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 120 | // get action |
| 121 | if (action) { |
| 122 | for (i = 0; i < n_act; i++) { |
| 123 | if (strstr(action, act_tab[i].key)) { |
| 124 | action = act_tab[i].action; |
| 125 | break; |
| 126 | } |
| 127 | } |
| 128 | } |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 129 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 130 | return action; |
| 131 | } |
| 132 | |
| 133 | static void parse_conf_file(const char *filename) |
| 134 | { |
| 135 | parser_t *parser; |
| 136 | char *tokens[2]; |
| 137 | |
| 138 | parser = config_open2(filename, fopen_for_read); |
| 139 | |
| 140 | if (parser) { |
| 141 | while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) { |
| 142 | act_tab = xrealloc_vector(act_tab, 1, n_act); |
| 143 | act_tab[n_act].key = xstrdup(tokens[0]); |
| 144 | act_tab[n_act].action = xstrdup(tokens[1]); |
| 145 | n_act++; |
| 146 | } |
| 147 | config_close(parser); |
| 148 | } else { |
| 149 | act_tab = (void*)f_act_tab; |
| 150 | n_act = ARRAY_SIZE(f_act_tab); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | static void parse_map_file(const char *filename) |
| 155 | { |
| 156 | parser_t *parser; |
| 157 | char *tokens[6]; |
| 158 | |
| 159 | parser = config_open2(filename, fopen_for_read); |
| 160 | |
| 161 | if (parser) { |
| 162 | while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) { |
| 163 | evt_tab = xrealloc_vector(evt_tab, 1, n_evt); |
| 164 | evt_tab[n_evt].s_type = xstrdup(tokens[0]); |
| 165 | evt_tab[n_evt].n_type = xstrtou(tokens[1], 16); |
| 166 | evt_tab[n_evt].s_code = xstrdup(tokens[2]); |
| 167 | evt_tab[n_evt].n_code = xatou16(tokens[3]); |
| 168 | evt_tab[n_evt].value = xatoi_positive(tokens[4]); |
| 169 | evt_tab[n_evt].desc = xstrdup(tokens[5]); |
| 170 | n_evt++; |
| 171 | } |
| 172 | config_close(parser); |
| 173 | } else { |
| 174 | evt_tab = (void*)f_evt_tab; |
| 175 | n_evt = ARRAY_SIZE(f_evt_tab); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | /* |
| 180 | * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file] |
| 181 | */ |
| 182 | |
| 183 | int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 184 | int acpid_main(int argc UNUSED_PARAM, char **argv) |
| 185 | { |
| 186 | struct input_event ev; |
| 187 | int nfd; |
| 188 | int opts; |
| 189 | struct pollfd *pfd; |
| 190 | const char *opt_dir = "/etc/acpi"; |
| 191 | const char *opt_input = "/dev/input/event"; |
| 192 | const char *opt_logfile = "/var/log/acpid.log"; |
| 193 | const char *opt_action = "/etc/acpid.conf"; |
| 194 | const char *opt_map = "/etc/acpi.map"; |
| 195 | #if ENABLE_FEATURE_PIDFILE |
| 196 | const char *opt_pidfile = "/var/run/acpid.pid"; |
| 197 | #endif |
| 198 | |
| 199 | INIT_G(); |
| 200 | |
| 201 | opt_complementary = "df:e--e"; |
| 202 | opts = getopt32(argv, "c:de:fl:p:a:M:" IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"), |
| 203 | &opt_dir, &opt_input, &opt_logfile, &opt_pidfile, &opt_action, &opt_map |
| 204 | IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL) |
| 205 | ); |
| 206 | |
| 207 | if (!(opts & OPT_f)) { |
| 208 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
| 209 | } |
| 210 | |
| 211 | if (!(opts & OPT_d)) { |
| 212 | openlog(applet_name, LOG_PID, LOG_DAEMON); |
| 213 | logmode = LOGMODE_SYSLOG | LOGMODE_STDIO; |
| 214 | } else { |
| 215 | xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); |
| 216 | } |
| 217 | |
| 218 | parse_conf_file(opt_action); |
| 219 | parse_map_file(opt_map); |
| 220 | |
| 221 | xchdir(opt_dir); |
| 222 | |
| 223 | bb_signals((1 << SIGCHLD), SIG_IGN); |
| 224 | bb_signals(BB_FATAL_SIGS, record_signo); |
| 225 | |
| 226 | pfd = NULL; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 227 | nfd = 0; |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 228 | while (1) { |
| 229 | int fd; |
| 230 | char *dev_event; |
| 231 | |
| 232 | dev_event = xasprintf((option_mask32 & OPT_e) ? "%s" : "%s%u", opt_input, nfd); |
| 233 | fd = open(dev_event, O_RDONLY | O_NONBLOCK); |
| 234 | if (fd < 0) { |
| 235 | if (nfd == 0) |
| 236 | bb_simple_perror_msg_and_die(dev_event); |
| 237 | break; |
| 238 | } |
| 239 | pfd = xrealloc_vector(pfd, 1, nfd); |
| 240 | pfd[nfd].fd = fd; |
| 241 | pfd[nfd].events = POLLIN; |
| 242 | nfd++; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 243 | } |
| 244 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 245 | write_pidfile(opt_pidfile); |
| 246 | |
| 247 | while (poll(pfd, nfd, -1) > 0) { |
| 248 | int i; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 249 | for (i = 0; i < nfd; i++) { |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 250 | const char *event = NULL; |
| 251 | |
| 252 | memset(&ev, 0, sizeof(ev)); |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 253 | |
| 254 | if (!(pfd[i].revents & POLLIN)) |
| 255 | continue; |
| 256 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 257 | if (option_mask32 & OPT_e) { |
| 258 | char *buf; |
| 259 | int len; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 260 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 261 | buf = xmalloc_reads(pfd[i].fd, NULL, NULL); |
| 262 | /* buf = "button/power PWRB 00000080 00000000" */ |
| 263 | len = strlen(buf) - 9; |
| 264 | if (len >= 0) |
| 265 | buf[len] = '\0'; |
| 266 | event = find_action(NULL, buf); |
| 267 | } else { |
| 268 | if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev))) |
| 269 | continue; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 270 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 271 | if (ev.value != 1 && ev.value != 0) |
| 272 | continue; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 273 | |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 274 | event = find_action(&ev, NULL); |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 275 | } |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 276 | if (!event) |
| 277 | continue; |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 278 | // spawn event handler |
| 279 | process_event(event); |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | if (ENABLE_FEATURE_CLEAN_UP) { |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 284 | while (nfd--) { |
| 285 | if (pfd[nfd].fd) { |
| 286 | close(pfd[nfd].fd); |
| 287 | } |
| 288 | } |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 289 | free(pfd); |
| 290 | } |
Souf Oued | ccb7a43 | 2010-09-26 12:40:05 +0200 | [diff] [blame] | 291 | remove_pidfile(opt_pidfile); |
Denis Vlasenko | d16950d | 2008-11-29 09:05:50 +0000 | [diff] [blame] | 292 | |
| 293 | return EXIT_SUCCESS; |
| 294 | } |