diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 386702ac40..e9c6384363 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -216,7 +216,9 @@
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
RemainAfterExit= in
- systemd.service5.
+ systemd.service5. This
+ option may not be combined with , , ,
+ , or .
@@ -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 .
- This option may not be combined with , or the various
- path, socket, or timer options.
+ This option may not be combined with ,
+ , or the various path, socket, or timer
+ options.
@@ -575,7 +578,7 @@
By default, if the specified command fails the invoked unit will be marked failed
(though possibly still unloaded, see , 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.
+ command is treated as success. This option is not supported in mode.
@@ -616,6 +619,10 @@
+ may not be combined with ,
+ , , ,
+ , or path, socket, or timer options.
+
All command line arguments after the first non-option argument become part of the command line of
the launched process.
diff --git a/src/run/run.c b/src/run/run.c
index 63c0718ad9..85256a2b1b 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -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);
diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh
index bdb0fd1a7e..1c0efdf08f 100755
--- a/test/units/TEST-74-AUX-UTILS.run.sh
+++ b/test/units/TEST-74-AUX-UTILS.run.sh
@@ -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 '[[ "$(/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)