loginctl: show the 10 most recent log user/session log lines in "loginctl user-status" and "loginctl session-status"

This commit is contained in:
Lennart Poettering 2015-01-08 14:38:52 +01:00
parent 3a8d368a61
commit 3c7560019e
6 changed files with 173 additions and 82 deletions

View file

@ -5539,6 +5539,8 @@ loginctl_SOURCES = \
loginctl_LDADD = \
libsystemd-internal.la \
libsystemd-logs.la \
libsystemd-journal-internal.la \
libudev-internal.la \
libsystemd-shared.la

View file

@ -157,6 +157,35 @@
<constant>SIGTERM</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--lines=</option></term>
<listitem><para>When used with
<command>user-status</command> and
<command>session-status</command>,
controls the number of journal lines
to show, counting from the most recent
ones. Takes a positive integer
argument. Defaults to 10.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-o</option></term>
<term><option>--output=</option></term>
<listitem><para>When used with
<command>user-status</command> and
<command>session-status</command>,
controls the formatting of the journal
entries that are shown. For the
available choices, see
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
Defaults to
<literal>short</literal>.</para></listitem>
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="host" />
<xi:include href="user-system-options.xml" xpointer="machine" />
@ -179,11 +208,12 @@
<listitem><para>Show terse runtime
status information about one or more
sessions. This function is intended to
generate human-readable output. If you
are looking for computer-parsable
output, use
<command>show-session</command>
sessions, followed by the most recent
log data from the journal. This
function is intended to generate
human-readable output. If you are
looking for computer-parsable output,
use <command>show-session</command>
instead.</para></listitem>
</varlistentry>
@ -274,13 +304,14 @@
<listitem><para>Show terse runtime
status information about one or more
logged in users. This function is
intended to generate human-readable
output. If you are looking for
computer-parsable output, use
<command>show-user</command> instead.
Users may be specified by their
usernames or numeric user IDs.
logged in users, followed by the most
recent log data from the journal. This
function is intended to generate
human-readable output. If you are
looking for computer-parsable output,
use <command>show-user</command>
instead. Users may be specified by
their usernames or numeric user IDs.
</para></listitem>
</varlistentry>

View file

