Merge pull request #13207 from keszybz/symbolic-exit-code-names

Symbolic exit code names
This commit is contained in:
Lennart Poettering 2019-07-29 18:58:06 +02:00 committed by GitHub
commit 1d7458fbb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 414 additions and 394 deletions

5
NEWS
View File

@ -105,6 +105,11 @@ CHANGES WITH 243 in spe:
long number (with the length varying by architecture), so they can be long number (with the length varying by architecture), so they can be
unambiguously distinguished. unambiguously distinguished.
* SuccessExitStatus=, RestartPreventExitStatus=, and
RestartForceExitStatus= now accept exit code names (e.g. "DATAERR" is
equivalent to "65"). systemd-analyze learnt a new 'exit-codes' verb
to display those exit code name mappings.
* /usr/sbin/halt.local is no longer supported. Implementation in * /usr/sbin/halt.local is no longer supported. Implementation in
distributions was inconsistent and it seems this functionality was distributions was inconsistent and it seems this functionality was
very rarely used. very rarely used.

3
TODO
View File

@ -235,9 +235,6 @@ Features:
* add --vacuum-xyz options to coredumpctl, matching those journalctl already has. * add --vacuum-xyz options to coredumpctl, matching those journalctl already has.
* SuccessExitStatus= and friends should probably also accept symbolic exit
codes names, i.e. error codes from the list maintained in exit-codes.[ch]
* introduce Ephemeral= unit file switch, that creates an ephemeral copy of all * introduce Ephemeral= unit file switch, that creates an ephemeral copy of all
files and directories that are left writable for a unit, and which are files and directories that are left writable for a unit, and which are
removed after the unit goes down again. A bit like --ephemeral for removed after the unit goes down again. A bit like --ephemeral for

View File

@ -83,6 +83,12 @@
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">unit-paths</arg> <arg choice="plain">unit-paths</arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">exit-codes</arg>
<arg choice="opt" rep="repeat"><replaceable>CODE</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis> <cmdsynopsis>
<command>systemd-analyze</command> <command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">OPTIONS</arg>
@ -365,6 +371,30 @@ $ eog targets.svg</programlisting>
to retrieve the actual list that the manager uses, with any empty directories omitted.</para> to retrieve the actual list that the manager uses, with any empty directories omitted.</para>
</refsect2> </refsect2>
<refsect2>
<title><command>systemd-analyze exit-codes <optional><replaceable>CODE</replaceable>...</optional></command></title>
<para>This command prints a list of exit codes along with their "class", i.e. the source of the
definition (one of <literal>glibc</literal>, <literal>systemd</literal>, <literal>LSB</literal>, or
<literal>BSD</literal>), see the Process Exit Codes section in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
If no additional arguments are specified, all known codes are are shown. Otherwise, only the
definitions for the specified codes are shown.</para>
<example>
<title><command>Show some example exit code names</command></title>
<programlisting>$ systemd-analyze exit-codes 0 1 {63..65}
NAME CODE CLASS
SUCCESS 0 glibc
FAILURE 1 glibc
- 63 -
USAGE 64 BSD
DATAERR 65 BSD
</programlisting>
</example>
</refsect2>
<refsect2> <refsect2>
<title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title> <title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title>

View File

