1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "alloc-util.h"
28 #include "bus-error.h"
29 #include "bus-unit-util.h"
31 #include "calendarspec.h"
34 #include "format-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
39 #include "signal-util.h"
40 #include "spawn-polkit-agent.h"
42 #include "terminal-util.h"
43 #include "unit-name.h"
44 #include "user-util.h"
46 static bool arg_ask_password
= true;
47 static bool arg_scope
= false;
48 static bool arg_remain_after_exit
= false;
49 static bool arg_no_block
= false;
50 static bool arg_wait
= false;
51 static const char *arg_unit
= NULL
;
52 static const char *arg_description
= NULL
;
53 static const char *arg_slice
= NULL
;
54 static bool arg_send_sighup
= false;
55 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
56 static const char *arg_host
= NULL
;
57 static bool arg_user
= false;
58 static const char *arg_service_type
= NULL
;
59 static const char *arg_exec_user
= NULL
;
60 static const char *arg_exec_group
= NULL
;
61 static int arg_nice
= 0;
62 static bool arg_nice_set
= false;
63 static char **arg_environment
= NULL
;
64 static char **arg_property
= NULL
;
66 ARG_STDIO_NONE
, /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */
67 ARG_STDIO_PTY
, /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */
68 ARG_STDIO_DIRECT
, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
69 ARG_STDIO_AUTO
, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
70 } arg_stdio
= ARG_STDIO_NONE
;
71 static usec_t arg_on_active
= 0;
72 static usec_t arg_on_boot
= 0;
73 static usec_t arg_on_startup
= 0;
74 static usec_t arg_on_unit_active
= 0;
75 static usec_t arg_on_unit_inactive
= 0;
76 static const char *arg_on_calendar
= NULL
;
77 static char **arg_timer_property
= NULL
;
78 static bool arg_quiet
= false;
79 static bool arg_aggressive_gc
= false;
81 static void help(void) {
82 printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
83 "Run the specified command in a transient scope or service.\n\n"
84 " -h --help Show this help\n"
85 " --version Show package version\n"
86 " --no-ask-password Do not prompt for password\n"
87 " --user Run as user unit\n"
88 " -H --host=[USER@]HOST Operate on remote host\n"
89 " -M --machine=CONTAINER Operate on local container\n"
90 " --scope Run this as scope rather than service\n"
91 " --unit=UNIT Run under the specified unit name\n"
92 " -p --property=NAME=VALUE Set service or scope unit property\n"
93 " --description=TEXT Description for unit\n"
94 " --slice=SLICE Run in the specified slice\n"
95 " --no-block Do not wait until operation finished\n"
96 " -r --remain-after-exit Leave service around until explicitly stopped\n"
97 " --wait Wait until service stopped again\n"
98 " --send-sighup Send SIGHUP when terminating\n"
99 " --service-type=TYPE Service type\n"
100 " --uid=USER Run as system user\n"
101 " --gid=GROUP Run as system group\n"
102 " --nice=NICE Nice level\n"
103 " -E --setenv=NAME=VALUE Set environment\n"
104 " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
106 " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
107 " -q --quiet Suppress information messages during runtime\n"
108 " -G --collect Unload unit after it ran, even when failed\n\n"
110 " --on-active=SECONDS Run after SECONDS delay\n"
111 " --on-boot=SECONDS Run SECONDS after machine was booted up\n"
112 " --on-startup=SECONDS Run SECONDS after systemd activation\n"
113 " --on-unit-active=SECONDS Run SECONDS after the last activation\n"
114 " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
115 " --on-calendar=SPEC Realtime timer\n"
116 " --timer-property=NAME=VALUE Set timer unit property\n"
117 , program_invocation_short_name
);
120 static bool with_timer(void) {
121 return arg_on_active
|| arg_on_boot
|| arg_on_startup
|| arg_on_unit_active
|| arg_on_unit_inactive
|| arg_on_calendar
;
124 static int parse_argv(int argc
, char *argv
[]) {
143 ARG_ON_UNIT_INACTIVE
,
151 static const struct option options
[] = {
152 { "help", no_argument
, NULL
, 'h' },
153 { "version", no_argument
, NULL
, ARG_VERSION
},
154 { "user", no_argument
, NULL
, ARG_USER
},
155 { "system", no_argument
, NULL
, ARG_SYSTEM
},
156 { "scope", no_argument
, NULL
, ARG_SCOPE
},
157 { "unit", required_argument
, NULL
, ARG_UNIT
},
158 { "description", required_argument
, NULL
, ARG_DESCRIPTION
},
159 { "slice", required_argument
, NULL
, ARG_SLICE
},
160 { "remain-after-exit", no_argument
, NULL
, 'r' },
161 { "send-sighup", no_argument
, NULL
, ARG_SEND_SIGHUP
},
162 { "host", required_argument
, NULL
, 'H' },
163 { "machine", required_argument
, NULL
, 'M' },
164 { "service-type", required_argument
, NULL
, ARG_SERVICE_TYPE
},
165 { "wait", no_argument
, NULL
, ARG_WAIT
},
166 { "uid", required_argument
, NULL
, ARG_EXEC_USER
},
167 { "gid", required_argument
, NULL
, ARG_EXEC_GROUP
},
168 { "nice", required_argument
, NULL
, ARG_NICE
},
169 { "setenv", required_argument
, NULL
, 'E' },
170 { "property", required_argument
, NULL
, 'p' },
171 { "tty", no_argument
, NULL
, 't' }, /* deprecated alias */
172 { "pty", no_argument
, NULL
, 't' },
173 { "pipe", no_argument
, NULL
, 'P' },
174 { "quiet", no_argument
, NULL
, 'q' },
175 { "on-active", required_argument
, NULL
, ARG_ON_ACTIVE
},
176 { "on-boot", required_argument
, NULL
, ARG_ON_BOOT
},
177 { "on-startup", required_argument
, NULL
, ARG_ON_STARTUP
},
178 { "on-unit-active", required_argument
, NULL
, ARG_ON_UNIT_ACTIVE
},
179 { "on-unit-inactive", required_argument
, NULL
, ARG_ON_UNIT_INACTIVE
},
180 { "on-calendar", required_argument
, NULL
, ARG_ON_CALENDAR
},
181 { "timer-property", required_argument
, NULL
, ARG_TIMER_PROPERTY
},
182 { "no-block", no_argument
, NULL
, ARG_NO_BLOCK
},
183 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
184 { "collect", no_argument
, NULL
, 'G' },
193 while ((c
= getopt_long(argc
, argv
, "+hrH:M:E:p:tPqG", options
, NULL
)) >= 0)
204 case ARG_NO_ASK_PASSWORD
:
205 arg_ask_password
= false;
224 case ARG_DESCRIPTION
:
225 arg_description
= optarg
;
232 case ARG_SEND_SIGHUP
:
233 arg_send_sighup
= true;
237 arg_remain_after_exit
= true;
241 arg_transport
= BUS_TRANSPORT_REMOTE
;
246 arg_transport
= BUS_TRANSPORT_MACHINE
;
250 case ARG_SERVICE_TYPE
:
251 arg_service_type
= optarg
;
255 arg_exec_user
= optarg
;
259 arg_exec_group
= optarg
;
263 r
= parse_nice(optarg
, &arg_nice
);
265 return log_error_errno(r
, "Failed to parse nice value: %s", optarg
);
271 if (strv_extend(&arg_environment
, optarg
) < 0)
277 if (strv_extend(&arg_property
, optarg
) < 0)
282 case 't': /* --pty */
283 if (IN_SET(arg_stdio
, ARG_STDIO_DIRECT
, ARG_STDIO_AUTO
)) /* if --pipe is already used, upgrade to auto mode */
284 arg_stdio
= ARG_STDIO_AUTO
;
286 arg_stdio
= ARG_STDIO_PTY
;
289 case 'P': /* --pipe */
290 if (IN_SET(arg_stdio
, ARG_STDIO_PTY
, ARG_STDIO_AUTO
)) /* If --pty is already used, upgrade to auto mode */
291 arg_stdio
= ARG_STDIO_AUTO
;
293 arg_stdio
= ARG_STDIO_DIRECT
;
302 r
= parse_sec(optarg
, &arg_on_active
);
304 log_error("Failed to parse timer value: %s", optarg
);
312 r
= parse_sec(optarg
, &arg_on_boot
);
314 log_error("Failed to parse timer value: %s", optarg
);
322 r
= parse_sec(optarg
, &arg_on_startup
);
324 log_error("Failed to parse timer value: %s", optarg
);
330 case ARG_ON_UNIT_ACTIVE
:
332 r
= parse_sec(optarg
, &arg_on_unit_active
);
334 log_error("Failed to parse timer value: %s", optarg
);
340 case ARG_ON_UNIT_INACTIVE
:
342 r
= parse_sec(optarg
, &arg_on_unit_inactive
);
344 log_error("Failed to parse timer value: %s", optarg
);
350 case ARG_ON_CALENDAR
: {
351 CalendarSpec
*spec
= NULL
;
353 r
= calendar_spec_from_string(optarg
, &spec
);
355 log_error("Invalid calendar spec: %s", optarg
);
359 calendar_spec_free(spec
);
360 arg_on_calendar
= optarg
;
364 case ARG_TIMER_PROPERTY
:
366 if (strv_extend(&arg_timer_property
, optarg
) < 0)
380 arg_aggressive_gc
= true;
387 assert_not_reached("Unhandled option");
391 if (arg_stdio
== ARG_STDIO_AUTO
) {
392 /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
393 * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
394 * pipeline, but we are neatly interactive with tty-level isolation otherwise. */
395 arg_stdio
= isatty(STDIN_FILENO
) && isatty(STDOUT_FILENO
) && isatty(STDERR_FILENO
) ?
400 if ((optind
>= argc
) && (!arg_unit
|| !with_timer())) {
401 log_error("Command line to execute required.");
405 if (arg_user
&& arg_transport
!= BUS_TRANSPORT_LOCAL
) {
406 log_error("Execution in user context is not supported on non-local systems.");
410 if (arg_scope
&& arg_transport
!= BUS_TRANSPORT_LOCAL
) {
411 log_error("Scope execution is not supported on non-local systems.");
415 if (arg_scope
&& (arg_remain_after_exit
|| arg_service_type
)) {
416 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
420 if (arg_stdio
!= ARG_STDIO_NONE
&& (with_timer() || arg_scope
)) {
421 log_error("--pty/--pipe is not compatible in timer or --scope mode.");
425 if (arg_stdio
!= ARG_STDIO_NONE
&& arg_transport
== BUS_TRANSPORT_REMOTE
) {
426 log_error("--pty/--pipe is only supported when connecting to the local system or containers.");
430 if (arg_stdio
!= ARG_STDIO_NONE
&& arg_no_block
) {
431 log_error("--pty/--pipe is not compatible with --no-block.");
435 if (arg_scope
&& with_timer()) {
436 log_error("Timer options are not supported in --scope mode.");
440 if (arg_timer_property
&& !with_timer()) {
441 log_error("--timer-property= has no effect without any other timer options.");
447 log_error("--wait may not be combined with --no-block.");
452 log_error("--wait may not be combined with timer operations.");
457 log_error("--wait may not be combined with --scope.");
465 static int transient_unit_set_properties(sd_bus_message
*m
, char **properties
) {
468 r
= sd_bus_message_append(m
, "(sv)", "Description", "s", arg_description
);
472 if (arg_aggressive_gc
) {
473 r
= sd_bus_message_append(m
, "(sv)", "CollectMode", "s", "inactive-or-failed");
478 r
= bus_append_unit_property_assignment_many(m
, properties
);
485 static int transient_cgroup_set_properties(sd_bus_message
*m
) {
489 if (!isempty(arg_slice
)) {
490 _cleanup_free_
char *slice
;
492 r
= unit_name_mangle_with_suffix(arg_slice
, UNIT_NAME_NOGLOB
, ".slice", &slice
);
496 r
= sd_bus_message_append(m
, "(sv)", "Slice", "s", slice
);
504 static int transient_kill_set_properties(sd_bus_message
*m
) {
508 return sd_bus_message_append(m
, "(sv)", "SendSIGHUP", "b", arg_send_sighup
);
513 static int transient_service_set_properties(sd_bus_message
*m
, char **argv
, const char *pty_path
) {
514 bool send_term
= false;
519 r
= transient_unit_set_properties(m
, arg_property
);
523 r
= transient_kill_set_properties(m
);
527 r
= transient_cgroup_set_properties(m
);
531 if (arg_wait
|| arg_stdio
!= ARG_STDIO_NONE
) {
532 r
= sd_bus_message_append(m
, "(sv)", "AddRef", "b", 1);
537 if (arg_remain_after_exit
) {
538 r
= sd_bus_message_append(m
, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit
);
543 if (arg_service_type
) {
544 r
= sd_bus_message_append(m
, "(sv)", "Type", "s", arg_service_type
);
550 r
= sd_bus_message_append(m
, "(sv)", "User", "s", arg_exec_user
);
555 if (arg_exec_group
) {
556 r
= sd_bus_message_append(m
, "(sv)", "Group", "s", arg_exec_group
);
562 r
= sd_bus_message_append(m
, "(sv)", "Nice", "i", arg_nice
);
568 r
= sd_bus_message_append(m
,
570 "StandardInput", "s", "tty",
571 "StandardOutput", "s", "tty",
572 "StandardError", "s", "tty",
573 "TTYPath", "s", pty_path
);
579 } else if (arg_stdio
== ARG_STDIO_DIRECT
) {
580 r
= sd_bus_message_append(m
,
582 "StandardInputFileDescriptor", "h", STDIN_FILENO
,
583 "StandardOutputFileDescriptor", "h", STDOUT_FILENO
,
584 "StandardErrorFileDescriptor", "h", STDERR_FILENO
);
588 send_term
= isatty(STDIN_FILENO
) || isatty(STDOUT_FILENO
) || isatty(STDERR_FILENO
);
598 n
= strjoina("TERM=", e
);
599 r
= sd_bus_message_append(m
,
601 "Environment", "as", 1, n
);
607 if (!strv_isempty(arg_environment
)) {
608 r
= sd_bus_message_open_container(m
, 'r', "sv");
612 r
= sd_bus_message_append(m
, "s", "Environment");
616 r
= sd_bus_message_open_container(m
, 'v', "as");
620 r
= sd_bus_message_append_strv(m
, arg_environment
);
624 r
= sd_bus_message_close_container(m
);
628 r
= sd_bus_message_close_container(m
);
635 r
= sd_bus_message_open_container(m
, 'r', "sv");
639 r
= sd_bus_message_append(m
, "s", "ExecStart");
643 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
647 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
651 r
= sd_bus_message_open_container(m
, 'r', "sasb");
655 r
= sd_bus_message_append(m
, "s", argv
[0]);
659 r
= sd_bus_message_append_strv(m
, argv
);
663 r
= sd_bus_message_append(m
, "b", false);
667 r
= sd_bus_message_close_container(m
);
671 r
= sd_bus_message_close_container(m
);
675 r
= sd_bus_message_close_container(m
);
679 r
= sd_bus_message_close_container(m
);
687 static int transient_scope_set_properties(sd_bus_message
*m
) {
692 r
= transient_unit_set_properties(m
, arg_property
);
696 r
= transient_kill_set_properties(m
);
700 r
= transient_cgroup_set_properties(m
);
704 r
= sd_bus_message_append(m
, "(sv)", "PIDs", "au", 1, (uint32_t) getpid_cached());
711 static int transient_timer_set_properties(sd_bus_message
*m
) {
716 r
= transient_unit_set_properties(m
, arg_timer_property
);
720 /* Automatically clean up our transient timers */
721 r
= sd_bus_message_append(m
, "(sv)", "RemainAfterElapse", "b", false);
726 r
= sd_bus_message_append(m
, "(sv)", "OnActiveSec", "t", arg_on_active
);
732 r
= sd_bus_message_append(m
, "(sv)", "OnBootSec", "t", arg_on_boot
);
737 if (arg_on_startup
) {
738 r
= sd_bus_message_append(m
, "(sv)", "OnStartupSec", "t", arg_on_startup
);
743 if (arg_on_unit_active
) {
744 r
= sd_bus_message_append(m
, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active
);
749 if (arg_on_unit_inactive
) {
750 r
= sd_bus_message_append(m
, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive
);
755 if (arg_on_calendar
) {
756 r
= sd_bus_message_append(m
, "(sv)", "OnCalendar", "s", arg_on_calendar
);
764 static int make_unit_name(sd_bus
*bus
, UnitType t
, char **ret
) {
765 const char *unique
, *id
;
771 assert(t
< _UNIT_TYPE_MAX
);
773 r
= sd_bus_get_unique_name(bus
, &unique
);
777 /* We couldn't get the unique name, which is a pretty
778 * common case if we are connected to systemd
779 * directly. In that case, just pick a random uuid as
782 r
= sd_id128_randomize(&rnd
);
784 return log_error_errno(r
, "Failed to generate random run unit name: %m");
786 if (asprintf(ret
, "run-r" SD_ID128_FORMAT_STR
".%s", SD_ID128_FORMAT_VAL(rnd
), unit_type_to_string(t
)) < 0)
792 /* We managed to get the unique name, then let's use that to
793 * name our transient units. */
795 id
= startswith(unique
, ":1.");
797 log_error("Unique name %s has unexpected format.", unique
);
801 p
= strjoin("run-u", id
, ".", unit_type_to_string(t
));
809 typedef struct RunContext
{
815 /* The exit data of the unit */
817 uint64_t inactive_exit_usec
;
818 uint64_t inactive_enter_usec
;
820 uint64_t cpu_usage_nsec
;
821 uint64_t ip_ingress_bytes
;
822 uint64_t ip_egress_bytes
;
824 uint32_t exit_status
;
827 static void run_context_free(RunContext
*c
) {
830 c
->forward
= pty_forward_free(c
->forward
);
831 c
->match
= sd_bus_slot_unref(c
->match
);
832 c
->bus
= sd_bus_unref(c
->bus
);
833 c
->event
= sd_event_unref(c
->event
);
835 free(c
->active_state
);
839 static void run_context_check_done(RunContext
*c
) {
845 done
= STRPTR_IN_SET(c
->active_state
, "inactive", "failed");
849 if (c
->forward
&& done
) /* If the service is gone, it's time to drain the output */
850 done
= pty_forward_drain(c
->forward
);
853 sd_event_exit(c
->event
, EXIT_SUCCESS
);
856 static int run_context_update(RunContext
*c
, const char *path
) {
858 static const struct bus_properties_map map
[] = {
859 { "ActiveState", "s", NULL
, offsetof(RunContext
, active_state
) },
860 { "InactiveExitTimestampMonotonic", "t", NULL
, offsetof(RunContext
, inactive_exit_usec
) },
861 { "InactiveEnterTimestampMonotonic", "t", NULL
, offsetof(RunContext
, inactive_enter_usec
) },
862 { "Result", "s", NULL
, offsetof(RunContext
, result
) },
863 { "ExecMainCode", "i", NULL
, offsetof(RunContext
, exit_code
) },
864 { "ExecMainStatus", "i", NULL
, offsetof(RunContext
, exit_status
) },
865 { "CPUUsageNSec", "t", NULL
, offsetof(RunContext
, cpu_usage_nsec
) },
866 { "IPIngressBytes", "t", NULL
, offsetof(RunContext
, ip_ingress_bytes
) },
867 { "IPEgressBytes", "t", NULL
, offsetof(RunContext
, ip_egress_bytes
) },
871 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
874 r
= bus_map_all_properties(c
->bus
,
875 "org.freedesktop.systemd1",
881 sd_event_exit(c
->event
, EXIT_FAILURE
);
882 return log_error_errno(r
, "Failed to query unit state: %s", bus_error_message(&error
, r
));
885 run_context_check_done(c
);
889 static int on_properties_changed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
890 RunContext
*c
= userdata
;
895 return run_context_update(c
, sd_bus_message_get_path(m
));
898 static int pty_forward_handler(PTYForward
*f
, int rcode
, void *userdata
) {
899 RunContext
*c
= userdata
;
904 sd_event_exit(c
->event
, EXIT_FAILURE
);
905 return log_error_errno(rcode
, "Error on PTY forwarding logic: %m");
908 run_context_check_done(c
);
912 static int start_transient_service(
917 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
918 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
919 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
920 _cleanup_free_
char *service
= NULL
, *pty_path
= NULL
;
921 _cleanup_close_
int master
= -1;
928 if (arg_stdio
== ARG_STDIO_PTY
) {
930 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
931 master
= posix_openpt(O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NDELAY
);
933 return log_error_errno(errno
, "Failed to acquire pseudo tty: %m");
935 r
= ptsname_malloc(master
, &pty_path
);
937 return log_error_errno(r
, "Failed to determine tty name: %m");
939 if (unlockpt(master
) < 0)
940 return log_error_errno(errno
, "Failed to unlock tty: %m");
942 } else if (arg_transport
== BUS_TRANSPORT_MACHINE
) {
943 _cleanup_(sd_bus_unrefp
) sd_bus
*system_bus
= NULL
;
944 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*pty_reply
= NULL
;
947 r
= sd_bus_default_system(&system_bus
);
949 return log_error_errno(r
, "Failed to connect to system bus: %m");
951 r
= sd_bus_call_method(system_bus
,
952 "org.freedesktop.machine1",
953 "/org/freedesktop/machine1",
954 "org.freedesktop.machine1.Manager",
960 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
964 r
= sd_bus_message_read(pty_reply
, "hs", &master
, &s
);
966 return bus_log_parse_error(r
);
968 master
= fcntl(master
, F_DUPFD_CLOEXEC
, 3);
970 return log_error_errno(errno
, "Failed to duplicate master fd: %m");
972 pty_path
= strdup(s
);
976 assert_not_reached("Can't allocate tty via ssh");
980 r
= bus_wait_for_jobs_new(bus
, &w
);
982 return log_error_errno(r
, "Could not watch jobs: %m");
986 r
= unit_name_mangle_with_suffix(arg_unit
, UNIT_NAME_NOGLOB
, ".service", &service
);
988 return log_error_errno(r
, "Failed to mangle unit name: %m");
990 r
= make_unit_name(bus
, UNIT_SERVICE
, &service
);
995 r
= sd_bus_message_new_method_call(
998 "org.freedesktop.systemd1",
999 "/org/freedesktop/systemd1",
1000 "org.freedesktop.systemd1.Manager",
1001 "StartTransientUnit");
1003 return bus_log_create_error(r
);
1005 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1007 return bus_log_create_error(r
);
1010 r
= sd_bus_message_append(m
, "ss", service
, "fail");
1012 return bus_log_create_error(r
);
1015 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
1017 return bus_log_create_error(r
);
1019 r
= transient_service_set_properties(m
, argv
, pty_path
);
1021 return bus_log_create_error(r
);
1023 r
= sd_bus_message_close_container(m
);
1025 return bus_log_create_error(r
);
1027 /* Auxiliary units */
1028 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
1030 return bus_log_create_error(r
);
1032 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1034 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1036 return log_error_errno(r
, "Failed to start transient service unit: %s", bus_error_message(&error
, r
));
1041 r
= sd_bus_message_read(reply
, "o", &object
);
1043 return bus_log_parse_error(r
);
1045 r
= bus_wait_for_jobs_one(w
, object
, arg_quiet
);
1051 log_info("Running as unit: %s", service
);
1053 if (arg_wait
|| arg_stdio
!= ARG_STDIO_NONE
) {
1054 _cleanup_(run_context_free
) RunContext c
= {
1055 .cpu_usage_nsec
= NSEC_INFINITY
,
1056 .ip_ingress_bytes
= UINT64_MAX
,
1057 .ip_egress_bytes
= UINT64_MAX
,
1058 .inactive_exit_usec
= USEC_INFINITY
,
1059 .inactive_enter_usec
= USEC_INFINITY
,
1061 _cleanup_free_
char *path
= NULL
;
1064 c
.bus
= sd_bus_ref(bus
);
1066 r
= sd_event_default(&c
.event
);
1068 return log_error_errno(r
, "Failed to get event loop: %m");
1071 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1072 (void) sd_event_add_signal(c
.event
, NULL
, SIGINT
, NULL
, NULL
);
1073 (void) sd_event_add_signal(c
.event
, NULL
, SIGTERM
, NULL
, NULL
);
1076 log_info("Press ^] three times within 1s to disconnect TTY.");
1078 r
= pty_forward_new(c
.event
, master
, PTY_FORWARD_IGNORE_INITIAL_VHANGUP
, &c
.forward
);
1080 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1082 pty_forward_set_handler(c
.forward
, pty_forward_handler
, &c
);
1085 path
= unit_dbus_path_from_name(service
);
1089 mt
= strjoina("type='signal',"
1090 "sender='org.freedesktop.systemd1',"
1091 "path='", path
, "',"
1092 "interface='org.freedesktop.DBus.Properties',"
1093 "member='PropertiesChanged'");
1094 r
= sd_bus_add_match(bus
, &c
.match
, mt
, on_properties_changed
, &c
);
1096 return log_error_errno(r
, "Failed to add properties changed signal.");
1098 r
= sd_bus_attach_event(bus
, c
.event
, 0);
1100 return log_error_errno(r
, "Failed to attach bus to event loop.");
1102 r
= run_context_update(&c
, path
);
1106 r
= sd_event_loop(c
.event
);
1108 return log_error_errno(r
, "Failed to run event loop: %m");
1113 r
= pty_forward_get_last_char(c
.forward
, &last_char
);
1114 if (r
>= 0 && !arg_quiet
&& last_char
!= '\n')
1115 fputc('\n', stdout
);
1118 if (arg_wait
&& !arg_quiet
) {
1120 /* Explicitly destroy the PTY forwarder, so that the PTY device is usable again, in its
1121 * original settings (i.e. proper line breaks), so that we can show the summary in a pretty
1123 c
.forward
= pty_forward_free(c
.forward
);
1125 if (!isempty(c
.result
))
1126 log_info("Finished with result: %s", strna(c
.result
));
1128 if (c
.exit_code
== CLD_EXITED
)
1129 log_info("Main processes terminated with: code=%s/status=%i", sigchld_code_to_string(c
.exit_code
), c
.exit_status
);
1130 else if (c
.exit_code
> 0)
1131 log_info("Main processes terminated with: code=%s/status=%s", sigchld_code_to_string(c
.exit_code
), signal_to_string(c
.exit_status
));
1133 if (c
.inactive_enter_usec
> 0 && c
.inactive_enter_usec
!= USEC_INFINITY
&&
1134 c
.inactive_exit_usec
> 0 && c
.inactive_exit_usec
!= USEC_INFINITY
&&
1135 c
.inactive_enter_usec
> c
.inactive_exit_usec
) {
1136 char ts
[FORMAT_TIMESPAN_MAX
];
1137 log_info("Service runtime: %s", format_timespan(ts
, sizeof(ts
), c
.inactive_enter_usec
- c
.inactive_exit_usec
, USEC_PER_MSEC
));
1140 if (c
.cpu_usage_nsec
!= NSEC_INFINITY
) {
1141 char ts
[FORMAT_TIMESPAN_MAX
];
1142 log_info("CPU time consumed: %s", format_timespan(ts
, sizeof(ts
), (c
.cpu_usage_nsec
+ NSEC_PER_USEC
- 1) / NSEC_PER_USEC
, USEC_PER_MSEC
));
1145 if (c
.ip_ingress_bytes
!= UINT64_MAX
) {
1146 char bytes
[FORMAT_BYTES_MAX
];
1147 log_info("IP traffic received: %s", format_bytes(bytes
, sizeof(bytes
), c
.ip_ingress_bytes
));
1149 if (c
.ip_egress_bytes
!= UINT64_MAX
) {
1150 char bytes
[FORMAT_BYTES_MAX
];
1151 log_info("IP traffic sent: %s", format_bytes(bytes
, sizeof(bytes
), c
.ip_egress_bytes
));
1155 /* Try to propagate the service's return value */
1156 if (c
.result
&& STR_IN_SET(c
.result
, "success", "exit-code") && c
.exit_code
== CLD_EXITED
)
1157 *retval
= c
.exit_status
;
1159 *retval
= EXIT_FAILURE
;
1165 static int start_transient_scope(
1169 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1170 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1171 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1172 _cleanup_strv_free_
char **env
= NULL
, **user_env
= NULL
;
1173 _cleanup_free_
char *scope
= NULL
;
1174 const char *object
= NULL
;
1180 r
= bus_wait_for_jobs_new(bus
, &w
);
1185 r
= unit_name_mangle_with_suffix(arg_unit
, UNIT_NAME_NOGLOB
, ".scope", &scope
);
1187 return log_error_errno(r
, "Failed to mangle scope name: %m");
1189 r
= make_unit_name(bus
, UNIT_SCOPE
, &scope
);
1194 r
= sd_bus_message_new_method_call(
1197 "org.freedesktop.systemd1",
1198 "/org/freedesktop/systemd1",
1199 "org.freedesktop.systemd1.Manager",
1200 "StartTransientUnit");
1202 return bus_log_create_error(r
);
1204 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1206 return bus_log_create_error(r
);
1209 r
= sd_bus_message_append(m
, "ss", scope
, "fail");
1211 return bus_log_create_error(r
);
1214 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
1216 return bus_log_create_error(r
);
1218 r
= transient_scope_set_properties(m
);
1220 return bus_log_create_error(r
);
1222 r
= sd_bus_message_close_container(m
);
1224 return bus_log_create_error(r
);
1226 /* Auxiliary units */
1227 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
1229 return bus_log_create_error(r
);
1231 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1233 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1235 log_error("Failed to start transient scope unit: %s", bus_error_message(&error
, -r
));
1240 if (setpriority(PRIO_PROCESS
, 0, arg_nice
) < 0)
1241 return log_error_errno(errno
, "Failed to set nice level: %m");
1244 if (arg_exec_group
) {
1247 r
= get_group_creds(&arg_exec_group
, &gid
);
1249 return log_error_errno(r
, "Failed to resolve group %s: %m", arg_exec_group
);
1251 if (setresgid(gid
, gid
, gid
) < 0)
1252 return log_error_errno(errno
, "Failed to change GID to " GID_FMT
": %m", gid
);
1255 if (arg_exec_user
) {
1256 const char *home
, *shell
;
1260 r
= get_user_creds_clean(&arg_exec_user
, &uid
, &gid
, &home
, &shell
);
1262 return log_error_errno(r
, "Failed to resolve user %s: %m", arg_exec_user
);
1265 r
= strv_extendf(&user_env
, "HOME=%s", home
);
1271 r
= strv_extendf(&user_env
, "SHELL=%s", shell
);
1276 r
= strv_extendf(&user_env
, "USER=%s", arg_exec_user
);
1280 r
= strv_extendf(&user_env
, "LOGNAME=%s", arg_exec_user
);
1284 if (!arg_exec_group
) {
1285 if (setresgid(gid
, gid
, gid
) < 0)
1286 return log_error_errno(errno
, "Failed to change GID to " GID_FMT
": %m", gid
);
1289 if (setresuid(uid
, uid
, uid
) < 0)
1290 return log_error_errno(errno
, "Failed to change UID to " UID_FMT
": %m", uid
);
1293 env
= strv_env_merge(3, environ
, user_env
, arg_environment
);
1297 r
= sd_bus_message_read(reply
, "o", &object
);
1299 return bus_log_parse_error(r
);
1301 r
= bus_wait_for_jobs_one(w
, object
, arg_quiet
);
1306 log_info("Running scope as unit: %s", scope
);
1308 execvpe(argv
[0], argv
, env
);
1310 return log_error_errno(errno
, "Failed to execute: %m");
1313 static int start_transient_timer(
1317 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1318 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1319 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1320 _cleanup_free_
char *timer
= NULL
, *service
= NULL
;
1321 const char *object
= NULL
;
1327 r
= bus_wait_for_jobs_new(bus
, &w
);
1332 switch (unit_name_to_type(arg_unit
)) {
1335 service
= strdup(arg_unit
);
1339 r
= unit_name_change_suffix(service
, ".timer", &timer
);
1341 return log_error_errno(r
, "Failed to change unit suffix: %m");
1345 timer
= strdup(arg_unit
);
1349 r
= unit_name_change_suffix(timer
, ".service", &service
);
1351 return log_error_errno(r
, "Failed to change unit suffix: %m");
1355 r
= unit_name_mangle_with_suffix(arg_unit
, UNIT_NAME_NOGLOB
, ".service", &service
);
1357 return log_error_errno(r
, "Failed to mangle unit name: %m");
1359 r
= unit_name_mangle_with_suffix(arg_unit
, UNIT_NAME_NOGLOB
, ".timer", &timer
);
1361 return log_error_errno(r
, "Failed to mangle unit name: %m");
1366 r
= make_unit_name(bus
, UNIT_SERVICE
, &service
);
1370 r
= unit_name_change_suffix(service
, ".timer", &timer
);
1372 return log_error_errno(r
, "Failed to change unit suffix: %m");
1375 r
= sd_bus_message_new_method_call(
1378 "org.freedesktop.systemd1",
1379 "/org/freedesktop/systemd1",
1380 "org.freedesktop.systemd1.Manager",
1381 "StartTransientUnit");
1383 return bus_log_create_error(r
);
1385 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1387 return bus_log_create_error(r
);
1390 r
= sd_bus_message_append(m
, "ss", timer
, "fail");
1392 return bus_log_create_error(r
);
1395 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
1397 return bus_log_create_error(r
);
1399 r
= transient_timer_set_properties(m
);
1401 return bus_log_create_error(r
);
1403 r
= sd_bus_message_close_container(m
);
1405 return bus_log_create_error(r
);
1407 r
= sd_bus_message_open_container(m
, 'a', "(sa(sv))");
1409 return bus_log_create_error(r
);
1411 if (!strv_isempty(argv
)) {
1412 r
= sd_bus_message_open_container(m
, 'r', "sa(sv)");
1414 return bus_log_create_error(r
);
1416 r
= sd_bus_message_append(m
, "s", service
);
1418 return bus_log_create_error(r
);
1420 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
1422 return bus_log_create_error(r
);
1424 r
= transient_service_set_properties(m
, argv
, NULL
);
1426 return bus_log_create_error(r
);
1428 r
= sd_bus_message_close_container(m
);
1430 return bus_log_create_error(r
);
1432 r
= sd_bus_message_close_container(m
);
1434 return bus_log_create_error(r
);
1437 r
= sd_bus_message_close_container(m
);
1439 return bus_log_create_error(r
);
1441 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1443 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1445 log_error("Failed to start transient timer unit: %s", bus_error_message(&error
, -r
));
1449 r
= sd_bus_message_read(reply
, "o", &object
);
1451 return bus_log_parse_error(r
);
1453 r
= bus_wait_for_jobs_one(w
, object
, arg_quiet
);
1458 log_info("Running timer as unit: %s", timer
);
1460 log_info("Will run service as unit: %s", service
);
1466 int main(int argc
, char* argv
[]) {
1467 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1468 _cleanup_free_
char *description
= NULL
, *command
= NULL
;
1469 int r
, retval
= EXIT_SUCCESS
;
1471 log_parse_environment();
1474 r
= parse_argv(argc
, argv
);
1478 if (argc
> optind
&& arg_transport
== BUS_TRANSPORT_LOCAL
) {
1479 /* Patch in an absolute path */
1481 r
= find_binary(argv
[optind
], &command
);
1483 log_error_errno(r
, "Failed to find executable %s: %m", argv
[optind
]);
1487 argv
[optind
] = command
;
1490 if (!arg_description
) {
1491 description
= strv_join(argv
+ optind
, " ");
1497 if (arg_unit
&& isempty(description
)) {
1498 r
= free_and_strdup(&description
, arg_unit
);
1503 arg_description
= description
;
1506 /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
1508 if (arg_wait
|| arg_stdio
!= ARG_STDIO_NONE
)
1509 r
= bus_connect_transport(arg_transport
, arg_host
, arg_user
, &bus
);
1511 r
= bus_connect_transport_systemd(arg_transport
, arg_host
, arg_user
, &bus
);
1513 log_error_errno(r
, "Failed to create bus connection: %m");
1518 r
= start_transient_scope(bus
, argv
+ optind
);
1519 else if (with_timer())
1520 r
= start_transient_timer(bus
, argv
+ optind
);
1522 r
= start_transient_service(bus
, argv
+ optind
, &retval
);
1525 strv_free(arg_environment
);
1526 strv_free(arg_property
);
1527 strv_free(arg_timer_property
);
1529 return r
< 0 ? EXIT_FAILURE
: retval
;