mirror of
https://github.com/systemd/systemd.git
synced 2025-03-28 02:50:16 +03:00
Merge pull request #13207 from keszybz/symbolic-exit-code-names
Symbolic exit code names
This commit is contained in:
commit
1d7458fbb1
5
NEWS
5
NEWS
@ -105,6 +105,11 @@ CHANGES WITH 243 in spe:
|
||||
long number (with the length varying by architecture), so they can be
|
||||
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
|
||||
distributions was inconsistent and it seems this functionality was
|
||||
very rarely used.
|
||||
|
3
TODO
3
TODO
@ -235,9 +235,6 @@ Features:
|
||||
|
||||
* 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
|
||||
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
|
||||
|
@ -83,6 +83,12 @@
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">unit-paths</arg>
|
||||
</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>
|
||||
<command>systemd-analyze</command>
|
||||
<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>
|
||||
</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>
|
||||
<title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title>
|
||||
|
||||
|
@ -852,27 +852,35 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SuccessExitStatus=</varname></term>
|
||||
<listitem><para>Takes a list of exit status definitions that,
|
||||
when returned by the main service process, will be considered
|
||||
successful termination, in addition to the normal successful
|
||||
exit code 0 and the signals <constant>SIGHUP</constant>,
|
||||
<constant>SIGINT</constant>, <constant>SIGTERM</constant>, and
|
||||
<constant>SIGPIPE</constant>. Exit status definitions can
|
||||
either be numeric exit codes or termination signal names,
|
||||
separated by spaces. For example:
|
||||
|
||||
<programlisting>SuccessExitStatus=1 2 8 SIGKILL</programlisting>
|
||||
|
||||
ensures that exit codes 1, 2, 8 and
|
||||
the termination signal <constant>SIGKILL</constant> are
|
||||
considered clean service terminations.
|
||||
</para>
|
||||
<listitem><para>Takes a list of exit status definitions that, when returned by the main service
|
||||
process, will be considered successful termination, in addition to the normal successful exit code 0
|
||||
and the signals <constant>SIGHUP</constant>, <constant>SIGINT</constant>,
|
||||
<constant>SIGTERM</constant>, and <constant>SIGPIPE</constant>. Exit status definitions can be
|
||||
numeric exit codes, termination code names, or termination signal names, separated by spaces. See the
|
||||
Process Exit Codes section in
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
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
|
||||
<citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
|
||||
a list of signal names.</para>
|
||||
|
||||
<para>This option may appear more than once, in which case the
|
||||
list of successful exit statuses is merged. If the empty
|
||||
string is assigned to this option, the list is reset, all
|
||||
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>
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)); )
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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, \
|
||||
|
@ -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";
|
||||
|
||||
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;
|
||||
[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;
|
||||
}
|
||||
|
||||
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)
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -305,6 +305,10 @@ tests += [
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-exit-status.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-specifier.c'],
|
||||
[],
|
||||
[]],
|
||||
|
41
src/test/test-exit-status.c
Normal file
41
src/test/test-exit-status.c
Normal 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;
|
||||
}
|
@ -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"
|
||||
|
@ -18,5 +18,5 @@ Before=shutdown.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@rootbindir@/systemd-tmpfiles --clean
|
||||
SuccessExitStatus=65
|
||||
SuccessExitStatus=DATAERR
|
||||
IOSchedulingClass=idle
|
||||
|
@ -19,4 +19,4 @@ Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot
|
||||
SuccessExitStatus=65 73
|
||||
SuccessExitStatus=DATAERR CANTCREAT
|
||||
|
@ -20,4 +20,4 @@ RefuseManualStop=yes
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
|
||||
SuccessExitStatus=65 73
|
||||
SuccessExitStatus=DATAERR CANTCREAT
|
||||
|
@ -17,5 +17,5 @@ Before=basic.target shutdown.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@rootbindir@/systemd-tmpfiles --user --clean
|
||||
SuccessExitStatus=65
|
||||
SuccessExitStatus=DATAERR
|
||||
IOSchedulingClass=idle
|
||||
|
@ -19,7 +19,7 @@ RefuseManualStop=yes
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot
|
||||
SuccessExitStatus=65
|
||||
SuccessExitStatus=DATAERR
|
||||
|
||||
[Install]
|
||||
WantedBy=basic.target
|
||||
|
Loading…
x
Reference in New Issue
Block a user