@ -852,27 +852,35 @@
<varlistentry> <varlistentry>
<term><varname>SuccessExitStatus=</varname></term> <term><varname>SuccessExitStatus=</varname></term>
<listitem><para>Takes a list of exit status definitions that, <listitem><para>Takes a list of exit status definitions that, when returned by the main service
when returned by the main service process, will be considered process, will be considered successful termination, in addition to the normal successful exit code 0
successful termination, in addition to the normal successful and the signals <constant>SIGHUP</constant>, <constant>SIGINT</constant>,
exit code 0 and the signals <constant>SIGHUP</constant>, <constant>SIGTERM</constant>, and <constant>SIGPIPE</constant>. Exit status definitions can be
<constant>SIGINT</constant>, <constant>SIGTERM</constant>, and numeric exit codes, termination code names, or termination signal names, separated by spaces. See the
<constant>SIGPIPE</constant>. Exit status definitions can Process Exit Codes section in
either be numeric exit codes or termination signal names, <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
separated by spaces. For example: a list of termination codes names (for this setting only the part without the
<literal>EXIT_</literal> or <literal>EX_</literal> prefix should be used). See
<programlisting>SuccessExitStatus=1 2 8 SIGKILL</programlisting> <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
a list of signal names.</para>
ensures that exit codes 1, 2, 8 and
the termination signal <constant>SIGKILL</constant> are
considered clean service terminations.
</para>
<para>This option may appear more than once, in which case the <para>This option may appear more than once, in which case the
list of successful exit statuses is merged. If the empty list of successful exit statuses is merged. If the empty
string is assigned to this option, the list is reset, all string is assigned to this option, the list is reset, all
prior assignments of this option will have no prior assignments of this option will have no
effect.</para></listitem> effect.</para>
<example>
<title>A service with with the the <varname>SuccessExitStatus=</varname> setting</title>
<programlisting>SuccessExitStatus=TEMPFAIL 250 SIGUSR1</programlisting>
<para>Exit codes 75 (<constant>TEMPFAIL</constant>), 250, and the termination signal
<constant>SIGKILL</constant> are considered clean service terminations.</para>
</example>
<para>Note: <command>systemd-analyze exit-codes</command> may be used to list exit
codes and translate between numerical code values and names.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -24,6 +24,7 @@
#include "conf-files.h" #include "conf-files.h"
#include "copy.h" #include "copy.h"
#include "def.h" #include "def.h"
#include "exit-status.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "format-table.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()); 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) { static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
bool first = true; bool first = true;
@ -2170,6 +2213,7 @@ static int help(int argc, char *argv[], void *userdata) {
" dump Output state serialization of service manager\n" " dump Output state serialization of service manager\n"
" cat-config Show configuration file and drop-ins\n" " cat-config Show configuration file and drop-ins\n"
" unit-paths List load directories for units\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" " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
" condition CONDITION... Evaluate conditions and asserts\n" " condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\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 }, { "dump", VERB_ANY, 1, 0, dump },
{ "cat-config", 2, VERB_ANY, 0, cat_config }, { "cat-config", 2, VERB_ANY, 0, cat_config },
{ "unit-paths", 1, 1, 0, dump_unit_paths }, { "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 }, { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
{ "condition", 2, VERB_ANY, 0, do_condition }, { "condition", 2, VERB_ANY, 0, do_condition },
{ "verify", 2, VERB_ANY, 0, do_verify }, { "verify", 2, VERB_ANY, 0, do_verify },

View File

@ -39,9 +39,9 @@ static int property_get_exit_status_set(
void *userdata, void *userdata,
sd_bus_error *error) { sd_bus_error *error) {
ExitStatusSet *status_set = userdata; const ExitStatusSet *status_set = userdata;
unsigned n;
Iterator i; Iterator i;
void *id;
int r; int r;
assert(bus); assert(bus);
@ -56,13 +56,10 @@ static int property_get_exit_status_set(
if (r < 0) if (r < 0)
return r; return r;
SET_FOREACH(id, status_set->status, i) { BITMAP_FOREACH(n, &status_set->status, i) {
int32_t val = PTR_TO_INT(id); assert(n < 256);
if (val < 0 || val > 255) r = sd_bus_message_append_basic(reply, 'i', &n);
continue;
r = sd_bus_message_append_basic(reply, 'i', &val);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -75,15 +72,14 @@ static int property_get_exit_status_set(
if (r < 0) if (r < 0)
return r; return r;
SET_FOREACH(id, status_set->signal, i) { BITMAP_FOREACH(n, &status_set->signal, i) {
int32_t val = PTR_TO_INT(id);
const char *str; const char *str;
str = signal_to_string((int) val); str = signal_to_string(n);
if (!str) if (!str)
continue; continue;
r = sd_bus_message_append_basic(reply, 'i', &val); r = sd_bus_message_append_basic(reply, 'i', &n);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -163,18 +159,18 @@ static int bus_set_transient_exit_status(
sd_bus_error *error) { sd_bus_error *error) {
const int32_t *status, *signal; const int32_t *status, *signal;
size_t sz_status, sz_signal, i; size_t n_status, n_signal, i;
int r; int r;
r = sd_bus_message_enter_container(message, 'r', "aiai"); r = sd_bus_message_enter_container(message, 'r', "aiai");
if (r < 0) if (r < 0)
return r; 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) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
@ -182,25 +178,21 @@ static int bus_set_transient_exit_status(
if (r < 0) if (r < 0)
return r; return r;
sz_status /= sizeof(int32_t); n_status /= sizeof(int32_t);
sz_signal /= 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); exit_status_set_free(status_set);
unit_write_settingf(u, flags, name, "%s=", name); unit_write_settingf(u, flags, name, "%s=", name);
return 1; return 1;
} }
for (i = 0; i < sz_status; i++) { for (i = 0; i < n_status; i++) {
if (status[i] < 0 || status[i] > 255) 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]); 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)) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = set_ensure_allocated(&status_set->status, NULL); r = bitmap_set(&status_set->status, status[i]);
if (r < 0)
return r;
r = set_put(status_set->status, INT_TO_PTR((int) status[i]));
if (r < 0) if (r < 0)
return r; 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; const char *str;
str = signal_to_string((int) signal[i]); 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]); 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)) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = set_ensure_allocated(&status_set->signal, NULL); r = bitmap_set(&status_set->signal, signal[i]);
if (r < 0)
return r;
r = set_put(status_set->signal, INT_TO_PTR((int) signal[i]));
if (r < 0) if (r < 0)
return r; return r;

View File

@ -3878,15 +3878,19 @@ int exec_spawn(Unit *unit,
unit->manager->user_lookup_fds[1], unit->manager->user_lookup_fds[1],
&exit_status); &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, log_struct_errno(LOG_ERR, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit), LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit), LOG_UNIT_INVOCATION_ID(unit),
LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD), status, command->path),
command->path),
"EXECUTABLE=%s", command->path); "EXECUTABLE=%s", command->path);
}
_exit(exit_status); _exit(exit_status);
} }

