diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 29fc14b571..d7db518bf9 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -54,6 +54,29 @@ static OutputMode arg_output = OUTPUT_SHORT; STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); +typedef struct SessionStatusInfo { + const char *id; + uid_t uid; + const char *name; + struct dual_timestamp timestamp; + unsigned 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; + dual_timestamp idle_hint_timestamp; +} SessionStatusInfo; + static OutputFlags get_output_flags(void) { return @@ -115,6 +138,15 @@ 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) }, + { "IdleSinceHintMonotonic", "t", NULL, offsetof(SessionStatusInfo, idle_hint_timestamp.monotonic) }, + { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, + { "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; @@ -133,7 +165,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", "state"); + table = table_new("session", "uid", "user", "seat", "tty", "state", "idle", "since"); if (!table) return log_oom(); @@ -142,10 +174,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_property = SD_BUS_ERROR_NULL; - _cleanup_free_ char *tty = NULL, *state = 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) @@ -153,38 +186,13 @@ static int list_sessions(int argc, char *argv[], void *userdata) { if (r == 0) break; - r = sd_bus_get_property_string(bus, - "org.freedesktop.login1", - object, - "org.freedesktop.login1.Session", - "TTY", - &error_property, - &tty); + r = bus_map_all_properties(bus, "org.freedesktop.login1", object, map, BUS_MAP_BOOLEAN_AS_BOOL, &e, &m, &i); if (r < 0) { - if (sd_bus_error_has_name(&error_property, SD_BUS_ERROR_UNKNOWN_OBJECT)) - /* The session is already closed when we're querying the property */ - continue; - - log_warning_errno(r, "Failed to get TTY for session %s, ignoring: %s", - id, bus_error_message(&error_property, r)); - - sd_bus_error_free(&error_property); - } - - r = sd_bus_get_property_string(bus, - "org.freedesktop.login1", - object, - "org.freedesktop.login1.Session", - "State", - &error_property, - &state); - if (r < 0) { - if (sd_bus_error_has_name(&error_property, SD_BUS_ERROR_UNKNOWN_OBJECT)) - /* The session is already closed when we're querying the property */ - continue; - - return log_error_errno(r, "Failed to get state for session %s: %s", - id, bus_error_message(&error_property, r)); + 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, @@ -192,8 +200,16 @@ static int list_sessions(int argc, char *argv[], void *userdata) { TABLE_UID, (uid_t) uid, TABLE_STRING, user, TABLE_STRING, seat, - TABLE_STRING, strna(tty), - TABLE_STRING, state); + TABLE_STRING, strna(i.tty), + TABLE_STRING, i.state, + TABLE_BOOLEAN, i.idle_hint); + if (r < 0) + return table_log_add_error(r); + + if (i.idle_hint) + r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &i.idle_hint_timestamp.monotonic); + else + r = table_add_cell(table, NULL, TABLE_EMPTY, NULL); if (r < 0) return table_log_add_error(r); } @@ -376,27 +392,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 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; diff --git a/test/units/testsuite-35.sh b/test/units/testsuite-35.sh index e9d88e1c83..659882ed7a 100755 --- a/test/units/testsuite-35.sh +++ b/test/units/testsuite-35.sh @@ -522,7 +522,9 @@ testcase_session_properties() { /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2 } -testcase_list_users_sessions() { +testcase_list_users_sessions_seats() { + local session seat + if [[ ! -c /dev/tty2 ]]; then echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." return @@ -534,10 +536,21 @@ testcase_list_users_sessions() { # Activate the session loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')" + session=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }') + : check that we got a valid session id + busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id + assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)" + seat=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $4 }') + assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $5 }')" tty2 + assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $6 }')" active + assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $7 }')" no + assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $8 }')" '' + + loginctl list-seats --no-legend | grep -Fwq "${seat?}" + assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)" assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active - assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $6 }')" active loginctl enable-linger logind-test-user assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes