1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-14 04:58:28 +03:00

run: add --job-mode= argument (#34708)

systemctl has a --job-mode= argument, and adding the same argument to
systemd-run is useful for starting transient scopes with dependencies.
For example, if a transient scope BindsTo a service that is stopping,
specifying --job-mode=replace will wait for the service to stop before
starting it again, while the default job mode of "fail" will cause the
systemd-run invocation to fail.
This commit is contained in:
Yu Watanabe 2025-01-23 18:11:30 +09:00 committed by GitHub
commit 90cf374e45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 92 additions and 36 deletions

View File

@ -484,6 +484,23 @@
<xi:include href="version-info.xml" xpointer="v236"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--job-mode=<replaceable>MODE</replaceable></option></term>
<listitem>
<para>When queuing a new job, this option controls how to deal with
already queued jobs.</para>
<para>The option takes the same mode values as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<option>--job-mode=</option> option. The default job mode is <literal>fail</literal>.</para>
<para>Running <option>--job-mode=help</option> shows a list of available job modes.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--ignore-failure</option></term>

View File

@ -38,7 +38,7 @@ _systemd_run() {
--unit -p --property --slice --description --service-type --uid --gid --nice --working-directory
-E --setenv --on-active --on-boot --on-startup --on-unit-active --on-unit-inactive --on-calendar
--path-property --socket-property --timer-property -H --host -M --machine --expand-environment
--background --json
--background --json --job-mode
)
local OPTS="${opts_with_values[*]} --no-ask-password --scope -u --slice-inherit -r --remain-after-exit
--send-sighup -d --same-dir -t --pty -P --pipe -S --shell -q --quiet --ignore-failure
@ -131,6 +131,11 @@ _systemd_run() {
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
;;
--job-mode)
local comps=$( systemd-run --job-mode=help 2>/dev/null )
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
;;
esac
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )

View File

