Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#2156786) Add idle time column to loginctl list-sessions #393

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions src/basic/format-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/basic/format-table.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
103 changes: 62 additions & 41 deletions src/login/loginctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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();

Expand All @@ -147,40 +178,41 @@ 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)
return bus_log_parse_error(r);
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,
TABLE_STRING, id,
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");
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) },
{}
Expand Down Expand Up @@ -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);
Expand Down