diff options
Diffstat (limited to 'util-linux/acpid.c')
-rw-r--r-- | util-linux/acpid.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/util-linux/acpid.c b/util-linux/acpid.c new file mode 100644 index 000000000..ef4e54d5d --- /dev/null +++ b/util-linux/acpid.c @@ -0,0 +1,167 @@ +/* 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; +} |