about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2022-05-07 08:49:38 +0000
committerLaurent Bercot <ska@appnovation.com>2022-05-07 08:49:38 +0000
commitd918eccfc262cebb0d8f54237a959ac66896eba0 (patch)
tree8b79059726c83d06e1c932b9e0159876d1eb6840
parentd5bf63b3696e494dd77e8e00a63056ec2db3f744 (diff)
downloadmdevd-d918eccfc262cebb0d8f54237a959ac66896eba0.tar.gz
mdevd-d918eccfc262cebb0d8f54237a959ac66896eba0.tar.xz
mdevd-d918eccfc262cebb0d8f54237a959ac66896eba0.zip
Tentative fix for the spawn-before-fallthrough bug
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--src/mdevd/mdevd.c138
1 files changed, 75 insertions, 63 deletions
diff --git a/src/mdevd/mdevd.c b/src/mdevd/mdevd.c
index f360ac7..1426332 100644
--- a/src/mdevd/mdevd.c
+++ b/src/mdevd/mdevd.c
@@ -133,6 +133,19 @@ struct uevent_s
 } ;
 #define UEVENT_ZERO { .len = 0, .varn = 0 }
 
+typedef struct udata_s udata, *udata_ref ;
+struct udata_s
+{
+  char *devname ;
+  mode_t devtype ;
+  unsigned int action ;
+  int mmaj ;
+  int mmin ;
+  unsigned int i ;
+  char buf[UEVENT_MAX_SIZE] ;
+} ;
+#define UDATA_ZERO { .devname = 0, .devtype = 0, .action = 0, .mmaj = -1, .mmin = -1, .i = 0, .buf = "" }
+
 
  /* Utility functions */
 
@@ -673,9 +686,9 @@ static inline void spawn_command (char const *command, struct uevent_s const *ev
   }
 }
 
-static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem, char const *storage, struct envmatch_s const *envmatch, char *devname, mode_t devtype, unsigned int action, int mmaj, int mmin)
+static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem, char const *storage, struct envmatch_s const *envmatch, udata const *ud)
 {
-  size_t devnamelen = strlen(devname) ;
+  size_t devnamelen = strlen(ud->devname) ;
   size_t nodelen = 0 ;
   char *node = event->buf + event->len + 5 ;
   regmatch_t off[10] ;
@@ -692,11 +705,11 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
     case DEVMATCH_NOTHING:
     case DEVMATCH_CATCHALL: break ;
     case DEVMATCH_MAJMIN:
-      if (mmaj >= 0 && mmin >= 0 && mmaj == elem->devmatch.majmin.maj && mmin >= elem->devmatch.majmin.minlo && mmin <= elem->devmatch.majmin.minhi) break ;
+      if (ud->mmaj >= 0 && ud->mmin >= 0 && ud->mmaj == elem->devmatch.majmin.maj && ud->mmin >= elem->devmatch.majmin.minlo && ud->mmin <= elem->devmatch.majmin.minhi) break ;
       return 0 ;
     case DEVMATCH_DEVRE:
-      if (!regexec(&elem->devmatch.devre, devname, 10, off, 0)
-       && !off[0].rm_so && off[0].rm_eo == strlen(devname))
+      if (!regexec(&elem->devmatch.devre, ud->devname, 10, off, 0)
+       && !off[0].rm_so && off[0].rm_eo == strlen(ud->devname))
         break ;
       return 0 ;
   }
@@ -705,16 +718,16 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
   {
     case MOVEINFO_NOTHING :
     case MOVEINFO_NOCREATE :
-      memcpy(node, devname, devnamelen + 1) ;
+      memcpy(node, ud->devname, devnamelen + 1) ;
       nodelen = devnamelen ;
       break ;
     case MOVEINFO_MOVE :
     case MOVEINFO_MOVEANDLINK :
     {
-      ssize_t r = alias_format(node, PATH_MAX, storage + elem->movepath, devname, off) ;
+      ssize_t r = alias_format(node, PATH_MAX, storage + elem->movepath, ud->devname, off) ;
       if (r <= 1)
       {
-        if (verbosity) strerr_warnwu5sys("process expression \"", storage + elem->movepath, "\" with devname \"", devname, "\"") ;
+        if (verbosity) strerr_warnwu5sys("process expression \"", storage + elem->movepath, "\" with devname \"", ud->devname, "\"") ;
         return -1 ;
       }
       if (node[r - 2] == '/')
@@ -722,16 +735,16 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
         if (r + devnamelen >= PATH_MAX)
         {
           errno = ENAMETOOLONG ;
-          if (verbosity) strerr_warnwu2sys("create alias for ", devname) ;
+          if (verbosity) strerr_warnwu2sys("create alias for ", ud->devname) ;
           return -1 ;
         }
-        memcpy(node + r - 1, devname, devnamelen + 1) ;
+        memcpy(node + r - 1, ud->devname, devnamelen + 1) ;
         nodelen = r + devnamelen - 1 ;
       }
       break ;
     }
   }
