core: add method to enqueue multiple jobs in a single call (#42182)

Currently only a single job for a single unit can be enqueued
atomically,
so there is no guarantee that, e.g., starting a unit and its socket
at the same time will happen in the same transaction. That forces
callers to 'know' the right order in which to start new units being
installed, or failures will occur. It also means some ordering
constraints are ignored, in case the separate calls are done
in the wrong manual order.

Add a new EnqueueUnitJobMany() D-Bus method that takes a list of units
to start.
This commit is contained in:
Luca Boccassi
2026-06-22 21:35:17 +01:00
committed by GitHub
12 changed files with 1042 additions and 60 deletions
+40 -3
View File
@@ -109,6 +109,11 @@ node /org/freedesktop/systemd1 {
out o unit_path,
out s job_type,
out a(uosos) affected_jobs);
EnqueueUnitJobMany(in as units,
in s job_type,
in s job_mode,
in t flags,
out a(uosos) jobs);
KillUnit(in s name,
in s whom,
in i signal);
@@ -593,8 +598,6 @@ node /org/freedesktop/systemd1 {
<!--method GetUnitByControlGroup is not documented!-->
<!--method EnqueueUnitJob is not documented!-->
<!--method FreezeUnit is not documented!-->
<!--method ThawUnit is not documented!-->
@@ -871,6 +874,8 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-method" generated="True" extra-ref="EnqueueUnitJob()"/>
<variablelist class="dbus-method" generated="True" extra-ref="EnqueueUnitJobMany()"/>
<variablelist class="dbus-method" generated="True" extra-ref="KillUnit()"/>
<variablelist class="dbus-method" generated="True" extra-ref="KillUnitSubgroup()"/>
@@ -1362,6 +1367,37 @@ node /org/freedesktop/systemd1 {
the "Try" flavor is used in which case a service that is not running is not affected by the restart. The
"ReloadOrRestart" flavors attempt a reload if the unit supports it and use a restart otherwise.</para>
<para><function>EnqueueUnitJob()</function> is similar to the various
<function>StartUnit()</function>/<function>StopUnit()</function>/<function>RestartUnit()</function>
methods above, but provides a unified interface that operates on a single unit. The
<varname>job_type</varname> argument selects the job type and is one of
<literal>start</literal>, <literal>stop</literal>, <literal>reload</literal>,
<literal>restart</literal>, <literal>try-restart</literal>,
<literal>reload-or-restart</literal> or <literal>reload-or-try-restart</literal>. The
<varname>job_mode</varname> argument matches the <varname>mode</varname> argument of
<function>StartUnit()</function>. On reply the method returns the enqueued anchor job's
numeric id and object path, the affected unit's id and object path, the job type
actually queued (after type collapsing, e.g. <literal>reload-or-restart</literal> turns
into <literal>restart</literal> or <literal>reload</literal>), and an array of
additional jobs that were enqueued as part of the same transaction in order to satisfy
dependencies.</para>
<para><function>EnqueueUnitJobMany()</function> is similar to
<function>EnqueueUnitJob()</function>, but operates on multiple units at once. All
requested units are enqueued in a single transaction, which is necessary in order to
properly honour <varname>After=</varname>/<varname>Before=</varname> ordering between
units passed in the same call regardless of their order in the array. The
<varname>job_type</varname> argument is applied to each of the listed units, including
the magic values <literal>reload-or-restart</literal> and
<literal>reload-or-try-restart</literal> which are resolved per unit (units that
support reloading get a reload job, others get a restart). The <varname>flags</varname>
argument does not support any flags for now and must be passed as <literal>0</literal>;
it is reserved for future extensions. On reply the method returns an array of tuples
describing the anchor job that was enqueued for each requested unit: the numeric job
id, the job object path, the unit id, the unit object path, and the job type that was
actually queued. Note that the order of tuples in the reply is unspecified and may not
correspond to the order of the input array.</para>
<para><function>EnqueueMarkedJobs()</function> creates reload/restart jobs for units which have been
appropriately marked, see <varname>Markers</varname> property above. This is equivalent to calling
<function>TryRestartUnit()</function> or <function>ReloadOrTryRestartUnit()</function> for the marked
@@ -12764,7 +12800,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>ReloadCount</varname>,
<varname>EventLoopRateLimitIntervalUSec</varname>, and
<varname>EventLoopRateLimitBurst</varname> were added in version 261.</para>
<para><varname>KExecsCount</varname> was added in version 262.</para>
<para><varname>KExecsCount</varname>, and
<function>EnqueueUnitJobMany()</function> were added in version 262.</para>
</refsect2>
<refsect2>
<title>Unit Objects</title>
+7
View File
@@ -418,6 +418,13 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>Start (activate) one or more units specified on the command line.</para>
<para>When more than one unit is specified, the units are enqueued in a single job transaction.
This ensures that ordering dependencies (<varname>After=</varname>/<varname>Before=</varname>)
configured between the listed units are honored, regardless of the order in which they are given
on the command line. The same applies to <command>stop</command>, <command>restart</command> and
the reload/restart variants below. If a single transaction cannot be put together, separate
transactions will be submitted separately instead as a fallback.</para>
<para>Note that unit glob patterns expand to names of units currently in memory. Units which are
not active and are not in a failed state usually are not in memory, and will not be matched by
any pattern. In addition, in case of instantiated units, systemd is often unaware of the instance