View File

@ -3936,37 +3936,33 @@ int config_parse_set_status(
FOREACH_WORD(word, l, rvalue, state) { FOREACH_WORD(word, l, rvalue, state) {
_cleanup_free_ char *temp; _cleanup_free_ char *temp;
int val; Bitmap *bitmap;
Set **set;
temp = strndup(word, l); temp = strndup(word, l);
if (!temp) if (!temp)
return log_oom(); return log_oom();
r = safe_atoi(temp, &val); /* We need to call exit_status_from_string() first, because we want
if (r < 0) { * to parse numbers as exit statuses, not signals. */
val = signal_from_string(temp);
if (val <= 0) { r = exit_status_from_string(temp);
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word); if (r >= 0) {
continue; assert(r >= 0 && r < 256);
} bitmap = &status_set->status;
set = &status_set->signal;
} else { } else {
if (val < 0 || val > 255) { r = signal_from_string(temp);
log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
if (r <= 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Failed to parse value, ignoring: %s", word);
continue; continue;
} }
set = &status_set->status; bitmap = &status_set->signal;
} }
r = set_ensure_allocated(set, NULL); r = bitmap_set(bitmap, r);
if (r < 0) if (r < 0)
return log_oom(); return log_error_errno(r, "Failed to set signal or status %s: %m", word);
r = set_put(*set, INT_TO_PTR(val));
if (r < 0)
return log_oom();
} }
if (!isempty(state)) if (!isempty(state))
log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");

View File

@ -221,16 +221,19 @@ _noreturn_ static void crash(int sig) {
r = wait_for_terminate(pid, &status); r = wait_for_terminate(pid, &status);
if (r < 0) if (r < 0)
log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); 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).", log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).",
signal_to_string(sig), signal_to_string(sig),
pid, sigchld_code_to_string(status.si_code), pid,
status.si_status, sigchld_code_to_string(status.si_code),
strna(status.si_code == CLD_EXITED status.si_status, strna(s));
? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL) } else
: signal_to_string(status.si_status))); log_emergency("Caught <%s>, dumped core as pid "PID_FMT".",
else signal_to_string(sig), pid);
log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
} }
} }

View File