-  if (elem->movetype != MOVEINFO_NOCREATE && action == ACTION_ADD && mmaj >= 0)
+  if (elem->movetype != MOVEINFO_NOCREATE && ud->action == ACTION_ADD && ud->mmaj >= 0)
   {
     if (!makesubdirs(node)) return -1 ;
     if (dryrun)
@@ -739,13 +752,13 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
       char fmtmaj[UINT_FMT] ;
       char fmtmin[UINT_FMT] ;
       char fmtmode[UINT_OFMT] ;
-      fmtmaj[uint_fmt(fmtmaj, mmaj)] = 0 ;
-      fmtmin[uint_fmt(fmtmin, mmin)] = 0 ;
+      fmtmaj[uint_fmt(fmtmaj, ud->mmaj)] = 0 ;
+      fmtmin[uint_fmt(fmtmin, ud->mmin)] = 0 ;
       fmtmode[uint_ofmt(fmtmode, elem->mode)] = 0 ;
-      strerr_warni6x("dry run: mknod ", node, S_ISBLK(devtype) ? " b " : " c ", fmtmaj, " ", fmtmin) ;
+      strerr_warni6x("dry run: mknod ", node, S_ISBLK(ud->devtype) ? " b " : " c ", fmtmaj, " ", fmtmin) ;
       strerr_warni4x("dry run: chmod ", fmtmode, " ", node) ;
     }
-    else if (mknod(node, elem->mode | devtype, makedev(mmaj, mmin)) < 0)
+    else if (mknod(node, elem->mode | ud->devtype, makedev(ud->mmaj, ud->mmin)) < 0)
     {
       if (errno != EEXIST)
       {
@@ -768,24 +781,24 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
       else if (chown(node, elem->uid, elem->gid) < 0 && verbosity >= 2)
         strerr_warnwu2sys("chown ", node) ;
     }
-    if (mmaj == root_maj && mmin == root_min)
+    if (ud->mmaj == root_maj && ud->mmin == root_min)
     {
       if (dryrun) strerr_warni3x("dry run: symlink ", node, " to root") ;
       else symlink(node, "root") ;
     }
     if (elem->movetype == MOVEINFO_MOVEANDLINK)
     {
-      if (!makesubdirs(devname)) return -1 ;
-      if (dryrun) strerr_warni4x("dry run: symlink ", node, " to ", devname) ;
-      else if (atomic_symlink(node, devname, "mdevd") < 0)
+      if (!makesubdirs(ud->devname)) return -1 ;
+      if (dryrun) strerr_warni4x("dry run: symlink ", node, " to ", ud->devname) ;
+      else if (atomic_symlink(node, ud->devname, "mdevd") < 0)
       {
-        if (verbosity) strerr_warnwu4sys("symlink ", node, " to ", devname) ;
+        if (verbosity) strerr_warnwu4sys("symlink ", node, " to ", ud->devname) ;
         return -1 ;
       }
     }
   }
 
