[systemd-commits] 3 commits - Makefile.am configure.ac man/systemd-run.xml src/bus-proxyd src/core src/libsystemd src/run src/shared units/systemd-bus-proxyd at .service.in units/systemd-bus-proxyd at .service.m4.in units/user at .service.in units/user at .service.m4.in

Lennart Poettering lennart at kemper.freedesktop.org
Tue Dec 9 09:25:16 PST 2014


 Makefile.am                             |   11 
 configure.ac                            |    4 
 man/systemd-run.xml                     |   94 ++++
 src/bus-proxyd/bus-proxyd.c             |   22 +
 src/core/dbus-manager.c                 |    8 
 src/core/unit.c                         |   21 -
 src/libsystemd/sd-bus/bus-util.c        |   14 
 src/run/run.c                           |  628 ++++++++++++++++++++++++--------
 src/shared/capability.c                 |   18 
 src/shared/capability.h                 |    2 
 units/systemd-bus-proxyd at .service.in    |   22 -
 units/systemd-bus-proxyd at .service.m4.in |   22 +
 units/user at .service.in                  |   19 
 units/user at .service.m4.in               |   23 +
 14 files changed, 715 insertions(+), 193 deletions(-)

New commits:
commit adb76a701d870b980f7949bcfece2c1547b4976d
Author: WaLyong Cho <walyong.cho at samsung.com>
Date:   Tue Dec 9 21:46:30 2014 +0900

    unit: update unit dropin paths and time when dropin file is written.
    
    If a unit is set property by "systemctl set-property", a new dropin
    file is generated. But the unit's dropin_paths and dropin_mtime are
    not updated. So the unit is shown as need daemon reload.
    Update unit dropin_paths and dropin_mtime also when dropin file is
    written.