@ -5797,9 +5797,11 @@ int unit_test_trigger_loaded(Unit *u) {
trigger = UNIT_TRIGGER(u); trigger = UNIT_TRIGGER(u);
if (!trigger) 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) 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; return 0;
} }

View File

@ -12,12 +12,6 @@
#include "macro.h" #include "macro.h"
#include "memory-util.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 /* Bitmaps are only meant to store relatively small numbers
* (corresponding to, say, an enum), so it is ok to limit * (corresponding to, say, an enum), so it is ok to limit
* the max entry. 64k should be plenty. */ * the max entry. 64k should be plenty. */
@ -117,7 +111,7 @@ void bitmap_unset(Bitmap *b, unsigned n) {
b->bitmaps[offset] &= ~bitmask; b->bitmaps[offset] &= ~bitmask;
} }
bool bitmap_isset(Bitmap *b, unsigned n) { bool bitmap_isset(const Bitmap *b, unsigned n) {
uint64_t bitmask; uint64_t bitmask;
unsigned offset; unsigned offset;
@ -134,7 +128,7 @@ bool bitmap_isset(Bitmap *b, unsigned n) {
return !!(b->bitmaps[offset] & bitmask); return !!(b->bitmaps[offset] & bitmask);
} }
bool bitmap_isclear(Bitmap *b) { bool bitmap_isclear(const Bitmap *b) {
unsigned i; unsigned i;
if (!b) if (!b)
@ -148,7 +142,6 @@ bool bitmap_isclear(Bitmap *b) {
} }
void bitmap_clear(Bitmap *b) { void bitmap_clear(Bitmap *b) {
if (!b) if (!b)
return; return;
@ -157,7 +150,7 @@ void bitmap_clear(Bitmap *b) {
b->bitmaps_allocated = 0; 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; uint64_t bitmask;
unsigned offset, rem; unsigned offset, rem;
@ -192,9 +185,9 @@ bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) {
return false; return false;
} }
bool bitmap_equal(Bitmap *a, Bitmap *b) { bool bitmap_equal(const Bitmap *a, const Bitmap *b) {
size_t common_n_bitmaps; size_t common_n_bitmaps;
Bitmap *c; const Bitmap *c;
unsigned i; unsigned i;
if (a == b) if (a == b)

View File

@ -6,7 +6,11 @@
#include "hashmap.h" #include "hashmap.h"
#include "macro.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_new(void);
Bitmap *bitmap_copy(Bitmap *b); Bitmap *bitmap_copy(Bitmap *b);
@ -15,13 +19,13 @@ void bitmap_free(Bitmap *b);
int bitmap_set(Bitmap *b, unsigned n); int bitmap_set(Bitmap *b, unsigned n);
void bitmap_unset(Bitmap *b, unsigned n); void bitmap_unset(Bitmap *b, unsigned n);
bool bitmap_isset(Bitmap *b, unsigned n); bool bitmap_isset(const Bitmap *b, unsigned n);
bool bitmap_isclear(Bitmap *b); bool bitmap_isclear(const Bitmap *b);
void bitmap_clear(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) \ #define BITMAP_FOREACH(n, b, i) \
for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); ) for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); )

View File

@ -10,6 +10,7 @@
#include "cpu-set-util.h" #include "cpu-set-util.h"
#include "escape.h" #include "escape.h"
#include "exec-util.h" #include "exec-util.h"
#include "exit-status.h"
#include "hexdecoct.h" #include "hexdecoct.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "in-addr-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")) { if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
_cleanup_free_ int *status = NULL, *signal = NULL; _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; const char *p;
for (p = eq;;) { for (p = eq;;) {
_cleanup_free_ char *word = NULL; _cleanup_free_ char *word = NULL;
int val;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0) if (r == 0)
@ -1454,24 +1454,30 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
if (r < 0) if (r < 0)
return log_error_errno(r, "Invalid syntax in %s: %s", field, eq); return log_error_errno(r, "Invalid syntax in %s: %s", field, eq);
r = safe_atoi(word, &val); /* We need to call exit_status_from_string() first, because we want
if (r < 0) { * to parse numbers as exit statuses, not signals. */
val = signal_from_string(word);
if (val < 0)
return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field);
signal = reallocarray(signal, sz_signal + 1, sizeof(int)); r = exit_status_from_string(word);
if (!signal) if (r >= 0) {
return log_oom(); assert(r >= 0 && r < 256);
signal[sz_signal++] = val; status = reallocarray(status, n_status + 1, sizeof(int));
} else {
status = reallocarray(status, sz_status + 1, sizeof(int));
if (!status) if (!status)
return log_oom(); 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"); 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) if (r < 0)
return bus_log_create_error(r); 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) if (r < 0)
return bus_log_create_error(r); 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) if (r < 0)
return bus_log_create_error(r); return bus_log_create_error(r);

