]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: split $MONITOR_METADATA and return it only if a single unit triggers OnFailure...
authorLuca Boccassi <bluca@debian.org>
Wed, 9 Feb 2022 11:48:30 +0000 (11:48 +0000)
committerLuca Boccassi <bluca@debian.org>
Thu, 10 Mar 2022 14:43:14 +0000 (14:43 +0000)
Remove the list logic, and simply skip passing metadata if more than one
unit triggered an OnFailure/OnSuccess handler.
Instead of a single env var to loop over, provide each separate item
as its own variable.

Fixes https://github.com/systemd/systemd/issues/22370

man/systemd.exec.xml
src/core/job.c
src/core/job.h
src/core/service.c
src/core/unit-dependency-atom.c
src/core/unit-dependency-atom.h
src/core/unit.c
src/core/unit.h
src/test/test-engine.c
test/units/testsuite-68.sh

index 3b57f8d2f11a9f70b03b1dbd6c2bc93caf7ea5d1..defca12d1ba4c2509c5d535463bc2c7eb183083c 100644 (file)
@@ -3404,7 +3404,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <varlistentry>
           <term><varname>$SERVICE_RESULT</varname></term>
 
-          <listitem><para>Only defined for the service unit type, this environment variable is passed to all
+          <listitem><para>Only used 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:</para>
 
@@ -3472,7 +3472,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
           <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
+          <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>
@@ -3585,38 +3585,28 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         </varlistentry>
 
         <varlistentry>
-          <term><varname>$MONITOR_METADATA</varname></term>
-
-          <listitem><para>Only defined for the service unit type, this environment variable is passed to all
-          <varname>ExecStart=</varname> and <varname>ExecStartPre=</varname> processes which run in services
-          triggered by <varname>OnFailure=</varname> or <varname>OnSuccess=</varname> dependencies.</para>
-
-          <para>
-          The contents of this variable consists of a semi-colon separated list of metadata fields associated with the triggering
-          service. For each service which triggered the <varname>OnFailure=</varname> or <varname>OnSuccess=</varname>
-          dependency the following fields will be set:
+          <term><varname>$MONITOR_SERVICE_RESULT</varname></term>
+          <term><varname>$MONITOR_EXIT_CODE</varname></term>
+          <term><varname>$MONITOR_EXIT_STATUS</varname></term>
+          <term><varname>$MONITOR_INVOCATION_ID</varname></term>
+          <term><varname>$MONITOR_UNIT</varname></term>
+
+          <listitem><para>Only defined for the service unit type. Those environment variable are passed to
+          all <varname>ExecStart=</varname> and <varname>ExecStartPre=</varname> processes which run in
+          services triggered by <varname>OnFailure=</varname> or <varname>OnSuccess=</varname> dependencies.
           </para>
 
-          <itemizedlist>
-            <listitem><para><constant>SERVICE_RESULT</constant></para></listitem>
-            <listitem><para><constant>EXIT_CODE</constant></para></listitem>
-            <listitem><para><constant>EXIT_STATUS</constant></para></listitem>
-            <listitem><para><constant>INVOCATION_ID</constant></para></listitem>
-            <listitem><para><constant>UNIT</constant></para></listitem>
-          </itemizedlist>
-
-          <para>The fields <constant>SERVICE_RESULT</constant>, <constant>EXIT_CODE</constant> and
-          <constant>EXIT_STATUS</constant> may take the same values that are allowed when set for
-          <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes. The fields
-          <constant>INVOCATION_ID</constant> and <constant>UNIT</constant> are the invocaton id and unit
-          name of the service which triggered the dependency. Each field is comma separated, i.e.</para>
-
-          <programlisting>
-SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=triggering.service
-          </programlisting>
-
-          </listitem>
-
+          <para>Variables <varname>$MONITOR_SERVICE_RESULT</varname>, <varname>$MONITOR_EXIT_CODE</varname>
+          and <varname>$MONITOR_EXIT_STATUS</varname> take the same values as for
+          <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes. Variables
+          <varname>$MONITOR_INVOCATION_ID</varname> and <varname>$MONITOR_UNIT</varname> are set to the
+          invocaton id and unit name of the service which triggered the dependency.</para>
+
+          <para>Note that when multiple services trigger the same unit, those variables will be
+          <emphasis>not</emphasis> be passed. Consider using a template handler unit for that case instead:
+          <literal>OnFailure=<replaceable>handler</replaceable>@%n.service</literal> for non-templated units,
+          or <literal>OnFailure=<replaceable>handler</replaceable>@%p-%i.service</literal> for templated
+          units.</para></listitem>
         </varlistentry>
 
         <varlistentry>
@@ -4060,7 +4050,7 @@ SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCAT
     <title>Examples</title>
 
       <example>
