diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 657e231b0d..f124e6f016 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -422,7 +422,7 @@ char *format_timestamp_style( return buf; } -char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implicit_left) { +char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) { const char *s; usec_t n, d; @@ -431,7 +431,7 @@ char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implici if (!timestamp_is_set(t)) return NULL; - n = now(CLOCK_REALTIME); + n = now(clock); if (n > t) { d = n - t; s = " ago"; diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 79a6715656..b49137d5c3 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -123,12 +123,16 @@ struct timeval* timeval_store(struct timeval *tv, usec_t u); #define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u)) char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_; -char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implicit_left) _warn_unused_result_; +char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) _warn_unused_result_; char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_; _warn_unused_result_ -static inline char* format_timestamp_relative(char *buf, size_t l, usec_t t) { - return format_timestamp_relative_full(buf, l, t, false); +static inline char* format_timestamp_relative(char *buf, size_t l, usec_t t) { + return format_timestamp_relative_full(buf, l, t, CLOCK_REALTIME, /* implicit_left = */ false); +} +_warn_unused_result_ +static inline char* format_timestamp_relative_monotonic(char *buf, size_t l, usec_t t) { + return format_timestamp_relative_full(buf, l, t, CLOCK_MONOTONIC, /* implicit_left = */ false); } _warn_unused_result_ @@ -142,6 +146,8 @@ static inline char* format_timestamp(char *buf, size_t l, usec_t t) { #define FORMAT_TIMESTAMP(t) format_timestamp((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t) #define FORMAT_TIMESTAMP_RELATIVE(t) \ format_timestamp_relative((char[FORMAT_TIMESTAMP_RELATIVE_MAX]){}, FORMAT_TIMESTAMP_RELATIVE_MAX, t) +#define FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(t) \ + format_timestamp_relative_monotonic((char[FORMAT_TIMESTAMP_RELATIVE_MAX]){}, FORMAT_TIMESTAMP_RELATIVE_MAX, t) #define FORMAT_TIMESPAN(t, accuracy) format_timespan((char[FORMAT_TIMESPAN_MAX]){}, FORMAT_TIMESPAN_MAX, t, accuracy) #define FORMAT_TIMESTAMP_STYLE(t, style) \ format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style) diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 81bb8f48dd..22164cd9bf 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -74,7 +74,7 @@ typedef struct SessionStatusInfo { const char *scope; const char *desktop; bool idle_hint; - usec_t idle_hint_timestamp; + dual_timestamp idle_hint_timestamp; } SessionStatusInfo; typedef struct UserStatusInfo { @@ -173,10 +173,10 @@ static int show_table(Table *table, const char *word) { static int list_sessions(int argc, char *argv[], void *userdata) { static const struct bus_properties_map map[] = { - { "IdleHint", "b", NULL, offsetof(SessionStatusInfo, idle_hint) }, - { "IdleSinceHint", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp) }, - { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, - { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, + { "IdleHint", "b", NULL, offsetof(SessionStatusInfo, idle_hint) }, + { "IdleSinceHintMonotonic", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.monotonic) }, + { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, + { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, {}, }; @@ -240,7 +240,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) { return table_log_add_error(r); if (i.idle_hint) - r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &i.idle_hint_timestamp); + r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE_MONOTONIC, &i.idle_hint_timestamp.monotonic); else r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); if (r < 0) @@ -470,27 +470,28 @@ static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_messag static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) { static const struct bus_properties_map map[] = { - { "Id", "s", NULL, offsetof(SessionStatusInfo, id) }, - { "Name", "s", NULL, offsetof(SessionStatusInfo, name) }, - { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, - { "Display", "s", NULL, offsetof(SessionStatusInfo, display) }, - { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) }, - { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) }, - { "Service", "s", NULL, offsetof(SessionStatusInfo, service) }, - { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) }, - { "Type", "s", NULL, offsetof(SessionStatusInfo, type) }, - { "Class", "s", NULL, offsetof(SessionStatusInfo, class) }, - { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) }, - { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, - { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) }, - { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) }, - { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) }, - { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) }, - { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) }, - { "IdleHint", "b", NULL, offsetof(SessionStatusInfo, idle_hint) }, - { "IdleSinceHint", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp) }, - { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) }, - { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) }, + { "Id", "s", NULL, offsetof(SessionStatusInfo, id) }, + { "Name", "s", NULL, offsetof(SessionStatusInfo, name) }, + { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, + { "Display", "s", NULL, offsetof(SessionStatusInfo, display) }, + { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) }, + { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) }, + { "Service", "s", NULL, offsetof(SessionStatusInfo, service) }, + { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) }, + { "Type", "s", NULL, offsetof(SessionStatusInfo, type) }, + { "Class", "s", NULL, offsetof(SessionStatusInfo, class) }, + { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) }, + { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, + { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) }, + { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) }, + { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) }, + { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) }, + { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) }, + { "IdleHint", "b", NULL, offsetof(SessionStatusInfo, idle_hint) }, + { "IdleSinceHint", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.realtime) }, + { "IdleSinceHintMonotonic", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.monotonic) }, + { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) }, + { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) }, {} }; @@ -515,10 +516,10 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li else printf(UID_FMT "\n", i.uid); - if (timestamp_is_set(i.timestamp.realtime)) + if (dual_timestamp_is_set(&i.timestamp)) printf("\t Since: %s; %s\n", FORMAT_TIMESTAMP(i.timestamp.realtime), - FORMAT_TIMESTAMP_RELATIVE(i.timestamp.realtime)); + FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic)); if (i.leader > 0) { _cleanup_free_ char *t = NULL; @@ -581,11 +582,11 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li if (i.state) printf("\t State: %s\n", i.state); - if (i.idle_hint && timestamp_is_set(i.idle_hint_timestamp)) + if (i.idle_hint && dual_timestamp_is_set(&i.idle_hint_timestamp)) printf("\t Idle: %s since %s (%s)\n", yes_no(i.idle_hint), - FORMAT_TIMESTAMP(i.idle_hint_timestamp), - FORMAT_TIMESTAMP_RELATIVE(i.idle_hint_timestamp)); + FORMAT_TIMESTAMP(i.idle_hint_timestamp.realtime), + FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.idle_hint_timestamp.monotonic)); else printf("\t Idle: %s\n", yes_no(i.idle_hint)); @@ -646,10 +647,10 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) else printf("%"PRIu32"\n", i.uid); - if (timestamp_is_set(i.timestamp.realtime)) + if (dual_timestamp_is_set(&i.timestamp)) printf("\t Since: %s; %s\n", FORMAT_TIMESTAMP(i.timestamp.realtime), - FORMAT_TIMESTAMP_RELATIVE(i.timestamp.realtime)); + FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic)); if (!isempty(i.state)) printf("\t State: %s\n", i.state); @@ -692,9 +693,9 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) { static const struct bus_properties_map map[] = { - { "Id", "s", NULL, offsetof(SeatStatusInfo, id) }, + { "Id", "s", NULL, offsetof(SeatStatusInfo, id) }, { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) }, - { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) }, + { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) }, {} }; diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 83b749d677..22efd042e2 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -300,6 +300,7 @@ static size_t table_data_size(TableDataType type, const void *data) { case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: + case TABLE_TIMESTAMP_RELATIVE_MONOTONIC: case TABLE_TIMESTAMP_LEFT: case TABLE_TIMESTAMP_DATE: case TABLE_TIMESPAN: @@ -905,6 +906,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: + case TABLE_TIMESTAMP_RELATIVE_MONOTONIC: case TABLE_TIMESTAMP_LEFT: case TABLE_TIMESTAMP_DATE: case TABLE_TIMESPAN: @@ -1322,6 +1324,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: + case TABLE_TIMESTAMP_RELATIVE_MONOTONIC: case TABLE_TIMESTAMP_LEFT: case TABLE_TIMESTAMP_DATE: return CMP(a->timestamp, b->timestamp); @@ -1564,13 +1567,14 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: + case TABLE_TIMESTAMP_RELATIVE_MONOTONIC: case TABLE_TIMESTAMP_LEFT: case TABLE_TIMESTAMP_DATE: { _cleanup_free_ char *p = NULL; char *ret; p = new(char, - IN_SET(d->type, TABLE_TIMESTAMP_RELATIVE, TABLE_TIMESTAMP_LEFT) ? + IN_SET(d->type, TABLE_TIMESTAMP_RELATIVE, TABLE_TIMESTAMP_RELATIVE_MONOTONIC, TABLE_TIMESTAMP_LEFT) ? FORMAT_TIMESTAMP_RELATIVE_MAX : FORMAT_TIMESTAMP_MAX); if (!p) return NULL; @@ -1581,10 +1585,14 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_UTC); else if (d->type == TABLE_TIMESTAMP_DATE) ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_DATE); + else if (d->type == TABLE_TIMESTAMP_RELATIVE_MONOTONIC) + ret = format_timestamp_relative_full(p, FORMAT_TIMESTAMP_RELATIVE_MAX, + d->timestamp, CLOCK_MONOTONIC, + /* implicit_left = */ false); else - ret = format_timestamp_relative_full( - p, FORMAT_TIMESTAMP_RELATIVE_MAX, d->timestamp, - /* implicit_left= */ d->type == TABLE_TIMESTAMP_LEFT); + ret = format_timestamp_relative_full(p, FORMAT_TIMESTAMP_RELATIVE_MAX, + d->timestamp, CLOCK_REALTIME, + /* implicit_left = */ d->type == TABLE_TIMESTAMP_LEFT); if (!ret) return "-"; @@ -2640,6 +2648,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { case TABLE_TIMESTAMP: case TABLE_TIMESTAMP_UTC: case TABLE_TIMESTAMP_RELATIVE: + case TABLE_TIMESTAMP_RELATIVE_MONOTONIC: case TABLE_TIMESTAMP_LEFT: case TABLE_TIMESTAMP_DATE: if (d->timestamp == USEC_INFINITY) diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 148418c70f..742d5e3ae2 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -23,6 +23,7 @@ typedef enum TableDataType { TABLE_TIMESTAMP, TABLE_TIMESTAMP_UTC, TABLE_TIMESTAMP_RELATIVE, + TABLE_TIMESTAMP_RELATIVE_MONOTONIC, TABLE_TIMESTAMP_LEFT, TABLE_TIMESTAMP_DATE, TABLE_TIMESPAN, diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c index 9be9510a2c..77ff836bb0 100644 --- a/src/systemctl/systemctl-list-units.c +++ b/src/systemctl/systemctl-list-units.c @@ -536,9 +536,10 @@ static int get_next_elapse( static int get_last_trigger( sd_bus *bus, const char *path, - usec_t *last) { + dual_timestamp *last) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + dual_timestamp t; int r; assert(bus); @@ -553,10 +554,23 @@ static int get_last_trigger( "LastTriggerUSec", &error, 't', - last); + &t.realtime); if (r < 0) return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r)); + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "LastTriggerUSecMonotonic", + &error, + 't', + &t.monotonic); + if (r < 0) + return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r)); + + *last = t; return 0; } @@ -564,7 +578,7 @@ typedef struct TimerInfo { const char* machine; const char* id; usec_t next_elapse; - usec_t last_trigger; + dual_timestamp last_trigger; char **triggered; } TimerInfo; @@ -623,8 +637,8 @@ static int output_timers_list(const TimerInfo *timers, size_t n_timers) { r = table_add_many(table, TABLE_TIMESTAMP, t->next_elapse, TABLE_TIMESTAMP_LEFT, t->next_elapse, - TABLE_TIMESTAMP, t->last_trigger, - TABLE_TIMESTAMP_RELATIVE, t->last_trigger, + TABLE_TIMESTAMP, t->last_trigger.realtime, + TABLE_TIMESTAMP_RELATIVE_MONOTONIC, t->last_trigger.monotonic, TABLE_STRING, unit); if (r < 0) return table_log_add_error(r); @@ -677,8 +691,8 @@ static int add_timer_info( size_t *n_timers) { _cleanup_strv_free_ char **triggered = NULL; - dual_timestamp next = DUAL_TIMESTAMP_NULL; - usec_t m, last = 0; + dual_timestamp next, last; + usec_t m; int r; assert(bus); diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index e80922901e..235c6cf164 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -464,64 +464,69 @@ TEST(format_timestamp_relative_full) { /* Years and months */ x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 1*USEC_PER_MONTH); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 year 1 month ago")); + x = now(CLOCK_MONOTONIC) + (1*USEC_PER_YEAR + 1.5*USEC_PER_MONTH); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_MONOTONIC, false)); + log_debug("%s", buf); + assert_se(streq(buf, "1 year 1 month left")); + x = now(CLOCK_REALTIME) - (1*USEC_PER_YEAR + 2*USEC_PER_MONTH); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 year 2 months ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 1*USEC_PER_MONTH); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 years 1 month ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_YEAR + 2*USEC_PER_MONTH); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 years 2 months ago")); /* Months and days */ x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 1*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 month 1 day ago")); x = now(CLOCK_REALTIME) - (1*USEC_PER_MONTH + 2*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 month 2 days ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 1*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 months 1 day ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_MONTH + 2*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 months 2 days ago")); /* Weeks and days */ x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 1*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 week 1 day ago")); x = now(CLOCK_REALTIME) - (1*USEC_PER_WEEK + 2*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "1 week 2 days ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 1*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 weeks 1 day ago")); x = now(CLOCK_REALTIME) - (2*USEC_PER_WEEK + 2*USEC_PER_DAY); - assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, true)); + assert_se(format_timestamp_relative_full(buf, sizeof(buf), x, CLOCK_REALTIME, true)); log_debug("%s", buf); assert_se(streq(buf, "2 weeks 2 days ago")); }