]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
localectl: port over to bus_log_create_error()
[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 log_info("Running as unit %s.", name);
182
183 r = sd_bus_message_new_method_call(
184 bus,
185 "org.freedesktop.systemd1",
186 "/org/freedesktop/systemd1",
187 "org.freedesktop.systemd1.Manager",
188 "StartTransientUnit", &m);
189 if (r < 0)
190 return r;
191
192 r = sd_bus_message_append(m, "ss", name, "fail");
193 if (r < 0)
194 return r;
195
196 r = sd_bus_message_open_container(m, 'a', "(sv)");
197 if (r < 0)
198 return r;
199
200 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
201 if (r < 0)
202 return r;
203
204 if (!isempty(arg_slice)) {
205 _cleanup_free_ char *slice;
206
207 slice = unit_name_mangle_with_suffix(arg_slice, ".slice");
208 if (!slice)
209 return -ENOMEM;
210
211 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
212 if (r < 0)
213 return r;
214 }
215
216 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
217 if (r < 0)
218 return r;
219
220 *ret = m;
221 m = NULL;
222
223 return 0;
224 }
225
226 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
227 int r;
228
229 r = sd_bus_message_close_container(m);
230 if (r < 0)
231 return r;
232
233 return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
234 }
235
236 static int start_transient_service(
237 sd_bus *bus,
238 char **argv,
239 sd_bus_error *error) {
240
241 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
242 _cleanup_free_ char *name = NULL;
243 char **i;
244 int r;
245
246 if (arg_unit)
247 name = unit_name_mangle_with_suffix(arg_unit, ".service");
248 else
249 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
250 if (!name)
251 return -ENOMEM;
252
253 r = message_start_transient_unit_new(bus, name, &m);
254 if (r < 0)
255 return r;
256
257 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
258 if (r < 0)
259 return r;
260
261 r = sd_bus_message_open_container(m, 'r', "sv");
262 if (r < 0)
263 return r;
264
265 r = sd_bus_message_append(m, "s", "ExecStart");
266 if (r < 0)
267 return r;
268
269 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
270 if (r < 0)
271 return r;
272
273 r = sd_bus_message_open_container(m, 'a', "(sasb)");
274 if (r < 0)
275 return r;
276
277 r = sd_bus_message_open_container(m, 'r', "sasb");
278 if (r < 0)
279 return r;
280
281 r = sd_bus_message_append(m, "s", argv[0]);
282 if (r < 0)
283 return r;
284
285 r = sd_bus_message_open_container(m, 'a', "s");
286 if (r < 0)
287 return r;
288
289 STRV_FOREACH(i, argv) {
290 r = sd_bus_message_append(m, "s", *i);
291 if (r < 0)
292 return r;
293 }
294
295 r = sd_bus_message_close_container(m);
296 if (r < 0)
297 return r;
298
299 r = sd_bus_message_append(m, "b", false);
300 if (r < 0)
301 return r;
302
303 r = sd_bus_message_close_container(m);
304 if (r < 0)
305 return r;
306
307 r = sd_bus_message_close_container(m);
308 if (r < 0)
309 return r;
310
311 r = sd_bus_message_close_container(m);
312 if (r < 0)
313 return r;
314
315 r = sd_bus_message_close_container(m);
316 if (r < 0)
317 return r;
318
319 return message_start_transient_unit_send(bus, m, error, &reply);
320 }
321
322 static int start_transient_scope(
323 sd_bus *bus,
324 char **argv,
325 sd_bus_error *error) {
326
327 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
328 _cleanup_free_ char *name = NULL;
329 int r;
330
331 if (arg_unit)
332 name = unit_name_mangle_with_suffix(arg_unit, ".scope");
333 else
334 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
335 if (!name)
336 return -ENOMEM;
337
338 r = message_start_transient_unit_new(bus, name, &m);
339 if (r < 0)
340 return r;
341
342 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
343 if (r < 0)
344 return r;
345
346 r = message_start_transient_unit_send(bus, m, error, &reply);
347 if (r < 0)
348 return r;
349
350 execvp(argv[0], argv);
351 log_error("Failed to execute: %m");
352 return -errno;
353 }
354
355 int main(int argc, char* argv[]) {
356 sd_bus_error error = SD_BUS_ERROR_NULL;
357 _cleanup_bus_unref_ sd_bus *bus = NULL;
358 _cleanup_free_ char *description = NULL, *command = NULL;
359 int r;
360
361 log_parse_environment();
362 log_open();
363
364 r = parse_argv(argc, argv);
365 if (r <= 0)
366 goto finish;
367
368 r = find_binary(argv[optind], &command);
369 if (r < 0) {
370 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
371 goto finish;
372 }
373 argv[optind] = command;
374
375 if (!arg_description) {
376 description = strv_join(argv + optind, " ");
377 if (!description) {
378 r = log_oom();
379 goto finish;
380 }
381
382 arg_description = description;
383 }
384
385 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
386 if (r < 0) {
387 log_error("Failed to create bus connection: %s", strerror(-r));
388 goto finish;
389 }
390
391 if (arg_scope)
392 r = start_transient_scope(bus, argv + optind, &error);
393 else
394 r = start_transient_service(bus, argv + optind, &error);
395 if (r < 0) {
396 log_error("Failed start transient unit: %s", error.message ? error.message : strerror(-r));
397 sd_bus_error_free(&error);
398 goto finish;
399 }
400
401 finish:
402 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
403 }