-        <title><varname>$MONITOR_METADATA</varname> usage</title>
+        <title><varname>$MONITOR_<replaceable>*</replaceable></varname> usage</title>
 
         <para>A service <filename index="false">myfailer.service</filename> which can trigger an
         <varname>OnFailure=</varname> dependency.</para>
@@ -4094,33 +4084,31 @@ ExecStart=/bin/mysecondprogram
 Description=Acts on service failing or succeeding
 
 [Service]
-ExecStart=/bin/bash -c "echo $MONITOR_METADATA"
+ExecStart=/bin/bash -c "echo $MONITOR_SERVICE_RESULT $MONITOR_EXIT_CODE $MONITOR_EXIT_STATUS $MONITOR_INVOCATION_ID $MONITOR_UNIT"
         </programlisting>
 
         <para>If <filename index="false">myfailer.service</filename> were to run and exit in failure,
         then <filename index="false">myhandler.service</filename> would be triggered and the
-        <varname>$MONITOR_METADATA</varname> variable would be set as follows:</para>
+        monitor variables would be set as follows:</para>
 
         <programlisting>
-MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=myfailer.service
+MONITOR_SERVICE_RESULT=exit-code
+MONITOR_EXIT_CODE=exited
+MONITOR_EXIT_STATUS=1
+MONITOR_INVOCATION_ID=cc8fdc149b2b4ca698d4f259f4054236
+MONITOR_UNIT=myfailer.service
         </programlisting>
 
         <para>If <filename index="false">mysuccess.service</filename> were to run and exit in success,
         then <filename index="false">myhandler.service</filename> would be triggered and the
-        <varname>$MONITOR_METADATA</varname> variable would be set as follows:</para>
-
-        <programlisting>
-MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=mysuccess.service
-        </programlisting>
-
-        <para>If <filename index="false">myfailer.service</filename> and <filename index="false">mysuccess.service</filename> were to run and exit,
-        there is a chance that the triggered dependency start job might be merged. Thus only a single invocation of
-        <filename index="false">myhandler.service</filename> would be triggered. In this case the <varname>$MONITOR_METADATA</varname> variable
-        would be a list containing exit metadata for both of <filename index="false">myfailer.service</filename>
-        and <filename index="false">mysuccess.service</filename>.</para>
+        monitor variables would be set as follows:</para>
 
         <programlisting>
-MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=myfailer.service;SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=mysuccess.service
+MONITOR_SERVICE_RESULT=success
+MONITOR_EXIT_CODE=exited
+MONITOR_EXIT_STATUS=0
+MONITOR_INVOCATION_ID=6ab9af147b8c4a3ebe36e7a5f8611697
+MONITOR_UNIT=mysuccess.service
         </programlisting>
 
     </example>
