summaryrefslogtreecommitdiff
path: root/util-linux/acpid.c
blob: 7dd4f5b154177e3b91e99366969aaa316c031ac8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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"
		IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"),
		&opt_conf, &opt_input, &opt_logfile
		IF_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);

	// prevent zombies
	signal(SIGCHLD, SIG_IGN);

	// 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;
}