diff options
Diffstat (limited to 'src/run/run.c')
-rw-r--r-- | src/run/run.c | 130 |
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) { |