index f28821071be2c92ac2062ebfe09ff6d01720dfc0..94ab381626475d7a0519290dbda93903f0c4c353 100644 (file)
@@ -99,9 +99,6 @@ Job* job_free(Job *j) {
         assert(!j->subject_list);
         assert(!j->object_list);
 
-        while (!LIST_IS_EMPTY(j->triggered_by))
-                LIST_POP(triggered_by, j->triggered_by);
-
         job_unlink(j);
 
         sd_bus_track_unref(j->bus_track);
@@ -110,13 +107,6 @@ Job* job_free(Job *j) {
         return mfree(j);
 }
 
-void job_add_triggering_unit(Job *j, Unit *u) {
-        assert(j);
-        assert(u);
-
-        LIST_APPEND(triggered_by, j->triggered_by, u);
-}
-
 static void job_set_state(Job *j, JobState state) {
         assert(j);
         assert(state >= 0);
@@ -197,8 +187,6 @@ static void job_merge_into_installed(Job *j, Job *other) {
 
         j->irreversible = j->irreversible || other->irreversible;
         j->ignore_order = j->ignore_order || other->ignore_order;
-        if (other->triggered_by)
-                LIST_JOIN(triggered_by, j->triggered_by, other->triggered_by);
 }
 
 Job* job_install(Job *j) {
index 762b0bb19bc395ef8b064ae5a7b62e1f296e1792..a66e5985b8520d3cdea0cec1332c965cd7d4c713 100644 (file)
@@ -124,8 +124,6 @@ struct Job {
         LIST_HEAD(JobDependency, subject_list);
         LIST_HEAD(JobDependency, object_list);
 
-        LIST_HEAD(Unit, triggered_by);
-
         /* Used for graph algs as a "I have been here" marker */
         Job* marker;
         unsigned generation;
@@ -246,5 +244,3 @@ JobResult job_result_from_string(const char *s) _pure_;
 const char* job_type_to_access_method(JobType t);
 
 int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
-
-void job_add_triggering_unit(Job *j, Unit *u);
index 92af448ff48b37346d3af2b074ea64d6e2b55f7b..551f3df3557b0010a67815345f6caa3f5f96d6f7 100644 (file)
@@ -1447,81 +1447,37 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
         return s->notify_access != NOTIFY_NONE;
 }
 
-static int service_create_monitor_md_env(Job *j, char **ret) {
-        _cleanup_free_ char *var = NULL;
-        const char *list_delim = ";";
-        bool first = true;
-        Unit *tu;
+static Service *service_get_triggering_service(Service *s) {
+        Unit *candidate = NULL, *other;
 
-        assert(j);
-        assert(ret);
+        assert(s);
 
-        /* Create an environment variable 'MONITOR_METADATA', if creation is successful
-         * a pointer to it is returned via ret.
-         *
-         * This variable contains a space separated set of fields which relate to
-         * the service(s) which triggered job 'j'. Job 'j' is the JOB_START job for
-         * an OnFailure= or OnSuccess= dependency. Format of the MONITOR_METADATA
-         * variable is as follows:
-         *
-         * MONITOR_METADATA="SERVICE_RESULT=<result-string0>,EXIT_CODE=<exit-code0>,EXIT_STATUS=<exit-status0>,
-         *                   INVOCATION_ID=<id>,UNIT=<triggering-unit0.service>;
-         *                   SERVICE_RESULT=<result-stringN>,EXIT_CODE=<exit-codeN>,EXIT_STATUS=<exit-statusN>,
-         *                   INVOCATION_ID=<id>,UNIT=<triggering-unitN.service>"
-         *
-         * Multiple results may be passed as in the above example if jobs are merged, i.e.
-         * some services a and b contain an OnFailure= or OnSuccess= dependency on the same
-         * service.
-         *
-         * For example:
+        /* Return the service which triggered service 's', this means dependency
+         * types which include the UNIT_ATOM_ON_{FAILURE,SUCCESS}_OF atoms.
          *
-         * MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=02dd868af2f344b18edaf74b618b2f90,UNIT=failure.service;
-         *                   SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=80cb228bd7344f77a090eda603a3cfe2,UNIT=failure2.service"
-         */
-
-        LIST_FOREACH(triggered_by, tu, j->triggered_by) {
-                Service *env_source = SERVICE(tu);
-                int r;
-
-                if (!env_source)
-                        continue;
-
-                /* Add the environment variable name first. */
-                if (first && !strextend(&var, "MONITOR_METADATA="))
-                        return -ENOMEM;
-
-                if (!strextend(&var, !first ? list_delim : "", "SERVICE_RESULT=", service_result_to_string(env_source->result)))
-                        return -ENOMEM;
-
-                first = false;
-
-                if (env_source->main_exec_status.pid > 0 &&
-                    dual_timestamp_is_set(&env_source->main_exec_status.exit_timestamp)) {
-                        if (!strextend(&var, ",EXIT_CODE=", sigchld_code_to_string(env_source->main_exec_status.code)))
-                                return -ENOMEM;
-
-                        if (env_source->main_exec_status.code == CLD_EXITED) {
-                                r = strextendf(&var, ",EXIT_STATUS=%i",
-                                               env_source->main_exec_status.status);
-                                if (r < 0)
-                                        return r;
-                        } else if (!strextend(&var, ",EXIT_STATUS=", signal_to_string(env_source->main_exec_status.status)))
-                                return -ENOMEM;
+         * N.B. if there are multiple services which could trigger 's' via OnFailure=
+         * or OnSuccess= then we return NULL. This is since we don't know from which
+         * one to propagate the exit status. */
+
+        UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_ON_FAILURE_OF) {
+                if (candidate) {
+                        log_unit_debug(UNIT(s), "multiple trigger source candidates for exit status propagation, skipping.");
+                        return NULL;
                 }
 
-                if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) {
-                        r = strextendf(&var, ",INVOCATION_ID=" SD_ID128_FORMAT_STR,
-                                       SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id));
-                        if (r < 0)
-                                return r;
+                candidate = other;
+        }
+
+        UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_ON_SUCCESS_OF) {
+                if (candidate) {
+                        log_unit_debug(UNIT(s), "multiple trigger source candidates for exit status propagation, skipping.");
+                        return NULL;
                 }
 
-                if (!strextend(&var, ",UNIT=", UNIT(env_source)->id))
-                        return -ENOMEM;
+                candidate = other;
         }
 
-        *ret = TAKE_PTR(var);
-        return 0;
+        return SERVICE(candidate);
 }
 
 static int service_spawn(
@@ -1588,7 +1544,7 @@ static int service_spawn(
         if (r < 0)
                 return r;
 
-        our_env = new0(char*, 10);
+        our_env = new0(char*, 12);
         if (!our_env)
                 return -ENOMEM;
 
@@ -1645,30 +1601,44 @@ static int service_spawn(
                 }
         }
 
+        Service *env_source = NULL;
+        const char *monitor_prefix;
         if (flags & EXEC_SETENV_RESULT) {
-                if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0)
+                env_source = s;
+                monitor_prefix = "";
+        } else if (flags & EXEC_SETENV_MONITOR_RESULT) {
+                env_source = service_get_triggering_service(s);
+                monitor_prefix = "MONITOR_";
+        }
+
+        if (env_source) {
+                if (asprintf(our_env + n_env++, "%sSERVICE_RESULT=%s", monitor_prefix, service_result_to_string(env_source->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)
+                if (env_source->main_exec_status.pid > 0 &&
+                    dual_timestamp_is_set(&env_source->main_exec_status.exit_timestamp)) {
+                        if (asprintf(our_env + n_env++, "%sEXIT_CODE=%s", monitor_prefix, sigchld_code_to_string(env_source->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);
+                        if (env_source->main_exec_status.code == CLD_EXITED)
+                                r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%i", monitor_prefix, env_source->main_exec_status.status);
                         else
-                                r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
+                                r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%s", monitor_prefix, signal_to_string(env_source->main_exec_status.status));
 
                         if (r < 0)
                                 return -ENOMEM;
                 }
 
-        } else if (flags & EXEC_SETENV_MONITOR_RESULT) {
-                Job *j = UNIT(s)->job;
-                if (j) {
-                        r = service_create_monitor_md_env(j, our_env + n_env++);
-                        if (r < 0)
-                                return r;
+                if (env_source != s) {
+                        if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) {
+                                r = asprintf(our_env + n_env++, "%sINVOCATION_ID=" SD_ID128_FORMAT_STR,
+                                             monitor_prefix, SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id));
+                                if (r < 0)
+                                        return -ENOMEM;
+                        }
+
+                        if (asprintf(our_env + n_env++, "%sUNIT=%s", monitor_prefix, UNIT(env_source)->id) < 0)
+                                return -ENOMEM;
                 }
         }
 
index 333eea6c3d368d037594059b340112d6b69b6078..81333523e73865ee8166ae3fe5fc2a33a3040fbc 100644 (file)
@@ -82,13 +82,11 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
         [UNIT_PROPAGATES_STOP_TO]     = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
                                         UNIT_ATOM_PROPAGATE_STOP,
 
-        [UNIT_ON_FAILURE]             = UNIT_ATOM_ON_FAILURE |
-                                        UNIT_ATOM_BACK_REFERENCE_IMPLIED,
-
-        [UNIT_ON_SUCCESS]             = UNIT_ATOM_ON_SUCCESS |
-                                        UNIT_ATOM_BACK_REFERENCE_IMPLIED,
-
         /* These are simple dependency types: they consist of a single atom only */
+        [UNIT_ON_FAILURE]             = UNIT_ATOM_ON_FAILURE,
+        [UNIT_ON_SUCCESS]             = UNIT_ATOM_ON_SUCCESS,
+        [UNIT_ON_FAILURE_OF]          = UNIT_ATOM_ON_FAILURE_OF,
+        [UNIT_ON_SUCCESS_OF]          = UNIT_ATOM_ON_SUCCESS_OF,
         [UNIT_BEFORE]                 = UNIT_ATOM_BEFORE,
         [UNIT_AFTER]                  = UNIT_ATOM_AFTER,
         [UNIT_TRIGGERS]               = UNIT_ATOM_TRIGGERS,
@@ -104,8 +102,6 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
          * things discoverable/debuggable as they are the inverse dependencies to some of the above. As they
          * have no effect of their own, they all map to no atoms at all, i.e. the value 0. */
         [UNIT_RELOAD_PROPAGATED_FROM] = 0,
-        [UNIT_ON_SUCCESS_OF]          = 0,
-        [UNIT_ON_FAILURE_OF]          = 0,
         [UNIT_STOP_PROPAGATED_FROM]   = 0,
 };
 
@@ -200,17 +196,19 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
         case UNIT_ATOM_PROPAGATE_STOP_FAILURE:
                 return UNIT_CONFLICTED_BY;
 
-        case UNIT_ATOM_ON_FAILURE |
-                UNIT_ATOM_BACK_REFERENCE_IMPLIED:
+        /* And now, the simple ones */
+
         case UNIT_ATOM_ON_FAILURE:
                 return UNIT_ON_FAILURE;
 
-        case UNIT_ATOM_ON_SUCCESS |
-                UNIT_ATOM_BACK_REFERENCE_IMPLIED:
         case UNIT_ATOM_ON_SUCCESS:
                 return UNIT_ON_SUCCESS;
 
-        /* And now, the simple ones */
+        case UNIT_ATOM_ON_SUCCESS_OF:
+                return UNIT_ON_SUCCESS_OF;
+
+        case UNIT_ATOM_ON_FAILURE_OF:
+                return UNIT_ON_FAILURE_OF;
 
         case UNIT_ATOM_BEFORE:
                 return UNIT_BEFORE;
index 1cf97957869a662244c55949b40ae6a7a5b9b46d..02532e57d6a17eb2fef0954a39183f61303667f3 100644 (file)
@@ -66,27 +66,22 @@ typedef enum UnitDependencyAtom {
         /* Recheck default target deps on other units (which are target units) */
         UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES         = UINT64_C(1) << 21,
 
-        /* Dependencies which include this atom automatically get a reverse
-         * REFERENCES/REFERENCED_BY dependency. */
-        UNIT_ATOM_BACK_REFERENCE_IMPLIED              = UINT64_C(1) << 22,
-
-        /* Trigger a dependency on successful service exit. */
-        UNIT_ATOM_ON_SUCCESS                          = UINT64_C(1) << 23,
-        /* Trigger a dependency on unsuccessful service exit. */
-        UNIT_ATOM_ON_FAILURE                          = UINT64_C(1) << 24,
-
         /* The remaining atoms map 1:1 to the equally named high-level deps */
-        UNIT_ATOM_BEFORE                              = UINT64_C(1) << 25,
-        UNIT_ATOM_AFTER                               = UINT64_C(1) << 26,
-        UNIT_ATOM_TRIGGERS                            = UINT64_C(1) << 27,
-        UNIT_ATOM_TRIGGERED_BY                        = UINT64_C(1) << 28,
-        UNIT_ATOM_PROPAGATES_RELOAD_TO                = UINT64_C(1) << 29,
-        UNIT_ATOM_JOINS_NAMESPACE_OF                  = UINT64_C(1) << 30,
-        UNIT_ATOM_REFERENCES                          = UINT64_C(1) << 31,
-        UNIT_ATOM_REFERENCED_BY                       = UINT64_C(1) << 32,
-        UNIT_ATOM_IN_SLICE                            = UINT64_C(1) << 33,
-        UNIT_ATOM_SLICE_OF                            = UINT64_C(1) << 34,
-        _UNIT_DEPENDENCY_ATOM_MAX                     = (UINT64_C(1) << 35) - 1,
+        UNIT_ATOM_ON_FAILURE                          = UINT64_C(1) << 22,
+        UNIT_ATOM_ON_SUCCESS                          = UINT64_C(1) << 23,
+        UNIT_ATOM_ON_FAILURE_OF                       = UINT64_C(1) << 24,
+        UNIT_ATOM_ON_SUCCESS_OF                       = UINT64_C(1) << 25,
+        UNIT_ATOM_BEFORE                              = UINT64_C(1) << 26,
+        UNIT_ATOM_AFTER                               = UINT64_C(1) << 27,
+        UNIT_ATOM_TRIGGERS                            = UINT64_C(1) << 28,
+        UNIT_ATOM_TRIGGERED_BY                        = UINT64_C(1) << 29,
+        UNIT_ATOM_PROPAGATES_RELOAD_TO                = UINT64_C(1) << 30,
+        UNIT_ATOM_JOINS_NAMESPACE_OF                  = UINT64_C(1) << 31,
+        UNIT_ATOM_REFERENCES                          = UINT64_C(1) << 32,
+        UNIT_ATOM_REFERENCED_BY                       = UINT64_C(1) << 33,
+        UNIT_ATOM_IN_SLICE                            = UINT64_C(1) << 34,
+        UNIT_ATOM_SLICE_OF                            = UINT64_C(1) << 35,
+        _UNIT_DEPENDENCY_ATOM_MAX                     = (UINT64_C(1) << 36) - 1,
         _UNIT_DEPENDENCY_ATOM_INVALID                 = -EINVAL,
 } UnitDependencyAtom;
 
index 2cddc924f366c93000e4e993ff3438b8d7933427..af6cf097fcc820a91830775b86338842ce898a1b 100644 (file)
@@ -2222,24 +2222,17 @@ void unit_start_on_failure(
 
         UNIT_FOREACH_DEPENDENCY(other, u, atom) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-                Job *job = NULL;
 
                 if (!logged) {
                         log_unit_info(u, "Triggering %s dependencies.", dependency_name);
                         logged = true;
                 }
 
-                r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, &job);
+                r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, NULL);
                 if (r < 0)
                         log_unit_warning_errno(
                                         u, r, "Failed to enqueue %s job, ignoring: %s",
                                         dependency_name, bus_error_message(&error, r));
-                else if (job)
-                        /* u will be kept pinned since both UNIT_ON_FAILURE and UNIT_ON_SUCCESS includes
-                         * UNIT_ATOM_BACK_REFERENCE_IMPLIED. We save the triggering unit here since we
-                         * want to be able to reference it when we come to run the OnFailure= or OnSuccess=
-                         * dependency. */
-                        job_add_triggering_unit(job, u);
         }
 
         if (logged)
@@ -3121,20 +3114,6 @@ int unit_add_dependency(
                         noop = false;
         }
 
-        if (FLAGS_SET(a, UNIT_ATOM_BACK_REFERENCE_IMPLIED)) {
-                r = unit_add_dependency_hashmap(&other->dependencies, UNIT_REFERENCES, u, 0, mask);
-                if (r < 0)
-                        return r;
-                if (r)
-                        noop = false;
-
-                r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCED_BY, other, 0, mask);
-                if (r < 0)
-                        return r;
-                if (r)
-                        noop = false;
-        }
-
         if (add_reference) {
                 r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCES, other, mask, 0);
                 if (r < 0)
index 786c15d6233b75a3e42a9ae179f0cb9cb4e1a098..94f2180951cdc51ed1d936ebe2861744e278f260 100644 (file)
@@ -242,9 +242,6 @@ typedef struct Unit {
         /* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */
         LIST_FIELDS(Unit, stop_when_bound_queue);
 
-        /* Queue of units which have triggered an OnFailure= or OnSuccess= dependency job. */
-        LIST_FIELDS(Unit, triggered_by);
-
         /* PIDs we keep an eye on. Note that a unit might have many
          * more, but these are the ones we care enough about to
          * process SIGCHLD for */
index 673c66561240f6b9241503fc49afc03c5f16a6eb..473e896acc7f365286706a2d06a12bc61dae5f07 100644 (file)
@@ -223,6 +223,7 @@ int main(int argc, char *argv[]) {
         assert_se(unit_add_dependency_by_name(stub, UNIT_AFTER, SPECIAL_ROOT_SLICE, true, UNIT_DEPENDENCY_FILE) >= 0);
         assert_se(unit_add_dependency_by_name(stub, UNIT_REQUIRES, "non-existing.mount", true, UNIT_DEPENDENCY_FILE) >= 0);
         assert_se(unit_add_dependency_by_name(stub, UNIT_ON_FAILURE, "non-existing-on-failure.target", true, UNIT_DEPENDENCY_FILE) >= 0);
+        assert_se(unit_add_dependency_by_name(stub, UNIT_ON_SUCCESS, "non-existing-on-success.target", true, UNIT_DEPENDENCY_FILE) >= 0);
 
         log_info("/* Merging a+stub, dumps before */");
         unit_dump(a, stderr, NULL);
@@ -237,7 +238,13 @@ int main(int argc, char *argv[]) {
         assert_se(unit_has_dependency(a, UNIT_ATOM_PULL_IN_START, manager_get_unit(m, "non-existing.mount")));
         assert_se(unit_has_dependency(a, UNIT_ATOM_RETROACTIVE_START_REPLACE, manager_get_unit(m, "non-existing.mount")));
         assert_se(unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "non-existing-on-failure.target")));
+        assert_se(unit_has_dependency(manager_get_unit(m, "non-existing-on-failure.target"), UNIT_ATOM_ON_FAILURE_OF, a));
+        assert_se(unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "non-existing-on-success.target")));
+        assert_se(unit_has_dependency(manager_get_unit(m, "non-existing-on-success.target"), UNIT_ATOM_ON_SUCCESS_OF, a));
         assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "basic.target")));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "basic.target")));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE_OF, manager_get_unit(m, "basic.target")));