diff --git a/src/core/unit.c b/src/core/unit.c
index 43280ec..b3b0892 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3272,7 +3272,7 @@ static int unit_drop_in_file(Unit *u,
 
 int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
 
-        _cleanup_free_ char *dir = NULL;
+        _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
         int r;
 
         assert(u);
@@ -3284,7 +3284,24 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co
         if (r < 0)
                 return r;
 
-        return write_drop_in(dir, u->id, 50, name, data);
+        r = write_drop_in(dir, u->id, 50, name, data);
+        if (r < 0)
+                return r;
+
+        r = drop_in_file(dir, u->id, 50, name, &p, &q);
+        if (r < 0)
+                return r;
+
+        r = strv_extend(&u->dropin_paths, q);
+        if (r < 0)
+                return r;
+
+        strv_sort(u->dropin_paths);
+        strv_uniq(u->dropin_paths);
+
+        u->dropin_mtime = now(CLOCK_REALTIME);
+
+        return 0;
 }
 
 int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {

commit dd5ae4c36c89da5dbe8d1628939b26c00db98753
Author: Przemyslaw Kedzierski <p.kedzierski at samsung.com>
Date:   Tue Dec 9 12:17:24 2014 +0100

    bus-proxy: cloning smack label
    
    When dbus client connects to systemd-bus-proxyd through
    Unix domain socket proxy takes client's smack label and sets for itself.
    
    It is done before and independent of dropping privileges.
    
    The reason of such soluton is fact that tests of access rights
    performed by lsm may take place inside kernel, not only
    in userspace of recipient of message.
    
    The bus-proxyd needs CAP_MAC_ADMIN to manipulate its label.
    
    In case of systemd running in system mode, CAP_MAC_ADMIN
    should be added to CapabilityBoundingSet in service file of bus-proxyd.
    
    In case of systemd running in user mode ('systemd --user')
    it can be achieved by addition
    Capabilities=cap_mac_admin=i and SecureBits=keep-caps
    to user at .service file
    and setting cap_mac_admin+ei on bus-proxyd binary.

diff --git a/Makefile.am b/Makefile.am
index 7b43733..78cf4a9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -591,7 +591,7 @@ EXTRA_DIST += \
 	units/systemd-fsck at .service.in \
 	units/systemd-fsck-root.service.in \
 	units/systemd-machine-id-commit.service.in \
-	units/user at .service.in \
+	units/user at .service.m4.in \
 	units/debug-shell.service.in \
 	units/systemd-suspend.service.in \
 	units/quotaon.service.in \
@@ -2579,9 +2579,16 @@ dist_userunit_DATA += \
 endif
 
 EXTRA_DIST += \
-	units/systemd-bus-proxyd at .service.in \
+	units/systemd-bus-proxyd at .service.m4.in \
 	units/user/systemd-bus-proxyd at .service.in
 
+if HAVE_SMACK
+bus-proxyd-set-cap-hook:
+	$(SETCAP) cap_mac_admin+ei $(DESTDIR)$(rootlibexecdir)/systemd-bus-proxyd
+
+INSTALL_EXEC_HOOKS += bus-proxyd-set-cap-hook
+endif
+
 # ------------------------------------------------------------------------------
 systemd_tty_ask_password_agent_SOURCES = \
 	src/tty-ask-password-agent/tty-ask-password-agent.c
diff --git a/configure.ac b/configure.ac
index 356a3c3..94b4a02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,6 +90,8 @@ AC_PATH_PROG([XSLTPROC], [xsltproc])
 AC_PATH_PROG([QUOTAON], [quotaon], [/usr/sbin/quotaon], [$PATH:/usr/sbin:/sbin])
 AC_PATH_PROG([QUOTACHECK], [quotacheck], [/usr/sbin/quotacheck], [$PATH:/usr/sbin:/sbin])
 
+AC_PATH_PROG([SETCAP], [setcap], [/usr/sbin/setcap], [$PATH:/usr/sbin:/sbin])
+
 AC_PATH_PROG([KILL], [kill], [/usr/bin/kill], [$PATH:/usr/sbin:/sbin])
 
 AC_PATH_PROG([KMOD], [kmod], [/usr/bin/kmod], [$PATH:/usr/sbin:/sbin])
@@ -674,6 +676,8 @@ if test "x${have_smack}" = xyes ; then
         AC_DEFINE(HAVE_SMACK, 1, [Define if SMACK is available])
 fi
 
+AM_CONDITIONAL([HAVE_SMACK], [test "x$have_smack" = "xyes"])
+
 # ------------------------------------------------------------------------------
 AC_ARG_ENABLE([gcrypt],
         AS_HELP_STRING([--disable-gcrypt],[Disable optional GCRYPT support]),
diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c
index 42fb0da..5d30453 100644
--- a/src/bus-proxyd/bus-proxyd.c
+++ b/src/bus-proxyd/bus-proxyd.c
@@ -46,6 +46,7 @@
 #include "capability.h"
 #include "bus-policy.h"
 #include "bus-control.h"
+#include "smack-util.h"
 
 static char *arg_address = NULL;
 static char *arg_command_line_buffer = NULL;
@@ -1235,6 +1236,23 @@ static int patch_sender(sd_bus *a, sd_bus_message *m) {
         return 0;
 }
 
+static int mac_smack_apply_label_and_drop_cap_mac_admin(pid_t its_pid, const char *new_label) {
+#ifdef HAVE_SMACK
+        int r = 0, k;
+
+        if (!mac_smack_use())
+                return 0;
+
+        if (new_label && its_pid > 0)
+                r = mac_smack_apply_pid(its_pid, new_label);
+
+        k = drop_capability(CAP_MAC_ADMIN);
+        return r < 0 ? r : k;
+#else
+        return 0;
+#endif
+}
+
 int main(int argc, char *argv[]) {
 
         _cleanup_bus_close_unref_ sd_bus *a = NULL, *b = NULL;
@@ -1274,6 +1292,10 @@ int main(int argc, char *argv[]) {
         if (is_unix) {
                 (void) getpeercred(in_fd, &ucred);
                 (void) getpeersec(in_fd, &peersec);
+
+                r = mac_smack_apply_label_and_drop_cap_mac_admin(getpid(), peersec);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to set SMACK label (%s) and drop CAP_MAC_ADMIN: %m", peersec);
         }
 
         if (arg_drop_privileges) {
diff --git a/src/shared/capability.c b/src/shared/capability.c
index 5d156ab..65d7e03 100644
--- a/src/shared/capability.c
+++ b/src/shared/capability.c
@@ -271,3 +271,21 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
 
         return 0;
 }
+
+int drop_capability(cap_value_t cv) {
+        _cleanup_cap_free_ cap_t tmp_cap = NULL;
+
+        tmp_cap = cap_get_proc();
+        if (!tmp_cap)
+                return -errno;
+
+        if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) ||
+            (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) ||
+            (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0))
+                return -errno;
+
+        if (cap_set_proc(tmp_cap) < 0)
+                return -errno;
+
+        return 0;
+}
diff --git a/src/shared/capability.h b/src/shared/capability.h
index 3e6d999..6f2f6f9 100644
--- a/src/shared/capability.h
+++ b/src/shared/capability.h
@@ -34,6 +34,8 @@ int capability_bounding_set_drop_usermode(uint64_t drop);
 
 int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites);
 
+int drop_capability(cap_value_t cv);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free);
 #define _cleanup_cap_free_ _cleanup_(cap_freep)
 
