diff options
-rw-r--r-- | man/systemd.exec.xml | 37 | ||||
-rw-r--r-- | man/systemd.service.xml | 8 | ||||
-rw-r--r-- | src/core/execute.h | 1 | ||||
-rw-r--r-- | src/core/service.c | 27 |
4 files changed, 68 insertions, 5 deletions
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 58ba582911..0fc658f180 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1602,6 +1602,43 @@ functions) if their standard output or standard error output is connected to the journal anyway, thus enabling delivery of structured metadata along with logged messages.</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>$SERVICE_RESULT</varname></term> + + <listitem><para>Only defined for the service unit type, this environment variable is passed to all + <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service + "result". Currently, the following values are defined: <literal>timeout</literal> (in case of an operation + timeout), <literal>exit-code</literal> (if a service process exited with a non-zero exit code; see + <varname>$EXIT_STATUS</varname> below for the actual exit status returned), <literal>signal</literal> (if a + service process was terminated abnormally by a signal; see <varname>$EXIT_STATUS</varname> below for the actual + signal used for the termination), <literal>core-dump</literal> (if a service process terminated abnormally and + dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the service but it + missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system operation + failed).</para> + + <para>This environment variable is useful to monitor failure or successful termination of a service. Even + though this variable is available in both <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname>, it + is usually a better choice to place monitoring tools in the latter, as the former is only invoked for services + that managed to start up correctly, and the latter covers both services that failed during their start-up and + those which failed during their runtime.</para></listitem> + </varlistentry> + + <varlistentry> + <term><varname>$EXIT_CODE</varname></term> + <term><varname>$EXIT_STATUS</varname></term> + + <listitem><para>Only defined for the service unit type, these environment variables are passed to all + <varname>ExecStop=</varname>, <varname>ExecStopPost=</varname> processes and contain exit status/code + information of the main process of the service. For the precise definition of the exit code and status, see + <citerefentry><refentrytitle>wait</refentrytitle><manvolnum>2</manvolnum></citerefentry>. <varname>$EXIT_CODE</varname> + is one of <literal>exited</literal>, <literal>killed</literal>, + <literal>dumped</literal>. <varname>$EXIT_STATUS</varname> contains the numeric exit code formatted as string + if <varname>$EXIT_CODE</varname> is <literal>exited</literal>, and the signal name in all other cases. Note + that these environment variables are only set if the service manager succeeded to start and identify the main + process of the service.</para></listitem> + </varlistentry> + </variablelist> <para>Additional variables may be configured by the following diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 875d368fcf..e82edbe93e 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -429,7 +429,13 @@ service failed to start up correctly. Commands configured with this setting need to be able to operate even if the service failed starting up half-way and left incompletely initialized data around. As the service's processes have been terminated already when the commands specified with this setting are executed they should - not attempt to communicate with them.</para></listitem> + not attempt to communicate with them.</para> + + <para>Note that all commands that are configured with this setting are invoked with the result code of the + service, as well as the main process' exit code and status, set in the <varname>$SERVICE_RESULT</varname>, + <varname>$EXIT_CODE</varname> and <varname>$EXIT_STATUS</varname> environment variables, see + <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for + details.</para></listitem> </varlistentry> <varlistentry> diff --git a/src/core/execute.h b/src/core/execute.h index 8d659ca178..2b4238ed7e 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -217,6 +217,7 @@ typedef enum ExecFlags { /* The following are not used by execute.c, but by consumers internally */ EXEC_PASS_FDS = 1U << 4, EXEC_IS_CONTROL = 1U << 5, + EXEC_SETENV_RESULT = 1U << 6, } ExecFlags; struct ExecParameters { diff --git a/src/core/service.c b/src/core/service.c index 32b8e7d2c5..0cbea52276 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1216,7 +1216,7 @@ static int service_spawn( if (r < 0) return r; - our_env = new0(char*, 6); + our_env = new0(char*, 9); if (!our_env) return -ENOMEM; @@ -1264,6 +1264,24 @@ static int service_spawn( } } + if (flags & EXEC_SETENV_RESULT) { + if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0) + return -ENOMEM; + + if (s->main_exec_status.pid > 0 && + dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { + if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0) + return -ENOMEM; + + if (s->main_exec_status.code == CLD_EXITED) + r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status); + else + r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status)); + if (r < 0) + return -ENOMEM; + } + } + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); if (!final_env) return -ENOMEM; @@ -1467,7 +1485,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1578,7 +1596,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1898,7 +1916,8 @@ static void service_run_next_control(Service *s) { s->control_command, timeout, EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| - (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0), + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0), &s->control_pid); if (r < 0) goto fail; |