+        assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS_OF, manager_get_unit(m, "basic.target")));
         assert_se(!unit_has_dependency(a, UNIT_ATOM_PROPAGATES_RELOAD_TO, manager_get_unit(m, "non-existing-on-failure.target")));
 
         assert_se(unit_has_name(a, "a.service"));
index 15e2e0353fd392e6dfbb5352e1388f1e2fb0f8ef..d9584e4539bc507d0c105914767e2ccd03ac474b 100755 (executable)
@@ -36,16 +36,6 @@ OnFailure=testservice-failure-exit-handler-68.service
 ExecStart=/bin/bash -c "exit 1"
 EOF
 
-# Another service which triggers testservice-failure-exit-handler-68.service
-cat >/run/systemd/system/testservice-failure-68-additional.service <<EOF
-[Unit]
-Description=TEST-68-PROPAGATE-EXIT-STATUS Additional service with OnFailure= trigger
-OnFailure=testservice-failure-exit-handler-68.service
-
-[Service]
-ExecStart=/bin/bash -c "exit 1"
-EOF
-
 # Trigger testservice-success-exit-handler-68.service
 cat >/run/systemd/system/testservice-success-68.service <<EOF
 [Unit]
@@ -56,67 +46,37 @@ OnSuccess=testservice-success-exit-handler-68.service
 ExecStart=/bin/bash -c "exit 0"
 EOF
 
