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