about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2021-08-27 00:39:48 +0000
committerLaurent Bercot <ska@appnovation.com>2021-08-27 00:39:48 +0000
commitb61ab0373a724d38e059655d561b5fb3dcb2aff6 (patch)
tree389a18205e8e5f316285365a4244d3253dbd4dc8
parent7d30371be2837dafa3f09c2ea23b9cc9054ebadc (diff)
downloadmdevd-b61ab0373a724d38e059655d561b5fb3dcb2aff6.tar.gz
mdevd-b61ab0373a724d38e059655d561b5fb3dcb2aff6.tar.xz
mdevd-b61ab0373a724d38e059655d561b5fb3dcb2aff6.zip
Add -O nlgroups for netlink rebroadcast
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--doc/mdevd.html7
-rw-r--r--src/mdevd/mdevd.c59
2 files changed, 53 insertions, 13 deletions
diff --git a/doc/mdevd.html b/doc/mdevd.html
index 58568cd..dfb44a3 100644
--- a/doc/mdevd.html
+++ b/doc/mdevd.html
@@ -49,7 +49,7 @@ to running mdevd over mdev. </li>
 <h2> Interface </h2>
 
 <pre>
-     mdevd [ -v <em>verbosity</em> ] [ -D <em>notif</em> ] [ -o <em>outputfd</em> ] [ -b <em>kbufsz</em> ] [ -f <em>conffile</em> ] [ -n ] [ -s <em>slashsys</em> ] [ -d <em>slashdev</em> ] [ -F <em>fwbase</em> ] [ -C ]
+     mdevd [ -v <em>verbosity</em> ] [ -D <em>notif</em> ] [ -o <em>outputfd</em> ] [ -O <em>nlgroups</em> ] [ -b <em>kbufsz</em> ] [ -f <em>conffile</em> ] [ -n ] [ -s <em>slashsys</em> ] [ -d <em>slashdev</em> ] [ -F <em>fwbase</em> ] [ -C ]
 </pre>
 
 <ul>
@@ -104,6 +104,11 @@ character. The uevent is terminated by an additional null character.
 If for any reason, at any point, mdevd fails to write to <em>outputfd</em>,
 it stops writing, until it is restarted. (This is to preserve mdevd's memory
 stability guarantee.) By default, the uevents are not written anywhere. </li>
+ <li> <tt>-O</tt>&nbsp;<em>nlgroups</em>&nbsp;: after mdevd has handled the
+uevents, rebroadcast them to the netlink groups identified by the mask
+<em>nlgroups</em>. Bit 0 of <em>nlgroups</em> is always ignored (because
+netlink group 1 is the one used by the kernel to send the original uevents and
+that mdevd listens to, and we don't want to loopback on it). </li>
  <li> <tt>-b</tt>&nbsp;<em>kbufsz</em>&nbsp;: try and reserve a kernel buffer of
 <em>kbufsz</em> bytes for the netlink queue. Too large a buffer wastes kernel memory;
 too small a buffer risks losing events. The default is 500 kB, which should be
diff --git a/src/mdevd/mdevd.c b/src/mdevd/mdevd.c
index 3e73393..a486a9a 100644
--- a/src/mdevd/mdevd.c
+++ b/src/mdevd/mdevd.c
@@ -42,7 +42,7 @@
 
 #include <mdevd/config.h>
 
-#define USAGE "mdevd [ -v verbosity ] [ -D notif ] [ -o outputfd ] [ -b kbufsz ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ] [ -F fwbase ] [ -C ]"
+#define USAGE "mdevd [ -v verbosity ] [ -D notif ] [ -o outputfd ] [ -O nlgroups ] [ -b kbufsz ] [ -f conffile ] [ -n ] [ -s slashsys ] [ -d slashdev ] [ -F fwbase ] [ -C ]"
 #define dieusage() strerr_dieusage(100, USAGE)
 
 #define CONFBUFSIZE 8192
@@ -56,7 +56,6 @@
 static int dryrun = 0 ;
 static int cont = 1 ;
 static pid_t pid = 0 ;
-static unsigned int outputfd = 0 ;
 static unsigned int verbosity = 1 ;
 static char const *slashsys = "/sys" ;
 static char const *fwbase = "/lib/firmware" ;
@@ -219,6 +218,19 @@ static inline int netlink_init (unsigned int kbufsz)
   return -1 ;
 }
 
+static inline int rebc_init (unsigned int groups)
+{
+  struct sockaddr_nl nl = { .nl_family = AF_NETLINK, .nl_pad = 0, .nl_groups = groups & ~1U, .nl_pid = 0 } ;
+  int fd = socket_internal(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT, O_CLOEXEC) ;
+  if (fd == -1) return -1 ;
+  if (connect(fd, (struct sockaddr *)&nl, sizeof nl) == -1)
+  {
+    fd_close(fd) ;
+    return -1 ;
+  }
+  return fd ;
+}
+
 static inline size_t netlink_read (int fd, char *s)
 {
   struct sockaddr_nl nl;
@@ -290,6 +302,7 @@ static inline int uevent_read (int fd, struct uevent_s *event)
 }
 
 