-# Trigger testservice-success-exit-handler-68.service
-cat >/run/systemd/system/testservice-success-68-additional.service <<EOF
-[Unit]
-Description=TEST-68-PROPAGATE-EXIT-STATUS Addition service with OnSuccess= trigger
-OnSuccess=testservice-success-exit-handler-68.service
-
-[Service]
-ExecStart=/bin/bash -c "exit 0"
-EOF
-
 # Script to check that when an OnSuccess= dependency fires, the correct
-# MONITOR* env variables are passed. This script handles the case where
-# multiple services triggered the unit that calls this script. In this
-# case we need to check the MONITOR_METADATA variable for >= 1 service
-# details since jobs may merge.
+# MONITOR* env variables are passed.
 cat >/tmp/check_on_success.sh <<EOF
 #!/usr/bin/env bash
 
 set -ex
-
-echo "MONITOR_METADATA=\$MONITOR_METADATA"
-
-IFS=';' read -ra ALL_SERVICE_MD <<< "\$MONITOR_METADATA"
-for SERVICE_MD in "\${ALL_SERVICE_MD[@]}"; do
-    IFS=',' read -ra METADATA <<< "\$SERVICE_MD"
-    IFS='=' read -ra SERVICE_RESULT <<< "\${METADATA[0]}"
-    SERVICE_RESULT=\${SERVICE_RESULT[1]}
-    IFS='=' read -ra EXIT_CODE <<< "\${METADATA[1]}"
-    EXIT_CODE=\${EXIT_CODE[1]}
-    IFS='=' read -ra EXIT_STATUS <<< "\${METADATA[2]}"
-    EXIT_STATUS=\${EXIT_STATUS[1]}
-    IFS='=' read -ra INVOCATION_ID <<< "\${METADATA[3]}"
-    INVOCATION_ID=\${INVOCATION_ID[1]}
-    IFS='=' read -ra UNIT <<< "\${METADATA[4]}"
-    UNIT=\${UNIT[1]}
-
-    if [ "\$SERVICE_RESULT" != "success" ]; then
-        echo 'SERVICE_RESULT was "\$SERVICE_RESULT", expected "success"';
-        exit 1;
-    fi
-
-    if [ "\$EXIT_CODE" != "exited" ]; then
-        echo 'EXIT_CODE was "\$EXIT_CODE", expected "exited"';
-        exit 1;
-    fi
-
-    if [ "\$EXIT_STATUS" != "0" ]; then
-        echo 'EXIT_STATUS was "\$EXIT_STATUS", expected "0"';
-        exit 1;
-    fi
-
-    if [ -z "\$INVOCATION_ID" ]; then
-        echo 'INVOCATION_ID unset';
-        exit 1;
-    fi
-
-    if [[ "\$UNIT" != "testservice-success-68.service" && "\$UNIT" != "testservice-success-68-additional.service" && "\$UNIT" != "testservice-transient-success-68.service" ]]; then
-        echo 'UNIT was "\$UNIT", expected "testservice-success-68{-additional,-transient}.service"';
-        exit 1;
-    fi
-done
+env
+if [ "\$MONITOR_SERVICE_RESULT" != "success" ]; then
+    echo "MONITOR_SERVICE_RESULT was \"\$MONITOR_SERVICE_RESULT\", expected \"success\"";
+    exit 1;
+fi
+
+if [ "\$MONITOR_EXIT_CODE" != "exited" ]; then
+    echo "MONITOR_EXIT_CODE was \"\$MONITOR_EXIT_CODE\", expected \"exited\"";
+    exit 1;
+fi
+
+if [ "\$MONITOR_EXIT_STATUS" != "0" ]; then
+    echo "MONITOR_EXIT_STATUS was \"\$MONITOR_EXIT_STATUS\", expected \"0\"";
+    exit 1;
+fi
+
+if [ -z "\$MONITOR_INVOCATION_ID" ]; then
+    echo "MONITOR_INVOCATION_ID unset";
+    exit 1;
+fi
+
+if [[ "\$MONITOR_UNIT" != "testservice-success-68.service" && "\$MONITOR_UNIT" != "testservice-transient-success-68.service" ]]; then
+    echo "MONITOR_UNIT was \"\$MONITOR_UNIT\", expected \"testservice-success-68{-transient}.service\"";
+    exit 1;
+fi
 
 exit 0;
 EOF
