diff options
author | Lennart Poettering <lennart@poettering.net> | 2019-07-29 18:58:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-29 18:58:06 +0200 |
commit | 1d7458fbb1b09ebbfdcf775909e09c56d6a6023e (patch) | |
tree | ed02e8a66927287165a16931a5deb410924f705e /src | |
parent | pid1: use LOG_DEBUG/INFO/NOTICE for unit resource consumption message (diff) | |
parent | NEWS: add entry about exit status changes (diff) | |
download | systemd-1d7458fbb1b09ebbfdcf775909e09c56d6a6023e.tar.xz systemd-1d7458fbb1b09ebbfdcf775909e09c56d6a6023e.zip |
Merge pull request #13207 from keszybz/symbolic-exit-code-names
Symbolic exit code names
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze/analyze.c | 45 | ||||
-rw-r--r-- | src/core/dbus-service.c | 48 | ||||
-rw-r--r-- | src/core/execute.c | 10 | ||||
-rw-r--r-- | src/core/load-fragment.c | 34 | ||||
-rw-r--r-- | src/core/main.c | 19 | ||||
-rw-r--r-- | src/core/unit.c | 6 | ||||
-rw-r--r-- | src/shared/bitmap.c | 17 | ||||
-rw-r--r-- | src/shared/bitmap.h | 14 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 40 | ||||
-rw-r--r-- | src/shared/bus-util.c | 8 | ||||
-rw-r--r-- | src/shared/bus-util.h | 7 | ||||
-rw-r--r-- | src/shared/exit-status.c | 341 | ||||
-rw-r--r-- | src/shared/exit-status.h | 44 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 31 | ||||
-rw-r--r-- | src/test/meson.build | 4 | ||||
-rw-r--r-- | src/test/test-exit-status.c | 41 | ||||
-rw-r--r-- | src/tty-ask-password-agent/tty-ask-password-agent.c | 1 |
17 files changed, 345 insertions, 365 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 384924ed34..45e41fedee 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -24,6 +24,7 @@ #include "conf-files.h" #include "copy.h" #include "def.h" +#include "exit-status.h" #include "fd-util.h" #include "fileio.h" #include "format-table.h" @@ -1637,6 +1638,48 @@ static void dump_syscall_filter(const SyscallFilterSet *set) { printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal()); } +static int dump_exit_codes(int argc, char *argv[], void *userdata) { + _cleanup_(table_unrefp) Table *table = NULL; + int r; + + table = table_new("name", "code", "class"); + if (!table) + return log_oom(); + + if (strv_isempty(strv_skip(argv, 1))) + for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) { + if (!exit_status_mappings[i].name) + continue; + + r = table_add_many(table, + TABLE_STRING, exit_status_mappings[i].name, + TABLE_UINT, i, + TABLE_STRING, exit_status_class(i)); + if (r < 0) + return r; + } + else + for (int i = 1; i < argc; i++) { + int code; + + code = exit_status_from_string(argv[i]); + if (code < 0) + return log_error_errno(r, "Invalid exit code \"%s\": %m", argv[i]); + + assert(code >= 0 && (size_t) code < ELEMENTSOF(exit_status_mappings)); + r = table_add_many(table, + TABLE_STRING, exit_status_mappings[code].name ?: "-", + TABLE_UINT, code, + TABLE_STRING, exit_status_class(code) ?: "-"); + if (r < 0) + return r; + } + + (void) pager_open(arg_pager_flags); + + return table_print(table, NULL); +} + static int dump_syscall_filters(int argc, char *argv[], void *userdata) { bool first = true; @@ -2170,6 +2213,7 @@ static int help(int argc, char *argv[], void *userdata) { " dump Output state serialization of service manager\n" " cat-config Show configuration file and drop-ins\n" " unit-paths List load directories for units\n" + " exit-codes List exit code definitions\n" " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " condition CONDITION... Evaluate conditions and asserts\n" " verify FILE... Check unit files for correctness\n" @@ -2374,6 +2418,7 @@ static int run(int argc, char *argv[]) { { "dump", VERB_ANY, 1, 0, dump }, { "cat-config", 2, VERB_ANY, 0, cat_config }, { "unit-paths", 1, 1, 0, dump_unit_paths }, + { "exit-codes", VERB_ANY, VERB_ANY, 0, dump_exit_codes }, { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, { "condition", 2, VERB_ANY, 0, do_condition }, { "verify", 2, VERB_ANY, 0, do_verify }, diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 07f02deed6..fbda8d8a4c 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -39,9 +39,9 @@ static int property_get_exit_status_set( void *userdata, sd_bus_error *error) { - ExitStatusSet *status_set = userdata; + const ExitStatusSet *status_set = userdata; + unsigned n; Iterator i; - void *id; int r; assert(bus); @@ -56,13 +56,10 @@ static int property_get_exit_status_set( if (r < 0) return r; - SET_FOREACH(id, status_set->status, i) { - int32_t val = PTR_TO_INT(id); + BITMAP_FOREACH(n, &status_set->status, i) { + assert(n < 256); - if (val < 0 || val > 255) - continue; - - r = sd_bus_message_append_basic(reply, 'i', &val); + r = sd_bus_message_append_basic(reply, 'i', &n); if (r < 0) return r; } @@ -75,15 +72,14 @@ static int property_get_exit_status_set( if (r < 0) return r; - SET_FOREACH(id, status_set->signal, i) { - int32_t val = PTR_TO_INT(id); + BITMAP_FOREACH(n, &status_set->signal, i) { const char *str; - str = signal_to_string((int) val); + str = signal_to_string(n); if (!str) continue; - r = sd_bus_message_append_basic(reply, 'i', &val); + r = sd_bus_message_append_basic(reply, 'i', &n); if (r < 0) return r; } @@ -163,18 +159,18 @@ static int bus_set_transient_exit_status( sd_bus_error *error) { const int32_t *status, *signal; - size_t sz_status, sz_signal, i; + size_t n_status, n_signal, i; int r; r = sd_bus_message_enter_container(message, 'r', "aiai"); if (r < 0) return r; - r = sd_bus_message_read_array(message, 'i', (const void **) &status, &sz_status); + r = sd_bus_message_read_array(message, 'i', (const void **) &status, &n_status); if (r < 0) return r; - r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &sz_signal); + r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &n_signal); if (r < 0) return r; @@ -182,25 +178,21 @@ static int bus_set_transient_exit_status( if (r < 0) return r; - sz_status /= sizeof(int32_t); - sz_signal /= sizeof(int32_t); + n_status /= sizeof(int32_t); + n_signal /= sizeof(int32_t); - if (sz_status == 0 && sz_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) { + if (n_status == 0 && n_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) { exit_status_set_free(status_set); unit_write_settingf(u, flags, name, "%s=", name); return 1; } - for (i = 0; i < sz_status; i++) { + for (i = 0; i < n_status; i++) { if (status[i] < 0 || status[i] > 255) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %"PRIi32, name, status[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = set_ensure_allocated(&status_set->status, NULL); - if (r < 0) - return r; - - r = set_put(status_set->status, INT_TO_PTR((int) status[i])); + r = bitmap_set(&status_set->status, status[i]); if (r < 0) return r; @@ -208,7 +200,7 @@ static int bus_set_transient_exit_status( } } - for (i = 0; i < sz_signal; i++) { + for (i = 0; i < n_signal; i++) { const char *str; str = signal_to_string((int) signal[i]); @@ -216,11 +208,7 @@ static int bus_set_transient_exit_status( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %"PRIi32, name, signal[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = set_ensure_allocated(&status_set->signal, NULL); - if (r < 0) - return r; - - r = set_put(status_set->signal, INT_TO_PTR((int) signal[i])); + r = bitmap_set(&status_set->signal, signal[i]); if (r < 0) return r; 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/load-fragment.c b/src/core/load-fragment.c index 3288b0b838..8664500e1d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3936,37 +3936,33 @@ int config_parse_set_status( FOREACH_WORD(word, l, rvalue, state) { _cleanup_free_ char *temp; - int val; - Set **set; + Bitmap *bitmap; temp = strndup(word, l); if (!temp) return log_oom(); - r = safe_atoi(temp, &val); - if (r < 0) { - val = signal_from_string(temp); + /* We need to call exit_status_from_string() first, because we want + * to parse numbers as exit statuses, not signals. */ - if (val <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word); - continue; - } - set = &status_set->signal; + r = exit_status_from_string(temp); + if (r >= 0) { + assert(r >= 0 && r < 256); + bitmap = &status_set->status; } else { - if (val < 0 || val > 255) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val); + r = signal_from_string(temp); + + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse value, ignoring: %s", word); continue; } - set = &status_set->status; + bitmap = &status_set->signal; } - r = set_ensure_allocated(set, NULL); + r = bitmap_set(bitmap, r); if (r < 0) - return log_oom(); - - r = set_put(*set, INT_TO_PTR(val)); - if (r < 0) - return log_oom(); + return log_error_errno(r, "Failed to set signal or status %s: %m", word); } if (!isempty(state)) log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); 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/core/unit.c b/src/core/unit.c index c130fff7bb..d783e5c867 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5797,9 +5797,11 @@ int unit_test_trigger_loaded(Unit *u) { trigger = UNIT_TRIGGER(u); if (!trigger) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit to trigger not loaded."); + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "Refusing to start, no unit to trigger."); if (trigger->load_state != UNIT_LOADED) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit %s to trigger not loaded.", u->id); + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "Refusing to start, unit %s to trigger not loaded.", trigger->id); return 0; } diff --git a/src/shared/bitmap.c b/src/shared/bitmap.c index a956a42cab..2eba72dd59 100644 --- a/src/shared/bitmap.c +++ b/src/shared/bitmap.c @@ -12,12 +12,6 @@ #include "macro.h" #include "memory-util.h" -struct Bitmap { - uint64_t *bitmaps; - size_t n_bitmaps; - size_t bitmaps_allocated; -}; - /* Bitmaps are only meant to store relatively small numbers * (corresponding to, say, an enum), so it is ok to limit * the max entry. 64k should be plenty. */ @@ -117,7 +111,7 @@ void bitmap_unset(Bitmap *b, unsigned n) { b->bitmaps[offset] &= ~bitmask; } -bool bitmap_isset(Bitmap *b, unsigned n) { +bool bitmap_isset(const Bitmap *b, unsigned n) { uint64_t bitmask; unsigned offset; @@ -134,7 +128,7 @@ bool bitmap_isset(Bitmap *b, unsigned n) { return !!(b->bitmaps[offset] & bitmask); } -bool bitmap_isclear(Bitmap *b) { +bool bitmap_isclear(const Bitmap *b) { unsigned i; if (!b) @@ -148,7 +142,6 @@ bool bitmap_isclear(Bitmap *b) { } void bitmap_clear(Bitmap *b) { - if (!b) return; @@ -157,7 +150,7 @@ void bitmap_clear(Bitmap *b) { b->bitmaps_allocated = 0; } -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) { +bool bitmap_iterate(const Bitmap *b, Iterator *i, unsigned *n) { uint64_t bitmask; unsigned offset, rem; @@ -192,9 +185,9 @@ bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) { return false; } -bool bitmap_equal(Bitmap *a, Bitmap *b) { +bool bitmap_equal(const Bitmap *a, const Bitmap *b) { size_t common_n_bitmaps; - Bitmap *c; + const Bitmap *c; unsigned i; if (a == b) diff --git a/src/shared/bitmap.h b/src/shared/bitmap.h index 843d27d24d..f65a050584 100644 --- a/src/shared/bitmap.h +++ b/src/shared/bitmap.h @@ -6,7 +6,11 @@ #include "hashmap.h" #include "macro.h" -typedef struct Bitmap Bitmap; +typedef struct Bitmap { + uint64_t *bitmaps; + size_t n_bitmaps; + size_t bitmaps_allocated; +} Bitmap; Bitmap *bitmap_new(void); Bitmap *bitmap_copy(Bitmap *b); @@ -15,13 +19,13 @@ void bitmap_free(Bitmap *b); int bitmap_set(Bitmap *b, unsigned n); void bitmap_unset(Bitmap *b, unsigned n); -bool bitmap_isset(Bitmap *b, unsigned n); -bool bitmap_isclear(Bitmap *b); +bool bitmap_isset(const Bitmap *b, unsigned n); +bool bitmap_isclear(const Bitmap *b); void bitmap_clear(Bitmap *b); -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n); +bool bitmap_iterate(const Bitmap *b, Iterator *i, unsigned *n); -bool bitmap_equal(Bitmap *a, Bitmap *b); +bool bitmap_equal(const Bitmap *a, const Bitmap *b); #define BITMAP_FOREACH(n, b, i) \ for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); ) diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 322204dd22..e53b9d5ea2 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -10,6 +10,7 @@ #include "cpu-set-util.h" #include "escape.h" #include "exec-util.h" +#include "exit-status.h" #include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" @@ -1439,12 +1440,11 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) { _cleanup_free_ int *status = NULL, *signal = NULL; - size_t sz_status = 0, sz_signal = 0; + size_t n_status = 0, n_signal = 0; const char *p; for (p = eq;;) { _cleanup_free_ char *word = NULL; - int val; r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); if (r == 0) @@ -1454,24 +1454,30 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con if (r < 0) return log_error_errno(r, "Invalid syntax in %s: %s", field, eq); - r = safe_atoi(word, &val); - if (r < 0) { - val = signal_from_string(word); - if (val < 0) - return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field); + /* We need to call exit_status_from_string() first, because we want + * to parse numbers as exit statuses, not signals. */ - signal = reallocarray(signal, sz_signal + 1, sizeof(int)); - if (!signal) - return log_oom(); + r = exit_status_from_string(word); + if (r >= 0) { + assert(r >= 0 && r < 256); - signal[sz_signal++] = val; - } else { - status = reallocarray(status, sz_status + 1, sizeof(int)); + status = reallocarray(status, n_status + 1, sizeof(int)); if (!status) return log_oom(); - status[sz_status++] = val; - } + status[n_status++] = r; + + } else if ((r = signal_from_string(word)) >= 0) { + signal = reallocarray(signal, n_signal + 1, sizeof(int)); + if (!signal) + return log_oom(); + + signal[n_signal++] = r; + + } else + /* original r from exit_status_to_string() */ + return log_error_errno(r, "Invalid status or signal %s in %s: %m", + word, field); } r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); @@ -1490,11 +1496,11 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'i', status, sz_status); + r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int)); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'i', signal, sz_signal); + r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int)); if (r < 0) return bus_log_create_error(r); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 8e301250bc..6af115e7aa 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1480,14 +1480,6 @@ int bus_property_get_ulong( } #endif -int bus_log_parse_error(int r) { - return log_error_errno(r, "Failed to parse bus message: %m"); -} - -int bus_log_create_error(int r) { - return log_error_errno(r, "Failed to create bus message: %m"); -} - /** * bus_path_encode_unique() - encode unique object path * @b: bus connection or NULL diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 3216b0c37a..1e2f04cc5d 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -114,8 +114,11 @@ assert_cc(sizeof(pid_t) == sizeof(uint32_t)); assert_cc(sizeof(mode_t) == sizeof(uint32_t)); #define bus_property_get_mode ((sd_bus_property_get_t) NULL) -int bus_log_parse_error(int r); -int bus_log_create_error(int r); +#define bus_log_parse_error(r) \ + log_error_errno(r, "Failed to parse bus message: %m") + +#define bus_log_create_error(r) \ + log_error_errno(r, "Failed to create bus message: %m") #define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val) \ int function(sd_bus *bus, \ diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c index 58ebc3ca4d..80ac4868cb 100644 --- a/src/shared/exit-status.c +++ b/src/shared/exit-status.c @@ -6,10 +6,11 @@ #include "exit-status.h" #include "macro.h" +#include "parse-util.h" #include "set.h" +#include "string-util.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,235 +26,127 @@ 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"; + [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 EXIT_NOTRUNNING: - return "NOTRUNNING"; - } +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 (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"; +int exit_status_from_string(const char *s) { + uint8_t val; + int r; - case EX_PROTOCOL: - return "PROTOCOL"; + for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) + if (streq_ptr(s, exit_status_mappings[i].name)) + return i; - case EX_NOPERM: - return "NOPERM"; + r = safe_atou8(s, &val); + if (r < 0) + return r; - case EX_CONFIG: - return "CONFIG"; - } - } - - return NULL; + return val; } -bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) { - +bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status) { if (code == CLD_EXITED) return status == 0 || (success_status && - set_contains(success_status->status, INT_TO_PTR(status))); + bitmap_isset(&success_status->status, status)); - /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */ + /* If a daemon does not implement handlers for some of the signals, we do not consider this an + unclean shutdown */ if (code == CLD_KILLED) return (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) || (success_status && - set_contains(success_status->signal, INT_TO_PTR(status))); + bitmap_isset(&success_status->signal, status)); return false; } @@ -261,26 +154,22 @@ bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success void exit_status_set_free(ExitStatusSet *x) { assert(x); - x->status = set_free(x->status); - x->signal = set_free(x->signal); + bitmap_clear(&x->status); + bitmap_clear(&x->signal); } -bool exit_status_set_is_empty(ExitStatusSet *x) { +bool exit_status_set_is_empty(const ExitStatusSet *x) { if (!x) return true; - return set_isempty(x->status) && set_isempty(x->signal); + return bitmap_isclear(&x->status) && bitmap_isclear(&x->signal); } -bool exit_status_set_test(ExitStatusSet *x, int code, int status) { - - if (exit_status_set_is_empty(x)) - return false; - - if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status))) +bool exit_status_set_test(const ExitStatusSet *x, int code, int status) { + if (code == CLD_EXITED && bitmap_isset(&x->status, status)) return true; - if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status))) + if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && bitmap_isset(&x->signal, status)) return true; return false; diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h index 5637e6aa04..d6da8c19b9 100644 --- a/src/shared/exit-status.h +++ b/src/shared/exit-status.h @@ -3,12 +3,12 @@ #include <stdbool.h> +#include "bitmap.h" #include "hashmap.h" #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,27 +74,37 @@ 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; + Bitmap status; + Bitmap 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_; +int exit_status_from_string(const char *s) _pure_; + +typedef struct ExitStatusMapping { + const char *name; + ExitStatusClass class; +} ExitStatusMapping; + +extern const ExitStatusMapping exit_status_mappings[256]; typedef enum ExitClean { EXIT_CLEAN_DAEMON, EXIT_CLEAN_COMMAND, } ExitClean; -bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status); +bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status); void exit_status_set_free(ExitStatusSet *x); -bool exit_status_set_is_empty(ExitStatusSet *x); -bool exit_status_set_test(ExitStatusSet *x, int code, int status); +bool exit_status_set_is_empty(const ExitStatusSet *x); +bool exit_status_set_test(const ExitStatusSet *x, int code, int status); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 95a8524594..880a04411c 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); @@ -4910,17 +4911,17 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m } else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) { const int32_t *status, *signal; - size_t sz_status, sz_signal, i; + size_t n_status, n_signal, i; r = sd_bus_message_enter_container(m, 'r', "aiai"); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read_array(m, 'i', (const void **) &status, &sz_status); + r = sd_bus_message_read_array(m, 'i', (const void **) &status, &n_status); if (r < 0) return bus_log_parse_error(r); - r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &sz_signal); + r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &n_signal); if (r < 0) return bus_log_parse_error(r); @@ -4928,10 +4929,10 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m if (r < 0) return bus_log_parse_error(r); - sz_status /= sizeof(int32_t); - sz_signal /= sizeof(int32_t); + n_status /= sizeof(int32_t); + n_signal /= sizeof(int32_t); - if (all || sz_status > 0 || sz_signal > 0) { + if (all || n_status > 0 || n_signal > 0) { bool first = true; if (!value) { @@ -4939,10 +4940,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m fputc('=', stdout); } - for (i = 0; i < sz_status; i++) { - if (status[i] < 0 || status[i] > 255) - continue; - + for (i = 0; i < n_status; i++) { if (first) first = false; else @@ -4951,19 +4949,20 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m printf("%"PRIi32, status[i]); } - for (i = 0; i < sz_signal; i++) { + for (i = 0; i < n_signal; i++) { const char *str; str = signal_to_string((int) signal[i]); - if (!str) - continue; if (first) first = false; else fputc(' ', stdout); - fputs(str, stdout); + if (str) + fputs(str, stdout); + else + printf("%"PRIi32, status[i]); } fputc('\n', stdout); 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..3bcebd06a4 --- /dev/null +++ b/src/test/test-exit-status.c @@ -0,0 +1,41 @@ +/* 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 ? ")" : ""); + + if (s) + assert_se(exit_status_from_string(s) == i); + } +} + +static void test_exit_status_from_string(void) { + log_info("/* %s */", __func__); + + assert_se(exit_status_from_string("11") == 11); + assert_se(exit_status_from_string("-1") == -ERANGE); + assert_se(exit_status_from_string("256") == -ERANGE); + assert_se(exit_status_from_string("foo") == -EINVAL); + assert_se(exit_status_from_string("SUCCESS") == 0); + assert_se(exit_status_from_string("FAILURE") == 1); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_exit_status_to_string(); + test_exit_status_from_string(); + + return 0; +} diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index e17140ea0c..5f5245e48a 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -39,6 +39,7 @@ #include "plymouth-util.h" #include "pretty-print.h" #include "process-util.h" +#include "set.h" #include "signal-util.h" #include "socket-util.h" #include "string-util.h" |