aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util-linux/mdev.c137
1 files changed, 120 insertions, 17 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index e730548f7..88c82b6fb 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -64,15 +64,30 @@
64//config: These devices will request userspace look up the files in 64//config: These devices will request userspace look up the files in
65//config: /lib/firmware/ and if it exists, send it to the kernel for 65//config: /lib/firmware/ and if it exists, send it to the kernel for
66//config: loading into the hardware. 66//config: loading into the hardware.
67//config:
68//config:config FEATURE_MDEV_DAEMON
69//config: bool "Support daemon mode"
70//config: default y
71//config: depends on MDEV
72//config: help
73//config: Adds the -d option to run mdev in daemon mode handling hotplug
74//config: events from the kernel like udev. If the system generates many
75//config: hotplug events this mode of operation will consume less
76//config: resources than registering mdev as hotplug helper or using the
77//config: uevent applet.
67 78
68//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP)) 79//applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
69 80
70//kbuild:lib-$(CONFIG_MDEV) += mdev.o 81//kbuild:lib-$(CONFIG_MDEV) += mdev.o
71 82
72//usage:#define mdev_trivial_usage 83//usage:#define mdev_trivial_usage
73//usage: "[-s]" 84//usage: "[-s]" IF_FEATURE_MDEV_DAEMON(" | [-df]")
74//usage:#define mdev_full_usage "\n\n" 85//usage:#define mdev_full_usage "\n\n"
75//usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n" 86//usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
87//usage: IF_FEATURE_MDEV_DAEMON(
88//usage: "mdev -d[f]: daemon, listen on netlink.\n"
89//usage: " -f: stay in foreground.\n"
90//usage: )
76//usage: "\n" 91//usage: "\n"
77//usage: "Bare mdev is a kernel hotplug helper. To activate it:\n" 92//usage: "Bare mdev is a kernel hotplug helper. To activate it:\n"
78//usage: " echo /sbin/mdev >/proc/sys/kernel/hotplug\n" 93//usage: " echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
@@ -98,6 +113,7 @@
98#include "libbb.h" 113#include "libbb.h"
99#include "common_bufsiz.h" 114#include "common_bufsiz.h"
100#include "xregex.h" 115#include "xregex.h"
116#include <linux/netlink.h>
101 117
102/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev 118/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
103 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev 119 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
@@ -249,10 +265,9 @@
249#endif 265#endif
250 266
251 267
252enum { 268#ifndef SO_RCVBUFFORCE
253 MDEV_OPT_SCAN = 1 << 0, 269#define SO_RCVBUFFORCE 33
254}; 270#endif
255
256static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" 271static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
257enum { OP_add, OP_remove }; 272enum { OP_add, OP_remove };
258 273
@@ -1051,7 +1066,7 @@ static void signal_mdevs(unsigned my_pid)
1051 } 1066 }
1052} 1067}
1053 1068
1054static NOINLINE void process_action(char *temp, unsigned my_pid) 1069static void process_action(char *temp, unsigned my_pid)
1055{ 1070{
1056 char *fw; 1071 char *fw;
1057 char *seq; 1072 char *seq;
@@ -1077,10 +1092,11 @@ static NOINLINE void process_action(char *temp, unsigned my_pid)
1077 seq = getenv("SEQNUM"); 1092 seq = getenv("SEQNUM");
1078 op = index_in_strings(keywords, action); 1093 op = index_in_strings(keywords, action);
1079 1094
1080 open_mdev_log(seq, my_pid); 1095 if (my_pid)
1096 open_mdev_log(seq, my_pid);
1081 1097
1082 seq_fd = -1; 1098 seq_fd = -1;
1083 if (seq) { 1099 if (my_pid && seq) {
1084 seqnum = atoll(seq); 1100 seqnum = atoll(seq);
1085 seq_fd = wait_for_seqfile(seqnum); 1101 seq_fd = wait_for_seqfile(seqnum);
1086 } 1102 }
@@ -1131,18 +1147,60 @@ static void initial_scan(char *temp)
1131 fileAction, dirAction, temp, 0); 1147 fileAction, dirAction, temp, 0);
1132} 1148}
1133 1149
1150#if ENABLE_FEATURE_MDEV_DAEMON
1151
1152/* uevent applet uses 16k buffer, and mmaps it before every read */
1153# define BUFFER_SIZE (2 * 1024)
1154# define RCVBUF (2 * 1024 * 1024)
1155# define MAX_ENV 32
1156
1157static void daemon_loop(char *temp, int fd)
1158{
1159 for (;;) {
1160 char netbuf[BUFFER_SIZE];
1161 char *env[MAX_ENV];
1162 char *s, *end;
1163 ssize_t len;
1164 int idx;
1165
1166 len = safe_read(fd, netbuf, sizeof(netbuf) - 1);
1167 if (len < 0) {
1168 bb_perror_msg_and_die("read");
1169 }
1170 end = netbuf + len;
1171 *end = '\0';
1172
1173 idx = 0;
1174 s = netbuf;
1175 while (s < end && idx < MAX_ENV) {
1176 if (endofname(s)[0] == '=') {
1177 env[idx++] = s;
1178 putenv(s);
1179 }
1180 s += strlen(s) + 1;
1181 }
1182
1183 process_action(temp, 0);
1184
1185 while (idx)
1186 bb_unsetenv(env[--idx]);
1187 }
1188}
1189#endif
1190
1134int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1191int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1135int mdev_main(int argc UNUSED_PARAM, char **argv) 1192int mdev_main(int argc UNUSED_PARAM, char **argv)
1136{ 1193{
1194 enum {
1195 MDEV_OPT_SCAN = 1 << 0,
1196 MDEV_OPT_DAEMON = 1 << 1,
1197 MDEV_OPT_FOREGROUND = 1 << 2,
1198 };
1137 int opt; 1199 int opt;
1138 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); 1200 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
1139 1201
1140 INIT_G(); 1202 INIT_G();
1141 1203
1142#if ENABLE_FEATURE_MDEV_CONF
1143 G.filename = "/etc/mdev.conf";
1144#endif
1145
1146 /* We can be called as hotplug helper */ 1204 /* We can be called as hotplug helper */
1147 /* Kernel cannot provide suitable stdio fds for us, do it ourself */ 1205 /* Kernel cannot provide suitable stdio fds for us, do it ourself */
1148 bb_sanitize_stdio(); 1206 bb_sanitize_stdio();
@@ -1152,17 +1210,62 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
1152 1210
1153 xchdir("/dev"); 1211 xchdir("/dev");
1154 1212
1155 opt = getopt32(argv, "s"); 1213 opt = getopt32(argv, "s" IF_FEATURE_MDEV_DAEMON("df"));
1156 1214
1157 if (opt & MDEV_OPT_SCAN) {
1158 /*
1159 * Scan: mdev -s
1160 */
1161#if ENABLE_FEATURE_MDEV_CONF 1215#if ENABLE_FEATURE_MDEV_CONF
1216 G.filename = "/etc/mdev.conf";
1217 if (opt & (MDEV_OPT_SCAN|MDEV_OPT_DAEMON)) {
1162 /* Same as xrealloc_vector(NULL, 4, 0): */ 1218 /* Same as xrealloc_vector(NULL, 4, 0): */
1163 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec)); 1219 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
1220 }
1164#endif 1221#endif
1165 1222
1223#if ENABLE_FEATURE_MDEV_DAEMON
1224 if (opt & MDEV_OPT_DAEMON) {
1225 /*
1226 * Daemon mode listening on uevent netlink socket.
1227 */
1228 struct sockaddr_nl sa;
1229 int fd;
1230
1231//TODO: reuse same code in uevent
1232 // Subscribe for UEVENT kernel messages
1233 sa.nl_family = AF_NETLINK;
1234 sa.nl_pad = 0;
1235 sa.nl_pid = getpid();
1236 sa.nl_groups = 1 << 0;
1237 fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
1238 xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
1239 close_on_exec_on(fd);
1240
1241 // Without a sufficiently big RCVBUF, a ton of simultaneous events
1242 // can trigger ENOBUFS on read, which is unrecoverable.
1243 // Reproducer:
1244 // mdev -d
1245 // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
1246 //
1247 // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl
1248 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF, RCVBUF);
1249 setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, RCVBUF);
1250
1251 /*
1252 * Make inital scan after the uevent socket is alive and
1253 * _before_ we fork away.
1254 */
1255 initial_scan(temp);
1256
1257 if (!(opt & MDEV_OPT_FOREGROUND))
1258 bb_daemonize_or_rexec(0, argv);
1259
1260 open_mdev_log(NULL, getpid());
1261
1262 daemon_loop(temp, fd);
1263 }
1264#endif
1265 if (opt & MDEV_OPT_SCAN) {
1266 /*
1267 * Scan: mdev -s
1268 */
1166 initial_scan(temp); 1269 initial_scan(temp);
1167 } else { 1270 } else {
1168 process_action(temp, getpid()); 1271 process_action(temp, getpid());