[systemd-devel] [PATCH 3/3] run: introduce timer support option

WaLyong Cho walyong.cho at samsung.com
Mon Oct 6 22:20:10 PDT 2014


If systemd-run is called with timer option, then systemd-run call
NewTransientUnit with service unit. And also call StartTransientUnit
with timer unit which has same name with the service. So actually, two
method call is coming and two transient unit is generated. One is
service and the other is timer.

Supported timer options are --after=, --after-boot=, --after-startup=,
--after-active=, --after-inactive=, --calendar=, --accuracy= and
--wake-system. Each option corresponding with OnActiveSec=,
OnBootSec=, OnStartupSec=, OnUnitActiveSec=, OnUnitInactiveSec=,
AccuracySec= and AccuracySec= of timer respectively.
---
 man/systemd-run.xml |  70 +++++++++++++
 src/run/run.c       | 294 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 331 insertions(+), 33 deletions(-)

diff --git a/man/systemd-run.xml b/man/systemd-run.xml
index 0c9d13d..452721c 100644
--- a/man/systemd-run.xml
+++ b/man/systemd-run.xml
@@ -210,6 +210,64 @@ 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>--after=</option></term>
+        <term><option>--after-boot=</option></term>
+        <term><option>--after-startup=</option></term>
+        <term><option>--after-active=</option></term>
+        <term><option>--after-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
+        option has no effect in conjunction with
+        <option>--scope</option>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--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>--accuracy=</option></term>
+
+        <listitem><para>Specify the accuracy the timer shall elapse
+        with. Also see <varname>AccuracySec=</varname> in
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+        option only has effect with <option>--after=</option>,
+        <option>--after-boot=</option>, <option>--after-startup=</option>,
+        <option>--after-active=</option>, <option>--after-inactive=</option>
+        or <option>--calendar=</option>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--wake-system</option></term>
+
+        <listitem><para>Elapsing timer will cause the system to resume
+        from suspend, should it be suspended and if the system supports
+        this. Also see <varname>WakeSystem=</varname> in
+        <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+        option only has effect with <option>--after=</option>,
+        <option>--after-boot=</option>, <option>--after-startup=</option>,
+        <option>--after-active=</option>, <option>--after-inactive=</option>
+        or <option>--calendar=</option>.</para>
+        </listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
@@ -250,6 +308,17 @@ 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 10 seconds.</para>
+
+    <programlisting># date; systemd-run --after=10 --accuracy=100ms touch /tmp/hello
+Tue Oct  7 14:00:37 KST 2014
+Will running as unit run-115.service.
+Running as unit run-115.timer.
+# journalctl -u run-115.service
+-- Logs begin at Sat 2014-10-04 13:54:49 KST, end at Tue 2014-10-07 14:00:47 KST. --
+Oct 07 14:00:47 container systemd[1]: Starting /bin/touch /tmp/hello...
+Oct 07 14:00:47 container systemd[1]: Started /bin/touch /tmp/hello.</programlisting>
   </refsect1>
 
   <refsect1>
@@ -263,6 +332,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/run/run.c b/src/run/run.c
index e3b6293..50c0d5d 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,6 +48,15 @@ static int arg_nice = 0;
 static bool arg_nice_set = false;
 static char **arg_environment = NULL;
 static char **arg_property = NULL;
+static bool with_timer = false;
+static char *arg_after = NULL;
+static char *arg_after_boot = NULL;
+static char *arg_after_startup = NULL;
+static char *arg_after_active = NULL;
+static char *arg_after_inactive = NULL;
+static char *arg_calendar = NULL;
+static char *arg_accuracy = NULL;
+static bool arg_wake_system = false;
 
 static void help(void) {
         printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n"
@@ -67,7 +77,16 @@ static void help(void) {
                "     --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",
+               "     --setenv=NAME=VALUE    Set environment\n\n"
+               "Timer options:\n\n"
+               "     --after=SEC            Run after seconds\n"
+               "     --after-boot=SEC       Run after seconds from machine was booted up\n"
+               "     --after-startup=SEC    Run after seconds from systemd was first started\n"
+               "     --after-active=SEC     Run after seconds from the last activation\n"
+               "     --after-inactive=SEC   Run after seconds from the last deactivation\n"
+               "     --calendar=SPEC        Realtime timer\n"
+               "     --accuracy=SEC         Accuracy seconds of delay\n"
+               "     --wake-system          System resume from suspend\n",
                program_invocation_short_name);
 }
 
@@ -86,32 +105,50 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_EXEC_GROUP,
                 ARG_SERVICE_TYPE,
                 ARG_NICE,
-                ARG_SETENV
+                ARG_SETENV,
+                ARG_AFTER,
+                ARG_AFTER_BOOT,
+                ARG_AFTER_STARTUP,
+                ARG_AFTER_ACTIVE,
+                ARG_AFTER_INACTIVE,
+                ARG_CALENDAR,
+                ARG_ACCURACY,
+                ARG_WAKE_SYSTEM
         };
 
         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'                },
+                { "after",             required_argument, NULL, ARG_AFTER          },
+                { "after-boot",        required_argument, NULL, ARG_AFTER_BOOT     },
+                { "after-startup",     required_argument, NULL, ARG_AFTER_STARTUP  },
+                { "after-active",      required_argument, NULL, ARG_AFTER_ACTIVE   },
+                { "after-inactive",    required_argument, NULL, ARG_AFTER_INACTIVE },
+                { "calendar",          required_argument, NULL, ARG_CALENDAR       },
+                { "accuracy",          required_argument, NULL, ARG_ACCURACY       },
+                { "wake-system",       no_argument,       NULL, ARG_WAKE_SYSTEM    },
                 {},
         };
 
         int r, c;
