]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared/exit-status: turn status level into a bitmask, add "test"
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sun, 28 Jul 2019 08:13:21 +0000 (10:13 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 29 Jul 2019 13:54:45 +0000 (15:54 +0200)
The "test" doesn't really test much automatically, but it is still useful
to look at the mappings.

src/core/execute.c
src/core/main.c
src/shared/exit-status.c
src/shared/exit-status.h
src/systemctl/systemctl.c
src/test/meson.build
src/test/test-exit-status.c [new file with mode: 0644]

index 911c3690428ae73fbf43bccba8069a51b5ece48e..5b55557f4e8cb51afe6b6f800dbba7b3a6e28c2b 100644 (file)
@@ -3878,15 +3878,19 @@ int exec_spawn(Unit *unit,
                                unit->manager->user_lookup_fds[1],
                                &exit_status);
 
-                if (r < 0)
+                if (r < 0) {
+                        const char *status =
+                                exit_status_to_string(exit_status,
+                                                      EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
+
                         log_struct_errno(LOG_ERR, r,
                                          "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
                                          LOG_UNIT_ID(unit),
                                          LOG_UNIT_INVOCATION_ID(unit),
                                          LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
-                                                          exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
-                                                          command->path),
+                                                          status, command->path),
                                          "EXECUTABLE=%s", command->path);
+                }
 
                 _exit(exit_status);
         }
index 0674e00ab01f849af4130155e26c33ed5351d830..0698f893fd9d92202ee8da85bf6a7868b329931e 100644 (file)
@@ -221,16 +221,19 @@ _noreturn_ static void crash(int sig) {
                         r = wait_for_terminate(pid, &status);
                         if (r < 0)
                                 log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig));
-                        else if (status.si_code != CLD_DUMPED)
+                        else if (status.si_code != CLD_DUMPED) {
+                                const char *s = status.si_code == CLD_EXITED
+                                        ? exit_status_to_string(status.si_status, EXIT_STATUS_GLIBC)
+                                        : signal_to_string(status.si_status);
+
                                 log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).",
                                               signal_to_string(sig),
-                                              pid, sigchld_code_to_string(status.si_code),
-                                              status.si_status,
-                                              strna(status.si_code == CLD_EXITED
-                                                    ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL)
-                                                    : signal_to_string(status.si_status)));
-                        else
-                                log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
+                                              pid,
+                                              sigchld_code_to_string(status.si_code),
+                                              status.si_status, strna(s));
+                        } else
+                                log_emergency("Caught <%s>, dumped core as pid "PID_FMT".",
+                                              signal_to_string(sig), pid);
                 }
         }
 
index 58ebc3ca4d6558cf1d28ba544f9e9bcf81c43da0..51f23800274e956c8636e2071f0142ea06e09bd7 100644 (file)
@@ -8,8 +8,7 @@
 #include "macro.h"
 #include "set.h"
 
