--- /dev/null
+/* vi: set sw=4 ts=4: */
+/*
+ * simple ACPI events listener
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+#include <linux/input.h>
+#ifndef SW_RFKILL_ALL
+# define SW_RFKILL_ALL 3
+#endif
+
+/*
+ * acpid listens to ACPI events coming either in textual form
+ * from /proc/acpi/event (though it is marked deprecated,
+ * it is still widely used and _is_ a standard) or in binary form
+ * from specified evdevs (just use /dev/input/event*).
+ * It parses the event to retrieve ACTION and a possible PARAMETER.
+ * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+ * (if the resulting path is a directory) or directly.
+ * If the resulting path does not exist it logs it via perror
+ * and continues listening.
+ */
+
+static void process_event(const char *event)
+{
+ struct stat st;
+ char *handler = xasprintf("./%s", event);
+ const char *args[] = { "run-parts", handler, NULL };
+
+ // debug info
+ if (option_mask32 & 8) { // -d
+ bb_error_msg("%s", event);
+ }
+
+ // spawn handler
+ // N.B. run-parts would require scripts to have #!/bin/sh
+ // handler is directory? -> use run-parts
+ // handler is file? -> run it directly
+ if (0 == stat(event, &st))
+ spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
+ else
+ bb_simple_perror_msg(event);
+ free(handler);
+}
+
+/*
+ * acpid [-c conf_dir] [-l log_file] [-e proc_event_file] [evdev_event_file...]
+*/
+
+int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int acpid_main(int argc, char **argv)
+{
+ struct pollfd *pfd;
+ int i, nfd;
+ const char *opt_conf = "/etc/acpi";
+ const char *opt_input = "/proc/acpi/event";
+ const char *opt_logfile = "/var/log/acpid.log";
+
+ getopt32(argv, "c:e:l:d"
+ USE_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
+ &opt_conf, &opt_input, &opt_logfile
+ USE_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL, NULL)
+ );
+
+ // daemonize unless -d given
+ if (!(option_mask32 & 8)) { // ! -d
+ bb_daemonize_or_rexec(0, argv);
+ close(2);
+ xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC);
+ }
+
+ argv += optind;
+
+ // goto configuration directory
+ xchdir(opt_conf);
+
+// // setup signals
+// bb_signals(BB_FATAL_SIGS, record_signo);
+
+ // no explicit evdev files given? -> use proc event interface
+ if (!*argv) {
+ // proc_event file is just a "config" :)
+ char *token[4];
+ parser_t *parser = config_open(opt_input);
+
+ // dispatch events
+ while (config_read(parser, token, 4, 4, "\0 ", PARSE_NORMAL)) {
+ char *event = xasprintf("%s/%s", token[1], token[2]);
+ process_event(event);
+ free(event);
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ config_close(parser);
+ return EXIT_SUCCESS;
+ }
+
+ // evdev files given, use evdev interface
+
+ // open event devices
+ pfd = xzalloc(sizeof(*pfd) * (argc - optind));
+ nfd = 0;
+ while (*argv) {
+ pfd[nfd].fd = open_or_warn(*argv++, O_RDONLY | O_NONBLOCK);
+ if (pfd[nfd].fd >= 0)
+ pfd[nfd++].events = POLLIN;
+ }
+
+ // dispatch events
+ while (/* !bb_got_signal && */ poll(pfd, nfd, -1) > 0) {
+ for (i = 0; i < nfd; i++) {
+ const char *event;
+ struct input_event ev;
+
+ if (!(pfd[i].revents & POLLIN))
+ continue;
+
+ if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
+ continue;
+//bb_info_msg("%d: %d %d %4d", i, ev.type, ev.code, ev.value);
+
+ // filter out unneeded events
+ if (ev.value != 1)
+ continue;
+
+ event = NULL;
+
+ // N.B. we will conform to /proc/acpi/event
+ // naming convention when assigning event names
+
+ // TODO: do we want other events?
+
+ // power and sleep buttons delivered as keys pressed
+ if (EV_KEY == ev.type) {
+ if (KEY_POWER == ev.code)
+ event = "PWRF/00000080";
+ else if (KEY_SLEEP == ev.code)
+ event = "SLPB/00000080";
+ }
+ // switches
+ else if (EV_SW == ev.type) {
+ if (SW_LID == ev.code)
+ event = "LID/00000080";
+ else if (SW_RFKILL_ALL == ev.code)
+ event = "RFKILL";
+ }
+ // filter out unneeded events
+ if (!event)
+ continue;
+
+ // spawn event handler
+ process_event(event);
+ }
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ for (i = 0; i < nfd; i++)
+ close(pfd[i].fd);
+ free(pfd);
+ }
+
+ return EXIT_SUCCESS;
+}