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 source tree.
   8 */
   9#include "libbb.h"
  10#include <syslog.h>
  11#include <linux/input.h>
  12
  13enum {
  14        OPT_c = (1 << 0),
  15        OPT_d = (1 << 1),
  16        OPT_e = (1 << 2),
  17        OPT_f = (1 << 3),
  18        OPT_l = (1 << 4),
  19        OPT_a = (1 << 5),
  20        OPT_M = (1 << 6),
  21        OPT_p = (1 << 7) * ENABLE_FEATURE_PIDFILE,
  22};
  23
  24struct acpi_event {
  25        const char *s_type;
  26        uint16_t n_type;
  27        const char *s_code;
  28        uint16_t n_code;
  29        uint32_t value;
  30        const char *desc;
  31};
  32
  33static const struct acpi_event f_evt_tab[] = {
  34        { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRF 00000080" },
  35        { "EV_KEY", 0x01, "KEY_POWER", 116, 1, "button/power PWRB 00000080" },
  36};
  37
  38struct acpi_action {
  39        const char *key;
  40        const char *action;
  41};
  42
  43static const struct acpi_action f_act_tab[] = {
  44        { "PWRF", "PWRF/00000080" },
  45        { "LID0", "LID/00000080" },
  46};
  47
  48struct globals {
  49        struct acpi_action *act_tab;
  50        int n_act;
  51        struct acpi_event *evt_tab;
  52        int n_evt;
  53} FIX_ALIASING;
  54#define G (*ptr_to_globals)
  55#define act_tab         (G.act_tab)
  56#define n_act           (G.n_act  )
  57#define evt_tab         (G.evt_tab)
  58#define n_evt           (G.n_evt  )
  59#define INIT_G() do { \
  60        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
  61} while (0)
  62
  63/*
  64 * acpid listens to ACPI events coming either in textual form
  65 * from /proc/acpi/event (though it is marked deprecated,
  66 * it is still widely used and _is_ a standard) or in binary form
  67 * from specified evdevs (just use /dev/input/event*).
  68 * It parses the event to retrieve ACTION and a possible PARAMETER.
  69 * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
  70 * (if the resulting path is a directory) or directly.
  71 * If the resulting path does not exist it logs it via perror
  72 * and continues listening.
  73 */
  74
  75static void process_event(const char *event)
  76{
  77        struct stat st;
  78        char *handler = xasprintf("./%s", event);
  79        const char *args[] = { "run-parts", handler, NULL };
  80
  81        // debug info
  82        if (option_mask32 & OPT_d) {
  83                bb_error_msg("%s", event);
  84        }
  85
  86        // spawn handler
  87        // N.B. run-parts would require scripts to have #!/bin/sh
  88        // handler is directory? -> use run-parts
  89        // handler is file? -> run it directly
  90        if (0 == stat(event, &st))
  91                spawn((char **)args + (0==(st.st_mode & S_IFDIR)));
  92        else
  93                bb_simple_perror_msg(event);
  94
  95        free(handler);
  96}
  97
  98static const char *find_action(struct input_event *ev, const char *buf)
  99{
 100        const char *action = NULL;
 101        int i;
 102
 103        // map event
 104        for (i = 0; i < n_evt; i++) {
 105                if (ev) {
 106                        if (ev->type == evt_tab[i].n_type && ev->code == evt_tab[i].n_code && ev->value == evt_tab[i].value) {
 107                                action = evt_tab[i].desc;
 108                                break;
 109                        }
 110                }
 111
 112                if (buf) {
 113                        if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) {
 114                                action = evt_tab[i].desc;
 115                                break;
 116                        }
 117                }
 118        }
 119
 120        // get action
 121        if (action) {
 122                for (i = 0; i < n_act; i++) {
 123                        if (strstr(action, act_tab[i].key)) {
 124                                action = act_tab[i].action;
 125                                break;
 126                        }
 127                }
 128        }
 129
 130        return action;
 131}
 132
 133static void parse_conf_file(const char *filename)
 134{
 135        parser_t *parser;
 136        char *tokens[2];
 137
 138        parser = config_open2(filename, fopen_for_read);
 139
 140        if (parser) {
 141                while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL)) {
 142                        act_tab = xrealloc_vector(act_tab, 1, n_act);
 143                        act_tab[n_act].key = xstrdup(tokens[0]);
 144                        act_tab[n_act].action = xstrdup(tokens[1]);
 145                        n_act++;
 146                }
 147                config_close(parser);
 148        } else {
 149                act_tab = (void*)f_act_tab;
 150                n_act = ARRAY_SIZE(f_act_tab);
 151        }
 152}
 153
 154static void parse_map_file(const char *filename)
 155{
 156        parser_t *parser;
 157        char *tokens[6];
 158
 159        parser = config_open2(filename, fopen_for_read);
 160
 161        if (parser) {
 162                while (config_read(parser, tokens, 6, 6, "# \t", PARSE_NORMAL)) {
 163                        evt_tab = xrealloc_vector(evt_tab, 1, n_evt);
 164                        evt_tab[n_evt].s_type = xstrdup(tokens[0]);
 165                        evt_tab[n_evt].n_type = xstrtou(tokens[1], 16);
 166                        evt_tab[n_evt].s_code = xstrdup(tokens[2]);
 167                        evt_tab[n_evt].n_code = xatou16(tokens[3]);
 168                        evt_tab[n_evt].value = xatoi_positive(tokens[4]);
 169                        evt_tab[n_evt].desc = xstrdup(tokens[5]);
 170                        n_evt++;
 171                }
 172                config_close(parser);
 173        } else {
 174                evt_tab = (void*)f_evt_tab;
 175                n_evt = ARRAY_SIZE(f_evt_tab);
 176        }
 177}
 178
 179/*
 180 * acpid [-c conf_dir] [-r conf_file ] [-a map_file ] [-l log_file] [-e proc_event_file]
 181 */
 182
 183int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 184int acpid_main(int argc UNUSED_PARAM, char **argv)
 185{
 186        struct input_event ev;
 187        int nfd;
 188        int opts;
 189        struct pollfd *pfd;
 190        const char *opt_dir = "/etc/acpi";
 191        const char *opt_input = "/dev/input/event";
 192        const char *opt_logfile = "/var/log/acpid.log";
 193        const char *opt_action = "/etc/acpid.conf";
 194        const char *opt_map = "/etc/acpi.map";
 195#if ENABLE_FEATURE_PIDFILE
 196        const char *opt_pidfile = "/var/run/acpid.pid";
 197#endif
 198
 199        INIT_G();
 200
 201        opt_complementary = "df:e--e";
 202        opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
 203                &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map
 204                IF_FEATURE_PIDFILE(, &opt_pidfile)
 205                IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL)
 206        );
 207
 208        if (!(opts & OPT_f)) {
 209                bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
 210        }
 211
 212        if (!(opts & OPT_d)) {
 213                openlog(applet_name, LOG_PID, LOG_DAEMON);
 214                logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
 215        } else {
 216                xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
 217        }
 218
 219        parse_conf_file(opt_action);
 220        parse_map_file(opt_map);
 221
 222        xchdir(opt_dir);
 223
 224        bb_signals((1 << SIGCHLD), SIG_IGN);
 225        bb_signals(BB_FATAL_SIGS, record_signo);
 226
 227        pfd = NULL;
 228        nfd = 0;
 229        while (1) {
 230                int fd;
 231                char *dev_event;
 232
 233                dev_event = xasprintf((option_mask32 & OPT_e) ? "%s" : "%s%u", opt_input, nfd);
 234                fd = open(dev_event, O_RDONLY | O_NONBLOCK);
 235                if (fd < 0) {
 236                        if (nfd == 0)
 237                                bb_simple_perror_msg_and_die(dev_event);
 238                        break;
 239                }
 240                pfd = xrealloc_vector(pfd, 1, nfd);
 241                pfd[nfd].fd = fd;
 242                pfd[nfd].events = POLLIN;
 243                nfd++;
 244        }
 245
 246        write_pidfile(opt_pidfile);
 247
 248        while (poll(pfd, nfd, -1) > 0) {
 249                int i;
 250                for (i = 0; i < nfd; i++) {
 251                        const char *event = NULL;
 252
 253                        memset(&ev, 0, sizeof(ev));
 254
 255                        if (!(pfd[i].revents & POLLIN))
 256                                continue;
 257
 258                        if (option_mask32 & OPT_e) {
 259                                char *buf;
 260                                int len;
 261
 262                                buf = xmalloc_reads(pfd[i].fd, NULL, NULL);
 263                                /* buf = "button/power PWRB 00000080 00000000" */
 264                                len = strlen(buf) - 9;
 265                                if (len >= 0)
 266                                        buf[len] = '\0';
 267                                event = find_action(NULL, buf);
 268                        } else {
 269                                if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
 270                                        continue;
 271
 272                                if (ev.value != 1 && ev.value != 0)
 273                                        continue;
 274
 275                                event = find_action(&ev, NULL);
 276                        }
 277                        if (!event)
 278                                continue;
 279                        // spawn event handler
 280                        process_event(event);
 281                }
 282        }
 283
 284        if (ENABLE_FEATURE_CLEAN_UP) {
 285                while (nfd--) {
 286                        if (pfd[nfd].fd) {
 287                                close(pfd[nfd].fd);
 288                        }
 289                }
 290                free(pfd);
 291        }
 292        remove_pidfile(opt_pidfile);
 293
 294        return EXIT_SUCCESS;
 295}
 296