core/transaction: add job mode "lenient" as an even weaker version of _FAIL

This commit is contained in:
Mike Yuan
2025-05-18 19:54:51 +02:00
parent 286c61732f
commit 1b9400f2ee
4 changed files with 30 additions and 12 deletions

View File

@@ -2197,6 +2197,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>When queuing a new job, this option controls how to deal with
already queued jobs. It takes one of <literal>fail</literal>,
<literal>lenient</literal>,
<literal>replace</literal>,
<literal>replace-irreversibly</literal>,
<literal>isolate</literal>,
@@ -2209,10 +2210,12 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<command>isolate</command> command is used which implies the
<literal>isolate</literal> job mode.</para>
<para>If <literal>fail</literal> is specified and a requested
operation conflicts with a pending job (more specifically:
causes an already pending start job to be reversed into a stop
job or vice versa), cause the operation to fail.</para>
<para>If <literal>fail</literal> is specified and a requested operation on weak dependencies
conflicts with a pending job (more specifically: causes an already pending start job to be reversed
into a stop job or vice versa), cause the operation to fail.</para>
<para>If <literal>lenient</literal> is specified and a requested operation conflicts with any
active/activating unit, cause the operation to fail.</para>
<para>If <literal>replace</literal> (the default) is
specified, any conflicting pending job will be replaced, as

View File

@@ -341,6 +341,7 @@ DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_LENIENT] = "lenient",
[JOB_REPLACE] = "replace",
[JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",

View File

@@ -282,6 +282,7 @@ typedef enum NotifyAccess {
typedef enum JobMode {
JOB_FAIL, /* Fail if a conflicting job is already queued */
JOB_LENIENT, /* Fail if any conflicting unit is active (even weaker than JOB_FAIL) */
JOB_REPLACE, /* Replace an existing conflicting job */
JOB_REPLACE_IRREVERSIBLY, /* Like JOB_REPLACE + produce irreversible jobs */
JOB_ISOLATE, /* Start a unit, and stop all others */

View File

@@ -565,7 +565,7 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
assert(!j->transaction_prev);
assert(!j->transaction_next);
if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
if (j->unit->job && (IN_SET(mode, JOB_FAIL, JOB_LENIENT) || j->unit->job->irreversible) &&
job_type_is_conflicting(j->unit->job->type, j->type))
return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
"Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
@@ -576,7 +576,7 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
return 0;
}
static void transaction_minimize_impact(Transaction *tr) {
static int transaction_minimize_impact(Transaction *tr, JobMode mode, sd_bus_error *e) {
Job *head;
assert(tr);
@@ -584,13 +584,16 @@ static void transaction_minimize_impact(Transaction *tr) {
/* Drops all unnecessary jobs that reverse already active jobs
* or that stop a running service. */
if (!IN_SET(mode, JOB_FAIL, JOB_LENIENT))
return 0;
rescan:
HASHMAP_FOREACH(head, tr->jobs) {
LIST_FOREACH(transaction, j, head) {
bool stops_running_service, changes_existing_job;
/* If it matters, we shouldn't drop it */
if (j->matters_to_anchor)
if (j->matters_to_anchor && mode != JOB_LENIENT)
continue;
/* Would this stop a running service?
@@ -607,6 +610,13 @@ rescan:
if (!stops_running_service && !changes_existing_job)
continue;
if (j->matters_to_anchor) {
assert(mode == JOB_LENIENT);
return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
"%s/%s would stop a running unit or change existing job, bailing",
j->unit->id, job_type_to_string(j->type));
}
if (stops_running_service)
log_unit_debug(j->unit,
"%s/%s would stop a running service.",
@@ -626,6 +636,8 @@ rescan:
goto rescan;
}
}
return 0;
}
static int transaction_apply(
@@ -734,11 +746,12 @@ int transaction_activate(
/* First step: figure out which jobs matter */
transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
/* Second step: Try not to stop any running services if
* we don't have to. Don't try to reverse running
* jobs if we don't have to. */
if (mode == JOB_FAIL)
transaction_minimize_impact(tr);
/* Second step: Try not to stop any running services if we don't have to. Don't try to reverse
* running jobs if we don't have to. */
r = transaction_minimize_impact(tr, mode, e);
if (r < 0)
return r; /* Note that we don't log here, because for JOB_LENIENT conflicts are very much expected
and shouldn't appear to be fatal for the unit. Only inform the caller via bus error. */
/* Third step: Drop redundant jobs */
transaction_drop_redundant(tr);