-  if (elem->cmdtype == ACTION_ANY || action == elem->cmdtype)
+  if (elem->cmdtype == ACTION_ANY || ud->action == elem->cmdtype)
   {
     if (!event_getvar(event, "MDEV"))
     {
@@ -815,39 +828,33 @@ static inline int run_scriptelem (struct uevent_s *event, scriptelem const *elem
     else spawn_command(storage + elem->command, event, elem->flagexecline) ;
   }
 
-  if (elem->movetype != MOVEINFO_NOCREATE && action == ACTION_REMOVE && mmaj >= 0)
+  if (elem->movetype != MOVEINFO_NOCREATE && ud->action == ACTION_REMOVE && ud->mmaj >= 0)
   {
     if (elem->movetype == MOVEINFO_MOVEANDLINK)
     {
-      if (dryrun) strerr_warni2x("dry run: unlink ", devname) ;
-      else unlink_void(devname) ;
+      if (dryrun) strerr_warni2x("dry run: unlink ", ud->devname) ;
+      else unlink_void(ud->devname) ;
     }
     if (dryrun) strerr_warni2x("dry run: unlink ", node) ;
     else unlink_void(node) ;
   }
 
-  return !elem->flagcont ;  
+  return pid || !elem->flagcont ;
 }
 
-static inline void run_script (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, char *devname, mode_t devtype, unsigned int action, int mmaj, int mmin)
+static inline void run_script (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, udata *ud)
 {
-  unsigned short i = 0 ;
-  for (; i < scriptlen ; i++)
-  {
-    int r ;
-    r = run_scriptelem(event, script + i, storage, envmatch, devname, devtype, action, mmaj, mmin) ;
-    if (r) break ;
-  }
+  while (ud->i < scriptlen)
+    if (run_scriptelem(event, script + ud->i++, storage, envmatch, ud)) break ;
 }
 
-static inline void act_on_event (struct uevent_s *event, char *sysdevpath, size_t sysdevpathlen, unsigned int action, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch)
+static inline void act_on_event (struct uevent_s *event, char *sysdevpath, size_t sysdevpathlen, unsigned int action, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, udata *ud)
 {
   ssize_t hasmajmin = 0 ;
   unsigned int mmaj, mmin ;
-  mode_t devtype = S_IFCHR ;
-  char *devname ;
   char const *x = event_getvar(event, "MAJOR") ;
-  char buf[UEVENT_MAX_SIZE] ;
+  ud->devtype = S_IFCHR ;
+  ud->action = action ;
   if (action == ACTION_ADD)
   {
     if (x && uint0_scan(x, &mmaj))
@@ -858,56 +865,59 @@ static inline void act_on_event (struct uevent_s *event, char *sysdevpath, size_
     if (!hasmajmin)
     {
       memcpy(sysdevpath + sysdevpathlen, "/dev", 5) ;
-      hasmajmin = openreadnclose(sysdevpath, buf, UINT_FMT << 1) ;
+      hasmajmin = openreadnclose(sysdevpath, ud->buf, UINT_FMT << 1) ;
       sysdevpath[sysdevpathlen] = 0 ;
       if (hasmajmin > 0)
       {
-        size_t i = uint_scan(buf, &mmaj) ;
-        if (i > 0 && buf[i] == ':')
+        size_t i = uint_scan(ud->buf, &mmaj) ;
+        if (i > 0 && ud->buf[i] == ':')
         {
-          size_t j = uint_scan(buf + i + 1, &mmin) ;
-          if (j > 0 && buf[i+1+j] == '\n') ;
+          size_t j = uint_scan(ud->buf + i + 1, &mmin) ;
+          if (j > 0 && ud->buf[i+1+j] == '\n') ;
           else hasmajmin = 0 ;
         }
         else hasmajmin = 0 ;
       }
     }
   }
+  ud->mmaj = hasmajmin > 0 ? mmaj : -1 ;
+  ud->mmin = hasmajmin > 0 ? mmin : -1 ;
 
-  devname = event_getvar(event, "DEVNAME") ;
-  if (!devname)
+  ud->devname = event_getvar(event, "DEVNAME") ;
+  if (!ud->devname)
   {
     ssize_t r ;
     memcpy(sysdevpath + sysdevpathlen, "/uevent", 8) ;
-    r = openreadnclose(sysdevpath, buf, UEVENT_MAX_SIZE-1) ;
+    r = openreadnclose(sysdevpath, ud->buf, UEVENT_MAX_SIZE-1) ;
     sysdevpath[sysdevpathlen] = 0 ;
     if (r > 0)
     {
-      buf[r] = 0 ;
-      devname = strstr(buf, "\nDEVNAME=") ;
-      if (devname)
+      ud->buf[r] = 0 ;
+      ud->devname = strstr(ud->buf, "\nDEVNAME=") ;
+      if (ud->devname)
       {
-        devname += 9 ;
-        *strchr(devname, '\n') = 0 ;
+        ud->devname += 9 ;
+        *strchr(ud->devname, '\n') = 0 ;
       }
     }
-    if (!devname) devname = basename(sysdevpath) ;
+    if (!ud->devname) ud->devname = basename(sysdevpath) ;
   }
-  if (strlen(devname) >= PATH_MAX - 1)
+  if (strlen(ud->devname) >= PATH_MAX - 1)
   {
-    if (verbosity) strerr_warnwu2x("device name too long: ", devname) ;
+    if (verbosity) strerr_warnwu2x("device name too long: ", ud->devname) ;
     return ;
   }
-  if (strstr(sysdevpath, "/block/")) devtype = S_IFBLK ;
+  if (strstr(sysdevpath, "/block/")) ud->devtype = S_IFBLK ;
   else
   {
     x = event_getvar(event, "SUBSYSTEM") ;
-    if (x && str_start(x, "block")) devtype = S_IFBLK ;
+    if (x && str_start(x, "block")) ud->devtype = S_IFBLK ;
   }
-  run_script(event, script, scriptlen, storage, envmatch, devname, devtype, action, hasmajmin > 0 ? mmaj : -1, hasmajmin > 0 ? mmin : -1) ;
+  ud->i = 0 ;
+  run_script(event, script, scriptlen, storage, envmatch, ud) ;
 }
 
-static inline void on_event (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch)
+static inline void on_event (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, udata *ud)
 {
   unsigned int action ;
   char const *x = event_getvar(event, "ACTION") ;
@@ -924,7 +934,7 @@ static inline void on_event (struct uevent_s *event, scriptelem const *script, u
     memcpy(sysdevpath, slashsys, slashsyslen) ;
     memcpy(sysdevpath + slashsyslen, x, devpathlen + 1) ;
     x = event_getvar(event, "FIRMWARE") ;
-    if (action == ACTION_ADD || !x) act_on_event(event, sysdevpath, slashsyslen + devpathlen, action, script, scriptlen, storage, envmatch) ;
+    if (action == ACTION_ADD || !x) act_on_event(event, sysdevpath, slashsyslen + devpathlen, action, script, scriptlen, storage, envmatch, ud) ;
     if (action == ACTION_ADD && x) load_firmware(x, sysdevpath) ;
   }
 }
@@ -932,7 +942,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 int handle_signals (struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, udata *ud)
 {
   int e = 0 ;
   for (;;)
@@ -956,6 +966,7 @@ static inline int handle_signals (void)
           else if (!r) break ;
           pid = 0 ;
           e = 1 ;
+          run_script(event, script, scriptlen, storage, envmatch, ud) ;
         }
         break ;
       default :
@@ -964,10 +975,10 @@ static inline int handle_signals (void)
   }
 }
 
