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_CONTAINER;
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(
661 sd_bus_error *error) {
663 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
664 _cleanup_free_ char *service = NULL;
665 _cleanup_close_ int master = -1;
666 const char *pty_path = NULL;
673 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
675 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
677 pty_path = ptsname(master);
679 return log_error_errno(errno, "Failed to determine tty name: %m");
681 if (unlockpt(master) < 0)
682 return log_error_errno(errno, "Failed to unlock tty: %m");
686 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
689 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
692 r = sd_bus_message_new_method_call(
695 "org.freedesktop.systemd1",
696 "/org/freedesktop/systemd1",
697 "org.freedesktop.systemd1.Manager",
698 "StartTransientUnit");
700 return bus_log_create_error(r);
703 r = sd_bus_message_append(m, "ss", service, "fail");
705 return bus_log_create_error(r);
708 r = sd_bus_message_open_container(m, 'a', "(sv)");
710 return bus_log_create_error(r);
712 r = transient_service_set_properties(m, argv, pty_path);
714 return bus_log_create_error(r);
716 r = sd_bus_message_close_container(m);
718 return bus_log_create_error(r);
720 /* Auxiliary units */
721 r = sd_bus_message_append(m, "a(sa(sv))", 0);
723 return bus_log_create_error(r);
725 r = sd_bus_call(bus, m, 0, error, NULL);
727 return bus_log_create_error(r);
730 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
731 _cleanup_event_unref_ sd_event *event = NULL;
735 r = sd_event_default(&event);
737 return log_error_errno(r, "Failed to get event loop: %m");
739 assert_se(sigemptyset(&mask) == 0);
740 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
741 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
743 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
744 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
747 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
749 r = pty_forward_new(event, master, false, &forward);
751 return log_error_errno(r, "Failed to create PTY forwarder: %m");
753 r = sd_event_loop(event);
755 return log_error_errno(r, "Failed to run event loop: %m");
757 pty_forward_last_char(forward, &last_char);
759 forward = pty_forward_free(forward);
761 if (!arg_quiet && last_char != '\n')
764 } else if (!arg_quiet)
765 log_info("Running as unit %s.", service);
770 static int start_transient_scope(
773 sd_bus_error *error) {
775 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
776 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
777 _cleanup_free_ char *scope = NULL;
784 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
787 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
790 r = sd_bus_message_new_method_call(
793 "org.freedesktop.systemd1",
794 "/org/freedesktop/systemd1",
795 "org.freedesktop.systemd1.Manager",
796 "StartTransientUnit");
798 return bus_log_create_error(r);
801 r = sd_bus_message_append(m, "ss", scope, "fail");
803 return bus_log_create_error(r);
806 r = sd_bus_message_open_container(m, 'a', "(sv)");
808 return bus_log_create_error(r);
810 r = transient_scope_set_properties(m);
812 return bus_log_create_error(r);
814 r = sd_bus_message_close_container(m);
816 return bus_log_create_error(r);
819 r = sd_bus_message_append(m, "a(sa(sv))", 0);
821 return bus_log_create_error(r);
824 r = sd_bus_call(bus, m, 0, error, NULL);
826 return bus_log_create_error(r);
829 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
830 return log_error_errno(errno, "Failed to set nice level: %m");
833 if (arg_exec_group) {
836 r = get_group_creds(&arg_exec_group, &gid);
838 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
840 if (setresgid(gid, gid, gid) < 0)
841 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
845 const char *home, *shell;
849 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
851 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
853 r = strv_extendf(&user_env, "HOME=%s", home);
857 r = strv_extendf(&user_env, "SHELL=%s", shell);
861 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
865 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
869 if (!arg_exec_group) {
870 if (setresgid(gid, gid, gid) < 0)
871 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
874 if (setresuid(uid, uid, uid) < 0)
875 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
878 env = strv_env_merge(3, environ, user_env, arg_environment);
883 log_info("Running as unit %s.", scope);
885 execvpe(argv[0], argv, env);
887 return log_error_errno(errno, "Failed to execute: %m");
890 static int start_transient_timer(
893 sd_bus_error *error) {
895 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
896 _cleanup_free_ char *timer = NULL, *service = NULL;
903 switch(unit_name_to_type(arg_unit)) {
906 service = strdup(arg_unit);
910 timer = unit_name_change_suffix(service, ".timer");
916 timer = strdup(arg_unit);
920 service = unit_name_change_suffix(timer, ".service");
926 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
930 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
936 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
937 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
940 r = sd_bus_message_new_method_call(
943 "org.freedesktop.systemd1",
944 "/org/freedesktop/systemd1",
945 "org.freedesktop.systemd1.Manager",
946 "StartTransientUnit");
948 return bus_log_create_error(r);
951 r = sd_bus_message_append(m, "ss", timer, "fail");
953 return bus_log_create_error(r);
956 r = sd_bus_message_open_container(m, 'a', "(sv)");
958 return bus_log_create_error(r);
960 r = transient_timer_set_properties(m);
962 return bus_log_create_error(r);
964 r = sd_bus_message_close_container(m);
966 return bus_log_create_error(r);
968 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
970 return bus_log_create_error(r);
973 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
975 return bus_log_create_error(r);
977 r = sd_bus_message_append(m, "s", service);
979 return bus_log_create_error(r);
981 r = sd_bus_message_open_container(m, 'a', "(sv)");
983 return bus_log_create_error(r);
985 r = transient_service_set_properties(m, argv, NULL);
987 return bus_log_create_error(r);
989 r = sd_bus_message_close_container(m);
991 return bus_log_create_error(r);
993 r = sd_bus_message_close_container(m);
995 return bus_log_create_error(r);
998 r = sd_bus_message_close_container(m);
1000 return bus_log_create_error(r);
1003 r = sd_bus_call(bus, m, 0, error, NULL);
1005 return bus_log_create_error(r);
1007 log_info("Running as unit %s.", timer);
1009 log_info("Will run as unit %s.", service);
1014 int main(int argc, char* argv[]) {
1015 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1016 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1017 _cleanup_free_ char *description = NULL, *command = NULL;
1020 log_parse_environment();
1023 r = parse_argv(argc, argv);
1027 if (argc > optind) {
1028 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
1030 log_error_errno(r, "Failed to find executable %s%s: %m",
1032 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
1035 argv[optind] = command;
1038 if (!arg_description) {
1039 description = strv_join(argv + optind, " ");
1045 if (arg_unit && isempty(description)) {
1047 description = strdup(arg_unit);
1055 arg_description = description;
1058 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1060 log_error_errno(r, "Failed to create bus connection: %m");
1065 r = start_transient_scope(bus, argv + optind, &error);
1066 else if (with_timer())
1067 r = start_transient_timer(bus, argv + optind, &error);
1069 r = start_transient_service(bus, argv + optind, &error);
1072 strv_free(arg_environment);
1073 strv_free(arg_property);
1074 strv_free(arg_timer_property);
1076 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;