View File

@ -1480,14 +1480,6 @@ int bus_property_get_ulong(
} }
#endif #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 * bus_path_encode_unique() - encode unique object path
* @b: bus connection or NULL * @b: bus connection or NULL

View File

@ -114,8 +114,11 @@ assert_cc(sizeof(pid_t) == sizeof(uint32_t));
assert_cc(sizeof(mode_t) == sizeof(uint32_t)); assert_cc(sizeof(mode_t) == sizeof(uint32_t));
#define bus_property_get_mode ((sd_bus_property_get_t) NULL) #define bus_property_get_mode ((sd_bus_property_get_t) NULL)
int bus_log_parse_error(int r); #define bus_log_parse_error(r) \
int bus_log_create_error(int 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) \ #define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val) \
int function(sd_bus *bus, \ int function(sd_bus *bus, \

View File

@ -6,10 +6,11 @@
#include "exit-status.h" #include "exit-status.h"
#include "macro.h" #include "macro.h"
#include "parse-util.h"
#include "set.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: /* Exit status ranges:
* *
* 01 ISO C, EXIT_SUCCESS + EXIT_FAILURE * 01 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.) * 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 },
case EXIT_FAILURE: [EXIT_FDS] = { "FDS", EXIT_STATUS_SYSTEMD },
return "FAILURE"; [EXIT_EXEC] = { "EXEC", EXIT_STATUS_SYSTEMD },
} [EXIT_MEMORY] = { "MEMORY", EXIT_STATUS_SYSTEMD },
[EXIT_LIMITS] = { "LIMITS", EXIT_STATUS_SYSTEMD },
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB, EXIT_STATUS_FULL)) { [EXIT_OOM_ADJUST] = { "OOM_ADJUST", EXIT_STATUS_SYSTEMD },
switch (status) { /* Optionally we cover our own ones */ [EXIT_SIGNAL_MASK] = { "SIGNAL_MASK", EXIT_STATUS_SYSTEMD },
[EXIT_STDIN] = { "STDIN", EXIT_STATUS_SYSTEMD },
case EXIT_CHDIR: [EXIT_STDOUT] = { "STDOUT", EXIT_STATUS_SYSTEMD },
return "CHDIR"; [EXIT_CHROOT] = { "CHROOT", EXIT_STATUS_SYSTEMD },
[EXIT_IOPRIO] = { "IOPRIO", EXIT_STATUS_SYSTEMD },
case EXIT_NICE: [EXIT_TIMERSLACK] = { "TIMERSLACK", EXIT_STATUS_SYSTEMD },
return "NICE"; [EXIT_SECUREBITS] = { "SECUREBITS", EXIT_STATUS_SYSTEMD },
[EXIT_SETSCHEDULER] = { "SETSCHEDULER", EXIT_STATUS_SYSTEMD },
case EXIT_FDS: [EXIT_CPUAFFINITY] = { "CPUAFFINITY", EXIT_STATUS_SYSTEMD },
return "FDS"; [EXIT_GROUP] = { "GROUP", EXIT_STATUS_SYSTEMD },
[EXIT_USER] = { "USER", EXIT_STATUS_SYSTEMD },
case EXIT_EXEC: [EXIT_CAPABILITIES] = { "CAPABILITIES", EXIT_STATUS_SYSTEMD },
return "EXEC"; [EXIT_CGROUP] = { "CGROUP", EXIT_STATUS_SYSTEMD },
[EXIT_SETSID] = { "SETSID", EXIT_STATUS_SYSTEMD },
case EXIT_MEMORY: [EXIT_CONFIRM] = { "CONFIRM", EXIT_STATUS_SYSTEMD },
return "MEMORY"; [EXIT_STDERR] = { "STDERR", EXIT_STATUS_SYSTEMD },
[EXIT_PAM] = { "PAM", EXIT_STATUS_SYSTEMD },
case EXIT_LIMITS: [EXIT_NETWORK] = { "NETWORK", EXIT_STATUS_SYSTEMD },
return "LIMITS"; [EXIT_NAMESPACE] = { "NAMESPACE", EXIT_STATUS_SYSTEMD },
[EXIT_NO_NEW_PRIVILEGES] = { "NO_NEW_PRIVILEGES", EXIT_STATUS_SYSTEMD },
case EXIT_OOM_ADJUST: [EXIT_SECCOMP] = { "SECCOMP", EXIT_STATUS_SYSTEMD },
return "OOM_ADJUST"; [EXIT_SELINUX_CONTEXT] = { "SELINUX_CONTEXT", EXIT_STATUS_SYSTEMD },
[EXIT_PERSONALITY] = { "PERSONALITY", EXIT_STATUS_SYSTEMD },
case EXIT_SIGNAL_MASK: [EXIT_APPARMOR_PROFILE] = { "APPARMOR", EXIT_STATUS_SYSTEMD },
return "SIGNAL_MASK"; [EXIT_ADDRESS_FAMILIES] = { "ADDRESS_FAMILIES", EXIT_STATUS_SYSTEMD },
[EXIT_RUNTIME_DIRECTORY] = { "RUNTIME_DIRECTORY", EXIT_STATUS_SYSTEMD },
case EXIT_STDIN: [EXIT_CHOWN] = { "CHOWN", EXIT_STATUS_SYSTEMD },
return "STDIN"; [EXIT_SMACK_PROCESS_LABEL] = { "SMACK_PROCESS_LABEL", EXIT_STATUS_SYSTEMD },
[EXIT_KEYRING] = { "KEYRING", EXIT_STATUS_SYSTEMD },
case EXIT_STDOUT: [EXIT_STATE_DIRECTORY] = { "STATE_DIRECTORY", EXIT_STATUS_SYSTEMD },
return "STDOUT"; [EXIT_CACHE_DIRECTORY] = { "CACHE_DIRECTORY", EXIT_STATUS_SYSTEMD },
[EXIT_LOGS_DIRECTORY] = { "LOGS_DIRECTORY", EXIT_STATUS_SYSTEMD },
case EXIT_CHROOT: [EXIT_CONFIGURATION_DIRECTORY] = { "CONFIGURATION_DIRECTORY", EXIT_STATUS_SYSTEMD },
return "CHROOT"; [EXIT_NUMA_POLICY] = { "NUMA_POLICY", EXIT_STATUS_SYSTEMD },
[EXIT_EXCEPTION] = { "EXCEPTION", EXIT_STATUS_SYSTEMD },
case EXIT_IOPRIO:
return "IOPRIO"; [EXIT_INVALIDARGUMENT] = { "INVALIDARGUMENT", EXIT_STATUS_LSB },
[EXIT_NOTIMPLEMENTED] = { "NOTIMPLEMENTED", EXIT_STATUS_LSB },
case EXIT_TIMERSLACK: [EXIT_NOPERMISSION] = { "NOPERMISSION", EXIT_STATUS_LSB },
return "TIMERSLACK"; [EXIT_NOTINSTALLED] = { "NOTINSTALLED", EXIT_STATUS_LSB },
[EXIT_NOTCONFIGURED] = { "NOTCONFIGURED", EXIT_STATUS_LSB },
case EXIT_SECUREBITS: [EXIT_NOTRUNNING] = { "NOTRUNNING", EXIT_STATUS_LSB },
return "SECUREBITS";
[EX_USAGE] = { "USAGE", EXIT_STATUS_BSD },
case EXIT_SETSCHEDULER: [EX_DATAERR] = { "DATAERR", EXIT_STATUS_BSD },
return "SETSCHEDULER"; [EX_NOINPUT] = { "NOINPUT", EXIT_STATUS_BSD },
[EX_NOUSER] = { "NOUSER", EXIT_STATUS_BSD },
case EXIT_CPUAFFINITY: [EX_NOHOST] = { "NOHOST", EXIT_STATUS_BSD },
return "CPUAFFINITY"; [EX_UNAVAILABLE] = { "UNAVAILABLE", EXIT_STATUS_BSD },
[EX_SOFTWARE] = { "SOFTWARE", EXIT_STATUS_BSD },
case EXIT_GROUP: [EX_OSERR] = { "OSERR", EXIT_STATUS_BSD },
return "GROUP"; [EX_OSFILE] = { "OSFILE", EXIT_STATUS_BSD },
[EX_CANTCREAT] = { "CANTCREAT", EXIT_STATUS_BSD },
case EXIT_USER: [EX_IOERR] = { "IOERR", EXIT_STATUS_BSD },
return "USER"; [EX_TEMPFAIL] = { "TEMPFAIL", EXIT_STATUS_BSD },
[EX_PROTOCOL] = { "PROTOCOL", EXIT_STATUS_BSD },
case EXIT_CAPABILITIES: [EX_NOPERM] = { "NOPERM", EXIT_STATUS_BSD },
return "CAPABILITIES"; [EX_CONFIG] = { "CONFIG", EXIT_STATUS_BSD },
};
case EXIT_CGROUP:
return "CGROUP"; const char* exit_status_to_string(int code, ExitStatusClass class) {
if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
case EXIT_SETSID: return NULL;
return "SETSID"; return FLAGS_SET(exit_status_mappings[code].class, class) ? exit_status_mappings[code].name : NULL;
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) { 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;
}
}
int exit_status_from_string(const char *s) {
uint8_t val;
int r;
for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++)
if (streq_ptr(s, exit_status_mappings[i].name))
return i;
r = safe_atou8(s, &val);
if (r < 0)
return r;
return val;
}
bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status) {
if (code == CLD_EXITED) if (code == CLD_EXITED)
return status == 0 || return status == 0 ||
(success_status && (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) if (code == CLD_KILLED)
return return
(clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) || (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
(success_status && (success_status &&
set_contains(success_status->signal, INT_TO_PTR(status))); bitmap_isset(&success_status->signal, status));
return false; 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) { void exit_status_set_free(ExitStatusSet *x) {
assert(x); assert(x);
x->status = set_free(x->status); bitmap_clear(&x->status);
x->signal = set_free(x->signal); bitmap_clear(&x->signal);
} }
bool exit_status_set_is_empty(ExitStatusSet *x) { bool exit_status_set_is_empty(const ExitStatusSet *x) {
if (!x) if (!x)
return true; 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) { bool exit_status_set_test(const ExitStatusSet *x, int code, int status) {
if (code == CLD_EXITED && bitmap_isset(&x->status, status))
if (exit_status_set_is_empty(x))
return false;
if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status)))
return true; 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 true;
return false; return false;

View File

@ -3,12 +3,12 @@
#include <stdbool.h> #include <stdbool.h>
#include "bitmap.h"
#include "hashmap.h" #include "hashmap.h"
#include "macro.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 /* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with
* 'status' verb exit codes which are defined very differently. For details see: * 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 * 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 */ /* 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 /* The LSB suggests that error codes >= 200 are "reserved". We use them here under the assumption
* hence are unused by init scripts. */ * that they hence are unused by init scripts. */
EXIT_CHDIR = 200, EXIT_CHDIR = 200,
EXIT_NICE, EXIT_NICE,
EXIT_FDS, EXIT_FDS,
@ -74,27 +74,37 @@ enum {
EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */ EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
}; };
typedef enum ExitStatusLevel { typedef enum ExitStatusClass {
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */ EXIT_STATUS_GLIBC = 1 << 0, /* libc EXIT_STATUS/EXIT_FAILURE */
EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */ EXIT_STATUS_SYSTEMD = 1 << 1, /* systemd's own exit codes */
EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */ EXIT_STATUS_LSB = 1 << 2, /* LSB exit codes */
EXIT_STATUS_FULL, /* cover libc, systemd's own, LSB and BSD (EX_xyz) exit codes */ EXIT_STATUS_BSD = 1 << 3, /* BSD (EX_xyz) exit codes */
} ExitStatusLevel; EXIT_STATUS_FULL = EXIT_STATUS_GLIBC | EXIT_STATUS_SYSTEMD | EXIT_STATUS_LSB | EXIT_STATUS_BSD,
} ExitStatusClass;
typedef struct ExitStatusSet { typedef struct ExitStatusSet {
Set *status; Bitmap status;
Set *signal; Bitmap signal;
} ExitStatusSet; } 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 { typedef enum ExitClean {
EXIT_CLEAN_DAEMON, EXIT_CLEAN_DAEMON,
EXIT_CLEAN_COMMAND, EXIT_CLEAN_COMMAND,
} ExitClean; } 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); void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x); bool exit_status_set_is_empty(const ExitStatusSet *x);
bool exit_status_set_test(ExitStatusSet *x, int code, int status); bool exit_status_set_test(const ExitStatusSet *x, int code, int status);

