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");
244 if (strv_extend(&arg_environment, optarg) < 0)
250 if (strv_extend(&arg_property, optarg) < 0)
265 r = parse_sec(optarg, &arg_on_active);
267 log_error("Failed to parse timer value: %s", optarg);
275 r = parse_sec(optarg, &arg_on_boot);
277 log_error("Failed to parse timer value: %s", optarg);
285 r = parse_sec(optarg, &arg_on_startup);
287 log_error("Failed to parse timer value: %s", optarg);
293 case ARG_ON_UNIT_ACTIVE:
295 r = parse_sec(optarg, &arg_on_unit_active);
297 log_error("Failed to parse timer value: %s", optarg);
303 case ARG_ON_UNIT_INACTIVE:
305 r = parse_sec(optarg, &arg_on_unit_inactive);
307 log_error("Failed to parse timer value: %s", optarg);
313 case ARG_ON_CALENDAR:
315 r = calendar_spec_from_string(optarg, &spec);
317 log_error("Invalid calendar spec: %s", optarg);
321 arg_on_calendar = optarg;
324 case ARG_TIMER_PROPERTY:
326 if (strv_extend(&arg_timer_property, optarg) < 0)
335 assert_not_reached("Unhandled option");
338 if ((optind >= argc) && (!arg_unit || !with_timer())) {
339 log_error("Command line to execute required.");
343 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
344 log_error("Execution in user context is not supported on non-local systems.");
348 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
349 log_error("Scope execution is not supported on non-local systems.");
353 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
354 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
358 if (arg_pty && (with_timer() || arg_scope)) {
359 log_error("--pty is not compatible in timer or --scope mode.");
363 if (arg_scope && with_timer()) {
364 log_error("Timer options are not supported in --scope mode.");
368 if (arg_timer_property && !with_timer()) {
369 log_error("--timer-property= has no effect without any other timer options.");
376 static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
380 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
384 STRV_FOREACH(i, properties) {
385 r = sd_bus_message_open_container(m, 'r', "sv");
389 r = bus_append_unit_property_assignment(m, *i);
393 r = sd_bus_message_close_container(m);
401 static int transient_cgroup_set_properties(sd_bus_message *m) {
405 if (!isempty(arg_slice)) {
406 _cleanup_free_ char *slice;
408 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
412 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
420 static int transient_kill_set_properties(sd_bus_message *m) {
424 return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
429 static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
434 r = transient_unit_set_properties(m, arg_property);
438 r = transient_kill_set_properties(m);
442 r = transient_cgroup_set_properties(m);
446 if (arg_remain_after_exit) {
447 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
452 if (arg_service_type) {
453 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
459 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
464 if (arg_exec_group) {
465 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
471 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
479 r = sd_bus_message_append(m,
481 "StandardInput", "s", "tty",
482 "StandardOutput", "s", "tty",
483 "StandardError", "s", "tty",
484 "TTYPath", "s", pty_path);
492 n = strjoina("TERM=", e);
493 r = sd_bus_message_append(m,
495 "Environment", "as", 1, n);
501 if (!strv_isempty(arg_environment)) {
502 r = sd_bus_message_open_container(m, 'r', "sv");
506 r = sd_bus_message_append(m, "s", "Environment");
510 r = sd_bus_message_open_container(m, 'v', "as");
514 r = sd_bus_message_append_strv(m, arg_environment);
518 r = sd_bus_message_close_container(m);
522 r = sd_bus_message_close_container(m);
529 r = sd_bus_message_open_container(m, 'r', "sv");
533 r = sd_bus_message_append(m, "s", "ExecStart");
537 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
541 r = sd_bus_message_open_container(m, 'a', "(sasb)");
545 r = sd_bus_message_open_container(m, 'r', "sasb");
549 r = sd_bus_message_append(m, "s", argv[0]);
553 r = sd_bus_message_append_strv(m, argv);
557 r = sd_bus_message_append(m, "b", false);
561 r = sd_bus_message_close_container(m);
565 r = sd_bus_message_close_container(m);
569 r = sd_bus_message_close_container(m);
573 r = sd_bus_message_close_container(m);
581 static int transient_scope_set_properties(sd_bus_message *m) {
586 r = transient_unit_set_properties(m, arg_property);
590 r = transient_kill_set_properties(m);
594 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
601 static int transient_timer_set_properties(sd_bus_message *m) {
606 r = transient_unit_set_properties(m, arg_timer_property);
611 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
617 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
622 if (arg_on_startup) {
623 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
628 if (arg_on_unit_active) {
629 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
634 if (arg_on_unit_inactive) {
635 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
640 if (arg_on_calendar) {
641 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
649 static int start_transient_service(
653 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
654 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
655 _cleanup_free_ char *service = NULL, *pty_path = NULL;
656 _cleanup_close_ int master = -1;
664 if (arg_transport == BUS_TRANSPORT_LOCAL) {
665 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
667 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
669 r = ptsname_malloc(master, &pty_path);
671 return log_error_errno(r, "Failed to determine tty name: %m");
673 } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
674 _cleanup_bus_unref_ sd_bus *system_bus = NULL;
675 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
678 r = sd_bus_open_system(&system_bus);
680 log_error_errno(r, "Failed to connect to system bus: %m");
682 r = sd_bus_call_method(system_bus,
683 "org.freedesktop.machine1",
684 "/org/freedesktop/machine1",
685 "org.freedesktop.machine1.Manager",
691 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
695 r = sd_bus_message_read(reply, "hs", &master, &s);
697 return bus_log_parse_error(r);
699 master = fcntl(master, F_DUPFD_CLOEXEC, 3);
701 return log_error_errno(errno, "Failed to duplicate master fd: %m");
703 pty_path = strdup(s);
707 assert_not_reached("Can't allocate tty via ssh");
709 if (unlockpt(master) < 0)
710 return log_error_errno(errno, "Failed to unlock tty: %m");
714 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
717 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
720 r = sd_bus_message_new_method_call(
723 "org.freedesktop.systemd1",
724 "/org/freedesktop/systemd1",
725 "org.freedesktop.systemd1.Manager",
726 "StartTransientUnit");
728 return bus_log_create_error(r);
731 r = sd_bus_message_append(m, "ss", service, "fail");
733 return bus_log_create_error(r);
736 r = sd_bus_message_open_container(m, 'a', "(sv)");
738 return bus_log_create_error(r);
740 r = transient_service_set_properties(m, argv, pty_path);
742 return bus_log_create_error(r);
744 r = sd_bus_message_close_container(m);
746 return bus_log_create_error(r);
748 /* Auxiliary units */
749 r = sd_bus_message_append(m, "a(sa(sv))", 0);
751 return bus_log_create_error(r);
753 r = sd_bus_call(bus, m, 0, &error, NULL);
755 log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r));
760 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
761 _cleanup_event_unref_ sd_event *event = NULL;
765 r = sd_event_default(&event);
767 return log_error_errno(r, "Failed to get event loop: %m");
769 assert_se(sigemptyset(&mask) == 0);
770 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
771 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
773 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
774 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
777 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
779 r = pty_forward_new(event, master, false, false, &forward);
781 return log_error_errno(r, "Failed to create PTY forwarder: %m");
783 r = sd_event_loop(event);
785 return log_error_errno(r, "Failed to run event loop: %m");
787 pty_forward_get_last_char(forward, &last_char);
789 forward = pty_forward_free(forward);
791 if (!arg_quiet && last_char != '\n')
794 } else if (!arg_quiet)
795 log_info("Running as unit %s.", service);
800 static int start_transient_scope(
804 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
805 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
806 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
807 _cleanup_free_ char *scope = NULL;
814 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
817 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
820 r = sd_bus_message_new_method_call(
823 "org.freedesktop.systemd1",
824 "/org/freedesktop/systemd1",
825 "org.freedesktop.systemd1.Manager",
826 "StartTransientUnit");
828 return bus_log_create_error(r);
831 r = sd_bus_message_append(m, "ss", scope, "fail");
833 return bus_log_create_error(r);
836 r = sd_bus_message_open_container(m, 'a', "(sv)");
838 return bus_log_create_error(r);
840 r = transient_scope_set_properties(m);
842 return bus_log_create_error(r);
844 r = sd_bus_message_close_container(m);
846 return bus_log_create_error(r);
848 /* Auxiliary units */
849 r = sd_bus_message_append(m, "a(sa(sv))", 0);
851 return bus_log_create_error(r);
853 r = sd_bus_call(bus, m, 0, &error, NULL);
855 log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
860 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
861 return log_error_errno(errno, "Failed to set nice level: %m");
864 if (arg_exec_group) {
867 r = get_group_creds(&arg_exec_group, &gid);
869 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
871 if (setresgid(gid, gid, gid) < 0)
872 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
876 const char *home, *shell;
880 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
882 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
884 r = strv_extendf(&user_env, "HOME=%s", home);
888 r = strv_extendf(&user_env, "SHELL=%s", shell);
892 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
896 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
900 if (!arg_exec_group) {
901 if (setresgid(gid, gid, gid) < 0)
902 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
905 if (setresuid(uid, uid, uid) < 0)
906 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
909 env = strv_env_merge(3, environ, user_env, arg_environment);
914 log_info("Running as unit %s.", scope);
916 execvpe(argv[0], argv, env);
918 return log_error_errno(errno, "Failed to execute: %m");
921 static int start_transient_timer(
925 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
926 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
927 _cleanup_free_ char *timer = NULL, *service = NULL;
934 switch(unit_name_to_type(arg_unit)) {
937 service = strdup(arg_unit);
941 timer = unit_name_change_suffix(service, ".timer");
947 timer = strdup(arg_unit);
951 service = unit_name_change_suffix(timer, ".service");
957 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
961 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
967 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
968 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
971 r = sd_bus_message_new_method_call(
974 "org.freedesktop.systemd1",
975 "/org/freedesktop/systemd1",
976 "org.freedesktop.systemd1.Manager",
977 "StartTransientUnit");
979 return bus_log_create_error(r);
982 r = sd_bus_message_append(m, "ss", timer, "fail");
984 return bus_log_create_error(r);
987 r = sd_bus_message_open_container(m, 'a', "(sv)");
989 return bus_log_create_error(r);
991 r = transient_timer_set_properties(m);
993 return bus_log_create_error(r);
995 r = sd_bus_message_close_container(m);
997 return bus_log_create_error(r);
999 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
1001 return bus_log_create_error(r);
1004 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
1006 return bus_log_create_error(r);
1008 r = sd_bus_message_append(m, "s", service);
1010 return bus_log_create_error(r);
1012 r = sd_bus_message_open_container(m, 'a', "(sv)");
1014 return bus_log_create_error(r);
1016 r = transient_service_set_properties(m, argv, NULL);
1018 return bus_log_create_error(r);
1020 r = sd_bus_message_close_container(m);
1022 return bus_log_create_error(r);
1024 r = sd_bus_message_close_container(m);
1026 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_call(bus, m, 0, &error, NULL);
1035 log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
1039 log_info("Running as unit %s.", timer);
1041 log_info("Will run as unit %s.", service);
1046 int main(int argc, char* argv[]) {
1047 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1048 _cleanup_free_ char *description = NULL, *command = NULL;
1051 log_parse_environment();
1054 r = parse_argv(argc, argv);
1058 if (argc > optind) {
1059 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
1061 log_error_errno(r, "Failed to find executable %s%s: %m",
1063 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
1066 argv[optind] = command;
1069 if (!arg_description) {
1070 description = strv_join(argv + optind, " ");
1076 if (arg_unit && isempty(description)) {
1078 description = strdup(arg_unit);
1086 arg_description = description;
1089 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1091 log_error_errno(r, "Failed to create bus connection: %m");
1096 r = start_transient_scope(bus, argv + optind);
1097 else if (with_timer())
1098 r = start_transient_timer(bus, argv + optind);
1100 r = start_transient_service(bus, argv + optind);
1103 strv_free(arg_environment);
1104 strv_free(arg_property);
1105 strv_free(arg_timer_property);
1107 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;