busybox/util-linux/uevent.c
<<
>>
Prefs
   1/*
   2 * Copyright 2015 Denys Vlasenko
   3 *
   4 * Licensed under GPLv2, see file LICENSE in this source tree.
   5 */
   6//config:config UEVENT
   7//config:       bool "uevent (3.1 kb)"
   8//config:       default y
   9//config:       help
  10//config:       uevent is a netlink listener for kernel uevent notifications
  11//config:       sent via netlink. It is usually used for dynamic device creation.
  12
  13//applet:IF_UEVENT(APPLET(uevent, BB_DIR_SBIN, BB_SUID_DROP))
  14
  15//kbuild:lib-$(CONFIG_UEVENT) += uevent.o
  16
  17//usage:#define uevent_trivial_usage
  18//usage:       "[PROG ARGS]"
  19//usage:#define uevent_full_usage "\n\n"
  20//usage:       "uevent runs PROG for every netlink notification."
  21//usage:   "\n""PROG's environment contains data passed from the kernel."
  22//usage:   "\n""Typical usage (daemon for dynamic device node creation):"
  23//usage:   "\n""        # uevent mdev & mdev -s"
  24
  25#include "libbb.h"
  26#include "common_bufsiz.h"
  27#include <linux/netlink.h>
  28
  29#define env ((char **)bb_common_bufsiz1)
  30#define INIT_G() do { setup_common_bufsiz(); } while (0)
  31enum {
  32        MAX_ENV = COMMON_BUFSIZE / sizeof(char*) - 1,
  33        // ^^^sizeof(env[0]) instead of sizeof(char*)
  34        // makes gcc-6.3.0 emit "strict-aliasing" warning.
  35
  36        // socket receive buffer of 2MiB proved to be too small:
  37        //  http://lists.busybox.net/pipermail/busybox/2019-December/087665.html
  38        // udevd seems to use a whooping 128MiB.
  39        // The socket receive buffer size is just a resource limit.
  40        // The buffers are allocated lazily so the memory is not wasted.
  41        KERN_RCVBUF = 128 * 1024 * 1024,
  42
  43        // Might be made smaller: the kernel v5.4 passes up to 32 environment
  44        // variables with a total of 2kb on each event.
  45        // On top of that the action string and device path are added.
  46        USER_RCVBUF = 16 * 1024,
  47};
  48
  49int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  50int uevent_main(int argc UNUSED_PARAM, char **argv)
  51{
  52        int fd;
  53
  54        INIT_G();
  55
  56        argv++;
  57
  58        // Subscribe for UEVENT kernel messages.
  59        // Without a sufficiently big RCVBUF, a ton of simultaneous events
  60        // can trigger ENOBUFS on read, which is unrecoverable.
  61        // Reproducer:
  62        //      uevent mdev &
  63        //      find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
  64 reopen:
  65        fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, /*groups:*/ 1 << 0, KERN_RCVBUF);
  66
  67        for (;;) {
  68                char *netbuf;
  69                char *s, *end;
  70                ssize_t len;
  71                int idx;
  72
  73                // In many cases, a system sits for *days* waiting
  74                // for a new uevent notification to come in.
  75                // We use a fresh mmap so that buffer is not allocated
  76                // until kernel actually starts filling it.
  77                netbuf = xmmap_anon(USER_RCVBUF);
  78
  79                // Here we block, possibly for a very long time
  80                len = safe_read(fd, netbuf, USER_RCVBUF - 1);
  81                if (len < 0) {
  82                        if (errno == ENOBUFS) {
  83                                // Ran out of socket receive buffer
  84                                bb_simple_error_msg("uevent overrun");
  85                                close(fd);
  86                                munmap(netbuf, USER_RCVBUF);
  87                                goto reopen;
  88                        }
  89                        bb_simple_perror_msg_and_die("read");
  90                }
  91                end = netbuf + len;
  92                *end = '\0';
  93
  94                // Each netlink message starts with "ACTION@/path"
  95                // (which we currently ignore),
  96                // followed by environment variables.
  97                if (!argv[0])
  98                        putchar('\n');
  99                idx = 0;
 100                s = netbuf;
 101                while (s < end) {
 102                        if (!argv[0])
 103                                puts(s);
 104                        if (strchr(s, '=') && idx < MAX_ENV)
 105                                env[idx++] = s;
 106                        s += strlen(s) + 1;
 107                }
 108                env[idx] = NULL;
 109
 110                if (argv[0]) {
 111                        idx = 0;
 112                        while (env[idx])
 113                                putenv(env[idx++]);
 114                        spawn_and_wait(argv);
 115                        idx = 0;
 116                        while (env[idx])
 117                                bb_unsetenv(env[idx++]);
 118                }
 119                munmap(netbuf, USER_RCVBUF);
 120        }
 121
 122        return 0; // not reached
 123}
 124