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;
43 static int help(void) {
45 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
46 "Run the specified command in a transient scope or service unit.\n\n"
47 " -h --help Show this help\n"
48 " --version Show package version\n"
49 " --user Run as user unit\n"
50 " -H --host=[USER@]HOST Operate on remote host\n"
51 " -M --machine=CONTAINER Operate on local container\n"
52 " --scope Run this as scope rather than service\n"
53 " --unit=UNIT Run under the specified unit name\n"
54 " --description=TEXT Description for unit\n"
55 " --slice=SLICE Run in the specified slice\n"
56 " -r --remain-after-exit Leave service around until explicitly stopped\n"
57 " --send-sighup Send SIGHUP when terminating\n",
58 program_invocation_short_name);
63 static int parse_argv(int argc, char *argv[]) {
76 static const struct option options[] = {
77 { "help", no_argument, NULL, 'h' },
78 { "version", no_argument, NULL, ARG_VERSION },
79 { "user", no_argument, NULL, ARG_USER },
80 { "system", no_argument, NULL, ARG_SYSTEM },
81 { "scope", no_argument, NULL, ARG_SCOPE },
82 { "unit", required_argument, NULL, ARG_UNIT },
83 { "description", required_argument, NULL, ARG_DESCRIPTION },
84 { "slice", required_argument, NULL, ARG_SLICE },
85 { "remain-after-exit", no_argument, NULL, 'r' },
86 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
87 { "host", required_argument, NULL, 'H' },
88 { "machine", required_argument, NULL, 'M' },
97 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
105 puts(PACKAGE_STRING);
106 puts(SYSTEMD_FEATURES);
125 case ARG_DESCRIPTION:
126 arg_description = optarg;
133 case ARG_SEND_SIGHUP:
134 arg_send_sighup = true;
138 arg_remain_after_exit = true;
142 arg_transport = BUS_TRANSPORT_REMOTE;
147 arg_transport = BUS_TRANSPORT_CONTAINER;
155 assert_not_reached("Unhandled option");
159 if (optind >= argc) {
160 log_error("Command line to execute required.");
164 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
165 log_error("Execution in user context is not supported on non-local systems.");
169 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
170 log_error("Scope execution is not supported on non-local systems.");
177 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
178 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
181 log_info("Running as unit %s.", name);
183 r = sd_bus_message_new_method_call(
185 "org.freedesktop.systemd1",
186 "/org/freedesktop/systemd1",
187 "org.freedesktop.systemd1.Manager",
188 "StartTransientUnit", &m);
192 r = sd_bus_message_append(m, "ss", name, "fail");
196 r = sd_bus_message_open_container(m, 'a', "(sv)");
200 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
204 if (!isempty(arg_slice)) {
205 _cleanup_free_ char *slice;
207 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
211 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
216 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
226 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
229 r = sd_bus_message_close_container(m);
233 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
236 static int start_transient_service(
239 sd_bus_error *error) {
241 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
242 _cleanup_free_ char *name = NULL;
247 name = unit_name_mangle_with_suffix(arg_unit, ".service");
249 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
253 r = message_start_transient_unit_new(bus, name, &m);
257 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
261 r = sd_bus_message_open_container(m, 'r', "sv");
265 r = sd_bus_message_append(m, "s", "ExecStart");
269 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
273 r = sd_bus_message_open_container(m, 'a', "(sasb)");
277 r = sd_bus_message_open_container(m, 'r', "sasb");
281 r = sd_bus_message_append(m, "s", argv[0]);
285 r = sd_bus_message_open_container(m, 'a', "s");
289 STRV_FOREACH(i, argv) {
290 r = sd_bus_message_append(m, "s", *i);
295 r = sd_bus_message_close_container(m);
299 r = sd_bus_message_append(m, "b", false);
303 r = sd_bus_message_close_container(m);
307 r = sd_bus_message_close_container(m);
311 r = sd_bus_message_close_container(m);
315 r = sd_bus_message_close_container(m);
319 return message_start_transient_unit_send(bus, m, error, &reply);
322 static int start_transient_scope(
325 sd_bus_error *error) {
327 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
328 _cleanup_free_ char *name = NULL;
332 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
334 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
338 r = message_start_transient_unit_new(bus, name, &m);
342 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
346 r = message_start_transient_unit_send(bus, m, error, &reply);
350 execvp(argv[0], argv);
351 log_error("Failed to execute: %m");
355 int main(int argc, char* argv[]) {
356 sd_bus_error error = SD_BUS_ERROR_NULL;
357 _cleanup_bus_unref_ sd_bus *bus = NULL;
358 _cleanup_free_ char *description = NULL, *command = NULL;
361 log_parse_environment();
364 r = parse_argv(argc, argv);
368 r = find_binary(argv[optind], &command);
370 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
373 argv[optind] = command;
375 if (!arg_description) {
376 description = strv_join(argv + optind, " ");
382 arg_description = description;
385 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
387 log_error("Failed to create bus connection: %s", strerror(-r));
392 r = start_transient_scope(bus, argv + optind, &error);
394 r = start_transient_service(bus, argv + optind, &error);
396 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
397 sd_bus_error_free(&error);
402 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;