diff --git a/units/systemd-bus-proxyd at .service.in b/units/systemd-bus-proxyd at .service.in
deleted file mode 100644
index 23b5ffa..0000000
--- a/units/systemd-bus-proxyd at .service.in
+++ /dev/null
@@ -1,22 +0,0 @@
-#  This file is part of systemd.
-#
-#  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.
-
-[Unit]
-Description=Legacy D-Bus Protocol Compatibility Daemon
-
-[Service]
-# The first argument will be replaced by the service by information on
-# the process requesting the proxy, we need a placeholder to keep the
-# space available for this.
-ExecStart=@rootlibexecdir@/systemd-bus-proxyd --drop-privileges --address=kernel:path=/sys/fs/kdbus/0-system/bus xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-NotifyAccess=main
-CapabilityBoundingSet=CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP
-PrivateTmp=yes
-PrivateDevices=yes
-PrivateNetwork=yes
-ProtectSystem=full
-ProtectHome=yes
diff --git a/units/systemd-bus-proxyd at .service.m4.in b/units/systemd-bus-proxyd at .service.m4.in
new file mode 100644
index 0000000..3f3ab64
--- /dev/null
+++ b/units/systemd-bus-proxyd at .service.m4.in
@@ -0,0 +1,22 @@
+#  This file is part of systemd.
+#
+#  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.
+
+[Unit]
+Description=Legacy D-Bus Protocol Compatibility Daemon
+
+[Service]
+# The first argument will be replaced by the service by information on
+# the process requesting the proxy, we need a placeholder to keep the
+# space available for this.
+ExecStart=@rootlibexecdir@/systemd-bus-proxyd --drop-privileges --address=kernel:path=/sys/fs/kdbus/0-system/bus xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+NotifyAccess=main
+CapabilityBoundingSet=CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP m4_ifdef(`HAVE_SMACK', CAP_MAC_ADMIN )
+PrivateTmp=yes
+PrivateDevices=yes
+PrivateNetwork=yes
+ProtectSystem=full
+ProtectHome=yes
diff --git a/units/user at .service.in b/units/user at .service.in
deleted file mode 100644
index 1e21d51..0000000
--- a/units/user at .service.in
+++ /dev/null
@@ -1,19 +0,0 @@
-#  This file is part of systemd.
-#
-#  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.
-
-[Unit]
-Description=User Manager for UID %i
-After=systemd-user-sessions.service
-
-[Service]
-User=%i
-PAMName=systemd-user
-Type=notify
-ExecStart=- at rootlibexecdir@/systemd --user
-Slice=user-%i.slice
-KillMode=mixed
-Delegate=yes
diff --git a/units/user at .service.m4.in b/units/user at .service.m4.in
new file mode 100644
index 0000000..340c02b
--- /dev/null
+++ b/units/user at .service.m4.in
@@ -0,0 +1,23 @@
+#  This file is part of systemd.
+#
+#  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.
+
+[Unit]
+Description=User Manager for UID %i
+After=systemd-user-sessions.service
+
+[Service]
+User=%i
+PAMName=systemd-user
+Type=notify
+ExecStart=- at rootlibexecdir@/systemd --user
+Slice=user-%i.slice
+KillMode=mixed
+Delegate=yes
+m4_ifdef(`HAVE_SMACK',
+Capabilities=cap_mac_admin=i
+SecureBits=keep-caps
+)

commit 4c213d6cf416917c61f82d8bee795b8f3a4c5372
Author: WaLyong Cho <walyong.cho at samsung.com>
Date:   Tue Dec 9 16:07:16 2014 +0900

    run: introduce timer support option
    
    Support timer options --on-active=, --on-boot=, --on-startup=,
    --on-unit-active=, --on-unit-inactive=, --on-calendar=. Each options
    corresponding with OnActiveSec=, OnBootSec=, OnStartupSec=,
    OnUnitActiveSec=, OnUnitInactiveSec=, OnCalendar= of timer
    respectively. And OnCalendar= and WakeSystem= supported by
    --timer-property= option like --property= of systemd-run.
    
    And if --unit= option and timer options are specified the command can
    be omitted. In this case, systemd-run assumes the target service is
    already loaded. And just try to generate transient timer unit only.

diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 28a9878..b9cec91 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -45,7 +45,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
   <refnamediv>
     <refname>systemd-run</refname>
-    <refpurpose>Run programs in transient scope or service units</refpurpose>
+    <refpurpose>Run programs in transient scope or service or timer units</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -56,15 +56,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       <arg choice="opt" rep="repeat">ARGS</arg>
       </arg>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-run</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="opt" rep="repeat">TIMER OPTIONS</arg>