+        usec_t u = 0;
+        CalendarSpec *spec = NULL;
 
         assert(argc >= 0);
         assert(argv);
@@ -207,6 +244,80 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_AFTER:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_after = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_AFTER_BOOT:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_after_boot = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_AFTER_STARTUP:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_after_startup = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_AFTER_ACTIVE:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_after_active = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_AFTER_INACTIVE:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_after_inactive = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_CALENDAR:
+                        r = calendar_spec_from_string(optarg, &spec);
+                        if (r < 0) {
+                                log_error("Invalid calendar spec: %s", optarg);
+                                return r;
+                        }
+                        free(spec);
+                        arg_calendar = optarg;
+                        with_timer = true;
+                        break;
+
+                case ARG_ACCURACY:
+                        r = parse_sec(optarg, &u);
+                        if (r < 0) {
+                                log_error("Failed to parse timer value: %s", optarg);
+                                return r;
+                        }
+                        arg_accuracy = optarg;
+                        break;
+
+                case ARG_WAKE_SYSTEM:
+                        arg_wake_system = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -234,11 +345,20 @@ 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 (!with_timer && (arg_accuracy || arg_wake_system))
+                log_info("--accuracy and --wake-system have no effect alone, ignoring.");
+
         return 1;
 }
 
-static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
+static int message_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) {
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        UnitType t;
         char **i;
         int r;
 
@@ -246,13 +366,37 @@ static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bu
         assert(name);
         assert(ret);
 
-        r = sd_bus_message_new_method_call(
+        t = unit_name_to_type(name);
+        if (t < 0)
+                return -EINVAL;
+
+        switch(t) {
+
+        case UNIT_SERVICE:
+                r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        with_timer ? "NewTransientUnit" : "StartTransientUnit");
+                break;
+
+        case UNIT_TIMER:
+        case UNIT_SCOPE:
+                r = sd_bus_message_new_method_call(
                         bus,
                         &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
                         "StartTransientUnit");
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
         if (r < 0)
                 return r;
 
@@ -306,7 +450,10 @@ static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bu
         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 message_transient_unit_send(sd_bus *bus,
+                                       sd_bus_message *m,
+                                       sd_bus_error *error,
+                                       sd_bus_message **reply) {
         int r;
 
         assert(bus);
@@ -323,7 +470,85 @@ static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_
         return sd_bus_call(bus, m, 0, error, reply);
 }
 
-static int start_transient_service(
+static int transient_timer(
+                sd_bus *bus,
+                char **argv,
+                sd_bus_error *error) {
+
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+        _cleanup_free_ char *name = NULL, *service = NULL;
+        int r;
+
+        if (arg_unit) {
+                service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service");
+                name = unit_name_change_suffix(service, ".timer");
+                if (!name)
+                        return log_oom();
+        } else if (asprintf(&name, "run-"PID_FMT".timer", getpid()) < 0)
+                return log_oom();
+
+        r = message_transient_unit_new(bus, name, &m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        if (arg_after) {
+                r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "s", arg_after);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_after_boot) {
+                r = sd_bus_message_append(m, "(sv)", "OnBootSec", "s", arg_after_boot);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_after_startup) {
+                r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "s", arg_after_startup);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_after_active) {
+                r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "s", arg_after_active);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_after_inactive) {
+                r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "s", arg_after_inactive);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_calendar) {
+                r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_calendar);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_accuracy) {
+                r = sd_bus_message_append(m, "(sv)", "AccuracySec", "s", arg_accuracy);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        if (arg_wake_system) {
+                r = sd_bus_message_append(m, "(sv)", "WakeSystem", "b", arg_wake_system);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = message_transient_unit_send(bus, m, error, NULL);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        log_info("Running as unit %s.", name);
+
+        return 0;
+}
+
+static int transient_service(
                 sd_bus *bus,
                 char **argv,
                 sd_bus_error *error) {
@@ -339,7 +564,7 @@ static int start_transient_service(
         } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0)
                 return log_oom();
 
-        r = message_start_transient_unit_new(bus, name, &m);
+        r = message_transient_unit_new(bus, name, &m);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -447,16 +672,16 @@ static int start_transient_service(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = message_start_transient_unit_send(bus, m, error, NULL);
+        r = message_transient_unit_send(bus, m, error, NULL);
         if (r < 0)
                 return bus_log_create_error(r);
 
-        log_info("Running as unit %s.", name);
+        log_info("%s as unit %s.", with_timer ? "Will running" : "Running", name);
 
         return 0;
 }
 
-static int start_transient_scope(
+static int transient_scope(
                 sd_bus *bus,
                 char **argv,
                 sd_bus_error *error) {
@@ -475,7 +700,7 @@ static int start_transient_scope(
         } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0)
                 return log_oom();
 
-        r = message_start_transient_unit_new(bus, name, &m);
+        r = message_transient_unit_new(bus, name, &m);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -483,7 +708,7 @@ static int start_transient_scope(
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = message_start_transient_unit_send(bus, m, error, NULL);
+        r = message_transient_unit_send(bus, m, error, NULL);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -597,9 +822,12 @@ int main(int argc, char* argv[]) {
         }
 
         if (arg_scope)
-                r = start_transient_scope(bus, argv + optind, &error);
-        else
-                r = start_transient_service(bus, argv + optind, &error);
+                r = transient_scope(bus, argv + optind, &error);
+        else {
+                r = transient_service(bus, argv + optind, &error);
+                if (r == 0 && with_timer)
+                        r = transient_timer(bus, argv + optind, &error);
+        }
 
 finish:
         strv_free(arg_environment);
-- 
1.9.3



More information about the systemd-devel mailing list