diff --git a/src/basic/format-table.c b/src/basic/format-table.c index c541e92b3c..5f2e5e3d73 100644 --- a/src/basic/format-table.c +++ b/src/basic/format-table.c @@ -236,6 +236,8 @@ static size_t table_data_size(TableDataType type, const void *data) { return sizeof(bool); case TABLE_TIMESTAMP: + case TABLE_TIMESTAMP_UTC: + case TABLE_TIMESTAMP_RELATIVE: case TABLE_TIMESPAN: return sizeof(usec_t); @@ -700,6 +702,8 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { break; case TABLE_TIMESTAMP: + case TABLE_TIMESTAMP_UTC: + case TABLE_TIMESTAMP_RELATIVE: case TABLE_TIMESPAN: buffer.usec = va_arg(ap, usec_t); data = &buffer.usec; @@ -837,11 +841,9 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t return 0; case TABLE_TIMESTAMP: - if (a->timestamp < b->timestamp) - return -1; - if (a->timestamp > b->timestamp) - return 1; - return 0; + case TABLE_TIMESTAMP_UTC: + case TABLE_TIMESTAMP_RELATIVE: + return CMP(a->timestamp, b->timestamp); case TABLE_TIMESPAN: if (a->timespan < b->timespan) @@ -952,14 +954,23 @@ static const char *table_data_format(TableData *d) { case TABLE_BOOLEAN: return yes_no(d->boolean); - case TABLE_TIMESTAMP: { + case TABLE_TIMESTAMP: + case TABLE_TIMESTAMP_UTC: + case TABLE_TIMESTAMP_RELATIVE: { _cleanup_free_ char *p; + char *ret; p = new(char, FORMAT_TIMESTAMP_MAX); if (!p) return NULL; - if (!format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp)) + if (d->type == TABLE_TIMESTAMP) + ret = format_timestamp(p, FORMAT_TIMESTAMP_MAX, d->timestamp); + else if (d->type == TABLE_TIMESTAMP_UTC) + ret = format_timestamp_utc(p, FORMAT_TIMESTAMP_MAX, d->timestamp); + else + ret = format_timestamp_relative(p, FORMAT_TIMESTAMP_MAX, d->timestamp); + if (!ret) return "n/a"; d->formatted = TAKE_PTR(p); diff --git a/src/basic/format-table.h b/src/basic/format-table.h index 5a076b5383..1c8ab5436d 100644 --- a/src/basic/format-table.h +++ b/src/basic/format-table.h @@ -12,6 +12,8 @@ typedef enum TableDataType { TABLE_STRING, TABLE_BOOLEAN, TABLE_TIMESTAMP, + TABLE_TIMESTAMP_UTC, + TABLE_TIMESTAMP_RELATIVE, TABLE_TIMESPAN, TABLE_SIZE, TABLE_UINT32, diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 9b3fed928b..80d0a302d3 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -47,6 +47,29 @@ static bool arg_ask_password = true; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; +typedef struct SessionStatusInfo { + const char *id; + uid_t uid; + const char *name; + struct dual_timestamp timestamp; + unsigned int vtnr; + const char *seat; + const char *tty; + const char *display; + bool remote; + const char *remote_host; + const char *remote_user; + const char *service; + pid_t leader; + const char *type; + const char *class; + const char *state; + const char *scope; + const char *desktop; + bool idle_hint; + usec_t idle_hint_timestamp; +} SessionStatusInfo; + static OutputFlags get_output_flags(void) { return @@ -112,6 +135,14 @@ 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) }, + { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, + {}, + }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(table_unrefp) Table *table = NULL; @@ -138,7 +169,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - table = table_new("SESSION", "UID", "USER", "SEAT", "TTY"); + table = table_new("SESSION", "UID", "USER", "SEAT", "TTY", "IDLE", "SINCE"); if (!table) return log_oom(); @@ -147,10 +178,11 @@ static int list_sessions(int argc, char *argv[], void *userdata) { (void) table_set_align_percent(table, TABLE_HEADER_CELL(1), 100); for (;;) { - _cleanup_(sd_bus_error_free) sd_bus_error error_tty = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_tty = NULL; - const char *id, *user, *seat, *object, *tty = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + const char *id, *user, *seat, *object; uint32_t uid; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + SessionStatusInfo i = {}; r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object); if (r < 0) @@ -158,21 +190,13 @@ static int list_sessions(int argc, char *argv[], void *userdata) { if (r == 0) break; - r = sd_bus_get_property( - bus, - "org.freedesktop.login1", - object, - "org.freedesktop.login1.Session", - "TTY", - &error_tty, - &reply_tty, - "s"); - if (r < 0) - log_warning_errno(r, "Failed to get TTY for session %s: %s", id, bus_error_message(&error_tty, r)); - else { - r = sd_bus_message_read(reply_tty, "s", &tty); - if (r < 0) - return bus_log_parse_error(r); + r = bus_map_all_properties(bus, "org.freedesktop.login1", object, map, BUS_MAP_BOOLEAN_AS_BOOL, &e, &m, &i); + if (r < 0) { + log_full_errno(sd_bus_error_has_name(&e, SD_BUS_ERROR_UNKNOWN_OBJECT) ? LOG_DEBUG : LOG_WARNING, + r, + "Failed to get properties of session %s, ignoring: %s", + id, bus_error_message(&e, r)); + continue; } r = table_add_many(table, @@ -180,7 +204,15 @@ static int list_sessions(int argc, char *argv[], void *userdata) { TABLE_UINT32, uid, TABLE_STRING, user, TABLE_STRING, seat, - TABLE_STRING, strna(tty)); + TABLE_STRING, strna(i.tty), + TABLE_BOOLEAN, i.idle_hint); + if (r < 0) + return log_error_errno(r, "Failed to add row to table: %m"); + + if (i.idle_hint) + r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &i.idle_hint_timestamp); + else + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); if (r < 0) return log_error_errno(r, "Failed to add row to table: %m"); } @@ -341,27 +373,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit return 0; } -typedef struct SessionStatusInfo { - const char *id; - uid_t uid; - const char *name; - struct dual_timestamp timestamp; - unsigned int vtnr; - const char *seat; - const char *tty; - const char *display; - bool remote; - const char *remote_host; - const char *remote_user; - const char *service; - pid_t leader; - const char *type; - const char *class; - const char *state; - const char *scope; - const char *desktop; -} SessionStatusInfo; - typedef struct UserStatusInfo { uid_t uid; bool linger; @@ -462,6 +473,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li { "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) }, {} @@ -560,6 +573,14 @@ 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 && i.idle_hint_timestamp > 0) { + s1 = format_timestamp_relative(since1, sizeof(since1), i.idle_hint_timestamp); + s2 = format_timestamp(since2, sizeof(since2), i.idle_hint_timestamp); + + printf("\t Idle: %s since %s (%s)\n", yes_no(i.idle_hint), s2, s1); + } else + printf("\t Idle: %s\n", yes_no(i.idle_hint)); + if (i.scope) { printf("\t Unit: %s\n", i.scope); show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);