]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/run/run.c
885d881ec6763ebf652bc295f5d5972b46627687
[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 static const char *arg_service_type = NULL;
43 static const char *arg_exec_user = NULL;
44 static const char *arg_exec_group = NULL;
45 static int arg_nice = 0;
46 static bool arg_nice_set = false;
47 static char **arg_environment = NULL;
48
49 static int help(void) {
50
51 printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
52 "Run the specified command in a transient scope or service unit.\n\n"
53 " -h --help Show this help\n"
54 " --version Show package version\n"
55 " --user Run as user unit\n"
56 " -H --host=[USER@]HOST Operate on remote host\n"
57 " -M --machine=CONTAINER Operate on local container\n"
58 " --scope Run this as scope rather than service\n"
59 " --unit=UNIT Run under the specified unit name\n"
60 " --description=TEXT Description for unit\n"
61 " --slice=SLICE Run in the specified slice\n"
62 " -r --remain-after-exit Leave service around until explicitly stopped\n"
63 " --send-sighup Send SIGHUP when terminating\n"
64 " --service-type=TYPE Service type\n"
65 " --uid=USER Run as system user\n"
66 " --gid=GROUP Run as system group\n"
67 " --nice=NICE Nice level\n"
68 " --setenv=NAME=VALUE Set environment\n",
69 program_invocation_short_name);
70
71 return 0;
72 }
73
74 static int parse_argv(int argc, char *argv[]) {
75
76 enum {
77 ARG_VERSION = 0x100,
78 ARG_USER,
79 ARG_SYSTEM,
80 ARG_SCOPE,
81 ARG_UNIT,
82 ARG_DESCRIPTION,
83 ARG_SLICE,
84 ARG_SEND_SIGHUP,
85 ARG_EXEC_USER,
86 ARG_EXEC_GROUP,
87 ARG_SERVICE_TYPE,
88 ARG_NICE,
89 ARG_SETENV
90 };
91
92 static const struct option options[] = {
93 { "help", no_argument, NULL, 'h' },
94 { "version", no_argument, NULL, ARG_VERSION },
95 { "user", no_argument, NULL, ARG_USER },
96 { "system", no_argument, NULL, ARG_SYSTEM },
97 { "scope", no_argument, NULL, ARG_SCOPE },
98 { "unit", required_argument, NULL, ARG_UNIT },
99 { "description", required_argument, NULL, ARG_DESCRIPTION },
100 { "slice", required_argument, NULL, ARG_SLICE },
101 { "remain-after-exit", no_argument, NULL, 'r' },
102 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
103 { "host", required_argument, NULL, 'H' },
104 { "machine", required_argument, NULL, 'M' },
105 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
106 { "uid", required_argument, NULL, ARG_EXEC_USER },
107 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
108 { "nice", required_argument, NULL, ARG_NICE },
109 { "setenv", required_argument, NULL, ARG_SETENV },
110 {},
111 };
112
113 int r, c;
114
115 assert(argc >= 0);
116 assert(argv);
117
118 while ((c = getopt_long(argc, argv, "+hrH:M:", options, NULL)) >= 0) {
119
120 switch (c) {
121
122 case 'h':
123 return help();
124
125 case ARG_VERSION:
126 puts(PACKAGE_STRING);
127 puts(SYSTEMD_FEATURES);
128 return 0;
129
130 case ARG_USER:
131 arg_user = true;
132 break;
133
134 case ARG_SYSTEM:
135 arg_user = false;
136 break;
137
138 case ARG_SCOPE:
139 arg_scope = true;
140 break;
141
142 case ARG_UNIT:
143 arg_unit = optarg;
144 break;
145
146 case ARG_DESCRIPTION:
147 arg_description = optarg;
148 break;
149
150 case ARG_SLICE:
151 arg_slice = optarg;
152 break;
153
154 case ARG_SEND_SIGHUP:
155 arg_send_sighup = true;
156 break;
157
158 case 'r':
159 arg_remain_after_exit = true;
160 break;
161
162 case 'H':
163 arg_transport = BUS_TRANSPORT_REMOTE;
164 arg_host = optarg;
165 break;
166
167 case 'M':
168 arg_transport = BUS_TRANSPORT_CONTAINER;
169 arg_host = optarg;
170 break;
171
172 case ARG_SERVICE_TYPE:
173 arg_service_type = optarg;
174 break;
175
176 case ARG_EXEC_USER:
177 arg_exec_user = optarg;
178 break;
179
180 case ARG_EXEC_GROUP:
181 arg_exec_group = optarg;
182 break;
183
184 case ARG_NICE:
185 r = safe_atoi(optarg, &arg_nice);
186 if (r < 0) {
187 log_error("Failed to parse nice value");
188 return -EINVAL;
189 }
190
191 arg_nice_set = true;
192 break;
193
194 case ARG_SETENV:
195
196 if (strv_extend(&arg_environment, optarg) < 0)
197 return log_oom();
198
199 break;
200
201 case '?':
202 return -EINVAL;
203
204 default:
205 assert_not_reached("Unhandled option");
206 }
207 }
208
209 if (optind >= argc) {
210 log_error("Command line to execute required.");
211 return -EINVAL;
212 }
213
214 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
215 log_error("Execution in user context is not supported on non-local systems.");
216 return -EINVAL;
217 }
218
219 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
220 log_error("Scope execution is not supported on non-local systems.");
221 return -EINVAL;
222 }
223
224 if (arg_scope && (arg_remain_after_exit || arg_service_type || arg_exec_user || arg_exec_group || arg_nice_set || arg_environment)) {
225 log_error("--remain-after-exit, --service-type=, --user=, --group=, --nice= and --setenv= are not supported in --scope mode.");
226 return -EINVAL;
227 }
228
229 return 1;
230 }
231
232 static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
233 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
234 int r;
235
236 assert(bus);
237 assert(name);
238 assert(ret);
239
240 log_info("Running as unit %s.", name);
241
242 r = sd_bus_message_new_method_call(
243 bus,
244 &m,
245 "org.freedesktop.systemd1",
246 "/org/freedesktop/systemd1",
247 "org.freedesktop.systemd1.Manager",
248 "StartTransientUnit");
249 if (r < 0)
250 return r;
251
252 r = sd_bus_message_append(m, "ss", name, "fail");
253 if (r < 0)
254 return r;
255
256 r = sd_bus_message_open_container(m, 'a', "(sv)");
257 if (r < 0)
258 return r;
259
260 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
261 if (r < 0)
262 return r;
263
264 if (!isempty(arg_slice)) {
265 _cleanup_free_ char *slice;
266
267 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
268 if (!slice)
269 return -ENOMEM;
270
271 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
272 if (r < 0)
273 return r;
274 }
275
276 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
277 if (r < 0)
278 return r;
279
280 *ret = m;
281 m = NULL;
282
283 return 0;
284 }
285
286 static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
287 int r;
288
289 assert(bus);
290 assert(m);
291
292 r = sd_bus_message_close_container(m);
293 if (r < 0)
294 return r;
295
296 r = sd_bus_message_append(m, "a(sa(sv))", 0);
297 if (r < 0)
298 return r;
299
300 return sd_bus_call(bus, m, 0, error, reply);
301 }
302
303 static int start_transient_service(
304 sd_bus *bus,
305 char **argv,
306 sd_bus_error *error) {
307
308 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
309 _cleanup_free_ char *name = NULL;
310 int r;
311
312 if (arg_unit)
313 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
314 else
315 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
316 if (!name)
317 return -ENOMEM;
318
319 r = message_start_transient_unit_new(bus, name, &m);
320 if (r < 0)
321 return r;
322
323 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
324 if (r < 0)
325 return r;
326
327 if (arg_service_type) {
328 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
329 if (r < 0)
330 return r;
331 }
332
333 if (arg_exec_user) {
334 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
335 if (r < 0)
336 return r;
337 }
338
339 if (arg_exec_group) {
340 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
341 if (r < 0)
342 return r;
343 }
344
345 if (arg_nice_set) {
346 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
347 if (r < 0)
348 return r;
349 }
350
351 if (!strv_isempty(arg_environment)) {
352 r = sd_bus_message_open_container(m, 'r', "sv");
353 if (r < 0)
354 return r;
355
356 r = sd_bus_message_append(m, "s", "Environment");
357 if (r < 0)
358 return r;
359
360 r = sd_bus_message_open_container(m, 'v', "as");
361 if (r < 0)
362 return r;
363
364 r = sd_bus_message_append_strv(m, arg_environment);
365 if (r < 0)
366 return r;
367
368 r = sd_bus_message_close_container(m);
369 if (r < 0)
370 return r;
371
372 r = sd_bus_message_close_container(m);
373 if (r < 0)
374 return r;
375 }
376
377 r = sd_bus_message_open_container(m, 'r', "sv");
378 if (r < 0)
379 return r;
380
381 r = sd_bus_message_append(m, "s", "ExecStart");
382 if (r < 0)
383 return r;
384
385 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
386 if (r < 0)
387 return r;
388
389 r = sd_bus_message_open_container(m, 'a', "(sasb)");
390 if (r < 0)
391 return r;
392
393 r = sd_bus_message_open_container(m, 'r', "sasb");
394 if (r < 0)
395 return r;
396
397 r = sd_bus_message_append(m, "s", argv[0]);
398 if (r < 0)
399 return r;
400
401 r = sd_bus_message_append_strv(m, argv);
402 if (r < 0)
403 return r;
404
405 r = sd_bus_message_append(m, "b", false);
406 if (r < 0)
407 return r;
408
409 r = sd_bus_message_close_container(m);
410 if (r < 0)
411 return r;
412
413 r = sd_bus_message_close_container(m);
414 if (r < 0)
415 return r;
416
417 r = sd_bus_message_close_container(m);
418 if (r < 0)
419 return r;
420
421 r = sd_bus_message_close_container(m);
422 if (r < 0)
423 return r;
424
425 return message_start_transient_unit_send(bus, m, error, NULL);
426 }
427
428 static int start_transient_scope(
429 sd_bus *bus,
430 char **argv,
431 sd_bus_error *error) {
432
433 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
434 _cleanup_free_ char *name = NULL;
435 int r;
436
437 assert(bus);
438
439 if (arg_unit)
440 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
441 else
442 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
443 if (!name)
444 return -ENOMEM;
445
446 r = message_start_transient_unit_new(bus, name, &m);
447 if (r < 0)
448 return r;
449
450 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
451 if (r < 0)
452 return r;
453
454 r = message_start_transient_unit_send(bus, m, error, NULL);
455 if (r < 0)
456 return r;
457
458 execvp(argv[0], argv);
459 log_error("Failed to execute: %m");
460 return -errno;
461 }
462
463 int main(int argc, char* argv[]) {
464 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
465 _cleanup_bus_unref_ sd_bus *bus = NULL;
466 _cleanup_free_ char *description = NULL, *command = NULL;
467 int r;
468
469 log_parse_environment();
470 log_open();
471
472 r = parse_argv(argc, argv);
473 if (r <= 0)
474 goto finish;
475
476 r = find_binary(argv[optind], &command);
477 if (r < 0) {
478 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
479 goto finish;
480 }
481 argv[optind] = command;
482
483 if (!arg_description) {
484 description = strv_join(argv + optind, " ");
485 if (!description) {
486 r = log_oom();
487 goto finish;
488 }
489
490 arg_description = description;
491 }
492
493 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
494 if (r < 0) {
495 log_error("Failed to create bus connection: %s", strerror(-r));
496 goto finish;
497 }
498
499 if (arg_scope)
500 r = start_transient_scope(bus, argv + optind, &error);
501 else
502 r = start_transient_service(bus, argv + optind, &error);
503 if (r < 0)
504 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
505
506 finish:
507 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
508 }