diff options
-rw-r--r-- | util-linux/mdev.c | 137 |
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 | ||
252 | enum { | 268 | #ifndef SO_RCVBUFFORCE |
253 | MDEV_OPT_SCAN = 1 << 0, | 269 | #define SO_RCVBUFFORCE 33 |
254 | }; | 270 | #endif |
255 | |||
256 | static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" | 271 | static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" |
257 | enum { OP_add, OP_remove }; | 272 | enum { OP_add, OP_remove }; |
258 | 273 | ||
@@ -1051,7 +1066,7 @@ static void signal_mdevs(unsigned my_pid) | |||
1051 | } | 1066 | } |
1052 | } | 1067 | } |
1053 | 1068 | ||
1054 | static NOINLINE void process_action(char *temp, unsigned my_pid) | 1069 | static 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 | |||
1157 | static 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 | |||
1134 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1191 | int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1135 | int mdev_main(int argc UNUSED_PARAM, char **argv) | 1192 | int 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()); |