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"
32 static bool arg_scope = false;
33 static bool arg_user = 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;
39 static int help(void) {
41 printf("%s [OPTIONS...] [COMMAND LINE...]\n\n"
42 "Run the specified command in a transient scope or service unit.\n\n"
43 " -h --help Show this help\n"
44 " --version Show package version\n"
45 " --user Run as user unit\n"
46 " --scope Run this as scope rather than service\n"
47 " --unit=UNIT Run under the specified unit name\n"
48 " --description=TEXT Description for unit\n"
49 " --slice=SLICE Run in the specified slice\n"
50 " -r --remain-after-exit Leave service around until explicitly stopped\n",
51 program_invocation_short_name);
56 static int parse_argv(int argc, char *argv[]) {
67 static const struct option options[] = {
68 { "help", no_argument, NULL, 'h' },
69 { "version", no_argument, NULL, ARG_VERSION },
70 { "user", no_argument, NULL, ARG_USER },
71 { "scope", no_argument, NULL, ARG_SCOPE },
72 { "unit", required_argument, NULL, ARG_UNIT },
73 { "description", required_argument, NULL, ARG_DESCRIPTION },
74 { "slice", required_argument, NULL, ARG_SLICE },
75 { "remain-after-exit", required_argument, NULL, 'r' },
84 while ((c = getopt_long(argc, argv, "+hr", options, NULL)) >= 0) {
94 puts(SYSTEMD_FEATURES);
109 case ARG_DESCRIPTION:
110 arg_description = optarg;
118 arg_remain_after_exit = true;
125 log_error("Unknown option code %c", c);
130 if (optind >= argc) {
131 log_error("Command line to execute required.");
138 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
139 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
142 log_info("Running as unit %s.", name);
144 r = sd_bus_message_new_method_call(
146 "org.freedesktop.systemd1",
147 "/org/freedesktop/systemd1",
148 "org.freedesktop.systemd1.Manager",
149 "StartTransientUnit", &m);
153 r = sd_bus_message_append(m, "ss", name, "fail");
157 r = sd_bus_message_open_container(m, 'a', "(sv)");
161 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
165 if (!isempty(arg_slice)) {
166 _cleanup_free_ char *slice;
168 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
172 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
183 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
186 r = sd_bus_message_close_container(m);
190 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
193 static int start_transient_service(
196 sd_bus_error *error) {
198 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
199 _cleanup_free_ char *name = NULL;
204 name = unit_name_mangle_with_suffix(arg_unit, ".service");
206 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
210 r = message_start_transient_unit_new(bus, name, &m);
214 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
218 r = sd_bus_message_open_container(m, 'r', "sv");
222 r = sd_bus_message_append(m, "s", "ExecStart");
226 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
230 r = sd_bus_message_open_container(m, 'a', "(sasb)");
234 r = sd_bus_message_open_container(m, 'r', "sasb");
238 r = sd_bus_message_append(m, "s", argv[0]);
242 r = sd_bus_message_open_container(m, 'a', "s");
246 STRV_FOREACH(i, argv) {
247 r = sd_bus_message_append(m, "s", *i);
252 r = sd_bus_message_close_container(m);
256 r = sd_bus_message_append(m, "b", false);
260 r = sd_bus_message_close_container(m);
264 r = sd_bus_message_close_container(m);
268 r = sd_bus_message_close_container(m);
272 r = sd_bus_message_close_container(m);
276 return message_start_transient_unit_send(bus, m, error, &reply);
279 static int start_transient_scope(
282 sd_bus_error *error) {
284 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
285 _cleanup_free_ char *name = NULL;
289 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
291 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
295 r = message_start_transient_unit_new(bus, name, &m);
299 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
303 r = message_start_transient_unit_send(bus, m, error, &reply);
307 execvp(argv[0], argv);
308 log_error("Failed to execute: %m");
312 int main(int argc, char* argv[]) {
313 sd_bus_error error = SD_BUS_ERROR_NULL;
314 _cleanup_bus_unref_ sd_bus *bus = NULL;
315 _cleanup_free_ char *description = NULL;
318 log_parse_environment();
321 r = parse_argv(argc, argv);
325 if (!arg_description) {
326 description = strv_join(argv + optind, " ");
332 arg_description = description;
336 r = sd_bus_open_user(&bus);
338 r = sd_bus_open_system(&bus);
340 log_error("Failed to create new bus connection: %s", strerror(-r));
345 r = start_transient_scope(bus, argv + optind, &error);
347 r = start_transient_service(bus, argv + optind, &error);
349 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
350 sd_bus_error_free(&error);
355 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;