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