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/>.
26 #include "bus-internal.h"
27 #include "bus-message.h"
30 #include "unit-name.h"
31 #include "path-util.h"
33 static bool arg_scope = false;
34 static bool arg_user = false;
35 static bool arg_remain_after_exit = false;
36 static const char *arg_unit = NULL;
37 static const char *arg_description = NULL;
38 static const char *arg_slice = NULL;
39 static bool arg_send_sighup = false;
41 static int help(void) {
43 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
44 "Run the specified command in a transient scope or service unit.\n\n"
45 " -h --help Show this help\n"
46 " --version Show package version\n"
47 " --user Run as user unit\n"
48 " --scope Run this as scope rather than service\n"
49 " --unit=UNIT Run under the specified unit name\n"
50 " --description=TEXT Description for unit\n"
51 " --slice=SLICE Run in the specified slice\n"
52 " -r --remain-after-exit Leave service around until explicitly stopped\n"
53 " --send-sighup Send SIGHUP when terminating\n",
54 program_invocation_short_name);
59 static int parse_argv(int argc, char *argv[]) {
71 static const struct option options[] = {
72 { "help", no_argument, NULL, 'h' },
73 { "version", no_argument, NULL, ARG_VERSION },
74 { "user", no_argument, NULL, ARG_USER },
75 { "scope", no_argument, NULL, ARG_SCOPE },
76 { "unit", required_argument, NULL, ARG_UNIT },
77 { "description", required_argument, NULL, ARG_DESCRIPTION },
78 { "slice", required_argument, NULL, ARG_SLICE },
79 { "remain-after-exit", no_argument, NULL, 'r' },
80 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
89 while ((c = getopt_long(argc, argv, "+hr", options, NULL)) >= 0) {
99 puts(SYSTEMD_FEATURES);
114 case ARG_DESCRIPTION:
115 arg_description = optarg;
122 case ARG_SEND_SIGHUP:
123 arg_send_sighup = true;
127 arg_remain_after_exit = true;
134 log_error("Unknown option code %c", c);
139 if (optind >= argc) {
140 log_error("Command line to execute required.");
147 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
148 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
151 log_info("Running as unit %s.", name);
153 r = sd_bus_message_new_method_call(
155 "org.freedesktop.systemd1",
156 "/org/freedesktop/systemd1",
157 "org.freedesktop.systemd1.Manager",
158 "StartTransientUnit", &m);
162 r = sd_bus_message_append(m, "ss", name, "fail");
166 r = sd_bus_message_open_container(m, 'a', "(sv)");
170 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
174 if (!isempty(arg_slice)) {
175 _cleanup_free_ char *slice;
177 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
181 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
186 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
196 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
199 r = sd_bus_message_close_container(m);
203 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
206 static int start_transient_service(
209 sd_bus_error *error) {
211 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
212 _cleanup_free_ char *name = NULL;
217 name = unit_name_mangle_with_suffix(arg_unit, ".service");
219 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
223 r = message_start_transient_unit_new(bus, name, &m);
227 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
231 r = sd_bus_message_open_container(m, 'r', "sv");
235 r = sd_bus_message_append(m, "s", "ExecStart");
239 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
243 r = sd_bus_message_open_container(m, 'a', "(sasb)");
247 r = sd_bus_message_open_container(m, 'r', "sasb");
251 r = sd_bus_message_append(m, "s", argv[0]);
255 r = sd_bus_message_open_container(m, 'a', "s");
259 STRV_FOREACH(i, argv) {
260 r = sd_bus_message_append(m, "s", *i);
265 r = sd_bus_message_close_container(m);
269 r = sd_bus_message_append(m, "b", false);
273 r = sd_bus_message_close_container(m);
277 r = sd_bus_message_close_container(m);
281 r = sd_bus_message_close_container(m);
285 r = sd_bus_message_close_container(m);
289 return message_start_transient_unit_send(bus, m, error, &reply);
292 static int start_transient_scope(
295 sd_bus_error *error) {
297 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
298 _cleanup_free_ char *name = NULL;
302 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
304 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
308 r = message_start_transient_unit_new(bus, name, &m);
312 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
316 r = message_start_transient_unit_send(bus, m, error, &reply);
320 execvp(argv[0], argv);
321 log_error("Failed to execute: %m");
325 int main(int argc, char* argv[]) {
326 sd_bus_error error = SD_BUS_ERROR_NULL;
327 _cleanup_bus_unref_ sd_bus *bus = NULL;
328 _cleanup_free_ char *description = NULL, *command = NULL;
331 log_parse_environment();
334 r = parse_argv(argc, argv);
338 r = find_binary(argv[optind], &command);
340 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
343 argv[optind] = command;
345 if (!arg_description) {
346 description = strv_join(argv + optind, " ");
352 arg_description = description;
356 r = sd_bus_open_user(&bus);
358 r = sd_bus_open_system(&bus);
360 log_error("Failed to create new bus connection: %s", strerror(-r));
365 r = start_transient_scope(bus, argv + optind, &error);
367 r = start_transient_service(bus, argv + optind, &error);
369 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
370 sd_bus_error_free(&error);
375 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;