@ -37,6 +37,7 @@
#include "strv.h"
#include "unit-name.h"
#include "sysfs-show.h"
#include "logs-show.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "spawn-polkit-agent.h"
@ -51,6 +52,8 @@ static int arg_signal = SIGTERM;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static bool arg_ask_password = true;
static char *arg_host = NULL;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
static void pager_open_if_enabled(void) {
@ -73,6 +76,15 @@ static void polkit_agent_open_if_enabled(void) {
polkit_agent_open();
}
static OutputFlags get_output_flags(void) {
return
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR;
}
static int list_sessions(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
@ -206,7 +218,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL;
const char *cgroup;
int r, output_flags;
int r;
unsigned c;
assert(bus);
@ -239,17 +251,13 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
return 0;
output_flags =
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH;
c = columns();
if (c > 18)
c -= 18;
else
c = 0;
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
return 0;
}
@ -257,7 +265,7 @@ typedef struct SessionStatusInfo {
const char *id;
uid_t uid;
const char *name;
usec_t timestamp;
struct dual_timestamp timestamp;
unsigned int vtnr;
const char *seat;
const char *tty;
@ -277,7 +285,7 @@ typedef struct SessionStatusInfo {
typedef struct UserStatusInfo {
uid_t uid;
const char *name;
usec_t timestamp;
struct dual_timestamp timestamp;
const char *state;
char **sessions;
const char *display;
@ -357,24 +365,25 @@ 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) },
{ "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) },
{ "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
{ "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
{}
};
@ -399,8 +408,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
else
printf("%u\n", (unsigned) i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
if (s1)
printf("\t Since: %s; %s\n", s2, s1);
@ -471,6 +480,22 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
if (i.scope) {
printf("\t Unit: %s\n", i.scope);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
if (arg_transport == BUS_TRANSPORT_LOCAL) {
show_journal_by_unit(
stdout,
i.scope,
arg_output,
0,
i.timestamp.monotonic,
arg_lines,
0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
true,
NULL);
}
}
return 0;
@ -479,13 +504,14 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) },
{ "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
{ "State", "s", NULL, offsetof(UserStatusInfo, state) },
{ "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
{ "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp) },
{ "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
{ "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) },
{ "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
{ "State", "s", NULL, offsetof(UserStatusInfo, state) },
{ "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
{ "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) },
{ "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) },
{ "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
{ "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
{}
};
@ -510,8 +536,8 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
else
printf("%u\n", (unsigned) i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
if (s1)
printf("\t Since: %s; %s\n", s2, s1);
@ -538,6 +564,19 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
if (i.slice) {
printf("\t Unit: %s\n", i.slice);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
show_journal_by_unit(
stdout,
i.slice,
arg_output,
0,
i.timestamp.monotonic,
arg_lines,
0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
true,
NULL);
}
finish:
@ -1051,7 +1090,10 @@ static void help(void) {
" -a --all Show all properties, including empty ones\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n\n"
" -s --signal=SIGNAL Which signal to send\n"
" -n --lines=INTEGER Number of journal entries to show\n"
" -o --output=STRING Change journal output mode (short, short-monotonic,\n"
" verbose, export, json, json-pretty, json-sse, cat)\n\n"
"Session Commands:\n"
" list-sessions List sessions\n"
" session-status ID... Show session status\n"
@ -1104,6 +1146,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "lines", required_argument, NULL, 'n' },
{ "output", required_argument, NULL, 'o' },
{}
};
@ -1112,7 +1156,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
switch (c) {
@ -1145,6 +1189,21 @@ static int parse_argv(int argc, char *argv[]) {
arg_full = true;
break;
case 'n':
if (safe_atou(optarg, &arg_lines) < 0) {
log_error("Failed to parse lines '%s'", optarg);
return -EINVAL;
}
break;
case 'o':
arg_output = output_mode_from_string(optarg);
if (arg_output < 0) {
log_error("Unknown output '%s'.", optarg);
return -EINVAL;
}
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;

View file

@ -1234,12 +1234,12 @@ int show_journal_by_unit(
unsigned how_many,
uid_t uid,
OutputFlags flags,
bool system,
int journal_open_flags,
bool system_unit,
bool *ellipsized) {
_cleanup_journal_close_ sd_journal*j = NULL;
int r;
int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
@ -1248,7 +1248,7 @@ int show_journal_by_unit(
if (how_many <= 0)
return 0;
r = sd_journal_open(&j, jflags);
r = sd_journal_open(&j, journal_open_flags);
if (r < 0)
return r;
@ -1256,7 +1256,7 @@ int show_journal_by_unit(
if (r < 0)
return r;
if (system)
if (system_unit)
r = add_matches_for_unit(j, unit);
else
r = add_matches_for_user_unit(j, unit, uid);

View file

@ -25,7 +25,7 @@
#include <unistd.h>
#include <sys/types.h>
#include "systemd/sd-journal.h"
#include "sd-journal.h"
#include "util.h"
#include "output-mode.h"
@ -58,7 +58,8 @@ int show_journal_by_unit(
unsigned how_many,
uid_t uid,
OutputFlags flags,
bool system,
int journal_open_flags,
bool system_unit,
bool *ellipsized);
void json_escape(

View file

@ -194,6 +194,15 @@ static void polkit_agent_open_if_enabled(void) {
}
#endif
static OutputFlags get_output_flags(void) {
return
arg_all * OUTPUT_SHOW_ALL |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF |
arg_full * OUTPUT_FULL_WIDTH;
}
static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) {
assert(error);
@ -3224,12 +3233,6 @@ static void print_status_info(
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
const char *path;
int flags =
arg_all * OUTPUT_SHOW_ALL |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF |
arg_full * OUTPUT_FULL_WIDTH;
char **t, **t2;
assert(i);
@ -3499,21 +3502,23 @@ static void print_status_info(
if (i->control_pid > 0)
extra[k++] = i->control_pid;
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, flags);
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags());
}
}
if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) {
show_journal_by_unit(stdout,
i->id,
arg_output,
0,
i->inactive_exit_timestamp_monotonic,
arg_lines,
getuid(),
flags | OUTPUT_BEGIN_NEWLINE,
arg_scope == UNIT_FILE_SYSTEM,
ellipsized);
show_journal_by_unit(
stdout,
i->id,
arg_output,
0,
i->inactive_exit_timestamp_monotonic,
arg_lines,
getuid(),
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
arg_scope == UNIT_FILE_SYSTEM,
ellipsized);
}
if (i->need_daemon_reload)
@ -4377,13 +4382,6 @@ static int show_system_status(sd_bus *bus) {
printf(" CGroup: %s\n", mi.control_group ?: "/");
if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_MACHINE) {
int flags =
arg_all * OUTPUT_SHOW_ALL |
(!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR |
!arg_quiet * OUTPUT_WARN_CUTOFF |
arg_full * OUTPUT_FULL_WIDTH;
static const char prefix[] = " ";
unsigned c;
@ -4393,7 +4391,7 @@ static int show_system_status(sd_bus *bus) {
else
c = 0;
show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, flags);
show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, get_output_flags());
}
free(mi.state);