]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/run/run.c
systemd-run: make sure --nice=, --uid=, --gid=, --setenv= also work in --scope mode
[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
6c12b52e
LP
252 log_info("Running as unit %s.", name);
253
c2756a68
LP
254 r = sd_bus_message_new_method_call(
255 bus,
151b9b96 256 &m,
c2756a68
LP
257 "org.freedesktop.systemd1",
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
151b9b96 260 "StartTransientUnit");
c2756a68
LP
261 if (r < 0)
262 return r;
263
264 r = sd_bus_message_append(m, "ss", name, "fail");
265 if (r < 0)
266 return r;
267
268 r = sd_bus_message_open_container(m, 'a', "(sv)");
269 if (r < 0)
270 return r;
271
df31a6c0
LP
272 STRV_FOREACH(i, arg_property) {
273 r = sd_bus_message_open_container(m, 'r', "sv");
274 if (r < 0)
275 return r;
276
277 r = bus_append_unit_property_assignment(m, *i);
278 if (r < 0)
279 return r;
280
281 r = sd_bus_message_close_container(m);
282 if (r < 0)
283 return r;
284 }
285
9f2e86af 286 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
c2756a68
LP
287 if (r < 0)
288 return r;
289
c221420b
LP
290 if (!isempty(arg_slice)) {
291 _cleanup_free_ char *slice;
292
f78e6385 293 slice = unit_name_mangle_with_suffix(arg_slice, MANGLE_NOGLOB, ".slice");
c221420b
LP
294 if (!slice)
295 return -ENOMEM;
296
297 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
298 if (r < 0)
299 return r;
300 }
301
df31a6c0
LP
302 if (arg_send_sighup) {
303 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
304 if (r < 0)
305 return r;
306 }
a6c0353b 307
6c12b52e
LP
308 *ret = m;
309 m = NULL;
310
311 return 0;
312}
313
314static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
315 int r;
316
8159d91a
LP
317 assert(bus);
318 assert(m);
319
6c12b52e
LP
320 r = sd_bus_message_close_container(m);
321 if (r < 0)
322 return r;
323
86b8d289
LP
324 r = sd_bus_message_append(m, "a(sa(sv))", 0);
325 if (r < 0)
326 return r;
327
c49b30a2 328 return sd_bus_call(bus, m, 0, error, reply);
6c12b52e
LP
329}
330
331static int start_transient_service(
332 sd_bus *bus,
333 char **argv,
334 sd_bus_error *error) {
335
8159d91a 336 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
6c12b52e 337 _cleanup_free_ char *name = NULL;
6c12b52e
LP
338 int r;
339
340 if (arg_unit)
f78e6385 341 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
6c12b52e
LP
342 else
343 asprintf(&name, "run-%lu.service", (unsigned long) getpid());
344 if (!name)
345 return -ENOMEM;
346
347 r = message_start_transient_unit_new(bus, name, &m);
348 if (r < 0)
349 return r;
350
df31a6c0
LP
351 if (arg_remain_after_exit) {
352 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
353 if (r < 0)
354 return r;
355 }
6577c7ce 356
c7040b5d
LP
357 if (arg_service_type) {
358 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
359 if (r < 0)
360 return r;
361 }
362
363 if (arg_exec_user) {
364 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
365 if (r < 0)
366 return r;
367 }
368
369 if (arg_exec_group) {
370 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
371 if (r < 0)
372 return r;
373 }
374
375 if (arg_nice_set) {
376 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
377 if (r < 0)
378 return r;
379 }
380
381 if (!strv_isempty(arg_environment)) {
382 r = sd_bus_message_open_container(m, 'r', "sv");
383 if (r < 0)
384 return r;
385
386 r = sd_bus_message_append(m, "s", "Environment");
387 if (r < 0)
388 return r;
389
390 r = sd_bus_message_open_container(m, 'v', "as");
391 if (r < 0)
392 return r;
393
394 r = sd_bus_message_append_strv(m, arg_environment);
395 if (r < 0)
396 return r;
397
398 r = sd_bus_message_close_container(m);
399 if (r < 0)
400 return r;
401
402 r = sd_bus_message_close_container(m);
403 if (r < 0)
404 return r;
405 }
406
9f2e86af
LP
407 r = sd_bus_message_open_container(m, 'r', "sv");
408 if (r < 0)
409 return r;
410
c2756a68
LP
411 r = sd_bus_message_append(m, "s", "ExecStart");
412 if (r < 0)
413 return r;
414
415 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
416 if (r < 0)
417 return r;
418
419 r = sd_bus_message_open_container(m, 'a', "(sasb)");
420 if (r < 0)
421 return r;
422
423 r = sd_bus_message_open_container(m, 'r', "sasb");
424 if (r < 0)
425 return r;
426
427 r = sd_bus_message_append(m, "s", argv[0]);
428 if (r < 0)
429 return r;
430
c7040b5d 431 r = sd_bus_message_append_strv(m, argv);
c2756a68
LP
432 if (r < 0)
433 return r;
434
435 r = sd_bus_message_append(m, "b", false);
436 if (r < 0)
437 return r;
438
439 r = sd_bus_message_close_container(m);
440 if (r < 0)
441 return r;
442
443 r = sd_bus_message_close_container(m);
444 if (r < 0)
445 return r;
446
447 r = sd_bus_message_close_container(m);
448 if (r < 0)
449 return r;
450
9f2e86af
LP
451 r = sd_bus_message_close_container(m);
452 if (r < 0)
453 return r;
454
8159d91a 455 return message_start_transient_unit_send(bus, m, error, NULL);
6c12b52e
LP
456}
457
458static int start_transient_scope(
459 sd_bus *bus,
460 char **argv,
461 sd_bus_error *error) {
462
8159d91a 463 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
6c12b52e 464 _cleanup_free_ char *name = NULL;
4de33e7f 465 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
6c12b52e
LP
466 int r;
467
8159d91a
LP
468 assert(bus);
469
6c12b52e 470 if (arg_unit)
f78e6385 471 name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
6c12b52e
LP
472 else
473 asprintf(&name, "run-%lu.scope", (unsigned long) getpid());
474 if (!name)
475 return -ENOMEM;
476
477 r = message_start_transient_unit_new(bus, name, &m);
c2756a68
LP
478 if (r < 0)
479 return r;
480
9f2e86af 481 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
6c12b52e
LP
482 if (r < 0)
483 return r;
484
8159d91a 485 r = message_start_transient_unit_send(bus, m, error, NULL);
c2756a68
LP
486 if (r < 0)
487 return r;
488
4de33e7f
LP
489 if (arg_nice_set) {
490 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) {
491 log_error("Failed to set nice level: %m");
492 return -errno;
493 }
494 }
495
496 if (arg_exec_group) {
497 gid_t gid;
498
499 r = get_group_creds(&arg_exec_group, &gid);
500 if (r < 0) {
501 log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r));
502 return r;
503 }
504
505 if (setresgid(gid, gid, gid) < 0) {
506 log_error("Failed to change GID to " GID_FMT ": %m", gid);
507 return -errno;
508 }
509 }
510
511 if (arg_exec_user) {
512 const char *home, *shell;
513 uid_t uid;
514 gid_t gid;
515
516 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
517 if (r < 0) {
518 log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r));
519 return r;
520 }
521
522 r = strv_extendf(&user_env, "HOME=%s", home);
523 if (r < 0)
524 return log_oom();
525
526 r = strv_extendf(&user_env, "SHELL=%s", shell);
527 if (r < 0)
528 return log_oom();
529
530 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
531 if (r < 0)
532 return log_oom();
533
534 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
535 if (r < 0)
536 return log_oom();
537
538 if (!arg_exec_group) {
539 if (setresgid(gid, gid, gid) < 0) {
540 log_error("Failed to change GID to " GID_FMT ": %m", gid);
541 return -errno;
542 }
543 }
544
545 if (setresuid(uid, uid, uid) < 0) {
546 log_error("Failed to change UID to " UID_FMT ": %m", uid);
547 return -errno;
548 }
549 }
550
551 env = strv_env_merge(3, environ, user_env, arg_environment);
552 if (!env)
553 return log_oom();
554
555 execvpe(argv[0], argv, env);
6c12b52e
LP
556 log_error("Failed to execute: %m");
557 return -errno;
c2756a68
LP
558}
559
560int main(int argc, char* argv[]) {
8159d91a 561 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
c2756a68 562 _cleanup_bus_unref_ sd_bus *bus = NULL;
c9d954b2 563 _cleanup_free_ char *description = NULL, *command = NULL;
c2756a68
LP
564 int r;
565
566 log_parse_environment();
567 log_open();
568
6c12b52e
LP
569 r = parse_argv(argc, argv);
570 if (r <= 0)
66b1e746 571 goto finish;
c2756a68 572
c9d954b2
ZJS
573 r = find_binary(argv[optind], &command);
574 if (r < 0) {
575 log_error("Failed to find executable %s: %s", argv[optind], strerror(-r));
66b1e746 576 goto finish;
c9d954b2
ZJS
577 }
578 argv[optind] = command;
579
9f2e86af
LP
580 if (!arg_description) {
581 description = strv_join(argv + optind, " ");
582 if (!description) {
583 r = log_oom();
66b1e746 584 goto finish;
9f2e86af
LP
585 }
586
587 arg_description = description;
588 }
589
d21ed1ea 590 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
c2756a68 591 if (r < 0) {
d21ed1ea 592 log_error("Failed to create bus connection: %s", strerror(-r));
66b1e746 593 goto finish;
c2756a68
LP
594 }
595
6c12b52e
LP
596 if (arg_scope)
597 r = start_transient_scope(bus, argv + optind, &error);
598 else
599 r = start_transient_service(bus, argv + optind, &error);
8159d91a
LP
600 if (r < 0)
601 log_error("Failed start transient unit: %s", bus_error_message(&error, r));
c2756a68 602
66b1e746 603finish:
df31a6c0
LP
604 strv_free(arg_environment);
605 strv_free(arg_property);
606
c2756a68
LP
607 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
608}