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