-static inline int handle_event (int fd, struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch)
+static inline int handle_event (int fd, struct uevent_s *event, scriptelem const *script, unsigned short scriptlen, char const *storage, struct envmatch_s const *envmatch, udata *ud)
 {
   if (!uevent_read(fd, event) || event->varn <= 1) return 0 ;
-    on_event(event, script, scriptlen, storage, envmatch) ;
+    on_event(event, script, scriptlen, storage, envmatch, ud) ;
   return 1 ;
 }
 
@@ -1098,6 +1109,7 @@ int main (int argc, char const *const *argv)
 
     {
       struct uevent_s event = UEVENT_ZERO ;
+      udata ud = UDATA_ZERO ;
       struct envmatch_s envmatch[envmatchlen ? envmatchlen : 1] ;
       scriptelem script[scriptlen + 1] ;
       memset(script, 0, scriptlen * sizeof(scriptelem)) ;
@@ -1123,13 +1135,13 @@ 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())
+          if (handle_signals(&event, script, scriptlen, storage, envmatch, &ud) && !pid)
           {
             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)
+          if (handle_event(x[1].fd, &event, script, scriptlen, storage, envmatch, &ud) && !pid)
           {
             if (outputfd && !output_event(outputfd, &event)) outputfd = 0 ;
             if (rebc) rebc_event(rebc, &event) ;