+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include "fileio.h"
#include "fs-util.h"
#include "journal-internal.h"
+#include "journal-util.h"
#include "log.h"
#include "macro.h"
#include "pager.h"
#include "user-util.h"
#include "util.h"
+#define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
+
+static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
+
static enum {
ACTION_NONE,
ACTION_INFO,
static FILE* arg_output = NULL;
static bool arg_reverse = false;
static char** arg_matches = NULL;
+static bool arg_quiet = false;
static int add_match(sd_journal *j, const char *match) {
_cleanup_free_ char *p = NULL;
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not print the column headers.\n"
" -1 Show information about most recent entry only\n"
+ " -S --since=DATE Only print coredumps since the date\n"
+ " -U --until=DATE Only print coredumps until the date\n"
" -r --reverse Show the newest entries first\n"
" -F --field=FIELD List all values a certain field takes\n"
" -o --output=FILE Write output to FILE\n"
" -D --directory=DIR Use journal files from directory\n\n"
-
+ " -q --quiet Do not show info messages and privilege warning\n"
"Commands:\n"
" list [MATCHES...] List available coredumps (default)\n"
" info [MATCHES...] Show detailed information about one or more coredumps\n"
ARG_NO_LEGEND,
};
- int c;
+ int c, r;
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "field", required_argument, NULL, 'F' },
{ "directory", required_argument, NULL, 'D' },
{ "reverse", no_argument, NULL, 'r' },
+ { "since", required_argument, NULL, 'S' },
+ { "until", required_argument, NULL, 'U' },
+ { "quiet", no_argument, NULL, 'q' },
{}
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ho:F:1D:r", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
switch(c) {
-
case 'h':
arg_action = ACTION_NONE;
help();
break;
+ case 'S':
+ r = parse_timestamp(optarg, &arg_since);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ break;
+
+ case 'U':
+ r = parse_timestamp(optarg, &arg_until);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+ break;
+
case 'F':
if (arg_field) {
log_error("cannot use --field/-F more than once");
arg_reverse = true;
break;
+ case 'q':
+ arg_quiet = true;
+ break;
+
case '?':
return -EINVAL;
assert_not_reached("Unhandled option");
}
+ if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
+ arg_since > arg_until) {
+ log_error("--since= must be before --until=.");
+ return -EINVAL;
+ }
+
if (optind < argc) {
const char *cmd = argv[optind++];
if (streq(cmd, "list"))
_cleanup_free_ char
*mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
- *filename = NULL, *coredump = NULL;
+ *filename = NULL, *truncated = NULL, *coredump = NULL;
const void *d;
size_t l;
usec_t t;
RETRIEVE(d, l, "COREDUMP_COMM", comm);
RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
}
format_timestamp(buf, sizeof(buf), t);
if (!had_legend && !arg_no_legend)
- fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+ fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, "TIME",
6, "PID",
5, "UID",
5, "GID",
3, "SIG",
- 8, "COREFILE",
+ 9, "COREFILE",
"EXE");
normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
else
present = "-";
+ if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
+ present = "truncated";
+
fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
FORMAT_TIMESTAMP_WIDTH, buf,
6, strna(pid),
5, strna(uid),
5, strna(gid),
3, normal_coredump ? strna(sgnl) : "-",
- 8, present,
+ 9, present,
strna(exe ?: (comm ?: cmdline)));
return 0;
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
*message = NULL, *timestamp = NULL, *filename = NULL,
- *coredump = NULL;
+ *truncated = NULL, *coredump = NULL;
const void *d;
size_t l;
bool normal_coredump;
RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+ RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
RETRIEVE(d, l, "_BOOT_ID", boot_id);
RETRIEVE(d, l, "_MACHINE_ID", machine_id);
if (hostname)
fprintf(file, " Hostname: %s\n", hostname);
- if (filename)
- fprintf(file, " Storage: %s%s\n", filename,
- access(filename, R_OK) < 0 ? " (inaccessible)" : "");
+ if (filename) {
+ bool inacc, trunc;
+
+ inacc = access(filename, R_OK) < 0;
+ trunc = truncated && parse_boolean(truncated) > 0;
+
+ if (inacc || trunc)
+ fprintf(file, " Storage: %s%s (%s%s%s)%s\n",
+ ansi_highlight_red(),
+ filename,
+ inacc ? "inaccessible" : "",
+ inacc && trunc ? ", " : "",
+ trunc ? "truncated" : "",
+ ansi_normal());
+ else
+ fprintf(file, " Storage: %s\n", filename);
+ }
+
else if (coredump)
fprintf(file, " Storage: journal\n");
else
return print_entry(j, 0);
} else {
- if (!arg_reverse) {
- SD_JOURNAL_FOREACH(j) {
- r = print_entry(j, n_found++);
+ if (arg_since != USEC_INFINITY && !arg_reverse)
+ r = sd_journal_seek_realtime_usec(j, arg_since);
+ else if (arg_until != USEC_INFINITY && arg_reverse)
+ r = sd_journal_seek_realtime_usec(j, arg_until);
+ else if (arg_reverse)
+ r = sd_journal_seek_tail(j);
+ else
+ r = sd_journal_seek_head(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to seek to date: %m");
+
+ for (;;) {
+ if (!arg_reverse)
+ r = sd_journal_next(j);
+ else
+ r = sd_journal_previous(j);
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to iterate through journal: %m");
+
+ if (r == 0)
+ break;
+
+ if (arg_until != USEC_INFINITY && !arg_reverse) {
+ usec_t usec;
+
+ r = sd_journal_get_realtime_usec(j, &usec);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to determine timestamp: %m");
+ if (usec > arg_until)
+ continue;
}
- } else {
- SD_JOURNAL_FOREACH_BACKWARDS(j) {
- r = print_entry(j, n_found++);
+
+ if (arg_since != USEC_INFINITY && arg_reverse) {
+ usec_t usec;
+
+ r = sd_journal_get_realtime_usec(j, &usec);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to determine timestamp: %m");
+ if (usec < arg_since)
+ continue;
}
+
+ r = print_entry(j, n_found++);
+ if (r < 0)
+ return r;
}
if (!arg_field && n_found <= 0) {
- log_notice("No coredumps found.");
+ if (!arg_quiet)
+ log_notice("No coredumps found.");
return -ESRCH;
}
}
}
if (filename) {
-#if defined(HAVE_XZ) || defined(HAVE_LZ4)
+#if HAVE_XZ || HAVE_LZ4
_cleanup_close_ int fdf;
fdf = open(filename, O_RDONLY | O_CLOEXEC);
return r;
r = sd_journal_previous(j);
- if (r > 0)
- log_warning("More than one entry matches, ignoring rest.");
+ if (r > 0 && !arg_quiet)
+ log_notice("More than one entry matches, ignoring rest.");
return 0;
}
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int c = 0, r;
- const char *state;
+ const char *id, *state, *substate;
+
+ if (arg_quiet)
+ return false;
r = sd_bus_default_system(&bus);
if (r < 0)
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_call(bus, m, 0, &error, &reply);
+ r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
bus_error_message(&error, r));
while ((r = sd_bus_message_read(
reply, "(ssssssouso)",
- NULL, NULL, NULL, &state, NULL,
- NULL, NULL, NULL, NULL, NULL)) > 0)
- if (!STR_IN_SET(state, "dead", "failed"))
- c++;
-
+ &id, NULL, NULL, &state, &substate,
+ NULL, NULL, NULL, NULL, NULL)) > 0) {
+ bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
+ log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
+ c += found;
+ }
if (r < 0)
return bus_log_parse_error(r);
}
}
+ r = journal_access_check_and_warn(j, arg_quiet);
+ if (r < 0)
+ goto end;
+
r = add_matches(j);
if (r < 0)
goto end;