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