busybox/util-linux/acpid.c
<<
>>
Prefs
   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 tarball for details.
   8 */
   9#include "libbb.h"
  10
  11#include <linux/input.h>
  12#ifndef EV_SW
  13# define EV_SW         0x05
  14#endif
  15#ifndef EV_KEY
  16# define EV_KEY        0x01
  17#endif
  18#ifndef SW_LID
  19# define SW_LID        0x00
  20#endif
  21#ifndef SW_RFKILL_ALL
  22# define SW_RFKILL_ALL 0x03
  23#endif
  24#ifndef KEY_POWER
  25# define KEY_POWER     116     /* SC System Power Down */
  26#endif
  27#ifndef KEY_SLEEP
  28# define KEY_SLEEP     142     /* SC System Sleep */
  29#endif
  30
  31
  32/*
  33 * acpid listens to ACPI events coming either in textual form
  34 * from /proc/acpi/event (though it is marked deprecated,
  35 * it is still widely used and _is_ a standard) or in binary form
  36 * from specified evdevs (just use /dev/input/event*).
  37 * It parses the event to retrieve ACTION and a possible PARAMETER.
  38 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
  39 * (if the resulting path is a directory) or directly.
  40 * If the resulting path does not exist it logs it via perror
  41 * and continues listening.
  42 */
  43
  44static void process_event(const char *event)
  45{
  46        struct stat st;
  47        char *handler = xasprintf("./%s", event);
  48        const char *args[] = { "run-parts", handler, NULL };
  49
  50        // debug info
  51        if (option_mask32 & 8) { // -d
  52                bb_error_msg("%s", event);
  53        }
  54
  55        // spawn handler
  56        // N.B. run-parts would require scripts to have #!/bin/sh
  57        // handler is directory? -> use run-parts
  58        // handler is file? -> run it directly
  59        if (0 == stat(event, &st))
  60                spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
  61        else
  62                bb_simple_perror_msg(event);
  63        free(handler);
  64}
  65
  66/*
  67 * acpid [-c conf_dir] [-l log_file] [-e proc_event_file] [evdev_event_file...]
  68*/
  69
  70int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  71int acpid_main(int argc, char **argv)
  72{
  73        struct pollfd *pfd;
  74        int i, nfd;
  75        const char *opt_conf = "/etc/acpi";
  76        const char *opt_input = "/proc/acpi/event";
  77        const char *opt_logfile = "/var/log/acpid.log";
  78
  79        getopt32(argv, "c:e:l:d"
  80                IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
  81                &opt_conf, &opt_input, &opt_logfile
  82                IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL, NULL)
  83        );
  84
  85        // daemonize unless -d given
  86        if (!(option_mask32 & 8)) { // ! -d
  87                bb_daemonize_or_rexec(0, argv);
  88                close(2);
  89                xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC);
  90        }
  91
  92        argv += optind;
  93        argc -= optind;
  94
  95        // goto configuration directory
  96        xchdir(opt_conf);
  97
  98        // prevent zombies
  99        signal(SIGCHLD, SIG_IGN);
 100
 101        // no explicit evdev files given? -> use proc event interface
 102        if (!*argv) {
 103                // proc_event file is just a "config" :)
 104                char *token[4];
 105                parser_t *parser = config_open(opt_input);
 106
 107                // dispatch events
 108                while (config_read(parser, token, 4, 4, "\0 ", PARSE_NORMAL)) {
 109                        char *event = xasprintf("%s/%s", token[1], token[2]);
 110                        process_event(event);
 111                        free(event);
 112                }
 113
 114                if (ENABLE_FEATURE_CLEAN_UP)
 115                        config_close(parser);
 116                return EXIT_SUCCESS;
 117        }
 118
 119        // evdev files given, use evdev interface
 120
 121        // open event devices
 122        pfd = xzalloc(sizeof(*pfd) * argc);
 123        nfd = 0;
 124        while (*argv) {
 125                pfd[nfd].fd = open_or_warn(*argv++, O_RDONLY | O_NONBLOCK);
 126                if (pfd[nfd].fd >= 0)
 127                        pfd[nfd++].events = POLLIN;
 128        }
 129
 130        // dispatch events
 131        while (/* !bb_got_signal && */ poll(pfd, nfd, -1) > 0) {
 132                for (i = 0; i < nfd; i++) {
 133                        const char *event;
 134                        struct input_event ev;
 135
 136                        if (!(pfd[i].revents & POLLIN))
 137                                continue;
 138
 139                        if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
 140                                continue;
 141//bb_info_msg("%d: %d %d %4d", i, ev.type, ev.code, ev.value);
 142
 143                        // filter out unneeded events
 144                        if (ev.value != 1)
 145                                continue;
 146
 147                        event = NULL;
 148
 149                        // N.B. we will conform to /proc/acpi/event
 150                        // naming convention when assigning event names
 151
 152                        // TODO: do we want other events?
 153
 154                        // power and sleep buttons delivered as keys pressed
 155                        if (EV_KEY == ev.type) {
 156                                if (KEY_POWER == ev.code)
 157                                        event = "PWRF/00000080";
 158                                else if (KEY_SLEEP == ev.code)
 159                                        event = "SLPB/00000080";
 160                        }
 161                        // switches
 162                        else if (EV_SW == ev.type) {
 163                                if (SW_LID == ev.code)
 164                                        event = "LID/00000080";
 165                                else if (SW_RFKILL_ALL == ev.code)
 166                                        event = "RFKILL";
 167                        }
 168                        // filter out unneeded events
 169                        if (!event)
 170                                continue;
 171
 172                        // spawn event handler
 173                        process_event(event);
 174                }
 175        }
 176
 177        if (ENABLE_FEATURE_CLEAN_UP) {
 178                for (i = 0; i < nfd; i++)
 179                        close(pfd[i].fd);
 180                free(pfd);
 181        }
 182
 183        return EXIT_SUCCESS;
 184}
 185