systemd-run: reject unsupported option combinations (#43003)
Boundary tests were conducted on the "run" command tool, and the identified issues were resolved.
This commit is contained in:
+11
-4
@@ -216,7 +216,9 @@
|
||||
<listitem><para>After the service process has terminated, keep the service around until it is explicitly
|
||||
stopped. This is useful to collect runtime information about the service after it finished running. Also see
|
||||
<varname>RemainAfterExit=</varname> in
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
|
||||
option may not be combined with <option>--wait</option>, <option>--scope</option>, <option>--pty</option>,
|
||||
<option>--pty-late</option>, or <option>--pipe</option>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v207"/>
|
||||
@@ -529,8 +531,9 @@
|
||||
completed). On exit, terse information about the unit's runtime is shown, including total runtime (as well as
|
||||
CPU, memory, IO, and IP accounting data, if the corresponding cgroup accounting settings are enabled)
|
||||
and the exit code and status of the main process. This output may be suppressed with <option>--quiet</option>.
|
||||
This option may not be combined with <option>--no-block</option>, <option>--scope</option> or the various
|
||||
path, socket, or timer options.</para>
|
||||
This option may not be combined with <option>--no-block</option>,
|
||||
<option>--remain-after-exit</option>, <option>--scope</option> or the various path, socket, or timer
|
||||
options.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v232"/></listitem>
|
||||
</varlistentry>
|
||||
@@ -575,7 +578,7 @@
|
||||
<listitem><para>By default, if the specified command fails the invoked unit will be marked failed
|
||||
(though possibly still unloaded, see <option>--collect=</option>, above), and this is reported in the
|
||||
logs. If this switch is specified this is suppressed and any non-success exit status/code of the
|
||||
command is treated as success.</para>
|
||||
command is treated as success. This option is not supported in <option>--scope</option> mode.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
@@ -616,6 +619,10 @@
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para><option>--json=</option> may not be combined with <option>--scope</option>,
|
||||
<option>--pty</option>, <option>--pty-late</option>, <option>--pipe</option>,
|
||||
<option>--verbose</option>, or path, socket, or timer options.</para>
|
||||
|
||||
<para>All command line arguments after the first non-option argument become part of the command line of
|
||||
the launched process.</para>
|
||||
</refsect1>
|
||||
|
||||
+88
-23
@@ -697,9 +697,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Scope execution is not supported on remote systems.");
|
||||
|
||||
if (arg_scope && (arg_remain_after_exit || arg_service_type))
|
||||
if (arg_scope && (arg_remain_after_exit || arg_service_type || arg_ignore_failure))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--remain-after-exit and --service-type= are not supported in --scope mode.");
|
||||
"--remain-after-exit, --service-type= and --ignore-failure are not supported in --scope mode.");
|
||||
|
||||
if (arg_scope && arg_no_block)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
@@ -717,6 +717,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_no_block)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--pty/--pty-late/--pipe is not compatible with --no-block.");
|
||||
|
||||
if (arg_remain_after_exit)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--pty/--pty-late/--pipe is not compatible with --remain-after-exit.");
|
||||
}
|
||||
|
||||
if (arg_stdio == ARG_STDIO_PTY && arg_pty_late && streq_ptr(arg_service_type, "oneshot"))
|
||||
@@ -727,6 +731,24 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Path, socket or timer options are not supported in --scope mode.");
|
||||
|
||||
if (sd_json_format_enabled(arg_json_format_flags)) {
|
||||
if (arg_scope)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--json= is not supported in --scope mode.");
|
||||
|
||||
if (arg_stdio != ARG_STDIO_NONE)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--json= is not compatible with --pty/--pty-late/--pipe.");
|
||||
|
||||
if (arg_verbose)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--json= is not compatible with --verbose.");
|
||||
|
||||
if (with_trigger)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--json= is not compatible with path, socket or timer operations.");
|
||||
}
|
||||
|
||||
if (arg_timer_property && !arg_with_timer)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--timer-property= has no effect without any other timer options.");
|
||||
@@ -743,6 +765,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_scope)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--wait may not be combined with --scope.");
|
||||
|
||||
if (arg_remain_after_exit)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--wait may not be combined with --remain-after-exit.");
|
||||
}
|
||||
|
||||
if (arg_scope && arg_root_directory)
|
||||
@@ -2517,6 +2543,17 @@ static int start_transient_service(sd_bus *bus) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int log_scope_group_setup_errno(int r, gid_t gid, const char *message) {
|
||||
assert(r < 0);
|
||||
assert(message);
|
||||
|
||||
if (!ERRNO_IS_PRIVILEGE(r) || gid != getgid())
|
||||
return log_error_errno(r, "%s: %m", message);
|
||||
|
||||
log_debug_errno(r, "%s, ignoring: %m", message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_transient_scope(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
|
||||
@@ -2623,26 +2660,22 @@ static int start_transient_scope(sd_bus *bus) {
|
||||
return log_error_errno(errno, "Failed to set nice level: %m");
|
||||
}
|
||||
|
||||
gid_t gid = GID_INVALID;
|
||||
if (arg_exec_group) {
|
||||
gid_t gid;
|
||||
|
||||
r = get_group_creds(arg_exec_group, /* flags= */ 0, /* ret_name= */ NULL, &gid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve group '%s': %s",
|
||||
arg_exec_group, STRERROR_GROUP(r));
|
||||
|
||||
if (setresgid(gid, gid, gid) < 0)
|
||||
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
|
||||
}
|
||||
|
||||
uid_t uid = UID_INVALID;
|
||||
if (arg_exec_user) {
|
||||
_cleanup_free_ char *user = NULL, *home = NULL, *shell = NULL;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
gid_t user_gid;
|
||||
|
||||
r = get_user_creds(arg_exec_user,
|
||||
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS,
|
||||
&user, &uid, &gid, &home, &shell);
|
||||
&user, &uid, &user_gid, &home, &shell);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve user '%s': %s",
|
||||
arg_exec_user, STRERROR_USER(r));
|
||||
@@ -2669,14 +2702,33 @@ static int start_transient_scope(sd_bus *bus) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (!arg_exec_group &&
|
||||
setresgid(gid, gid, gid) < 0)
|
||||
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
|
||||
if (!gid_is_valid(gid))
|
||||
gid = user_gid;
|
||||
|
||||
if (setresuid(uid, uid, uid) < 0)
|
||||
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
|
||||
r = initgroups_wrapper(arg_exec_user, gid);
|
||||
if (r < 0) {
|
||||
r = log_scope_group_setup_errno(
|
||||
r,
|
||||
gid,
|
||||
strjoina("Failed to initialize supplementary groups for user '", arg_exec_user, "'"));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else if (gid_is_valid(gid)) {
|
||||
r = maybe_setgroups(/* size= */ 0, /* list= */ NULL);
|
||||
if (r < 0) {
|
||||
r = log_scope_group_setup_errno(r, gid, "Failed to drop supplementary groups");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (gid_is_valid(gid) && setresgid(gid, gid, gid) < 0)
|
||||
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
|
||||
|
||||
if (uid_is_valid(uid) && setresuid(uid, uid, uid) < 0)
|
||||
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
|
||||
|
||||
if (arg_working_directory && chdir(arg_working_directory) < 0)
|
||||
return log_error_errno(errno, "Failed to change directory to '%s': %m", arg_working_directory);
|
||||
|
||||
@@ -2808,12 +2860,16 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
|
||||
assert(bus);
|
||||
assert(suffix);
|
||||
|
||||
r = bus_wait_for_jobs_new(bus, &w);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not watch jobs: %m");
|
||||
if (!arg_no_block) {
|
||||
r = bus_wait_for_jobs_new(bus, &w);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not watch jobs: %m");
|
||||
}
|
||||
|
||||
if (arg_unit) {
|
||||
switch (unit_name_to_type(arg_unit)) {
|
||||
UnitType t = unit_name_to_type(arg_unit);
|
||||
|
||||
switch (t) {
|
||||
|
||||
case UNIT_SERVICE:
|
||||
service = strdup(arg_unit);
|
||||
@@ -2825,7 +2881,14 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
|
||||
return log_error_errno(r, "Failed to change unit suffix: %m");
|
||||
break;
|
||||
|
||||
case UNIT_PATH:
|
||||
case UNIT_SOCKET:
|
||||
case UNIT_TIMER:
|
||||
if (!streq(suffix + 1, unit_type_to_string(t)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Specified unit '%s' does not match requested %s trigger.",
|
||||
arg_unit, suffix + 1);
|
||||
|
||||
trigger = strdup(arg_unit);
|
||||
if (!trigger)
|
||||
return log_oom();
|
||||
@@ -2875,10 +2938,12 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
|
||||
arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (w) {
|
||||
r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
|
||||
arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!arg_quiet) {
|
||||
log_info("Running %s as unit: %s", suffix + 1, trigger);
|
||||
|
||||
@@ -72,6 +72,8 @@ systemctl cat "$UNIT"
|
||||
grep -q "^Type=oneshot" "/run/systemd/transient/$UNIT.service"
|
||||
systemctl stop "$UNIT"
|
||||
(! systemctl cat "$UNIT")
|
||||
(! systemd-run --wait --remain-after-exit true)
|
||||
(! systemd-run --pipe --remain-after-exit true)
|
||||
|
||||
: "Transient service (user daemon)"
|
||||
systemd-run --wait --pipe --user --machine=testuser@ \
|
||||
@@ -111,10 +113,16 @@ systemd-run --scope --slice-inherit --slice=foo \
|
||||
bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
|
||||
# We should inherit caller's environment
|
||||
systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
|
||||
systemd-run --scope --uid=testuser \
|
||||
bash -xec '[[ " $(id -G) " != *" 0 "* ]]'
|
||||
systemd-run --scope --gid=testuser \
|
||||
bash -xec '[[ " $(id -G) " != *" 0 "* ]]'
|
||||
systemd-run --scope \
|
||||
--property=RuntimeMaxSec=10 \
|
||||
--property=RuntimeMaxSec=infinity \
|
||||
true
|
||||
(! systemd-run --scope --ignore-failure false)
|
||||
(! systemd-run --scope --json=short true)
|
||||
|
||||
: "Transient scope (user daemon)"
|
||||
# FIXME: https://github.com/systemd/systemd/issues/27883
|
||||
@@ -188,6 +196,17 @@ grep -q "^PathChanged=/root/bar$" "/run/systemd/transient/$UNIT.path"
|
||||
grep -qE "^ExecStart=.*true.*$" "/run/systemd/transient/$UNIT.service"
|
||||
systemctl stop "$UNIT.path" "$UNIT.service" || :
|
||||
|
||||
UNIT="path-explicit-$RANDOM.path"
|
||||
SERVICE="${UNIT%.path}.service"
|
||||
systemd-run --remain-after-exit \
|
||||
--unit="$UNIT" \
|
||||
--path-property=PathExists=/tmp \
|
||||
true
|
||||
systemctl cat "$UNIT" "$SERVICE"
|
||||
grep -q "^PathExists=/tmp$" "/run/systemd/transient/$UNIT"
|
||||
grep -qE "^ExecStart=.*true.*$" "/run/systemd/transient/$SERVICE"
|
||||
systemctl stop "$UNIT" "$SERVICE" || :
|
||||
|
||||
: "Transient socket unit"
|
||||
UNIT="socket-0-$RANDOM"
|
||||
systemd-run --remain-after-exit \
|
||||
@@ -205,6 +224,24 @@ grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
|
||||
grep -qE "^ExecStart=.*true.*$" "/run/systemd/transient/$UNIT.service"
|
||||
systemctl stop "$UNIT.socket" "$UNIT.service" || :
|
||||
|
||||
UNIT="socket-explicit-$RANDOM.socket"
|
||||
SERVICE="${UNIT%.socket}.service"
|
||||
systemd-run --remain-after-exit \
|
||||
--unit="$UNIT" \
|
||||
--socket-property=ListenFIFO=/tmp/socket-explicit.fifo \
|
||||
true
|
||||
systemctl cat "$UNIT" "$SERVICE"
|
||||
grep -q "^ListenFIFO=/tmp/socket-explicit.fifo$" "/run/systemd/transient/$UNIT"
|
||||
grep -qE "^ExecStart=.*true.*$" "/run/systemd/transient/$SERVICE"
|
||||
systemctl stop "$UNIT" "$SERVICE" || :
|
||||
|
||||
UNIT="socket-no-block-$RANDOM"
|
||||
systemd-run --no-block --collect \
|
||||
--unit="$UNIT" \
|
||||
--socket-property=ListenStream=/proc/systemd-run-repro/socket \
|
||||
true
|
||||
systemctl stop "$UNIT.socket" "$UNIT.service" || :
|
||||
|
||||
: "Job mode"
|
||||
systemd-run --job-mode=help
|
||||
(! systemd-run --job-mode=foo --scope true)
|
||||
@@ -222,6 +259,9 @@ systemd-run --wait --pty true
|
||||
systemd-run --wait --machine=.host --pty true
|
||||
systemd-run --json=short true | jq . >/dev/null
|
||||
systemd-run --json=pretty true | jq . >/dev/null
|
||||
(! systemd-run --wait --pipe --json=short true)
|
||||
(! systemd-run --json=short --on-active=30s true)
|
||||
(! systemd-run --wait --verbose --json=short true)
|
||||
(! SHELL=/bin/false systemd-run --quiet --shell)
|
||||
|
||||
(! systemd-run)
|
||||
|
||||
Reference in New Issue
Block a user