libbb: conditionalize AF_* usage in error reporting
[oweals/busybox.git] / util-linux / acpid.c
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
44 static 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
70 int acpid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
71 int 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 }