From 93a3444a0941c293cecd84920c049b1ba8c81b3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Sun, 6 Jan 2019 22:17:00 +0100
Subject: [PATCH] udev: rework how we handle the return value from spawned
 programs

When running PROGRAM="...", we would log
systemd-udevd[447]: Failed to wait spawned command '...': Input/output error
no matter why the program actually failed, at error level.

The code wouldn't distinguish between an internal failure and a failure in the
program being called and run sd_event_exit(..., -EIO) on any kind of error. EIO
is rather misleading here, becuase it suggests a serious error.

on_spawn_sigchld is updated to set the return code to distinguish failure to
spawn, including the program being killed by a signal (a negative return value),
and the program failing (positive return value).

The logging levels are adjusted, so that for PROGRAM= calls, which are
essentially "if" statements, we only log at debug level (unless we get a
timeout or segfault or another unexpected error).
---
 src/udev/udev-event.c | 38 +++++++++++++-------------------------
 src/udev/udev-rules.c | 12 +++++++-----
 2 files changed, 20 insertions(+), 30 deletions(-)

diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 3e916976c03..07b7365e3aa 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -504,38 +504,34 @@ static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *use
 
 static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
         Spawn *spawn = userdata;
+        int ret = -EIO;
 
         assert(spawn);
 
         switch (si->si_code) {
         case CLD_EXITED:
-                if (si->si_status == 0) {
+                if (si->si_status == 0)
                         log_debug("Process '%s' succeeded.", spawn->cmd);
-                        sd_event_exit(sd_event_source_get_event(s), 0);
-
-                        return 1;
-                }
-
-                log_full(spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
-                         "Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+                else
+                        log_full(spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
+                                 "Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+                ret = si->si_status;
                 break;
         case CLD_KILLED:
         case CLD_DUMPED:
-                log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
-
+                log_error("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
                 break;
         default:
                 log_error("Process '%s' failed due to unknown reason.", spawn->cmd);
         }
 
-        sd_event_exit(sd_event_source_get_event(s), -EIO);
-
+        sd_event_exit(sd_event_source_get_event(s), ret);
         return 1;
 }
 
 static int spawn_wait(Spawn *spawn) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
-        int r, ret;
+        int r;
 
         assert(spawn);
 
@@ -586,15 +582,7 @@ static int spawn_wait(Spawn *spawn) {
         if (r < 0)
                 return r;
 
-        r = sd_event_loop(e);
-        if (r < 0)
-                return r;
-
-        r = sd_event_get_exit_code(e, &ret);
-        if (r < 0)
-                return r;
-
-        return ret;
+        return sd_event_loop(e);
 }
 
 int udev_event_spawn(UdevEvent *event,
@@ -679,12 +667,12 @@ int udev_event_spawn(UdevEvent *event,
         };
         r = spawn_wait(&spawn);
         if (r < 0)
-                return log_error_errno(r, "Failed to wait spawned command '%s': %m", cmd);
+                return log_error_errno(r, "Failed to wait for spawned command '%s': %m", cmd);
 
         if (result)
                 result[spawn.result_len] = '\0';
 
-        return r;
+        return r; /* 0 for success, and positive if the program failed */
 }
 
 static int rename_netif(UdevEvent *event) {
@@ -899,7 +887,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
                                 (void) usleep(event->exec_delay_usec);
                         }
 
-                        udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
+                        (void) udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
                 }
         }
 }
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index 53c68d254a6..f697972debe 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -645,11 +645,13 @@ static int import_program_into_properties(UdevEvent *event,
                                           const char *program) {
         char result[UTIL_LINE_SIZE];
         char *line;
-        int err;
+        int r;
 
-        err = udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result));
-        if (err < 0)
-                return err;
+        r = udev_event_spawn(event, timeout_usec, false, program, result, sizeof result);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return -EIO;
 
         line = result;
         while (line) {
@@ -1959,7 +1961,7 @@ int udev_rules_apply_to_event(
                                          rules_str(rules, rule->rule.filename_off),
                                          rule->rule.filename_line);
 
-                        if (udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result)) < 0) {
+                        if (udev_event_spawn(event, timeout_usec, true, program, result, sizeof(result)) != 0) {
                                 if (cur->key.op != OP_NOMATCH)
                                         goto nomatch;
                         } else {