-const char* exit_status_to_string(int status, ExitStatusLevel level) {
-
+const ExitStatusMapping exit_status_mappings[256] = {
         /* Exit status ranges:
          *
          *   0…1   │ ISO C, EXIT_SUCCESS + EXIT_FAILURE
@@ -25,224 +24,100 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
          *         │ signal or such, and we follow that logic here.)
          */
 
-        switch (status) {  /* We always cover the ISO C ones */
-
-        case EXIT_SUCCESS:
-                return "SUCCESS";
-
-        case EXIT_FAILURE:
-                return "FAILURE";
-        }
-
-        if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
-                switch (status) { /* Optionally we cover our own ones */
-
-                case EXIT_CHDIR:
-                        return "CHDIR";
-
-                case EXIT_NICE:
-                        return "NICE";
-
-                case EXIT_FDS:
-                        return "FDS";
-
-                case EXIT_EXEC:
-                        return "EXEC";
-
-                case EXIT_MEMORY:
-                        return "MEMORY";
-
-                case EXIT_LIMITS:
-                        return "LIMITS";
-
-                case EXIT_OOM_ADJUST:
-                        return "OOM_ADJUST";
-
-                case EXIT_SIGNAL_MASK:
-                        return "SIGNAL_MASK";
-
-                case EXIT_STDIN:
-                        return "STDIN";
-
-                case EXIT_STDOUT:
-                        return "STDOUT";
-
-                case EXIT_CHROOT:
-                        return "CHROOT";
-
-                case EXIT_IOPRIO:
-                        return "IOPRIO";
-
-                case EXIT_TIMERSLACK:
-                        return "TIMERSLACK";
-
-                case EXIT_SECUREBITS:
-                        return "SECUREBITS";
-
-                case EXIT_SETSCHEDULER:
-                        return "SETSCHEDULER";
-
-                case EXIT_CPUAFFINITY:
-                        return "CPUAFFINITY";
-
-                case EXIT_GROUP:
-                        return "GROUP";
-
-                case EXIT_USER:
-                        return "USER";
-
-                case EXIT_CAPABILITIES:
-                        return "CAPABILITIES";
-
-                case EXIT_CGROUP:
-                        return "CGROUP";
-
-                case EXIT_SETSID:
-                        return "SETSID";
-
-                case EXIT_CONFIRM:
-                        return "CONFIRM";
-
-                case EXIT_STDERR:
-                        return "STDERR";
-
-                case EXIT_PAM:
-                        return "PAM";
-
-                case EXIT_NETWORK:
-                        return "NETWORK";
-
-                case EXIT_NAMESPACE:
-                        return "NAMESPACE";
-
-                case EXIT_NO_NEW_PRIVILEGES:
-                        return "NO_NEW_PRIVILEGES";
-
-                case EXIT_SECCOMP:
-                        return "SECCOMP";
-
-                case EXIT_SELINUX_CONTEXT:
-                        return "SELINUX_CONTEXT";
-
-                case EXIT_PERSONALITY:
-                        return "PERSONALITY";
-
-                case EXIT_APPARMOR_PROFILE:
-                        return "APPARMOR";
-
-                case EXIT_ADDRESS_FAMILIES:
-                        return "ADDRESS_FAMILIES";
-
-                case EXIT_RUNTIME_DIRECTORY:
-                        return "RUNTIME_DIRECTORY";
-
-                case EXIT_CHOWN:
-                        return "CHOWN";
-
-                case EXIT_SMACK_PROCESS_LABEL:
-                        return "SMACK_PROCESS_LABEL";
-
-                case EXIT_KEYRING:
-                        return "KEYRING";
-
-                case EXIT_STATE_DIRECTORY:
-                        return "STATE_DIRECTORY";
-
-                case EXIT_CACHE_DIRECTORY:
-                        return "CACHE_DIRECTORY";
-
-                case EXIT_LOGS_DIRECTORY:
-                        return "LOGS_DIRECTORY";
-
-                case EXIT_CONFIGURATION_DIRECTORY:
-                        return "CONFIGURATION_DIRECTORY";
-
-                case EXIT_NUMA_POLICY:
-                        return "NUMA_POLICY";
-
-                case EXIT_EXCEPTION:
-                        return "EXCEPTION";
-                }
-        }
-
-        if (IN_SET(level, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) {
-                switch (status) { /* Optionally we support LSB ones */
-
-                case EXIT_INVALIDARGUMENT:
-                        return "INVALIDARGUMENT";
-
-                case EXIT_NOTIMPLEMENTED:
-                        return "NOTIMPLEMENTED";
-
-                case EXIT_NOPERMISSION:
-                        return "NOPERMISSION";
-
-                case EXIT_NOTINSTALLED:
-                        return "NOTINSTALLED";
-
-                case EXIT_NOTCONFIGURED:
-                        return "NOTCONFIGURED";
-
-                case EXIT_NOTRUNNING:
-                        return "NOTRUNNING";
-                }
-        }
-
-        if (level == EXIT_STATUS_FULL) {
-                switch (status) { /* Optionally, we support BSD exit statusses */
-
-                case EX_USAGE:
-                        return "USAGE";
-
-                case EX_DATAERR:
-                        return "DATAERR";
-
-                case EX_NOINPUT:
-                        return "NOINPUT";
-
-                case EX_NOUSER:
-                        return "NOUSER";
-
-                case EX_NOHOST:
-                        return "NOHOST";
-
-                case EX_UNAVAILABLE:
-                        return "UNAVAILABLE";
-
-                case EX_SOFTWARE:
-                        return "SOFTWARE";
-
-                case EX_OSERR:
-                        return "OSERR";
-
-                case EX_OSFILE:
-                        return "OSFILE";
-
-                case EX_CANTCREAT:
-                        return "CANTCREAT";
-
-                case EX_IOERR:
-                        return "IOERR";
-
-                case EX_TEMPFAIL:
-                        return "TEMPFAIL";
-
-                case EX_PROTOCOL:
-                        return "PROTOCOL";
-
-                case EX_NOPERM:
-                        return "NOPERM";
+        [EXIT_SUCCESS] =                 { "SUCCESS",                 EXIT_STATUS_GLIBC },
+        [EXIT_FAILURE] =                 { "FAILURE",                 EXIT_STATUS_GLIBC },
+
+        [EXIT_CHDIR] =                   { "CHDIR",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_NICE] =                    { "NICE",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_FDS] =                     { "FDS",                     EXIT_STATUS_SYSTEMD },
+        [EXIT_EXEC] =                    { "EXEC",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_MEMORY] =                  { "MEMORY",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_LIMITS] =                  { "LIMITS",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_OOM_ADJUST] =              { "OOM_ADJUST",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SIGNAL_MASK] =             { "SIGNAL_MASK",             EXIT_STATUS_SYSTEMD },
+        [EXIT_STDIN] =                   { "STDIN",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_STDOUT] =                  { "STDOUT",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_CHROOT] =                  { "CHROOT",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_IOPRIO] =                  { "IOPRIO",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_TIMERSLACK] =              { "TIMERSLACK",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SECUREBITS] =              { "SECUREBITS",              EXIT_STATUS_SYSTEMD },
+        [EXIT_SETSCHEDULER] =            { "SETSCHEDULER",            EXIT_STATUS_SYSTEMD },
+        [EXIT_CPUAFFINITY] =             { "CPUAFFINITY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_GROUP] =                   { "GROUP",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_USER] =                    { "USER",                    EXIT_STATUS_SYSTEMD },
+        [EXIT_CAPABILITIES] =            { "CAPABILITIES",            EXIT_STATUS_SYSTEMD },
+        [EXIT_CGROUP] =                  { "CGROUP",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_SETSID] =                  { "SETSID",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_CONFIRM] =                 { "CONFIRM",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_STDERR] =                  { "STDERR",                  EXIT_STATUS_SYSTEMD },
+        [EXIT_PAM] =                     { "PAM",                     EXIT_STATUS_SYSTEMD },
+        [EXIT_NETWORK] =                 { "NETWORK",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_NAMESPACE] =               { "NAMESPACE",               EXIT_STATUS_SYSTEMD },
+        [EXIT_NO_NEW_PRIVILEGES] =       { "NO_NEW_PRIVILEGES",       EXIT_STATUS_SYSTEMD },
+        [EXIT_SECCOMP] =                 { "SECCOMP",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_SELINUX_CONTEXT] =         { "SELINUX_CONTEXT",         EXIT_STATUS_SYSTEMD },
+        [EXIT_PERSONALITY] =             { "PERSONALITY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_APPARMOR_PROFILE] =        { "APPARMOR",                EXIT_STATUS_SYSTEMD },
+        [EXIT_ADDRESS_FAMILIES] =        { "ADDRESS_FAMILIES",        EXIT_STATUS_SYSTEMD },
+        [EXIT_RUNTIME_DIRECTORY] =       { "RUNTIME_DIRECTORY",       EXIT_STATUS_SYSTEMD },
+        [EXIT_CHOWN] =                   { "CHOWN",                   EXIT_STATUS_SYSTEMD },
+        [EXIT_SMACK_PROCESS_LABEL] =     { "SMACK_PROCESS_LABEL",     EXIT_STATUS_SYSTEMD },
+        [EXIT_KEYRING] =                 { "KEYRING",                 EXIT_STATUS_SYSTEMD },
+        [EXIT_STATE_DIRECTORY] =         { "STATE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
+        [EXIT_CACHE_DIRECTORY] =         { "CACHE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
+        [EXIT_LOGS_DIRECTORY] =          { "LOGS_DIRECTORY",          EXIT_STATUS_SYSTEMD },
+        [EXIT_CONFIGURATION_DIRECTORY] = { "CONFIGURATION_DIRECTORY", EXIT_STATUS_SYSTEMD },
+        [EXIT_NUMA_POLICY] =             { "NUMA_POLICY",             EXIT_STATUS_SYSTEMD },
+        [EXIT_EXCEPTION] =               { "EXCEPTION",               EXIT_STATUS_SYSTEMD },
+
+        [EXIT_INVALIDARGUMENT] =         { "INVALIDARGUMENT",         EXIT_STATUS_LSB },
+        [EXIT_NOTIMPLEMENTED] =          { "NOTIMPLEMENTED",          EXIT_STATUS_LSB },
+        [EXIT_NOPERMISSION] =            { "NOPERMISSION",            EXIT_STATUS_LSB },
+        [EXIT_NOTINSTALLED] =            { "NOTINSTALLED",            EXIT_STATUS_LSB },
+        [EXIT_NOTCONFIGURED] =           { "NOTCONFIGURED",           EXIT_STATUS_LSB },
+        [EXIT_NOTRUNNING] =              { "NOTRUNNING",              EXIT_STATUS_LSB },
+
+        [EX_USAGE] =                     { "USAGE",                   EXIT_STATUS_BSD },
+        [EX_DATAERR] =                   { "DATAERR",                 EXIT_STATUS_BSD },
+        [EX_NOINPUT] =                   { "NOINPUT",                 EXIT_STATUS_BSD },
+        [EX_NOUSER] =                    { "NOUSER",                  EXIT_STATUS_BSD },
+        [EX_NOHOST] =                    { "NOHOST",                  EXIT_STATUS_BSD },
+        [EX_UNAVAILABLE] =               { "UNAVAILABLE",             EXIT_STATUS_BSD },
+        [EX_SOFTWARE] =                  { "SOFTWARE",                EXIT_STATUS_BSD },
+        [EX_OSERR] =                     { "OSERR",                   EXIT_STATUS_BSD },
+        [EX_OSFILE] =                    { "OSFILE",                  EXIT_STATUS_BSD },
+        [EX_CANTCREAT] =                 { "CANTCREAT",               EXIT_STATUS_BSD },
+        [EX_IOERR] =                     { "IOERR",                   EXIT_STATUS_BSD },
+        [EX_TEMPFAIL] =                  { "TEMPFAIL",                EXIT_STATUS_BSD },
+        [EX_PROTOCOL] =                  { "PROTOCOL",                EXIT_STATUS_BSD },
+        [EX_NOPERM] =                    { "NOPERM",                  EXIT_STATUS_BSD },
+        [EX_CONFIG] =                    { "CONFIG",                  EXIT_STATUS_BSD },
+};
+
+const char* exit_status_to_string(int code, ExitStatusClass class) {
+        if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
+                return NULL;
+        return FLAGS_SET(exit_status_mappings[code].class, class) ? exit_status_mappings[code].name : NULL;
+}
 
-                case EX_CONFIG:
-                        return "CONFIG";
-                }
+const char* exit_status_class(int code) {
+        if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
+                return NULL;
+
+        switch (exit_status_mappings[code].class) {
+        case EXIT_STATUS_GLIBC:
+                return "glibc";
+        case EXIT_STATUS_SYSTEMD:
+                return "systemd";
+        case EXIT_STATUS_LSB:
+                return "LSB";
+        case EXIT_STATUS_BSD:
+                return "BSD";
+        default: return NULL;
         }
-
-        return NULL;
 }
 
 bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
-
         if (code == CLD_EXITED)
                 return status == 0 ||
                        (success_status &&
index 5637e6aa04ddd9df731caf843baebc5b29667539..425f8415234a34824fabe659e34b122743b3525d 100644 (file)
@@ -7,8 +7,8 @@
 #include "macro.h"
 #include "set.h"
 
-/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB
- * 'status' verb exit codes which are defined very differently. For details see:
+/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with
+ * the LSB 'status' verb exit codes which are defined very differently. For details see:
  *
  * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
  */
@@ -25,8 +25,8 @@ enum {
 
         /* BSD's sysexits.h defines a couple EX_xyz exit codes in the range 64 … 78 */
 
-        /* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption that they
-         * hence are unused by init scripts. */
+        /* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption
+         * that they hence are unused by init scripts. */
         EXIT_CHDIR = 200,
         EXIT_NICE,
         EXIT_FDS,
@@ -74,19 +74,28 @@ enum {
         EXIT_EXCEPTION = 255,  /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
 };
 
-typedef enum ExitStatusLevel {
-        EXIT_STATUS_MINIMAL,   /* only cover libc EXIT_STATUS/EXIT_FAILURE */
-        EXIT_STATUS_SYSTEMD,   /* cover libc and systemd's own exit codes */
-        EXIT_STATUS_LSB,       /* cover libc, systemd's own and LSB exit codes */
-        EXIT_STATUS_FULL,      /* cover libc, systemd's own, LSB and BSD (EX_xyz) exit codes */
-} ExitStatusLevel;
+typedef enum ExitStatusClass {
+        EXIT_STATUS_GLIBC   = 1 << 0,  /* libc EXIT_STATUS/EXIT_FAILURE */
+        EXIT_STATUS_SYSTEMD = 1 << 1,  /* systemd's own exit codes */
+        EXIT_STATUS_LSB     = 1 << 2,  /* LSB exit codes */
+        EXIT_STATUS_BSD     = 1 << 3,  /* BSD (EX_xyz) exit codes */
+        EXIT_STATUS_FULL    = EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD | EXIT_STATUS_LSB | EXIT_STATUS_BSD,
+} ExitStatusClass;
 
 typedef struct ExitStatusSet {
         Set *status;
         Set *signal;
 } ExitStatusSet;
 
-const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
+const char* exit_status_to_string(int code, ExitStatusClass class) _const_;
+const char* exit_status_class(int code) _const_;
+
+typedef struct ExitStatusMapping {
+        const char *name;
+        ExitStatusClass class;
+} ExitStatusMapping;
+
+extern const ExitStatusMapping exit_status_mappings[256];
 
 typedef enum ExitClean {
         EXIT_CLEAN_DAEMON,
index 95a85245946a88f0f5f65ba4bd529a2863e89b1a..b9a7d488bd442352436eeff20e5d72117a47ecd6 100644 (file)
@@ -4380,7 +4380,7 @@ static void print_status_info(
 
                         printf("status=%i", p->status);
 
-                        c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
+                        c = exit_status_to_string(p->status, EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
                         if (c)
                                 printf("/%s", c);
 
@@ -4421,7 +4421,8 @@ static void print_status_info(
 
                                         printf("status=%i", i->exit_status);
 
-                                        c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
+                                        c = exit_status_to_string(i->exit_status,
+                                                                  EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD);
                                         if (c)
                                                 printf("/%s", c);
 
index 0595cfe37a6dc271af20e479b9379edbef86cbad..5625e682cf1cbc2f24c355bab54075867b961e97 100644 (file)
@@ -305,6 +305,10 @@ tests += [
          [],
          []],
 
+        [['src/test/test-exit-status.c'],
+         [],
+         []],
+
         [['src/test/test-specifier.c'],
          [],
          []],
diff --git a/src/test/test-exit-status.c b/src/test/test-exit-status.c
new file mode 100644 (file)
index 0000000..bab0596
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "exit-status.h"
+#include "tests.h"
+
+static void test_exit_status_to_string(void) {
+        log_info("/* %s */", __func__);
+
+        for (int i = -1; i <= 256; i++) {
+                const char *s, *class;
+
+                s = exit_status_to_string(i, EXIT_STATUS_FULL);
+                class = exit_status_class(i);
+                log_info("%d: %s%s%s%s",
+                         i, s ?: "-",
+                         class ? " (" : "", class ?: "", class ? ")" : "");
+        }
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_exit_status_to_string();
+
+        return 0;
+}