core: set $SERVICE_RESULT, $EXIT_CODE and $EXIT_STATUS in ExecStop=/ExecStopPost...
authorLennart Poettering <lennart@poettering.net>
Wed, 27 Jul 2016 09:51:11 +0000 (11:51 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 4 Aug 2016 21:08:05 +0000 (23:08 +0200)
This should simplify monitoring tools for services, by passing the most basic
information about service result/exit information via environment variables,
thus making it unnecessary to retrieve them explicitly via the bus.

man/systemd.exec.xml
man/systemd.service.xml
src/core/execute.h
src/core/service.c

index 58ba582..0fc658f 100644 (file)
         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
index 875d368..e82edbe 100644 (file)
         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>
index 8d659ca..2b4238e 100644 (file)
@@ -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 {
index 32b8e7d..0cbea52 100644 (file)
@@ -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;