View File

@ -4380,7 +4380,7 @@ static void print_status_info(
printf("status=%i", p->status); 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) if (c)
printf("/%s", c); printf("/%s", c);
@ -4421,7 +4421,8 @@ static void print_status_info(
printf("status=%i", i->exit_status); 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) if (c)
printf("/%s", 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")) { } else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) {
const int32_t *status, *signal; 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"); r = sd_bus_message_enter_container(m, 'r', "aiai");
if (r < 0) if (r < 0)
return bus_log_parse_error(r); 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) if (r < 0)
return bus_log_parse_error(r); 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) if (r < 0)
return bus_log_parse_error(r); 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) if (r < 0)
return bus_log_parse_error(r); return bus_log_parse_error(r);
sz_status /= sizeof(int32_t); n_status /= sizeof(int32_t);
sz_signal /= 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; bool first = true;
if (!value) { if (!value) {
@ -4939,10 +4940,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
fputc('=', stdout); fputc('=', stdout);
} }
for (i = 0; i < sz_status; i++) { for (i = 0; i < n_status; i++) {
if (status[i] < 0 || status[i] > 255)
continue;
if (first) if (first)
first = false; first = false;
else else
@ -4951,19 +4949,20 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
printf("%"PRIi32, status[i]); printf("%"PRIi32, status[i]);
} }
for (i = 0; i < sz_signal; i++) { for (i = 0; i < n_signal; i++) {
const char *str; const char *str;
str = signal_to_string((int) signal[i]); str = signal_to_string((int) signal[i]);
if (!str)
continue;
if (first) if (first)
first = false; first = false;
else else
fputc(' ', stdout); fputc(' ', stdout);
fputs(str, stdout); if (str)
fputs(str, stdout);
else
printf("%"PRIi32, status[i]);
} }
fputc('\n', stdout); fputc('\n', stdout);

