1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 18:55:40 +03:00

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 = \ loginctl_LDADD = \
libsystemd-internal.la \ libsystemd-internal.la \
libsystemd-logs.la \
libsystemd-journal-internal.la \
libudev-internal.la \ libudev-internal.la \
libsystemd-shared.la libsystemd-shared.la

View File

@ -157,6 +157,35 @@
<constant>SIGTERM</constant>.</para></listitem> <constant>SIGTERM</constant>.</para></listitem>
</varlistentry> </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="host" />
<xi:include href="user-system-options.xml" xpointer="machine" /> <xi:include href="user-system-options.xml" xpointer="machine" />
@ -179,11 +208,12 @@
<listitem><para>Show terse runtime <listitem><para>Show terse runtime
status information about one or more status information about one or more
sessions. This function is intended to sessions, followed by the most recent
generate human-readable output. If you log data from the journal. This
are looking for computer-parsable function is intended to generate
output, use human-readable output. If you are
<command>show-session</command> looking for computer-parsable output,
use <command>show-session</command>
instead.</para></listitem> instead.</para></listitem>
</varlistentry> </varlistentry>
@ -274,13 +304,14 @@
<listitem><para>Show terse runtime <listitem><para>Show terse runtime
status information about one or more status information about one or more
logged in users. This function is logged in users, followed by the most
intended to generate human-readable recent log data from the journal. This
output. If you are looking for function is intended to generate
computer-parsable output, use human-readable output. If you are
<command>show-user</command> instead. looking for computer-parsable output,
Users may be specified by their use <command>show-user</command>
usernames or numeric user IDs. instead. Users may be specified by
their usernames or numeric user IDs.
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>

View File

@ -37,6 +37,7 @@
#include "strv.h" #include "strv.h"
#include "unit-name.h" #include "unit-name.h"
#include "sysfs-show.h" #include "sysfs-show.h"
#include "logs-show.h"
#include "cgroup-show.h" #include "cgroup-show.h"
#include "cgroup-util.h" #include "cgroup-util.h"
#include "spawn-polkit-agent.h" #include "spawn-polkit-agent.h"
@ -51,6 +52,8 @@ static int arg_signal = SIGTERM;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static bool arg_ask_password = true; static bool arg_ask_password = true;
static char *arg_host = NULL; static char *arg_host = NULL;
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
static void pager_open_if_enabled(void) { static void pager_open_if_enabled(void) {
@ -73,6 +76,15 @@ static void polkit_agent_open_if_enabled(void) {
polkit_agent_open(); 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) { 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_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = 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_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_free_ char *path = NULL; _cleanup_free_ char *path = NULL;
const char *cgroup; const char *cgroup;
int r, output_flags; int r;
unsigned c; unsigned c;
assert(bus); 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) if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
return 0; return 0;
output_flags =
arg_all * OUTPUT_SHOW_ALL |
arg_full * OUTPUT_FULL_WIDTH;
c = columns(); c = columns();
if (c > 18) if (c > 18)
c -= 18; c -= 18;
else else
c = 0; 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; return 0;
} }
@ -257,7 +265,7 @@ typedef struct SessionStatusInfo {
const char *id; const char *id;
uid_t uid; uid_t uid;
const char *name; const char *name;
usec_t timestamp; struct dual_timestamp timestamp;
unsigned int vtnr; unsigned int vtnr;
const char *seat; const char *seat;
const char *tty; const char *tty;
@ -277,7 +285,7 @@ typedef struct SessionStatusInfo {
typedef struct UserStatusInfo { typedef struct UserStatusInfo {
uid_t uid; uid_t uid;
const char *name; const char *name;
usec_t timestamp; struct dual_timestamp timestamp;
const char *state; const char *state;
char **sessions; char **sessions;
const char *display; 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 int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = { static const struct bus_properties_map map[] = {
{ "Id", "s", NULL, offsetof(SessionStatusInfo, id) }, { "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
{ "Name", "s", NULL, offsetof(SessionStatusInfo, name) }, { "Name", "s", NULL, offsetof(SessionStatusInfo, name) },
{ "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
{ "Display", "s", NULL, offsetof(SessionStatusInfo, display) }, { "Display", "s", NULL, offsetof(SessionStatusInfo, display) },
{ "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) }, { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
{ "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) }, { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
{ "Service", "s", NULL, offsetof(SessionStatusInfo, service) }, { "Service", "s", NULL, offsetof(SessionStatusInfo, service) },
{ "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) }, { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) },
{ "Type", "s", NULL, offsetof(SessionStatusInfo, type) }, { "Type", "s", NULL, offsetof(SessionStatusInfo, type) },
{ "Class", "s", NULL, offsetof(SessionStatusInfo, class) }, { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
{ "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) }, { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) },
{ "State", "s", NULL, offsetof(SessionStatusInfo, state) }, { "State", "s", NULL, offsetof(SessionStatusInfo, state) },
{ "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) }, { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) },
{ "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) }, { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
{ "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) }, { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) },
{ "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp) }, { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) },
{ "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) }, { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) },
{ "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) }, { "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 else
printf("%u\n", (unsigned) i.uid); printf("%u\n", (unsigned) i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp); s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp); s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
if (s1) if (s1)
printf("\t Since: %s; %s\n", s2, 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) { if (i.scope) {
printf("\t Unit: %s\n", i.scope); printf("\t Unit: %s\n", i.scope);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader); 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; 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 int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
static const struct bus_properties_map map[] = { static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) }, { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
{ "Slice", "s", NULL, offsetof(UserStatusInfo, slice) }, { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
{ "State", "s", NULL, offsetof(UserStatusInfo, state) }, { "State", "s", NULL, offsetof(UserStatusInfo, state) },
{ "UID", "u", NULL, offsetof(UserStatusInfo, uid) }, { "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
{ "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp) }, { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) },
{ "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) }, { "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) },
{ "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) }, { "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 else
printf("%u\n", (unsigned) i.uid); printf("%u\n", (unsigned) i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp); s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp); s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
if (s1) if (s1)
printf("\t Since: %s; %s\n", s2, 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) { if (i.slice) {
printf("\t Unit: %s\n", i.slice); printf("\t Unit: %s\n", i.slice);
show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0); 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: finish:
@ -1051,7 +1090,10 @@ static void help(void) {
" -a --all Show all properties, including empty ones\n" " -a --all Show all properties, including empty ones\n"
" -l --full Do not ellipsize output\n" " -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\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" "Session Commands:\n"
" list-sessions List sessions\n" " list-sessions List sessions\n"
" session-status ID... Show session status\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' }, { "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' }, { "machine", required_argument, NULL, 'M' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { "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(argc >= 0);
assert(argv); 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) { switch (c) {
@ -1145,6 +1189,21 @@ static int parse_argv(int argc, char *argv[]) {
arg_full = true; arg_full = true;
break; 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: case ARG_NO_PAGER:
arg_no_pager = true; arg_no_pager = true;
break; break;

View File

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

View File

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

View File

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