diff --git a/docs/busybox.net/news.html b/docs/busybox.net/news.html index 3fccaae12..5c005fb0e 100644 --- a/docs/busybox.net/news.html +++ b/docs/busybox.net/news.html @@ -32,8 +32,8 @@ patches, how to add a patch)

-

Bug-fix releases. 1.13.1 has fixes for ash, option parsing, id, init, - inotify, klogd, line editing and modprobe. 1.12.3 has fixes +

Bug fix releases. 1.13.1 has fixes for ash, option parsing, id, init, + inotifyd, klogd, line editing and modprobe. 1.12.3 has fixes for option parsing and line editing.

diff --git a/include/applets.h b/include/applets.h index fb904bb9f..ad3925577 100644 --- a/include/applets.h +++ b/include/applets.h @@ -69,6 +69,7 @@ s - suid type: USE_TEST(APPLET_NOFORK([, test, _BB_DIR_USR_BIN, _BB_SUID_NEVER, test)) USE_TEST(APPLET_NOFORK([[, test, _BB_DIR_USR_BIN, _BB_SUID_NEVER, test)) +USE_ACPID(APPLET(acpid, _BB_DIR_SBIN, _BB_SUID_NEVER)) USE_ADDGROUP(APPLET(addgroup, _BB_DIR_BIN, _BB_SUID_NEVER)) USE_ADDUSER(APPLET(adduser, _BB_DIR_BIN, _BB_SUID_NEVER)) USE_ADJTIMEX(APPLET(adjtimex, _BB_DIR_SBIN, _BB_SUID_NEVER)) diff --git a/include/usage.h b/include/usage.h index d35f8ae71..502fea7a0 100644 --- a/include/usage.h +++ b/include/usage.h @@ -16,6 +16,23 @@ #define NOUSAGE_STR "\b" +#define acpid_trivial_usage \ + "[-d] [-c CONFDIR] [-l LOGFILE] [-e PROC_EVENT_FILE] [EVDEV_EVENT_FILE...]" + +#define acpid_full_usage "\n\n" \ + "Listen to ACPI events and spawn specific helpers on event arrival\n" \ + "\nOptions:" \ + "\n -d Do not daemonize and log to stderr" \ + "\n -c DIR Config directory [/etc/acpi]" \ + "\n -e FILE /proc event file [/proc/acpi/event]" \ + "\n -l FILE Log file [/var/log/acpid]" \ + USE_FEATURE_ACPID_COMPAT( \ + "\n\nAccept and ignore compatibility options -g -m -s -S -v" \ + ) + +#define acpid_example_usage \ + "# acpid -l /var/log/my-acpi-log\n" \ + "# acpid -d /dev/input/event*\n" #define addgroup_trivial_usage \ "[-g GID] " USE_FEATURE_ADDUSER_TO_GROUP("[user_name] ") "group_name" diff --git a/util-linux/Config.in b/util-linux/Config.in index 976507b68..3bba2e26d 100644 --- a/util-linux/Config.in +++ b/util-linux/Config.in @@ -5,6 +5,28 @@ menu "Linux System Utilities" +config ACPID + bool "acpid" + default n + help + 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/[/] either via run-parts + (if the resulting path is a directory) or directly as an executable. + + N.B. acpid relies on run-parts so have the latter installed. + +config FEATURE_ACPID_COMPAT + bool "Accept and ignore redundant options" + default n + depends on ACPID + help + Accept and ignore compatibility options -g -m -s -S -v. + config BLKID bool "blkid" default n diff --git a/util-linux/Kbuild b/util-linux/Kbuild index 2d0fc4928..842afb756 100644 --- a/util-linux/Kbuild +++ b/util-linux/Kbuild @@ -5,6 +5,7 @@ # Licensed under the GPL v2, see the file LICENSE in this tarball. lib-y:= +lib-$(CONFIG_ACPID) += acpid.o lib-$(CONFIG_BLKID) += blkid.o lib-$(CONFIG_DMESG) += dmesg.o lib-$(CONFIG_FBSET) += fbset.o 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 + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ +#include "libbb.h" + +#include +#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/[/] 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; +}