]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/run/run.c
bus: rework message handlers to always take an error argument
[thirdparty/systemd.git] / src / run / run.c
CommitLineData
c2756a68
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdio.h>
6c12b52e 23#include <getopt.h>
c2756a68
LP
24
25#include "sd-bus.h"
40ca29a1 26#include "bus-util.h"
c2756a68 27#include "strv.h"
6c12b52e
LP
28#include "build.h"
29#include "unit-name.h"
4bcc8c3c 30#include "path-util.h"
94676f3e 31#include "bus-error.h"
c2756a68 32
6c12b52e 33static bool arg_scope = false;
6577c7ce 34static bool arg_remain_after_exit = false;
6c12b52e 35static const char *arg_unit = NULL;
9f2e86af 36static const char *arg_description = NULL;
c221420b 37static const char *arg_slice = NULL;
a6c0353b 38static bool arg_send_sighup = false;
d21ed1ea
LP
39static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
40static char *arg_host = NULL;
41static bool arg_user = false;
c2756a68 42
6c12b52e
LP
43static int help(void) {
44
c9d954b2 45 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
3803cde4 46 "Run the specified command in a transient scope or service unit.\n\n"
6577c7ce
LP
47 " -h --help Show this help\n"
48 " --version Show package version\n"
49 " --user Run as user unit\n"
66b1e746
LP
50 " -H --host=[USER@]HOST Operate on remote host\n"
51 " -M --machine=CONTAINER Operate on local container\n"
6577c7ce
LP
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"
a6c0353b
LP
56 " -r --remain-after-exit Leave service around until explicitly stopped\n"
57 " --send-sighup Send SIGHUP when terminating\n",
6c12b52e
LP
58 program_invocation_short_name);
59
60 return 0;
61}
62
63static int parse_argv(int argc, char *argv[]) {
64
65 enum {
66 ARG_VERSION = 0x100,
67 ARG_USER,
66b1e746 68 ARG_SYSTEM,
6c12b52e 69 ARG_SCOPE,
9f2e86af 70 ARG_UNIT,
c221420b 71 ARG_DESCRIPTION,
a6c0353b
LP
72 ARG_SLICE,
73 ARG_SEND_SIGHUP,
6c12b52e
LP
74 };
75
76 static const struct option options[] = {
6577c7ce
LP
77 { "help", no_argument, NULL, 'h' },
78 { "version", no_argument, NULL, ARG_VERSION },
79 { "user", no_argument, NULL, ARG_USER },
66b1e746 80 { "system", no_argument, NULL, ARG_SYSTEM },
6577c7ce
LP
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 },
a6c0353b
LP
85 { "remain-after-exit", no_argument, NULL, 'r' },
86 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
d21ed1ea
LP
87 { "host", required_argument, NULL, 'H' },
88 { "machine", required_argument, NULL, 'M' },
eb9da376 89 {},
6c12b52e
LP
90 };
91
92 int c;
93
94 assert(argc >= 0);
95 assert(argv);
96
d21ed1ea 97 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
6c12b52e
LP
98
99 switch (c) {
100
101 case 'h':
eb9da376 102 return help();
6c12b52e
LP
103
104 case ARG_VERSION:
105 puts(PACKAGE_STRING);
106 puts(SYSTEMD_FEATURES);
107 return 0;
108
109 case ARG_USER:
110 arg_user = true;
111 break;
112
66b1e746
LP
113 case ARG_SYSTEM:
114 arg_user = false;
115 break;
116
6c12b52e
LP
117 case ARG_SCOPE:
118 arg_scope = true;
119 break;
120
121 case ARG_UNIT:
122 arg_unit = optarg;
123 break;
124
9f2e86af
LP
125 case ARG_DESCRIPTION:
126 arg_description = optarg;
127 break;
128
c221420b
LP
129 case ARG_SLICE:
130 arg_slice = optarg;
131 break;
132
a6c0353b
LP
133 case ARG_SEND_SIGHUP:
134 arg_send_sighup = true;
135 break;
136
6577c7ce
LP
137 case 'r':
138 arg_remain_after_exit = true;
139 break;
140
d21ed1ea
LP
141 case 'H':
142 arg_transport = BUS_TRANSPORT_REMOTE;
143 arg_host = optarg;
144 break;
145
146 case 'M':
147 arg_transport = BUS_TRANSPORT_CONTAINER;
148 arg_host = optarg;
149 break;
150
6c12b52e
LP
151 case '?':
152 return -EINVAL;
153
154 default:
eb9da376 155 assert_not_reached("Unhandled option");
6c12b52e
LP
156 }
157 }
158
159 if (optind >= argc) {
160 log_error("Command line to execute required.");
161 return -EINVAL;
162 }
163
d21ed1ea
LP
164 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
165 log_error("Execution in user context is not supported on non-local systems.");
166 return -EINVAL;
167 }
168
169 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
170 log_error("Scope execution is not supported on non-local systems.");
171 return -EINVAL;
172 }
173
6c12b52e
LP
174 return 1;
175}
176
177static 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;
c2756a68
LP
179 int r;
180
8159d91a
LP
181 assert(bus);
182 assert(name);
183 assert(ret);
184
6c12b52e
LP
185 log_info("Running as unit %s.", name);
186
c2756a68
LP
187 r = sd_bus_message_new_method_call(
188 bus,
189 "org.freedesktop.systemd1",
190 "/org/freedesktop/systemd1",
191 "org.freedesktop.systemd1.Manager",
192 "StartTransientUnit", &m);
193 if (r < 0)
194 return r;
195
196 r = sd_bus_message_append(m, "ss", name, "fail");
197 if (r < 0)
198 return r;
199
200 r = sd_bus_message_open_container(m, 'a', "(sv)");
201 if (r < 0)
202 return r;
203
9f2e86af 204 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
c2756a68
LP
205 if (r < 0)
206 return r;
207
c221420b
LP
208 if (!isempty(arg_slice)) {
209 _cleanup_free_ char *slice;
210
211 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
212 if (!slice)
213 return -ENOMEM;
214
215 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
216 if (r < 0)
217 return r;
218 }
219
a6c0353b
LP
220 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
221 if (r < 0)
222 return r;
223
6c12b52e
LP
224 *ret = m;
225 m = NULL;
226
227 return 0;
228}
229
230static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
231 int r;
232
8159d91a
LP
233 assert(bus);
234 assert(m);
235
6c12b52e
LP
236 r = sd_bus_message_close_container(m);
237 if (r < 0)
238 return r;
239
c49b30a2 240 return sd_bus_call(bus, m, 0, error, reply);
6c12b52e
LP
241}
242
243static int start_transient_service(
244 sd_bus *bus,
245 char **argv,
246 sd_bus_error *error) {
247
8159d91a 248 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
6c12b52e
LP
249 _cleanup_free_ char *name = NULL;
250 char **i;
251 int r;
252
253 if (arg_unit)
254 name = unit_name_mangle_with_suffix(arg_unit, ".service");
255 else
256 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
257 if (!name)
258 return -ENOMEM;
259
260 r = message_start_transient_unit_new(bus, name, &m);
261 if (r < 0)
262 return r;
263
6577c7ce
LP
264 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
265 if (r < 0)
266 return r;
267
9f2e86af
LP
268 r = sd_bus_message_open_container(m, 'r', "sv");
269 if (r < 0)
270 return r;
271
c2756a68
LP
272 r = sd_bus_message_append(m, "s", "ExecStart");
273 if (r < 0)
274 return r;
275
276 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
277 if (r < 0)
278 return r;
279
280 r = sd_bus_message_open_container(m, 'a', "(sasb)");
281 if (r < 0)
282 return r;
283
284 r = sd_bus_message_open_container(m, 'r', "sasb");
285 if (r < 0)
286 return r;
287
288 r = sd_bus_message_append(m, "s", argv[0]);
289 if (r < 0)
290 return r;
291
292 r = sd_bus_message_open_container(m, 'a', "s");
293 if (r < 0)
294 return r;
295
296 STRV_FOREACH(i, argv) {
297 r = sd_bus_message_append(m, "s", *i);
298 if (r < 0)
299 return r;
300 }
301
302 r = sd_bus_message_close_container(m);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_message_append(m, "b", false);
307 if (r < 0)
308 return r;
309
310 r = sd_bus_message_close_container(m);
311 if (r < 0)
312 return r;
313
314 r = sd_bus_message_close_container(m);
315 if (r < 0)
316 return r;
317
318 r = sd_bus_message_close_container(m);
319 if (r < 0)
320 return r;
321
9f2e86af
LP
322 r = sd_bus_message_close_container(m);
323 if (r < 0)
324 return r;
325
8159d91a 326 return message_start_transient_unit_send(bus, m, error, NULL);
6c12b52e
LP
327}
328
329static int start_transient_scope(
330 sd_bus *bus,
331 char **argv,
332 sd_bus_error *error) {
333
8159d91a 334 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
6c12b52e
LP
335 _cleanup_free_ char *name = NULL;
336 int r;
337
8159d91a
LP
338 assert(bus);
339
6c12b52e
LP
340 if (arg_unit)
341 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
342 else
343 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
344 if (!name)
345 return -ENOMEM;
346
347 r = message_start_transient_unit_new(bus, name, &m);
c2756a68
LP
348 if (r < 0)
349 return r;
350
9f2e86af 351 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
6c12b52e
LP
352 if (r < 0)
353 return r;
354
8159d91a 355 r = message_start_transient_unit_send(bus, m, error, NULL);
c2756a68
LP
356 if (r < 0)
357 return r;
358
6c12b52e
LP
359 execvp(argv[0], argv);
360 log_error("Failed to execute: %m");
361 return -errno;
c2756a68
LP
362}
363
364int main(int argc, char* argv[]) {
8159d91a 365 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
c2756a68 366 _cleanup_bus_unref_ sd_bus *bus = NULL;
c9d954b2 367 _cleanup_free_ char *description = NULL, *command = NULL;
c2756a68
LP
368 int r;
369
370 log_parse_environment();
371 log_open();
372
6c12b52e
LP
373 r = parse_argv(argc, argv);
374 if (r <= 0)
66b1e746 375 goto finish;
c2756a68 376
c9d954b2
ZJS
377 r = find_binary(argv[optind], &command);
378 if (r < 0) {
379 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
66b1e746 380 goto finish;
c9d954b2
ZJS
381 }
382 argv[optind] = command;
383
9f2e86af
LP
384 if (!arg_description) {
385 description = strv_join(argv + optind, " ");
386 if (!description) {
387 r = log_oom();
66b1e746 388 goto finish;
9f2e86af
LP
389 }
390
391 arg_description = description;
392 }
393
d21ed1ea 394 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
c2756a68 395 if (r < 0) {
d21ed1ea 396 log_error("Failed to create bus connection: %s", strerror(-r));
66b1e746 397 goto finish;
c2756a68
LP
398 }
399
6c12b52e
LP
400 if (arg_scope)
401 r = start_transient_scope(bus, argv + optind, &error);
402 else
403 r = start_transient_service(bus, argv + optind, &error);
8159d91a
LP
404 if (r < 0)
405 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
c2756a68 406
66b1e746 407finish:
c2756a68
LP
408 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
409}