summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2019-07-29 18:58:06 +0200
committerGitHub <noreply@github.com>2019-07-29 18:58:06 +0200
commit1d7458fbb1b09ebbfdcf775909e09c56d6a6023e (patch)
treeed02e8a66927287165a16931a5deb410924f705e /src
parentpid1: use LOG_DEBUG/INFO/NOTICE for unit resource consumption message (diff)
parentNEWS: add entry about exit status changes (diff)
downloadsystemd-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.c45
-rw-r--r--src/core/dbus-service.c48
-rw-r--r--src/core/execute.c10
-rw-r--r--src/core/load-fragment.c34
-rw-r--r--src/core/main.c19
-rw-r--r--src/core/unit.c6
-rw-r--r--src/shared/bitmap.c17
-rw-r--r--src/shared/bitmap.h14
-rw-r--r--src/shared/bus-unit-util.c40
-rw-r--r--src/shared/bus-util.c8
-rw-r--r--src/shared/bus-util.h7
-rw-r--r--src/shared/exit-status.c341
-rw-r--r--src/shared/exit-status.h44
-rw-r--r--src/systemctl/systemctl.c31
-rw-r--r--src/test/meson.build4
-rw-r--r--src/test/test-exit-status.c41
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c1
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"