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"
30 #include "path-util.h"
31 #include "bus-error.h"
33 static bool arg_scope
= false;
34 static bool arg_remain_after_exit
= false;
35 static const char *arg_unit
= NULL
;
36 static const char *arg_description
= NULL
;
37 static const char *arg_slice
= NULL
;
38 static bool arg_send_sighup
= false;
39 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
40 static char *arg_host
= NULL
;
41 static bool arg_user
= false;
42 static const char *arg_service_type
= NULL
;
43 static const char *arg_exec_user
= NULL
;
44 static const char *arg_exec_group
= NULL
;
45 static int arg_nice
= 0;
46 static bool arg_nice_set
= false;
47 static char **arg_environment
= NULL
;
49 static int help(void) {
51 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
52 "Run the specified command in a transient scope or service unit.\n\n"
53 " -h --help Show this help\n"
54 " --version Show package version\n"
55 " --user Run as user unit\n"
56 " -H --host=[USER@]HOST Operate on remote host\n"
57 " -M --machine=CONTAINER Operate on local container\n"
58 " --scope Run this as scope rather than service\n"
59 " --unit=UNIT Run under the specified unit name\n"
60 " --description=TEXT Description for unit\n"
61 " --slice=SLICE Run in the specified slice\n"
62 " -r --remain-after-exit Leave service around until explicitly stopped\n"
63 " --send-sighup Send SIGHUP when terminating\n"
64 " --service-type=TYPE Service type\n"
65 " --uid=USER Run as system user\n"
66 " --gid=GROUP Run as system group\n"
67 " --nice=NICE Nice level\n"
68 " --setenv=NAME=VALUE Set environment\n",
69 program_invocation_short_name
);
74 static int parse_argv(int argc
, char *argv
[]) {
92 static const struct option options
[] = {
93 { "help", no_argument
, NULL
, 'h' },
94 { "version", no_argument
, NULL
, ARG_VERSION
},
95 { "user", no_argument
, NULL
, ARG_USER
},
96 { "system", no_argument
, NULL
, ARG_SYSTEM
},
97 { "scope", no_argument
, NULL
, ARG_SCOPE
},
98 { "unit", required_argument
, NULL
, ARG_UNIT
},
99 { "description", required_argument
, NULL
, ARG_DESCRIPTION
},
100 { "slice", required_argument
, NULL
, ARG_SLICE
},
101 { "remain-after-exit", no_argument
, NULL
, 'r' },
102 { "send-sighup", no_argument
, NULL
, ARG_SEND_SIGHUP
},
103 { "host", required_argument
, NULL
, 'H' },
104 { "machine", required_argument
, NULL
, 'M' },
105 { "service-type", required_argument
, NULL
, ARG_SERVICE_TYPE
},
106 { "uid", required_argument
, NULL
, ARG_EXEC_USER
},
107 { "gid", required_argument
, NULL
, ARG_EXEC_GROUP
},
108 { "nice", required_argument
, NULL
, ARG_NICE
},
109 { "setenv", required_argument
, NULL
, ARG_SETENV
},
118 while ((c
= getopt_long(argc
, argv
, "+hrH:M:", options
, NULL
)) >= 0) {
126 puts(PACKAGE_STRING
);
127 puts(SYSTEMD_FEATURES
);
146 case ARG_DESCRIPTION
:
147 arg_description
= optarg
;
154 case ARG_SEND_SIGHUP
:
155 arg_send_sighup
= true;
159 arg_remain_after_exit
= true;
163 arg_transport
= BUS_TRANSPORT_REMOTE
;
168 arg_transport
= BUS_TRANSPORT_CONTAINER
;
172 case ARG_SERVICE_TYPE
:
173 arg_service_type
= optarg
;
177 arg_exec_user
= optarg
;
181 arg_exec_group
= optarg
;
185 r
= safe_atoi(optarg
, &arg_nice
);
187 log_error("Failed to parse nice value");
196 if (strv_extend(&arg_environment
, optarg
) < 0)
205 assert_not_reached("Unhandled option");
209 if (optind
>= argc
) {
210 log_error("Command line to execute required.");
214 if (arg_user
&& arg_transport
!= BUS_TRANSPORT_LOCAL
) {
215 log_error("Execution in user context is not supported on non-local systems.");
219 if (arg_scope
&& arg_transport
!= BUS_TRANSPORT_LOCAL
) {
220 log_error("Scope execution is not supported on non-local systems.");
224 if (arg_scope
&& (arg_remain_after_exit
|| arg_service_type
|| arg_exec_user
|| arg_exec_group
|| arg_nice_set
|| arg_environment
)) {
225 log_error("--remain-after-exit, --service-type=, --user=, --group=, --nice= and --setenv= are not supported in --scope mode.");
232 static int message_start_transient_unit_new(sd_bus
*bus
, const char *name
, sd_bus_message
**ret
) {
233 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
240 log_info("Running as unit %s.", name
);
242 r
= sd_bus_message_new_method_call(
245 "org.freedesktop.systemd1",
246 "/org/freedesktop/systemd1",
247 "org.freedesktop.systemd1.Manager",
248 "StartTransientUnit");
252 r
= sd_bus_message_append(m
, "ss", name
, "fail");
256 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
260 r
= sd_bus_message_append(m
, "(sv)", "Description", "s", arg_description
);
264 if (!isempty(arg_slice
)) {
265 _cleanup_free_
char *slice
;
267 slice
= unit_name_mangle_with_suffix(arg_slice
, MANGLE_NOGLOB
, ".slice");
271 r
= sd_bus_message_append(m
, "(sv)", "Slice", "s", slice
);
276 r
= sd_bus_message_append(m
, "(sv)", "SendSIGHUP", "b", arg_send_sighup
);
286 static int message_start_transient_unit_send(sd_bus
*bus
, sd_bus_message
*m
, sd_bus_error
*error
, sd_bus_message
**reply
) {
292 r
= sd_bus_message_close_container(m
);
296 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
300 return sd_bus_call(bus
, m
, 0, error
, reply
);
303 static int start_transient_service(
306 sd_bus_error
*error
) {
308 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
309 _cleanup_free_
char *name
= NULL
;
313 name
= unit_name_mangle_with_suffix(arg_unit
, MANGLE_NOGLOB
, ".service");
315 asprintf(&name
, "run-%lu.service", (unsigned long) getpid());
319 r
= message_start_transient_unit_new(bus
, name
, &m
);
323 r
= sd_bus_message_append(m
, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit
);
327 if (arg_service_type
) {
328 r
= sd_bus_message_append(m
, "(sv)", "Type", "s", arg_service_type
);
334 r
= sd_bus_message_append(m
, "(sv)", "User", "s", arg_exec_user
);
339 if (arg_exec_group
) {
340 r
= sd_bus_message_append(m
, "(sv)", "Group", "s", arg_exec_group
);
346 r
= sd_bus_message_append(m
, "(sv)", "Nice", "i", arg_nice
);
351 if (!strv_isempty(arg_environment
)) {
352 r
= sd_bus_message_open_container(m
, 'r', "sv");
356 r
= sd_bus_message_append(m
, "s", "Environment");
360 r
= sd_bus_message_open_container(m
, 'v', "as");
364 r
= sd_bus_message_append_strv(m
, arg_environment
);
368 r
= sd_bus_message_close_container(m
);
372 r
= sd_bus_message_close_container(m
);
377 r
= sd_bus_message_open_container(m
, 'r', "sv");
381 r
= sd_bus_message_append(m
, "s", "ExecStart");
385 r
= sd_bus_message_open_container(m
, 'v', "a(sasb)");
389 r
= sd_bus_message_open_container(m
, 'a', "(sasb)");
393 r
= sd_bus_message_open_container(m
, 'r', "sasb");
397 r
= sd_bus_message_append(m
, "s", argv
[0]);
401 r
= sd_bus_message_append_strv(m
, argv
);
405 r
= sd_bus_message_append(m
, "b", false);
409 r
= sd_bus_message_close_container(m
);
413 r
= sd_bus_message_close_container(m
);
417 r
= sd_bus_message_close_container(m
);
421 r
= sd_bus_message_close_container(m
);
425 return message_start_transient_unit_send(bus
, m
, error
, NULL
);
428 static int start_transient_scope(
431 sd_bus_error
*error
) {
433 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
434 _cleanup_free_
char *name
= NULL
;
440 name
= unit_name_mangle_with_suffix(arg_unit
, MANGLE_NOGLOB
, ".scope");
442 asprintf(&name
, "run-%lu.scope", (unsigned long) getpid());
446 r
= message_start_transient_unit_new(bus
, name
, &m
);
450 r
= sd_bus_message_append(m
, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
454 r
= message_start_transient_unit_send(bus
, m
, error
, NULL
);
458 execvp(argv
[0], argv
);
459 log_error("Failed to execute: %m");
463 int main(int argc
, char* argv
[]) {
464 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
465 _cleanup_bus_unref_ sd_bus
*bus
= NULL
;
466 _cleanup_free_
char *description
= NULL
, *command
= NULL
;
469 log_parse_environment();
472 r
= parse_argv(argc
, argv
);
476 r
= find_binary(argv
[optind
], &command
);
478 log_error("Failed to find executable %s: %s", argv
[optind
], strerror(-r
));
481 argv
[optind
] = command
;
483 if (!arg_description
) {
484 description
= strv_join(argv
+ optind
, " ");
490 arg_description
= description
;
493 r
= bus_open_transport(arg_transport
, arg_host
, arg_user
, &bus
);
495 log_error("Failed to create bus connection: %s", strerror(-r
));
500 r
= start_transient_scope(bus
, argv
+ optind
, &error
);
502 r
= start_transient_service(bus
, argv
+ optind
, &error
);
504 log_error("Failed start transient unit: %s", bus_error_message(&error
, r
));
507 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;