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/>.
29 #include "unit-name.h"
31 #include "path-util.h"
32 #include "bus-error.h"
33 #include "calendarspec.h"
35 static bool arg_scope = false;
36 static bool arg_remain_after_exit = false;
37 static const char *arg_unit = NULL;
38 static const char *arg_description = NULL;
39 static const char *arg_slice = NULL;
40 static bool arg_send_sighup = false;
41 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
42 static const char *arg_host = NULL;
43 static bool arg_user = false;
44 static const char *arg_service_type = NULL;
45 static const char *arg_exec_user = NULL;
46 static const char *arg_exec_group = NULL;
47 static int arg_nice = 0;
48 static bool arg_nice_set = false;
49 static char **arg_environment = NULL;
50 static char **arg_property = NULL;
51 static usec_t arg_on_active = 0;
52 static usec_t arg_on_boot = 0;
53 static usec_t arg_on_startup = 0;
54 static usec_t arg_on_unit_active = 0;
55 static usec_t arg_on_unit_inactive = 0;
56 static char *arg_on_calendar = NULL;
57 static char **arg_timer_property = NULL;
59 static void help(void) {
60 printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
61 "Run the specified command in a transient scope or service or timer\n"
62 "unit. If timer option is specified and unit is exist which is\n"
63 "specified with --unit option then command can be ommited.\n\n"
64 " -h --help Show this help\n"
65 " --version Show package version\n"
66 " --user Run as user unit\n"
67 " -H --host=[USER@]HOST Operate on remote host\n"
68 " -M --machine=CONTAINER Operate on local container\n"
69 " --scope Run this as scope rather than service\n"
70 " --unit=UNIT Run under the specified unit name\n"
71 " -p --property=NAME=VALUE Set unit property\n"
72 " --description=TEXT Description for unit\n"
73 " --slice=SLICE Run in the specified slice\n"
74 " -r --remain-after-exit Leave service around until explicitly stopped\n"
75 " --send-sighup Send SIGHUP when terminating\n"
76 " --service-type=TYPE Service type\n"
77 " --uid=USER Run as system user\n"
78 " --gid=GROUP Run as system group\n"
79 " --nice=NICE Nice level\n"
80 " --setenv=NAME=VALUE Set environment\n\n"
82 " --on-active=SEC Run after seconds\n"
83 " --on-boot=SEC Run after seconds from machine was booted up\n"
84 " --on-startup=SEC Run after seconds from systemd was first started\n"
85 " --on-unit-active=SEC Run after seconds from the last activation\n"
86 " --on-unit-inactive=SEC Run after seconds from the last deactivation\n"
87 " --on-calendar=SPEC Realtime timer\n"
88 " --timer-property=NAME=VALUE Set timer unit property\n",
89 program_invocation_short_name);
92 static bool with_timer(void) {
93 return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
96 static int parse_argv(int argc, char *argv[]) {
116 ARG_ON_UNIT_INACTIVE,
121 static const struct option options[] = {
122 { "help", no_argument, NULL, 'h' },
123 { "version", no_argument, NULL, ARG_VERSION },
124 { "user", no_argument, NULL, ARG_USER },
125 { "system", no_argument, NULL, ARG_SYSTEM },
126 { "scope", no_argument, NULL, ARG_SCOPE },
127 { "unit", required_argument, NULL, ARG_UNIT },
128 { "description", required_argument, NULL, ARG_DESCRIPTION },
129 { "slice", required_argument, NULL, ARG_SLICE },
130 { "remain-after-exit", no_argument, NULL, 'r' },
131 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
132 { "host", required_argument, NULL, 'H' },
133 { "machine", required_argument, NULL, 'M' },
134 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
135 { "uid", required_argument, NULL, ARG_EXEC_USER },
136 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
137 { "nice", required_argument, NULL, ARG_NICE },
138 { "setenv", required_argument, NULL, ARG_SETENV },
139 { "property", required_argument, NULL, 'p' },
140 { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
141 { "on-boot", required_argument, NULL, ARG_ON_BOOT },
142 { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
143 { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
144 { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
145 { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
146 { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
151 CalendarSpec *spec = NULL;
156 while ((c = getopt_long(argc, argv, "+hrH:M:p:", options, NULL)) >= 0)
165 puts(PACKAGE_STRING);
166 puts(SYSTEMD_FEATURES);
185 case ARG_DESCRIPTION:
186 arg_description = optarg;
193 case ARG_SEND_SIGHUP:
194 arg_send_sighup = true;
198 arg_remain_after_exit = true;
202 arg_transport = BUS_TRANSPORT_REMOTE;
207 arg_transport = BUS_TRANSPORT_CONTAINER;
211 case ARG_SERVICE_TYPE:
212 arg_service_type = optarg;
216 arg_exec_user = optarg;
220 arg_exec_group = optarg;
224 r = safe_atoi(optarg, &arg_nice);
225 if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
226 log_error("Failed to parse nice value");
235 if (strv_extend(&arg_environment, optarg) < 0)
242 if (strv_extend(&arg_property, optarg) < 0)
249 r = parse_sec(optarg, &arg_on_active);
251 log_error("Failed to parse timer value: %s", optarg);
259 r = parse_sec(optarg, &arg_on_boot);
261 log_error("Failed to parse timer value: %s", optarg);
269 r = parse_sec(optarg, &arg_on_startup);
271 log_error("Failed to parse timer value: %s", optarg);
277 case ARG_ON_UNIT_ACTIVE:
279 r = parse_sec(optarg, &arg_on_unit_active);
281 log_error("Failed to parse timer value: %s", optarg);
287 case ARG_ON_UNIT_INACTIVE:
289 r = parse_sec(optarg, &arg_on_unit_inactive);
291 log_error("Failed to parse timer value: %s", optarg);
297 case ARG_ON_CALENDAR:
299 r = calendar_spec_from_string(optarg, &spec);
301 log_error("Invalid calendar spec: %s", optarg);
305 arg_on_calendar = optarg;
308 case ARG_TIMER_PROPERTY:
310 if (strv_extend(&arg_timer_property, optarg) < 0)
319 assert_not_reached("Unhandled option");
322 if ((optind >= argc) && (!arg_unit || !with_timer())) {
323 log_error("Command line to execute required.");
327 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
328 log_error("Execution in user context is not supported on non-local systems.");
332 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
333 log_error("Scope execution is not supported on non-local systems.");
337 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
338 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
342 if (arg_scope && with_timer()) {
343 log_error("Timer options are not supported in --scope mode.");
347 if (arg_timer_property && !with_timer()) {
348 log_error("--timer-property= has no effect without any other timer options.");
355 static int transient_unit_set_properties(sd_bus_message *m, UnitType t) {
359 STRV_FOREACH(i, t == UNIT_TIMER ? arg_timer_property : arg_property) {
360 r = sd_bus_message_open_container(m, 'r', "sv");
364 r = bus_append_unit_property_assignment(m, *i);
366 r = sd_bus_message_append(m, "sv", 0);
371 r = sd_bus_message_close_container(m);
376 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
380 if (!isempty(arg_slice)) {
381 _cleanup_free_ char *slice;
383 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
387 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
392 if (arg_send_sighup && t != UNIT_TIMER) {
393 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
401 static int transient_service_set_properties(sd_bus_message *m, char **argv) {
406 r = transient_unit_set_properties(m, UNIT_SERVICE);
410 if (arg_remain_after_exit) {
411 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
416 if (arg_service_type) {
417 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
423 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
428 if (arg_exec_group) {
429 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
435 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
440 if (!strv_isempty(arg_environment)) {
441 r = sd_bus_message_open_container(m, 'r', "sv");
445 r = sd_bus_message_append(m, "s", "Environment");
449 r = sd_bus_message_open_container(m, 'v', "as");
453 r = sd_bus_message_append_strv(m, arg_environment);
457 r = sd_bus_message_close_container(m);
461 r = sd_bus_message_close_container(m);
468 r = sd_bus_message_open_container(m, 'r', "sv");
472 r = sd_bus_message_append(m, "s", "ExecStart");
476 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
480 r = sd_bus_message_open_container(m, 'a', "(sasb)");
484 r = sd_bus_message_open_container(m, 'r', "sasb");
488 r = sd_bus_message_append(m, "s", argv[0]);
492 r = sd_bus_message_append_strv(m, argv);
496 r = sd_bus_message_append(m, "b", false);
500 r = sd_bus_message_close_container(m);
504 r = sd_bus_message_close_container(m);
508 r = sd_bus_message_close_container(m);
512 r = sd_bus_message_close_container(m);
520 static int transient_timer_set_properties(sd_bus_message *m) {
525 r = transient_unit_set_properties(m, UNIT_TIMER);
530 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
536 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
541 if (arg_on_startup) {
542 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
547 if (arg_on_unit_active) {
548 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
553 if (arg_on_unit_inactive) {
554 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
559 if (arg_on_calendar) {
560 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
568 static int transient_scope_set_properties(sd_bus_message *m) {
573 r = transient_unit_set_properties(m, UNIT_SCOPE);
577 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
584 static int start_transient_service(
587 sd_bus_error *error) {
589 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
590 _cleanup_free_ char *service = NULL;
597 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
600 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
603 r = sd_bus_message_new_method_call(
606 "org.freedesktop.systemd1",
607 "/org/freedesktop/systemd1",
608 "org.freedesktop.systemd1.Manager",
609 "StartTransientUnit");
611 return bus_log_create_error(r);
614 r = sd_bus_message_append(m, "ss", service, "fail");
616 return bus_log_create_error(r);
619 r = sd_bus_message_open_container(m, 'a', "(sv)");
621 return bus_log_create_error(r);
623 r = transient_service_set_properties(m, argv);
625 return bus_log_create_error(r);
627 r = sd_bus_message_close_container(m);
629 return bus_log_create_error(r);
632 r = sd_bus_message_append(m, "a(sa(sv))", 0);
634 return bus_log_create_error(r);
637 r = sd_bus_call(bus, m, 0, error, NULL);
639 return bus_log_create_error(r);
641 log_info("Running as unit %s.", service);
646 static int start_transient_timer(
649 sd_bus_error *error) {
651 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
652 _cleanup_free_ char *timer = NULL, *service = NULL;
659 switch(unit_name_to_type(arg_unit)) {
661 service = strdup(arg_unit);
662 timer = unit_name_change_suffix(service, ".timer");
668 timer = strdup(arg_unit);
669 service = unit_name_change_suffix(timer, ".service");
675 service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
679 timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
685 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
686 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
689 r = sd_bus_message_new_method_call(
692 "org.freedesktop.systemd1",
693 "/org/freedesktop/systemd1",
694 "org.freedesktop.systemd1.Manager",
695 "StartTransientUnit");
697 return bus_log_create_error(r);
700 r = sd_bus_message_append(m, "ss", timer, "fail");
702 return bus_log_create_error(r);
705 r = sd_bus_message_open_container(m, 'a', "(sv)");
707 return bus_log_create_error(r);
709 r = transient_timer_set_properties(m);
711 return bus_log_create_error(r);
713 r = sd_bus_message_close_container(m);
715 return bus_log_create_error(r);
718 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
720 return bus_log_create_error(r);
722 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
724 return bus_log_create_error(r);
726 r = sd_bus_message_append(m, "s", service);
728 return bus_log_create_error(r);
730 r = sd_bus_message_open_container(m, 'a', "(sv)");
732 return bus_log_create_error(r);
734 r = transient_service_set_properties(m, argv);
736 return bus_log_create_error(r);
738 r = sd_bus_message_close_container(m);
740 return bus_log_create_error(r);
742 r = sd_bus_message_close_container(m);
744 return bus_log_create_error(r);
746 r = sd_bus_message_close_container(m);
748 return bus_log_create_error(r);
750 r = sd_bus_message_append(m, "a(sa(sv))", 0);
752 return bus_log_create_error(r);
756 r = sd_bus_call(bus, m, 0, error, NULL);
758 return bus_log_create_error(r);
760 log_info("Running as unit %s.", timer);
762 log_info("Will run as unit %s.", service);
767 static int start_transient_scope(
770 sd_bus_error *error) {
772 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
773 _cleanup_free_ char *scope = NULL;
774 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
781 scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
784 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
787 r = sd_bus_message_new_method_call(
790 "org.freedesktop.systemd1",
791 "/org/freedesktop/systemd1",
792 "org.freedesktop.systemd1.Manager",
793 "StartTransientUnit");
795 return bus_log_create_error(r);
798 r = sd_bus_message_append(m, "ss", scope, "fail");
800 return bus_log_create_error(r);
803 r = sd_bus_message_open_container(m, 'a', "(sv)");
805 return bus_log_create_error(r);
807 r = transient_scope_set_properties(m);
809 return bus_log_create_error(r);
811 r = sd_bus_message_close_container(m);
813 return bus_log_create_error(r);
816 r = sd_bus_message_append(m, "a(sa(sv))", 0);
818 return bus_log_create_error(r);
821 r = sd_bus_call(bus, m, 0, error, NULL);
823 return bus_log_create_error(r);
826 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
827 return log_error_errno(errno, "Failed to set nice level: %m");
830 if (arg_exec_group) {
833 r = get_group_creds(&arg_exec_group, &gid);
835 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
837 if (setresgid(gid, gid, gid) < 0)
838 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
842 const char *home, *shell;
846 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
848 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
850 r = strv_extendf(&user_env, "HOME=%s", home);
854 r = strv_extendf(&user_env, "SHELL=%s", shell);
858 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
862 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
866 if (!arg_exec_group) {
867 if (setresgid(gid, gid, gid) < 0)
868 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
871 if (setresuid(uid, uid, uid) < 0)
872 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
875 env = strv_env_merge(3, environ, user_env, arg_environment);
879 log_info("Running as unit %s.", scope);
881 execvpe(argv[0], argv, env);
882 log_error_errno(errno, "Failed to execute: %m");
886 int main(int argc, char* argv[]) {
887 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
888 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
889 _cleanup_free_ char *description = NULL, *command = NULL;
892 log_parse_environment();
895 r = parse_argv(argc, argv);
900 r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
902 log_error_errno(r, "Failed to find executable %s%s: %m",
904 arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
907 argv[optind] = command;
910 if (!arg_description) {
911 description = strv_join(argv + optind, " ");
917 if (arg_unit && isempty(description)) {
919 description = strdup(arg_unit);
927 arg_description = description;
930 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
932 log_error_errno(r, "Failed to create bus connection: %m");
937 r = start_transient_scope(bus, argv + optind, &error);
938 else if (with_timer())
939 r = start_transient_timer(bus, argv + optind, &error);
941 r = start_transient_service(bus, argv + optind, &error);
944 strv_free(arg_environment);
945 strv_free(arg_property);
946 strv_free(arg_timer_property);
948 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;