[RFC] mdev: support reading events from stdin, add features to mdev -s

Denys Vlasenko vda.linux at googlemail.com
Wed Apr 15 16:46:08 UTC 2015


On Mon, Apr 13, 2015 at 7:15 PM, Isaac Dunham <ibid.ag at gmail.com> wrote:
> Here's a patch series to add mdev -i and make mdev -s a little more
> featureful, so rules like this one:
> $MODALIAS=.*    root:root 660 @modprobe "$MODALIAS"
> will work even with coldplug.
>
> mdev -i reads hotplug events from stdin, in a format proposed by Laurent Bercot
> wherein each "line" is terminated with '\0' (rather than '\n') and each event
> is terminated by an additional '\0'.
> Internally, each event is loaded into the environment (without copying) at
> the start of handle_event(), then the environment is cleared before
> handle_event() returns.
> This is done instead of using a new key lookup method because helper
> scripts rely extensively on environment variables; if we did not load
> the events into the environment at some point, autoloading modules and
> other important features would no longer work.
>
> The new mdev -s (FEATURE_MDEV_NEWSCAN) uses the same codepath as mdev -i
> instead of calling make_device() directly.
>
> Both mdev -i and the new mdev -s are compile-time options, separately
> selectable.

How about the following applet, which would listen to netlink and spawn a helper
for every new message?

So far not tested with "mdev".

# ndev --help
BusyBox v1.24.0.git (2015-04-15 18:31:44 CEST) multi-call binary.

Usage: ndev [PROG [ARGS]]

ndev runs PROG for every netlink notification.
PROG's environment contains data passed from the kernel.
Typical usage:

ndev mdev &    - daemon for dynamic device node creation
ndev        - print notifications to stdout

# ndev

change@/devices/platform/regulatory.0
ACTION=change
DEVPATH=/devices/platform/regulatory.0
SUBSYSTEM=platform
COUNTRY=CZ
MODALIAS=platform:regulatory
SEQNUM=4678

change@/devices/platform/regulatory.0
ACTION=change
DEVPATH=/devices/platform/regulatory.0
SUBSYSTEM=platform
COUNTRY=00
MODALIAS=platform:regulatory
SEQNUM=4679
^C


diff -d -urpN busybox.1/util-linux/ndev.c busybox.2/util-linux/ndev.c
--- busybox.1/util-linux/ndev.c    1970-01-01 01:00:00.000000000 +0100
+++ busybox.2/util-linux/ndev.c    2015-04-15 18:27:58.607840699 +0200
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//config:config NDEV
+//config:    bool "ndev"
+//config:    default y
+//config:    select PLATFORM_LINUX
+//config:    help
+//config:      ndev is a netlink listener for kernel uevent notifications
+//config:      sent via netlink. It is usually used for dynamic
device creation.
+
+//applet:IF_NDEV(APPLET(ndev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NDEV) += ndev.o
+
+//usage:#define ndev_trivial_usage
+//usage:    "[PROG [ARGS]]"
+//usage:#define ndev_full_usage "\n\n"
+//usage:    "ndev runs PROG for every netlink notification.\n"
+//usage:    "PROG's environment contains data passed from the kernel.\n"
+//usage:    "Typical usage:\n"
+//usage:    "\n"
+//usage:    "ndev mdev &    - daemon for dynamical device node creation\n"
+//usage:    "ndev        - print notifications to stdout\n"
+
+#include "libbb.h"
+#include <linux/netlink.h>
+
+#define BUFFER_SIZE 16*1024
+
+int ndev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ndev_main(int argc UNUSED_PARAM, char **argv)
+{
+    struct sockaddr_nl sa;
+    struct pollfd pfd;
+
+    argv++;
+
+    // Subscribe for UEVENT kernel messages
+    close(STDIN_FILENO);
+    // opened socket will be STDIN_FILENO fd
+    xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+    sa.nl_family = AF_NETLINK;
+    sa.nl_pad = 0;
+    sa.nl_pid = getpid();
+    sa.nl_groups = 1 << 0;
+    xbind(STDIN_FILENO, (struct sockaddr *) &sa, sizeof(sa));
+
+    pfd.fd = STDIN_FILENO;
+    pfd.events = POLLIN;
+
+    for (;;) {
+        char *netbuf;
+        char *s;
+        char **env;
+        ssize_t len;
+        int idx;
+
+        /* In many cases, a system can sit for DAYS waiting
+         * for a new uevent notification to come in.
+         * We use mmap so that the buffer is not allocated
+         * until kernel actually starts filling it.
+         */
+        netbuf = mmap(NULL, BUFFER_SIZE,
+                    PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_ANON,
+                    /* ignored: */ -1, 0);
+        if (netbuf == MAP_FAILED)
+            bb_perror_msg_and_die("mmap");
+
+        // Here we block. Possibly for a very long time.
+        len = safe_read(STDIN_FILENO, netbuf, BUFFER_SIZE - 1);
+        if (len < 0)
+            bb_perror_msg_and_die("read");
+        netbuf[len] = '\0';
+
+        // Each netlink message starts with "ACTION@/path",
+        // followed by environment variables.
+        if (!argv[0])
+            putchar('\n');
+        env = NULL;
+        idx = 0;
+        s = netbuf;
+        while (len > 0) {
+            char *p;
+
+            if (!argv[0])
+                puts(s);
+
+            p = (char*)endofname(s);
+            if (*p == '@') {
+                // This is "ACTION@/path", ignore
+            } else if (*p == '=') {
+                env = xrealloc_vector(env, 2, idx);
+                env[idx++] = s;
+            }
+            p += strlen(p) + 1;
+            len -= p - s;
+            s = p;
+        }
+
+        idx = 0;
+        while (env && env[idx])
+            putenv(env[idx++]);
+        if (argv[0])
+            spawn_and_wait(argv);
+        idx = 0;
+        while (env && env[idx])
+            bb_unsetenv(env[idx++]);
+        free(env);
+        munmap(netbuf, BUFFER_SIZE);
+    }
+
+    return 0;
+}

(sorry for formatting, I'm using gmail web interface...)


More information about the busybox mailing list