acpid: new applet by Vladimir. +737 bytes
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 29 Nov 2008 09:05:50 +0000 (09:05 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 29 Nov 2008 09:05:50 +0000 (09:05 -0000)
docs/busybox.net/news.html
include/applets.h
include/usage.h
util-linux/Config.in
util-linux/Kbuild
util-linux/acpid.c [new file with mode: 0644]

index 3fccaae..5c005fb 100644 (file)
@@ -32,8 +32,8 @@
     <a href="http://busybox.net/downloads/fixes-1.12.3/">patches</a>,
     <a href="http://busybox.net/fix.html">how to add a patch</a>)</p>
 
-    <p>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
+    <p>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.
     </p>
   </li>
index fb904bb..ad39255 100644 (file)
@@ -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))
index d35f8ae..502fea7 100644 (file)
 
 #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"
index 976507b..3bba2e2 100644 (file)
@@ -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/<ACTION>[/<PARAMETER>] 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
index 2d0fc49..842afb7 100644 (file)
@@ -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 (file)
index 0000000..ef4e54d
--- /dev/null
@@ -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;
+}