[PATCH v2 3/3] mdev: add support to run as daemon

Denys Vlasenko vda.linux at googlemail.com
Mon Jun 3 12:20:33 UTC 2019


Applied, thanks

On Tue, May 7, 2019 at 9:00 PM Jan Klötzke <jan at kloetzke.net> wrote:
>
> Adds the -d option to run mdev in daemon mode handling hotplug events
> from the kernel like udev. If the system generates many hotplug events
> this mode of operation will consume less resources than registering
> mdev as hotplug helper or using the uevent applet.
>
> Signed-off-by: Jan Klötzke <jan at kloetzke.net>
> ---
>
> v2: fix typo in commit message and config text
>
>  util-linux/mdev.c | 120 +++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 108 insertions(+), 12 deletions(-)
>
> diff --git a/util-linux/mdev.c b/util-linux/mdev.c
> index 9e2f527d7..3c0b7d104 100644
> --- a/util-linux/mdev.c
> +++ b/util-linux/mdev.c
> @@ -64,15 +64,32 @@
>  //config:      These devices will request userspace look up the files in
>  //config:      /lib/firmware/ and if it exists, send it to the kernel for
>  //config:      loading into the hardware.
> +//config:
> +//config:config FEATURE_MDEV_DAEMON
> +//config:      bool "Support daemon mode"
> +//config:      default y
> +//config:      depends on MDEV
> +//config:      help
> +//config:      Add support to run mdev as daemon.
> +//config:
> +//config:      Adds the -d option to run mdev in daemon mode handling hotplug
> +//config:      events from the kernel like udev. If the system generates many
> +//config:      hotplug events this mode of operation will consume less
> +//config:      resources than registering mdev as hotplug helper or using the
> +//config:      uevent applet.
>
>  //applet:IF_MDEV(APPLET(mdev, BB_DIR_SBIN, BB_SUID_DROP))
>
>  //kbuild:lib-$(CONFIG_MDEV) += mdev.o
>
>  //usage:#define mdev_trivial_usage
> -//usage:       "[-s]"
> +//usage:       "[-s" IF_FEATURE_MDEV_DAEMON(" | -d [-f]") "]"
>  //usage:#define mdev_full_usage "\n\n"
>  //usage:       "mdev -s is to be run during boot to scan /sys and populate /dev.\n"
> +//usage:       IF_FEATURE_MDEV_DAEMON(
> +//usage:       "mdev -d runs mdev as daemon like udev. With -f it will stay in\n"
> +//usage:       "the foreground instead of forking.\n"
> +//usage:       )
>  //usage:       "\n"
>  //usage:       "Bare mdev is a kernel hotplug helper. To activate it:\n"
>  //usage:       "       echo /sbin/mdev >/proc/sys/kernel/hotplug\n"
> @@ -98,6 +115,7 @@
>  #include "libbb.h"
>  #include "common_bufsiz.h"
>  #include "xregex.h"
> +#include <linux/netlink.h>
>
>  /* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
>   * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
> @@ -249,8 +267,18 @@
>  #endif
>
>
> +#define BUFFER_SIZE (2*1024)
> +#define MAX_ENV 32
> +
> +#ifndef SO_RCVBUFFORCE
> +#define SO_RCVBUFFORCE 33
> +#endif
> +enum { RCVBUF = 128 * BUFFER_SIZE };
> +
>  enum {
>         MDEV_OPT_SCAN       = 1 << 0,
> +       MDEV_OPT_DAEMON     = 1 << 1,
> +       MDEV_OPT_FOREGROUND = 1 << 2,
>  };
>
>  static const char keywords[] ALIGN1 = "add\0remove\0"; // "change\0"
> @@ -1077,10 +1105,11 @@ static void process_action(char *temp, unsigned my_pid)
>         seq = getenv("SEQNUM");
>         op = index_in_strings(keywords, action);
>
> -       open_mdev_log(seq, my_pid);
> +       if (my_pid)
> +               open_mdev_log(seq, my_pid);
>
>         seq_fd = -1;
> -       if (seq) {
> +       if (my_pid && seq) {
>                 seqnum = atoll(seq);
>                 seq_fd = wait_for_seqfile(seqnum);
>         }
> @@ -1139,10 +1168,6 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
>
>         INIT_G();
>
> -#if ENABLE_FEATURE_MDEV_CONF
> -       G.filename = "/etc/mdev.conf";
> -#endif
> -
>         /* We can be called as hotplug helper */
>         /* Kernel cannot provide suitable stdio fds for us, do it ourself */
>         bb_sanitize_stdio();
> @@ -1152,17 +1177,88 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
>
>         xchdir("/dev");
>
> -       opt = getopt32(argv, "s");
> +       opt = getopt32(argv, "s" IF_FEATURE_MDEV_DAEMON("df"));
>
> -       if (opt & MDEV_OPT_SCAN) {
> -               /*
> -                * Scan: mdev -s
> -                */
>  #if ENABLE_FEATURE_MDEV_CONF
> +       G.filename = "/etc/mdev.conf";
> +       if (opt & (MDEV_OPT_SCAN|MDEV_OPT_DAEMON)) {
>                 /* Same as xrealloc_vector(NULL, 4, 0): */
>                 G.rule_vec = xzalloc((1 << 4) * sizeof(*G.rule_vec));
> +       }
>  #endif
>
> +#if ENABLE_FEATURE_MDEV_DAEMON
> +       if (opt & MDEV_OPT_DAEMON) {
> +               /*
> +                * Daemon mode listening on uevent netlink socket.
> +                */
> +               struct sockaddr_nl sa;
> +               int fd;
> +
> +               // Subscribe for UEVENT kernel messages
> +               sa.nl_family = AF_NETLINK;
> +               sa.nl_pad = 0;
> +               sa.nl_pid = getpid();
> +               sa.nl_groups = 1 << 0;
> +               fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
> +               xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
> +               close_on_exec_on(fd);
> +
> +               // Without a sufficiently big RCVBUF, a ton of simultaneous events
> +               // can trigger ENOBUFS on read, which is unrecoverable.
> +               // Reproducer:
> +               //      mdev -d
> +               //      find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
> +               //
> +               // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl
> +               setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF,      RCVBUF);
> +               setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, RCVBUF);
> +
> +               /*
> +                * Make inital scan after the uevent socket is alive and
> +                * _before_ we fork away.
> +                */
> +               initial_scan(temp);
> +
> +               if (!(opt & MDEV_OPT_FOREGROUND))
> +                       bb_daemonize_or_rexec(0, argv);
> +
> +               open_mdev_log(NULL, getpid());
> +
> +               for (;;) {
> +                       char netbuf[BUFFER_SIZE];
> +                       char *env_tmp[MAX_ENV];
> +                       char *s, *end;
> +                       ssize_t len;
> +                       int env_idx = 0;
> +
> +                       len = recv(fd, netbuf, BUFFER_SIZE - 1, 0);
> +                       if (len < 0) {
> +                               if (errno == EINTR)
> +                                       continue;
> +                               bb_perror_msg_and_die("recv");
> +                       }
> +                       end = netbuf + len;
> +                       *end = '\0';
> +
> +                       for (s = netbuf; s < end; s = s+strlen(s)+1) {
> +                               if (strchr(s, '=') && env_idx < MAX_ENV-1) {
> +                                       env_tmp[env_idx++] = s;
> +                                       putenv(s);
> +                               }
> +                       }
> +
> +                       process_action(temp, 0);
> +
> +                       while (env_idx)
> +                               bb_unsetenv(env_tmp[--env_idx]);
> +               }
> +       }
> +#endif
> +       if (opt & MDEV_OPT_SCAN) {
> +               /*
> +                * Scan: mdev -s
> +                */
>                 initial_scan(temp);
>         } else {
>                 process_action(temp, getpid());
> --
> 2.20.1
>
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox


More information about the busybox mailing list