+
  /* mdev.conf parsing. See PARSING.txt for details. */
 
  /* The first pass is simple. The goal is just to compute scriptlen and envmatchlen. */
@@ -966,9 +979,9 @@ static inline int handle_signals (void)
     switch (c)
     {
       case -1 : strerr_diefu1sys(111, "selfpipe_read") ;
-      case SIGTERM :
-      case SIGHUP : cont = c == SIGHUP ;
       case 0 : return e ;
+      case SIGTERM : cont = 0 ; break ;
+      case SIGHUP : cont = 1 ; break ;
       case SIGCHLD :
         if (!pid) wait_reap() ;
         else
@@ -996,7 +1009,7 @@ static inline int handle_event (int fd, struct uevent_s *event, scriptelem const
   return 1 ;
 }
 
-static void output_event (struct uevent_s *event)
+static int output_event (unsigned int outputfd, struct uevent_s *event)
 {
   static char const c = 0 ;
   struct iovec v[2] = { { .iov_base = event->buf, .iov_len = event->len }, { .iov_base = (char *)&c, .iov_len = 1 } } ;
@@ -1005,9 +1018,16 @@ static void output_event (struct uevent_s *event)
     char fmt[UINT_FMT] ;
     fmt[uint_fmt(fmt, outputfd)] = 0 ;
     fd_close(outputfd) ;
-    outputfd = 0 ;
     strerr_warnwu3sys("write to descriptor ", fmt, " (closing it)") ;
+    return 0 ;
   }
+  return 1 ;
+}
+
+static void rebc_event (int fd, struct uevent_s const *event)
+{
+  if (fd_send(fd, event->buf, event->len, 0) == -1)
+    strerr_warnwu1sys("rebroadcast uevent") ;
 }
 
 int main (int argc, char const *const *argv)
@@ -1018,12 +1038,14 @@ int main (int argc, char const *const *argv)
   unsigned int kbufsz = 512288 ;
   char const *slashdev = "/dev" ;
   int docoldplug = 0 ;
+  unsigned int outputfd = 0 ;
+  unsigned int rebc = 0 ;
   PROG = "mdevd" ;
   {
     subgetopt l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "nv:D:o:b:f:s:d:F:C", &l) ;
+      int opt = subgetopt_r(argc, argv, "nv:D:o:O:b:f:s:d:F:C", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
@@ -1031,6 +1053,7 @@ int main (int argc, char const *const *argv)
         case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
         case 'D' : if (!uint0_scan(l.arg, &notif)) dieusage() ; break ;
         case 'o' : if (!uint0_scan(l.arg, &outputfd)) dieusage() ; break ;
+        case 'O' : if (!uint0_scan(l.arg, &rebc)) dieusage() ; break ;
         case 'b' : if (!uint0_scan(l.arg, &kbufsz)) dieusage() ; break ;
         case 'f' : configfile = l.arg ; break ;
         case 's' : slashsys = l.arg ; break ;
@@ -1063,7 +1086,6 @@ int main (int argc, char const *const *argv)
     if (ndelay_on(outputfd) < 0) strerr_diefu1sys(111, "set output fd non-blocking") ;
     if (coe(outputfd) < 0) strerr_diefu1sys(111, "set output fd close-on-exec") ;
   }
-
   {
     struct stat st ;
     if (stat("/", &st) < 0) strerr_diefu1sys(111, "stat /") ;
@@ -1073,6 +1095,13 @@ int main (int argc, char const *const *argv)
 
   x[1].fd = netlink_init(kbufsz) ;
   if (x[1].fd < 0) strerr_diefu1sys(111, "init netlink") ;
+  if (rebc)
+  {
+    int fd = rebc_init(rebc) ;
+    if (fd == -1) strerr_diefu2sys(111, "init netlink", " rebroadcast socket") ;
+    rebc = fd ;
+  }
+
   x[0].fd = selfpipe_init() ;
   if (x[0].fd < 0) strerr_diefu1sys(111, "init selfpipe") ;
   if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ;
@@ -1133,11 +1162,17 @@ int main (int argc, char const *const *argv)
       {
         if (iopause_stamp(x, 1 + (!pid && cont == 2), 0, 0) < 0) strerr_diefu1sys(111, "iopause") ;
         if (x[0].revents & IOPAUSE_READ)
-          if (handle_signals() && outputfd)
-            output_event(&event) ;
+          if (handle_signals())
+          {
+            if (outputfd && !output_event(outputfd, &event)) outputfd = 0 ;
+            if (rebc) rebc_event(rebc, &event) ;
+          }
         if (!pid && cont == 2 && x[1].revents & IOPAUSE_READ)
-          if (handle_event(x[1].fd, &event, script, scriptlen, storage, envmatch) && !pid && outputfd)
-            output_event(&event) ;
+          if (handle_event(x[1].fd, &event, script, scriptlen, storage, envmatch) && !pid)
+          {
+            if (outputfd && !output_event(outputfd, &event)) outputfd = 0 ;
+            if (rebc) rebc_event(rebc, &event) ;
+          }
       }
 
       script_free(script, scriptlen, envmatch, envmatchlen) ;