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