ACTION_DUMP,
ACTION_GDB,
} arg_action = ACTION_LIST;
-
-static FILE* output = NULL;
static const char* arg_field = NULL;
-
static int arg_no_pager = false;
static int arg_no_legend = false;
+static int arg_one = false;
+
+static FILE* output = NULL;
static Set *new_matches(void) {
Set *set;
printf("%s [OPTIONS...]\n\n"
"List or retrieve coredumps from the journal.\n\n"
"Flags:\n"
- " -o --output=FILE Write output to FILE\n"
+ " -h --help Show this help\n"
+ " --version Print version string\n"
" --no-pager Do not pipe output into a pager\n"
- " --no-legend Do not print the column headers.\n\n"
+ " --no-legend Do not print the column headers.\n"
+ " -1 Show information about most recent entry only\n"
+ " -F --field=FIELD List all values a certain field takes\n"
+ " -o --output=FILE Write output to FILE\n\n"
"Commands:\n"
- " -h --help Show this help\n"
- " --version Print version string\n"
- " -F --field=FIELD List all values a certain field takes\n"
- " list [MATCHES...] List available coredumps\n"
+ " list [MATCHES...] List available coredumps (default)\n"
" info [MATCHES...] Show detailed information about one or more coredumps\n"
" dump [MATCHES...] Print first matching coredump to stdout\n"
" gdb [MATCHES...] Start gdb for the first matching coredump\n"
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
switch(c) {
case 'h':
arg_field = optarg;
break;
+ case '1':
+ arg_one = true;
+ break;
+
case '?':
return -EINVAL;
return 0;
}
-#define filename_escape(s) xescape((s), "./")
+#define filename_escape(s) xescape((s), "./ ")
static int make_coredump_path(sd_journal *j, char **ret) {
_cleanup_free_ char
if (!had_legend && !arg_no_legend)
fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
- FORMAT_TIMESTAMP_MAX-1, "TIME",
+ FORMAT_TIMESTAMP_WIDTH, "TIME",
6, "PID",
5, "UID",
5, "GID",
3, "SIG",
"EXE");
- fprintf(file, "%*s %*s %*s %*s %*s %s\n",
- FORMAT_TIMESTAMP_MAX-1, buf,
+ fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
+ FORMAT_TIMESTAMP_WIDTH, buf,
6, strna(pid),
5, strna(uid),
5, strna(gid),
*unit = NULL, *user_unit = NULL, *session = NULL,
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*coredump = NULL, *slice = NULL, *cgroup = NULL,
- *owner_uid = NULL, *message = NULL;
+ *owner_uid = NULL, *message = NULL, *timestamp = NULL;
const void *d;
size_t l;
+ int r;
assert(file);
assert(j);
retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
retrieve(d, l, "COREDUMP_SLICE", &slice);
retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
+ retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp);
retrieve(d, l, "_BOOT_ID", &boot_id);
retrieve(d, l, "_MACHINE_ID", &machine_id);
retrieve(d, l, "_HOSTNAME", &hostname);
if (need_space)
fputs("\n", file);
- fprintf(file,
- " PID: %s%s%s\n",
- ansi_highlight(), strna(pid), ansi_highlight_off());
+ if (comm)
+ fprintf(file,
+ " PID: %s%s%s (%s)\n",
+ ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
+ else
+ fprintf(file,
+ " PID: %s%s%s\n",
+ ansi_highlight(), strna(pid), ansi_highlight_off());
if (uid) {
uid_t n;
fprintf(file, " Signal: %s\n", sgnl);
}
- if (exe)
- fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
- if (comm)
- fprintf(file, " Comm: %s\n", comm);
+ if (timestamp) {
+ usec_t u;
+
+ r = safe_atou64(timestamp, &u);
+ if (r >= 0) {
+ char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
+
+ fprintf(file,
+ " Timestamp: %s (%s)\n",
+ format_timestamp(absolute, sizeof(absolute), u),
+ format_timestamp_relative(relative, sizeof(relative), u));
+
+ } else
+ fprintf(file, " Timestamp: %s\n", timestamp);
+ }
+
if (cmdline)
fprintf(file, " Command Line: %s\n", cmdline);
+ if (exe)
+ fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
if (cgroup)
fprintf(file, " Control Group: %s\n", cgroup);
if (unit)
return 0;
}
-static int dump_list(sd_journal *j) {
- int found = 0;
-
- assert(j);
-
- /* The coredumps are likely to compressed, and for just
- * listing them we don't need to decompress them, so let's
- * pick a fairly low data threshold here */
- sd_journal_set_data_threshold(j, 4096);
-
- SD_JOURNAL_FOREACH(j) {
- if (arg_action == ACTION_INFO)
- print_info(stdout, j, found++);
- else if (arg_field)
- print_field(stdout, j);
- else
- print_list(stdout, j, found++);
- }
-
- if (!arg_field && !found) {
- log_notice("No coredumps found");
- return -ESRCH;
- }
-
- return 0;
-}
-
static int focus(sd_journal *j) {
int r;
return r;
}
if (r == 0) {
- log_error("No match found");
+ log_error("No match found.");
return -ESRCH;
}
return r;
}
+static void print_entry(sd_journal *j, unsigned n_found) {
+ assert(j);
+
+ if (arg_action == ACTION_INFO)
+ print_info(stdout, j, n_found);
+ else if (arg_field)
+ print_field(stdout, j);
+ else
+ print_list(stdout, j, n_found);
+}
+
+static int dump_list(sd_journal *j) {
+ unsigned n_found = 0;
+ int r;
+
+ assert(j);
+
+ /* The coredumps are likely to compressed, and for just
+ * listing them we don't need to decompress them, so let's
+ * pick a fairly low data threshold here */
+ sd_journal_set_data_threshold(j, 4096);
+
+ if (arg_one) {
+ r = focus(j);
+ if (r < 0)
+ return r;
+
+ print_entry(j, 0);
+ } else {
+ SD_JOURNAL_FOREACH(j)
+ print_entry(j, n_found++);
+
+ if (!arg_field && n_found <= 0) {
+ log_notice("No coredumps found.");
+ return -ESRCH;
+ }
+ }
+
+ return 0;
+}
+
static int dump_core(sd_journal* j) {
const void *data;
size_t len, ret;