+      <arg choice="req"><replaceable>COMMAND</replaceable></arg>
+      <arg choice="opt" rep="repeat">ARGS</arg>
+    </cmdsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-run</command> may be used to create and start
-    a transient <filename>.service</filename> or a
-    <filename>.scope</filename> unit and run the specified
-    <replaceable>COMMAND</replaceable> in it.</para>
+    <para><command>systemd-run</command> may be used to create and
+    start a transient <filename>.service</filename> or a transient
+    <filename>.timer</filename> or a <filename>.scope</filename> unit
+    and run the specified <replaceable>COMMAND</replaceable> in
+    it.</para>
 
     <para>If a command is run as transient service unit, it will be
     started and managed by the service manager like any other service,
@@ -74,6 +82,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
     will start the service asynchronously in the background and
     immediately return.</para>
 
+    <para>If a command is run with timer options, transient timer unit
+    also be created with transient service unit. But the transient
+    timer unit is only started immediately. The transient service unit
+    will be started when the transient timer is elapsed. If
+    <option>--unit=</option> is specified with timer options, the
+    <replaceable>COMMAND</replaceable> can be omitted. In this case,
+    <command>systemd-run</command> assumes service unit is already
+    loaded and creates transient timer unit only. To successfully
+    create timer unit, already loaded service unit should be specified
+    with <option>--unit=</option>. This transient timer unit can
+    activate the existing service unit like any other timer.</para>
+
     <para>If a command is run as transient scope unit, it will be
     started directly by <command>systemd-run</command> and thus
     inherit the execution environment of the caller. It is however
@@ -210,6 +230,54 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
+      <varlistentry>
+        <term><option>--on-active=</option></term>
+        <term><option>--on-boot=</option></term>
+        <term><option>--on-startup=</option></term>
+        <term><option>--on-unit-active=</option></term>
+        <term><option>--on-unit-inactive=</option></term>
+
+        <listitem><para>Defines monotonic timers relative to different
+        starting points. Also see <varname>OnActiveSec=</varname>,
+        <varname>OnBootSec=</varname>,
+        <varname>OnStartupSec=</varname>,
+        <varname>OnUnitActiveSec=</varname> and
+        <varname>OnUnitInactiveSec=</varname> in
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+        options have no effect in conjunction with
+        <option>--scope</option>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--on-calendar=</option></term>
+
+        <listitem><para>Defines realtime (i.e. wallclock) timers with
+        calendar event expressions. Also see
+        <varname>OnCalendar=</varname> in
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+        option has no effect in conjunction with
+        <option>--scope</option>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--timer-property=</option></term>
+
+        <listitem><para>Sets a timer unit property for the timer unit
+        that is created. It is similar with
+        <option>--property</option> but only for created timer
+        unit. This option only has effect in conjunction with
+        <option>--on-active=</option>, <option>--on-boot=</option>,
+        <option>--on-startup=</option>,
+        <option>--on-unit-active=</option>,
+        <option>--on-unit-inactive=</option>,
+        <option>--on-calendar=</option>. This takes an assignment in
+        the same format as
+        <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+        <command>set-property</command> command.</para> </listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
@@ -250,6 +318,21 @@ Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.
     property.</para>
 
     <programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting>
+
+    <para>The following command will touch a file after 30 seconds.</para>
+
+    <programlisting># date; systemd-run --on-active=30 --timer-property=AccuracySec=100ms /bin/touch /tmp/foo
+Mon Dec  8 20:44:24 KST 2014
+Running as unit run-71.timer.
+Will run as unit run-71.service.
+# journalctl -b -u run-73.timer
+-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
+Dec 08 20:44:38 container systemd[1]: Starting /bin/touch /tmp/foo.
+Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo.
+# journalctl -b -u run-73.service
+-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. --
+Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo...
+Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo.</programlisting>
   </refsect1>
 
   <refsect1>
