[systemd-devel] [PATCH] timer: allow user to control activation time of cron-like timers

Alexander Bashmakov pkunk.ab at gmail.com
Sun May 11 08:53:55 PDT 2014


Issue was rised in this thread:
https://mailman.archlinux.org/pipermail/arch-general/2014-May/036162.html

Disclaimer:
I almost have no expereince in C.
So this patch can contain some silly mistakes. But it 'works for me'.
Please consider it as RFC.

-8<-------
Cron-like timers are useful for maintenance tasks
and already used in some distros by default.
But midnight is not best time for this.
Just add a new option allowing user to specify
activation time (and date) for such timers globally.

Signed-off-by: Alexander Bashmakov <pkunk.ab at gmail.com>
---
 man/systemd-system.conf.xml  | 19 +++++++++++++
 man/systemd.time.xml         |  6 ++++-
 src/core/load-fragment.c     |  2 +-
 src/core/main.c              |  7 +++++
 src/core/manager.c           | 42 +++++++++++++++++++++++++++++
 src/core/manager.h           |  4 +++
 src/core/system.conf         |  1 +
 src/core/user.conf           |  1 +
 src/shared/calendarspec.c    | 64 +++++++++++++++++++++++++++++++-------------
 src/shared/calendarspec.h    |  4 ++-
 src/test/test-calendarspec.c | 20 +++++++++-----
 11 files changed, 141 insertions(+), 29 deletions(-)

diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index 3814bd2..e38e70a 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -302,6 +302,25 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><varname>DefaultTimerEventTime=</varname></term>
+
+                                <listitem><para>Sets the default
+                                elapsing time for cron-like timer units.
+                                This controls
+                                the global default for
+                                timer units with
+                                <varname>OnCalendar=</varname>
+                                set on
+                                <literal>hourly</literal>, <literal>daily</literal>,
+                                <literal>monthly</literal>, <literal>yearly</literal>
+                                or <literal>weekly</literal>.
+                                Only relevant fields are used.
+                                See
+                                <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                                for details.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><varname>DefaultCPUQuotaPeriodSec=</varname></term>
 
                                 <listitem><para>Sets the default CPU
diff --git a/man/systemd.time.xml b/man/systemd.time.xml
index 0706cdf..776b6bf 100644
--- a/man/systemd.time.xml
+++ b/man/systemd.time.xml
@@ -248,7 +248,11 @@
                 <literal>*-*-* *:00:00</literal>, <literal>*-*-*
                 00:00:00</literal>, <literal>*-*-01 00:00:00</literal> and
                 <literal>Mon *-*-* 00:00:00</literal>,
