From 7c123d49fc215a87e29b28cc7f86d5197889c83f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Nov 2017 10:32:48 +0100 Subject: [PATCH 1/5] calendarspec: rework destructor to return its own type Let's make it more alike most of our other destructors. --- src/basic/calendarspec.c | 6 +++--- src/basic/calendarspec.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 8e406aa643..6b3a9a4441 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -51,10 +51,10 @@ static void free_chain(CalendarComponent *c) { } } -void calendar_spec_free(CalendarSpec *c) { +CalendarSpec* calendar_spec_free(CalendarSpec *c) { if (!c) - return; + return NULL; free_chain(c->year); free_chain(c->month); @@ -64,7 +64,7 @@ void calendar_spec_free(CalendarSpec *c) { free_chain(c->microsecond); free(c->timezone); - free(c); + return mfree(c); } static int component_compare(const void *_a, const void *_b) { diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index bd87794bc4..d2a19dd7dd 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -52,7 +52,7 @@ typedef struct CalendarSpec { CalendarComponent *microsecond; } CalendarSpec; -void calendar_spec_free(CalendarSpec *c); +CalendarSpec* calendar_spec_free(CalendarSpec *c); int calendar_spec_normalize(CalendarSpec *spec); bool calendar_spec_valid(CalendarSpec *spec); From 9a9a4f10e96f602cb7c19bbf95f7aa8ce632dad3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Nov 2017 10:52:20 +0100 Subject: [PATCH 2/5] util: add new helper in_utc_timezone() As the name suggests it checks whether we are running in an UTC timezone. --- src/basic/time-util.c | 6 ++++++ src/basic/time-util.h | 2 ++ src/test/test-time-util.c | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 6bf3c8db1d..f5af3281a1 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1457,3 +1457,9 @@ usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { /* x lies in the past */ return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); } + +bool in_utc_timezone(void) { + tzset(); + + return timezone == 0 && daylight == 0; +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index f600bc3cbb..dc4a159310 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -156,6 +156,8 @@ struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); unsigned long usec_to_jiffies(usec_t usec); +bool in_utc_timezone(void); + static inline usec_t usec_add(usec_t a, usec_t b) { usec_t c; diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e639ded48c..ebf85fcc7c 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -383,6 +383,22 @@ static void test_usec_shift_clock(void) { } } +static void test_in_utc_timezone(void) { + assert_se(setenv("TZ", ":UTC", 1) >= 0); + assert_se(in_utc_timezone()); + assert_se(streq(tzname[0], "UTC")); + assert_se(streq(tzname[1], "UTC")); + assert_se(timezone == 0); + assert_se(daylight == 0); + + assert_se(setenv("TZ", "Europe/Berlin", 1) >= 0); + assert_se(!in_utc_timezone()); + assert_se(streq(tzname[0], "CET")); + assert_se(streq(tzname[1], "CEST")); + + assert_se(unsetenv("TZ") >= 0); +} + int main(int argc, char *argv[]) { uintmax_t x; @@ -409,6 +425,7 @@ int main(int argc, char *argv[]) { test_format_timestamp_utc(); test_dual_timestamp_deserialize(); test_usec_shift_clock(); + test_in_utc_timezone(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); From 6d86f4bd11c2cfaadc924a8a1ac04b0b463b8036 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Nov 2017 10:33:22 +0100 Subject: [PATCH 3/5] analyze: add new "calendar" command This little new command can parse, validate, normalize calendar events, and calculate when they will elapse next. This should be useful for anyone writing calendar events and who'd like to validate the expression before running them as timer units. --- man/systemd-analyze.xml | 12 +++++++ man/systemd.time.xml | 7 ++++- src/analyze/analyze.c | 68 ++++++++++++++++++++++++++++++++++++++++ src/basic/calendarspec.h | 2 ++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 956790c50c..876f96d559 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -125,6 +125,12 @@ verify FILES + + systemd-analyze + OPTIONS + calendar + SPECS + @@ -220,6 +226,12 @@ All units files present in the directories containing the command line arguments will be used in preference to the other paths. + systemd-analyze calendar will parse and normalize repetitive calendar time events, and + will calculate when they will elapse next. This takes the same input as the OnCalendar= setting + in systemd.timer5, following the + syntax described in + systemd.time7. + If no command is passed, systemd-analyze time is implied. diff --git a/man/systemd.time.xml b/man/systemd.time.xml index a2fff2d101..6cb32f13b7 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -302,6 +302,10 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 systemd.timer5 for details. + Use the calendar command of + systemd-analyze1 to validate + and normalize calendar time specifications for testing purposes. The tool also calculates when a specified + calendar event would elapse next. @@ -311,7 +315,8 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 journalctl1, systemd.timer5, systemd.unit5, - systemd.directives7 + systemd.directives7, + systemd-analyze1 diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 913aa35016..5229b3a082 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -31,6 +31,7 @@ #include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "calendarspec.h" #include "glob-util.h" #include "hashmap.h" #include "locale-util.h" @@ -1395,6 +1396,70 @@ static int dump_syscall_filters(char** names) { } #endif +static int test_calendar(char **args) { + int ret = 0, r; + char **p; + usec_t n; + + if (strv_isempty(args)) { + log_error("Expected at least one calendar specification string as argument."); + return -EINVAL; + } + + n = now(CLOCK_REALTIME); + + STRV_FOREACH(p, args) { + _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; + _cleanup_free_ char *t = NULL; + usec_t next; + + r = calendar_spec_from_string(*p, &spec); + if (r < 0) { + ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p); + continue; + } + + r = calendar_spec_normalize(spec); + if (r < 0) { + ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p); + continue; + } + + r = calendar_spec_to_string(spec, &t); + if (r < 0) { + ret = log_error_errno(r, "Failed to fomat calendar specification '%s': %m", *p); + continue; + } + + if (!streq(t, *p)) + printf(" Original form: %s\n", *p); + + printf("Normalized form: %s\n", t); + + r = calendar_spec_next_usec(spec, n, &next); + if (r == -ENOENT) + printf(" Next elapse: never\n"); + else if (r < 0) { + ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p); + continue; + } else { + char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)]; + + printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next)); + + if (!in_utc_timezone()) + printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next)); + + printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next)); + } + + if (*(p+1)) + putchar('\n'); + } + + return ret; +} + static void help(void) { pager_open(arg_no_pager, false); @@ -1429,6 +1494,7 @@ static void help(void) { " dump Output state serialization of service manager\n" " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " verify FILE... Check unit files for correctness\n" + " calendar SPEC... Validate repetitive calendar time events\n" , program_invocation_short_name); /* When updating this list, including descriptions, apply @@ -1618,6 +1684,8 @@ int main(int argc, char *argv[]) { r = get_log_target(bus, argv+optind+1); else if (streq(argv[optind], "syscall-filter")) r = dump_syscall_filters(argv+optind+1); + else if (streq(argv[optind], "calendar")) + r = test_calendar(argv+optind+1); else log_error("Unknown operation '%s'.", argv[optind]); } diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index d2a19dd7dd..124f7f5880 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -61,3 +61,5 @@ int calendar_spec_to_string(const CalendarSpec *spec, char **p); int calendar_spec_from_string(const char *p, CalendarSpec **spec); int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next); + +DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarSpec*, calendar_spec_free); From 4154ce8a8506b6e5e9e8edf651367fbc6d9cc1ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Nov 2017 10:34:52 +0100 Subject: [PATCH 4/5] update TODO --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index fc66b6a578..d8de3b4a23 100644 --- a/TODO +++ b/TODO @@ -28,6 +28,10 @@ Features: the runtime dir as we maintain for the fdstore: i.e. keep it around as long as the unit is running or has a job queued. +* hook up sd-bus' creds stuff with SO_PEERGROUPS + +* add async version of sd_bus_add_match and make use of that + * let's log the "tainted" string at boot * Add NetworkNamespacePath= to specify a path to a network namespace From 021723c63eccb5cd6de1a850f3f395463bd79685 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Nov 2017 10:54:21 +0100 Subject: [PATCH 5/5] =?UTF-8?q?systemctl:=20elapsation=20=E2=86=92=20elaps?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systemctl/systemctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index c046fe72d8..0672e4ffe0 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1084,7 +1084,7 @@ static int get_next_elapse( 't', &t.monotonic); if (r < 0) - return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to get next elapse time: %s", bus_error_message(&error, r)); r = sd_bus_get_property_trivial( bus, @@ -1096,7 +1096,7 @@ static int get_next_elapse( 't', &t.realtime); if (r < 0) - return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to get next elapse time: %s", bus_error_message(&error, r)); *next = t; return 0;