blob: 514a9e934a943204d7df8d99d84def0d26d5be89 [file] [log] [blame]
Denys Vlasenko71a5b672015-04-16 12:44:02 +02001/*
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)
33enum {
34 MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1,
35};
36
37#ifndef SO_RCVBUFFORCE
38#define SO_RCVBUFFORCE 33
39#endif
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +020040enum { RCVBUF = 2 * 1024 * 1024 };
Denys Vlasenko71a5b672015-04-16 12:44:02 +020041
42int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
43int 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
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +020066 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF, RCVBUF);
67 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, RCVBUF);
Denys Vlasenko71a5b672015-04-16 12:44:02 +020068 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}