summaryrefslogtreecommitdiffstats
path: root/src/run/run.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/run/run.c')
-rw-r--r--src/run/run.c130
1 files changed, 114 insertions, 16 deletions
diff --git a/src/run/run.c b/src/run/run.c
index 3f7e0a6360..7538029548 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -88,6 +88,7 @@ static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
+static int arg_lightweight = -1;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@@ -199,6 +200,8 @@ static int help_sudo_mode(void) {
" --pty Request allocation of a pseudo TTY for stdio\n"
" --pipe Request direct pipe for stdio\n"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
+ " --lightweight=BOOLEAN Control whether to register a session with service manager\n"
+ " or without\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -778,6 +781,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
+ ARG_LIGHTWEIGHT,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@@ -802,6 +806,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "pty", no_argument, NULL, ARG_PTY },
{ "pipe", no_argument, NULL, ARG_PIPE },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
+ { "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT },
{},
};
@@ -914,6 +919,12 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
return r;
break;
+ case ARG_LIGHTWEIGHT:
+ r = parse_tristate_argument("--lightweight=", optarg, &arg_lightweight);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
@@ -947,6 +958,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO))
arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
+ log_debug("Using %s stdio mode.", arg_stdio == ARG_STDIO_PTY ? "pty" : "direct");
+
arg_expand_environment = false;
arg_send_sighup = true;
@@ -1045,6 +1058,28 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
+ /* When using run0 to acquire privileges temporarily, let's not pull in session manager by
+ * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
+ * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
+ * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
+ * this for root though, under the assumption that if a regular user temporarily transitions into
+ * another regular user it's a better default that the full user environment is uniformly
+ * available. */
+ if (arg_lightweight < 0 && !strv_env_get(arg_environment, "XDG_SESSION_CLASS") && privileged_execution())
+ arg_lightweight = true;
+
+ if (arg_lightweight >= 0) {
+ const char *class =
+ arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
+ (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background");
+
+ log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
+
+ r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
+ }
+
return 1;
}
@@ -1901,6 +1936,46 @@ static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) {
return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
}
+typedef struct JobDoneContext {
+ char *unit;
+ char *start_job;
+ sd_bus_slot *match;
+} JobDoneContext;
+
+static void job_done_context_done(JobDoneContext *c) {
+ assert(c);
+
+ c->unit = mfree(c->unit);
+ c->start_job = mfree(c->start_job);
+ c->match = sd_bus_slot_unref(c->match);
+}
+
+static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ JobDoneContext *c = ASSERT_PTR(userdata);
+ const char *path;
+ int r;
+
+ assert(m);
+
+ r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, /* unit= */ NULL, /* result= */ NULL);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ if (!streq_ptr(path, c->start_job))
+ return 0;
+
+ /* Notify our caller that the service is now running, just in case. */
+ (void) sd_notifyf(/* unset_environment= */ false,
+ "READY=1\n"
+ "RUN_UNIT=%s",
+ c->unit);
+
+ job_done_context_done(c);
+ return 0;
+}
+
static int start_transient_service(sd_bus *bus) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1974,16 +2049,6 @@ static int start_transient_service(sd_bus *bus) {
assert_not_reached();
}
- /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
- * lets skip this however, because we should start that already when the start job is running, and
- * there's little point in waiting for the start job to complete in that case anyway, as we'll wait
- * for EOF anyway, which is going to be much later. */
- if (!arg_no_block && arg_stdio == ARG_STDIO_NONE) {
- r = bus_wait_for_jobs_new(bus, &w);
- if (r < 0)
- return log_error_errno(r, "Could not watch jobs: %m");
- }
-
if (arg_unit) {
r = unit_name_mangle_with_suffix(arg_unit, "as unit",
arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
@@ -1996,6 +2061,36 @@ static int start_transient_service(sd_bus *bus) {
return r;
}
+ /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
+ * lets skip this however, because we should start that already when the start job is running, and
+ * there's little point in waiting for the start job to complete in that case anyway, as we'll wait
+ * for EOF anyway, which is going to be much later. */
+ _cleanup_(job_done_context_done) JobDoneContext job_done_context = {};
+ if (!arg_no_block) {
+ if (arg_stdio == ARG_STDIO_NONE) {
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ } else {
+ job_done_context.unit = strdup(service);
+ if (!job_done_context.unit)
+ return log_oom();
+
+ /* When we are a bus client we match by sender. Direct connections OTOH have no
+ * initialized sender field, and hence we ignore the sender then */
+ r = sd_bus_match_signal_async(
+ bus,
+ &job_done_context.match,
+ sd_bus_is_bus_client(bus) ? "org.freedesktop.systemd1" : NULL,
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "JobRemoved",
+ match_job_removed, NULL, &job_done_context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install JobRemove match: %m");
+ }
+ }
+
r = make_transient_service_unit(bus, &m, service, pty_path, peer_fd);
if (r < 0)
return r;
@@ -2005,19 +2100,22 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return r;
- if (w) {
- const char *object;
-
- r = sd_bus_message_read(reply, "o", &object);
- if (r < 0)
- return bus_log_parse_error(r);
+ const char *object;
+ r = sd_bus_message_read(reply, "o", &object);
+ if (r < 0)
+ return bus_log_parse_error(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;
+ } else if (job_done_context.match) {
+ job_done_context.start_job = strdup(object);
+ if (!job_done_context.start_job)
+ return log_oom();
}
if (!arg_quiet) {