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:       select PLATFORM_LINUX
  10//config:       help
  11//config:       uevent is a netlink listener for kernel uevent notifications
  12//config:       sent via netlink. It is usually used for dynamic device creation.
  13
  14//applet:IF_UEVENT(APPLET(uevent, BB_DIR_SBIN, BB_SUID_DROP))
  15
  16//kbuild:lib-$(CONFIG_UEVENT) += uevent.o
  17
  18//usage:#define uevent_trivial_usage
  19//usage:       "[PROG [ARGS]]"
  20//usage:#define uevent_full_usage "\n\n"
  21//usage:       "uevent runs PROG for every netlink notification."
  22//usage:   "\n""PROG's environment contains data passed from the kernel."
  23//usage:   "\n""Typical usage (daemon for dynamic device node creation):"
  24//usage:   "\n""        # uevent mdev & mdev -s"
  25
  26#include "libbb.h"
  27#include "common_bufsiz.h"
  28#include <linux/netlink.h>
  29
  30#define BUFFER_SIZE 16*1024
  31
  32#define env ((char **)bb_common_bufsiz1)
  33#define INIT_G() do { setup_common_bufsiz(); } while (0)
  34enum {
  35        MAX_ENV = COMMON_BUFSIZE / sizeof(char*) - 1,
  36        /* sizeof(env[0]) instead of sizeof(char*)
  37         * makes gcc-6.3.0 emit "strict-aliasing" warning.
  38         */
  39};
  40
  41#ifndef SO_RCVBUFFORCE
  42#define SO_RCVBUFFORCE 33
  43#endif
  44enum { RCVBUF = 2 * 1024 * 1024 };
  45
  46int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  47int uevent_main(int argc UNUSED_PARAM, char **argv)
  48{
  49        int fd;
  50
  51        INIT_G();
  52
  53        argv++;
  54
  55        // Subscribe for UEVENT kernel messages.
  56        // Without a sufficiently big RCVBUF, a ton of simultaneous events
  57        // can trigger ENOBUFS on read, which is unrecoverable.
  58        // Reproducer:
  59        //      uevent mdev &
  60        //      find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
  61        fd = create_and_bind_to_netlink(NETLINK_KOBJECT_UEVENT, /*groups:*/ 1 << 0, RCVBUF);
  62
  63        for (;;) {
  64                char *netbuf;
  65                char *s, *end;
  66                ssize_t len;
  67                int idx;
  68
  69                // In many cases, a system sits for *days* waiting
  70                // for a new uevent notification to come in.
  71                // We use a fresh mmap so that buffer is not allocated
  72                // until kernel actually starts filling it.
  73                netbuf = mmap(NULL, BUFFER_SIZE,
  74                                        PROT_READ | PROT_WRITE,
  75                                        MAP_PRIVATE | MAP_ANON,
  76                                        /* ignored: */ -1, 0);
  77                if (netbuf == MAP_FAILED)
  78                        bb_perror_msg_and_die("mmap");
  79
  80                // Here we block, possibly for a very long time
  81                len = safe_read(fd, netbuf, BUFFER_SIZE - 1);
  82                if (len < 0)
  83                        bb_perror_msg_and_die("read");
  84                end = netbuf + len;
  85                *end = '\0';
  86
  87                // Each netlink message starts with "ACTION@/path"
  88                // (which we currently ignore),
  89                // followed by environment variables.
  90                if (!argv[0])
  91                        putchar('\n');
  92                idx = 0;
  93                s = netbuf;
  94                while (s < end) {
  95                        if (!argv[0])
  96                                puts(s);
  97                        if (strchr(s, '=') && idx < MAX_ENV)
  98                                env[idx++] = s;
  99                        s += strlen(s) + 1;
 100                }
 101                env[idx] = NULL;
 102
 103                if (argv[0]) {
 104                        idx = 0;
 105                        while (env[idx])
 106                                putenv(env[idx++]);
 107                        spawn_and_wait(argv);
 108                        idx = 0;
 109                        while (env[idx])
 110                                bb_unsetenv(env[idx++]);
 111                }
 112                munmap(netbuf, BUFFER_SIZE);
 113        }
 114
 115        return 0; // not reached
 116}
 117