summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-07-28 10:13:21 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-07-29 15:54:45 +0200
commite1714f0250a9128a89f5610bd53064cc7c7ec960 (patch)
treed207e3b827fd14b0b213aecc7687c1e71ce202e0
parenteeba9cc3d751c75e7f5e35333e1630662fe8257b (diff)
downloadsystemd-e1714f0250a9128a89f5610bd53064cc7c7ec960.tar.gz
shared/exit-status: turn status level into a bitmask, add "test"
The "test" doesn't really test much automatically, but it is still useful to look at the mappings.
-rw-r--r--src/core/execute.c10
-rw-r--r--src/core/main.c19
-rw-r--r--src/shared/exit-status.c305
-rw-r--r--src/shared/exit-status.h31
-rw-r--r--src/systemctl/systemctl.c5
-rw-r--r--src/test/meson.build4
-rw-r--r--src/test/test-exit-status.c26
7 files changed, 161 insertions, 239 deletions
diff --git a/src/core/execute.c b/src/core/execute.c
index 911c369042..5b55557f4e 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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);
}
diff --git a/src/core/main.c b/src/core/main.c
index 0674e00ab0..0698f893fd 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -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);
}
}
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
index 58ebc3ca4d..51f2380027 100644
--- a/src/shared/exit-status.c
+++ b/src/shared/exit-status.c
@@ -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 &&
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 5637e6aa04..425f841523 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -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,
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 95a8524594..b9a7d488bd 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -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);
diff --git a/src/test/meson.build b/src/test/meson.build
index 0595cfe37a..5625e682cf 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -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
index 0000000000..bab0596580
--- /dev/null
+++ b/src/test/test-exit-status.c
@@ -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;
+}