From 4984158b95c5a0c3922cd34940c393652d080f99 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Sun, 14 Jan 2018 17:54:42 +0000 Subject: New mdevd model, prepare for 0.1.0.0 - mdevd-netlink removed - mdevd listens to the netlink itself - mdevd-coldplug writes nothing to stdout, but triggers the kernel to create uevents --- COPYING | 2 +- INSTALL | 2 +- NEWS | 9 ++ doc/index.html | 5 +- doc/mdevd-coldplug.html | 25 ++--- doc/mdevd-netlink.html | 124 ---------------------- doc/mdevd.html | 44 ++++---- doc/upgrade.html | 11 ++ package/deps.mak | 7 +- package/info | 2 +- package/modes | 1 - package/targets.mak | 1 - src/mdevd/deps-exe/mdevd-netlink | 1 - src/mdevd/mdevd-coldplug.c | 152 ++++++++++++--------------- src/mdevd/mdevd-netlink.c | 177 ------------------------------- src/mdevd/mdevd.c | 219 ++++++++++++++++++++++++++++----------- src/mdevd/mdevd.h | 8 -- 17 files changed, 279 insertions(+), 511 deletions(-) delete mode 100644 doc/mdevd-netlink.html delete mode 100644 src/mdevd/deps-exe/mdevd-netlink delete mode 100644 src/mdevd/mdevd-netlink.c delete mode 100644 src/mdevd/mdevd.h diff --git a/COPYING b/COPYING index 9e2739b..8a62df1 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2017 Laurent Bercot +Copyright (c) 2017-2018 Laurent Bercot Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/INSTALL b/INSTALL index 7385164..876e1da 100644 --- a/INSTALL +++ b/INSTALL @@ -6,7 +6,7 @@ Build Instructions - A Linux-based system with a standard C development environment - GNU make version 3.81 or later - - skalibs version 2.6.0.2 or later: http://skarnet.org/software/skalibs/ + - skalibs version 2.6.3.0 or later: http://skarnet.org/software/skalibs/ This software is Linux-specific. It will run on a Linux kernel, version 2.6.10 or later. diff --git a/NEWS b/NEWS index 78f3327..32d7343 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,14 @@ Changelog for mdevd. +In 0.1.0.0 +---------- + + - mdevd-netlink removed. + - mdevd now listens to the netlink itself. + - mdevd-coldplug now doesn't print events to stdout; it triggers +the events in the kernel instead. This means mdevd should run first +to catch the events, and then mdevd-coldplug should be ran. + In 0.0.1.0 ---------- diff --git a/doc/index.html b/doc/index.html index c11b508..19a43a4 100644 --- a/doc/index.html +++ b/doc/index.html @@ -53,7 +53,7 @@ entirely compatible with advanced mdev usage such as The Linux kernel must be 2.6.10 or later.
  • GNU make, version 3.81 or later
  • skalibs version -2.6.0.2 or later. It's a build-time requirement. It's also a run-time +2.6.3.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library.
  • @@ -69,7 +69,7 @@ library.
    • The current released version of mdevd is -0.0.1.0.
    • +0.1.0.0.
    • Alternatively, you can checkout a copy of the mdevd git repository: @@ -100,7 +100,6 @@ the previous versions of mdevd and the current one.
    • diff --git a/doc/mdevd-coldplug.html b/doc/mdevd-coldplug.html index fbbb5aa..027c849 100644 --- a/doc/mdevd-coldplug.html +++ b/doc/mdevd-coldplug.html @@ -21,8 +21,8 @@

      mdevd-coldplug performs a coldplug: it scans /sys for all registered devices a uevent manager would -want to perform actions on, and generates uevents for all these -devices. +want to perform actions on, and tells the kernel to generate uevents +for all these devices.

      Interface

      @@ -33,17 +33,13 @@ devices.
      • mdevd-coldplug scans /sys for devices.
      • -
      • For every suitable device it finds, it generates a -uevent and writes it to its stdout, using the same format -as mdevd-netlink.
      • -
      • It exits when it has finished scanning.
      • +
      • For every suitable device it finds, it tells the kernel +to generate an event. If a device manager such as +as mdevd is listening to the netlink +at this point, it will pick up the series of events.
      • +
      • mdevd-coldplug exits when it has finished scanning.
      -

      - This implies that the mdevd-coldplug | mdevd command line -will function as a coldplug manager, just like mdev -s. -

      -

      Options

        @@ -62,12 +58,7 @@ pseudo-filesystem is mounted on slashsys. Default is /sys.
      • mdevd-coldplug is a short-lived program, just like -mdev -s.
      • -
      • Unlike mdev -s, however, mdevd-coldplug does -not act on the uevents it generates. It simply prints them. -This allows for easy debugging.
      • -
      • To act on the uevents, simply pipe the output of -mdevd-coldplug into mdevd.
      • +mdev -s or udevadm trigger.
      diff --git a/doc/mdevd-netlink.html b/doc/mdevd-netlink.html deleted file mode 100644 index d0aa054..0000000 --- a/doc/mdevd-netlink.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - mdevd: the mdevd-netlink program - - - - - - -

      -mdevd
      -Software
      -skarnet.org -

      - -

      The mdevd-netlink program

      - -

      -mdevd-netlink listens to the netlink interface for uevents -(also called "hotplug" or "udev" events), and writes those uevents to -its standard output, using a simple format. -

      - -

      -mdevd expects uevents in the same -format mdevd-netlink writes them. So the -mdevd-netlink | mdevd command line will function as -a daemon reading uevents from the netlink and handling them. -

      - -

      Interface

      - -
      -     mdevd-netlink [ -d notif ] [ -v verbosity ] [ -b kbufsz ]
      -
      - -
        -
      • mdevd-netlink binds to the netlink interface and listens for -hotplug events, as the udevd program does.
      • -
      • It writes event information to its stdout. The output contains -null characters, so a terminal will not display them correctly. To -properly use mdevd-netlink, it should be piped into a handler -program such as mdevd.
      • -
      • mdevd-netlink is a long-lived program. -When it receives a SIGTERM, it stops listening to hotplug events; -it will exit as soon as it has flushed its event queue to stdout.
      • -
      - -

      Options

      - -
        -
      • -d notif : when ready (actually -listening to the netlink), write a newline to file descriptor notif -then close it. This allows mdevd-netlink to use the -s6 mechanism to -notify readiness. notif cannot be lesser than 3. If this -option is not given, no readiness notification is sent.
      • -
      • -v verbosity : be more or less verbose. -Default verbosity is 1. 0 will only print fatal error messages, 3 will -print warnings every time the netlink interface sends something -unexpected.
      • -
      • -b kbufsz : try and reserve a kernel buffer of -kbufsz bytes for the netlink queue. Too large a buffer wastes kernel memory; -too small a buffer risks losing events. The default is 65536 (which is on -the large side).
      • -
      - -

      Protocol

      - -
        -
      • An event is a series of null-terminated strings as they are sent by -the kernel to the netlink; mdevd-netlink adds a final empty string -(i.e. an additional null character) to mark the end of the series.
      • -
      • The first string is a short description of the event; it normally -contains the string "@/". Other strings after the first are of the form -"VARIABLE=value", and describe the environment which a hotplug helper -for the event (registered in /proc/sys/kernel/hotplug) would be -spawned with.
      • -
      • Example (newlines added for clarity):
        -add@/class/input/input9/mouse2\0
        -ACTION=add\0
        -DEVPATH=/class/input/input9/mouse2\0
        -SUBSYSTEM=input\0
        -SEQNUM=106\0
        -PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2­2/2­2:1.0
        -PHYSDEVBUS=usb\0
        -PHYSDEVDRIVER=usbhid\0
        -MAJOR=13\0
        -MINOR=34\0
        -\0 
      • -
      - -

      Notes

      - -
        -
      • mdevd-netlink is a daemon; it should be run under a proper supervision system such -as s6.
      • -
      • If you are running mdevd-netlink, the program you pipe its -output into should be the only program handling uevents; that means -that /proc/sys/kernel/hotplug should be empty.
      • -
      • The mdevd-netlink | mdevd command line is a quick -way of running a uevent manager, but there are ways to improve it: -
          -
        • Using the -execline scripting -language, the pipeline { mdevd-netlink } mdevd command -line will avoid leaving a shell around.
        • -
        • The best way to use mdevd-netlink and mdevd is with a -supervision system: -under s6 or -s6-rc, write a service -pipeline where mdevd-netlink is a producer and -mdevd is a consumer. This setup has the advantage, among -others, that you can restart the netlink listener and the event handler -separately. The examples/ subdirectory of the mdevd -package contains example configurations for such a setup.
        • -
      • -
      - - - diff --git a/doc/mdevd.html b/doc/mdevd.html index 14d2867..0ea61bb 100644 --- a/doc/mdevd.html +++ b/doc/mdevd.html @@ -19,8 +19,8 @@

      The mdevd program

      -mdevd is a uevent manager. It reads a series of -uevents on its stdin; for every uevent it reads, it performs +mdevd is a uevent manager. It connects to the netlink and reads +a series of uevents; for every uevent it reads, it performs actions according to its configuration file. Actions can be inserting a kernel module, creating or modifying device entries in /dev, etc. @@ -36,35 +36,29 @@ The differences between mdevd and mdev are:

      • mdev needs to be registered as a hotplug manager and the kernel spawns an instance of mdev per uevent; for every uevent, -mdev has to parse its configuration file. By contrast, there -is only one instance of mdevd, reading a series of uevents and +mdev has to parse its configuration file. By contrast, mdevd is +a daemon: it's long-lived, and there is only one instance, +reading a series of uevents and performing actions without forking; the configuration file is read and parsed only once.
      • -
      • mdevd reads uevents on its stdin. It is not suitable as -a hotplug manager, and it does not connect to the netlink itself -either. It is meant to be used in conjunction with -mdevd-netlink, which reads -uevents from the netlink, or with -mdevd-coldplug, which generates -coldplug uevents.

      Interface

      -     mdevd [ -v verbosity ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ] [ -F fwbase ]
      +     mdevd [ -v verbosity ] [ -D notif ] [ -b kbufsz ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ] [ -F fwbase ]
       
      • mdevd reads and parses its configuration file /etc/mdev.conf.
      • -
      • It then reads its stdin, waiting for uevents. -
      • It exits when the stream of uevents ends. (EOF on stdin.)
      • +
      • It then connects to the netlink and reads from it, waiting for uevents. +
      • It exits 0 on a SIGTERM.

      Exit codes

        -
      • 0: EOF read on standard input
      • +
      • 0: SIGTERM received, clean exit
      • 1: received an invalid event
      • 2: syntax error in the configuration file
      • 100: wrong usage
      • @@ -79,6 +73,7 @@ coldplug uevents.
        • SIGHUP: re-read the configuration file
        • +
        • SIGTERM: exit as soon as possible

        Options

        @@ -87,6 +82,17 @@ coldplug uevents.
      • -v verbosity : be more or less verbose. Default verbosity is 1. 0 will only print fatal error messages, 3 or more is seriously verbose debugging.
      • +
      • -D notif : when ready +(actually listening to the netlink), +write a newline to file descriptor notif then close it. +This allows mdevd to use the +s6 mechanism to notify +readiness. notif cannot be lesser than 3. +If this option is not given, no readiness notification is sent.
      • +
      • -b kbufsz : try and reserve a kernel buffer of +kbufsz bytes for the netlink queue. Too large a buffer wastes kernel memory; +too small a buffer risks losing events. The default is 65536 (which is reasonable +for average systems).
      • -n : dry run. mdevd will not create or delete device nodes, and it will not spawn commands. Instead, it will print to stdout the actions it would have performed.
      • @@ -121,14 +127,6 @@ nothing else.

        Notes

          -
        • Strictly speaking, mdevd is a short-lived program: it has -a normal exit condition, which is when it receives EOF on its stdin. -That allows it to work as a coldplug manager when paired with -mdevd-coldplug.
        • -
        • However, when paired with mdevd-netlink, -it acts as a daemon, because mdev-netlink normally never exits until -the end of the machine lifetime and never closes its stdout, so -mdevd's stdin never receives EOF.
        • The examples/ subdirectory of the mdevd package contains examples on how to run mdevd under various init systems / supervisors.
        diff --git a/doc/upgrade.html b/doc/upgrade.html index 06c1654..fbbcc00 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -18,6 +18,17 @@

        What has changed in mdevd

        +

        in 0.1.0.0

        + +
          +
        • skalibs +dependency bumped to 2.6.3.0.
        • +
        • The mdevd-netlink program doesn't exist anymore.
        • +
        • mdevd now listens to the netlink itself.
        • +
        • mdevd-coldplug does not print events +to stdout anymore. Instead, it makes the kernel trigger events.
        • +
        +

        in 0.0.1.0

          diff --git a/package/deps.mak b/package/deps.mak index 64c7965..2d9cd98 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -2,13 +2,10 @@ # This file has been generated by tools/gen-deps.sh # -src/mdevd/mdevd-coldplug.o src/mdevd/mdevd-coldplug.lo: src/mdevd/mdevd-coldplug.c src/mdevd/mdevd.h -src/mdevd/mdevd-netlink.o src/mdevd/mdevd-netlink.lo: src/mdevd/mdevd-netlink.c src/mdevd/mdevd.h -src/mdevd/mdevd.o src/mdevd/mdevd.lo: src/mdevd/mdevd.c src/mdevd/mdevd.h +src/mdevd/mdevd-coldplug.o src/mdevd/mdevd-coldplug.lo: src/mdevd/mdevd-coldplug.c +src/mdevd/mdevd.o src/mdevd/mdevd.lo: src/mdevd/mdevd.c mdevd: EXTRA_LIBS := mdevd: src/mdevd/mdevd.o -lskarnet mdevd-coldplug: EXTRA_LIBS := mdevd-coldplug: src/mdevd/mdevd-coldplug.o -lskarnet -mdevd-netlink: EXTRA_LIBS := -mdevd-netlink: src/mdevd/mdevd-netlink.o -lskarnet diff --git a/package/info b/package/info index 41df5d6..1c86703 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=mdevd -version=0.0.1.0 +version=0.1.0.0 category=admin package_macro_name=MDEVD diff --git a/package/modes b/package/modes index 63c3a48..9ea80ce 100644 --- a/package/modes +++ b/package/modes @@ -1,3 +1,2 @@ mdevd 0755 -mdevd-netlink 0755 mdevd-coldplug 0755 diff --git a/package/targets.mak b/package/targets.mak index 6b16bfa..79e56ea 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -1,6 +1,5 @@ BIN_TARGETS := \ mdevd \ -mdevd-netlink \ mdevd-coldplug LIBEXEC_TARGETS := diff --git a/src/mdevd/deps-exe/mdevd-netlink b/src/mdevd/deps-exe/mdevd-netlink deleted file mode 100644 index e7187fe..0000000 --- a/src/mdevd/deps-exe/mdevd-netlink +++ /dev/null @@ -1 +0,0 @@ --lskarnet diff --git a/src/mdevd/mdevd-coldplug.c b/src/mdevd/mdevd-coldplug.c index dabd678..0d64007 100644 --- a/src/mdevd/mdevd-coldplug.c +++ b/src/mdevd/mdevd-coldplug.c @@ -3,70 +3,83 @@ #include #include #include -#include #include #include -#include -#include #include #include #include -#include "mdevd.h" #define USAGE "mdevd-coldplug [ -s slashsys ]" #define dieusage() strerr_dieusage(100, USAGE) -#define die1() strerr_diefu1sys(111, "write to stdout") -static inline void create_event (int fddir, char const *sysdev, char const *sub, char const *name) +static void scan_subdir (int fdat, char const *pathat, char const *list) { - if (faccessat(fddir, "dev", R_OK, AT_EACCESS) < 0) + DIR *dir ; + int fdlist = openat(fdat, list, O_RDONLY | O_DIRECTORY) ; + if (fdlist < 0) strerr_diefu4sys(111, "open ", pathat, "/", list) ; + dir = fdopendir(fdlist) ; + if (!dir) strerr_diefu4sys(111, "fdopendir ", pathat, "/", list) ; + for (;;) { - if (errno == ENOENT) return ; - strerr_diefu6sys(111, "access dev in ", sysdev, "/", sub, "/", name) ; - } - if (buffer_put(buffer_1, "add@/", 5) < 0 - || buffer_puts(buffer_1, sub) < 0 - || buffer_put(buffer_1, "/", 1) < 0 - || buffer_puts(buffer_1, name) < 0 - || buffer_put(buffer_1, "\0ACTION=add\0DEVPATH=/dev/", sizeof("\0ACTION=add\0DEVPATH=/dev/") - 1) < 0 - || buffer_puts(buffer_1, sub) < 0 - || buffer_put(buffer_1, "/", 1) < 0 - || buffer_puts(buffer_1, name) < 0 - || buffer_put(buffer_1, "\0SUBSYSTEM=", sizeof("\0SUBSYSTEM=") - 1) < 0) - die1() ; - - { - char *p ; - ssize_t r ; - char buf[PATH_MAX] ; - r = readlinkat(fddir, "subsystem", buf, PATH_MAX - 1) ; - if (r < 0) strerr_diefu6sys(111, "readlink subsystem in ", sysdev, "/", sub, "/", name) ; - buf[r] = 0 ; - p = strrchr(buf, '/') ; - if (p && buffer_put(buffer_1, p+1, strlen(p)) < 0) die1() ; + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + { + int fd ; + size_t dlen = strlen(d->d_name) ; + char fn[dlen + 8] ; + memcpy(fn, d->d_name, dlen) ; + memcpy(fn + dlen, "/uevent", 8) ; + fd = openat(fdlist, fn, O_WRONLY) ; + if (fd < 0) + { + strerr_warnwu6sys("open ", pathat, "/", list, "/", fn) ; + continue ; + } + if (write(fd, "add\n", 4) < 4) + strerr_warnwu6sys("write to ", pathat, "/", list, "/", fn) ; + close(fd) ; + } } + if (errno) strerr_diefu4sys(111, "readdir ", pathat, "/", list) ; + dir_close(dir) ; +} +static int scan_dir (char const *path, int add_devices) +{ + DIR *dir ; + int fdpath = open(path, O_RDONLY | O_DIRECTORY) ; + if (fdpath < 0) return 0 ; + dir = fdopendir(fdpath) ; + if (!dir) strerr_diefu2sys(111, "fdopendir ", path) ; + for (;;) { - size_t len ; - size_t i = 0 ; - char buf[UEVENT_MAX_SIZE] ; - int fd = openat(fddir, "uevent", O_RDONLY) ; - if (fd < 0) - strerr_diefu6sys(111, "open uevent in ", sysdev, "/", sub, "/", name) ; - len = allread(fd, buf, UEVENT_MAX_SIZE) ; - if (!len) strerr_diefu6sys(111, "read uevent in ", sysdev, "/", sub, "/", name) ; - close(fd) ; - for (; i < len ; i++) if (buf[i] == '\n') buf[i] = 0 ; - if (buffer_put(buffer_1, buf, len) < 0) die1() ; + direntry *d ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + if (add_devices) + { + size_t dlen = strlen(d->d_name) ; + char fn[dlen + 9] ; + memcpy(fn, d->d_name, dlen) ; + memcpy(fn + dlen, "/devices", 9) ; + scan_subdir(fdpath, path, fn) ; + } + else scan_subdir(fdpath, path, d->d_name) ; } - - if (buffer_put(buffer_1, "", 1) < 1) die1() ; + if (errno) strerr_diefu2sys(111, "readdir ", path) ; + dir_close(dir) ; + return 1 ; } + int main (int argc, char const *const *argv, char const *const *envp) { char const *slashsys = "/sys" ; - size_t slashsyslen ; PROG = "mdevd-coldplug" ; { subgetopt_t l = SUBGETOPT_ZERO ; @@ -83,51 +96,18 @@ int main (int argc, char const *const *argv, char const *const *envp) argc -= l.ind ; argv += l.ind ; } - slashsyslen = strlen(slashsys) ; { - int fdsysdev ; - DIR *dirsysdev ; - char sysdev[slashsyslen + 5] ; - memcpy(sysdev, slashsys, slashsyslen) ; - memcpy(sysdev + slashsyslen, "/dev", 5) ; - fdsysdev = open(sysdev, O_RDONLY | O_DIRECTORY) ; - if (fdsysdev < 0) strerr_diefu2sys(111, "open ", sysdev) ; - dirsysdev = fdopendir(fdsysdev) ; - if (!dirsysdev) strerr_diefu2sys(111, "fdopendir ", sysdev) ; - for (;;) + size_t slashsyslen = strlen(slashsys) ; + char fn[slashsyslen + 11] ; + memcpy(fn, slashsys, slashsyslen) ; + memcpy(fn + slashsyslen, "/subsystem", 11) ; + if (!scan_dir(fn, 1)) { - direntry *d ; - int fdsub ; - DIR *dirsub ; - errno = 0 ; - d = readdir(dirsysdev) ; - if (!d) break ; - if (d->d_name[0] == '.') continue ; - fdsub = openat(fdsysdev, d->d_name, O_RDONLY | O_DIRECTORY) ; - if (fdsub < 0) strerr_diefu4sys(111, "open ", sysdev, "/", d->d_name) ; - dirsub = fdopendir(fdsub) ; - if (!dirsub) strerr_diefu4sys(111, "fdopendir ", sysdev, "/", d->d_name) ; - for (;;) - { - direntry *dd ; - int fddevice ; - errno = 0 ; - dd = readdir(dirsub) ; - if (!dd) break ; - if (dd->d_name[0] == '.') continue ; - fddevice = openat(fdsub, dd->d_name, O_RDONLY | O_DIRECTORY) ; - if (fddevice < 0) strerr_diefu6sys(111, "open ", sysdev, "/", d->d_name, "/", dd->d_name) ; - create_event(fddevice, sysdev, d->d_name, dd->d_name) ; - close(fddevice) ; - } - if (errno) strerr_diefu4sys(111, "readdir ", sysdev, "/", d->d_name) ; - dir_close(dirsub) ; - close(fdsub) ; + memcpy(fn + slashsyslen + 1, "class", 6) ; + if (!scan_dir(fn, 0)) strerr_diefu2sys(111, "open ", fn) ; + memcpy(fn + slashsyslen + 1, "bus", 4) ; + if (!scan_dir(fn, 1)) strerr_diefu2sys(111, "open ", fn) ; } - if (errno) strerr_diefu2sys(111, "readdir ", sysdev) ; - dir_close(dirsysdev) ; - close(fdsysdev) ; } - if (!buffer_flush(buffer_1)) die1() ; return 0 ; } diff --git a/src/mdevd/mdevd-netlink.c b/src/mdevd/mdevd-netlink.c deleted file mode 100644 index 3f55f23..0000000 --- a/src/mdevd/mdevd-netlink.c +++ /dev/null @@ -1,177 +0,0 @@ -/* ISC license. */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mdevd.h" - -#define USAGE "mdevd-netlink [ -d notification-fd ] [ -v verbosity ] [ -b kbufsz ]" -#define dieusage() strerr_dieusage(100, USAGE) -#define dienomem() strerr_diefu1sys(111, "build string") ; - -static unsigned int cont = 1, verbosity = 1 ; - -static inline ssize_t fd_recvmsg (int fd, struct msghdr *hdr) -{ - ssize_t r ; - do r = recvmsg(fd, hdr, MSG_DONTWAIT) ; - while ((r == -1) && (errno == EINTR)) ; - return r ; -} - -static inline int netlink_init_stdin (unsigned int kbufsz) -{ - struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = 1, .nl_pid = 0 } ; - close(0) ; - if (socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, DJBUNIX_FLAG_NB|DJBUNIX_FLAG_COE) < 0 - || bind(0, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) < 0) - return 0 ; - - if (setsockopt(0, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) < 0 - && errno == EPERM - && setsockopt(0, SOL_SOCKET, SO_RCVBUF, &kbufsz, sizeof(unsigned int)) < 0) - return 0 ; - return 1 ; -} - -static inline void handle_signals (void) -{ - for (;;) - { - int c = selfpipe_read() ; - switch (c) - { - case -1 : strerr_diefu1sys(111, "selfpipe_read") ; - case 0 : return ; - case SIGTERM : - cont = 0 ; - fd_close(0) ; - break ; - default : - strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; - } - } -} - -static inline void handle_stdout (void) -{ - if (!buffer_flush(buffer_1) && !error_isagain(errno)) - strerr_diefu1sys(111, "flush stdout") ; -} - -static inline void handle_netlink (void) -{ - struct sockaddr_nl nl; - struct iovec v[2] ; - struct msghdr msg = - { - .msg_name = &nl, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = v, - .msg_iovlen = 2, - .msg_control = 0, - .msg_controllen = 0, - .msg_flags = 0 - } ; - ssize_t r ; - buffer_wpeek(buffer_1, v) ; - siovec_trunc(v, 2, siovec_len(v, 2) - 1) ; - r = sanitize_read(fd_recvmsg(0, &msg)) ; - if (r < 0) - { - if (errno == EPIPE) - { - if (verbosity >= 2) strerr_warnw1x("received EOF on netlink") ; - cont = 0 ; - fd_close(0) ; - return ; - } - else strerr_diefu1sys(111, "receive netlink message") ; - } - if (!r) return ; - if (msg.msg_flags & MSG_TRUNC) - strerr_diefu2x(111, "buffer too small for ", "netlink message") ; - if (nl.nl_pid) - { - if (verbosity >= 3) - { - char fmt[PID_FMT] ; - fmt[pid_fmt(fmt, nl.nl_pid)] = 0 ; - strerr_warnw3x("netlink message", " from userspace process ", fmt) ; - } - return ; - } - buffer_wseek(buffer_1, r) ; - buffer_putnoflush(buffer_1, "", 1) ; -} - - -int main (int argc, char const *const *argv, char const *const *envp) -{ - iopause_fd x[3] = { { .events = IOPAUSE_READ }, { .fd = 1 }, { .fd = 0 } } ; - unsigned int notif = 0 ; - PROG = "mdevd-netlink" ; - { - unsigned int kbufsz = 65536 ; - subgetopt_t l = SUBGETOPT_ZERO ; - for (;;) - { - int opt = subgetopt_r(argc, argv, "d:v:b:", &l) ; - if (opt == -1) break ; - switch (opt) - { - case 'd' : if (!uint0_scan(l.arg, ¬if) || notif < 3) dieusage() ; break ; - case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; - case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ; - default : dieusage() ; - } - } - argc -= l.ind ; argv += l.ind ; - if (!netlink_init_stdin(kbufsz)) strerr_diefu1sys(111, "init netlink") ; - } - - x[0].fd = selfpipe_init() ; - if (x[0].fd < 0) strerr_diefu1sys(111, "init selfpipe") ; - if (selfpipe_trap(SIGTERM) < 0) strerr_diefu1sys(111, "trap SIGTERM") ; - - if (verbosity >= 2) strerr_warni1x("starting") ; - if (notif) - { - fd_write(notif, "\n", 1) ; - fd_close(notif) ; - } - - while (cont || buffer_len(buffer_1)) - { - int r ; - x[1].events = buffer_len(buffer_1) ? IOPAUSE_WRITE : 0 ; - x[2].events = buffer_available(buffer_1) >= UEVENT_MAX_SIZE + 1 ? IOPAUSE_READ : 0 ; - r = iopause(x, 2 + cont, 0, 0) ; - if (r < 0) strerr_diefu1sys(111, "iopause") ; - if (!r) continue ; - if (x[1].revents & IOPAUSE_EXCEPT) break ; - if (x[1].revents & IOPAUSE_WRITE) handle_stdout() ; - if (x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT)) handle_signals() ; - if (cont && x[2].events & IOPAUSE_READ && x[2].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT)) - handle_netlink() ; - } - if (verbosity >= 2) strerr_warni1x("exiting") ; - return 0 ; -} diff --git a/src/mdevd/mdevd.c b/src/mdevd/mdevd.c index 7fafea0..ca1e070 100644 --- a/src/mdevd/mdevd.c +++ b/src/mdevd/mdevd.c @@ -1,7 +1,11 @@ /* ISC license. */ -#include /* makedev, major, minor */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include +#include /* makedev, major, minor */ #include #include #include @@ -15,10 +19,12 @@ #include #include /* basename */ #include /* rename */ +#include +#include + #include #include #include -#include #include #include #include @@ -28,16 +34,17 @@ #include #include #include +#include #include #include #include -#include "mdevd.h" -#define USAGE "mdevd [ -v verbosity ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ]" +#define USAGE "mdevd [ -v verbosity ] [ -1 ] [ -b kbufsz ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ]" #define dieusage() strerr_dieusage(100, USAGE) -#define BUFSIZE 8192 +#define CONFBUFSIZE 8192 #define UEVENT_MAX_VARS 63 +#define UEVENT_MAX_SIZE 4096 #define ACTION_NONE 0x0 #define ACTION_ADD 0x1 @@ -177,6 +184,103 @@ static int makesubdirs (char *path) } + /* Netlink isolation layer */ + +static inline ssize_t fd_recvmsg (int fd, struct msghdr *hdr) +{ + ssize_t r ; + do r = recvmsg(fd, hdr, MSG_DONTWAIT) ; + while ((r == -1) && (errno == EINTR)) ; + return r ; +} + +static inline int netlink_init (unsigned int kbufsz) +{ + struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = 1, .nl_pid = 0 } ; + int fd = socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, DJBUNIX_FLAG_NB|DJBUNIX_FLAG_COE) ; + if (fd < 0) return -1 ; + if (bind(fd, (struct sockaddr *)&nl, sizeof(struct sockaddr_nl)) < 0 + || (setsockopt(0, SOL_SOCKET, SO_RCVBUFFORCE, &kbufsz, sizeof(unsigned int)) < 0 + && errno == EPERM + && setsockopt(0, SOL_SOCKET, SO_RCVBUF, &kbufsz, sizeof(unsigned int)) < 0)) + { + fd_close(fd) ; + return -1 ; + } + return fd ; +} + +static inline size_t netlink_read (char *s) +{ + struct sockaddr_nl nl; + struct iovec v = { .iov_base = s, .iov_len = UEVENT_MAX_SIZE } ; + struct msghdr msg = + { + .msg_name = &nl, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &v, + .msg_iovlen = 1, + .msg_control = 0, + .msg_controllen = 0, + .msg_flags = 0 + } ; + ssize_t r = sanitize_read(fd_recvmsg(0, &msg)) ; + if (r < 0) + { + if (errno == EPIPE) + { + if (verbosity >= 2) strerr_warnw1x("received EOF on netlink") ; + cont = 0 ; + return 0 ; + } + else strerr_diefu1sys(111, "receive netlink message") ; + } + if (!r) return 0 ; + if (msg.msg_flags & MSG_TRUNC) + strerr_diefu1x(111, "buffer too small for netlink message") ; + if (nl.nl_pid) + { + if (verbosity >= 3) + { + char fmt[PID_FMT] ; + fmt[pid_fmt(fmt, nl.nl_pid)] = 0 ; + strerr_warnw2x("received netlink message from userspace process ", fmt) ; + } + return 0 ; + } + if (s[r-1]) + { + if (verbosity) strerr_warnw2x("received invalid event: ", "improperly terminated") ; + return 0 ; + } + if (!strstr(s, "@/")) + { + if (verbosity) strerr_warnw2x("received invalid event: ", "bad initial summary") ; + return 0 ; + } + return r ; +} + +static inline int uevent_read (struct uevent_s *event) +{ + unsigned short len = 0 ; + event->len = netlink_read(event->buf) ; + if (!event->len) return 0 ; + event->varn = 0 ; + while (len < event->len) + { + if (event->varn >= UEVENT_MAX_VARS) + { + if (verbosity) strerr_warnw2x("received invalid event: ", "too many variables") ; + return 0 ; + } + event->vars[event->varn++] = len ; + len += strlen(event->buf + len) + 1 ; + } + return 1 ; +} + + /* mdev.conf parsing. See PARSING.txt for details. */ /* The first pass is simple. The goal is just to compute scriptlen and envmatchlen. */ @@ -840,7 +944,7 @@ static inline void on_event (struct uevent_s *event, scriptelem const *script, u /* Tying it all together */ -static inline int handle_signals (void) +static inline void handle_signals (void) { for (;;) { @@ -848,8 +952,9 @@ static inline int handle_signals (void) switch (c) { case -1 : strerr_diefu1sys(111, "selfpipe_read") ; - case 0 : return 0 ; - case SIGHUP : return 1 ; + case SIGTERM : + case SIGHUP : cont = c == SIGHUP ; + case 0 : return ; case SIGCHLD : if (!pid) wait_reap() ; else @@ -869,54 +974,37 @@ static inline int handle_signals (void) } } -static void handle_stdin (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, size_t *w) +static inline void handle_event (scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch) { - while (!pid) - { - ssize_t r ; - if (event->len >= UEVENT_MAX_SIZE) - strerr_dief2x(1, "received invalid event: ", "too long") ; - r = sanitize_read(getlnmax(buffer_0, event->buf + event->len, UEVENT_MAX_SIZE - event->len, w, '\0')) ; - if (r < 0) - { - cont = 0 ; - if (errno != EPIPE && verbosity) strerr_warnwu1sys("read from stdin") ; - if (event->len) strerr_dief1x(1, "received incomplete event") ; - } - if (r <= 0) break ; - if (*w > 1) - { - if (event->varn >= UEVENT_MAX_VARS) - strerr_dief2x(1, "received invalid event: ", "too many variables") ; - event->vars[event->varn++] = event->len ; - event->len += *w ; - } - else - { - if (event->varn > 1) on_event(event, script, scriptlen, storage, envmatch) ; - event->len = 0 ; - event->varn = 0 ; - } - *w = 0 ; - } + struct uevent_s event = UEVENT_ZERO ; + if (uevent_read(&event) && event.varn > 1) + on_event(&event, script, scriptlen, storage, envmatch) ; } int main (int argc, char const *const *argv) { char const *configfile = "/etc/mdev.conf" ; - iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .fd = 0 } } ; + iopause_fd x[2] = { { .events = IOPAUSE_READ }, { .events = IOPAUSE_READ } } ; + unsigned int notif = 0 ; + unsigned int kbufsz = 65536 ; + char const *slashdev = "/dev" ; PROG = "mdevd" ; { - char const *slashdev = "/dev" ; subgetopt_t l = SUBGETOPT_ZERO ; for (;;) { - int opt = subgetopt_r(argc, argv, "nv:f:s:d:F:", &l) ; + int opt = subgetopt_r(argc, argv, "nv:D:b:f:s:d:F:", &l) ; if (opt == -1) break ; switch (opt) { case 'n' : dryrun = 1 ; break ; case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'D' : + if (!uint0_scan(l.arg, ¬if)) dieusage() ; + if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; + if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ; + break ; + case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ; case 'f' : configfile = l.arg ; break ; case 's' : slashsys = l.arg ; break ; case 'd' : slashdev = l.arg ; break ; @@ -925,13 +1013,13 @@ int main (int argc, char const *const *argv) } } argc -= l.ind ; argv += l.ind ; - if (configfile[0] != '/') strerr_dief2x(100, configfile, " is not an absolute path") ; - if (slashsys[0] != '/') strerr_dief2x(100, slashsys, " is not an absolute path") ; - if (slashdev[0] != '/') strerr_dief2x(100, slashdev, " is not an absolute path") ; - if (fwbase[0] != '/') strerr_dief2x(100, fwbase, " is not an absolute path") ; - if (chdir(slashdev) < 0) strerr_diefu2sys(111, "chdir to ", slashdev) ; } + if (configfile[0] != '/') strerr_dief2x(100, configfile, " is not an absolute path") ; + if (slashsys[0] != '/') strerr_dief2x(100, slashsys, " is not an absolute path") ; + if (slashdev[0] != '/') strerr_dief2x(100, slashdev, " is not an absolute path") ; + if (fwbase[0] != '/') strerr_dief2x(100, fwbase, " is not an absolute path") ; + if (chdir(slashdev) < 0) strerr_diefu2sys(111, "chdir to ", slashdev) ; if (strlen(slashsys) >= PATH_MAX - 1) strerr_dief1x(100, "paths too long") ; if (!fd_sanitize()) strerr_diefu1sys(111, "sanitize standard fds") ; @@ -942,13 +1030,16 @@ int main (int argc, char const *const *argv) root_min = minor(st.st_dev) ; } - if (ndelay_on(0) < 0) strerr_diefu1sys(111, "set stdin nonblocking") ; + x[1].fd = netlink_init(kbufsz) ; + if (x[1].fd < 0) strerr_diefu1sys(111, "init netlink") ; + x[0].fd = selfpipe_init() ; if (x[0].fd < 0) strerr_diefu1sys(111, "init selfpipe") ; if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; { sigset_t set ; sigemptyset(&set) ; + sigaddset(&set, SIGTERM) ; sigaddset(&set, SIGCHLD) ; sigaddset(&set, SIGHUP) ; if (selfpipe_trapset(&set) < 0) @@ -957,43 +1048,47 @@ int main (int argc, char const *const *argv) mdevd_random_init() ; umask(0) ; + if (notif) + { + fd_write(notif, "\n", 1) ; + fd_close(notif) ; + } + while (cont) { ssize_t len ; unsigned short scriptlen = 0 ; unsigned short envmatchlen = 0 ; - char buf[BUFSIZE] ; - len = openreadnclose(configfile, buf, BUFSIZE - 1) ; + char storage[CONFBUFSIZE] ; + len = openreadnclose(configfile, storage, CONFBUFSIZE - 1) ; if (len < 0) { if (errno != ENOENT) strerr_diefu2sys(111, "read ", configfile) ; if (verbosity) strerr_warnwu2sys("read ", configfile) ; len = 0 ; } - buf[len++] = 0 ; - script_firstpass(buf, &scriptlen, &envmatchlen) ; + storage[len++] = 0 ; + script_firstpass(storage, &scriptlen, &envmatchlen) ; { - size_t w = 0 ; - int reload = 0 ; - struct uevent_s event = UEVENT_ZERO ; struct envmatch_s envmatch[envmatchlen ? envmatchlen : 1] ; scriptelem script[scriptlen + 1] ; memset(script, 0, scriptlen * sizeof(scriptelem)) ; script[scriptlen++] = scriptelem_catchall ; - script_secondpass(buf, script, envmatch) ; - while (pid || (cont && (!reload || buffer_len(buffer_0)))) + script_secondpass(storage, script, envmatch) ; + cont = 2 ; + + while (pid || cont == 2) { - if (buffer_len(buffer_0)) handle_stdin(&event, script, scriptlen, buf, envmatch, &w) ; - x[1].events = pid ? 0 : IOPAUSE_READ ; - if (iopause(x, 1 + cont, 0, 0) < 0) strerr_diefu1sys(111, "iopause") ; - if (x[0].revents & IOPAUSE_READ && handle_signals()) reload = 1 ; - if (cont && !pid && x[1].revents & IOPAUSE_READ) - handle_stdin(&event, script, scriptlen, buf, envmatch, &w) ; + if (iopause(x, 1 + (!pid && cont == 2), 0, 0) < 0) strerr_diefu1sys(111, "iopause") ; + if (x[0].revents & IOPAUSE_READ) + handle_signals() ; + if (!pid && cont == 2 && x[1].revents & IOPAUSE_READ) + handle_event(script, scriptlen, storage, envmatch) ; } + script_free(script, scriptlen, envmatch, envmatchlen) ; } } - ndelay_off(0) ; return 0 ; } diff --git a/src/mdevd/mdevd.h b/src/mdevd/mdevd.h deleted file mode 100644 index a99f692..0000000 --- a/src/mdevd/mdevd.h +++ /dev/null @@ -1,8 +0,0 @@ - /* ISC license. */ - -#ifndef MDEVD_H -#define MDEVD_H - -#define UEVENT_MAX_SIZE 4096 - -#endif -- cgit 1.4.1