]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/run/run.c
util-lib: split out user/group/uid/gid calls into user-util.[ch]
[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
6c12b52e 22#include <getopt.h>
3f6fd1ba 23#include <stdio.h>
c2756a68
LP
24
25#include "sd-bus.h"
9b15b784 26#include "sd-event.h"
3f6fd1ba
LP
27
28#include "bus-error.h"
40ca29a1 29#include "bus-util.h"
3f6fd1ba 30#include "calendarspec.h"
4de33e7f 31#include "env-util.h"
3f6fd1ba 32#include "event-util.h"
3ffd4af2 33#include "fd-util.h"
3f6fd1ba 34#include "formats-util.h"
4bcc8c3c 35#include "path-util.h"
9b15b784 36#include "ptyfwd.h"
24882e06 37#include "signal-util.h"
8c7db2fb 38#include "spawn-polkit-agent.h"
3f6fd1ba 39#include "strv.h"
66cb2fde 40#include "terminal-util.h"
3f6fd1ba 41#include "unit-name.h"
b1d4f8e1 42#include "user-util.h"
c2756a68 43
8c7db2fb 44static bool arg_ask_password = true;
6c12b52e 45static bool arg_scope = false;
6577c7ce 46static bool arg_remain_after_exit = false;
3d161f99 47static bool arg_no_block = false;
6c12b52e 48static const char *arg_unit = NULL;
9f2e86af 49static const char *arg_description = NULL;
c221420b 50static const char *arg_slice = NULL;
a6c0353b 51static bool arg_send_sighup = false;
d21ed1ea 52static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
1ac67edb 53static const char *arg_host = NULL;
d21ed1ea 54static bool arg_user = false;
c7040b5d
LP
55static const char *arg_service_type = NULL;
56static const char *arg_exec_user = NULL;
57static const char *arg_exec_group = NULL;
58static int arg_nice = 0;
59static bool arg_nice_set = false;
60static char **arg_environment = NULL;
df31a6c0 61static char **arg_property = NULL;
9b15b784 62static bool arg_pty = false;
4c213d6c
WC
63static usec_t arg_on_active = 0;
64static usec_t arg_on_boot = 0;
65static usec_t arg_on_startup = 0;
66static usec_t arg_on_unit_active = 0;
67static usec_t arg_on_unit_inactive = 0;
024a8ec1 68static const char *arg_on_calendar = NULL;
4c213d6c 69static char **arg_timer_property = NULL;
095dc596 70static bool arg_quiet = false;
c2756a68 71
8c7db2fb
EV
72static void polkit_agent_open_if_enabled(void) {
73
74 /* Open the polkit agent as a child process if necessary */
75 if (!arg_ask_password)
76 return;
77
78 if (arg_transport != BUS_TRANSPORT_LOCAL)
79 return;
80
81 polkit_agent_open();
82}
83
601185b4 84static void help(void) {
4c213d6c
WC
85 printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
86 "Run the specified command in a transient scope or service or timer\n"
87 "unit. If timer option is specified and unit is exist which is\n"
cd32b977 88 "specified with --unit option then command can be omitted.\n\n"
4c213d6c
WC
89 " -h --help Show this help\n"
90 " --version Show package version\n"
8c7db2fb 91 " --no-ask-password Do not prompt for password\n"
4c213d6c
WC
92 " --user Run as user unit\n"
93 " -H --host=[USER@]HOST Operate on remote host\n"
94 " -M --machine=CONTAINER Operate on local container\n"
95 " --scope Run this as scope rather than service\n"
96 " --unit=UNIT Run under the specified unit name\n"
97 " -p --property=NAME=VALUE Set unit property\n"
98 " --description=TEXT Description for unit\n"
99 " --slice=SLICE Run in the specified slice\n"
3d161f99 100 " --no-block Do not wait until operation finished\n"
4c213d6c
WC
101 " -r --remain-after-exit Leave service around until explicitly stopped\n"
102 " --send-sighup Send SIGHUP when terminating\n"
103 " --service-type=TYPE Service type\n"
104 " --uid=USER Run as system user\n"
105 " --gid=GROUP Run as system group\n"
106 " --nice=NICE Nice level\n"
9b15b784 107 " --setenv=NAME=VALUE Set environment\n"
095dc596
LP
108 " -t --pty Run service on pseudo tty\n"
109 " -q --quiet Suppress information messages during runtime\n\n"
4c213d6c 110 "Timer options:\n\n"
b57b0625
ZJS
111 " --on-active=SECONDS Run after SECONDS delay\n"
112 " --on-boot=SECONDS Run SECONDS after machine was booted up\n"
113 " --on-startup=SECONDS Run SECONDS after systemd activation\n"
114 " --on-unit-active=SECONDS Run SECONDS after the last activation\n"
115 " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n"
4c213d6c
WC
116 " --on-calendar=SPEC Realtime timer\n"
117 " --timer-property=NAME=VALUE Set timer unit property\n",
6c12b52e 118 program_invocation_short_name);
6c12b52e
LP
119}
120
4c213d6c
WC
121static bool with_timer(void) {
122 return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
123}
124
6c12b52e
LP
125static int parse_argv(int argc, char *argv[]) {
126
127 enum {
128 ARG_VERSION = 0x100,
8c7db2fb 129 ARG_NO_ASK_PASSWORD,
6c12b52e 130 ARG_USER,
66b1e746 131 ARG_SYSTEM,
6c12b52e 132 ARG_SCOPE,
9f2e86af 133 ARG_UNIT,
c221420b 134 ARG_DESCRIPTION,
a6c0353b
LP
135 ARG_SLICE,
136 ARG_SEND_SIGHUP,
c7040b5d
LP
137 ARG_EXEC_USER,
138 ARG_EXEC_GROUP,
139 ARG_SERVICE_TYPE,
140 ARG_NICE,
4c213d6c 141 ARG_SETENV,
9b15b784 142 ARG_TTY,
4c213d6c
WC
143 ARG_ON_ACTIVE,
144 ARG_ON_BOOT,
145 ARG_ON_STARTUP,
146 ARG_ON_UNIT_ACTIVE,
147 ARG_ON_UNIT_INACTIVE,
148 ARG_ON_CALENDAR,
3d161f99
LP
149 ARG_TIMER_PROPERTY,
150 ARG_NO_BLOCK,
6c12b52e
LP
151 };
152
153 static const struct option options[] = {
4c213d6c
WC
154 { "help", no_argument, NULL, 'h' },
155 { "version", no_argument, NULL, ARG_VERSION },
156 { "user", no_argument, NULL, ARG_USER },
157 { "system", no_argument, NULL, ARG_SYSTEM },
158 { "scope", no_argument, NULL, ARG_SCOPE },
159 { "unit", required_argument, NULL, ARG_UNIT },
160 { "description", required_argument, NULL, ARG_DESCRIPTION },
161 { "slice", required_argument, NULL, ARG_SLICE },
162 { "remain-after-exit", no_argument, NULL, 'r' },
163 { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP },
164 { "host", required_argument, NULL, 'H' },
165 { "machine", required_argument, NULL, 'M' },
166 { "service-type", required_argument, NULL, ARG_SERVICE_TYPE },
167 { "uid", required_argument, NULL, ARG_EXEC_USER },
168 { "gid", required_argument, NULL, ARG_EXEC_GROUP },
169 { "nice", required_argument, NULL, ARG_NICE },
170 { "setenv", required_argument, NULL, ARG_SETENV },
171 { "property", required_argument, NULL, 'p' },
9b15b784 172 { "tty", no_argument, NULL, 't' },
095dc596 173 { "quiet", no_argument, NULL, 'q' },
4c213d6c
WC
174 { "on-active", required_argument, NULL, ARG_ON_ACTIVE },
175 { "on-boot", required_argument, NULL, ARG_ON_BOOT },
176 { "on-startup", required_argument, NULL, ARG_ON_STARTUP },
177 { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE },
178 { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE },
179 { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR },
180 { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY },
3d161f99 181 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
8c7db2fb 182 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
eb9da376 183 {},
6c12b52e
LP
184 };
185
c7040b5d 186 int r, c;
6c12b52e
LP
187
188 assert(argc >= 0);
189 assert(argv);
190
095dc596 191 while ((c = getopt_long(argc, argv, "+hrH:M:p:tq", options, NULL)) >= 0)
6c12b52e
LP
192
193 switch (c) {
194
195 case 'h':
601185b4
ZJS
196 help();
197 return 0;
6c12b52e 198
8c7db2fb
EV
199 case ARG_NO_ASK_PASSWORD:
200 arg_ask_password = false;
201 break;
202
6c12b52e 203 case ARG_VERSION:
3f6fd1ba 204 return version();
6c12b52e
LP
205
206 case ARG_USER:
207 arg_user = true;
208 break;
209
66b1e746
LP
210 case ARG_SYSTEM:
211 arg_user = false;
212 break;
213
6c12b52e
LP
214 case ARG_SCOPE:
215 arg_scope = true;
216 break;
217
218 case ARG_UNIT:
219 arg_unit = optarg;
220 break;
221
9f2e86af
LP
222 case ARG_DESCRIPTION:
223 arg_description = optarg;
224 break;
225
c221420b
LP
226 case ARG_SLICE:
227 arg_slice = optarg;
228 break;
229
a6c0353b
LP
230 case ARG_SEND_SIGHUP:
231 arg_send_sighup = true;
232 break;
233
6577c7ce
LP
234 case 'r':
235 arg_remain_after_exit = true;
236 break;
237
d21ed1ea
LP
238 case 'H':
239 arg_transport = BUS_TRANSPORT_REMOTE;
240 arg_host = optarg;
241 break;
242
243 case 'M':
de33fc62 244 arg_transport = BUS_TRANSPORT_MACHINE;
d21ed1ea
LP
245 arg_host = optarg;
246 break;
247
c7040b5d
LP
248 case ARG_SERVICE_TYPE:
249 arg_service_type = optarg;
250 break;
251
252 case ARG_EXEC_USER:
253 arg_exec_user = optarg;
254 break;
255
256 case ARG_EXEC_GROUP:
257 arg_exec_group = optarg;
258 break;
259
260 case ARG_NICE:
261 r = safe_atoi(optarg, &arg_nice);
1ac67edb 262 if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
c7040b5d
LP
263 log_error("Failed to parse nice value");
264 return -EINVAL;
265 }
266
267 arg_nice_set = true;
268 break;
269
270 case ARG_SETENV:
c7040b5d
LP
271 if (strv_extend(&arg_environment, optarg) < 0)
272 return log_oom();
273
274 break;
275
df31a6c0 276 case 'p':
df31a6c0
LP
277 if (strv_extend(&arg_property, optarg) < 0)
278 return log_oom();
279
280 break;
281
9b15b784
LP
282 case 't':
283 arg_pty = true;
284 break;
285
095dc596
LP
286 case 'q':
287 arg_quiet = true;
288 break;
289
4c213d6c
WC
290 case ARG_ON_ACTIVE:
291
292 r = parse_sec(optarg, &arg_on_active);
293 if (r < 0) {
294 log_error("Failed to parse timer value: %s", optarg);
295 return r;
296 }
297
298 break;
299
300 case ARG_ON_BOOT:
301
302 r = parse_sec(optarg, &arg_on_boot);
303 if (r < 0) {
304 log_error("Failed to parse timer value: %s", optarg);
305 return r;
306 }
307
308 break;
309
310 case ARG_ON_STARTUP:
311
312 r = parse_sec(optarg, &arg_on_startup);
313 if (r < 0) {
314 log_error("Failed to parse timer value: %s", optarg);
315 return r;
316 }
317
318 break;
319
320 case ARG_ON_UNIT_ACTIVE:
321
322 r = parse_sec(optarg, &arg_on_unit_active);
323 if (r < 0) {
324 log_error("Failed to parse timer value: %s", optarg);
325 return r;
326 }
327
328 break;
329
330 case ARG_ON_UNIT_INACTIVE:
331
332 r = parse_sec(optarg, &arg_on_unit_inactive);
333 if (r < 0) {
334 log_error("Failed to parse timer value: %s", optarg);
335 return r;
336 }
337
338 break;
339
024a8ec1
LP
340 case ARG_ON_CALENDAR: {
341 CalendarSpec *spec = NULL;
4c213d6c
WC
342
343 r = calendar_spec_from_string(optarg, &spec);
344 if (r < 0) {
345 log_error("Invalid calendar spec: %s", optarg);
346 return r;
347 }
024a8ec1
LP
348
349 calendar_spec_free(spec);
4c213d6c
WC
350 arg_on_calendar = optarg;
351 break;
024a8ec1 352 }
4c213d6c
WC
353
354 case ARG_TIMER_PROPERTY:
355
356 if (strv_extend(&arg_timer_property, optarg) < 0)
357 return log_oom();
358
359 break;
360
3d161f99
LP
361 case ARG_NO_BLOCK:
362 arg_no_block = true;
363 break;
364
6c12b52e
LP
365 case '?':
366 return -EINVAL;
367
368 default:
eb9da376 369 assert_not_reached("Unhandled option");
6c12b52e 370 }
6c12b52e 371
4c213d6c 372 if ((optind >= argc) && (!arg_unit || !with_timer())) {
6c12b52e
LP
373 log_error("Command line to execute required.");
374 return -EINVAL;
375 }
376
d21ed1ea
LP
377 if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) {
378 log_error("Execution in user context is not supported on non-local systems.");
379 return -EINVAL;
380 }
381
382 if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) {
383 log_error("Scope execution is not supported on non-local systems.");
384 return -EINVAL;
385 }
386
4de33e7f
LP
387 if (arg_scope && (arg_remain_after_exit || arg_service_type)) {
388 log_error("--remain-after-exit and --service-type= are not supported in --scope mode.");
c7040b5d
LP
389 return -EINVAL;
390 }
391
9b15b784
LP
392 if (arg_pty && (with_timer() || arg_scope)) {
393 log_error("--pty is not compatible in timer or --scope mode.");
394 return -EINVAL;
395 }
396
024a8ec1
LP
397 if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) {
398 log_error("--pty is only supported when connecting to the local system or containers.");
399 return -EINVAL;
400 }
401
4c213d6c
WC
402 if (arg_scope && with_timer()) {
403 log_error("Timer options are not supported in --scope mode.");
404 return -EINVAL;
405 }
406
407 if (arg_timer_property && !with_timer()) {
408 log_error("--timer-property= has no effect without any other timer options.");
409 return -EINVAL;
410 }
411
6c12b52e
LP
412 return 1;
413}
414
9b15b784 415static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
df31a6c0 416 char **i;
c2756a68
LP
417 int r;
418
9b15b784
LP
419 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
420 if (r < 0)
421 return r;
422
423 STRV_FOREACH(i, properties) {
df31a6c0
LP
424 r = sd_bus_message_open_container(m, 'r', "sv");
425 if (r < 0)
426 return r;
427
428 r = bus_append_unit_property_assignment(m, *i);
8962620e
LP
429 if (r < 0)
430 return r;
df31a6c0
LP
431
432 r = sd_bus_message_close_container(m);
433 if (r < 0)
434 return r;
435 }
436
9b15b784
LP
437 return 0;
438}
439
440static int transient_cgroup_set_properties(sd_bus_message *m) {
441 int r;
442 assert(m);
c2756a68 443
c221420b
LP
444 if (!isempty(arg_slice)) {
445 _cleanup_free_ char *slice;
446
7410616c
LP
447 r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice);
448 if (r < 0)
449 return r;
c221420b
LP
450
451 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
452 if (r < 0)
453 return r;
454 }
455
9b15b784
LP
456 return 0;
457}
458
459static int transient_kill_set_properties(sd_bus_message *m) {
9b15b784
LP
460 assert(m);
461
aa1aad74
ZJS
462 if (arg_send_sighup)
463 return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
464 else
465 return 0;
6c12b52e
LP
466}
467
9b15b784 468static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
6c12b52e
LP
469 int r;
470
8159d91a
LP
471 assert(m);
472
9b15b784
LP
473 r = transient_unit_set_properties(m, arg_property);
474 if (r < 0)
475 return r;
476
477 r = transient_kill_set_properties(m);
478 if (r < 0)
479 return r;
480
481 r = transient_cgroup_set_properties(m);
86b8d289
LP
482 if (r < 0)
483 return r;
484
df31a6c0
LP
485 if (arg_remain_after_exit) {
486 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
487 if (r < 0)
4c213d6c 488 return r;
df31a6c0 489 }
6577c7ce 490
c7040b5d
LP
491 if (arg_service_type) {
492 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
493 if (r < 0)
4c213d6c 494 return r;
c7040b5d
LP
495 }
496
497 if (arg_exec_user) {
498 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
499 if (r < 0)
4c213d6c 500 return r;
c7040b5d
LP
501 }
502
503 if (arg_exec_group) {
504 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
505 if (r < 0)
4c213d6c 506 return r;
c7040b5d
LP
507 }
508
509 if (arg_nice_set) {
510 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
511 if (r < 0)
4c213d6c 512 return r;
c7040b5d
LP
513 }
514
9b15b784
LP
515 if (pty_path) {
516 const char *e;
517
518 r = sd_bus_message_append(m,
519 "(sv)(sv)(sv)(sv)",
520 "StandardInput", "s", "tty",
521 "StandardOutput", "s", "tty",
522 "StandardError", "s", "tty",
523 "TTYPath", "s", pty_path);
524 if (r < 0)
525 return r;
526
527 e = getenv("TERM");
528 if (e) {
529 char *n;
530
63c372cb 531 n = strjoina("TERM=", e);
9b15b784
LP
532 r = sd_bus_message_append(m,
533 "(sv)",
534 "Environment", "as", 1, n);
535 if (r < 0)
536 return r;
537 }
538 }
539
c7040b5d
LP
540 if (!strv_isempty(arg_environment)) {
541 r = sd_bus_message_open_container(m, 'r', "sv");
542 if (r < 0)
4c213d6c 543 return r;
c7040b5d
LP
544
545 r = sd_bus_message_append(m, "s", "Environment");
546 if (r < 0)
4c213d6c 547 return r;
c7040b5d
LP
548
549 r = sd_bus_message_open_container(m, 'v', "as");
550 if (r < 0)
4c213d6c 551 return r;
c7040b5d
LP
552
553 r = sd_bus_message_append_strv(m, arg_environment);
554 if (r < 0)
4c213d6c 555 return r;
c7040b5d
LP
556
557 r = sd_bus_message_close_container(m);
558 if (r < 0)
4c213d6c 559 return r;
c7040b5d
LP
560
561 r = sd_bus_message_close_container(m);
562 if (r < 0)
4c213d6c
WC
563 return r;
564 }
565
566 /* Exec container */
567 {
568 r = sd_bus_message_open_container(m, 'r', "sv");
569 if (r < 0)
570 return r;
571
572 r = sd_bus_message_append(m, "s", "ExecStart");
573 if (r < 0)
574 return r;
575
576 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
577 if (r < 0)
578 return r;
579
580 r = sd_bus_message_open_container(m, 'a', "(sasb)");
581 if (r < 0)
582 return r;
583
584 r = sd_bus_message_open_container(m, 'r', "sasb");
585 if (r < 0)
586 return r;
587
588 r = sd_bus_message_append(m, "s", argv[0]);
589 if (r < 0)
590 return r;
591
592 r = sd_bus_message_append_strv(m, argv);
593 if (r < 0)
594 return r;
595
596 r = sd_bus_message_append(m, "b", false);
597 if (r < 0)
598 return r;
599
600 r = sd_bus_message_close_container(m);
601 if (r < 0)
602 return r;
603
604 r = sd_bus_message_close_container(m);
605 if (r < 0)
606 return r;
607
608 r = sd_bus_message_close_container(m);
609 if (r < 0)
610 return r;
611
612 r = sd_bus_message_close_container(m);
613 if (r < 0)
614 return r;
615 }
616
617 return 0;
618}
619
9b15b784
LP
620static int transient_scope_set_properties(sd_bus_message *m) {
621 int r;
622
623 assert(m);
624
625 r = transient_unit_set_properties(m, arg_property);
626 if (r < 0)
627 return r;
628
629 r = transient_kill_set_properties(m);
630 if (r < 0)
631 return r;
632
633 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
634 if (r < 0)
635 return r;
636
637 return 0;
638}
639
4c213d6c
WC
640static int transient_timer_set_properties(sd_bus_message *m) {
641 int r;
642
643 assert(m);
644
9b15b784 645 r = transient_unit_set_properties(m, arg_timer_property);
4c213d6c
WC
646 if (r < 0)
647 return r;
648
649 if (arg_on_active) {
650 r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
651 if (r < 0)
652 return r;
c7040b5d
LP
653 }
654
4c213d6c
WC
655 if (arg_on_boot) {
656 r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
657 if (r < 0)
658 return r;
659 }
660
661 if (arg_on_startup) {
662 r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
663 if (r < 0)
664 return r;
665 }
666
667 if (arg_on_unit_active) {
668 r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
669 if (r < 0)
670 return r;
671 }
672
673 if (arg_on_unit_inactive) {
674 r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
675 if (r < 0)
676 return r;
677 }
678
679 if (arg_on_calendar) {
680 r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
681 if (r < 0)
682 return r;
683 }
684
685 return 0;
686}
687
4c213d6c
WC
688static int start_transient_service(
689 sd_bus *bus,
ee451d76 690 char **argv) {
4c213d6c 691
3d161f99 692 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
ee451d76 693 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
3d161f99 694 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
ee451d76 695 _cleanup_free_ char *service = NULL, *pty_path = NULL;
9b15b784 696 _cleanup_close_ int master = -1;
4c213d6c
WC
697 int r;
698
699 assert(bus);
700 assert(argv);
701
9b15b784 702 if (arg_pty) {
9b15b784 703
ee451d76
LP
704 if (arg_transport == BUS_TRANSPORT_LOCAL) {
705 master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
706 if (master < 0)
707 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
708
709 r = ptsname_malloc(master, &pty_path);
710 if (r < 0)
711 return log_error_errno(r, "Failed to determine tty name: %m");
712
395745ba
LP
713 if (unlockpt(master) < 0)
714 return log_error_errno(errno, "Failed to unlock tty: %m");
715
de33fc62 716 } else if (arg_transport == BUS_TRANSPORT_MACHINE) {
ee451d76 717 _cleanup_bus_unref_ sd_bus *system_bus = NULL;
ee451d76
LP
718 const char *s;
719
024a8ec1 720 r = sd_bus_default_system(&system_bus);
ee451d76 721 if (r < 0)
024a8ec1 722 return log_error_errno(r, "Failed to connect to system bus: %m");
ee451d76
LP
723
724 r = sd_bus_call_method(system_bus,
725 "org.freedesktop.machine1",
726 "/org/freedesktop/machine1",
727 "org.freedesktop.machine1.Manager",
728 "OpenMachinePTY",
729 &error,
730 &reply,
731 "s", arg_host);
732 if (r < 0) {
733 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
734 return r;
735 }
736
737 r = sd_bus_message_read(reply, "hs", &master, &s);
738 if (r < 0)
739 return bus_log_parse_error(r);
740
3d161f99
LP
741 reply = sd_bus_message_unref(reply);
742
ee451d76
LP
743 master = fcntl(master, F_DUPFD_CLOEXEC, 3);
744 if (master < 0)
745 return log_error_errno(errno, "Failed to duplicate master fd: %m");
746
747 pty_path = strdup(s);
748 if (!pty_path)
749 return log_oom();
750 } else
751 assert_not_reached("Can't allocate tty via ssh");
9b15b784
LP
752 }
753
3d161f99
LP
754 if (!arg_no_block) {
755 r = bus_wait_for_jobs_new(bus, &w);
756 if (r < 0)
757 return log_error_errno(r, "Could not watch jobs: %m");
758 }
759
4c213d6c 760 if (arg_unit) {
7410616c
LP
761 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
762 if (r < 0)
763 return log_error_errno(r, "Failed to mangle unit name: %m");
4c213d6c
WC
764 } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
765 return log_oom();
766
767 r = sd_bus_message_new_method_call(
768 bus,
769 &m,
770 "org.freedesktop.systemd1",
771 "/org/freedesktop/systemd1",
772 "org.freedesktop.systemd1.Manager",
773 "StartTransientUnit");
9f2e86af 774 if (r < 0)
7040b626 775 return bus_log_create_error(r);
9f2e86af 776
8c7db2fb
EV
777 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
778 if (r < 0)
779 return bus_log_create_error(r);
780
9b15b784 781 /* Name and mode */
4c213d6c 782 r = sd_bus_message_append(m, "ss", service, "fail");
c2756a68 783 if (r < 0)
7040b626 784 return bus_log_create_error(r);
c2756a68 785
9b15b784 786 /* Properties */
4c213d6c 787 r = sd_bus_message_open_container(m, 'a', "(sv)");
c2756a68 788 if (r < 0)
7040b626 789 return bus_log_create_error(r);
c2756a68 790
9b15b784 791 r = transient_service_set_properties(m, argv, pty_path);
c2756a68 792 if (r < 0)
7040b626 793 return bus_log_create_error(r);
c2756a68 794
4c213d6c 795 r = sd_bus_message_close_container(m);
c2756a68 796 if (r < 0)
7040b626 797 return bus_log_create_error(r);
c2756a68 798
9b15b784 799 /* Auxiliary units */
4c213d6c 800 r = sd_bus_message_append(m, "a(sa(sv))", 0);
c2756a68 801 if (r < 0)
7040b626 802 return bus_log_create_error(r);
c2756a68 803
8c7db2fb
EV
804 polkit_agent_open_if_enabled();
805
3d161f99 806 r = sd_bus_call(bus, m, 0, &error, &reply);
024a8ec1
LP
807 if (r < 0)
808 return log_error_errno(r, "Failed to start transient service unit: %s", bus_error_message(&error, r));
c2756a68 809
3d161f99
LP
810 if (w) {
811 const char *object;
812
813 r = sd_bus_message_read(reply, "o", &object);
814 if (r < 0)
815 return bus_log_parse_error(r);
816
817 r = bus_wait_for_jobs_one(w, object, arg_quiet);
818 if (r < 0)
819 return r;
820 }
821
9b15b784
LP
822 if (master >= 0) {
823 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
824 _cleanup_event_unref_ sd_event *event = NULL;
9b15b784 825 char last_char = 0;
4c213d6c 826
9b15b784 827 r = sd_event_default(&event);
4c213d6c 828 if (r < 0)
9b15b784 829 return log_error_errno(r, "Failed to get event loop: %m");
4c213d6c 830
72c0a2c2 831 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0);
4c213d6c 832
72c0a2c2
LP
833 (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
834 (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
4c213d6c 835
095dc596
LP
836 if (!arg_quiet)
837 log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service);
4c213d6c 838
ae3dde80 839 r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward);
4c213d6c 840 if (r < 0)
9b15b784 841 return log_error_errno(r, "Failed to create PTY forwarder: %m");
4c213d6c 842
9b15b784 843 r = sd_event_loop(event);
4c213d6c 844 if (r < 0)
9b15b784 845 return log_error_errno(r, "Failed to run event loop: %m");
4c213d6c 846
0ec5543c 847 pty_forward_get_last_char(forward, &last_char);
4c213d6c 848
9b15b784 849 forward = pty_forward_free(forward);
4c213d6c 850
095dc596 851 if (!arg_quiet && last_char != '\n')
9b15b784 852 fputc('\n', stdout);
9f2e86af 853
095dc596 854 } else if (!arg_quiet)
9b15b784 855 log_info("Running as unit %s.", service);
7040b626
LP
856
857 return 0;
6c12b52e
LP
858}
859
860static int start_transient_scope(
861 sd_bus *bus,
ee451d76 862 char **argv) {
6c12b52e 863
ee451d76 864 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de158ed2
LP
865 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
866 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
9b15b784 867 _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
4c213d6c 868 _cleanup_free_ char *scope = NULL;
de158ed2 869 const char *object = NULL;
6c12b52e
LP
870 int r;
871
8159d91a 872 assert(bus);
4c213d6c 873 assert(argv);
8159d91a 874
de158ed2
LP
875 r = bus_wait_for_jobs_new(bus, &w);
876 if (r < 0)
877 return log_oom();
878
7de80bfe 879 if (arg_unit) {
7410616c
LP
880 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope);
881 if (r < 0)
882 return log_error_errno(r, "Failed to mangle scope name: %m");
4c213d6c 883 } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
7040b626 884 return log_oom();
6c12b52e 885
4c213d6c 886 r = sd_bus_message_new_method_call(
9b15b784
LP
887 bus,
888 &m,
889 "org.freedesktop.systemd1",
890 "/org/freedesktop/systemd1",
891 "org.freedesktop.systemd1.Manager",
892 "StartTransientUnit");
c2756a68 893 if (r < 0)
7040b626 894 return bus_log_create_error(r);
c2756a68 895
8c7db2fb
EV
896 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
897 if (r < 0)
898 return bus_log_create_error(r);
899
9b15b784 900 /* Name and Mode */
4c213d6c
WC
901 r = sd_bus_message_append(m, "ss", scope, "fail");
902 if (r < 0)
903 return bus_log_create_error(r);
904
9b15b784 905 /* Properties */
4c213d6c
WC
906 r = sd_bus_message_open_container(m, 'a', "(sv)");
907 if (r < 0)
908 return bus_log_create_error(r);
909
910 r = transient_scope_set_properties(m);
911 if (r < 0)
912 return bus_log_create_error(r);
913
914 r = sd_bus_message_close_container(m);
915 if (r < 0)
916 return bus_log_create_error(r);
917
ee451d76 918 /* Auxiliary units */
4c213d6c 919 r = sd_bus_message_append(m, "a(sa(sv))", 0);
6c12b52e 920 if (r < 0)
7040b626 921 return bus_log_create_error(r);
6c12b52e 922
8c7db2fb
EV
923 polkit_agent_open_if_enabled();
924
de158ed2 925 r = sd_bus_call(bus, m, 0, &error, &reply);
ee451d76
LP
926 if (r < 0) {
927 log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r));
928 return r;
929 }
c2756a68 930
4de33e7f 931 if (arg_nice_set) {
4a62c710
MS
932 if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0)
933 return log_error_errno(errno, "Failed to set nice level: %m");
4de33e7f
LP
934 }
935
936 if (arg_exec_group) {
937 gid_t gid;
938
939 r = get_group_creds(&arg_exec_group, &gid);
f647962d
MS
940 if (r < 0)
941 return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group);
4de33e7f 942
4a62c710
MS
943 if (setresgid(gid, gid, gid) < 0)
944 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
4de33e7f
LP
945 }
946
947 if (arg_exec_user) {
948 const char *home, *shell;
949 uid_t uid;
950 gid_t gid;
951
952 r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell);
f647962d
MS
953 if (r < 0)
954 return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
4de33e7f
LP
955
956 r = strv_extendf(&user_env, "HOME=%s", home);
957 if (r < 0)
958 return log_oom();
959
960 r = strv_extendf(&user_env, "SHELL=%s", shell);
961 if (r < 0)
962 return log_oom();
963
964 r = strv_extendf(&user_env, "USER=%s", arg_exec_user);
965 if (r < 0)
966 return log_oom();
967
968 r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user);
969 if (r < 0)
970 return log_oom();
971
972 if (!arg_exec_group) {
4a62c710
MS
973 if (setresgid(gid, gid, gid) < 0)
974 return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
4de33e7f
LP
975 }
976
4a62c710
MS
977 if (setresuid(uid, uid, uid) < 0)
978 return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
4de33e7f
LP
979 }
980
981 env = strv_env_merge(3, environ, user_env, arg_environment);
982 if (!env)
983 return log_oom();
984
de158ed2
LP
985 r = sd_bus_message_read(reply, "o", &object);
986 if (r < 0)
987 return bus_log_parse_error(r);
988
989 r = bus_wait_for_jobs_one(w, object, arg_quiet);
990 if (r < 0)
991 return r;
992
095dc596 993 if (!arg_quiet)
de158ed2 994 log_info("Running scope as unit %s.", scope);
7040b626 995
4de33e7f 996 execvpe(argv[0], argv, env);
9b15b784
LP
997
998 return log_error_errno(errno, "Failed to execute: %m");
999}
1000
1001static int start_transient_timer(
1002 sd_bus *bus,
ee451d76 1003 char **argv) {
9b15b784 1004
ee451d76 1005 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de158ed2
LP
1006 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1007 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
9b15b784 1008 _cleanup_free_ char *timer = NULL, *service = NULL;
de158ed2 1009 const char *object = NULL;
9b15b784
LP
1010 int r;
1011
1012 assert(bus);
1013 assert(argv);
1014
de158ed2
LP
1015 r = bus_wait_for_jobs_new(bus, &w);
1016 if (r < 0)
1017 return log_oom();
1018
9b15b784 1019 if (arg_unit) {
7410616c 1020 switch (unit_name_to_type(arg_unit)) {
9b15b784
LP
1021
1022 case UNIT_SERVICE:
1023 service = strdup(arg_unit);
1024 if (!service)
1025 return log_oom();
1026
7410616c
LP
1027 r = unit_name_change_suffix(service, ".timer", &timer);
1028 if (r < 0)
1029 return log_error_errno(r, "Failed to change unit suffix: %m");
9b15b784
LP
1030 break;
1031
1032 case UNIT_TIMER:
1033 timer = strdup(arg_unit);
1034 if (!timer)
1035 return log_oom();
1036
7410616c
LP
1037 r = unit_name_change_suffix(timer, ".service", &service);
1038 if (r < 0)
1039 return log_error_errno(r, "Failed to change unit suffix: %m");
9b15b784
LP
1040 break;
1041
1042 default:
7410616c
LP
1043 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
1044 if (r < 0)
1045 return log_error_errno(r, "Failed to mangle unit name: %m");
9b15b784 1046
7410616c
LP
1047 r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer);
1048 if (r < 0)
1049 return log_error_errno(r, "Failed to mangle unit name: %m");
9b15b784
LP
1050
1051 break;
1052 }
1053 } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
1054 (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
1055 return log_oom();
1056
1057 r = sd_bus_message_new_method_call(
1058 bus,
1059 &m,
1060 "org.freedesktop.systemd1",
1061 "/org/freedesktop/systemd1",
1062 "org.freedesktop.systemd1.Manager",
1063 "StartTransientUnit");
1064 if (r < 0)
1065 return bus_log_create_error(r);
1066
8c7db2fb
EV
1067 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1068 if (r < 0)
1069 return bus_log_create_error(r);
1070
9b15b784
LP
1071 /* Name and Mode */
1072 r = sd_bus_message_append(m, "ss", timer, "fail");
1073 if (r < 0)
1074 return bus_log_create_error(r);
1075
1076 /* Properties */
1077 r = sd_bus_message_open_container(m, 'a', "(sv)");
1078 if (r < 0)
1079 return bus_log_create_error(r);
1080
1081 r = transient_timer_set_properties(m);
1082 if (r < 0)
1083 return bus_log_create_error(r);
1084
1085 r = sd_bus_message_close_container(m);
1086 if (r < 0)
1087 return bus_log_create_error(r);
1088
1089 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
1090 if (r < 0)
1091 return bus_log_create_error(r);
1092
1093 if (argv[0]) {
1094 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
1095 if (r < 0)
1096 return bus_log_create_error(r);
1097
1098 r = sd_bus_message_append(m, "s", service);
1099 if (r < 0)
1100 return bus_log_create_error(r);
1101
1102 r = sd_bus_message_open_container(m, 'a', "(sv)");
1103 if (r < 0)
1104 return bus_log_create_error(r);
1105
1106 r = transient_service_set_properties(m, argv, NULL);
1107 if (r < 0)
1108 return bus_log_create_error(r);
1109
1110 r = sd_bus_message_close_container(m);
1111 if (r < 0)
1112 return bus_log_create_error(r);
1113
1114 r = sd_bus_message_close_container(m);
1115 if (r < 0)
1116 return bus_log_create_error(r);
1117 }
1118
1119 r = sd_bus_message_close_container(m);
1120 if (r < 0)
1121 return bus_log_create_error(r);
1122
8c7db2fb
EV
1123 polkit_agent_open_if_enabled();
1124
de158ed2 1125 r = sd_bus_call(bus, m, 0, &error, &reply);
ee451d76
LP
1126 if (r < 0) {
1127 log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
1128 return r;
1129 }
9b15b784 1130
de158ed2
LP
1131 r = sd_bus_message_read(reply, "o", &object);
1132 if (r < 0)
1133 return bus_log_parse_error(r);
1134
1135 r = bus_wait_for_jobs_one(w, object, arg_quiet);
1136 if (r < 0)
1137 return r;
1138
1139 log_info("Running timer as unit %s.", timer);
9b15b784 1140 if (argv[0])
de158ed2 1141 log_info("Will run service as unit %s.", service);
9b15b784
LP
1142
1143 return 0;
c2756a68
LP
1144}
1145
1146int main(int argc, char* argv[]) {
03976f7b 1147 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
c9d954b2 1148 _cleanup_free_ char *description = NULL, *command = NULL;
c2756a68
LP
1149 int r;
1150
1151 log_parse_environment();
1152 log_open();
1153
6c12b52e
LP
1154 r = parse_argv(argc, argv);
1155 if (r <= 0)
66b1e746 1156 goto finish;
c2756a68 1157
85eca92e
LP
1158 if (argc > optind && arg_transport == BUS_TRANSPORT_LOCAL) {
1159 /* Patch in an absolute path */
1160
1161 r = find_binary(argv[optind], &command);
4c213d6c 1162 if (r < 0) {
85eca92e
LP
1163 log_error_errno(r, "Failed to find executable %s: %m", argv[optind]);
1164 goto finish;
1165 }
85eca92e 1166
4c213d6c 1167 argv[optind] = command;
c9d954b2 1168 }
c9d954b2 1169
9f2e86af
LP
1170 if (!arg_description) {
1171 description = strv_join(argv + optind, " ");
1172 if (!description) {
1173 r = log_oom();
66b1e746 1174 goto finish;
9f2e86af
LP
1175 }
1176
4c213d6c 1177 if (arg_unit && isempty(description)) {
2fc09a9c
DM
1178 r = free_and_strdup(&description, arg_unit);
1179 if (r < 0)
4c213d6c 1180 goto finish;
4c213d6c
WC
1181 }
1182
9f2e86af
LP
1183 arg_description = description;
1184 }
1185
266f3e26 1186 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
c2756a68 1187 if (r < 0) {
da927ba9 1188 log_error_errno(r, "Failed to create bus connection: %m");
66b1e746 1189 goto finish;
c2756a68
LP
1190 }
1191
6c12b52e 1192 if (arg_scope)
ee451d76 1193 r = start_transient_scope(bus, argv + optind);
4c213d6c 1194 else if (with_timer())
ee451d76 1195 r = start_transient_timer(bus, argv + optind);
6c12b52e 1196 else
ee451d76 1197 r = start_transient_service(bus, argv + optind);
c2756a68 1198
66b1e746 1199finish:
df31a6c0
LP
1200 strv_free(arg_environment);
1201 strv_free(arg_property);
4c213d6c 1202 strv_free(arg_timer_property);
df31a6c0 1203
c2756a68
LP
1204 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1205}