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 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.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..124f7f5880 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); @@ -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); 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/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; 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);