@@ -263,6 +346,7 @@ Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.
       <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 5fe06f9..140a413 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -681,9 +681,11 @@ static int transient_aux_units_from_message(
                 if (r < 0 && r != -EEXIST)
                         return r;
 
-                r = unit_load(u);
-                if (r < 0)
-                        return r;
+                if (r != -EEXIST) {
+                        r = unit_load(u);
+                        if (r < 0)
+                                return r;
+                }
 
                 r = sd_bus_message_exit_container(message);
                 if (r < 0)
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
index bdaa449..0f1a89c 100644
--- a/src/libsystemd/sd-bus/bus-util.c
+++ b/src/libsystemd/sd-bus/bus-util.c
@@ -1372,7 +1372,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
 
         if (STR_IN_SET(field,
                        "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
-                       "SendSIGHUP", "SendSIGKILL")) {
+                       "SendSIGHUP", "SendSIGKILL",
+                       "WakeSystem")) {
 
                 r = parse_boolean(eq);
                 if (r < 0) {
@@ -1533,6 +1534,17 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
 
                 r = sd_bus_message_append(m, "v", "i", sig);
 
+        } else if (streq(field, "AccuracySec")) {
+                usec_t u;
+
+                r = parse_sec(eq, &u);
+                if (r < 0) {
+                        log_error("Failed to parse %s value %s", field, eq);
+                        return -EINVAL;
+                }
+
+                r = sd_bus_message_append(m, "v", "t", u);
+
         } else {
                 log_error("Unknown assignment %s.", assignment);
                 return -EINVAL;
diff --git a/src/run/run.c b/src/run/run.c
index 85eb052..7a80223 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -30,6 +30,7 @@
 #include "env-util.h"
 #include "path-util.h"
 #include "bus-error.h"
+#include "calendarspec.h"
 
 static bool arg_scope = false;
 static bool arg_remain_after_exit = false;
@@ -47,30 +48,51 @@ static int arg_nice = 0;
 static bool arg_nice_set = false;
 static char **arg_environment = NULL;
 static char **arg_property = NULL;
+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 char *arg_on_calendar = NULL;
+static char **arg_timer_property = NULL;
 
 static void help(void) {
-        printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
-               "Run the specified command in a transient scope or service unit.\n\n"
-               "  -h --help                 Show this help\n"
-               "     --version              Show package version\n"
-               "     --user                 Run as user unit\n"
-               "  -H --host=[USER@]HOST     Operate on remote host\n"
-               "  -M --machine=CONTAINER    Operate on local container\n"
-               "     --scope                Run this as scope rather than service\n"
-               "     --unit=UNIT            Run under the specified unit name\n"
-               "  -p --property=NAME=VALUE  Set unit property\n"
-               "     --description=TEXT     Description for unit\n"
-               "     --slice=SLICE          Run in the specified slice\n"
-               "  -r --remain-after-exit    Leave service around until explicitly stopped\n"
-               "     --send-sighup          Send SIGHUP when terminating\n"
-               "     --service-type=TYPE    Service type\n"
-               "     --uid=USER             Run as system user\n"
-               "     --gid=GROUP            Run as system group\n"
-               "     --nice=NICE            Nice level\n"
-               "     --setenv=NAME=VALUE    Set environment\n",
+        printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n"
+               "Run the specified command in a transient scope or service or timer\n"
+               "unit. If timer option is specified and unit is exist which is\n"
+               "specified with --unit option then command can be ommited.\n\n"
+               "  -h --help                       Show this help\n"
+               "     --version                    Show package version\n"
+               "     --user                       Run as user unit\n"
+               "  -H --host=[USER@]HOST           Operate on remote host\n"
+               "  -M --machine=CONTAINER          Operate on local container\n"
+               "     --scope                      Run this as scope rather than service\n"
+               "     --unit=UNIT                  Run under the specified unit name\n"
+               "  -p --property=NAME=VALUE        Set unit property\n"
+               "     --description=TEXT           Description for unit\n"
+               "     --slice=SLICE                Run in the specified slice\n"
+               "  -r --remain-after-exit          Leave service around until explicitly stopped\n"
+               "     --send-sighup                Send SIGHUP when terminating\n"
+               "     --service-type=TYPE          Service type\n"
+               "     --uid=USER                   Run as system user\n"
+               "     --gid=GROUP                  Run as system group\n"
+               "     --nice=NICE                  Nice level\n"
+               "     --setenv=NAME=VALUE          Set environment\n\n"
+               "Timer options:\n\n"
+               "     --on-active=SEC              Run after seconds\n"
+               "     --on-boot=SEC                Run after seconds from machine was booted up\n"
+               "     --on-startup=SEC             Run after seconds from systemd was first started\n"
+               "     --on-unit-active=SEC         Run after seconds from the last activation\n"
+               "     --on-unit-inactive=SEC       Run after seconds from the last deactivation\n"
+               "     --on-calendar=SPEC           Realtime timer\n"
+               "     --timer-property=NAME=VALUE  Set timer unit property\n",
                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 parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -86,32 +108,47 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_EXEC_GROUP,
                 ARG_SERVICE_TYPE,
                 ARG_NICE,
-                ARG_SETENV
+                ARG_SETENV,
+                ARG_ON_ACTIVE,
+                ARG_ON_BOOT,
+                ARG_ON_STARTUP,
+                ARG_ON_UNIT_ACTIVE,
+                ARG_ON_UNIT_INACTIVE,
+                ARG_ON_CALENDAR,
+                ARG_TIMER_PROPERTY
         };
 
         static const struct option options[] = {
-                { "help",              no_argument,       NULL, 'h'              },
-                { "version",           no_argument,       NULL, ARG_VERSION      },
-                { "user",              no_argument,       NULL, ARG_USER         },
-                { "system",            no_argument,       NULL, ARG_SYSTEM       },
-                { "scope",             no_argument,       NULL, ARG_SCOPE        },
-                { "unit",              required_argument, NULL, ARG_UNIT         },
-                { "description",       required_argument, NULL, ARG_DESCRIPTION  },
-                { "slice",             required_argument, NULL, ARG_SLICE        },
-                { "remain-after-exit", no_argument,       NULL, 'r'              },
-                { "send-sighup",       no_argument,       NULL, ARG_SEND_SIGHUP  },
-                { "host",              required_argument, NULL, 'H'              },
-                { "machine",           required_argument, NULL, 'M'              },
-                { "service-type",      required_argument, NULL, ARG_SERVICE_TYPE },
-                { "uid",               required_argument, NULL, ARG_EXEC_USER    },
-                { "gid",               required_argument, NULL, ARG_EXEC_GROUP   },
-                { "nice",              required_argument, NULL, ARG_NICE         },
-                { "setenv",            required_argument, NULL, ARG_SETENV       },
-                { "property",          required_argument, NULL, 'p'              },
+                { "help",              no_argument,       NULL, 'h'                  },
+                { "version",           no_argument,       NULL, ARG_VERSION          },
+                { "user",              no_argument,       NULL, ARG_USER             },
+                { "system",            no_argument,       NULL, ARG_SYSTEM           },
+                { "scope",             no_argument,       NULL, ARG_SCOPE            },
+                { "unit",              required_argument, NULL, ARG_UNIT             },
+                { "description",       required_argument, NULL, ARG_DESCRIPTION      },
+                { "slice",             required_argument, NULL, ARG_SLICE            },
+                { "remain-after-exit", no_argument,       NULL, 'r'                  },
+                { "send-sighup",       no_argument,       NULL, ARG_SEND_SIGHUP      },
+                { "host",              required_argument, NULL, 'H'                  },
+                { "machine",           required_argument, NULL, 'M'                  },
+                { "service-type",      required_argument, NULL, ARG_SERVICE_TYPE     },
+                { "uid",               required_argument, NULL, ARG_EXEC_USER        },
+                { "gid",               required_argument, NULL, ARG_EXEC_GROUP       },
+                { "nice",              required_argument, NULL, ARG_NICE             },
+                { "setenv",            required_argument, NULL, ARG_SETENV           },
+                { "property",          required_argument, NULL, 'p'                  },
+                { "on-active",         required_argument, NULL, ARG_ON_ACTIVE        },
+                { "on-boot",           required_argument, NULL, ARG_ON_BOOT          },
+                { "on-startup",        required_argument, NULL, ARG_ON_STARTUP       },
+                { "on-unit-active",    required_argument, NULL, ARG_ON_UNIT_ACTIVE   },
+                { "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   },
                 {},
         };
 
         int r, c;
+        CalendarSpec *spec = NULL;
 
         assert(argc >= 0);
         assert(argv);
@@ -207,6 +244,74 @@ 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);
+                                return r;
+                        }
+
+                        break;
+
+                case ARG_ON_BOOT:
+
+                        r = parse_sec(optarg, &arg_on_boot);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+
+                        break;
+
+                case ARG_ON_STARTUP:
+
+                        r = parse_sec(optarg, &arg_on_startup);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+
+                        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);
+                                return r;
+                        }
+
+                        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);
+                                return r;
+                        }
+
+                        break;
+
+                case ARG_ON_CALENDAR:
+
+                        r = calendar_spec_from_string(optarg, &spec);
+                        if (r < 0) {
+                                log_error("Invalid calendar spec: %s", optarg);
+                                return r;
+                        }
+                        free(spec);
+                        arg_on_calendar = optarg;
+                        break;
+
+                case ARG_TIMER_PROPERTY:
+
+                        if (strv_extend(&arg_timer_property, optarg) < 0)
+                                return log_oom();
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -214,7 +319,7 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached("Unhandled option");
                 }
 
