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