mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-09-16 17:45:52 +03:00
Merge pull request #7373 from poettering/analyze-calendar
add nifty little "systemd-analyze calendar" command
This commit is contained in:
4
TODO
4
TODO
@@ -28,6 +28,10 @@ Features:
|
|||||||
the runtime dir as we maintain for the fdstore: i.e. keep it around as long
|
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.
|
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
|
* let's log the "tainted" string at boot
|
||||||
|
|
||||||
* Add NetworkNamespacePath= to specify a path to a network namespace
|
* Add NetworkNamespacePath= to specify a path to a network namespace
|
||||||
|
@@ -125,6 +125,12 @@
|
|||||||
<arg choice="plain">verify</arg>
|
<arg choice="plain">verify</arg>
|
||||||
<arg choice="opt" rep="repeat"><replaceable>FILES</replaceable></arg>
|
<arg choice="opt" rep="repeat"><replaceable>FILES</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
|
<cmdsynopsis>
|
||||||
|
<command>systemd-analyze</command>
|
||||||
|
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||||
|
<arg choice="plain">calendar</arg>
|
||||||
|
<arg choice="plain" rep="repeat"><replaceable>SPECS</replaceable></arg>
|
||||||
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
@@ -220,6 +226,12 @@
|
|||||||
All units files present in the directories containing the command line arguments will
|
All units files present in the directories containing the command line arguments will
|
||||||
be used in preference to the other paths.</para>
|
be used in preference to the other paths.</para>
|
||||||
|
|
||||||
|
<para><command>systemd-analyze calendar</command> will parse and normalize repetitive calendar time events, and
|
||||||
|
will calculate when they will elapse next. This takes the same input as the <varname>OnCalendar=</varname> setting
|
||||||
|
in <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>, following the
|
||||||
|
syntax described in
|
||||||
|
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||||
|
|
||||||
<para>If no command is passed, <command>systemd-analyze
|
<para>If no command is passed, <command>systemd-analyze
|
||||||
time</command> is implied.</para>
|
time</command> is implied.</para>
|
||||||
|
|
||||||
|
@@ -302,6 +302,10 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
|
|||||||
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||||
for details.</para>
|
for details.</para>
|
||||||
|
|
||||||
|
<para>Use the <command>calendar</command> command of
|
||||||
|
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry> to validate
|
||||||
|
and normalize calendar time specifications for testing purposes. The tool also calculates when a specified
|
||||||
|
calendar event would elapse next.</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
@@ -311,7 +315,8 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
|
|||||||
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "bus-error.h"
|
#include "bus-error.h"
|
||||||
#include "bus-unit-util.h"
|
#include "bus-unit-util.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
|
#include "calendarspec.h"
|
||||||
#include "glob-util.h"
|
#include "glob-util.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "locale-util.h"
|
#include "locale-util.h"
|
||||||
@@ -1395,6 +1396,70 @@ static int dump_syscall_filters(char** names) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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) {
|
static void help(void) {
|
||||||
|
|
||||||
pager_open(arg_no_pager, false);
|
pager_open(arg_no_pager, false);
|
||||||
@@ -1429,6 +1494,7 @@ static void help(void) {
|
|||||||
" dump Output state serialization of service manager\n"
|
" dump Output state serialization of service manager\n"
|
||||||
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
|
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
|
||||||
" verify FILE... Check unit files for correctness\n"
|
" verify FILE... Check unit files for correctness\n"
|
||||||
|
" calendar SPEC... Validate repetitive calendar time events\n"
|
||||||
, program_invocation_short_name);
|
, program_invocation_short_name);
|
||||||
|
|
||||||
/* When updating this list, including descriptions, apply
|
/* 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);
|
r = get_log_target(bus, argv+optind+1);
|
||||||
else if (streq(argv[optind], "syscall-filter"))
|
else if (streq(argv[optind], "syscall-filter"))
|
||||||
r = dump_syscall_filters(argv+optind+1);
|
r = dump_syscall_filters(argv+optind+1);
|
||||||
|
else if (streq(argv[optind], "calendar"))
|
||||||
|
r = test_calendar(argv+optind+1);
|
||||||
else
|
else
|
||||||
log_error("Unknown operation '%s'.", argv[optind]);
|
log_error("Unknown operation '%s'.", argv[optind]);
|
||||||
}
|
}
|
||||||
|
@@ -51,10 +51,10 @@ static void free_chain(CalendarComponent *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void calendar_spec_free(CalendarSpec *c) {
|
CalendarSpec* calendar_spec_free(CalendarSpec *c) {
|
||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return NULL;
|
||||||
|
|
||||||
free_chain(c->year);
|
free_chain(c->year);
|
||||||
free_chain(c->month);
|
free_chain(c->month);
|
||||||
@@ -64,7 +64,7 @@ void calendar_spec_free(CalendarSpec *c) {
|
|||||||
free_chain(c->microsecond);
|
free_chain(c->microsecond);
|
||||||
free(c->timezone);
|
free(c->timezone);
|
||||||
|
|
||||||
free(c);
|
return mfree(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int component_compare(const void *_a, const void *_b) {
|
static int component_compare(const void *_a, const void *_b) {
|
||||||
|
@@ -52,7 +52,7 @@ typedef struct CalendarSpec {
|
|||||||
CalendarComponent *microsecond;
|
CalendarComponent *microsecond;
|
||||||
} CalendarSpec;
|
} CalendarSpec;
|
||||||
|
|
||||||
void calendar_spec_free(CalendarSpec *c);
|
CalendarSpec* calendar_spec_free(CalendarSpec *c);
|
||||||
|
|
||||||
int calendar_spec_normalize(CalendarSpec *spec);
|
int calendar_spec_normalize(CalendarSpec *spec);
|
||||||
bool calendar_spec_valid(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_from_string(const char *p, CalendarSpec **spec);
|
||||||
|
|
||||||
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
|
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next);
|
||||||
|
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarSpec*, calendar_spec_free);
|
||||||
|
@@ -1457,3 +1457,9 @@ usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
|
|||||||
/* x lies in the past */
|
/* x lies in the past */
|
||||||
return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
|
return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool in_utc_timezone(void) {
|
||||||
|
tzset();
|
||||||
|
|
||||||
|
return timezone == 0 && daylight == 0;
|
||||||
|
}
|
||||||
|
@@ -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);
|
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) {
|
static inline usec_t usec_add(usec_t a, usec_t b) {
|
||||||
usec_t c;
|
usec_t c;
|
||||||
|
|
||||||
|
@@ -1084,7 +1084,7 @@ static int get_next_elapse(
|
|||||||
't',
|
't',
|
||||||
&t.monotonic);
|
&t.monotonic);
|
||||||
if (r < 0)
|
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(
|
r = sd_bus_get_property_trivial(
|
||||||
bus,
|
bus,
|
||||||
@@ -1096,7 +1096,7 @@ static int get_next_elapse(
|
|||||||
't',
|
't',
|
||||||
&t.realtime);
|
&t.realtime);
|
||||||
if (r < 0)
|
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;
|
*next = t;
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -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[]) {
|
int main(int argc, char *argv[]) {
|
||||||
uintmax_t x;
|
uintmax_t x;
|
||||||
|
|
||||||
@@ -409,6 +425,7 @@ int main(int argc, char *argv[]) {
|
|||||||
test_format_timestamp_utc();
|
test_format_timestamp_utc();
|
||||||
test_dual_timestamp_deserialize();
|
test_dual_timestamp_deserialize();
|
||||||
test_usec_shift_clock();
|
test_usec_shift_clock();
|
||||||
|
test_in_utc_timezone();
|
||||||
|
|
||||||
/* Ensure time_t is signed */
|
/* Ensure time_t is signed */
|
||||||
assert_cc((time_t) -1 < (time_t) 1);
|
assert_cc((time_t) -1 < (time_t) 1);
|
||||||
|
Reference in New Issue
Block a user