diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2015-04-16 12:44:02 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2015-04-16 12:44:02 +0200 |
commit | 71a5b67ba0339265153217df646827a1213e03f5 (patch) | |
tree | 1ed390d311f503f41b853d543d6edd4102047972 | |
parent | f7466e477691fd29f47ebe8ae27489e065c69e5e (diff) | |
download | busybox-w32-71a5b67ba0339265153217df646827a1213e03f5.tar.gz busybox-w32-71a5b67ba0339265153217df646827a1213e03f5.tar.bz2 busybox-w32-71a5b67ba0339265153217df646827a1213e03f5.zip |
uevent: new applet
This applet listens on netlink socket with kernel's uevent messages.
Run-tested.
function old new delta
uevent_main - 416 +416
packed_usage 30671 30713 +42
applet_names 2531 2538 +7
applet_main 1468 1472 +4
RCVBUF - 4 +4
applet_nameofs 734 736 +2
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 4/0 up/down: 475/0) Total: 475 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | util-linux/uevent.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/util-linux/uevent.c b/util-linux/uevent.c new file mode 100644 index 000000000..fb98b4845 --- /dev/null +++ b/util-linux/uevent.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * Copyright 2015 Denys Vlasenko | ||
3 | * | ||
4 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
5 | */ | ||
6 | |||
7 | //config:config UEVENT | ||
8 | //config: bool "uevent" | ||
9 | //config: default y | ||
10 | //config: select PLATFORM_LINUX | ||
11 | //config: help | ||
12 | //config: uevent is a netlink listener for kernel uevent notifications | ||
13 | //config: sent via netlink. It is usually used for dynamic device creation. | ||
14 | |||
15 | //applet:IF_UEVENT(APPLET(uevent, BB_DIR_SBIN, BB_SUID_DROP)) | ||
16 | |||
17 | //kbuild:lib-$(CONFIG_UEVENT) += uevent.o | ||
18 | |||
19 | //usage:#define uevent_trivial_usage | ||
20 | //usage: "[PROG [ARGS]]" | ||
21 | //usage:#define uevent_full_usage "\n\n" | ||
22 | //usage: "uevent runs PROG for every netlink notification." | ||
23 | //usage: "\n""PROG's environment contains data passed from the kernel." | ||
24 | //usage: "\n""Typical usage (daemon for dynamic device node creation):" | ||
25 | //usage: "\n"" # uevent mdev & mdev -s" | ||
26 | |||
27 | #include "libbb.h" | ||
28 | #include <linux/netlink.h> | ||
29 | |||
30 | #define BUFFER_SIZE 16*1024 | ||
31 | |||
32 | #define env ((char **)&bb_common_bufsiz1) | ||
33 | enum { | ||
34 | MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1, | ||
35 | }; | ||
36 | |||
37 | #ifndef SO_RCVBUFFORCE | ||
38 | #define SO_RCVBUFFORCE 33 | ||
39 | #endif | ||
40 | static const int RCVBUF = 2 * 1024 * 1024; | ||
41 | |||
42 | int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
43 | int uevent_main(int argc UNUSED_PARAM, char **argv) | ||
44 | { | ||
45 | struct sockaddr_nl sa; | ||
46 | int fd; | ||
47 | |||
48 | argv++; | ||
49 | |||
50 | // Subscribe for UEVENT kernel messages | ||
51 | sa.nl_family = AF_NETLINK; | ||
52 | sa.nl_pad = 0; | ||
53 | sa.nl_pid = getpid(); | ||
54 | sa.nl_groups = 1 << 0; | ||
55 | fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); | ||
56 | xbind(fd, (struct sockaddr *) &sa, sizeof(sa)); | ||
57 | close_on_exec_on(fd); | ||
58 | |||
59 | // Without a sufficiently big RCVBUF, a ton of simultaneous events | ||
60 | // can trigger ENOBUFS on read, which is unrecoverable. | ||
61 | // Reproducer: | ||
62 | // uevent mdev & | ||
63 | // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';' | ||
64 | // | ||
65 | // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl | ||
66 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &RCVBUF, sizeof(RCVBUF)); | ||
67 | setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &RCVBUF, sizeof(RCVBUF)); | ||
68 | if (0) { | ||
69 | int z; | ||
70 | socklen_t zl = sizeof(z); | ||
71 | getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl); | ||
72 | bb_error_msg("SO_RCVBUF:%d", z); | ||
73 | } | ||
74 | |||
75 | for (;;) { | ||
76 | char *netbuf; | ||
77 | char *s, *end; | ||
78 | ssize_t len; | ||
79 | int idx; | ||
80 | |||
81 | // In many cases, a system sits for *days* waiting | ||
82 | // for a new uevent notification to come in. | ||
83 | // We use a fresh mmap so that buffer is not allocated | ||
84 | // until kernel actually starts filling it. | ||
85 | netbuf = mmap(NULL, BUFFER_SIZE, | ||
86 | PROT_READ | PROT_WRITE, | ||
87 | MAP_PRIVATE | MAP_ANON, | ||
88 | /* ignored: */ -1, 0); | ||
89 | if (netbuf == MAP_FAILED) | ||
90 | bb_perror_msg_and_die("mmap"); | ||
91 | |||
92 | // Here we block, possibly for a very long time | ||
93 | len = safe_read(fd, netbuf, BUFFER_SIZE - 1); | ||
94 | if (len < 0) | ||
95 | bb_perror_msg_and_die("read"); | ||
96 | end = netbuf + len; | ||
97 | *end = '\0'; | ||
98 | |||
99 | // Each netlink message starts with "ACTION@/path" | ||
100 | // (which we currently ignore), | ||
101 | // followed by environment variables. | ||
102 | if (!argv[0]) | ||
103 | putchar('\n'); | ||
104 | idx = 0; | ||
105 | s = netbuf; | ||
106 | while (s < end) { | ||
107 | if (!argv[0]) | ||
108 | puts(s); | ||
109 | if (strchr(s, '=') && idx < MAX_ENV) | ||
110 | env[idx++] = s; | ||
111 | s += strlen(s) + 1; | ||
112 | } | ||
113 | env[idx] = NULL; | ||
114 | |||
115 | idx = 0; | ||
116 | while (env[idx]) | ||
117 | putenv(env[idx++]); | ||
118 | if (argv[0]) | ||
119 | spawn_and_wait(argv); | ||
120 | idx = 0; | ||
121 | while (env[idx]) | ||
122 | bb_unsetenv(env[idx++]); | ||
123 | munmap(netbuf, BUFFER_SIZE); | ||
124 | } | ||
125 | |||
126 | return 0; // not reached | ||
127 | } | ||