Systemd/src/shared/logs-show.c
Zbigniew Jędrzejewski-Szmek a688baa8b7 journal: add ability to filter by current user
This is the just the library part.

SD_JOURNAL_CURRENT_USER flags is added to sd_j_open(), to open
files from current user.

SD_JOURNAL_SYSTEM_ONLY is renamed to SD_JOURNAL_SYSTEM,
and changed to mean to (also) open system files. This way various
flags can be combined, which gives them nicer semantics, especially
if other ones are added later.

Backwards compatibility is kept, because SD_JOURNAL_SYSTEM_ONLY
is equivalent to SD_JOURNAL_SYSTEM if used alone, and before there
we no other flags.
2013-06-10 10:10:06 -04:00

1058 lines
32 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <sys/poll.h>
#include <string.h>
#include "logs-show.h"
#include "log.h"
#include "util.h"
#include "utf8.h"
#include "hashmap.h"
#include "journal-internal.h"
#define PRINT_THRESHOLD 128
#define JSON_THRESHOLD 4096
static int print_catalog(FILE *f, sd_journal *j) {
int r;
_cleanup_free_ char *t = NULL, *z = NULL;
r = sd_journal_get_catalog(j, &t);
if (r < 0)
return r;
z = strreplace(strstrip(t), "\n", "\n-- ");
if (!z)
return log_oom();
fputs("-- ", f);
fputs(z, f);
fputc('\n', f);
return 0;
}
static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
size_t fl, nl;
void *buf;
assert(data);
assert(field);
assert(target);
assert(target_size);
fl = strlen(field);
if (length < fl)
return 0;
if (memcmp(data, field, fl))
return 0;
nl = length - fl;
buf = malloc(nl+1);
if (!buf)
return log_oom();
memcpy(buf, (const char*) data + fl, nl);
((char*)buf)[nl] = 0;
free(*target);
*target = buf;
*target_size = nl;
return 1;
}
static bool shall_print(const char *p, size_t l, OutputFlags flags) {
assert(p);
if (flags & OUTPUT_SHOW_ALL)
return true;
if (l >= PRINT_THRESHOLD)
return false;
if (!utf8_is_printable(p, l))
return false;
return true;
}
static void print_multiline(FILE *f, unsigned prefix, unsigned n_columns, int flags, int priority, const char* message, size_t message_len) {
const char *color_on = "", *color_off = "";
const char *pos, *end;
bool continuation = false;
if (flags & OUTPUT_COLOR) {
if (priority <= LOG_ERR) {
color_on = ANSI_HIGHLIGHT_RED_ON;
color_off = ANSI_HIGHLIGHT_OFF;
} else if (priority <= LOG_NOTICE) {
color_on = ANSI_HIGHLIGHT_ON;
color_off = ANSI_HIGHLIGHT_OFF;
}
}
for (pos = message; pos < message + message_len; pos = end + 1) {
int len;
for (end = pos; end < message + message_len && *end != '\n'; end++)
;
len = end - pos;
assert(len >= 0);
if ((flags & OUTPUT_FULL_WIDTH) || (prefix + len + 1 < n_columns))
fprintf(f, "%*s%s%.*s%s\n",
continuation * prefix, "",
color_on, len, pos, color_off);
else if (prefix < n_columns && n_columns - prefix >= 3) {
_cleanup_free_ char *e;
e = ellipsize_mem(pos, len, n_columns - prefix, 90);
if (!e)
fprintf(f, "%s%.*s%s\n", color_on, len, pos, color_off);
else
fprintf(f, "%s%s%s\n", color_on, e, color_off);
} else
fputs("...\n", f);
continuation = true;
}
}
static int output_short(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
int r;
const void *data;
size_t length;
size_t n = 0;
_cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0;
int p = LOG_INFO;
assert(f);
assert(j);
sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "_COMM=", &comm, &comm_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "_PID=", &pid, &pid_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
if (r < 0)
return r;
else if (r > 0)
continue;
r = parse_field(data, length, "MESSAGE=", &message, &message_len);
if (r < 0)
return r;
}
if (r < 0)
return r;
if (!message)
return 0;
if (!(flags & OUTPUT_SHOW_ALL))
strip_tab_ansi(&message, &message_len);
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
if (mode == OUTPUT_SHORT_MONOTONIC) {
uint64_t t;
sd_id128_t boot_id;
r = -ENOENT;
if (monotonic)
r = safe_atou64(monotonic, &t);
if (r < 0)
r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic timestamp: %s", strerror(-r));
return r;
}
fprintf(f, "[%5llu.%06llu]",
(unsigned long long) (t / USEC_PER_SEC),
(unsigned long long) (t % USEC_PER_SEC));
n += 1 + 5 + 1 + 6 + 1;
} else {
char buf[64];
uint64_t x;
time_t t;
struct tm tm;
r = -ENOENT;
if (realtime)
r = safe_atou64(realtime, &x);
if (r < 0)
r = sd_journal_get_realtime_usec(j, &x);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
t = (time_t) (x / USEC_PER_SEC);
if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
log_error("Failed to format time.");
return r;
}
fputs(buf, f);
n += strlen(buf);
}
if (hostname && shall_print(hostname, hostname_len, flags)) {
fprintf(f, " %.*s", (int) hostname_len, hostname);
n += hostname_len + 1;
}
if (identifier && shall_print(identifier, identifier_len, flags)) {
fprintf(f, " %.*s", (int) identifier_len, identifier);
n += identifier_len + 1;
} else if (comm && shall_print(comm, comm_len, flags)) {
fprintf(f, " %.*s", (int) comm_len, comm);
n += comm_len + 1;
} else
fputc(' ', f);
if (pid && shall_print(pid, pid_len, flags)) {
fprintf(f, "[%.*s]", (int) pid_len, pid);
n += pid_len + 2;
} else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
n += fake_pid_len + 2;
}
if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
char bytes[FORMAT_BYTES_MAX];
fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
} else {
fputs(": ", f);
print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
}
if (flags & OUTPUT_CATALOG)
print_catalog(f, j);
return 0;
}
static int output_verbose(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
const void *data;
size_t length;
_cleanup_free_ char *cursor = NULL;
uint64_t realtime;
char ts[FORMAT_TIMESTAMP_MAX];
int r;
assert(f);
assert(j);
sd_journal_set_data_threshold(j, 0);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
"Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
fprintf(f, "%s [%s]\n",
format_timestamp(ts, sizeof(ts), realtime),
cursor);
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
const char *c;
int fieldlen;
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
}
fieldlen = c - (const char*) data;
if ((flags & OUTPUT_SHOW_ALL) || (length < PRINT_THRESHOLD && utf8_is_printable(data, length))) {
fprintf(f, " %.*s=", fieldlen, (const char*)data);
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
} else {
char bytes[FORMAT_BYTES_MAX];
fprintf(f, " %.*s=[%s blob data]\n",
(int) (c - (const char*) data),
(const char*) data,
format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
}
}
if (r < 0)
return r;
if (flags & OUTPUT_CATALOG)
print_catalog(f, j);
return 0;
}
static int output_export(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
sd_id128_t boot_id;
char sid[33];
int r;
usec_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
const void *data;
size_t length;
assert(j);
sd_journal_set_data_threshold(j, 0);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
fprintf(f,
"__CURSOR=%s\n"
"__REALTIME_TIMESTAMP=%llu\n"
"__MONOTONIC_TIMESTAMP=%llu\n"
"_BOOT_ID=%s\n",
cursor,
(unsigned long long) realtime,
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
if (!utf8_is_printable(data, length)) {
const char *c;
uint64_t le64;
c = memchr(data, '=', length);
if (!c) {
log_error("Invalid field.");
return -EINVAL;
}
fwrite(data, c - (const char*) data, 1, f);
fputc('\n', f);
le64 = htole64(length - (c - (const char*) data) - 1);
fwrite(&le64, sizeof(le64), 1, f);
fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
} else
fwrite(data, length, 1, f);
fputc('\n', f);
}
if (r < 0)
return r;
fputc('\n', f);
return 0;
}
void json_escape(
FILE *f,
const char* p,
size_t l,
OutputFlags flags) {
assert(f);
assert(p);
if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
fputs("null", f);
else if (!utf8_is_printable(p, l)) {
bool not_first = false;
fputs("[ ", f);
while (l > 0) {
if (not_first)
fprintf(f, ", %u", (uint8_t) *p);
else {
not_first = true;
fprintf(f, "%u", (uint8_t) *p);
}
p++;
l--;
}
fputs(" ]", f);
} else {
fputc('\"', f);
while (l > 0) {
if (*p == '"' || *p == '\\') {
fputc('\\', f);
fputc(*p, f);
} else if (*p == '\n')
fputs("\\n", f);
else if (*p < ' ')
fprintf(f, "\\u%04x", *p);
else
fputc(*p, f);
p++;
l--;
}
fputc('\"', f);
}
}
static int output_json(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
uint64_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
const void *data;
size_t length;
sd_id128_t boot_id;
char sid[33], *k;
int r;
Hashmap *h = NULL;
bool done, separator;
assert(j);
sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
if (r < 0) {
log_error("Failed to get monotonic timestamp: %s", strerror(-r));
return r;
}
r = sd_journal_get_cursor(j, &cursor);
if (r < 0) {
log_error("Failed to get cursor: %s", strerror(-r));
return r;
}
if (mode == OUTPUT_JSON_PRETTY)
fprintf(f,
"{\n"
"\t\"__CURSOR\" : \"%s\",\n"
"\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
"\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
"\t\"_BOOT_ID\" : \"%s\"",
cursor,
(unsigned long long) realtime,
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
else {
if (mode == OUTPUT_JSON_SSE)
fputs("data: ", f);
fprintf(f,
"{ \"__CURSOR\" : \"%s\", "
"\"__REALTIME_TIMESTAMP\" : \"%llu\", "
"\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
"\"_BOOT_ID\" : \"%s\"",
cursor,
(unsigned long long) realtime,
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
}
h = hashmap_new(string_hash_func, string_compare_func);
if (!h)
return -ENOMEM;
/* First round, iterate through the entry and count how often each field appears */
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
const char *eq;
char *n;
unsigned u;
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
eq = memchr(data, '=', length);
if (!eq)
continue;
n = strndup(data, eq - (const char*) data);
if (!n) {
r = -ENOMEM;
goto finish;
}
u = PTR_TO_UINT(hashmap_get(h, n));
if (u == 0) {
r = hashmap_put(h, n, UINT_TO_PTR(1));
if (r < 0) {
free(n);
goto finish;
}
} else {
r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
free(n);
if (r < 0)
goto finish;
}
}
if (r < 0)
return r;
separator = true;
do {
done = true;
SD_JOURNAL_FOREACH_DATA(j, data, length) {
const char *eq;
char *kk, *n;
size_t m;
unsigned u;
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
eq = memchr(data, '=', length);
if (!eq)
continue;
if (separator) {
if (mode == OUTPUT_JSON_PRETTY)
fputs(",\n\t", f);
else
fputs(", ", f);
}
m = eq - (const char*) data;
n = strndup(data, m);
if (!n) {
r = -ENOMEM;
goto finish;
}
u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
if (u == 0) {
/* We already printed this, let's jump to the next */
free(n);
separator = false;
continue;
} else if (u == 1) {
/* Field only appears once, output it directly */
json_escape(f, data, m, flags);
fputs(" : ", f);
json_escape(f, eq + 1, length - m - 1, flags);
hashmap_remove(h, n);
free(kk);
free(n);
separator = true;
continue;
} else {
/* Field appears multiple times, output it as array */
json_escape(f, data, m, flags);
fputs(" : [ ", f);
json_escape(f, eq + 1, length - m - 1, flags);
/* Iterate through the end of the list */
while (sd_journal_enumerate_data(j, &data, &length) > 0) {
if (length < m + 1)
continue;
if (memcmp(data, n, m) != 0)
continue;
if (((const char*) data)[m] != '=')
continue;
fputs(", ", f);
json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
}
fputs(" ]", f);
hashmap_remove(h, n);
free(kk);
free(n);
/* Iterate data fields form the beginning */
done = false;
separator = true;
break;
}
}
} while (!done);
if (mode == OUTPUT_JSON_PRETTY)
fputs("\n}\n", f);
else if (mode == OUTPUT_JSON_SSE)
fputs("}\n\n", f);
else
fputs(" }\n", f);
r = 0;
finish:
while ((k = hashmap_steal_first_key(h)))
free(k);
hashmap_free(h);
return r;
}
static int output_cat(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
const void *data;
size_t l;
int r;
assert(j);
assert(f);
sd_journal_set_data_threshold(j, 0);
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
if (r < 0) {
/* An entry without MESSAGE=? */
if (r == -ENOENT)
return 0;
log_error("Failed to get data: %s", strerror(-r));
return r;
}
assert(l >= 8);
fwrite((const char*) data + 8, 1, l - 8, f);
fputc('\n', f);
return 0;
}
static int (*output_funcs[_OUTPUT_MODE_MAX])(
FILE *f,
sd_journal*j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_MONOTONIC] = output_short,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,
[OUTPUT_JSON_PRETTY] = output_json,
[OUTPUT_JSON_SSE] = output_json,
[OUTPUT_CAT] = output_cat
};
int output_journal(
FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
OutputFlags flags) {
int ret;
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
if (n_columns <= 0)
n_columns = columns();
ret = output_funcs[mode](f, j, mode, n_columns, flags);
fflush(stdout);
return ret;
}
static int show_journal(FILE *f,
sd_journal *j,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
OutputFlags flags) {
int r;
unsigned line = 0;
bool need_seek = false;
int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
assert(j);
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
/* Seek to end */
r = sd_journal_seek_tail(j);
if (r < 0)
goto finish;
r = sd_journal_previous_skip(j, how_many);
if (r < 0)
goto finish;
for (;;) {
for (;;) {
usec_t usec;
if (need_seek) {
r = sd_journal_next(j);
if (r < 0)
goto finish;
}
if (r == 0)
break;
need_seek = true;
if (not_before > 0) {
r = sd_journal_get_monotonic_usec(j, &usec, NULL);
/* -ESTALE is returned if the
timestamp is not from this boot */
if (r == -ESTALE)
continue;
else if (r < 0)
goto finish;
if (usec < not_before)
continue;
}
line ++;
r = output_journal(f, j, mode, n_columns, flags);
if (r < 0)
goto finish;
}
if (warn_cutoff && line < how_many && not_before > 0) {
sd_id128_t boot_id;
usec_t cutoff;
/* Check whether the cutoff line is too early */
r = sd_id128_get_boot(&boot_id);
if (r < 0)
goto finish;
r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
if (r < 0)
goto finish;
if (r > 0 && not_before < cutoff)
fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
warn_cutoff = false;
}
if (!(flags & OUTPUT_FOLLOW))
break;
r = sd_journal_wait(j, (usec_t) -1);
if (r < 0)
goto finish;
}
finish:
return r;
}
int add_matches_for_unit(sd_journal *j, const char *unit) {
int r;
_cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
assert(j);
assert(unit);
if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
asprintf(&m3, "UNIT=%s", unit) < 0)
return -ENOMEM;
(void)(
/* Look for messages from the service itself */
(r = sd_journal_add_match(j, m1, 0)) ||
/* Look for coredumps of the service */
(r = sd_journal_add_disjunction(j)) ||
(r = sd_journal_add_match(j,
"MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
(r = sd_journal_add_match(j, m2, 0)) ||
/* Look for messages from PID 1 about this service */
(r = sd_journal_add_disjunction(j)) ||
(r = sd_journal_add_match(j, "_PID=1", 0)) ||
(r = sd_journal_add_match(j, m3, 0))
);
return r;
}
int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
int r;
_cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
assert(j);
assert(unit);
if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 ||
asprintf(&m2, "USER_UNIT=%s", unit) < 0 ||
asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 ||
asprintf(&m4, "_UID=%d", uid) < 0)
return -ENOMEM;
(void) (
/* Look for messages from the user service itself */
(r = sd_journal_add_match(j, m1, 0)) ||
(r = sd_journal_add_match(j, m4, 0)) ||
/* Look for messages from systemd about this service */
(r = sd_journal_add_disjunction(j)) ||
(r = sd_journal_add_match(j, m2, 0)) ||
(r = sd_journal_add_match(j, m4, 0)) ||
/* Look for coredumps of the service */
(r = sd_journal_add_disjunction(j)) ||
(r = sd_journal_add_match(j, m3, 0)) ||
(r = sd_journal_add_match(j, m4, 0))
);
return r;
}
int add_match_this_boot(sd_journal *j) {
char match[9+32+1] = "_BOOT_ID=";
sd_id128_t boot_id;
int r;
assert(j);
r = sd_id128_get_boot(&boot_id);
if (r < 0) {
log_error("Failed to get boot id: %s", strerror(-r));
return r;
}
sd_id128_to_string(boot_id, match + 9);
r = sd_journal_add_match(j, match, strlen(match));
if (r < 0) {
log_error("Failed to add match: %s", strerror(-r));
return r;
}
r = sd_journal_add_conjunction(j);
if (r < 0)
return r;
return 0;
}
int show_journal_by_unit(
FILE *f,
const char *unit,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
uid_t uid,
OutputFlags flags,
bool system) {
_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);
assert(unit);
if (how_many <= 0)
return 0;
r = sd_journal_open(&j, jflags);
if (r < 0)
return r;
r = add_match_this_boot(j);
if (r < 0)
return r;
if (system)
r = add_matches_for_unit(j, unit);
else
r = add_matches_for_user_unit(j, unit, uid);
if (r < 0)
return r;
log_debug("Journal filter: %s", journal_make_match_string(j));
r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
if (r < 0)
return r;
return 0;
}
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
[OUTPUT_VERBOSE] = "verbose",
[OUTPUT_EXPORT] = "export",
[OUTPUT_JSON] = "json",
[OUTPUT_JSON_PRETTY] = "json-pretty",
[OUTPUT_JSON_SSE] = "json-sse",
[OUTPUT_CAT] = "cat"
};
DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);