View File

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

View File

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

View File

@ -39,6 +39,7 @@
#include "plymouth-util.h" #include "plymouth-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "process-util.h" #include "process-util.h"
#include "set.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-util.h" #include "string-util.h"

View File

@ -18,5 +18,5 @@ Before=shutdown.target
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=@rootbindir@/systemd-tmpfiles --clean ExecStart=@rootbindir@/systemd-tmpfiles --clean
SuccessExitStatus=65 SuccessExitStatus=DATAERR
IOSchedulingClass=idle IOSchedulingClass=idle

View File

@ -19,4 +19,4 @@ Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot
SuccessExitStatus=65 73 SuccessExitStatus=DATAERR CANTCREAT

View File

@ -20,4 +20,4 @@ RefuseManualStop=yes
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
SuccessExitStatus=65 73 SuccessExitStatus=DATAERR CANTCREAT

View File

@ -17,5 +17,5 @@ Before=basic.target shutdown.target
[Service] [Service]
Type=oneshot Type=oneshot
ExecStart=@rootbindir@/systemd-tmpfiles --user --clean ExecStart=@rootbindir@/systemd-tmpfiles --user --clean
SuccessExitStatus=65 SuccessExitStatus=DATAERR
IOSchedulingClass=idle IOSchedulingClass=idle

View File

@ -19,7 +19,7 @@ RefuseManualStop=yes
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot
SuccessExitStatus=65 SuccessExitStatus=DATAERR
[Install] [Install]
WantedBy=basic.target WantedBy=basic.target