-        if (optind >= argc) {
+        if ((optind >= argc) && (!arg_unit || !with_timer())) {
                 log_error("Command line to execute required.");
                 return -EINVAL;
         }
@@ -234,44 +339,34 @@ static int parse_argv(int argc, char *argv[]) {
                 return -EINVAL;
         }
 
+        if (arg_scope && with_timer()) {
+                log_error("Timer options are not supported in --scope mode.");
+                return -EINVAL;
+        }
+
+        if (arg_timer_property && !with_timer()) {
+                log_error("--timer-property= has no effect without any other timer options.");
+                return -EINVAL;
+        }
+
         return 1;
 }
 
-static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+static int transient_unit_set_properties(sd_bus_message *m, UnitType t) {
         char **i;
         int r;
 
-        assert(bus);
-        assert(name);
-        assert(ret);
-
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(m, "ss", name, "fail");
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_open_container(m, 'a', "(sv)");
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH(i, arg_property) {
+        STRV_FOREACH(i, t == UNIT_TIMER ? arg_timer_property : arg_property) {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
                         return r;
 
                 r = bus_append_unit_property_assignment(m, *i);
-                if (r < 0)
-                        return r;
+                if (r < 0) {
+                        r = sd_bus_message_append(m, "sv", 0);
+                        if (r < 0)
+                                return r;
+                }
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
@@ -294,152 +389,324 @@ static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bu
                         return r;
         }
 
-        if (arg_send_sighup) {
+        if (arg_send_sighup && t != UNIT_TIMER) {
                 r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup);
                 if (r < 0)
                         return r;
         }
 
-        *ret = m;
-        m = NULL;
-
         return 0;
 }
 
-static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) {
+static int transient_service_set_properties(sd_bus_message *m, char **argv) {
         int r;
 
-        assert(bus);
         assert(m);
 
-        r = sd_bus_message_close_container(m);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(m, "a(sa(sv))", 0);
+        r = transient_unit_set_properties(m, UNIT_SERVICE);
         if (r < 0)
                 return r;
 
-        return sd_bus_call(bus, m, 0, error, reply);
-}
-
-static int start_transient_service(
-                sd_bus *bus,
-                char **argv,
-                sd_bus_error *error) {
-
-        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        _cleanup_free_ char *name = NULL;
-        int r;
-
-        if (arg_unit) {
-                name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
-                if (!name)
-                        return log_oom();
-        } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0)
-                return log_oom();
-
-        r = message_start_transient_unit_new(bus, name, &m);
-        if (r < 0)
-                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 bus_log_create_error(r);
+                        return r;
         }
 
         if (arg_service_type) {
                 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
         }
 
         if (arg_exec_user) {
                 r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
         }
 
         if (arg_exec_group) {
                 r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
         }
 
         if (arg_nice_set) {
                 r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
         }
 
         if (!strv_isempty(arg_environment)) {
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_append(m, "s", "Environment");
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_open_container(m, 'v', "as");
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_append_strv(m, arg_environment);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
 
                 r = sd_bus_message_close_container(m);
                 if (r < 0)
-                        return bus_log_create_error(r);
+                        return r;
+        }
+
+        /* Exec container */
+        {
+                r = sd_bus_message_open_container(m, 'r', "sv");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(m, "s", "ExecStart");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_open_container(m, 'a', "(sasb)");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_open_container(m, 'r', "sasb");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(m, "s", argv[0]);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_strv(m, argv);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(m, "b", false);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int transient_timer_set_properties(sd_bus_message *m) {
+        int r;
+
+        assert(m);
+
+        r = transient_unit_set_properties(m, UNIT_TIMER);
+        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;
         }
 
-        r = sd_bus_message_open_container(m, 'r', "sv");
+        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 0;
+}
+
+static int transient_scope_set_properties(sd_bus_message *m) {
+        int r;
+
+        assert(m);
+
+        r = transient_unit_set_properties(m, UNIT_SCOPE);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int start_transient_service(
+                sd_bus *bus,
+                char **argv,
+                sd_bus_error *error) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        _cleanup_free_ char *service = NULL;
+        int r;
+
+        assert(bus);
+        assert(argv);
+
+        if (arg_unit) {
+                service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
+                if (!service)
+                        return log_oom();
+        } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
+                return log_oom();
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(m, "s", "ExecStart");
+        /* name and mode */
+        r = sd_bus_message_append(m, "ss", service, "fail");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+        /* properties */
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(m, 'a', "(sasb)");
+        r = transient_service_set_properties(m, argv);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(m, 'r', "sasb");
+        r = sd_bus_message_close_container(m);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(m, "s", argv[0]);
+        /* aux */
+        r = sd_bus_message_append(m, "a(sa(sv))", 0);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append_strv(m, argv);
+        /* send dbus */
+        r = sd_bus_call(bus, m, 0, error, NULL);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(m, "b", false);
+        log_info("Running as unit %s.", service);
+
+        return 0;
+}
+
+static int start_transient_timer(
+                sd_bus *bus,
+                char **argv,
+                sd_bus_error *error) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        _cleanup_free_ char *timer = NULL, *service = NULL;
+        int r;
+
+        assert(bus);
+        assert(argv);
+
+        if (arg_unit) {
+                switch(unit_name_to_type(arg_unit)) {
+                case UNIT_SERVICE:
+                        service = strdup(arg_unit);
+                        timer = unit_name_change_suffix(service, ".timer");
+                        if (!timer)
+                                return log_oom();
+                        break;
+
+                case UNIT_TIMER:
+                        timer = strdup(arg_unit);
+                        service = unit_name_change_suffix(timer, ".service");
+                        if (!service)
+                                return log_oom();
+                        break;
+
+                default:
+                        service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
+                        if (!service)
+                                return log_oom();
+
+                        timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer");
+                        if (!timer)
+                                return log_oom();
+
+                        break;
+                }
+        } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
+                   (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
+                return log_oom();
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_close_container(m);
+        /* name and mode */
+        r = sd_bus_message_append(m, "ss", timer, "fail");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_close_container(m);
+        /* properties */
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_close_container(m);
+        r = transient_timer_set_properties(m);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -447,11 +714,52 @@ static int start_transient_service(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = message_start_transient_unit_send(bus, m, error, NULL);
+        if (argv[0]) {
+                r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_open_container(m, 'r', "sa(sv)");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", service);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_open_container(m, 'a', "(sv)");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = transient_service_set_properties(m, argv);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        } else {
+                r = sd_bus_message_append(m, "a(sa(sv))", 0);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        /* send dbus */
+        r = sd_bus_call(bus, m, 0, error, NULL);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        log_info("Running as unit %s.", name);
+        log_info("Running as unit %s.", timer);
+        if (argv[0])
+                log_info("Will run as unit %s.", service);
 
         return 0;
 }
@@ -462,28 +770,55 @@ static int start_transient_scope(
                 sd_bus_error *error) {
 
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
-        _cleanup_free_ char *name = NULL;
+        _cleanup_free_ char *scope = NULL;
         _cleanup_strv_free_ char **env = NULL, **user_env = NULL;
         int r;
 
         assert(bus);
+        assert(argv);
 
         if (arg_unit) {
-                name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
-                if (!name)
+                scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope");
+                if (!scope)
                         return log_oom();
-        } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0)
+        } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
                 return log_oom();
 
-        r = message_start_transient_unit_new(bus, name, &m);
+        r = sd_bus_message_new_method_call(
+                bus,
+                &m,
+                "org.freedesktop.systemd1",
+                "/org/freedesktop/systemd1",
+                "org.freedesktop.systemd1.Manager",
+                "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid());
+        /* name and mode */
+        r = sd_bus_message_append(m, "ss", scope, "fail");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* properties */
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = transient_scope_set_properties(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* aux */
+        r = sd_bus_message_append(m, "a(sa(sv))", 0);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = message_start_transient_unit_send(bus, m, error, NULL);
+        /* send dbus */
+        r = sd_bus_call(bus, m, 0, error, NULL);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -541,7 +876,7 @@ static int start_transient_scope(
         if (!env)
                 return log_oom();
 
-        log_info("Running as unit %s.", name);
+        log_info("Running as unit %s.", scope);
 
         execvpe(argv[0], argv, env);
         log_error_errno(errno, "Failed to execute: %m");
@@ -561,14 +896,16 @@ int main(int argc, char* argv[]) {
         if (r <= 0)
                 goto finish;
 
-        r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
-        if (r < 0) {
-                log_error_errno(r, "Failed to find executable %s%s: %m",
-                                argv[optind],
-                                arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
-                goto finish;
+        if (argc > optind) {
+                r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to find executable %s%s: %m",
+                                        argv[optind],
+                                        arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system");
+                        goto finish;
+                }
+                argv[optind] = command;
         }
-        argv[optind] = command;
 
         if (!arg_description) {
                 description = strv_join(argv + optind, " ");
@@ -577,6 +914,16 @@ int main(int argc, char* argv[]) {
                         goto finish;
                 }
 
+                if (arg_unit && isempty(description)) {
+                        free(description);
+                        description = strdup(arg_unit);
+
+                        if (!description) {
+                                r = log_oom();
+                                goto finish;
+                        }
+                }
+
                 arg_description = description;
         }
 
@@ -588,12 +935,15 @@ int main(int argc, char* argv[]) {
 
         if (arg_scope)
                 r = start_transient_scope(bus, argv + optind, &error);
+        else if (with_timer())
+                r = start_transient_timer(bus, argv + optind, &error);
         else
                 r = start_transient_service(bus, argv + optind, &error);
 
 finish:
         strv_free(arg_environment);
         strv_free(arg_property);
+        strv_free(arg_timer_property);
 
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }



More information about the systemd-commits mailing list