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;
63 static void help(void) {
64 printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
65 "Run the specified command in a transient scope or service or timer\n"
66 "unit. If timer option is specified and unit is exist which is\n"
67 "specified with --unit option then command can be ommited.\n\n"
68 " -h --help Show this help\n"
69 " --version Show package version\n"
70 " --user Run as user unit\n"
71 " -H --host=[USER@]HOST Operate on remote host\n"
72 " -M --machine=CONTAINER Operate on local container\n"
73 " --scope Run this as scope rather than service\n"
74 " --unit=UNIT Run under the specified unit name\n"
75 " -p --property=NAME=VALUE Set unit property\n"
76 " --description=TEXT Description for unit\n"
77 " --slice=SLICE Run in the specified slice\n"
78 " -r --remain-after-exit Leave service around until explicitly stopped\n"
79 " --send-sighup Send SIGHUP when terminating\n"
80 " --service-type=TYPE Service type\n"
81 " --uid=USER Run as system user\n"
82 " --gid=GROUP Run as system group\n"
83 " --nice=NICE Nice level\n"
84 " --setenv=NAME=VALUE Set environment\n"
85 " -t --pty Run service on pseudo tty\n\n"
87 " --on-active=SEC Run after seconds\n"
88 " --on-boot=SEC Run after seconds from machine was booted up\n"
89 " --on-startup=SEC Run after seconds from systemd was first started\n"
90 " --on-unit-active=SEC Run after seconds from the last activation\n"
91 " --on-unit-inactive=SEC Run after seconds from the last deactivation\n"
92 " --on-calendar=SPEC Realtime timer\n"
93 " --timer-property=NAME=VALUE Set timer unit property\n",
94 program_invocation_short_name);
97 static bool with_timer(void) {
98 return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
101 static int parse_argv(int argc, char *argv[]) {
122 ARG_ON_UNIT_INACTIVE,
127 static const struct option options[] = {
128 { "help", no_argument, NULL, 'h' },
129 { "version", no_argument, NULL, ARG_VERSION },
130 { "user", no_argument, NULL, ARG_USER },
131 { "system", no_argument, NULL, ARG_SYSTEM },
132 { "scope", no_argument, NULL, ARG_SCOPE },
133 { "unit", required_argument, NULL, ARG_UNIT },
134 { "description", required_argument, NULL, ARG_DESCRIPTION },
135 { "slice", required_argument, NULL, ARG_SLICE },
136 { "remain-after-exit", no_argument, NULL, 'r' },
137 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
138 { "host", required_argument, NULL, 'H' },
139 { "machine", required_argument, NULL, 'M' },
140 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
141 { "uid", required_argument, NULL, ARG_EXEC_USER },
142 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
143 { "nice", required_argument, NULL, ARG_NICE },
144 { "setenv", required_argument, NULL, ARG_SETENV },
145 { "property", required_argument, NULL, 'p' },
146 { "tty", no_argument, NULL, 't' },
147 { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
148 { "on-boot", required_argument, NULL, ARG_ON_BOOT },
149 { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
150 { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
151 { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
152 { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
153 { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
158 CalendarSpec *spec = NULL;
163 while ((c = getopt_long(argc, argv, "+hrH:M:p:t", options, NULL)) >= 0)
172 puts(PACKAGE_STRING);
173 puts(SYSTEMD_FEATURES);
192 case ARG_DESCRIPTION:
193 arg_description = optarg;
200 case ARG_SEND_SIGHUP:
201 arg_send_sighup = true;
205 arg_remain_after_exit = true;
209 arg_transport = BUS_TRANSPORT_REMOTE;
214 arg_transport = BUS_TRANSPORT_CONTAINER;
218 case ARG_SERVICE_TYPE:
219 arg_service_type = optarg;
223 arg_exec_user = optarg;
227 arg_exec_group = optarg;
231 r = safe_atoi(optarg, &arg_nice);
232 if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
233 log_error("Failed to parse nice value");
242 if (strv_extend(&arg_environment, optarg) < 0)
249 if (strv_extend(&arg_property, optarg) < 0)
260 r = parse_sec(optarg, &arg_on_active);
262 log_error("Failed to parse timer value: %s", optarg);
270 r = parse_sec(optarg, &arg_on_boot);
272 log_error("Failed to parse timer value: %s", optarg);
280 r = parse_sec(optarg, &arg_on_startup);
282 log_error("Failed to parse timer value: %s", optarg);
288 case ARG_ON_UNIT_ACTIVE:
290 r = parse_sec(optarg, &arg_on_unit_active);
292 log_error("Failed to parse timer value: %s", optarg);
298 case ARG_ON_UNIT_INACTIVE:
300 r = parse_sec(optarg, &arg_on_unit_inactive);
302 log_error("Failed to parse timer value: %s", optarg);
308 case ARG_ON_CALENDAR:
310 r = calendar_spec_from_string(optarg, &spec);
312 log_error("Invalid calendar spec: %s", optarg);
316 arg_on_calendar = optarg;
319 case ARG_TIMER_PROPERTY:
321 if (strv_extend(&arg_timer_property, optarg) < 0)
330 assert_not_reached("Unhandled option");
333 if ((optind >= argc) && (!arg_unit || !with_timer())) {
334 log_error("Command line to execute required.");
338 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
339 log_error("Execution in user context is not supported on non-local systems.");
343 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
344 log_error("Scope execution is not supported on non-local systems.");
348 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
349 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
353 if (arg_pty && (with_timer() || arg_scope)) {
354 log_error("--pty is not compatible in timer or --scope mode.");
358 if (arg_scope && with_timer()) {
359 log_error("Timer options are not supported in --scope mode.");
363 if (arg_timer_property && !with_timer()) {
364 log_error("--timer-property= has no effect without any other timer options.");
371 static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
375 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
379 STRV_FOREACH(i, properties) {
380 r = sd_bus_message_open_container(m, 'r', "sv");
384 r = bus_append_unit_property_assignment(m, *i);
386 r = sd_bus_message_append(m, "sv", 0);
391 r = sd_bus_message_close_container(m);
399 static int transient_cgroup_set_properties(sd_bus_message *m) {
403 if (!isempty(arg_slice)) {
404 _cleanup_free_ char *slice;
406 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
410 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
418 static int transient_kill_set_properties(sd_bus_message *m) {
422 if (arg_send_sighup) {
423 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
431 static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
436 r = transient_unit_set_properties(m, arg_property);
440 r = transient_kill_set_properties(m);
444 r = transient_cgroup_set_properties(m);
448 if (arg_remain_after_exit) {
449 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
454 if (arg_service_type) {
455 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
461 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
466 if (arg_exec_group) {
467 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
473 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
481 r = sd_bus_message_append(m,
483 "StandardInput", "s", "tty",
484 "StandardOutput", "s", "tty",
485 "StandardError", "s", "tty",
486 "TTYPath", "s", pty_path);
494 n = strappenda("TERM=", e);
495 r = sd_bus_message_append(m,
497 "Environment", "as", 1, n);
503 if (!strv_isempty(arg_environment)) {
504 r = sd_bus_message_open_container(m, 'r', "sv");
508 r = sd_bus_message_append(m, "s", "Environment");
512 r = sd_bus_message_open_container(m, 'v', "as");
516 r = sd_bus_message_append_strv(m, arg_environment);
520 r = sd_bus_message_close_container(m);
524 r = sd_bus_message_close_container(m);
531 r = sd_bus_message_open_container(m, 'r', "sv");
535 r = sd_bus_message_append(m, "s", "ExecStart");
539 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
543 r = sd_bus_message_open_container(m, 'a', "(sasb)");
547 r = sd_bus_message_open_container(m, 'r', "sasb");
551 r = sd_bus_message_append(m, "s", argv[0]);
555 r = sd_bus_message_append_strv(m, argv);
559 r = sd_bus_message_append(m, "b", false);
563 r = sd_bus_message_close_container(m);
567 r = sd_bus_message_close_container(m);
571 r = sd_bus_message_close_container(m);
575 r = sd_bus_message_close_container(m);
583 static int transient_scope_set_properties(sd_bus_message *m) {
588 r = transient_unit_set_properties(m, arg_property);
592 r = transient_kill_set_properties(m);
596 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
603 static int transient_timer_set_properties(sd_bus_message *m) {
608 r = transient_unit_set_properties(m, arg_timer_property);
613 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
619 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
624 if (arg_on_startup) {
625 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
630 if (arg_on_unit_active) {
631 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
636 if (arg_on_unit_inactive) {
637 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
642 if (arg_on_calendar) {
643 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
651 static int start_transient_service(
654 sd_bus_error *error) {
656 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
657 _cleanup_free_ char *service = NULL;
658 _cleanup_close_ int master = -1;
659 const char *pty_path = NULL;
666 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
668 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
670 pty_path = ptsname(master);
672 return log_error_errno(errno, "Failed to determine tty name: %m");
674 if (unlockpt(master) < 0)
675 return log_error_errno(errno, "Failed to unlock tty: %m");
679 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
682 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
685 r = sd_bus_message_new_method_call(
688 "org.freedesktop.systemd1",
689 "/org/freedesktop/systemd1",
690 "org.freedesktop.systemd1.Manager",
691 "StartTransientUnit");
693 return bus_log_create_error(r);
696 r = sd_bus_message_append(m, "ss", service, "fail");
698 return bus_log_create_error(r);
701 r = sd_bus_message_open_container(m, 'a', "(sv)");
703 return bus_log_create_error(r);
705 r = transient_service_set_properties(m, argv, pty_path);
707 return bus_log_create_error(r);
709 r = sd_bus_message_close_container(m);
711 return bus_log_create_error(r);
713 /* Auxiliary units */
714 r = sd_bus_message_append(m, "a(sa(sv))", 0);
716 return bus_log_create_error(r);
718 r = sd_bus_call(bus, m, 0, error, NULL);
720 return bus_log_create_error(r);
723 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
724 _cleanup_event_unref_ sd_event *event = NULL;
728 r = sd_event_default(&event);
730 return log_error_errno(r, "Failed to get event loop: %m");
732 assert_se(sigemptyset(&mask) == 0);
733 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
734 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
736 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
737 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
739 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
741 r = pty_forward_new(event, master, false, &forward);
743 return log_error_errno(r, "Failed to create PTY forwarder: %m");
745 r = sd_event_loop(event);
747 return log_error_errno(r, "Failed to run event loop: %m");
749 pty_forward_last_char(forward, &last_char);
751 forward = pty_forward_free(forward);
753 if (last_char != '\n')
757 log_info("Running as unit %s.", service);
762 static int start_transient_scope(
765 sd_bus_error *error) {
767 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
768 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
769 _cleanup_free_ char *scope = NULL;
776 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
779 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
782 r = sd_bus_message_new_method_call(
785 "org.freedesktop.systemd1",
786 "/org/freedesktop/systemd1",
787 "org.freedesktop.systemd1.Manager",
788 "StartTransientUnit");
790 return bus_log_create_error(r);
793 r = sd_bus_message_append(m, "ss", scope, "fail");
795 return bus_log_create_error(r);
798 r = sd_bus_message_open_container(m, 'a', "(sv)");
800 return bus_log_create_error(r);
802 r = transient_scope_set_properties(m);
804 return bus_log_create_error(r);
806 r = sd_bus_message_close_container(m);
808 return bus_log_create_error(r);
811 r = sd_bus_message_append(m, "a(sa(sv))", 0);
813 return bus_log_create_error(r);
816 r = sd_bus_call(bus, m, 0, error, NULL);
818 return bus_log_create_error(r);
821 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
822 return log_error_errno(errno, "Failed to set nice level: %m");
825 if (arg_exec_group) {
828 r = get_group_creds(&arg_exec_group, &gid);
830 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
832 if (setresgid(gid, gid, gid) < 0)
833 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
837 const char *home, *shell;
841 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
843 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
845 r = strv_extendf(&user_env, "HOME=%s", home);
849 r = strv_extendf(&user_env, "SHELL=%s", shell);
853 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
857 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
861 if (!arg_exec_group) {
862 if (setresgid(gid, gid, gid) < 0)
863 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
866 if (setresuid(uid, uid, uid) < 0)
867 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
870 env = strv_env_merge(3, environ, user_env, arg_environment);
874 log_info("Running as unit %s.", scope);
876 execvpe(argv[0], argv, env);
878 return log_error_errno(errno, "Failed to execute: %m");
881 static int start_transient_timer(
884 sd_bus_error *error) {
886 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
887 _cleanup_free_ char *timer = NULL, *service = NULL;
894 switch(unit_name_to_type(arg_unit)) {
897 service = strdup(arg_unit);
901 timer = unit_name_change_suffix(service, ".timer");
907 timer = strdup(arg_unit);
911 service = unit_name_change_suffix(timer, ".service");
917 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
921 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
927 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
928 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
931 r = sd_bus_message_new_method_call(
934 "org.freedesktop.systemd1",
935 "/org/freedesktop/systemd1",
936 "org.freedesktop.systemd1.Manager",
937 "StartTransientUnit");
939 return bus_log_create_error(r);
942 r = sd_bus_message_append(m, "ss", timer, "fail");
944 return bus_log_create_error(r);
947 r = sd_bus_message_open_container(m, 'a', "(sv)");
949 return bus_log_create_error(r);
951 r = transient_timer_set_properties(m);
953 return bus_log_create_error(r);
955 r = sd_bus_message_close_container(m);
957 return bus_log_create_error(r);
959 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
961 return bus_log_create_error(r);
964 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
966 return bus_log_create_error(r);
968 r = sd_bus_message_append(m, "s", service);
970 return bus_log_create_error(r);
972 r = sd_bus_message_open_container(m, 'a', "(sv)");
974 return bus_log_create_error(r);
976 r = transient_service_set_properties(m, argv, NULL);
978 return bus_log_create_error(r);
980 r = sd_bus_message_close_container(m);
982 return bus_log_create_error(r);
984 r = sd_bus_message_close_container(m);
986 return bus_log_create_error(r);
989 r = sd_bus_message_close_container(m);
991 return bus_log_create_error(r);
994 r = sd_bus_call(bus, m, 0, error, NULL);
996 return bus_log_create_error(r);
998 log_info("Running as unit %s.", timer);
1000 log_info("Will run as unit %s.", service);
1005 int main(int argc, char* argv[]) {
1006 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1007 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1008 _cleanup_free_ char *description = NULL, *command = NULL;
1011 log_parse_environment();
1014 r = parse_argv(argc, argv);
1018 if (argc > optind) {
1019 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
1021 log_error_errno(r, "Failed to find executable %s%s: %m",
1023 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
1026 argv[optind] = command;
1029 if (!arg_description) {
1030 description = strv_join(argv + optind, " ");
1036 if (arg_unit && isempty(description)) {
1038 description = strdup(arg_unit);
1046 arg_description = description;
1049 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1051 log_error_errno(r, "Failed to create bus connection: %m");
1056 r = start_transient_scope(bus, argv + optind, &error);
1057 else if (with_timer())
1058 r = start_transient_timer(bus, argv + optind, &error);
1060 r = start_transient_service(bus, argv + optind, &error);
1063 strv_free(arg_environment);
1064 strv_free(arg_property);
1065 strv_free(arg_timer_property);
1067 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;