@@ -133,56 +93,36 @@ ExecStart=/tmp/check_on_success.sh
 EOF
 
 # Script to check that when an OnFailure= dependency fires, the correct
-# MONITOR* env variables are passed. This script handles the case where
-# multiple services triggered the unit that calls this script. In this
-# case we need to check the MONITOR_METADATA variable for >=1 service
-# details since jobs may merge.
+# MONITOR* env variables are passed.
 cat >/tmp/check_on_failure.sh <<EOF
 #!/usr/bin/env bash
 
 set -ex
-
-echo "MONITOR_METADATA=\$MONITOR_METADATA"
-
-IFS=';' read -ra ALL_SERVICE_MD <<< "\$MONITOR_METADATA"
-for SERVICE_MD in "\${ALL_SERVICE_MD[@]}"; do
-    IFS=',' read -ra METADATA <<< "\$SERVICE_MD"
-    IFS='=' read -ra SERVICE_RESULT <<< "\${METADATA[0]}"
-    SERVICE_RESULT=\${SERVICE_RESULT[1]}
-    IFS='=' read -ra EXIT_CODE <<< "\${METADATA[1]}"
-    EXIT_CODE=\${EXIT_CODE[1]}
-    IFS='=' read -ra EXIT_STATUS <<< "\${METADATA[2]}"
-    EXIT_STATUS=\${EXIT_STATUS[1]}
-    IFS='=' read -ra INVOCATION_ID <<< "\${METADATA[3]}"
-    INVOCATION_ID=\${INVOCATION_ID[1]}
-    IFS='=' read -ra UNIT <<< "\${METADATA[4]}"
-    UNIT=\${UNIT[1]}
-
-    if [ "\$SERVICE_RESULT" != "exit-code" ]; then
-        echo 'SERVICE_RESULT was "\$SERVICE_RESULT", expected "exit-code"';
-        exit 1;
-    fi
-
-    if [ "\$EXIT_CODE" != "exited" ]; then
-        echo 'EXIT_CODE was "\$EXIT_CODE", expected "exited"';
-        exit 1;
-    fi
-
-    if [ "\$EXIT_STATUS" != "1" ]; then
-        echo 'EXIT_STATUS was "\$EXIT_STATUS", expected "1"';
-        exit 1;
-    fi
-
-    if [ -z "\$INVOCATION_ID" ]; then
-        echo 'INVOCATION_ID unset';
-        exit 1;
-    fi
-
-    if [[ "\$UNIT" != "testservice-failure-68.service" && "\$UNIT" != "testservice-failure-68-additional.service" && "\$UNIT" != "testservice-transient-failure-68.service" ]]; then
-        echo 'UNIT was "\$UNIT", expected "testservice-failure-68{-additional,-transient}.service"';
-        exit 1;
-    fi
-done
+env
+if [ "\$MONITOR_SERVICE_RESULT" != "exit-code" ]; then
+    echo "MONITOR_SERVICE_RESULT was \"\$MONITOR_SERVICE_RESULT\", expected \"exit-code\"";
+    exit 1;
+fi
+
+if [ "\$MONITOR_EXIT_CODE" != "exited" ]; then
+    echo "MONITOR_EXIT_CODE was \"\$MONITOR_EXIT_CODE\", expected \"exited\"";
+    exit 1;
+fi
+
+if [ "\$MONITOR_EXIT_STATUS" != "1" ]; then
+    echo "MONITOR_EXIT_STATUS was \"\$MONITOR_EXIT_STATUS\", expected \"1\"";
+    exit 1;
+fi
+
+if [ -z "\$MONITOR_INVOCATION_ID" ]; then
+    echo "MONITOR_INVOCATION_ID unset";
+    exit 1;
+fi
+
+if [[ "\$MONITOR_UNIT" != "testservice-failure-68.service" && "\$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]]; then
+    echo "MONITOR_UNIT was \"\$MONITOR_UNIT\", expected \"testservice-failure-68{-transient}.service\"";
+    exit 1;
+fi
 
 exit 0;
 EOF
@@ -201,16 +141,10 @@ EOF
 
 systemctl daemon-reload
 
-# The running of the OnFailure= and OnSuccess= jobs for all of these services
-# may result in jobs being merged.
 systemctl start testservice-failure-68.service
 wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-systemctl start testservice-failure-68-additional.service
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
 systemctl start testservice-success-68.service
 wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-systemctl start testservice-success-68-additional.service
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
 
 # Test some transient units since these exit very quickly.
 systemd-run --unit=testservice-transient-success-68 --property=OnSuccess=testservice-success-exit-handler-68.service /bin/bash -c "exit 0;"
@@ -218,12 +152,6 @@ wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "
 systemd-run --unit=testservice-transient-failure-68 --property=OnFailure=testservice-failure-exit-handler-68.service /bin/bash -c "exit 1;"
 wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
 
-# These yield a higher chance of resulting in jobs merging.
-systemctl start testservice-failure-68.service testservice-failure-68-additional.service --no-block
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-systemctl start testservice-success-68.service testservice-success-68-additional.service --no-block
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-
 systemd-analyze log-level info
 echo OK >/testok