]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/run/run.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / run / run.c
index f8257abc93b8b71dd07a5b8c32810198c0962929..2910fcb2725ec6537f5f9645ccdaae0ad52dcbcf 100644 (file)
@@ -1,21 +1,4 @@
-/***
-  This file is part of systemd.
-
-  Copyright 2013 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <getopt.h>
 #include <stdio.h>
@@ -39,6 +22,7 @@
 #include "spawn-polkit-agent.h"
 #include "strv.h"
 #include "terminal-util.h"
+#include "unit-def.h"
 #include "unit-name.h"
 #include "user-util.h"
 
@@ -61,27 +45,18 @@ static int arg_nice = 0;
 static bool arg_nice_set = false;
 static char **arg_environment = NULL;
 static char **arg_property = NULL;
-static bool arg_pty = false;
-static usec_t arg_on_active = 0;
-static usec_t arg_on_boot = 0;
-static usec_t arg_on_startup = 0;
-static usec_t arg_on_unit_active = 0;
-static usec_t arg_on_unit_inactive = 0;
-static const char *arg_on_calendar = NULL;
+static enum {
+        ARG_STDIO_NONE,      /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */
+        ARG_STDIO_PTY,       /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */
+        ARG_STDIO_DIRECT,    /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
+        ARG_STDIO_AUTO,      /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
+} arg_stdio = ARG_STDIO_NONE;
+static char **arg_path_property = NULL;
+static char **arg_socket_property = NULL;
 static char **arg_timer_property = NULL;
+static bool with_timer = false;
 static bool arg_quiet = false;
