acpid.c 6.7 KB

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