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"
32 static bool arg_scope = false;
33 static bool arg_remain_after_exit = false;
34 static const char *arg_unit = NULL;
35 static const char *arg_description = NULL;
36 static const char *arg_slice = NULL;
37 static bool arg_send_sighup = false;
38 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
39 static char *arg_host = NULL;
40 static bool arg_user = false;
42 static int help(void) {
44 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
45 "Run the specified command in a transient scope or service unit.\n\n"
46 " -h --help Show this help\n"
47 " --version Show package version\n"
48 " --user Run as user unit\n"
49 " -H --host=[USER@]HOST Operate on remote host\n"
50 " -M --machine=CONTAINER Operate on local container\n"
51 " --scope Run this as scope rather than service\n"
52 " --unit=UNIT Run under the specified unit name\n"
53 " --description=TEXT Description for unit\n"
54 " --slice=SLICE Run in the specified slice\n"
55 " -r --remain-after-exit Leave service around until explicitly stopped\n"
56 " --send-sighup Send SIGHUP when terminating\n",
57 program_invocation_short_name);
62 static int parse_argv(int argc, char *argv[]) {
75 static const struct option options[] = {
76 { "help", no_argument, NULL, 'h' },
77 { "version", no_argument, NULL, ARG_VERSION },
78 { "user", no_argument, NULL, ARG_USER },
79 { "system", no_argument, NULL, ARG_SYSTEM },
80 { "scope", no_argument, NULL, ARG_SCOPE },
81 { "unit", required_argument, NULL, ARG_UNIT },
82 { "description", required_argument, NULL, ARG_DESCRIPTION },
83 { "slice", required_argument, NULL, ARG_SLICE },
84 { "remain-after-exit", no_argument, NULL, 'r' },
85 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
86 { "host", required_argument, NULL, 'H' },
87 { "machine", required_argument, NULL, 'M' },
96 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
104 puts(PACKAGE_STRING);
105 puts(SYSTEMD_FEATURES);
124 case ARG_DESCRIPTION:
125 arg_description = optarg;
132 case ARG_SEND_SIGHUP:
133 arg_send_sighup = true;
137 arg_remain_after_exit = true;
141 arg_transport = BUS_TRANSPORT_REMOTE;
146 arg_transport = BUS_TRANSPORT_CONTAINER;
154 assert_not_reached("Unhandled option");
158 if (optind >= argc) {
159 log_error("Command line to execute required.");
163 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
164 log_error("Execution in user context is not supported on non-local systems.");
168 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
169 log_error("Scope execution is not supported on non-local systems.");
176 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
177 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
180 log_info("Running as unit %s.", name);
182 r = sd_bus_message_new_method_call(
184 "org.freedesktop.systemd1",
185 "/org/freedesktop/systemd1",
186 "org.freedesktop.systemd1.Manager",
187 "StartTransientUnit", &m);
191 r = sd_bus_message_append(m, "ss", name, "fail");
195 r = sd_bus_message_open_container(m, 'a', "(sv)");
199 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
203 if (!isempty(arg_slice)) {
204 _cleanup_free_ char *slice;
206 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
210 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
215 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
225 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
228 r = sd_bus_message_close_container(m);
232 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
235 static int start_transient_service(
238 sd_bus_error *error) {
240 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
241 _cleanup_free_ char *name = NULL;
246 name = unit_name_mangle_with_suffix(arg_unit, ".service");
248 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
252 r = message_start_transient_unit_new(bus, name, &m);
256 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
260 r = sd_bus_message_open_container(m, 'r', "sv");
264 r = sd_bus_message_append(m, "s", "ExecStart");
268 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
272 r = sd_bus_message_open_container(m, 'a', "(sasb)");
276 r = sd_bus_message_open_container(m, 'r', "sasb");
280 r = sd_bus_message_append(m, "s", argv[0]);
284 r = sd_bus_message_open_container(m, 'a', "s");
288 STRV_FOREACH(i, argv) {
289 r = sd_bus_message_append(m, "s", *i);
294 r = sd_bus_message_close_container(m);
298 r = sd_bus_message_append(m, "b", false);
302 r = sd_bus_message_close_container(m);
306 r = sd_bus_message_close_container(m);
310 r = sd_bus_message_close_container(m);
314 r = sd_bus_message_close_container(m);
318 return message_start_transient_unit_send(bus, m, error, &reply);
321 static int start_transient_scope(
324 sd_bus_error *error) {
326 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
327 _cleanup_free_ char *name = NULL;
331 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
333 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
337 r = message_start_transient_unit_new(bus, name, &m);
341 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
345 r = message_start_transient_unit_send(bus, m, error, &reply);
349 execvp(argv[0], argv);
350 log_error("Failed to execute: %m");
354 int main(int argc, char* argv[]) {
355 sd_bus_error error = SD_BUS_ERROR_NULL;
356 _cleanup_bus_unref_ sd_bus *bus = NULL;
357 _cleanup_free_ char *description = NULL, *command = NULL;
360 log_parse_environment();
363 r = parse_argv(argc, argv);
367 r = find_binary(argv[optind], &command);
369 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
372 argv[optind] = command;
374 if (!arg_description) {
375 description = strv_join(argv + optind, " ");
381 arg_description = description;
384 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
386 log_error("Failed to create bus connection: %s", strerror(-r));
391 r = start_transient_scope(bus, argv + optind, &error);
393 r = start_transient_service(bus, argv + optind, &error);
395 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
396 sd_bus_error_free(&error);
401 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;