-                respectively.</para>
+                respectively.
+                Note that exact time for these expressions depends on
+                <varname>DefaultTimerEventTime=</varname>
+                setting.
+                </para>
 
                 <para>Examples for valid timestamps and their
                 normalized form:</para>
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 14c194b..7f9d71f 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1273,7 +1273,7 @@ int config_parse_timer(const char *unit,
         }
 
         if (b == TIMER_CALENDAR) {
-                if (calendar_spec_from_string(rvalue, &c) < 0) {
+                if (calendar_spec_from_string(rvalue, &c, t->meta.manager->default_timer_event_time) < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
                                    "Failed to parse calendar specification, ignoring: %s",
                                    rvalue);
diff --git a/src/core/main.c b/src/core/main.c
index c1b0ffd..78c9711 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -109,6 +109,7 @@ static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
 static uint64_t arg_capability_bounding_set_drop = 0;
 static nsec_t arg_timer_slack_nsec = (nsec_t) -1;
 static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
+static char* arg_default_timer_event_time = NULL;
 static usec_t arg_default_cpu_quota_period_usec = 100 * USEC_PER_MSEC;
 static Set* arg_syscall_archs = NULL;
 static FILE* arg_serialization = NULL;
@@ -684,6 +685,7 @@ static int parse_config_file(void) {
 #endif
                 { "Manager", "TimerSlackNSec",            config_parse_nsec,             0, &arg_timer_slack_nsec                  },
                 { "Manager", "DefaultTimerAccuracySec",   config_parse_sec,              0, &arg_default_timer_accuracy_usec       },
+                { "Manager", "DefaultTimerEventTime",     config_parse_string,           0, &arg_default_timer_event_time          },
                 { "Manager", "DefaultCPUQuotaPeriodSec",  config_parse_sec,              0, &arg_default_cpu_quota_period_usec     },
                 { "Manager", "DefaultStandardOutput",     config_parse_output,           0, &arg_default_std_output                },
                 { "Manager", "DefaultStandardError",      config_parse_output,           0, &arg_default_std_error                 },
@@ -1619,6 +1621,11 @@ int main(int argc, char *argv[]) {
 
         m->confirm_spawn = arg_confirm_spawn;
         m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec;
+
+        r = manager_set_default_timer_event_time(m, arg_default_timer_event_time);
+        if (r < 0)
+            goto finish;
+
         m->default_cpu_quota_period_usec = arg_default_cpu_quota_period_usec;
         m->default_std_output = arg_default_std_output;
         m->default_std_error = arg_default_std_error;
diff --git a/src/core/manager.c b/src/core/manager.c
index 5772f40..b2d31eb 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -429,6 +429,11 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
         m->running_as = running_as;
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+
+        r = manager_set_default_timer_event_time(m, NULL);
+        if (r < 0)
+                goto fail;
+
         m->default_cpu_quota_period_usec = 100 * USEC_PER_MSEC;
 
         m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
@@ -821,6 +826,8 @@ void manager_free(Manager *m) {
         hashmap_free(m->cgroup_unit);
         set_free_free(m->unit_path_cache);
 
+        calendar_spec_free(m->default_timer_event_time);
+
         free(m->switch_root);
         free(m->switch_root_init);
 
@@ -2720,6 +2727,41 @@ int manager_environment_add(Manager *m, char **minus, char **plus) {
         return 0;
 }
 
+int manager_set_default_timer_event_time(Manager *m, const char *default_event_time) {
+        int r;
+
+        assert(m);
+        CalendarSpec *c;
+
+        if (isempty(default_event_time)
+                || strcaseeq(default_event_time, "hourly")
+                || strcaseeq(default_event_time, "daily")
+                || strcaseeq(default_event_time, "weekly")
+                || strcaseeq(default_event_time, "monthly")
+                || strcaseeq(default_event_time, "anually")
+                || strcaseeq(default_event_time, "yearly")) {
+
+                c = new0(CalendarSpec, 1);
+                if (!c)
+                        return -ENOMEM;
+
+                r = calendar_spec_default(c);
+                if (r < 0)
+                        goto fail;
+        } else {
+                r = calendar_spec_from_string(default_event_time, &c, NULL);
+                if (r < 0)
+                        goto fail;
+        }
+
+        m->default_timer_event_time = c;
+        return 0;
+
+fail:
+        calendar_spec_free(c);
+        return r;
+}
+
 int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
         int i;
 
diff --git a/src/core/manager.h b/src/core/manager.h
index a3de351..3dc570b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -29,6 +29,7 @@
 #include "sd-event.h"
 #include "fdset.h"
 #include "cgroup-util.h"
+#include "calendarspec.h"
 
 /* Enforce upper limit how many names we allow */
 #define MANAGER_MAX_NAMES 131072 /* 128K */
@@ -244,6 +245,8 @@ struct Manager {
 
         usec_t default_timer_accuracy_usec;
 
+        CalendarSpec *default_timer_event_time;
+
         struct rlimit *rlimit[_RLIMIT_MAX];
 
         /* non-zero if we are reloading or reexecuting, */
@@ -301,6 +304,7 @@ void manager_clear_jobs(Manager *m);
 unsigned manager_dispatch_load_queue(Manager *m);
 
 int manager_environment_add(Manager *m, char **minus, char **plus);
+int manager_set_default_timer_event_time(Manager *m, const char *default_event_time);
 int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
 
 int manager_loop(Manager *m);
diff --git a/src/core/system.conf b/src/core/system.conf
index 4d775fa..3ef8b1b 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -24,6 +24,7 @@
 #SystemCallArchitectures=
 #TimerSlackNSec=
 #DefaultTimerAccuracySec=1min
+#DefaultTimerEventTime=
 #DefaultCPUQuotaPeriodSec=100ms
 #DefaultStandardOutput=journal
 #DefaultStandardError=inherit
diff --git a/src/core/user.conf b/src/core/user.conf
index 8c7ecde..c108722 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -15,6 +15,7 @@
 #SystemCallArchitectures=
 #TimerSlackNSec=
 #DefaultTimerAccuracySec=1min
+#DefaultTimerEventTime=
 #DefaultStandardOutput=inherit
 #DefaultStandardError=inherit
 #DefaultTimeoutStartSec=90s
diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
index 69b7427..7720cf6 100644
--- a/src/shared/calendarspec.c
+++ b/src/shared/calendarspec.c
@@ -639,7 +639,7 @@ fail:
         return r;
 }
 
-int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
+int calendar_spec_from_string(const char *p, CalendarSpec **spec, const CalendarSpec *d) {
         CalendarSpec *c;
         int r;
 
@@ -654,66 +654,66 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 return -ENOMEM;
 
         if (strcaseeq(p, "hourly")) {
-                r = const_chain(0, &c->minute);
+                r = const_chain(d->minute->value, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(d->second->value, &c->second);
                 if (r < 0)
                         goto fail;
 
         } else if (strcaseeq(p, "daily")) {
-                r = const_chain(0, &c->hour);
+                r = const_chain(d->hour->value, &c->hour);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->minute);
+                r = const_chain(d->minute->value, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(d->second->value, &c->second);
                 if (r < 0)
                         goto fail;
 
         } else if (strcaseeq(p, "monthly")) {
-                r = const_chain(1, &c->day);
+                r = const_chain(d->day->value, &c->day);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->hour);
+                r = const_chain(d->hour->value, &c->hour);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->minute);
+                r = const_chain(d->minute->value, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(d->second->value, &c->second);
                 if (r < 0)
                         goto fail;
 
         } else if (strcaseeq(p, "anually") || strcaseeq(p, "yearly")) {
-                r = const_chain(1, &c->month);
+                r = const_chain(d->month->value, &c->month);
                 if (r < 0)
                         goto fail;
-                r = const_chain(1, &c->day);
+                r = const_chain(d->day->value, &c->day);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->hour);
+                r = const_chain(d->hour->value, &c->hour);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->minute);
+                r = const_chain(d->minute->value, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(d->second->value, &c->second);
                 if (r < 0)
                         goto fail;
 
         } else if (strcaseeq(p, "weekly")) {
 
-                c->weekdays_bits = 1;
+                c->weekdays_bits = d->weekdays_bits;
 
-                r = const_chain(0, &c->hour);
+                r = const_chain(d->hour->value, &c->hour);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->minute);
+                r = const_chain(d->minute->value, &c->minute);
                 if (r < 0)
                         goto fail;
-                r = const_chain(0, &c->second);
+                r = const_chain(d->second->value, &c->second);
                 if (r < 0)
                         goto fail;
 
@@ -919,6 +919,32 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
         }
 }
 
+int calendar_spec_default(CalendarSpec *c) {
+        int r;
+
+        assert(c);
+
+        c->weekdays_bits = 1;
+
+        r = const_chain(1, &c->month);
+        if (r < 0)
+                return r;
+        r = const_chain(1, &c->day);
+        if (r < 0)
+                return r;
+        r = const_chain(0, &c->hour);
+        if (r < 0)
+                return r;
+        r = const_chain(0, &c->minute);
+        if (r < 0)
+                return r;
+        r = const_chain(0, &c->second);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
         struct tm tm;
         time_t t;
diff --git a/src/shared/calendarspec.h b/src/shared/calendarspec.h
index 7baf318..7415455 100644
--- a/src/shared/calendarspec.h
+++ b/src/shared/calendarspec.h
@@ -52,6 +52,8 @@ int calendar_spec_normalize(CalendarSpec *spec);
 bool calendar_spec_valid(CalendarSpec *spec);
 
 int calendar_spec_to_string(const CalendarSpec *spec, char **p);
-int calendar_spec_from_string(const char *p, CalendarSpec **spec);
+int calendar_spec_from_string(const char *p, CalendarSpec **spec, const CalendarSpec *d);
+
+int calendar_spec_default(CalendarSpec *c);
 
 int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index 21b0024..4660032 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -25,13 +25,18 @@
 #include "util.h"
 
 static void test_one(const char *input, const char *output) {
-        CalendarSpec *c;
+        CalendarSpec *c, *d;
         _cleanup_free_ char *p = NULL, *q = NULL;
         usec_t u;
         char buf[FORMAT_TIMESTAMP_MAX];
         int r;
 
-        assert_se(calendar_spec_from_string(input, &c) >= 0);
+        d = new0(CalendarSpec, 1);
+        assert_se(d);
+        r = calendar_spec_default(d);
+        assert_se(r >= 0);
+
+        assert_se(calendar_spec_from_string(input, &c, d) >= 0);
 
         assert_se(calendar_spec_to_string(c, &p) >= 0);
         printf("\"%s\" → \"%s\"\n", input, p);
@@ -43,9 +48,10 @@ static void test_one(const char *input, const char *output) {
         printf("Next: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
         calendar_spec_free(c);
 
-        assert_se(calendar_spec_from_string(p, &c) >= 0);
+        assert_se(calendar_spec_from_string(p, &c, d) >= 0);
         assert_se(calendar_spec_to_string(c, &q) >= 0);
         calendar_spec_free(c);
+        calendar_spec_free(d);
 
         assert_se(streq(q, p));
 }
@@ -79,10 +85,10 @@ int main(int argc, char* argv[]) {
         test_one("weekly", "Mon *-*-* 00:00:00");
         test_one("*:2/3", "*-*-* *:02/3:00");
 
-        assert_se(calendar_spec_from_string("test", &c) < 0);
-        assert_se(calendar_spec_from_string("", &c) < 0);
-        assert_se(calendar_spec_from_string("7", &c) < 0);
-        assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
+        assert_se(calendar_spec_from_string("test", &c, NULL) < 0);
+        assert_se(calendar_spec_from_string("", &c, NULL) < 0);
+        assert_se(calendar_spec_from_string("7", &c, NULL) < 0);
+        assert_se(calendar_spec_from_string("121212:1:2", &c, NULL) < 0);
 
         return 0;
 }
-- 
1.9.2



More information about the systemd-devel mailing list