@ -22,6 +22,13 @@ __systemctl() {
_describe 'slices' _slices
}
(( $+functions[_systemd-run_job_modes] )) ||
_systemd-run_job_modes() {
local -a _modes
_modes=($( systemd-run --job-mode=help 2>/dev/null ))
_values "${_modes[@]}"
}
_arguments \
'(-G --collect)'{-G,--collect}'[Unload the transient unit after it completed]' \
'--description=[Description for unit]:description' \
@ -60,6 +67,7 @@ _arguments \
'(-t --pty)'{-t,--pty}'[The service connects to the terminal]' \
'(-q --quiet)'{-q,--quiet}'[Suppresses additional informational output]' \
'--json=[Output as JSON]:JSON:(pretty short off)' \
'--job-mode=[Specify how to deal with other jobs]:mode:_systemd-run_job_modes' \
'(-r --remain-after-exit)'{-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \
'(-d --same-dir)'{-d,--same-dir}'[Run on the current working directory]' \
'--scope[Run this as scope rather than service]' \

View File

@ -344,6 +344,20 @@ static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
[JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_FLUSH] = "flush",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
[JOB_TRIGGERING] = "triggering",
[JOB_RESTART_DEPENDENCIES] = "restart-dependencies",
};
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
SpecialGlyph unit_active_state_to_glyph(UnitActiveState state) {
static const SpecialGlyph map[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = SPECIAL_GLYPH_BLACK_CIRCLE,

View File

@ -283,6 +283,20 @@ typedef enum NotifyAccess {
_NOTIFY_ACCESS_INVALID = -EINVAL,
} NotifyAccess;
typedef enum JobMode {
JOB_FAIL, /* Fail if a conflicting job is already queued */
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 */
JOB_FLUSH, /* Flush out all other queued jobs when queueing this one */
JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
JOB_TRIGGERING, /* Adds TRIGGERED_BY dependencies to the same transaction */
JOB_RESTART_DEPENDENCIES,/* A "start" job for the specified unit becomes "restart" for depending units */
_JOB_MODE_MAX,
_JOB_MODE_INVALID = -EINVAL,
} JobMode;
char* unit_dbus_path_from_name(const char *name);
int unit_name_from_dbus_path(const char *path, char **name);
@ -346,4 +360,7 @@ UnitDependency unit_dependency_from_string(const char *s) _pure_;
const char* notify_access_to_string(NotifyAccess i) _const_;
NotifyAccess notify_access_from_string(const char *s) _pure_;
const char* job_mode_to_string(JobMode t) _const_;
JobMode job_mode_from_string(const char *s) _pure_;
SpecialGlyph unit_active_state_to_glyph(UnitActiveState state);

View File

@ -1650,20 +1650,6 @@ static const char* const job_type_table[_JOB_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
[JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_FLUSH] = "flush",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
[JOB_TRIGGERING] = "triggering",
[JOB_RESTART_DEPENDENCIES] = "restart-dependencies",
};
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
static const char* const job_result_table[_JOB_RESULT_MAX] = {
[JOB_DONE] = "done",
[JOB_CANCELED] = "canceled",

View File

@ -14,7 +14,6 @@ typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef enum JobType JobType;
typedef enum JobState JobState;
typedef enum JobMode JobMode;
typedef enum JobResult JobResult;
typedef struct Manager Manager;
typedef struct Unit Unit;
@ -71,20 +70,6 @@ enum JobState {
_JOB_STATE_INVALID = -EINVAL,
};
enum JobMode {
JOB_FAIL, /* Fail if a conflicting job is already queued */
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 */
JOB_FLUSH, /* Flush out all other queued jobs when queueing this one */
JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
JOB_TRIGGERING, /* Adds TRIGGERED_BY dependencies to the same transaction */
JOB_RESTART_DEPENDENCIES,/* A "start" job for the specified unit becomes "restart" for depending units */
_JOB_MODE_MAX,
_JOB_MODE_INVALID = -EINVAL,
};
enum JobResult {
JOB_DONE, /* Job completed successfully (or skipped due to an unmet ConditionXYZ=) */
JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */
@ -243,9 +228,6 @@ JobType job_type_from_string(const char *s) _pure_;
const char* job_state_to_string(JobState t) _const_;
JobState job_state_from_string(const char *s) _pure_;
const char* job_mode_to_string(JobMode t) _const_;
JobMode job_mode_from_string(const char *s) _pure_;
const char* job_result_to_string(JobResult t) _const_;
JobResult job_result_from_string(const char *s) _pure_;

View File

@ -40,6 +40,7 @@
#include "ptyfwd.h"
#include "signal-util.h"
#include "special.h"
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
#include "uid-classification.h"
@ -82,6 +83,7 @@ static bool arg_quiet = false;
static bool arg_aggressive_gc = false;
static char *arg_working_directory = NULL;
static bool arg_shell = false;
static JobMode arg_job_mode = JOB_FAIL;
static char **arg_cmdline = NULL;
static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
@ -143,6 +145,8 @@ static int help(void) {
" --json=pretty|short|off Print unit name and invocation id as JSON\n"
" -G --collect Unload unit after it ran, even when failed\n"
" -S --shell Invoke a $SHELL interactively\n"
" --job-mode=MODE Specify how to deal with already queued jobs,\n"
" when queueing a new job\n"
" --ignore-failure Ignore the exit status of the invoked process\n"
" --background=COLOR Set ANSI color for background\n"
"\n%3$sPath options:%4$s\n"
@ -278,6 +282,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_WAIT,
ARG_WORKING_DIRECTORY,
ARG_SHELL,
ARG_JOB_MODE,
ARG_IGNORE_FAILURE,
ARG_BACKGROUND,
ARG_JSON,
@ -327,6 +332,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
{ "same-dir", no_argument, NULL, 'd' },
{ "shell", no_argument, NULL, 'S' },
{ "job-mode", required_argument, NULL, ARG_JOB_MODE },
{ "ignore-failure", no_argument, NULL, ARG_IGNORE_FAILURE },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "json", required_argument, NULL, ARG_JSON },
@ -621,6 +627,17 @@ static int parse_argv(int argc, char *argv[]) {
arg_shell = true;
break;
case ARG_JOB_MODE:
if (streq(optarg, "help"))
return DUMP_STRING_TABLE(job_mode, JobMode, _JOB_MODE_MAX);
r = job_mode_from_string(optarg);
if (r < 0)
return log_error_errno(r, "Invalid job mode: %s", optarg);
arg_job_mode = r;
break;
case ARG_IGNORE_FAILURE:
arg_ignore_failure = true;
break;
@ -1768,7 +1785,7 @@ static int make_transient_service_unit(
return bus_log_create_error(r);
/* Name and mode */
r = sd_bus_message_append(m, "ss", service, "fail");
r = sd_bus_message_append(m, "ss", service, job_mode_to_string(arg_job_mode));
if (r < 0)
return bus_log_create_error(r);
@ -2283,7 +2300,7 @@ static int start_transient_scope(sd_bus *bus) {
return bus_log_create_error(r);
/* Name and Mode */
r = sd_bus_message_append(m, "ss", scope, "fail");
r = sd_bus_message_append(m, "ss", scope, job_mode_to_string(arg_job_mode));
if (r < 0)
return bus_log_create_error(r);
@ -2452,7 +2469,7 @@ static int make_transient_trigger_unit(
return bus_log_create_error(r);
/* Name and Mode */
r = sd_bus_message_append(m, "ss", trigger, "fail");
r = sd_bus_message_append(m, "ss", trigger, job_mode_to_string(arg_job_mode));
if (r < 0)
return bus_log_create_error(r);

View File

@ -200,6 +200,16 @@ grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
systemctl stop "$UNIT.socket" "$UNIT.service" || :
: "Job mode"
systemd-run --job-mode=help
(! systemd-run --job-mode=foo --scope true)
systemd-run --no-block --unit=slowly-activating.service --collect \
--service-type=oneshot --remain-after-exit \
sleep 30
(! systemd-run --scope --property=Conflicts=slowly-activating.service true)
(! systemd-run --scope --property=Conflicts=slowly-activating.service --job-mode=fail true)
systemd-run --scope --property=Conflicts=slowly-activating.service --job-mode=replace true
: "Interactive options"
SHELL=/bin/true systemd-run --shell
SHELL=/bin/true systemd-run --scope --shell