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.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2019-07-28 10:13:21 +02:00
parent eeba9cc3d7
commit e1714f0250
7 changed files with 160 additions and 238 deletions

View 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);
}

View 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);
}
}

View 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:
*
* 01 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 */
[EXIT_SUCCESS] = { "SUCCESS", EXIT_STATUS_GLIBC },
[EXIT_FAILURE] = { "FAILURE", EXIT_STATUS_GLIBC },
case EXIT_SUCCESS:
return "SUCCESS";
[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 },
case EXIT_FAILURE:
return "FAILURE";
[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;
}
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;
}
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";
case EX_CONFIG:
return "CONFIG";
}
}
return NULL;
}
bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
return status == 0 ||
(success_status &&

View 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,

View 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);

View file

@ -305,6 +305,10 @@ tests += [
[],
[]],
[['src/test/test-exit-status.c'],
[],
[]],
[['src/test/test-specifier.c'],
[],
[]],

View file

@ -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;
}