1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "event-util.h"
31 #include "unit-name.h"
33 #include "path-util.h"
34 #include "bus-error.h"
35 #include "calendarspec.h"
38 static bool arg_scope = false;
39 static bool arg_remain_after_exit = false;
40 static const char *arg_unit = NULL;
41 static const char *arg_description = NULL;
42 static const char *arg_slice = NULL;
43 static bool arg_send_sighup = false;
44 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
45 static const char *arg_host = NULL;
46 static bool arg_user = false;
47 static const char *arg_service_type = NULL;
48 static const char *arg_exec_user = NULL;
49 static const char *arg_exec_group = NULL;
50 static int arg_nice = 0;
51 static bool arg_nice_set = false;
52 static char **arg_environment = NULL;
53 static char **arg_property = NULL;
54 static bool arg_pty = false;
55 static usec_t arg_on_active = 0;
56 static usec_t arg_on_boot = 0;
57 static usec_t arg_on_startup = 0;
58 static usec_t arg_on_unit_active = 0;
59 static usec_t arg_on_unit_inactive = 0;
60 static char *arg_on_calendar = NULL;
61 static char **arg_timer_property = NULL;
62 static bool arg_quiet = false;
64 static void help(void) {
65 printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
66 "Run the specified command in a transient scope or service or timer\n"
67 "unit. If timer option is specified and unit is exist which is\n"
68 "specified with --unit option then command can be ommited.\n\n"
69 " -h --help Show this help\n"
70 " --version Show package version\n"
71 " --user Run as user unit\n"
72 " -H --host=[USER@]HOST Operate on remote host\n"
73 " -M --machine=CONTAINER Operate on local container\n"
74 " --scope Run this as scope rather than service\n"
75 " --unit=UNIT Run under the specified unit name\n"
76 " -p --property=NAME=VALUE Set unit property\n"
77 " --description=TEXT Description for unit\n"
78 " --slice=SLICE Run in the specified slice\n"
79 " -r --remain-after-exit Leave service around until explicitly stopped\n"
80 " --send-sighup Send SIGHUP when terminating\n"
81 " --service-type=TYPE Service type\n"
82 " --uid=USER Run as system user\n"
83 " --gid=GROUP Run as system group\n"
84 " --nice=NICE Nice level\n"
85 " --setenv=NAME=VALUE Set environment\n"
86 " -t --pty Run service on pseudo tty\n"
87 " -q --quiet Suppress information messages during runtime\n\n"
89 " --on-active=SECONDS Run after SECONDS delay\n"
90 " --on-boot=SECONDS Run SECONDS after machine was booted up\n"
91 " --on-startup=SECONDS Run SECONDS after systemd activation\n"
92 " --on-unit-active=SECONDS Run SECONDS after the last activation\n"
93 " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
94 " --on-calendar=SPEC Realtime timer\n"
95 " --timer-property=NAME=VALUE Set timer unit property\n",
96 program_invocation_short_name);
99 static bool with_timer(void) {
100 return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
103 static int parse_argv(int argc, char *argv[]) {
124 ARG_ON_UNIT_INACTIVE,
129 static const struct option options[] = {
130 { "help", no_argument, NULL, 'h' },
131 { "version", no_argument, NULL, ARG_VERSION },
132 { "user", no_argument, NULL, ARG_USER },
133 { "system", no_argument, NULL, ARG_SYSTEM },
134 { "scope", no_argument, NULL, ARG_SCOPE },
135 { "unit", required_argument, NULL, ARG_UNIT },
136 { "description", required_argument, NULL, ARG_DESCRIPTION },
137 { "slice", required_argument, NULL, ARG_SLICE },
138 { "remain-after-exit", no_argument, NULL, 'r' },
139 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
140 { "host", required_argument, NULL, 'H' },
141 { "machine", required_argument, NULL, 'M' },
142 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
143 { "uid", required_argument, NULL, ARG_EXEC_USER },
144 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
145 { "nice", required_argument, NULL, ARG_NICE },
146 { "setenv", required_argument, NULL, ARG_SETENV },
147 { "property", required_argument, NULL, 'p' },
148 { "tty", no_argument, NULL, 't' },
149 { "quiet", no_argument, NULL, 'q' },
150 { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
151 { "on-boot", required_argument, NULL, ARG_ON_BOOT },
152 { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
153 { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
154 { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
155 { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
156 { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
161 CalendarSpec *spec = NULL;
166 while ((c = getopt_long(argc, argv, "+hrH:M:p:tq", options, NULL)) >= 0)
175 puts(PACKAGE_STRING);
176 puts(SYSTEMD_FEATURES);
195 case ARG_DESCRIPTION:
196 arg_description = optarg;
203 case ARG_SEND_SIGHUP:
204 arg_send_sighup = true;
208 arg_remain_after_exit = true;
212 arg_transport = BUS_TRANSPORT_REMOTE;
217 arg_transport = BUS_TRANSPORT_MACHINE;
221 case ARG_SERVICE_TYPE:
222 arg_service_type = optarg;
226 arg_exec_user = optarg;
230 arg_exec_group = optarg;
234 r = safe_atoi(optarg, &arg_nice);
235 if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
236 log_error("Failed to parse nice value");
245 if (strv_extend(&arg_environment, optarg) < 0)
252 if (strv_extend(&arg_property, optarg) < 0)
267 r = parse_sec(optarg, &arg_on_active);
269 log_error("Failed to parse timer value: %s", optarg);
277 r = parse_sec(optarg, &arg_on_boot);
279 log_error("Failed to parse timer value: %s", optarg);
287 r = parse_sec(optarg, &arg_on_startup);
289 log_error("Failed to parse timer value: %s", optarg);
295 case ARG_ON_UNIT_ACTIVE:
297 r = parse_sec(optarg, &arg_on_unit_active);
299 log_error("Failed to parse timer value: %s", optarg);
305 case ARG_ON_UNIT_INACTIVE:
307 r = parse_sec(optarg, &arg_on_unit_inactive);
309 log_error("Failed to parse timer value: %s", optarg);
315 case ARG_ON_CALENDAR:
317 r = calendar_spec_from_string(optarg, &spec);
319 log_error("Invalid calendar spec: %s", optarg);
323 arg_on_calendar = optarg;
326 case ARG_TIMER_PROPERTY:
328 if (strv_extend(&arg_timer_property, optarg) < 0)
337 assert_not_reached("Unhandled option");
340 if ((optind >= argc) && (!arg_unit || !with_timer())) {
341 log_error("Command line to execute required.");
345 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
346 log_error("Execution in user context is not supported on non-local systems.");
350 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
351 log_error("Scope execution is not supported on non-local systems.");
355 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
356 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
360 if (arg_pty && (with_timer() || arg_scope)) {
361 log_error("--pty is not compatible in timer or --scope mode.");
365 if (arg_scope && with_timer()) {
366 log_error("Timer options are not supported in --scope mode.");
370 if (arg_timer_property && !with_timer()) {
371 log_error("--timer-property= has no effect without any other timer options.");
378 static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
382 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
386 STRV_FOREACH(i, properties) {
387 r = sd_bus_message_open_container(m, 'r', "sv");
391 r = bus_append_unit_property_assignment(m, *i);
393 r = sd_bus_message_append(m, "sv", 0);
398 r = sd_bus_message_close_container(m);
406 static int transient_cgroup_set_properties(sd_bus_message *m) {
410 if (!isempty(arg_slice)) {
411 _cleanup_free_ char *slice;
413 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
417 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
425 static int transient_kill_set_properties(sd_bus_message *m) {
429 return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
434 static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
439 r = transient_unit_set_properties(m, arg_property);
443 r = transient_kill_set_properties(m);
447 r = transient_cgroup_set_properties(m);
451 if (arg_remain_after_exit) {
452 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
457 if (arg_service_type) {
458 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
464 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
469 if (arg_exec_group) {
470 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
476 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
484 r = sd_bus_message_append(m,
486 "StandardInput", "s", "tty",
487 "StandardOutput", "s", "tty",
488 "StandardError", "s", "tty",
489 "TTYPath", "s", pty_path);
497 n = strjoina("TERM=", e);
498 r = sd_bus_message_append(m,
500 "Environment", "as", 1, n);
506 if (!strv_isempty(arg_environment)) {
507 r = sd_bus_message_open_container(m, 'r', "sv");
511 r = sd_bus_message_append(m, "s", "Environment");
515 r = sd_bus_message_open_container(m, 'v', "as");
519 r = sd_bus_message_append_strv(m, arg_environment);
523 r = sd_bus_message_close_container(m);
527 r = sd_bus_message_close_container(m);
534 r = sd_bus_message_open_container(m, 'r', "sv");
538 r = sd_bus_message_append(m, "s", "ExecStart");
542 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
546 r = sd_bus_message_open_container(m, 'a', "(sasb)");
550 r = sd_bus_message_open_container(m, 'r', "sasb");
554 r = sd_bus_message_append(m, "s", argv[0]);
558 r = sd_bus_message_append_strv(m, argv);
562 r = sd_bus_message_append(m, "b", false);
566 r = sd_bus_message_close_container(m);
570 r = sd_bus_message_close_container(m);
574 r = sd_bus_message_close_container(m);
578 r = sd_bus_message_close_container(m);
586 static int transient_scope_set_properties(sd_bus_message *m) {
591 r = transient_unit_set_properties(m, arg_property);
595 r = transient_kill_set_properties(m);
599 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
606 static int transient_timer_set_properties(sd_bus_message *m) {
611 r = transient_unit_set_properties(m, arg_timer_property);
616 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
622 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
627 if (arg_on_startup) {
628 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
633 if (arg_on_unit_active) {
634 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
639 if (arg_on_unit_inactive) {
640 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
645 if (arg_on_calendar) {
646 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
654 static int start_transient_service(
658 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
659 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
660 _cleanup_free_ char *service = NULL, *pty_path = NULL;
661 _cleanup_close_ int master = -1;
669 if (arg_transport == BUS_TRANSPORT_LOCAL) {
670 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
672 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
674 r = ptsname_malloc(master, &pty_path);
676 return log_error_errno(r, "Failed to determine tty name: %m");
678 } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
679 _cleanup_bus_unref_ sd_bus *system_bus = NULL;
680 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
683 r = sd_bus_open_system(&system_bus);
685 log_error_errno(r, "Failed to connect to system bus: %m");
687 r = sd_bus_call_method(system_bus,
688 "org.freedesktop.machine1",
689 "/org/freedesktop/machine1",
690 "org.freedesktop.machine1.Manager",
696 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
700 r = sd_bus_message_read(reply, "hs", &master, &s);
702 return bus_log_parse_error(r);
704 master = fcntl(master, F_DUPFD_CLOEXEC, 3);
706 return log_error_errno(errno, "Failed to duplicate master fd: %m");
708 pty_path = strdup(s);
712 assert_not_reached("Can't allocate tty via ssh");
714 if (unlockpt(master) < 0)
715 return log_error_errno(errno, "Failed to unlock tty: %m");
719 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
722 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
725 r = sd_bus_message_new_method_call(
728 "org.freedesktop.systemd1",
729 "/org/freedesktop/systemd1",
730 "org.freedesktop.systemd1.Manager",
731 "StartTransientUnit");
733 return bus_log_create_error(r);
736 r = sd_bus_message_append(m, "ss", service, "fail");
738 return bus_log_create_error(r);
741 r = sd_bus_message_open_container(m, 'a', "(sv)");
743 return bus_log_create_error(r);
745 r = transient_service_set_properties(m, argv, pty_path);
747 return bus_log_create_error(r);
749 r = sd_bus_message_close_container(m);
751 return bus_log_create_error(r);
753 /* Auxiliary units */
754 r = sd_bus_message_append(m, "a(sa(sv))", 0);
756 return bus_log_create_error(r);
758 r = sd_bus_call(bus, m, 0, &error, NULL);
760 log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r));
765 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
766 _cleanup_event_unref_ sd_event *event = NULL;
770 r = sd_event_default(&event);
772 return log_error_errno(r, "Failed to get event loop: %m");
774 assert_se(sigemptyset(&mask) == 0);
775 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
776 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
778 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
779 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
782 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
784 r = pty_forward_new(event, master, false, &forward);
786 return log_error_errno(r, "Failed to create PTY forwarder: %m");
788 r = sd_event_loop(event);
790 return log_error_errno(r, "Failed to run event loop: %m");
792 pty_forward_get_last_char(forward, &last_char);
794 forward = pty_forward_free(forward);
796 if (!arg_quiet && last_char != '\n')
799 } else if (!arg_quiet)
800 log_info("Running as unit %s.", service);
805 static int start_transient_scope(
809 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
810 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
811 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
812 _cleanup_free_ char *scope = NULL;
819 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
822 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
825 r = sd_bus_message_new_method_call(
828 "org.freedesktop.systemd1",
829 "/org/freedesktop/systemd1",
830 "org.freedesktop.systemd1.Manager",
831 "StartTransientUnit");
833 return bus_log_create_error(r);
836 r = sd_bus_message_append(m, "ss", scope, "fail");
838 return bus_log_create_error(r);
841 r = sd_bus_message_open_container(m, 'a', "(sv)");
843 return bus_log_create_error(r);
845 r = transient_scope_set_properties(m);
847 return bus_log_create_error(r);
849 r = sd_bus_message_close_container(m);
851 return bus_log_create_error(r);
853 /* Auxiliary units */
854 r = sd_bus_message_append(m, "a(sa(sv))", 0);
856 return bus_log_create_error(r);
858 r = sd_bus_call(bus, m, 0, &error, NULL);
860 log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
865 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
866 return log_error_errno(errno, "Failed to set nice level: %m");
869 if (arg_exec_group) {
872 r = get_group_creds(&arg_exec_group, &gid);
874 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
876 if (setresgid(gid, gid, gid) < 0)
877 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
881 const char *home, *shell;
885 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
887 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
889 r = strv_extendf(&user_env, "HOME=%s", home);
893 r = strv_extendf(&user_env, "SHELL=%s", shell);
897 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
901 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
905 if (!arg_exec_group) {
906 if (setresgid(gid, gid, gid) < 0)
907 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
910 if (setresuid(uid, uid, uid) < 0)
911 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
914 env = strv_env_merge(3, environ, user_env, arg_environment);
919 log_info("Running as unit %s.", scope);
921 execvpe(argv[0], argv, env);
923 return log_error_errno(errno, "Failed to execute: %m");
926 static int start_transient_timer(
930 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
931 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
932 _cleanup_free_ char *timer = NULL, *service = NULL;
939 switch(unit_name_to_type(arg_unit)) {
942 service = strdup(arg_unit);
946 timer = unit_name_change_suffix(service, ".timer");
952 timer = strdup(arg_unit);
956 service = unit_name_change_suffix(timer, ".service");
962 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
966 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
972 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
973 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
976 r = sd_bus_message_new_method_call(
979 "org.freedesktop.systemd1",
980 "/org/freedesktop/systemd1",
981 "org.freedesktop.systemd1.Manager",
982 "StartTransientUnit");
984 return bus_log_create_error(r);
987 r = sd_bus_message_append(m, "ss", timer, "fail");
989 return bus_log_create_error(r);
992 r = sd_bus_message_open_container(m, 'a', "(sv)");
994 return bus_log_create_error(r);
996 r = transient_timer_set_properties(m);
998 return bus_log_create_error(r);
1000 r = sd_bus_message_close_container(m);
1002 return bus_log_create_error(r);
1004 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
1006 return bus_log_create_error(r);
1009 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
1011 return bus_log_create_error(r);
1013 r = sd_bus_message_append(m, "s", service);
1015 return bus_log_create_error(r);
1017 r = sd_bus_message_open_container(m, 'a', "(sv)");
1019 return bus_log_create_error(r);
1021 r = transient_service_set_properties(m, argv, NULL);
1023 return bus_log_create_error(r);
1025 r = sd_bus_message_close_container(m);
1027 return bus_log_create_error(r);
1029 r = sd_bus_message_close_container(m);
1031 return bus_log_create_error(r);
1034 r = sd_bus_message_close_container(m);
1036 return bus_log_create_error(r);
1038 r = sd_bus_call(bus, m, 0, &error, NULL);
1040 log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
1044 log_info("Running as unit %s.", timer);
1046 log_info("Will run as unit %s.", service);
1051 int main(int argc, char* argv[]) {
1052 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1053 _cleanup_free_ char *description = NULL, *command = NULL;
1056 log_parse_environment();
1059 r = parse_argv(argc, argv);
1063 if (argc > optind) {
1064 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
1066 log_error_errno(r, "Failed to find executable %s%s: %m",
1068 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
1071 argv[optind] = command;
1074 if (!arg_description) {
1075 description = strv_join(argv + optind, " ");
1081 if (arg_unit && isempty(description)) {
1083 description = strdup(arg_unit);
1091 arg_description = description;
1094 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1096 log_error_errno(r, "Failed to create bus connection: %m");
1101 r = start_transient_scope(bus, argv + optind);
1102 else if (with_timer())
1103 r = start_transient_timer(bus, argv + optind);
1105 r = start_transient_service(bus, argv + optind);
1108 strv_free(arg_environment);
1109 strv_free(arg_property);
1110 strv_free(arg_timer_property);
1112 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;