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