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=SEC Run after seconds\n"
90 " --on-boot=SEC Run after seconds from machine was booted up\n"
91 " --on-startup=SEC Run after seconds from systemd was first started\n"
92 " --on-unit-active=SEC Run after seconds from the last activation\n"
93 " --on-unit-inactive=SEC Run after seconds from 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 if (arg_send_sighup) {
430 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
438 static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
443 r = transient_unit_set_properties(m, arg_property);
447 r = transient_kill_set_properties(m);
451 r = transient_cgroup_set_properties(m);
455 if (arg_remain_after_exit) {
456 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
461 if (arg_service_type) {
462 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
468 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
473 if (arg_exec_group) {
474 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
480 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
488 r = sd_bus_message_append(m,
490 "StandardInput", "s", "tty",
491 "StandardOutput", "s", "tty",
492 "StandardError", "s", "tty",
493 "TTYPath", "s", pty_path);
501 n = strappenda("TERM=", e);
502 r = sd_bus_message_append(m,
504 "Environment", "as", 1, n);
510 if (!strv_isempty(arg_environment)) {
511 r = sd_bus_message_open_container(m, 'r', "sv");
515 r = sd_bus_message_append(m, "s", "Environment");
519 r = sd_bus_message_open_container(m, 'v', "as");
523 r = sd_bus_message_append_strv(m, arg_environment);
527 r = sd_bus_message_close_container(m);
531 r = sd_bus_message_close_container(m);
538 r = sd_bus_message_open_container(m, 'r', "sv");
542 r = sd_bus_message_append(m, "s", "ExecStart");
546 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
550 r = sd_bus_message_open_container(m, 'a', "(sasb)");
554 r = sd_bus_message_open_container(m, 'r', "sasb");
558 r = sd_bus_message_append(m, "s", argv[0]);
562 r = sd_bus_message_append_strv(m, argv);
566 r = sd_bus_message_append(m, "b", false);
570 r = sd_bus_message_close_container(m);
574 r = sd_bus_message_close_container(m);
578 r = sd_bus_message_close_container(m);
582 r = sd_bus_message_close_container(m);
590 static int transient_scope_set_properties(sd_bus_message *m) {
595 r = transient_unit_set_properties(m, arg_property);
599 r = transient_kill_set_properties(m);
603 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
610 static int transient_timer_set_properties(sd_bus_message *m) {
615 r = transient_unit_set_properties(m, arg_timer_property);
620 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
626 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
631 if (arg_on_startup) {
632 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
637 if (arg_on_unit_active) {
638 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
643 if (arg_on_unit_inactive) {
644 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
649 if (arg_on_calendar) {
650 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
658 static int start_transient_service(
662 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
663 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
664 _cleanup_free_ char *service = NULL, *pty_path = NULL;
665 _cleanup_close_ int master = -1;
673 if (arg_transport == BUS_TRANSPORT_LOCAL) {
674 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
676 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
678 r = ptsname_malloc(master, &pty_path);
680 return log_error_errno(r, "Failed to determine tty name: %m");
682 } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
683 _cleanup_bus_unref_ sd_bus *system_bus = NULL;
684 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
687 r = sd_bus_open_system(&system_bus);
689 log_error_errno(r, "Failed to connect to system bus: %m");
691 r = sd_bus_call_method(system_bus,
692 "org.freedesktop.machine1",
693 "/org/freedesktop/machine1",
694 "org.freedesktop.machine1.Manager",
700 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
704 r = sd_bus_message_read(reply, "hs", &master, &s);
706 return bus_log_parse_error(r);
708 master = fcntl(master, F_DUPFD_CLOEXEC, 3);
710 return log_error_errno(errno, "Failed to duplicate master fd: %m");
712 pty_path = strdup(s);
716 assert_not_reached("Can't allocate tty via ssh");
718 if (unlockpt(master) < 0)
719 return log_error_errno(errno, "Failed to unlock tty: %m");
723 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
726 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
729 r = sd_bus_message_new_method_call(
732 "org.freedesktop.systemd1",
733 "/org/freedesktop/systemd1",
734 "org.freedesktop.systemd1.Manager",
735 "StartTransientUnit");
737 return bus_log_create_error(r);
740 r = sd_bus_message_append(m, "ss", service, "fail");
742 return bus_log_create_error(r);
745 r = sd_bus_message_open_container(m, 'a', "(sv)");
747 return bus_log_create_error(r);
749 r = transient_service_set_properties(m, argv, pty_path);
751 return bus_log_create_error(r);
753 r = sd_bus_message_close_container(m);
755 return bus_log_create_error(r);
757 /* Auxiliary units */
758 r = sd_bus_message_append(m, "a(sa(sv))", 0);
760 return bus_log_create_error(r);
762 r = sd_bus_call(bus, m, 0, &error, NULL);
764 log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r));
769 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
770 _cleanup_event_unref_ sd_event *event = NULL;
774 r = sd_event_default(&event);
776 return log_error_errno(r, "Failed to get event loop: %m");
778 assert_se(sigemptyset(&mask) == 0);
779 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
780 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
782 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
783 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
786 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
788 r = pty_forward_new(event, master, false, &forward);
790 return log_error_errno(r, "Failed to create PTY forwarder: %m");
792 r = sd_event_loop(event);
794 return log_error_errno(r, "Failed to run event loop: %m");
796 pty_forward_last_char(forward, &last_char);
798 forward = pty_forward_free(forward);
800 if (!arg_quiet && last_char != '\n')
803 } else if (!arg_quiet)
804 log_info("Running as unit %s.", service);
809 static int start_transient_scope(
813 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
814 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
815 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
816 _cleanup_free_ char *scope = NULL;
823 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
826 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
829 r = sd_bus_message_new_method_call(
832 "org.freedesktop.systemd1",
833 "/org/freedesktop/systemd1",
834 "org.freedesktop.systemd1.Manager",
835 "StartTransientUnit");
837 return bus_log_create_error(r);
840 r = sd_bus_message_append(m, "ss", scope, "fail");
842 return bus_log_create_error(r);
845 r = sd_bus_message_open_container(m, 'a', "(sv)");
847 return bus_log_create_error(r);
849 r = transient_scope_set_properties(m);
851 return bus_log_create_error(r);
853 r = sd_bus_message_close_container(m);
855 return bus_log_create_error(r);
857 /* Auxiliary units */
858 r = sd_bus_message_append(m, "a(sa(sv))", 0);
860 return bus_log_create_error(r);
862 r = sd_bus_call(bus, m, 0, &error, NULL);
864 log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
869 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
870 return log_error_errno(errno, "Failed to set nice level: %m");
873 if (arg_exec_group) {
876 r = get_group_creds(&arg_exec_group, &gid);
878 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
880 if (setresgid(gid, gid, gid) < 0)
881 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
885 const char *home, *shell;
889 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
891 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
893 r = strv_extendf(&user_env, "HOME=%s", home);
897 r = strv_extendf(&user_env, "SHELL=%s", shell);
901 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
905 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
909 if (!arg_exec_group) {
910 if (setresgid(gid, gid, gid) < 0)
911 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
914 if (setresuid(uid, uid, uid) < 0)
915 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
918 env = strv_env_merge(3, environ, user_env, arg_environment);
923 log_info("Running as unit %s.", scope);
925 execvpe(argv[0], argv, env);
927 return log_error_errno(errno, "Failed to execute: %m");
930 static int start_transient_timer(
934 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
935 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
936 _cleanup_free_ char *timer = NULL, *service = NULL;
943 switch(unit_name_to_type(arg_unit)) {
946 service = strdup(arg_unit);
950 timer = unit_name_change_suffix(service, ".timer");
956 timer = strdup(arg_unit);
960 service = unit_name_change_suffix(timer, ".service");
966 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
970 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
976 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
977 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
980 r = sd_bus_message_new_method_call(
983 "org.freedesktop.systemd1",
984 "/org/freedesktop/systemd1",
985 "org.freedesktop.systemd1.Manager",
986 "StartTransientUnit");
988 return bus_log_create_error(r);
991 r = sd_bus_message_append(m, "ss", timer, "fail");
993 return bus_log_create_error(r);
996 r = sd_bus_message_open_container(m, 'a', "(sv)");
998 return bus_log_create_error(r);
1000 r = transient_timer_set_properties(m);
1002 return bus_log_create_error(r);
1004 r = sd_bus_message_close_container(m);
1006 return bus_log_create_error(r);
1008 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
1010 return bus_log_create_error(r);
1013 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
1015 return bus_log_create_error(r);
1017 r = sd_bus_message_append(m, "s", service);
1019 return bus_log_create_error(r);
1021 r = sd_bus_message_open_container(m, 'a', "(sv)");
1023 return bus_log_create_error(r);
1025 r = transient_service_set_properties(m, argv, NULL);
1027 return bus_log_create_error(r);
1029 r = sd_bus_message_close_container(m);
1031 return bus_log_create_error(r);
1033 r = sd_bus_message_close_container(m);
1035 return bus_log_create_error(r);
1038 r = sd_bus_message_close_container(m);
1040 return bus_log_create_error(r);
1042 r = sd_bus_call(bus, m, 0, &error, NULL);
1044 log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
1048 log_info("Running as unit %s.", timer);
1050 log_info("Will run as unit %s.", service);
1055 int main(int argc, char* argv[]) {
1056 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1057 _cleanup_free_ char *description = NULL, *command = NULL;
1060 log_parse_environment();
1063 r = parse_argv(argc, argv);
1067 if (argc > optind) {
1068 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
1070 log_error_errno(r, "Failed to find executable %s%s: %m",
1072 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
1075 argv[optind] = command;
1078 if (!arg_description) {
1079 description = strv_join(argv + optind, " ");
1085 if (arg_unit && isempty(description)) {
1087 description = strdup(arg_unit);
1095 arg_description = description;
1098 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1100 log_error_errno(r, "Failed to create bus connection: %m");
1105 r = start_transient_scope(bus, argv + optind);
1106 else if (with_timer())
1107 r = start_transient_timer(bus, argv + optind);
1109 r = start_transient_service(bus, argv + optind);
1112 strv_free(arg_environment);
1113 strv_free(arg_property);
1114 strv_free(arg_timer_property);
1116 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;