aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Klötzke <jan@kloetzke.net>2019-05-07 20:59:43 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2019-06-03 13:24:49 +0200
commit498cec202adbf69a7a72af5e204260682d614183 (patch)
treede90b887f83b143b7092bfcc054f5e09fe21bef1
parent728e53d38887c268b3557afadfc38472fa4f0cd6 (diff)
downloadbusybox-w32-498cec202adbf69a7a72af5e204260682d614183.tar.gz
busybox-w32-498cec202adbf69a7a72af5e204260682d614183.tar.bz2
busybox-w32-498cec202adbf69a7a72af5e204260682d614183.zip
mdev: add support to run as daemon
Adds the -d option to run mdev in daemon mode handling hotplug events from the kernel like udev. If the system generates many hotplug events this mode of operation will consume less resources than registering mdev as hotplug helper or using the uevent applet. function old new delta daemon_loop - 152 +152 initial_scan - 127 +127 open_mdev_log - 85 +85 mdev_main 255 314 +59 packed_usage 33284 33316 +32 process_action 1051 992 -59 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 2/1 up/down: 455/-59) Total: 396 bytes Signed-off-by: Jan Klötzke <jan@kloetzke.net> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-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());