]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
bus: rename sd_bus_send_with_reply_and_block() to sd_bus_call()
[thirdparty/systemd.git] / src / run / run.c
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>
23 #include <getopt.h>
24
25 #include "sd-bus.h"
26 #include "bus-util.h"
27 #include "strv.h"
28 #include "build.h"
29 #include "unit-name.h"
30 #include "path-util.h"
31 #include "bus-error.h"
32
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;
42
43 static int help(void) {
44
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);
59
60 return 0;
61 }
62
63 static int parse_argv(int argc, char *argv[]) {
64
65 enum {
66 ARG_VERSION = 0x100,
67 ARG_USER,
68 ARG_SYSTEM,
69 ARG_SCOPE,
70 ARG_UNIT,
71 ARG_DESCRIPTION,
72 ARG_SLICE,
73 ARG_SEND_SIGHUP,
74 };
75
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' },
89 {},
90 };
91
92 int c;
93
94 assert(argc >= 0);
95 assert(argv);
96
97 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
98
99 switch (c) {
100
101 case 'h':
102 return help();
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
113 case ARG_SYSTEM:
114 arg_user = false;
115 break;
116
117 case ARG_SCOPE:
118 arg_scope = true;
119 break;
120
121 case ARG_UNIT:
122 arg_unit = optarg;
123 break;
124
125 case ARG_DESCRIPTION:
126 arg_description = optarg;
127 break;
128
129 case ARG_SLICE:
130 arg_slice = optarg;
131 break;
132
133 case ARG_SEND_SIGHUP:
134 arg_send_sighup = true;
135 break;
136
137 case 'r':
138 arg_remain_after_exit = true;
139 break;
140
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
151 case '?':
152 return -EINVAL;
153
154 default:
155 assert_not_reached("Unhandled option");
156 }
157 }
158
159 if (optind >= argc) {
160 log_error("Command line to execute required.");
161 return -EINVAL;
162 }
163
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
174 return 1;
175 }
176
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;
179 int r;
180
181 assert(bus);
182 assert(name);
183 assert(ret);
184
185 log_info("Running as unit %s.", name);
186
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
204 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
205 if (r < 0)
206 return r;
207
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
220 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
221 if (r < 0)
222 return r;
223
224 *ret = m;
225 m = NULL;
226
227 return 0;
228 }
229
230 static 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
233 assert(bus);
234 assert(m);
235
236 r = sd_bus_message_close_container(m);
237 if (r < 0)
238 return r;
239
240 return sd_bus_call(bus, m, 0, error, reply);
241 }
242
243 static int start_transient_service(
244 sd_bus *bus,
245 char **argv,
246 sd_bus_error *error) {
247
248 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
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
264 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
265 if (r < 0)
266 return r;
267
268 r = sd_bus_message_open_container(m, 'r', "sv");
269 if (r < 0)
270 return r;
271
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
322 r = sd_bus_message_close_container(m);
323 if (r < 0)
324 return r;
325
326 return message_start_transient_unit_send(bus, m, error, NULL);
327 }
328
329 static int start_transient_scope(
330 sd_bus *bus,
331 char **argv,
332 sd_bus_error *error) {
333
334 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
335 _cleanup_free_ char *name = NULL;
336 int r;
337
338 assert(bus);
339
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);
348 if (r < 0)
349 return r;
350
351 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
352 if (r < 0)
353 return r;
354
355 r = message_start_transient_unit_send(bus, m, error, NULL);
356 if (r < 0)
357 return r;
358
359 execvp(argv[0], argv);
360 log_error("Failed to execute: %m");
361 return -errno;
362 }
363
364 int main(int argc, char* argv[]) {
365 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
366 _cleanup_bus_unref_ sd_bus *bus = NULL;
367 _cleanup_free_ char *description = NULL, *command = NULL;
368 int r;
369
370 log_parse_environment();
371 log_open();
372
373 r = parse_argv(argc, argv);
374 if (r <= 0)
375 goto finish;
376
377 r = find_binary(argv[optind], &command);
378 if (r < 0) {
379 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
380 goto finish;
381 }
382 argv[optind] = command;
383
384 if (!arg_description) {
385 description = strv_join(argv + optind, " ");
386 if (!description) {
387 r = log_oom();
388 goto finish;
389 }
390
391 arg_description = description;
392 }
393
394 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
395 if (r < 0) {
396 log_error("Failed to create bus connection: %s", strerror(-r));
397 goto finish;
398 }
399
400 if (arg_scope)
401 r = start_transient_scope(bus, argv + optind, &error);
402 else
403 r = start_transient_service(bus, argv + optind, &error);
404 if (r < 0)
405 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
406
407 finish:
408 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
409 }