1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <sys/resource.h>
11 #include "sd-daemon.h"
15 #include "alloc-util.h"
16 #include "argv-util.h"
17 #include "ask-password-agent.h"
19 #include "bus-error.h"
20 #include "bus-locator.h"
21 #include "bus-map-properties.h"
22 #include "bus-message-util.h"
23 #include "bus-unit-util.h"
25 #include "bus-wait-for-jobs.h"
26 #include "calendarspec.h"
27 #include "capsule-util.h"
30 #include "errno-util.h"
32 #include "event-util.h"
33 #include "exec-util.h"
34 #include "exit-status.h"
36 #include "fork-journal.h"
37 #include "format-table.h"
38 #include "format-util.h"
40 #include "hostname-util.h"
42 #include "main-func.h"
43 #include "osc-context.h"
44 #include "parse-argument.h"
45 #include "parse-util.h"
46 #include "path-util.h"
48 #include "polkit-agent.h"
49 #include "pretty-print.h"
50 #include "process-util.h"
52 #include "runtime-scope.h"
53 #include "signal-util.h"
55 #include "string-table.h"
56 #include "string-util.h"
58 #include "terminal-util.h"
59 #include "time-util.h"
60 #include "uid-classification.h"
62 #include "unit-name.h"
63 #include "user-util.h"
65 static bool arg_ask_password
= true;
66 static bool arg_scope
= false;
67 static bool arg_remain_after_exit
= false;
68 static bool arg_no_block
= false;
69 static bool arg_wait
= false;
70 static const char *arg_unit
= NULL
;
71 static char *arg_description
= NULL
;
72 static const char *arg_slice
= NULL
;
73 static bool arg_slice_inherit
= false;
74 static bool arg_expand_environment
= true;
75 static bool arg_send_sighup
= false;
76 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
77 static const char *arg_host
= NULL
;
78 static RuntimeScope arg_runtime_scope
= RUNTIME_SCOPE_SYSTEM
;
79 static const char *arg_service_type
= NULL
;
80 static const char *arg_exec_user
= NULL
;
81 static const char *arg_exec_group
= NULL
;
82 static int arg_nice
= 0;
83 static bool arg_nice_set
= false;
84 static char **arg_environment
= NULL
;
85 static char **arg_property
= NULL
;
87 ARG_STDIO_NONE
= 0, /* The default, as it is for normal services, stdin connected to
88 * /dev/null, and stdout+stderr to the journal */
89 ARG_STDIO_PTY
= 1 << 0, /* Interactive behaviour, requested by --pty/--pty-late: we allocate a pty
90 * and connect it to the TTY we are invoked from */
91 ARG_STDIO_DIRECT
= 1 << 1, /* Directly pass our stdin/stdout/stderr to the activated service, useful
92 * for usage in shell pipelines, requested by --pipe */
93 ARG_STDIO_AUTO
= ARG_STDIO_PTY
| ARG_STDIO_DIRECT
,
94 /* If --pipe and --pty/--pty-late are used together we use --pty/--pty-late
95 * when invoked on a TTY, and --pipe otherwise */
96 } arg_stdio
= ARG_STDIO_NONE
;
97 static int arg_pty_late
= -1; /* tristate */
98 static char **arg_path_property
= NULL
;
99 static char **arg_socket_property
= NULL
;
100 static char **arg_timer_property
= NULL
;
101 static bool arg_with_timer
= false;
102 static bool arg_quiet
= false;
103 static bool arg_verbose
= false;
104 static bool arg_aggressive_gc
= false;
105 static char *arg_working_directory
= NULL
;
106 static bool arg_shell
= false;
107 static JobMode arg_job_mode
= JOB_FAIL
;
108 static char **arg_cmdline
= NULL
;
109 static char *arg_exec_path
= NULL
;
110 static bool arg_ignore_failure
= false;
111 static char *arg_background
= NULL
;
112 static sd_json_format_flags_t arg_json_format_flags
= SD_JSON_FORMAT_OFF
;
113 static char *arg_shell_prompt_prefix
= NULL
;
114 static int arg_lightweight
= -1;
115 static char *arg_area
= NULL
;
116 static bool arg_via_shell
= false;
118 STATIC_DESTRUCTOR_REGISTER(arg_description
, freep
);
119 STATIC_DESTRUCTOR_REGISTER(arg_environment
, strv_freep
);
120 STATIC_DESTRUCTOR_REGISTER(arg_property
, strv_freep
);
121 STATIC_DESTRUCTOR_REGISTER(arg_path_property
, strv_freep
);
122 STATIC_DESTRUCTOR_REGISTER(arg_socket_property
, strv_freep
);
123 STATIC_DESTRUCTOR_REGISTER(arg_timer_property
, strv_freep
);
124 STATIC_DESTRUCTOR_REGISTER(arg_working_directory
, freep
);
125 STATIC_DESTRUCTOR_REGISTER(arg_cmdline
, strv_freep
);
126 STATIC_DESTRUCTOR_REGISTER(arg_exec_path
, freep
);
127 STATIC_DESTRUCTOR_REGISTER(arg_background
, freep
);
128 STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix
, freep
);
129 STATIC_DESTRUCTOR_REGISTER(arg_area
, freep
);
131 static int help(void) {
132 _cleanup_free_
char *link
= NULL
;
135 r
= terminal_urlify_man("systemd-run", "1", &link
);
139 printf("%1$s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
140 "\n%5$sRun the specified command in a transient scope or service.%6$s\n\n"
141 " -h --help Show this help\n"
142 " --version Show package version\n"
143 " --no-ask-password Do not prompt for password\n"
144 " --user Run as user unit\n"
145 " -H --host=[USER@]HOST Operate on remote host\n"
146 " -M --machine=CONTAINER Operate on local container\n"
147 " --scope Run this as scope rather than service\n"
148 " -u --unit=UNIT Run under the specified unit name\n"
149 " -p --property=NAME=VALUE Set service or scope unit property\n"
150 " --description=TEXT Description for unit\n"
151 " --slice=SLICE Run in the specified slice\n"
152 " --slice-inherit Inherit the slice from the caller\n"
153 " --expand-environment=BOOL Control expansion of environment variables\n"
154 " --no-block Do not wait until operation finished\n"
155 " -r --remain-after-exit Leave service around until explicitly stopped\n"
156 " --wait Wait until service stopped again\n"
157 " --send-sighup Send SIGHUP when terminating\n"
158 " --service-type=TYPE Service type\n"
159 " --uid=USER Run as system user\n"
160 " --gid=GROUP Run as system group\n"
161 " --nice=NICE Nice level\n"
162 " --working-directory=PATH Set working directory\n"
163 " -d --same-dir Inherit working directory from caller\n"
164 " -E --setenv=NAME[=VALUE] Set environment variable\n"
165 " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
167 " -T --pty-late Just like --pty, but leave TTY access to\n"
168 " agents until unit is started up\n"
169 " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
170 " -q --quiet Suppress information messages during runtime\n"
171 " -v --verbose Show unit logs while executing operation\n"
172 " --json=pretty|short|off Print unit name and invocation id as JSON\n"
173 " -G --collect Unload unit after it ran, even when failed\n"
174 " -S --shell Invoke a $SHELL interactively\n"
175 " --job-mode=MODE Specify how to deal with already queued jobs,\n"
176 " when queueing a new job\n"
177 " --ignore-failure Ignore the exit status of the invoked process\n"
178 " --background=COLOR Set ANSI color for background\n"
179 "\n%3$sPath options:%4$s\n"
180 " --path-property=NAME=VALUE Set path unit property\n"
181 "\n%3$sSocket options:%4$s\n"
182 " --socket-property=NAME=VALUE Set socket unit property\n"
183 "\n%3$sTimer options:%4$s\n"
184 " --on-active=SECONDS Run after SECONDS delay\n"
185 " --on-boot=SECONDS Run SECONDS after machine was booted up\n"
186 " --on-startup=SECONDS Run SECONDS after systemd activation\n"
187 " --on-unit-active=SECONDS Run SECONDS after the last activation\n"
188 " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
189 " --on-calendar=SPEC Realtime timer\n"
190 " --on-timezone-change Run when the timezone changes\n"
191 " --on-clock-change Run when the realtime clock jumps\n"
192 " --timer-property=NAME=VALUE Set timer unit property\n"
193 "\nSee the %2$s for details.\n",
194 program_invocation_short_name
,
196 ansi_underline(), ansi_normal(),
197 ansi_highlight(), ansi_normal());
202 static int help_sudo_mode(void) {
203 _cleanup_free_
char *link
= NULL
;
206 r
= terminal_urlify_man("run0", "1", &link
);
210 /* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with
211 * sudo's short switches, hence please do not introduce new short switches unless they have a roughly
212 * equivalent purpose on sudo. Use long options for everything private to run0. */
214 printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
215 "\n%sElevate privileges interactively.%s\n\n"
216 " -h --help Show this help\n"
217 " -V --version Show package version\n"
218 " --no-ask-password Do not prompt for password\n"
219 " --machine=CONTAINER Operate on local container\n"
220 " --unit=UNIT Run under the specified unit name\n"
221 " --property=NAME=VALUE Set service or scope unit property\n"
222 " --description=TEXT Description for unit\n"
223 " --slice=SLICE Run in the specified slice\n"
224 " --slice-inherit Inherit the slice\n"
225 " -u --user=USER Run as system user\n"
226 " -g --group=GROUP Run as system group\n"
227 " --nice=NICE Nice level\n"
228 " -D --chdir=PATH Set working directory\n"
229 " --via-shell Invoke command via target user's login shell\n"
230 " -i Shortcut for --via-shell --chdir='~'\n"
231 " --setenv=NAME[=VALUE] Set environment variable\n"
232 " --background=COLOR Set ANSI color for background\n"
233 " --pty Request allocation of a pseudo TTY for stdio\n"
234 " --pty-late Just like --pty, but leave TTY access to agents\n"
235 " until unit is started up\n"
236 " --pipe Request direct pipe for stdio\n"
237 " --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
238 " --lightweight=BOOLEAN Control whether to register a session with service manager\n"
240 " --area=AREA Home area to log into\n"
241 "\nSee the %s for details.\n",
242 program_invocation_short_name
,
250 static bool privileged_execution(void) {
251 if (arg_runtime_scope
!= RUNTIME_SCOPE_SYSTEM
)
254 return !arg_exec_user
|| STR_IN_SET(arg_exec_user
, "root", "0");
257 static int add_timer_property(const char *name
, const char *val
) {
263 p
= strjoin(name
, "=", val
);
267 if (strv_consume(&arg_timer_property
, p
) < 0)
273 static char** make_login_shell_cmdline(const char *shell
) {
274 _cleanup_free_
char *argv0
= NULL
;
278 argv0
= strjoin("-", shell
); /* The - is how shells determine if they shall be consider login shells */
282 return strv_new(argv0
);
285 static int parse_argv(int argc
, char *argv
[]) {
295 ARG_EXPAND_ENVIRONMENT
,
305 ARG_ON_UNIT_INACTIVE
,
307 ARG_ON_TIMEZONE_CHANGE
,
315 ARG_WORKING_DIRECTORY
,
323 static const struct option options
[] = {
324 { "help", no_argument
, NULL
, 'h' },
325 { "version", no_argument
, NULL
, ARG_VERSION
},
326 { "user", no_argument
, NULL
, ARG_USER
},
327 { "system", no_argument
, NULL
, ARG_SYSTEM
},
328 { "capsule", required_argument
, NULL
, 'C' },
329 { "scope", no_argument
, NULL
, ARG_SCOPE
},
330 { "unit", required_argument
, NULL
, 'u' },
331 { "description", required_argument
, NULL
, ARG_DESCRIPTION
},
332 { "slice", required_argument
, NULL
, ARG_SLICE
},
333 { "slice-inherit", no_argument
, NULL
, ARG_SLICE_INHERIT
},
334 { "remain-after-exit", no_argument
, NULL
, 'r' },
335 { "expand-environment", required_argument
, NULL
, ARG_EXPAND_ENVIRONMENT
},
336 { "send-sighup", no_argument
, NULL
, ARG_SEND_SIGHUP
},
337 { "host", required_argument
, NULL
, 'H' },
338 { "machine", required_argument
, NULL
, 'M' },
339 { "service-type", required_argument
, NULL
, ARG_SERVICE_TYPE
},
340 { "wait", no_argument
, NULL
, ARG_WAIT
},
341 { "uid", required_argument
, NULL
, ARG_EXEC_USER
},
342 { "gid", required_argument
, NULL
, ARG_EXEC_GROUP
},
343 { "nice", required_argument
, NULL
, ARG_NICE
},
344 { "setenv", required_argument
, NULL
, 'E' },
345 { "property", required_argument
, NULL
, 'p' },
346 { "tty", no_argument
, NULL
, 't' }, /* deprecated alias */
347 { "pty", no_argument
, NULL
, 't' },
348 { "pty-late", no_argument
, NULL
, 'T' },
349 { "pipe", no_argument
, NULL
, 'P' },
350 { "quiet", no_argument
, NULL
, 'q' },
351 { "verbose", no_argument
, NULL
, 'v' },
352 { "on-active", required_argument
, NULL
, ARG_ON_ACTIVE
},
353 { "on-boot", required_argument
, NULL
, ARG_ON_BOOT
},
354 { "on-startup", required_argument
, NULL
, ARG_ON_STARTUP
},
355 { "on-unit-active", required_argument
, NULL
, ARG_ON_UNIT_ACTIVE
},
356 { "on-unit-inactive", required_argument
, NULL
, ARG_ON_UNIT_INACTIVE
},
357 { "on-calendar", required_argument
, NULL
, ARG_ON_CALENDAR
},
358 { "on-timezone-change", no_argument
, NULL
, ARG_ON_TIMEZONE_CHANGE
},
359 { "on-clock-change", no_argument
, NULL
, ARG_ON_CLOCK_CHANGE
},
360 { "timer-property", required_argument
, NULL
, ARG_TIMER_PROPERTY
},
361 { "path-property", required_argument
, NULL
, ARG_PATH_PROPERTY
},
362 { "socket-property", required_argument
, NULL
, ARG_SOCKET_PROPERTY
},
363 { "no-block", no_argument
, NULL
, ARG_NO_BLOCK
},
364 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
365 { "collect", no_argument
, NULL
, 'G' },
366 { "working-directory", required_argument
, NULL
, ARG_WORKING_DIRECTORY
},
367 { "same-dir", no_argument
, NULL
, 'd' },
368 { "shell", no_argument
, NULL
, 'S' },
369 { "job-mode", required_argument
, NULL
, ARG_JOB_MODE
},
370 { "ignore-failure", no_argument
, NULL
, ARG_IGNORE_FAILURE
},
371 { "background", required_argument
, NULL
, ARG_BACKGROUND
},
372 { "json", required_argument
, NULL
, ARG_JSON
},
376 bool with_trigger
= false;
382 /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
383 * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
385 while ((c
= getopt_long(argc
, argv
, "+hrC:H:M:E:p:tTPqvGdSu:", options
, NULL
)) >= 0)
395 case ARG_NO_ASK_PASSWORD
:
396 arg_ask_password
= false;
400 arg_runtime_scope
= RUNTIME_SCOPE_USER
;
404 arg_runtime_scope
= RUNTIME_SCOPE_SYSTEM
;
408 r
= capsule_name_is_valid(optarg
);
410 return log_error_errno(r
, "Unable to validate capsule name '%s': %m", optarg
);
412 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid capsule name: %s", optarg
);
415 arg_transport
= BUS_TRANSPORT_CAPSULE
;
416 arg_runtime_scope
= RUNTIME_SCOPE_USER
;
427 case ARG_DESCRIPTION
:
428 r
= free_and_strdup_warn(&arg_description
, optarg
);
437 case ARG_SLICE_INHERIT
:
438 arg_slice_inherit
= true;
441 case ARG_EXPAND_ENVIRONMENT
:
442 r
= parse_boolean_argument("--expand-environment=", optarg
, &arg_expand_environment
);
447 case ARG_SEND_SIGHUP
:
448 arg_send_sighup
= true;
452 arg_remain_after_exit
= true;
456 arg_transport
= BUS_TRANSPORT_REMOTE
;
461 r
= parse_machine_argument(optarg
, &arg_host
, &arg_transport
);
466 case ARG_SERVICE_TYPE
:
467 arg_service_type
= optarg
;
471 arg_exec_user
= optarg
;
475 arg_exec_group
= optarg
;
479 r
= parse_nice(optarg
, &arg_nice
);
481 return log_error_errno(r
, "Failed to parse nice value: %s", optarg
);
487 r
= strv_env_replace_strdup_passthrough(&arg_environment
, optarg
);
489 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
494 if (strv_extend(&arg_property
, optarg
) < 0)
499 case 'T': /* --pty-late */
500 case 't': /* --pty */
501 arg_stdio
|= ARG_STDIO_PTY
;
502 arg_pty_late
= c
== 'T';
505 case 'P': /* --pipe */
506 arg_stdio
|= ARG_STDIO_DIRECT
;
518 r
= add_timer_property("OnActiveSec", optarg
);
522 arg_with_timer
= true;
526 r
= add_timer_property("OnBootSec", optarg
);
530 arg_with_timer
= true;
534 r
= add_timer_property("OnStartupSec", optarg
);
538 arg_with_timer
= true;
541 case ARG_ON_UNIT_ACTIVE
:
542 r
= add_timer_property("OnUnitActiveSec", optarg
);
546 arg_with_timer
= true;
549 case ARG_ON_UNIT_INACTIVE
:
550 r
= add_timer_property("OnUnitInactiveSec", optarg
);
554 arg_with_timer
= true;
557 case ARG_ON_CALENDAR
: {
558 _cleanup_(calendar_spec_freep
) CalendarSpec
*cs
= NULL
;
560 r
= calendar_spec_from_string(optarg
, &cs
);
562 return log_error_errno(r
, "Failed to parse calendar event specification: %m");
564 /* Let's make sure the given calendar event is not in the past */
565 r
= calendar_spec_next_usec(cs
, now(CLOCK_REALTIME
), NULL
);
567 /* The calendar event is in the past — let's warn about this, but install it
568 * anyway as is. The service manager will trigger the service right away.
569 * Moreover, the server side might have a different clock or timezone than we
570 * do, hence it should decide when or whether to run something. */
571 log_warning("Specified calendar expression is in the past, proceeding anyway.");
573 return log_error_errno(r
, "Failed to calculate next time calendar expression elapses: %m");
575 r
= add_timer_property("OnCalendar", optarg
);
579 arg_with_timer
= true;
583 case ARG_ON_TIMEZONE_CHANGE
:
584 r
= add_timer_property("OnTimezoneChange", "yes");
588 arg_with_timer
= true;
591 case ARG_ON_CLOCK_CHANGE
:
592 r
= add_timer_property("OnClockChange", "yes");
596 arg_with_timer
= true;
599 case ARG_TIMER_PROPERTY
:
601 if (strv_extend(&arg_timer_property
, optarg
) < 0)
604 arg_with_timer
= arg_with_timer
||
605 STARTSWITH_SET(optarg
,
610 "OnUnitInactiveSec=",
614 case ARG_PATH_PROPERTY
:
616 if (strv_extend(&arg_path_property
, optarg
) < 0)
621 case ARG_SOCKET_PROPERTY
:
623 if (strv_extend(&arg_socket_property
, optarg
) < 0)
636 case ARG_WORKING_DIRECTORY
:
637 r
= parse_path_argument(optarg
, true, &arg_working_directory
);
644 _cleanup_free_
char *p
= NULL
;
648 return log_error_errno(r
, "Failed to get current working directory: %m");
650 if (empty_or_root(p
))
651 arg_working_directory
= mfree(arg_working_directory
);
653 free_and_replace(arg_working_directory
, p
);
658 arg_aggressive_gc
= true;
666 if (streq(optarg
, "help"))
667 return DUMP_STRING_TABLE(job_mode
, JobMode
, _JOB_MODE_MAX
);
669 r
= job_mode_from_string(optarg
);
671 return log_error_errno(r
, "Invalid job mode: %s", optarg
);
676 case ARG_IGNORE_FAILURE
:
677 arg_ignore_failure
= true;
681 r
= free_and_strdup_warn(&arg_background
, optarg
);
687 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
696 assert_not_reached();
699 /* If we are talking to the per-user instance PolicyKit isn't going to help */
700 if (arg_runtime_scope
== RUNTIME_SCOPE_USER
)
701 arg_ask_password
= false;
703 with_trigger
= !!arg_path_property
|| !!arg_socket_property
|| arg_with_timer
;
705 /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
706 if ((int) !!arg_path_property
+ (int) !!arg_socket_property
+ (int) arg_with_timer
> 1)
707 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
708 "Only single trigger (path, socket, timer) unit can be created.");
711 /* If --shell is imply --pty --pipe --same-dir --service-type=exec --wait --collect, unless otherwise
715 if (arg_stdio
== ARG_STDIO_NONE
)
716 arg_stdio
= ARG_STDIO_AUTO
;
718 if (!arg_working_directory
) {
719 r
= safe_getcwd(&arg_working_directory
);
721 return log_error_errno(r
, "Failed to get current working directory: %m");
724 if (!arg_service_type
)
725 arg_service_type
= "exec";
730 arg_aggressive_gc
= true;
733 if (arg_stdio
== ARG_STDIO_AUTO
)
734 /* If we both --pty/--pty-late and --pipe are specified we'll automatically pick --pty/--pty-late if we
735 * are connected fully to a TTY and pick direct fd passing otherwise. This way, we
736 * automatically adapt to usage in a shell pipeline, but we are neatly interactive with
737 * tty-level isolation otherwise. */
738 arg_stdio
= isatty_safe(STDIN_FILENO
) && isatty_safe(STDOUT_FILENO
) && isatty_safe(STDERR_FILENO
) ?
742 if (arg_pty_late
< 0)
743 arg_pty_late
= false; /* For systemd-run this defaults to false, for compat reasons */
749 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "If --shell is used, no command line is expected.");
751 l
= strv_copy(argv
+ optind
);
755 strv_free_and_replace(arg_cmdline
, l
);
757 } else if (arg_shell
) {
758 _cleanup_free_
char *s
= NULL
;
763 return log_error_errno(r
, "Failed to determine shell: %m");
769 strv_free_and_replace(arg_cmdline
, l
);
771 } else if (!arg_unit
|| !with_trigger
)
772 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Command line to execute required.");
774 if (arg_runtime_scope
== RUNTIME_SCOPE_USER
&& arg_transport
== BUS_TRANSPORT_REMOTE
)
775 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
776 "Execution in user context is not supported on remote systems.");
778 if (arg_scope
&& arg_transport
== BUS_TRANSPORT_REMOTE
)
779 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
780 "Scope execution is not supported on remote systems.");
782 if (arg_scope
&& (arg_remain_after_exit
|| arg_service_type
))
783 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
784 "--remain-after-exit and --service-type= are not supported in --scope mode.");
786 if (arg_stdio
!= ARG_STDIO_NONE
) {
787 if (with_trigger
|| arg_scope
)
788 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
789 "--pty/--pty-late/--pipe is not compatible in trigger (path/socket/timer units) or --scope mode.");
791 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
792 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
793 "--pty/--pty-late/--pipe is only supported when connecting to the local system or containers.");
796 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
797 "--pty/--pty-late/--pipe is not compatible with --no-block.");
800 if (arg_stdio
== ARG_STDIO_PTY
&& arg_pty_late
&& streq_ptr(arg_service_type
, "oneshot"))
801 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
802 "--pty-late is not compatible with --service-type=oneshot.");
804 if (arg_scope
&& with_trigger
)
805 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
806 "Path, socket or timer options are not supported in --scope mode.");
808 if (arg_timer_property
&& !arg_with_timer
)
809 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
810 "--timer-property= has no effect without any other timer options.");
814 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
815 "--wait may not be combined with --no-block.");
818 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
819 "--wait may not be combined with path, socket or timer operations.");
822 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
823 "--wait may not be combined with --scope.");
829 static int parse_argv_sudo_mode(int argc
, char *argv
[]) {
832 ARG_NO_ASK_PASSWORD
= 0x100,
846 ARG_SHELL_PROMPT_PREFIX
,
852 /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
853 * though (but limit the extension to long options). */
855 static const struct option options
[] = {
856 { "help", no_argument
, NULL
, 'h' },
857 { "version", no_argument
, NULL
, 'V' },
858 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
859 { "machine", required_argument
, NULL
, ARG_MACHINE
},
860 { "unit", required_argument
, NULL
, ARG_UNIT
},
861 { "property", required_argument
, NULL
, ARG_PROPERTY
},
862 { "description", required_argument
, NULL
, ARG_DESCRIPTION
},
863 { "slice", required_argument
, NULL
, ARG_SLICE
},
864 { "slice-inherit", no_argument
, NULL
, ARG_SLICE_INHERIT
},
865 { "user", required_argument
, NULL
, 'u' },
866 { "group", required_argument
, NULL
, 'g' },
867 { "nice", required_argument
, NULL
, ARG_NICE
},
868 { "chdir", required_argument
, NULL
, 'D' },
869 { "via-shell", no_argument
, NULL
, ARG_VIA_SHELL
},
870 { "login", no_argument
, NULL
, 'i' }, /* compat with sudo, --via-shell + --chdir='~' */
871 { "setenv", required_argument
, NULL
, ARG_SETENV
},
872 { "background", required_argument
, NULL
, ARG_BACKGROUND
},
873 { "pty", no_argument
, NULL
, ARG_PTY
},
874 { "pty-late", no_argument
, NULL
, ARG_PTY_LATE
},
875 { "pipe", no_argument
, NULL
, ARG_PIPE
},
876 { "shell-prompt-prefix", required_argument
, NULL
, ARG_SHELL_PROMPT_PREFIX
},
877 { "lightweight", required_argument
, NULL
, ARG_LIGHTWEIGHT
},
878 { "area", required_argument
, NULL
, ARG_AREA
},
887 /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
888 * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
890 while ((c
= getopt_long(argc
, argv
, "+hVu:g:D:i", options
, NULL
)) >= 0)
895 return help_sudo_mode();
900 case ARG_NO_ASK_PASSWORD
:
901 arg_ask_password
= false;
905 r
= parse_machine_argument(optarg
, &arg_host
, &arg_transport
);
915 if (strv_extend(&arg_property
, optarg
) < 0)
920 case ARG_DESCRIPTION
:
921 r
= free_and_strdup_warn(&arg_description
, optarg
);
930 case ARG_SLICE_INHERIT
:
931 arg_slice_inherit
= true;
935 arg_exec_user
= optarg
;
939 arg_exec_group
= optarg
;
943 r
= parse_nice(optarg
, &arg_nice
);
945 return log_error_errno(r
, "Failed to parse nice value: %s", optarg
);
951 if (streq(optarg
, "~"))
952 r
= free_and_strdup_warn(&arg_working_directory
, optarg
);
954 /* Root will be manually suppressed later. */
955 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_working_directory
);
962 r
= strv_env_replace_strdup_passthrough(&arg_environment
, optarg
);
964 return log_error_errno(r
, "Cannot assign environment variable %s: %m", optarg
);
969 r
= free_and_strdup_warn(&arg_background
, optarg
);
977 arg_stdio
|= ARG_STDIO_PTY
;
978 arg_pty_late
= c
== ARG_PTY_LATE
;
982 arg_stdio
|= ARG_STDIO_DIRECT
;
985 case ARG_SHELL_PROMPT_PREFIX
:
986 r
= free_and_strdup_warn(&arg_shell_prompt_prefix
, optarg
);
991 case ARG_LIGHTWEIGHT
:
992 r
= parse_tristate_argument("--lightweight=", optarg
, &arg_lightweight
);
998 /* We allow an empty --area= specification to allow logging into the primary home directory */
999 if (!isempty(optarg
) && !filename_is_valid(optarg
))
1000 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid area name, refusing: %s", optarg
);
1002 r
= free_and_strdup_warn(&arg_area
, optarg
);
1009 r
= free_and_strdup_warn(&arg_working_directory
, "~");
1015 arg_via_shell
= true;
1022 assert_not_reached();
1025 if (!arg_exec_user
&& arg_area
) {
1026 /* If the user specifies --area= but not --user= then consider this an area switch request,
1027 * and default to logging into our own account */
1028 arg_exec_user
= getusername_malloc();
1033 if (!arg_working_directory
) {
1034 if (arg_exec_user
) {
1035 /* When switching to a specific user, also switch to its home directory. */
1036 arg_working_directory
= strdup("~");
1037 if (!arg_working_directory
)
1040 /* When switching to root without this being specified, then stay in the current directory */
1041 r
= safe_getcwd(&arg_working_directory
);
1043 return log_error_errno(r
, "Failed to get current working directory: %m");
1046 /* Root was not suppressed earlier, to allow the above check to work properly. */
1047 if (empty_or_root(arg_working_directory
))
1048 arg_working_directory
= mfree(arg_working_directory
);
1051 arg_service_type
= "exec";
1054 arg_aggressive_gc
= true;
1056 if (IN_SET(arg_stdio
, ARG_STDIO_NONE
, ARG_STDIO_AUTO
))
1057 arg_stdio
= isatty_safe(STDIN_FILENO
) && isatty_safe(STDOUT_FILENO
) && isatty_safe(STDERR_FILENO
) ? ARG_STDIO_PTY
: ARG_STDIO_DIRECT
;
1058 log_debug("Using %s stdio mode.", arg_stdio
== ARG_STDIO_PTY
? "pty" : "direct");
1059 if (arg_pty_late
< 0)
1060 arg_pty_late
= arg_ask_password
; /* for run0 this defaults to on, except if --no-ask-pasword is used */
1062 arg_expand_environment
= false;
1063 arg_send_sighup
= true;
1065 _cleanup_strv_free_
char **l
= NULL
;
1066 if (argc
> optind
) {
1067 l
= strv_copy(argv
+ optind
);
1070 } else if (!arg_via_shell
) {
1073 e
= strv_env_get(arg_environment
, "SHELL");
1075 arg_exec_path
= strdup(e
);
1079 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
1080 r
= get_shell(&arg_exec_path
);
1082 return log_error_errno(r
, "Failed to determine shell: %m");
1084 arg_exec_path
= strdup("/bin/sh");
1089 r
= strv_env_assign(&arg_environment
, "SHELL", arg_exec_path
);
1091 return log_error_errno(r
, "Failed to set $SHELL environment variable: %m");
1094 l
= make_login_shell_cmdline(arg_exec_path
);
1099 if (arg_via_shell
) {
1100 arg_exec_path
= strdup(_PATH_BSHELL
);
1104 r
= strv_prepend(&l
, "-sh");
1109 strv_free_and_replace(arg_cmdline
, l
);
1112 arg_slice
= strdup(SPECIAL_USER_SLICE
);
1117 _cleanup_free_
char *un
= NULL
;
1118 un
= getusername_malloc();
1122 /* Set a bunch of environment variables in a roughly sudo-compatible way */
1123 r
= strv_env_assign(&arg_environment
, "SUDO_USER", un
);
1125 return log_error_errno(r
, "Failed to set $SUDO_USER environment variable: %m");
1127 r
= strv_env_assignf(&arg_environment
, "SUDO_UID", UID_FMT
, getuid());
1129 return log_error_errno(r
, "Failed to set $SUDO_UID environment variable: %m");
1131 r
= strv_env_assignf(&arg_environment
, "SUDO_GID", GID_FMT
, getgid());
1133 return log_error_errno(r
, "Failed to set $SUDO_GID environment variable: %m");
1135 if (strv_extendf(&arg_property
, "LogExtraFields=ELEVATED_UID=" UID_FMT
, getuid()) < 0)
1138 if (strv_extendf(&arg_property
, "LogExtraFields=ELEVATED_GID=" GID_FMT
, getgid()) < 0)
1141 if (strv_extendf(&arg_property
, "LogExtraFields=ELEVATED_USER=%s", un
) < 0)
1144 if (strv_extend(&arg_property
, "PAMName=systemd-run0") < 0)
1147 /* The service manager ignores SIGPIPE for all spawned processes by default. Let's explicitly override
1148 * that here, since we're primarily invoked in interactive environments where this does matter. */
1149 if (strv_extend(&arg_property
, "IgnoreSIGPIPE=no") < 0)
1152 if (!arg_background
&& arg_stdio
== ARG_STDIO_PTY
&& shall_tint_background()) {
1155 if (privileged_execution())
1158 hue
= 60 /* yellow */;
1160 r
= terminal_tint_color(hue
, &arg_background
);
1162 log_debug_errno(r
, "Unable to get terminal background color, not tinting background: %m");
1165 if (!arg_shell_prompt_prefix
) {
1166 const char *e
= secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
1168 arg_shell_prompt_prefix
= strdup(e
);
1169 if (!arg_shell_prompt_prefix
)
1171 } else if (emoji_enabled()) {
1172 arg_shell_prompt_prefix
= strjoin(glyph(privileged_execution() ? GLYPH_SUPERHERO
: GLYPH_IDCARD
), " ");
1173 if (!arg_shell_prompt_prefix
)
1178 if (!isempty(arg_shell_prompt_prefix
)) {
1179 r
= strv_env_assign(&arg_environment
, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix
);
1181 return log_error_errno(r
, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
1184 if (!strv_env_get(arg_environment
, "XDG_SESSION_CLASS")) {
1186 /* If logging into an area, imply lightweight mode */
1187 if (arg_lightweight
< 0 && !isempty(arg_area
))
1188 arg_lightweight
= true;
1190 /* When using run0 to acquire privileges temporarily, let's not pull in session manager by
1191 * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege
1192 * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session
1193 * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do
1194 * this for root though, under the assumption that if a regular user temporarily transitions into
1195 * another regular user it's a better default that the full user environment is uniformly
1197 if (arg_lightweight
< 0 && privileged_execution())
1198 arg_lightweight
= true;
1200 if (arg_lightweight
>= 0) {
1202 arg_lightweight
? (arg_stdio
== ARG_STDIO_PTY
? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") :
1203 (arg_stdio
== ARG_STDIO_PTY
? (privileged_execution() ? "user-early" : "user") : "background");
1205 log_debug("Setting XDG_SESSION_CLASS to '%s'.", class);
1207 r
= strv_env_assign(&arg_environment
, "XDG_SESSION_CLASS", class);
1209 return log_error_errno(r
, "Failed to set $XDG_SESSION_CLASS environment variable: %m");
1214 r
= strv_env_assign(&arg_environment
, "XDG_AREA", arg_area
);
1216 return log_error_errno(r
, "Failed to set $XDG_AREA environment variable: %m");
1222 static int transient_unit_set_properties(sd_bus_message
*m
, UnitType t
, char **properties
) {
1227 r
= sd_bus_message_append(m
, "(sv)", "Description", "s", arg_description
);
1229 return bus_log_create_error(r
);
1231 if (arg_aggressive_gc
) {
1232 r
= sd_bus_message_append(m
, "(sv)", "CollectMode", "s", "inactive-or-failed");
1234 return bus_log_create_error(r
);
1237 r
= sd_bus_is_bus_client(sd_bus_message_get_bus(m
));
1239 return log_error_errno(r
, "Can't determine if bus connection is direct or to broker: %m");
1241 /* Pin the object as least as long as we are around. Note that AddRef (currently) only works
1242 * if we talk via the bus though. */
1243 r
= sd_bus_message_append(m
, "(sv)", "AddRef", "b", 1);
1245 return bus_log_create_error(r
);
1248 return bus_append_unit_property_assignment_many(m
, t
, properties
);
1251 static int transient_cgroup_set_properties(sd_bus_message
*m
) {
1252 _cleanup_free_
char *name
= NULL
;
1253 _cleanup_free_
char *slice
= NULL
;
1258 if (arg_slice_inherit
) {
1261 switch (arg_runtime_scope
) {
1263 case RUNTIME_SCOPE_USER
:
1264 r
= cg_pid_get_user_slice(0, &name
);
1267 case RUNTIME_SCOPE_SYSTEM
:
1268 r
= cg_pid_get_slice(0, &name
);
1272 assert_not_reached();
1276 return log_error_errno(r
, "Failed to get PID slice: %m");
1278 end
= endswith(name
, ".slice");
1284 if (!isempty(arg_slice
) && !strextend_with_separator(&name
, "-", arg_slice
))
1290 r
= unit_name_mangle_with_suffix(name
, "as slice",
1291 arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
,
1294 return log_error_errno(r
, "Failed to mangle name '%s': %m", arg_slice
);
1296 r
= sd_bus_message_append(m
, "(sv)", "Slice", "s", slice
);
1298 return bus_log_create_error(r
);
1303 static int transient_kill_set_properties(sd_bus_message
*m
) {
1308 if (arg_send_sighup
) {
1309 r
= sd_bus_message_append(m
, "(sv)", "SendSIGHUP", "b", arg_send_sighup
);
1311 return bus_log_create_error(r
);
1317 static int transient_service_set_properties(sd_bus_message
*m
, const char *pty_path
, int pty_fd
) {
1318 int r
, send_term
; /* tri-state */
1320 /* Use ExecStartEx if new exec flags are required. */
1321 bool use_ex_prop
= !arg_expand_environment
|| arg_via_shell
;
1324 assert((!!pty_path
) == (pty_fd
>= 0));
1326 r
= transient_unit_set_properties(m
, UNIT_SERVICE
, arg_property
);
1330 r
= transient_kill_set_properties(m
);
1334 r
= transient_cgroup_set_properties(m
);
1338 if (arg_remain_after_exit
) {
1339 r
= sd_bus_message_append(m
, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit
);
1341 return bus_log_create_error(r
);
1344 if (arg_service_type
) {
1345 r
= sd_bus_message_append(m
, "(sv)", "Type", "s", arg_service_type
);
1347 return bus_log_create_error(r
);
1350 if (arg_exec_user
) {
1351 r
= sd_bus_message_append(m
, "(sv)", "User", "s", arg_exec_user
);
1353 return bus_log_create_error(r
);
1356 if (arg_exec_group
) {
1357 r
= sd_bus_message_append(m
, "(sv)", "Group", "s", arg_exec_group
);
1359 return bus_log_create_error(r
);
1363 r
= sd_bus_message_append(m
, "(sv)", "Nice", "i", arg_nice
);
1365 return bus_log_create_error(r
);
1368 if (arg_working_directory
) {
1369 r
= sd_bus_message_append(m
, "(sv)", "WorkingDirectory", "s", arg_working_directory
);
1371 return bus_log_create_error(r
);
1375 r
= sd_bus_message_append(m
, "(sv)(sv)(sv)(sv)",
1376 "TTYPath", "s", pty_path
,
1377 "StandardInputFileDescriptor", "h", pty_fd
,
1378 "StandardOutputFileDescriptor", "h", pty_fd
,
1379 "StandardErrorFileDescriptor", "h", pty_fd
);
1381 return bus_log_create_error(r
);
1385 } else if (arg_stdio
== ARG_STDIO_DIRECT
) {
1386 r
= sd_bus_message_append(m
,
1388 "StandardInputFileDescriptor", "h", STDIN_FILENO
,
1389 "StandardOutputFileDescriptor", "h", STDOUT_FILENO
,
1390 "StandardErrorFileDescriptor", "h", STDERR_FILENO
);
1392 return bus_log_create_error(r
);
1398 if (send_term
!= 0) {
1399 const char *e
, *colorterm
= NULL
, *no_color
= NULL
;
1401 /* Propagate $TERM + $COLORTERM + $NO_COLOR if we are actually connected to a TTY */
1402 if (isatty_safe(STDIN_FILENO
) || isatty_safe(STDOUT_FILENO
) || isatty_safe(STDERR_FILENO
)) {
1403 e
= strv_find_prefix(environ
, "TERM=");
1407 /* If we send $TERM along, then also propagate $COLORTERM + $NO_COLOR right with it */
1408 colorterm
= strv_find_prefix(environ
, "COLORTERM=");
1409 no_color
= strv_find_prefix(environ
, "NO_COLOR=");
1412 /* If we are not connected to any TTY ourselves, then send TERM=dumb, but only if we
1413 * really need to (because we actually allocated a TTY for the service) */
1416 if (send_term
> 0) {
1417 r
= sd_bus_message_append(
1420 "Environment", "as", 1, e
);
1422 return bus_log_create_error(r
);
1425 r
= sd_bus_message_append(
1428 "Environment", "as", 1, colorterm
);
1430 return bus_log_create_error(r
);
1434 r
= sd_bus_message_append(
1437 "Environment", "as", 1, no_color
);
1439 return bus_log_create_error(r
);
1444 if (!strv_isempty(arg_environment
)) {
1445 r
= sd_bus_message_open_container(m
, 'r', "sv");
1447 return bus_log_create_error(r
);
1449 r
= sd_bus_message_append(m
, "s", "Environment");
1451 return bus_log_create_error(r
);
1453 r
= sd_bus_message_open_container(m
, 'v', "as");
1455 return bus_log_create_error(r
);
1457 r
= sd_bus_message_append_strv(m
, arg_environment
);
1459 return bus_log_create_error(r
);
1461 r
= sd_bus_message_close_container(m
);
1463 return bus_log_create_error(r
);
1465 r
= sd_bus_message_close_container(m
);
1467 return bus_log_create_error(r
);
1470 /* Exec container */
1471 if (!strv_isempty(arg_cmdline
)) {
1472 r
= sd_bus_message_open_container(m
, 'r', "sv");
1474 return bus_log_create_error(r
);
1476 r
= sd_bus_message_append(m
, "s",
1477 use_ex_prop
? "ExecStartEx" : "ExecStart");
1479 return bus_log_create_error(r
);
1481 r
= sd_bus_message_open_container(m
, 'v',
1482 use_ex_prop
? "a(sasas)" : "a(sasb)");
1484 return bus_log_create_error(r
);
1486 r
= sd_bus_message_open_container(m
, 'a',
1487 use_ex_prop
? "(sasas)" : "(sasb)");
1489 return bus_log_create_error(r
);
1491 r
= sd_bus_message_open_container(m
, 'r',
1492 use_ex_prop
? "sasas" : "sasb");
1494 return bus_log_create_error(r
);
1496 r
= sd_bus_message_append(m
, "s", arg_exec_path
?: arg_cmdline
[0]);
1498 return bus_log_create_error(r
);
1500 r
= sd_bus_message_append_strv(m
, arg_cmdline
);
1502 return bus_log_create_error(r
);
1505 _cleanup_strv_free_
char **opts
= NULL
;
1507 r
= exec_command_flags_to_strv(
1508 (arg_expand_environment
? 0 : EXEC_COMMAND_NO_ENV_EXPAND
)|
1509 (arg_ignore_failure
? EXEC_COMMAND_IGNORE_FAILURE
: 0)|
1510 (arg_via_shell
? EXEC_COMMAND_VIA_SHELL
: 0),
1513 return log_error_errno(r
, "Failed to format execute flags: %m");
1515 r
= sd_bus_message_append_strv(m
, opts
);
1517 r
= sd_bus_message_append(m
, "b", arg_ignore_failure
);
1519 return bus_log_create_error(r
);
1521 r
= sd_bus_message_close_container(m
);
1523 return bus_log_create_error(r
);
1525 r
= sd_bus_message_close_container(m
);
1527 return bus_log_create_error(r
);
1529 r
= sd_bus_message_close_container(m
);
1531 return bus_log_create_error(r
);
1533 r
= sd_bus_message_close_container(m
);
1535 return bus_log_create_error(r
);
1541 static int transient_scope_set_properties(sd_bus_message
*m
, bool allow_pidfd
) {
1546 r
= transient_unit_set_properties(m
, UNIT_SCOPE
, arg_property
);
1550 r
= transient_kill_set_properties(m
);
1554 r
= transient_cgroup_set_properties(m
);
1558 _cleanup_(pidref_done
) PidRef pidref
= PIDREF_NULL
;
1560 r
= pidref_set_self(&pidref
);
1564 r
= bus_append_scope_pidref(m
, &pidref
, allow_pidfd
);
1566 return bus_log_create_error(r
);
1571 static int transient_timer_set_properties(sd_bus_message
*m
) {
1576 r
= transient_unit_set_properties(m
, UNIT_TIMER
, arg_timer_property
);
1580 /* Automatically clean up our transient timers */
1581 r
= sd_bus_message_append(m
, "(sv)", "RemainAfterElapse", "b", false);
1583 return bus_log_create_error(r
);
1588 static int make_unit_name(UnitType t
, char **ret
) {
1592 assert(t
< _UNIT_TYPE_MAX
);
1595 /* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
1596 * managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
1597 * logic in place. */
1599 _cleanup_(pidref_done
) PidRef self
= PIDREF_NULL
;
1600 r
= pidref_set_self(&self
);
1602 return log_error_errno(r
, "Failed to get reference to my own process: %m");
1604 r
= pidref_acquire_pidfd_id(&self
);
1606 log_debug_errno(r
, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
1608 /* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
1610 r
= sd_id128_randomize(&rnd
);
1612 return log_error_errno(r
, "Failed to generate random run unit name: %m");
1614 r
= asprintf(ret
, "run-r" SD_ID128_FORMAT_STR
".%s", SD_ID128_FORMAT_VAL(rnd
), unit_type_to_string(t
));
1616 r
= asprintf(ret
, "run-p" PID_FMT
"-i%" PRIu64
".%s", self
.pid
, self
.fd_id
, unit_type_to_string(t
));
1623 static int connect_bus(sd_bus
**ret
) {
1624 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1629 /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the
1630 * limited direct connection */
1632 arg_stdio
!= ARG_STDIO_NONE
||
1633 (arg_runtime_scope
== RUNTIME_SCOPE_USER
&& !IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_CAPSULE
)))
1634 r
= bus_connect_transport(arg_transport
, arg_host
, arg_runtime_scope
, &bus
);
1636 r
= bus_connect_transport_systemd(arg_transport
, arg_host
, arg_runtime_scope
, &bus
);
1638 return bus_log_connect_error(r
, arg_transport
, arg_runtime_scope
);
1640 (void) sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
1642 *ret
= TAKE_PTR(bus
);
1646 typedef struct RunContext
{
1648 PTYForward
*forward
;
1656 sd_bus_slot
*match_properties_changed
;
1657 sd_bus_slot
*match_disconnected
;
1658 sd_event_source
*retry_timer
;
1660 /* Current state of the unit */
1664 /* The exit data of the unit */
1665 uint64_t inactive_exit_usec
;
1666 uint64_t inactive_enter_usec
;
1668 uint64_t cpu_usage_nsec
;
1669 uint64_t memory_peak
;
1670 uint64_t memory_swap_peak
;
1671 uint64_t ip_ingress_bytes
;
1672 uint64_t ip_egress_bytes
;
1673 uint64_t io_read_bytes
;
1674 uint64_t io_write_bytes
;
1676 uint32_t exit_status
;
1679 static int run_context_update(RunContext
*c
);
1680 static int run_context_attach_bus(RunContext
*c
, sd_bus
*bus
);
1681 static void run_context_detach_bus(RunContext
*c
);
1682 static int run_context_reconnect(RunContext
*c
);
1683 static int run_context_setup_ptyfwd(RunContext
*c
);
1685 static void run_context_done(RunContext
*c
) {
1688 run_context_detach_bus(c
);
1690 c
->retry_timer
= sd_event_source_disable_unref(c
->retry_timer
);
1691 c
->forward
= pty_forward_free(c
->forward
);
1692 c
->event
= sd_event_unref(c
->event
);
1694 free(c
->active_state
);
1701 safe_close(c
->pty_fd
);
1704 static int on_retry_timer(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1705 RunContext
*c
= ASSERT_PTR(userdata
);
1707 c
->retry_timer
= sd_event_source_disable_unref(c
->retry_timer
);
1709 return run_context_reconnect(c
);
1712 static int run_context_reconnect(RunContext
*c
) {
1713 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1714 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
1719 run_context_detach_bus(c
);
1721 r
= connect_bus(&bus
);
1725 r
= sd_bus_call_method(bus
,
1726 "org.freedesktop.systemd1",
1728 "org.freedesktop.systemd1.Unit",
1731 /* ret_reply = */ NULL
, NULL
);
1733 /* Hmm, the service manager probably hasn't finished reexecution just yet? Try again later. */
1734 if (bus_error_is_connection(&error
) || bus_error_is_unknown_service(&error
))
1737 if (sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_OBJECT
))
1738 log_warning_errno(r
, "Unit deactivated during reconnection to the bus, exiting.");
1740 log_error_errno(r
, "Failed to re-add reference to unit: %s", bus_error_message(&error
, r
));
1742 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1746 r
= run_context_attach_bus(c
, bus
);
1748 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1752 log_info("Reconnected to bus.");
1754 return run_context_update(c
);
1757 log_warning_errno(r
, "Failed to reconnect, retrying in 2s: %m");
1759 r
= event_reset_time_relative(
1763 2 * USEC_PER_SEC
, /* accuracy= */ 0,
1765 SD_EVENT_PRIORITY_NORMAL
,
1767 /* force_reset= */ false);
1769 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1770 return log_error_errno(r
, "Failed to install retry timer: %m");
1776 static int run_context_check_started(RunContext
*c
) {
1782 return 0; /* Already started? */
1784 if (streq_ptr(c
->start_job
, c
->job
))
1785 return 0; /* The start job is still active. */
1787 /* The start job is finished. */
1788 c
->start_job
= mfree(c
->start_job
);
1790 /* Setup ptyfwd now if --pty-late is specified. */
1791 r
= run_context_setup_ptyfwd(c
);
1793 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1797 if (STRPTR_IN_SET(c
->active_state
, "inactive", "failed"))
1798 return 0; /* Already finished or failed? */
1800 /* Notify our caller that the service is now running, just in case. */
1801 (void) sd_notifyf(/* unset_environment= */ false,
1808 static void run_context_check_done(RunContext
*c
) {
1811 bool done
= STRPTR_IN_SET(c
->active_state
, "inactive", "failed") &&
1812 !c
->start_job
&& /* our start job */
1813 !c
->job
; /* any other job */
1815 if (done
&& c
->forward
) /* If the service is gone, it's time to drain the output */
1816 done
= pty_forward_drain(c
->forward
);
1819 (void) sd_event_exit(c
->event
, EXIT_SUCCESS
);
1822 static int map_job(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
1823 char **p
= ASSERT_PTR(userdata
);
1830 r
= sd_bus_message_read(m
, "(uo)", &id
, &job
);
1834 return free_and_strdup(p
, id
== 0 ? NULL
: job
);
1837 static int run_context_update(RunContext
*c
) {
1839 static const struct bus_properties_map map
[] = {
1840 { "ActiveState", "s", NULL
, offsetof(RunContext
, active_state
) },
1841 { "InactiveExitTimestampMonotonic", "t", NULL
, offsetof(RunContext
, inactive_exit_usec
) },
1842 { "InactiveEnterTimestampMonotonic", "t", NULL
, offsetof(RunContext
, inactive_enter_usec
) },
1843 { "Result", "s", NULL
, offsetof(RunContext
, result
) },
1844 { "ExecMainCode", "i", NULL
, offsetof(RunContext
, exit_code
) },
1845 { "ExecMainStatus", "i", NULL
, offsetof(RunContext
, exit_status
) },
1846 { "CPUUsageNSec", "t", NULL
, offsetof(RunContext
, cpu_usage_nsec
) },
1847 { "MemoryPeak", "t", NULL
, offsetof(RunContext
, memory_peak
) },
1848 { "MemorySwapPeak", "t", NULL
, offsetof(RunContext
, memory_swap_peak
) },
1849 { "IPIngressBytes", "t", NULL
, offsetof(RunContext
, ip_ingress_bytes
) },
1850 { "IPEgressBytes", "t", NULL
, offsetof(RunContext
, ip_egress_bytes
) },
1851 { "IOReadBytes", "t", NULL
, offsetof(RunContext
, io_read_bytes
) },
1852 { "IOWriteBytes", "t", NULL
, offsetof(RunContext
, io_write_bytes
) },
1853 { "Job", "(uo)", map_job
, offsetof(RunContext
, job
) },
1857 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1863 r
= bus_map_all_properties(
1865 "org.freedesktop.systemd1",
1873 /* If this is a connection error, then try to reconnect. This might be because the service
1874 * manager is being restarted. Handle this gracefully. */
1875 if (bus_error_is_connection(&error
) || bus_error_is_unknown_service(&error
)) {
1876 log_info_errno(r
, "Bus call failed due to connection problems. Trying to reconnect...");
1877 /* Not propagating error, because we handled it already, by reconnecting. */
1878 return run_context_reconnect(c
);
1881 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1882 return log_error_errno(r
, "Failed to query unit state: %s", bus_error_message(&error
, r
));
1885 r
= run_context_check_started(c
);
1889 run_context_check_done(c
);
1893 static int on_properties_changed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1894 return run_context_update(ASSERT_PTR(userdata
));
1897 static int on_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1898 /* If our connection gets terminated, then try to reconnect. This might be because the service
1899 * manager is being restarted. Handle this gracefully. */
1900 log_info("Got disconnected from bus connection. Trying to reconnect...");
1901 return run_context_reconnect(ASSERT_PTR(userdata
));
1904 static int run_context_attach_bus(RunContext
*c
, sd_bus
*bus
) {
1911 assert(!c
->match_properties_changed
);
1912 assert(!c
->match_disconnected
);
1914 c
->bus
= sd_bus_ref(bus
);
1916 r
= sd_bus_match_signal_async(
1918 &c
->match_properties_changed
,
1919 "org.freedesktop.systemd1",
1921 "org.freedesktop.DBus.Properties",
1922 "PropertiesChanged",
1923 on_properties_changed
, NULL
, c
);
1925 return log_error_errno(r
, "Failed to request PropertiesChanged signal match: %m");
1927 r
= sd_bus_match_signal_async(
1929 &c
->match_disconnected
,
1930 "org.freedesktop.DBus.Local",
1932 "org.freedesktop.DBus.Local",
1934 on_disconnected
, NULL
, c
);
1936 return log_error_errno(r
, "Failed to request Disconnected signal match: %m");
1938 r
= sd_bus_attach_event(c
->bus
, c
->event
, SD_EVENT_PRIORITY_NORMAL
);
1940 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1945 static void run_context_detach_bus(RunContext
*c
) {
1949 (void) sd_bus_detach_event(c
->bus
);
1950 c
->bus
= sd_bus_unref(c
->bus
);
1953 c
->match_properties_changed
= sd_bus_slot_unref(c
->match_properties_changed
);
1954 c
->match_disconnected
= sd_bus_slot_unref(c
->match_disconnected
);
1957 static int pty_forward_handler(PTYForward
*f
, int rcode
, void *userdata
) {
1958 RunContext
*c
= ASSERT_PTR(userdata
);
1962 if (rcode
== -ECANCELED
) {
1963 log_debug_errno(rcode
, "PTY forwarder disconnected.");
1965 return sd_event_exit(c
->event
, EXIT_SUCCESS
);
1967 /* If --wait is specified, we'll only exit the pty forwarding, but will continue to wait
1968 * for the service to end. If the user hits ^C we'll exit too. */
1969 } else if (rcode
< 0) {
1970 (void) sd_event_exit(c
->event
, EXIT_FAILURE
);
1971 return log_error_errno(rcode
, "Error on PTY forwarding logic: %m");
1974 run_context_check_done(c
);
1978 static int make_transient_service_unit(
1980 sd_bus_message
**message
,
1981 const char *service
,
1982 const char *pty_path
,
1985 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1992 r
= bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "StartTransientUnit");
1994 return bus_log_create_error(r
);
1997 r
= sd_bus_message_append(m
, "ss", service
, job_mode_to_string(arg_job_mode
));
1999 return bus_log_create_error(r
);
2002 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
2004 return bus_log_create_error(r
);
2006 r
= transient_service_set_properties(m
, pty_path
, pty_fd
);
2010 r
= sd_bus_message_close_container(m
);
2012 return bus_log_create_error(r
);
2014 /* Auxiliary units */
2015 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
2017 return bus_log_create_error(r
);
2019 *message
= TAKE_PTR(m
);
2023 static int bus_call_with_hint(
2025 sd_bus_message
*message
,
2027 sd_bus_message
**reply
) {
2029 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2032 r
= sd_bus_call(bus
, message
, 0, &error
, reply
);
2034 log_error_errno(r
, "Failed to start transient %s unit: %s", name
, bus_error_message(&error
, r
));
2036 if (!arg_expand_environment
&&
2037 sd_bus_error_has_names(&error
,
2038 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
2039 SD_BUS_ERROR_PROPERTY_READ_ONLY
))
2040 log_notice_errno(r
, "Hint: --expand-environment=no is not supported by old systemd");
2046 static int acquire_invocation_id(sd_bus
*bus
, const char *unit
, sd_id128_t
*ret
) {
2047 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2048 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2049 _cleanup_free_
char *object
= NULL
;
2056 object
= unit_dbus_path_from_name(unit
);
2061 r
= sd_bus_get_property(bus
,
2062 "org.freedesktop.systemd1",
2063 object
?: "/org/freedesktop/systemd1/unit/self",
2064 "org.freedesktop.systemd1.Unit",
2070 /* Let's ignore connection errors. This might be caused by that the service manager is being
2071 * restarted. Handle this gracefully. */
2072 if (bus_error_is_connection(&error
) || bus_error_is_unknown_service(&error
)) {
2073 log_debug_errno(r
, "Invocation ID request failed due to bus connection problems, ignoring: %s",
2074 bus_error_message(&error
, r
));
2075 *ret
= SD_ID128_NULL
;
2079 return log_error_errno(r
, "Failed to request invocation ID for unit: %s", bus_error_message(&error
, r
));
2082 r
= bus_message_read_id128(reply
, ret
);
2084 return bus_log_parse_error(r
);
2086 return r
; /* Return true when we get a non-null invocation ID. */
2089 static int fchown_to_capsule(int fd
, const char *capsule
) {
2090 _cleanup_free_
char *p
= NULL
;
2096 p
= path_join("/run/capsules/", capsule
);
2101 r
= chase_and_stat(p
, /* root= */ NULL
, CHASE_SAFE
|CHASE_PROHIBIT_SYMLINKS
, /* ret_path= */ NULL
, &st
);
2105 if (uid_is_system(st
.st_uid
) || gid_is_system(st
.st_gid
)) /* paranoid safety check */
2108 return fchmod_and_chown(fd
, 0600, st
.st_uid
, st
.st_gid
);
2111 static int print_unit_invocation(const char *unit
, sd_id128_t invocation_id
) {
2112 _cleanup_(sd_json_variant_unrefp
) sd_json_variant
*v
= NULL
;
2117 if (!sd_json_format_enabled(arg_json_format_flags
)) {
2118 if (sd_id128_is_null(invocation_id
))
2119 log_info("Running as unit: %s", unit
);
2121 log_info("Running as unit: %s; invocation ID: " SD_ID128_FORMAT_STR
, unit
, SD_ID128_FORMAT_VAL(invocation_id
));
2125 r
= sd_json_variant_set_field_string(&v
, "unit", unit
);
2129 if (!sd_id128_is_null(invocation_id
)) {
2130 r
= sd_json_variant_set_field_id128(&v
, "invocation_id", invocation_id
);
2135 return sd_json_variant_dump(v
, arg_json_format_flags
, stdout
, NULL
);
2138 static int run_context_setup_ptyfwd(RunContext
*c
) {
2143 if (c
->pty_fd
< 0 || c
->forward
)
2146 /* Stop agents now that we are online, to avoid TTY conflicts */
2147 polkit_agent_close();
2148 ask_password_agent_close();
2151 log_info("Press ^] three times within 1s to disconnect TTY.");
2153 r
= pty_forward_new(c
->event
, c
->pty_fd
, PTY_FORWARD_IGNORE_INITIAL_VHANGUP
, &c
->forward
);
2155 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
2157 pty_forward_set_hangup_handler(c
->forward
, pty_forward_handler
, c
);
2159 /* Make sure to process any TTY events before we process bus events */
2160 (void) pty_forward_set_priority(c
->forward
, SD_EVENT_PRIORITY_IMPORTANT
);
2162 if (!isempty(arg_background
))
2163 (void) pty_forward_set_background_color(c
->forward
, arg_background
);
2165 (void) pty_forward_set_window_title(c
->forward
,
2166 privileged_execution() ? GLYPH_RED_CIRCLE
: GLYPH_YELLOW_CIRCLE
,
2167 arg_host
, arg_cmdline
);
2171 static int run_context_show_result(RunContext
*c
) {
2176 _cleanup_(table_unrefp
) Table
*t
= table_new_vertical();
2180 if (!isempty(c
->result
)) {
2183 TABLE_FIELD
, "Finished with result",
2184 TABLE_STRING
, c
->result
,
2185 TABLE_SET_COLOR
, streq(c
->result
, "success") ? ansi_highlight_green() : ansi_highlight_red());
2187 return table_log_add_error(r
);
2190 if (c
->exit_code
> 0) {
2193 /* ret_cell= */ NULL
,
2195 "Main processes terminated with");
2197 return table_log_add_error(r
);
2199 r
= table_add_cell_stringf(
2201 /* ret_cell= */ NULL
,
2202 "code=%s, status=%u/%s",
2203 sigchld_code_to_string(c
->exit_code
),
2205 strna(c
->exit_code
== CLD_EXITED
?
2206 exit_status_to_string(c
->exit_status
, EXIT_STATUS_FULL
) :
2207 signal_to_string(c
->exit_status
)));
2209 return table_log_add_error(r
);
2212 if (timestamp_is_set(c
->inactive_enter_usec
) &&
2213 timestamp_is_set(c
->inactive_exit_usec
) &&
2214 c
->inactive_enter_usec
> c
->inactive_exit_usec
) {
2217 TABLE_FIELD
, "Service runtime",
2218 TABLE_TIMESPAN_MSEC
, c
->inactive_enter_usec
- c
->inactive_exit_usec
);
2220 return table_log_add_error(r
);
2223 if (c
->cpu_usage_nsec
!= NSEC_INFINITY
) {
2226 TABLE_FIELD
, "CPU time consumed",
2227 TABLE_TIMESPAN_MSEC
, DIV_ROUND_UP(c
->cpu_usage_nsec
, NSEC_PER_USEC
));
2229 return table_log_add_error(r
);
2232 if (c
->memory_peak
!= UINT64_MAX
) {
2235 if (c
->memory_swap_peak
!= UINT64_MAX
)
2236 swap
= strjoina(" (swap: ", FORMAT_BYTES(c
->memory_swap_peak
), ")");
2242 /* ret_cell= */ NULL
,
2243 TABLE_FIELD
, "Memory peak");
2245 return table_log_add_error(r
);
2247 r
= table_add_cell_stringf(
2249 /* ret_cell= */ NULL
,
2251 FORMAT_BYTES(c
->memory_peak
), swap
);
2253 return table_log_add_error(r
);
2256 const char *ip_ingress
= NULL
, *ip_egress
= NULL
;
2257 if (!IN_SET(c
->ip_ingress_bytes
, 0, UINT64_MAX
))
2258 ip_ingress
= strjoina("received ", FORMAT_BYTES(c
->ip_ingress_bytes
));
2259 if (!IN_SET(c
->ip_egress_bytes
, 0, UINT64_MAX
))
2260 ip_egress
= strjoina("sent ", FORMAT_BYTES(c
->ip_egress_bytes
));
2262 if (ip_ingress
|| ip_egress
) {
2265 /* ret_cell= */ NULL
,
2266 TABLE_FIELD
, "IP Traffic");
2268 return table_log_add_error(r
);
2270 r
= table_add_cell_stringf(
2272 /* ret_cell= */ NULL
,
2273 "%s%s%s", strempty(ip_ingress
), ip_ingress
&& ip_egress
? ", " : "", strempty(ip_egress
));
2275 return table_log_add_error(r
);
2278 const char *io_read
= NULL
, *io_write
= NULL
;
2279 if (!IN_SET(c
->io_read_bytes
, 0, UINT64_MAX
))
2280 io_read
= strjoina("read ", FORMAT_BYTES(c
->io_read_bytes
));
2281 if (!IN_SET(c
->io_write_bytes
, 0, UINT64_MAX
))
2282 io_write
= strjoina("written ", FORMAT_BYTES(c
->io_write_bytes
));
2284 if (io_read
|| io_write
) {
2287 /* ret_cell= */ NULL
,
2288 TABLE_FIELD
, "IO Bytes");
2290 return table_log_add_error(r
);
2292 r
= table_add_cell_stringf(
2294 /* ret_cell= */ NULL
,
2295 "%s%s%s", strempty(io_read
), io_read
&& io_write
? ", " : "", strempty(io_write
));
2297 return table_log_add_error(r
);
2300 r
= table_print(t
, stderr
);
2302 return table_log_print_error(r
);
2307 static int start_transient_service(sd_bus
*bus
) {
2308 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2309 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2310 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
2311 _cleanup_free_
char *pty_path
= NULL
;
2312 _cleanup_close_
int peer_fd
= -EBADF
;
2317 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2318 (void) ask_password_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2320 _cleanup_(run_context_done
) RunContext c
= {
2322 .cpu_usage_nsec
= NSEC_INFINITY
,
2323 .memory_peak
= UINT64_MAX
,
2324 .memory_swap_peak
= UINT64_MAX
,
2325 .ip_ingress_bytes
= UINT64_MAX
,
2326 .ip_egress_bytes
= UINT64_MAX
,
2327 .io_read_bytes
= UINT64_MAX
,
2328 .io_write_bytes
= UINT64_MAX
,
2329 .inactive_exit_usec
= USEC_INFINITY
,
2330 .inactive_enter_usec
= USEC_INFINITY
,
2333 if (arg_stdio
== ARG_STDIO_PTY
) {
2335 if (IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_CAPSULE
)) {
2336 c
.pty_fd
= openpt_allocate(O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
, &pty_path
);
2338 return log_error_errno(c
.pty_fd
, "Failed to acquire pseudo tty: %m");
2340 peer_fd
= pty_open_peer(c
.pty_fd
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
2342 return log_error_errno(peer_fd
, "Failed to open pty peer: %m");
2344 if (arg_transport
== BUS_TRANSPORT_CAPSULE
) {
2345 /* If we are in capsule mode, we must give the capsule UID/GID access to the PTY we just allocated first. */
2347 r
= fchown_to_capsule(peer_fd
, arg_host
);
2349 return log_error_errno(r
, "Failed to chown tty to capsule UID/GID: %m");
2352 } else if (arg_transport
== BUS_TRANSPORT_MACHINE
) {
2353 _cleanup_(sd_bus_unrefp
) sd_bus
*system_bus
= NULL
;
2354 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*pty_reply
= NULL
;
2357 r
= sd_bus_default_system(&system_bus
);
2359 return log_error_errno(r
, "Failed to connect to system bus: %m");
2361 (void) sd_bus_set_allow_interactive_authorization(system_bus
, arg_ask_password
);
2363 /* Chop off a username prefix. We allow this for sd-bus machine connections, hence
2364 * support that here too. */
2365 _cleanup_free_
char *h
= NULL
;
2366 r
= split_user_at_host(arg_host
, /* ret_user= */ NULL
, &h
);
2368 return log_error_errno(r
, "Failed to split host specification '%s': %m", arg_host
);
2370 r
= bus_call_method(
2378 return log_error_errno(r
, "Failed to get machine PTY: %s", bus_error_message(&error
, r
));
2380 r
= sd_bus_message_read(pty_reply
, "hs", &c
.pty_fd
, &s
);
2382 return bus_log_parse_error(r
);
2384 c
.pty_fd
= fcntl(c
.pty_fd
, F_DUPFD_CLOEXEC
, 3);
2386 return log_error_errno(errno
, "Failed to duplicate master fd: %m");
2388 pty_path
= strdup(s
);
2392 peer_fd
= pty_open_peer(c
.pty_fd
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
2394 return log_error_errno(peer_fd
, "Failed to open PTY peer: %m");
2396 assert_not_reached();
2400 r
= unit_name_mangle_with_suffix(
2403 arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
,
2407 return log_error_errno(r
, "Failed to mangle unit name: %m");
2409 r
= make_unit_name(UNIT_SERVICE
, &c
.unit
);
2414 /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
2415 * lets skip this however, because we should start that already when the start job is running, and
2416 * there's little point in waiting for the start job to complete in that case anyway, as we'll wait
2417 * for EOF anyway, which is going to be much later. */
2418 if (!arg_no_block
&& arg_stdio
== ARG_STDIO_NONE
) {
2419 r
= bus_wait_for_jobs_new(bus
, &w
);
2421 return log_error_errno(r
, "Could not watch jobs: %m");
2424 r
= make_transient_service_unit(bus
, &m
, c
.unit
, pty_path
, peer_fd
);
2427 peer_fd
= safe_close(peer_fd
);
2429 _cleanup_(journal_terminate
) PidRef journal_pid
= PIDREF_NULL
;
2431 (void) journal_fork(arg_runtime_scope
, STRV_MAKE(c
.unit
), &journal_pid
);
2433 r
= bus_call_with_hint(bus
, m
, "service", &reply
);
2438 r
= sd_bus_message_read(reply
, "o", &object
);
2440 return bus_log_parse_error(r
);
2443 r
= bus_wait_for_jobs_one(
2446 arg_quiet
? 0 : BUS_WAIT_JOBS_LOG_ERROR
,
2447 arg_runtime_scope
== RUNTIME_SCOPE_USER
? STRV_MAKE_CONST("--user") : NULL
);
2450 } else if (!arg_no_block
) {
2451 c
.start_job
= strdup(object
);
2457 sd_id128_t invocation_id
;
2459 r
= acquire_invocation_id(bus
, c
.unit
, &invocation_id
);
2463 r
= print_unit_invocation(c
.unit
, invocation_id
);
2468 if (arg_wait
|| arg_stdio
!= ARG_STDIO_NONE
) {
2469 c
.bus_path
= unit_dbus_path_from_name(c
.unit
);
2473 r
= sd_event_default(&c
.event
);
2475 return log_error_errno(r
, "Failed to get event loop: %m");
2477 _cleanup_(osc_context_closep
) sd_id128_t osc_context_id
= SD_ID128_NULL
;
2478 if (c
.pty_fd
>= 0) {
2479 if (arg_exec_user
&& !terminal_is_dumb()) {
2480 r
= osc_context_open_chpriv(arg_exec_user
, /* ret_seq= */ NULL
, &osc_context_id
);
2482 return log_error_errno(r
, "Failed to set OSC context: %m");
2485 (void) sd_event_set_signal_exit(c
.event
, true);
2487 assert(arg_pty_late
>= 0);
2488 if (!arg_pty_late
) { /* If late PTY mode is off, start pty forwarder immediately */
2489 r
= run_context_setup_ptyfwd(&c
);
2495 r
= run_context_attach_bus(&c
, bus
);
2499 r
= run_context_update(&c
);
2503 r
= sd_event_loop(c
.event
);
2505 return log_error_errno(r
, "Failed to run event loop: %m");
2507 /* Close the journal watch logic before we output the exit summary */
2508 journal_terminate(&journal_pid
);
2510 if (arg_wait
&& !arg_quiet
)
2511 run_context_show_result(&c
);
2513 /* Try to propagate the service's return value. But if the service defines
2514 * e.g. SuccessExitStatus, honour this, and return 0 to mean "success". */
2515 if (streq_ptr(c
.result
, "success"))
2516 return EXIT_SUCCESS
;
2517 if (streq_ptr(c
.result
, "exit-code") && c
.exit_status
> 0)
2518 return c
.exit_status
;
2519 if (streq_ptr(c
.result
, "signal"))
2520 return EXIT_EXCEPTION
;
2521 return EXIT_FAILURE
;
2524 return EXIT_SUCCESS
;
2527 static int start_transient_scope(sd_bus
*bus
) {
2528 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2529 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
2530 _cleanup_strv_free_
char **env
= NULL
, **user_env
= NULL
;
2531 _cleanup_free_
char *scope
= NULL
;
2532 const char *object
= NULL
;
2533 sd_id128_t invocation_id
;
2534 bool allow_pidfd
= true;
2538 assert(!strv_isempty(arg_cmdline
));
2540 r
= bus_wait_for_jobs_new(bus
, &w
);
2542 return log_error_errno(r
, "Could not watch jobs: %m");
2545 r
= unit_name_mangle_with_suffix(arg_unit
, "as unit",
2546 arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
,
2549 return log_error_errno(r
, "Failed to mangle scope name: %m");
2551 r
= make_unit_name(UNIT_SCOPE
, &scope
);
2556 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2557 (void) ask_password_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2560 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2561 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2563 r
= bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "StartTransientUnit");
2565 return bus_log_create_error(r
);
2568 r
= sd_bus_message_append(m
, "ss", scope
, job_mode_to_string(arg_job_mode
));
2570 return bus_log_create_error(r
);
2573 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
2575 return bus_log_create_error(r
);
2577 r
= transient_scope_set_properties(m
, allow_pidfd
);
2581 r
= sd_bus_message_close_container(m
);
2583 return bus_log_create_error(r
);
2585 /* Auxiliary units */
2586 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
2588 return bus_log_create_error(r
);
2590 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
2592 if (sd_bus_error_has_names(&error
, SD_BUS_ERROR_UNKNOWN_PROPERTY
, SD_BUS_ERROR_PROPERTY_READ_ONLY
) && allow_pidfd
) {
2593 log_debug("Retrying with classic PIDs.");
2594 allow_pidfd
= false;
2598 return log_error_errno(r
, "Failed to start transient scope unit: %s", bus_error_message(&error
, r
));
2604 r
= sd_bus_message_read(reply
, "o", &object
);
2606 return bus_log_parse_error(r
);
2608 r
= bus_wait_for_jobs_one(w
, object
, arg_quiet
? 0 : BUS_WAIT_JOBS_LOG_ERROR
,
2609 arg_runtime_scope
== RUNTIME_SCOPE_USER
? STRV_MAKE_CONST("--user") : NULL
);
2613 r
= acquire_invocation_id(bus
, NULL
, &invocation_id
);
2617 log_debug("No invocation ID set.");
2619 if (strv_extendf(&user_env
, "INVOCATION_ID=" SD_ID128_FORMAT_STR
, SD_ID128_FORMAT_VAL(invocation_id
)) < 0)
2623 /* Stop agents before we pass control away and before we drop privileges, to avoid TTY conflicts and
2624 * before we become unable to stop agents. */
2625 polkit_agent_close();
2626 ask_password_agent_close();
2629 if (setpriority(PRIO_PROCESS
, 0, arg_nice
) < 0)
2630 return log_error_errno(errno
, "Failed to set nice level: %m");
2633 if (arg_exec_group
) {
2636 r
= get_group_creds(&arg_exec_group
, &gid
, 0);
2638 return log_error_errno(r
, "Failed to resolve group %s: %m", arg_exec_group
);
2640 if (setresgid(gid
, gid
, gid
) < 0)
2641 return log_error_errno(errno
, "Failed to change GID to " GID_FMT
": %m", gid
);
2644 if (arg_exec_user
) {
2645 const char *home
, *shell
;
2649 r
= get_user_creds(&arg_exec_user
, &uid
, &gid
, &home
, &shell
,
2650 USER_CREDS_CLEAN
|USER_CREDS_SUPPRESS_PLACEHOLDER
|USER_CREDS_PREFER_NSS
);
2652 return log_error_errno(r
, "Failed to resolve user %s: %m", arg_exec_user
);
2655 r
= strv_extendf(&user_env
, "HOME=%s", home
);
2661 r
= strv_extendf(&user_env
, "SHELL=%s", shell
);
2666 r
= strv_extendf(&user_env
, "USER=%s", arg_exec_user
);
2670 r
= strv_extendf(&user_env
, "LOGNAME=%s", arg_exec_user
);
2674 if (!arg_exec_group
) {
2675 if (setresgid(gid
, gid
, gid
) < 0)
2676 return log_error_errno(errno
, "Failed to change GID to " GID_FMT
": %m", gid
);
2679 if (setresuid(uid
, uid
, uid
) < 0)
2680 return log_error_errno(errno
, "Failed to change UID to " UID_FMT
": %m", uid
);
2683 if (arg_working_directory
&& chdir(arg_working_directory
) < 0)
2684 return log_error_errno(errno
, "Failed to change directory to '%s': %m", arg_working_directory
);
2686 env
= strv_env_merge(environ
, user_env
, arg_environment
);
2691 r
= print_unit_invocation(scope
, invocation_id
);
2696 if (arg_expand_environment
) {
2697 _cleanup_strv_free_
char **expanded_cmdline
= NULL
, **unset_variables
= NULL
, **bad_variables
= NULL
;
2699 r
= replace_env_argv(arg_cmdline
, env
, &expanded_cmdline
, &unset_variables
, &bad_variables
);
2701 return log_error_errno(r
, "Failed to expand environment variables: %m");
2703 free_and_replace(arg_cmdline
, expanded_cmdline
);
2705 if (!strv_isempty(unset_variables
)) {
2706 _cleanup_free_
char *ju
= strv_join(unset_variables
, ", ");
2707 log_warning("Referenced but unset environment variable evaluates to an empty string: %s", strna(ju
));
2710 if (!strv_isempty(bad_variables
)) {
2711 _cleanup_free_
char *jb
= strv_join(bad_variables
, ", ");
2712 log_warning("Invalid environment variable name evaluates to an empty string: %s", strna(jb
));
2716 execvpe(arg_cmdline
[0], arg_cmdline
, env
);
2718 return log_error_errno(errno
, "Failed to execute: %m");
2721 static int make_transient_trigger_unit(
2723 sd_bus_message
**message
,
2725 const char *trigger
,
2726 const char *service
) {
2728 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2737 r
= bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "StartTransientUnit");
2739 return bus_log_create_error(r
);
2742 r
= sd_bus_message_append(m
, "ss", trigger
, job_mode_to_string(arg_job_mode
));
2744 return bus_log_create_error(r
);
2747 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
2749 return bus_log_create_error(r
);
2751 if (streq(suffix
, ".path"))
2752 r
= transient_unit_set_properties(m
, UNIT_PATH
, arg_path_property
);
2753 else if (streq(suffix
, ".socket"))
2754 r
= transient_unit_set_properties(m
, UNIT_SOCKET
, arg_socket_property
);
2755 else if (streq(suffix
, ".timer"))
2756 r
= transient_timer_set_properties(m
);
2758 assert_not_reached();
2762 r
= sd_bus_message_close_container(m
);
2764 return bus_log_create_error(r
);
2766 r
= sd_bus_message_open_container(m
, 'a', "(sa(sv))");
2768 return bus_log_create_error(r
);
2770 if (!strv_isempty(arg_cmdline
)) {
2771 r
= sd_bus_message_open_container(m
, 'r', "sa(sv)");
2773 return bus_log_create_error(r
);
2775 r
= sd_bus_message_append(m
, "s", service
);
2777 return bus_log_create_error(r
);
2779 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
2781 return bus_log_create_error(r
);
2783 r
= transient_service_set_properties(m
, /* pty_path = */ NULL
, /* pty_fd = */ -EBADF
);
2787 r
= sd_bus_message_close_container(m
);
2789 return bus_log_create_error(r
);
2791 r
= sd_bus_message_close_container(m
);
2793 return bus_log_create_error(r
);
2796 r
= sd_bus_message_close_container(m
);
2798 return bus_log_create_error(r
);
2800 *message
= TAKE_PTR(m
);
2804 static int start_transient_trigger(sd_bus
*bus
, const char *suffix
) {
2805 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2806 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
2807 _cleanup_free_
char *trigger
= NULL
, *service
= NULL
;
2808 const char *object
= NULL
;
2814 r
= bus_wait_for_jobs_new(bus
, &w
);
2816 return log_error_errno(r
, "Could not watch jobs: %m");
2819 switch (unit_name_to_type(arg_unit
)) {
2822 service
= strdup(arg_unit
);
2826 r
= unit_name_change_suffix(service
, suffix
, &trigger
);
2828 return log_error_errno(r
, "Failed to change unit suffix: %m");
2832 trigger
= strdup(arg_unit
);
2836 r
= unit_name_change_suffix(trigger
, ".service", &service
);
2838 return log_error_errno(r
, "Failed to change unit suffix: %m");
2842 r
= unit_name_mangle_with_suffix(arg_unit
, "as unit",
2843 arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
,
2844 ".service", &service
);
2846 return log_error_errno(r
, "Failed to mangle unit name: %m");
2848 r
= unit_name_mangle_with_suffix(arg_unit
, "as trigger",
2849 arg_quiet
? 0 : UNIT_NAME_MANGLE_WARN
,
2852 return log_error_errno(r
, "Failed to mangle unit name: %m");
2857 r
= make_unit_name(UNIT_SERVICE
, &service
);
2861 r
= unit_name_change_suffix(service
, suffix
, &trigger
);
2863 return log_error_errno(r
, "Failed to change unit suffix: %m");
2866 r
= make_transient_trigger_unit(bus
, &m
, suffix
, trigger
, service
);
2870 (void) polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2871 (void) ask_password_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2873 r
= bus_call_with_hint(bus
, m
, suffix
+ 1, &reply
);
2877 r
= sd_bus_message_read(reply
, "o", &object
);
2879 return bus_log_parse_error(r
);
2881 r
= bus_wait_for_jobs_one(w
, object
, arg_quiet
? 0 : BUS_WAIT_JOBS_LOG_ERROR
,
2882 arg_runtime_scope
== RUNTIME_SCOPE_USER
? STRV_MAKE_CONST("--user") : NULL
);
2887 log_info("Running %s as unit: %s", suffix
+ 1, trigger
);
2888 if (!strv_isempty(arg_cmdline
))
2889 log_info("Will run service as unit: %s", service
);
2892 return EXIT_SUCCESS
;
2895 static bool shall_make_executable_absolute(void) {
2898 if (strv_isempty(arg_cmdline
))
2900 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
2903 FOREACH_STRING(f
, "RootDirectory=", "RootImage=", "ExecSearchPath=", "MountImages=", "ExtensionImages=")
2904 if (strv_find_startswith(arg_property
, f
))
2910 static int run(int argc
, char* argv
[]) {
2911 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2916 if (invoked_as(argv
, "run0"))
2917 r
= parse_argv_sudo_mode(argc
, argv
);
2919 r
= parse_argv(argc
, argv
);
2923 if (shall_make_executable_absolute()) {
2924 /* Patch in an absolute path to fail early for user convenience, but only when we can do it
2925 * (i.e. we will be running from the same file system). This also uses the user's $PATH,
2926 * while we use a fixed search path in the manager. */
2928 _cleanup_free_
char *command
= NULL
;
2929 r
= find_executable(arg_cmdline
[0], &command
);
2930 if (ERRNO_IS_NEG_PRIVILEGE(r
))
2931 log_debug_errno(r
, "Failed to find executable '%s' due to permission problems, leaving path as is: %m", arg_cmdline
[0]);
2933 return log_error_errno(r
, "Failed to find executable %s: %m", arg_cmdline
[0]);
2935 free_and_replace(arg_cmdline
[0], command
);
2938 if (!arg_description
) {
2939 _cleanup_free_
char *t
= NULL
;
2941 if (strv_isempty(arg_cmdline
))
2942 t
= strdup(arg_unit
);
2943 else if (arg_via_shell
) {
2945 t
= quote_command_line(arg_cmdline
+ 1, SHELL_ESCAPE_EMPTY
);
2947 t
= strjoin("LOGIN", arg_exec_user
? ": " : NULL
, arg_exec_user
);
2948 } else if (startswith(arg_cmdline
[0], "-")) {
2949 /* Drop the login shell marker from the command line when generating the description,
2950 * in order to minimize user confusion. */
2951 _cleanup_strv_free_
char **l
= strv_copy(arg_cmdline
);
2955 r
= free_and_strdup_warn(l
+ 0, l
[0] + 1);
2959 t
= quote_command_line(l
, SHELL_ESCAPE_EMPTY
);
2961 t
= quote_command_line(arg_cmdline
, SHELL_ESCAPE_EMPTY
);
2965 arg_description
= strjoin("[", program_invocation_short_name
, "] ", t
);
2966 if (!arg_description
)
2970 r
= connect_bus(&bus
);
2975 return start_transient_scope(bus
);
2976 if (arg_path_property
)
2977 return start_transient_trigger(bus
, ".path");
2978 if (arg_socket_property
)
2979 return start_transient_trigger(bus
, ".socket");
2981 return start_transient_trigger(bus
, ".timer");
2982 return start_transient_service(bus
);
2985 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);