-
-static void polkit_agent_open_if_enabled(void) {
-
-        /* Open the polkit agent as a child process if necessary */
-        if (!arg_ask_password)
-                return;
-
-        if (arg_transport != BUS_TRANSPORT_LOCAL)
-                return;
-
-        polkit_agent_open();
-}
+static bool arg_aggressive_gc = false;
 
 static void help(void) {
         printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
@@ -106,8 +81,15 @@ static void help(void) {
                "     --gid=GROUP                  Run as system group\n"
                "     --nice=NICE                  Nice level\n"
                "  -E --setenv=NAME=VALUE          Set environment\n"
-               "  -t --pty                        Run service on pseudo tty\n"
-               "  -q --quiet                      Suppress information messages during runtime\n\n"
+               "  -t --pty                        Run service on pseudo TTY as STDIN/STDOUT/\n"
+               "                                  STDERR\n"
+               "  -P --pipe                       Pass STDIN/STDOUT/STDERR directly to service\n"
+               "  -q --quiet                      Suppress information messages during runtime\n"
+               "  -G --collect                    Unload unit after it ran, even when failed\n\n"
+               "Path options:\n"
+               "     --path-property=NAME=VALUE   Set path unit property\n\n"
+               "Socket options:\n"
+               "     --socket-property=NAME=VALUE Set socket unit property\n\n"
                "Timer options:\n"
                "     --on-active=SECONDS          Run after SECONDS delay\n"
                "     --on-boot=SECONDS            Run SECONDS after machine was booted up\n"
@@ -119,8 +101,20 @@ static void help(void) {
                , program_invocation_short_name);
 }
 
-static bool with_timer(void) {
-        return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar;
+static int add_timer_property(const char *name, const char *val) {
+        char *p;
+
+        assert(name);
+        assert(val);
+
+        p = strjoin(name, "=", val);
+        if (!p)
+                return log_oom();
+
+        if (strv_consume(&arg_timer_property, p) < 0)
+                return log_oom();
+
+        return 0;
 }
 
 static int parse_argv(int argc, char *argv[]) {
@@ -145,6 +139,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_ON_UNIT_INACTIVE,
                 ARG_ON_CALENDAR,
                 ARG_TIMER_PROPERTY,
+                ARG_PATH_PROPERTY,
+                ARG_SOCKET_PROPERTY,
                 ARG_NO_BLOCK,
                 ARG_NO_ASK_PASSWORD,
                 ARG_WAIT,
@@ -170,8 +166,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "nice",              required_argument, NULL, ARG_NICE             },
                 { "setenv",            required_argument, NULL, 'E'                  },
                 { "property",          required_argument, NULL, 'p'                  },
-                { "tty",               no_argument,       NULL, 't'                  }, /* deprecated */
+                { "tty",               no_argument,       NULL, 't'                  }, /* deprecated alias */
                 { "pty",               no_argument,       NULL, 't'                  },
+                { "pipe",              no_argument,       NULL, 'P'                  },
                 { "quiet",             no_argument,       NULL, 'q'                  },
                 { "on-active",         required_argument, NULL, ARG_ON_ACTIVE        },
                 { "on-boot",           required_argument, NULL, ARG_ON_BOOT          },
@@ -180,17 +177,21 @@ static int parse_argv(int argc, char *argv[]) {
                 { "on-unit-inactive",  required_argument, NULL, ARG_ON_UNIT_INACTIVE },
                 { "on-calendar",       required_argument, NULL, ARG_ON_CALENDAR      },
                 { "timer-property",    required_argument, NULL, ARG_TIMER_PROPERTY   },
+                { "path-property",     required_argument, NULL, ARG_PATH_PROPERTY    },
+                { "socket-property",   required_argument, NULL, ARG_SOCKET_PROPERTY  },
                 { "no-block",          no_argument,       NULL, ARG_NO_BLOCK         },
                 { "no-ask-password",   no_argument,       NULL, ARG_NO_ASK_PASSWORD  },
+                { "collect",           no_argument,       NULL, 'G'                  },
                 {},
         };
 
+        bool with_trigger = false;
         int r, c;
 
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqG", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -279,8 +280,18 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
-                case 't':
-                        arg_pty = true;
+                case 't': /* --pty */
+                        if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
+                                arg_stdio = ARG_STDIO_AUTO;
+                        else
+                                arg_stdio = ARG_STDIO_PTY;
+                        break;
+
+                case 'P': /* --pipe */
+                        if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
+                                arg_stdio = ARG_STDIO_AUTO;
+                        else
+                                arg_stdio = ARG_STDIO_DIRECT;
                         break;
 
                 case 'q':
@@ -288,74 +299,79 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_ON_ACTIVE:
-
-                        r = parse_sec(optarg, &arg_on_active);
-                        if (r < 0) {
-                                log_error("Failed to parse timer value: %s", optarg);
+                        r = add_timer_property("OnActiveSec", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
+                        with_timer = true;
                         break;
 
                 case ARG_ON_BOOT:
-
-                        r = parse_sec(optarg, &arg_on_boot);
-                        if (r < 0) {
-                                log_error("Failed to parse timer value: %s", optarg);
+                        r = add_timer_property("OnBootSec", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
+                        with_timer = true;
                         break;
 
                 case ARG_ON_STARTUP:
-
-                        r = parse_sec(optarg, &arg_on_startup);
-                        if (r < 0) {
-                                log_error("Failed to parse timer value: %s", optarg);
+                        r = add_timer_property("OnStartupSec", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
+                        with_timer = true;
                         break;
 
                 case ARG_ON_UNIT_ACTIVE:
-
-                        r = parse_sec(optarg, &arg_on_unit_active);
-                        if (r < 0) {
-                                log_error("Failed to parse timer value: %s", optarg);
+                        r = add_timer_property("OnUnitActiveSec", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
+                        with_timer = true;
                         break;
 
                 case ARG_ON_UNIT_INACTIVE:
-
-                        r = parse_sec(optarg, &arg_on_unit_inactive);
-                        if (r < 0) {
-                                log_error("Failed to parse timer value: %s", optarg);
+                        r = add_timer_property("OnUnitInactiveSec", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
+                        with_timer = true;
                         break;
 
-                case ARG_ON_CALENDAR: {
-                        CalendarSpec *spec = NULL;
-
-                        r = calendar_spec_from_string(optarg, &spec);
-                        if (r < 0) {
-                                log_error("Invalid calendar spec: %s", optarg);
+                case ARG_ON_CALENDAR:
+                        r = add_timer_property("OnCalendar", optarg);
+                        if (r < 0)
                                 return r;
-                        }
 
-                        calendar_spec_free(spec);
-                        arg_on_calendar = optarg;
+                        with_timer = true;
                         break;
-                }
 
                 case ARG_TIMER_PROPERTY:
 
                         if (strv_extend(&arg_timer_property, optarg) < 0)
                                 return log_oom();
 
+                        with_timer = with_timer ||
+                                !!startswith(optarg, "OnActiveSec=") ||
+                                !!startswith(optarg, "OnBootSec=") ||
+                                !!startswith(optarg, "OnStartupSec=") ||
+                                !!startswith(optarg, "OnUnitActiveSec=") ||
+                                !!startswith(optarg, "OnUnitInactiveSec=") ||
+                                !!startswith(optarg, "OnCalendar=");
+                        break;
+
+                case ARG_PATH_PROPERTY:
+
+                        if (strv_extend(&arg_path_property, optarg) < 0)
+                                return log_oom();
+
+                        break;
+
+                case ARG_SOCKET_PROPERTY:
+
+                        if (strv_extend(&arg_socket_property, optarg) < 0)
+                                return log_oom();
+
                         break;
 
                 case ARG_NO_BLOCK:
@@ -366,6 +382,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_wait = true;
                         break;
 
+                case 'G':
+                        arg_aggressive_gc = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -373,7 +393,24 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached("Unhandled option");
                 }
 
-        if ((optind >= argc) && (!arg_unit || !with_timer())) {
+        with_trigger = !!arg_path_property || !!arg_socket_property || with_timer;
+
+        /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
+        if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1) {
+                log_error("Only single trigger (path, socket, timer) unit can be created.");
+                return -EINVAL;
+        }
+
+        if (arg_stdio == ARG_STDIO_AUTO) {
+                /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
+                 * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
+                 * pipeline, but we are neatly interactive with tty-level isolation otherwise. */
+                arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
+                        ARG_STDIO_PTY :
+                        ARG_STDIO_DIRECT;
+        }
+
+        if ((optind >= argc) && (!arg_unit || !with_trigger)) {
                 log_error("Command line to execute required.");
                 return -EINVAL;
         }
@@ -393,27 +430,27 @@ static int parse_argv(int argc, char *argv[]) {
                 return -EINVAL;
         }
 
-        if (arg_pty && (with_timer() || arg_scope)) {
-                log_error("--pty is not compatible in timer or --scope mode.");
+        if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope)) {
+                log_error("--pty/--pipe is not compatible in timer or --scope mode.");
                 return -EINVAL;
         }
 
-        if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) {
-                log_error("--pty is only supported when connecting to the local system or containers.");
+        if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE) {
+                log_error("--pty/--pipe is only supported when connecting to the local system or containers.");
                 return -EINVAL;
         }
 
-        if (arg_pty && arg_no_block) {
-                log_error("--pty is not compatible with --no-block.");
+        if (arg_stdio != ARG_STDIO_NONE && arg_no_block) {
+                log_error("--pty/--pipe is not compatible with --no-block.");
                 return -EINVAL;
         }
 
-        if (arg_scope && with_timer()) {
-                log_error("Timer options are not supported in --scope mode.");
+        if (arg_scope && with_trigger) {
+                log_error("Path, socket or timer options are not supported in --scope mode.");
                 return -EINVAL;
         }
 
-        if (arg_timer_property && !with_timer()) {
+        if (arg_timer_property && !with_timer) {
                 log_error("--timer-property= has no effect without any other timer options.");
                 return -EINVAL;
         }
@@ -424,8 +461,8 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
                 }
 
-                if (with_timer()) {
-                        log_error("--wait may not be combined with timer operations.");
+                if (with_trigger) {
+                        log_error("--wait may not be combined with path, socket or timer operations.");
                         return -EINVAL;
                 }
 
@@ -438,14 +475,20 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
+static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
         int r;
 
         r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
         if (r < 0)
-                return r;
+                return bus_log_create_error(r);
 
-        r = bus_append_unit_property_assignment_many(m, properties);
+        if (arg_aggressive_gc) {
+                r = sd_bus_message_append(m, "(sv)", "CollectMode", "s", "inactive-or-failed");
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = bus_append_unit_property_assignment_many(m, t, properties);
         if (r < 0)
                 return r;
 
@@ -457,35 +500,41 @@ static int transient_cgroup_set_properties(sd_bus_message *m) {
         assert(m);
 
         if (!isempty(arg_slice)) {
-                _cleanup_free_ char *slice;
+                _cleanup_free_ char *slice = NULL;
 
-                r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice);
+                r = unit_name_mangle_with_suffix(arg_slice, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".slice", &slice);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice);
 
                 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         return 0;
 }
 
 static int transient_kill_set_properties(sd_bus_message *m) {
+        int r;
+
         assert(m);
 
-        if (arg_send_sighup)
-                return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
-        else
-                return 0;
+        if (arg_send_sighup) {
+                r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        return 0;
 }
 
 static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
+        bool send_term = false;
         int r;
 
         assert(m);
 
-        r = transient_unit_set_properties(m, arg_property);
+        r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property);
         if (r < 0)
                 return r;
 
@@ -497,45 +546,43 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
         if (r < 0)
                 return r;
 
-        if (arg_wait) {
+        if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
                 r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (arg_remain_after_exit) {
                 r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (arg_service_type) {
                 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (arg_exec_user) {
                 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (arg_exec_group) {
                 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (arg_nice_set) {
                 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         if (pty_path) {
-                const char *e;
-
                 r = sd_bus_message_append(m,
                                           "(sv)(sv)(sv)(sv)",
                                           "StandardInput", "s", "tty",
@@ -543,7 +590,24 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
                                           "StandardError", "s", "tty",
                                           "TTYPath", "s", pty_path);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
+
+                send_term = true;
+
+        } else if (arg_stdio == ARG_STDIO_DIRECT) {
+                r = sd_bus_message_append(m,
+                                          "(sv)(sv)(sv)",
+                                          "StandardInputFileDescriptor", "h", STDIN_FILENO,
+                                          "StandardOutputFileDescriptor", "h", STDOUT_FILENO,
+                                          "StandardErrorFileDescriptor", "h", STDERR_FILENO);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                send_term = isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
+        }
+
+        if (send_term) {
+                const char *e;
 
                 e = getenv("TERM");
                 if (e) {
@@ -554,85 +618,85 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons
                                                   "(sv)",
                                                   "Environment", "as", 1, n);
                         if (r < 0)
-                                return r;
+                                return bus_log_create_error(r);
                 }
         }
 
         if (!strv_isempty(arg_environment)) {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append(m, "s", "Environment");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_open_container(m, 'v', "as");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append_strv(m, arg_environment);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         /* Exec container */
         {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append(m, "s", "ExecStart");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_open_container(m, 'v', "a(sasb)");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_open_container(m, 'a', "(sasb)");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_open_container(m, 'r', "sasb");
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append(m, "s", argv[0]);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append_strv(m, argv);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_append(m, "b", false);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return r;
+                        return bus_log_create_error(r);
         }
 
         return 0;
@@ -643,7 +707,7 @@ static int transient_scope_set_properties(sd_bus_message *m) {
 
         assert(m);
 
-        r = transient_unit_set_properties(m, arg_property);
+        r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property);
         if (r < 0)
                 return r;
 
@@ -655,9 +719,9 @@ static int transient_scope_set_properties(sd_bus_message *m) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
+        r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid_cached());
         if (r < 0)
-                return r;
+                return bus_log_create_error(r);
 
         return 0;
 }
@@ -667,50 +731,14 @@ static int transient_timer_set_properties(sd_bus_message *m) {
 
         assert(m);
 
-        r = transient_unit_set_properties(m, arg_timer_property);
+        r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property);
         if (r < 0)
                 return r;
 
         /* Automatically clean up our transient timers */
         r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
         if (r < 0)
-                return r;
-
-        if (arg_on_active) {
-                r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
-                if (r < 0)
-                        return r;
-        }
-
-        if (arg_on_boot) {
-                r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot);
-                if (r < 0)
-                        return r;
-        }
-
-        if (arg_on_startup) {
-                r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup);
-                if (r < 0)
-                        return r;
-        }
-
-        if (arg_on_unit_active) {
-                r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active);
-                if (r < 0)
-                        return r;
-        }
-
-        if (arg_on_unit_inactive) {
-                r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive);
-                if (r < 0)
-                        return r;
-        }
-
-        if (arg_on_calendar) {
-                r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar);
-                if (r < 0)
-                        return r;
-        }
+                return bus_log_create_error(r);
 
         return 0;
 }
@@ -772,6 +800,8 @@ typedef struct RunContext {
         uint64_t inactive_enter_usec;
         char *result;
         uint64_t cpu_usage_nsec;
+        uint64_t ip_ingress_bytes;
+        uint64_t ip_egress_bytes;
         uint32_t exit_code;
         uint32_t exit_status;
 } RunContext;
@@ -815,6 +845,8 @@ static int run_context_update(RunContext *c, const char *path) {
                 { "ExecMainCode",                     "i", NULL, offsetof(RunContext, exit_code)           },
                 { "ExecMainStatus",                   "i", NULL, offsetof(RunContext, exit_status)         },
                 { "CPUUsageNSec",                     "t", NULL, offsetof(RunContext, cpu_usage_nsec)      },
+                { "IPIngressBytes",                   "t", NULL, offsetof(RunContext, ip_ingress_bytes)    },
+                { "IPEgressBytes",                    "t", NULL, offsetof(RunContext, ip_egress_bytes)     },
                 {}
         };
 
@@ -825,7 +857,9 @@ static int run_context_update(RunContext *c, const char *path) {
                                    "org.freedesktop.systemd1",
                                    path,
                                    map,
+                                   BUS_MAP_STRDUP,
                                    &error,
+                                   NULL,
                                    c);
         if (r < 0) {
                 sd_event_exit(c->event, EXIT_FAILURE);
@@ -875,10 +909,10 @@ static int start_transient_service(
         assert(argv);
         assert(retval);
 
-        if (arg_pty) {
+        if (arg_stdio == ARG_STDIO_PTY) {
 
                 if (arg_transport == BUS_TRANSPORT_LOCAL) {
-                        master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
+                        master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
                         if (master < 0)
                                 return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
 
@@ -933,7 +967,7 @@ static int start_transient_service(
         }
 
         if (arg_unit) {
-                r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
+                r = unit_name_mangle_with_suffix(arg_unit, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".service", &service);
                 if (r < 0)
                         return log_error_errno(r, "Failed to mangle unit name: %m");
         } else {
@@ -968,7 +1002,7 @@ static int start_transient_service(
 
         r = transient_service_set_properties(m, argv, pty_path);
         if (r < 0)
-                return bus_log_create_error(r);
+                return r;
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
@@ -979,7 +1013,7 @@ static int start_transient_service(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        polkit_agent_open_if_enabled();
+        polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
         r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0)
@@ -1000,10 +1034,15 @@ static int start_transient_service(
         if (!arg_quiet)
                 log_info("Running as unit: %s", service);
 
-        if (arg_wait || master >= 0) {
-                _cleanup_(run_context_free) RunContext c = {};
+        if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
+                _cleanup_(run_context_free) RunContext c = {
+                        .cpu_usage_nsec = NSEC_INFINITY,
+                        .ip_ingress_bytes = UINT64_MAX,
+                        .ip_egress_bytes = UINT64_MAX,
+                        .inactive_exit_usec = USEC_INFINITY,
+                        .inactive_enter_usec = USEC_INFINITY,
+                };
                 _cleanup_free_ char *path = NULL;
-                const char *mt;
 
                 c.bus = sd_bus_ref(bus);
 
@@ -1024,25 +1063,29 @@ static int start_transient_service(
                                 return log_error_errno(r, "Failed to create PTY forwarder: %m");
 
                         pty_forward_set_handler(c.forward, pty_forward_handler, &c);
-                }
 
+                        /* Make sure to process any TTY events before we process bus events */
+                        (void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
+                }
 
                 path = unit_dbus_path_from_name(service);
                 if (!path)
                         return log_oom();
 
-                mt = strjoina("type='signal',"
-                              "sender='org.freedesktop.systemd1',"
-                              "path='", path, "',"
-                              "interface='org.freedesktop.DBus.Properties',"
-                              "member='PropertiesChanged'");
-                r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c);
+                r = sd_bus_match_signal_async(
+                                bus,
+                                &c.match,
+                                "org.freedesktop.systemd1",
+                                path,
+                                "org.freedesktop.DBus.Properties",
+                                "PropertiesChanged",
+                                on_properties_changed, NULL, &c);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add properties changed signal.");
+                        return log_error_errno(r, "Failed to request properties changed signal match: %m");
 
-                r = sd_bus_attach_event(bus, c.event, 0);
+                r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to attach bus to event loop.");
+                        return log_error_errno(r, "Failed to attach bus to event loop: %m");
 
                 r = run_context_update(&c, path);
                 if (r < 0)
@@ -1082,10 +1125,19 @@ static int start_transient_service(
                                 log_info("Service runtime: %s", format_timespan(ts, sizeof(ts), c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
                         }
 
-                        if (c.cpu_usage_nsec > 0 && c.cpu_usage_nsec != NSEC_INFINITY) {
+                        if (c.cpu_usage_nsec != NSEC_INFINITY) {
                                 char ts[FORMAT_TIMESPAN_MAX];
                                 log_info("CPU time consumed: %s", format_timespan(ts, sizeof(ts), (c.cpu_usage_nsec + NSEC_PER_USEC - 1) / NSEC_PER_USEC, USEC_PER_MSEC));
                         }
+
+                        if (c.ip_ingress_bytes != UINT64_MAX) {
+                                char bytes[FORMAT_BYTES_MAX];
+                                log_info("IP traffic received: %s", format_bytes(bytes, sizeof(bytes), c.ip_ingress_bytes));
+                        }
+                        if (c.ip_egress_bytes != UINT64_MAX) {
+                                char bytes[FORMAT_BYTES_MAX];
+                                log_info("IP traffic sent: %s", format_bytes(bytes, sizeof(bytes), c.ip_egress_bytes));
+                        }
                 }
 
                 /* Try to propagate the service's return value */
@@ -1118,7 +1170,7 @@ static int start_transient_scope(
                 return log_oom();
 
         if (arg_unit) {
-                r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope);
+                r = unit_name_mangle_with_suffix(arg_unit, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".scope", &scope);
                 if (r < 0)
                         return log_error_errno(r, "Failed to mangle scope name: %m");
         } else {
@@ -1153,7 +1205,7 @@ static int start_transient_scope(
 
         r = transient_scope_set_properties(m);
         if (r < 0)
-                return bus_log_create_error(r);
+                return r;
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
@@ -1164,7 +1216,7 @@ static int start_transient_scope(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        polkit_agent_open_if_enabled();
+        polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
         r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
@@ -1246,14 +1298,15 @@ static int start_transient_scope(
         return log_error_errno(errno, "Failed to execute: %m");
 }
 
-static int start_transient_timer(
+static int start_transient_trigger(
                 sd_bus *bus,
-                char **argv) {
+                char **argv,
+                const char *suffix) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
-        _cleanup_free_ char *timer = NULL, *service = NULL;
+        _cleanup_free_ char *trigger = NULL, *service = NULL;
         const char *object = NULL;
         int r;
 
@@ -1272,27 +1325,27 @@ static int start_transient_timer(
                         if (!service)
                                 return log_oom();
 
-                        r = unit_name_change_suffix(service, ".timer", &timer);
+                        r = unit_name_change_suffix(service, suffix, &trigger);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to change unit suffix: %m");
                         break;
 
                 case UNIT_TIMER:
-                        timer = strdup(arg_unit);
-                        if (!timer)
+                        trigger = strdup(arg_unit);
+                        if (!trigger)
                                 return log_oom();
 
-                        r = unit_name_change_suffix(timer, ".service", &service);
+                        r = unit_name_change_suffix(trigger, ".service", &service);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to change unit suffix: %m");
                         break;
 
                 default:
-                        r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
+                        r = unit_name_mangle_with_suffix(arg_unit, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".service", &service);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to mangle unit name: %m");
 
-                        r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer);
+                        r = unit_name_mangle_with_suffix(arg_unit, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, suffix, &trigger);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to mangle unit name: %m");
 
@@ -1303,7 +1356,7 @@ static int start_transient_timer(
                 if (r < 0)
                         return r;
 
-                r = unit_name_change_suffix(service, ".timer", &timer);
+                r = unit_name_change_suffix(service, suffix, &trigger);
                 if (r < 0)
                         return log_error_errno(r, "Failed to change unit suffix: %m");
         }
@@ -1323,7 +1376,7 @@ static int start_transient_timer(
                 return bus_log_create_error(r);
 
         /* Name and Mode */
-        r = sd_bus_message_append(m, "ss", timer, "fail");
+        r = sd_bus_message_append(m, "ss", trigger, "fail");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1332,9 +1385,16 @@ static int start_transient_timer(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = transient_timer_set_properties(m);
+        if (streq(suffix, ".path"))
+                r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property);
+        else if (streq(suffix, ".socket"))
+                r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property);
+        else if (streq(suffix, ".timer"))
+                r = transient_timer_set_properties(m);
+        else
+                assert_not_reached("Invalid suffix");
         if (r < 0)
-                return bus_log_create_error(r);
+                return r;
 
         r = sd_bus_message_close_container(m);
         if (r < 0)
@@ -1359,7 +1419,7 @@ static int start_transient_timer(
 
                 r = transient_service_set_properties(m, argv, NULL);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
@@ -1374,11 +1434,11 @@ static int start_transient_timer(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        polkit_agent_open_if_enabled();
+        polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
         r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
-                log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r));
+                log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r));
                 return r;
         }
 
@@ -1391,7 +1451,7 @@ static int start_transient_timer(
                 return r;
 
         if (!arg_quiet) {
-                log_info("Running timer as unit: %s", timer);
+                log_info("Running %s as unit: %s", suffix + 1, trigger);
                 if (argv[0])
                         log_info("Will run service as unit: %s", service);
         }
@@ -1441,7 +1501,7 @@ int main(int argc, char* argv[]) {
 
         /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
          * connection */
-        if (arg_wait || arg_pty)
+        if (arg_wait || arg_stdio != ARG_STDIO_NONE)
                 r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
         else
                 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
@@ -1452,14 +1512,20 @@ int main(int argc, char* argv[]) {
 
         if (arg_scope)
                 r = start_transient_scope(bus, argv + optind);
-        else if (with_timer())
-                r = start_transient_timer(bus, argv + optind);
+        else if (arg_path_property)
+                r = start_transient_trigger(bus, argv + optind, ".path");
+        else if (arg_socket_property)
+                r = start_transient_trigger(bus, argv + optind, ".socket");
+        else if (with_timer)
+                r = start_transient_trigger(bus, argv + optind, ".timer");
         else
                 r = start_transient_service(bus, argv + optind, &retval);
 
 finish:
         strv_free(arg_environment);
         strv_free(arg_property);
+        strv_free(arg_path_property);
+        strv_free(arg_socket_property);
         strv_free(arg_timer_property);
 
         return r < 0 ? EXIT_FAILURE : retval;