aboutsummaryrefslogtreecommitdiff
path: root/util-linux/mdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/mdev.c')
-rw-r--r--util-linux/mdev.c119
1 files changed, 80 insertions, 39 deletions
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index 59dbcf0cd..dbbcbc655 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -80,12 +80,14 @@
80//kbuild:lib-$(CONFIG_MDEV) += mdev.o 80//kbuild:lib-$(CONFIG_MDEV) += mdev.o
81 81
82//usage:#define mdev_trivial_usage 82//usage:#define mdev_trivial_usage
83//usage: "[-s]" IF_FEATURE_MDEV_DAEMON(" | [-df]") 83//usage: "[-vS] " IF_FEATURE_MDEV_DAEMON("{ ") "[-s]" IF_FEATURE_MDEV_DAEMON(" | [-df] }")
84//usage:#define mdev_full_usage "\n\n" 84//usage:#define mdev_full_usage "\n\n"
85//usage: "mdev -s is to be run during boot to scan /sys and populate /dev.\n" 85//usage: " -v verbose\n"
86//usage: " -S log to syslog too\n"
87//usage: " -s scan /sys and populate /dev\n"
86//usage: IF_FEATURE_MDEV_DAEMON( 88//usage: IF_FEATURE_MDEV_DAEMON(
87//usage: "mdev -d[f]: daemon, listen on netlink.\n" 89//usage: " -d daemon, listen on netlink\n"
88//usage: " -f: stay in foreground.\n" 90//usage: " -f stay in foreground\n"
89//usage: ) 91//usage: )
90//usage: "\n" 92//usage: "\n"
91//usage: "Bare mdev is a kernel hotplug helper. To activate it:\n" 93//usage: "Bare mdev is a kernel hotplug helper. To activate it:\n"
@@ -113,6 +115,7 @@
113#include "common_bufsiz.h" 115#include "common_bufsiz.h"
114#include "xregex.h" 116#include "xregex.h"
115#include <linux/netlink.h> 117#include <linux/netlink.h>
118#include <syslog.h>
116 119
117/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev 120/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
118 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev 121 * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
@@ -269,10 +272,6 @@
269# define dbg3s(msg) ((void)0) 272# define dbg3s(msg) ((void)0)
270#endif 273#endif
271 274
272
273#ifndef SO_RCVBUFFORCE
274#define SO_RCVBUFFORCE 33
275#endif
276static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0" 275static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
277enum { OP_add, OP_remove }; 276enum { OP_add, OP_remove };
278 277
@@ -297,7 +296,7 @@ struct rule {
297 296
298struct globals { 297struct globals {
299 int root_major, root_minor; 298 int root_major, root_minor;
300 smallint verbose; 299 int verbose;
301 char *subsystem; 300 char *subsystem;
302 char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */ 301 char *subsys_env; /* for putenv("SUBSYSTEM=subsystem") */
303#if ENABLE_FEATURE_MDEV_CONF 302#if ENABLE_FEATURE_MDEV_CONF
@@ -921,7 +920,7 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
921 loading_fd = open("loading", O_WRONLY); 920 loading_fd = open("loading", O_WRONLY);
922 if (loading_fd >= 0) 921 if (loading_fd >= 0)
923 goto loading; 922 goto loading;
924 sleep(1); 923 sleep1();
925 } 924 }
926 goto out; 925 goto out;
927 926
@@ -964,7 +963,7 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
964static char *curtime(void) 963static char *curtime(void)
965{ 964{
966 struct timeval tv; 965 struct timeval tv;
967 gettimeofday(&tv, NULL); 966 xgettimeofday(&tv);
968 sprintf( 967 sprintf(
969 strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec), 968 strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
970 ".%06u", 969 ".%06u",
@@ -1152,15 +1151,50 @@ static void initial_scan(char *temp)
1152 1151
1153#if ENABLE_FEATURE_MDEV_DAEMON 1152#if ENABLE_FEATURE_MDEV_DAEMON
1154 1153
1155/* uevent applet uses 16k buffer, and mmaps it before every read */ 1154/*
1156# define BUFFER_SIZE (2 * 1024) 1155 * The kernel (as of v5.4) will pass up to 32 environment variables with a
1157# define RCVBUF (2 * 1024 * 1024) 1156 * total of 2kiB on each event. On top of that the action string and device
1157 * path are added. Using a 3kiB buffer for the event should suffice in any
1158 * case.
1159 *
1160 * As far as the socket receive buffer size is concerned 2MiB proved to be too
1161 * small (see [1]). Udevd seems to use a whooping 128MiB. The socket receive
1162 * buffer size is just a resource limit. The buffers are allocated lazily so
1163 * the memory is not wasted.
1164 *
1165 * [1] http://lists.busybox.net/pipermail/busybox/2019-December/087665.html
1166 */
1167# define USER_RCVBUF (3 * 1024)
1168# define KERN_RCVBUF (128 * 1024 * 1024)
1158# define MAX_ENV 32 1169# define MAX_ENV 32
1159 1170
1171static int daemon_init(char *temp)
1172{
1173 int fd;
1174
1175 /* Subscribe for UEVENT kernel messages */
1176 /* Without a sufficiently big RCVBUF, a ton of simultaneous events
1177 * can trigger ENOBUFS on read, which is unrecoverable.
1178 * Reproducer:
1179 * mdev -d
1180 * find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
1181 */
1182 fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, 1 << 0, KERN_RCVBUF);
1183
1184 /*
1185 * Make inital scan after the uevent socket is alive and
1186 * _before_ we fork away. Already open mdev.log because we work
1187 * in daemon mode.
1188 */
1189 initial_scan(temp);
1190
1191 return fd;
1192}
1193
1160static void daemon_loop(char *temp, int fd) 1194static void daemon_loop(char *temp, int fd)
1161{ 1195{
1162 for (;;) { 1196 for (;;) {
1163 char netbuf[BUFFER_SIZE]; 1197 char netbuf[USER_RCVBUF];
1164 char *env[MAX_ENV]; 1198 char *env[MAX_ENV];
1165 char *s, *end; 1199 char *s, *end;
1166 ssize_t len; 1200 ssize_t len;
@@ -1168,6 +1202,16 @@ static void daemon_loop(char *temp, int fd)
1168 1202
1169 len = safe_read(fd, netbuf, sizeof(netbuf) - 1); 1203 len = safe_read(fd, netbuf, sizeof(netbuf) - 1);
1170 if (len < 0) { 1204 if (len < 0) {
1205 if (errno == ENOBUFS) {
1206 /*
1207 * We ran out of socket receive buffer space.
1208 * Start from scratch.
1209 */
1210 dbg1s("uevent overrun, rescanning");
1211 close(fd);
1212 fd = daemon_init(temp);
1213 continue;
1214 }
1171 bb_simple_perror_msg_and_die("read"); 1215 bb_simple_perror_msg_and_die("read");
1172 } 1216 }
1173 end = netbuf + len; 1217 end = netbuf + len;
@@ -1196,8 +1240,9 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
1196{ 1240{
1197 enum { 1241 enum {
1198 MDEV_OPT_SCAN = 1 << 0, 1242 MDEV_OPT_SCAN = 1 << 0,
1199 MDEV_OPT_DAEMON = 1 << 1, 1243 MDEV_OPT_SYSLOG = 1 << 1,
1200 MDEV_OPT_FOREGROUND = 1 << 2, 1244 MDEV_OPT_DAEMON = 1 << 2,
1245 MDEV_OPT_FOREGROUND = 1 << 3,
1201 }; 1246 };
1202 int opt; 1247 int opt;
1203 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE); 1248 RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
@@ -1213,7 +1258,11 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
1213 1258
1214 xchdir("/dev"); 1259 xchdir("/dev");
1215 1260
1216 opt = getopt32(argv, "s" IF_FEATURE_MDEV_DAEMON("df")); 1261 opt = getopt32(argv, "^"
1262 "sS" IF_FEATURE_MDEV_DAEMON("df") "v"
1263 "\0"
1264 "vv",
1265 &G.verbose);
1217 1266
1218#if ENABLE_FEATURE_MDEV_CONF 1267#if ENABLE_FEATURE_MDEV_CONF
1219 G.filename = "/etc/mdev.conf"; 1268 G.filename = "/etc/mdev.conf";
@@ -1223,32 +1272,24 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
1223 } 1272 }
1224#endif 1273#endif
1225 1274
1275 if (opt & MDEV_OPT_SYSLOG) {
1276 openlog(applet_name, LOG_PID, LOG_DAEMON);
1277 logmode |= LOGMODE_SYSLOG;
1278 }
1279
1226#if ENABLE_FEATURE_MDEV_DAEMON 1280#if ENABLE_FEATURE_MDEV_DAEMON
1227 if (opt & MDEV_OPT_DAEMON) { 1281 if (opt & MDEV_OPT_DAEMON) {
1228 /* 1282 /* Daemon mode listening on uevent netlink socket. Fork away
1229 * Daemon mode listening on uevent netlink socket. 1283 * after initial scan so that caller can be sure everything
1230 */ 1284 * is up-to-date when mdev process returns.
1231 int fd;
1232
1233 /* Subscribe for UEVENT kernel messages */
1234 /* Without a sufficiently big RCVBUF, a ton of simultaneous events
1235 * can trigger ENOBUFS on read, which is unrecoverable.
1236 * Reproducer:
1237 * mdev -d
1238 * find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
1239 */ 1285 */
1240 fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, 1 << 0, RCVBUF); 1286 int fd = daemon_init(temp);
1241 1287
1242 /* 1288 if (!(opt & MDEV_OPT_FOREGROUND)) {
1243 * Make inital scan after the uevent socket is alive and 1289 /* there is no point in logging to /dev/null */
1244 * _before_ we fork away. 1290 logmode &= ~LOGMODE_STDIO;
1245 */
1246 initial_scan(temp);
1247
1248 if (!(opt & MDEV_OPT_FOREGROUND))
1249 bb_daemonize_or_rexec(0, argv); 1291 bb_daemonize_or_rexec(0, argv);
1250 1292 }
1251 open_mdev_log(NULL, getpid());
1252 1293
1253 daemon_loop(temp, fd); 1294 daemon_loop(temp, fd);
1254 } 1295 }