1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <systemd/sd-journal.h>
35 #include "path-util.h"
38 #include "journal-internal.h"
48 } arg_action = ACTION_LIST;
49 static const char* arg_field = NULL;
50 static int arg_no_pager = false;
51 static int arg_no_legend = false;
52 static int arg_one = false;
54 static FILE* output = NULL;
56 static Set *new_matches(void) {
61 set = set_new(trivial_hash_func, trivial_compare_func);
67 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
74 r = set_consume(set, tmp);
76 log_error("failed to add to set: %s", strerror(-r));
84 static int help(void) {
86 printf("%s [OPTIONS...]\n\n"
87 "List or retrieve coredumps from the journal.\n\n"
89 " -h --help Show this help\n"
90 " --version Print version string\n"
91 " --no-pager Do not pipe output into a pager\n"
92 " --no-legend Do not print the column headers.\n"
93 " -1 Show information about most recent entry only\n"
94 " -F --field=FIELD List all values a certain field takes\n"
95 " -o --output=FILE Write output to FILE\n\n"
98 " list [MATCHES...] List available coredumps (default)\n"
99 " info [MATCHES...] Show detailed information about one or more coredumps\n"
100 " dump [MATCHES...] Print first matching coredump to stdout\n"
101 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
102 , program_invocation_short_name);
107 static int add_match(Set *set, const char *match) {
111 char *pattern = NULL;
112 _cleanup_free_ char *p = NULL;
114 if (strchr(match, '='))
116 else if (strchr(match, '/')) {
117 p = path_make_absolute_cwd(match);
122 prefix = "COREDUMP_EXE=";
124 else if (safe_atou(match, &pid) == 0)
125 prefix = "COREDUMP_PID=";
127 prefix = "COREDUMP_COMM=";
129 pattern = strjoin(prefix, match, NULL);
133 log_debug("Adding pattern: %s", pattern);
134 r = set_put(set, pattern);
136 log_error("Failed to add pattern '%s': %s",
137 pattern, strerror(-r));
144 log_error("Failed to add match: %s", strerror(-r));
148 static int parse_argv(int argc, char *argv[], Set *matches) {
157 static const struct option options[] = {
158 { "help", no_argument, NULL, 'h' },
159 { "version" , no_argument, NULL, ARG_VERSION },
160 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
161 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
162 { "output", required_argument, NULL, 'o' },
163 { "field", required_argument, NULL, 'F' },
170 while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
174 arg_action = ACTION_NONE;
178 arg_action = ACTION_NONE;
179 puts(PACKAGE_STRING);
180 puts(SYSTEMD_FEATURES);
188 arg_no_legend = true;
193 log_error("cannot set output more than once");
197 output = fopen(optarg, "we");
199 log_error("writing to '%s': %m", optarg);
207 log_error("cannot use --field/-F more than once");
221 assert_not_reached("Unhandled option");
225 const char *cmd = argv[optind++];
226 if (streq(cmd, "list"))
227 arg_action = ACTION_LIST;
228 else if (streq(cmd, "dump"))
229 arg_action = ACTION_DUMP;
230 else if (streq(cmd, "gdb"))
231 arg_action = ACTION_GDB;
232 else if (streq(cmd, "info"))
233 arg_action = ACTION_INFO;
235 log_error("Unknown action '%s'", cmd);
240 if (arg_field && arg_action != ACTION_LIST) {
241 log_error("Option --field/-F only makes sense with list");
245 while (optind < argc) {
246 r = add_match(matches, argv[optind]);
255 static int retrieve(const void *data,
263 ident = strlen(name) + 1; /* name + "=" */
268 if (memcmp(data, name, ident - 1) != 0)
271 if (((const char*) data)[ident - 1] != '=')
274 v = strndup((const char*)data + ident, len - ident);
284 static void print_field(FILE* file, sd_journal *j) {
285 _cleanup_free_ char *value = NULL;
294 SD_JOURNAL_FOREACH_DATA(j, d, l)
295 retrieve(d, l, arg_field, &value);
298 fprintf(file, "%s\n", value);
301 static int print_list(FILE* file, sd_journal *j, int had_legend) {
303 *pid = NULL, *uid = NULL, *gid = NULL,
304 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
309 char buf[FORMAT_TIMESTAMP_MAX];
316 SD_JOURNAL_FOREACH_DATA(j, d, l) {
317 retrieve(d, l, "COREDUMP_PID", &pid);
318 retrieve(d, l, "COREDUMP_UID", &uid);
319 retrieve(d, l, "COREDUMP_GID", &gid);
320 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
321 retrieve(d, l, "COREDUMP_EXE", &exe);
322 retrieve(d, l, "COREDUMP_COMM", &comm);
323 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
324 retrieve(d, l, "COREDUMP_FILENAME", &filename);
327 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
328 log_warning("Empty coredump log entry");
332 r = sd_journal_get_realtime_usec(j, &t);
334 log_error("Failed to get realtime timestamp: %s", strerror(-r));
338 format_timestamp(buf, sizeof(buf), t);
339 present = filename && access(filename, F_OK) == 0;
341 if (!had_legend && !arg_no_legend)
342 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
343 FORMAT_TIMESTAMP_WIDTH, "TIME",
351 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
352 FORMAT_TIMESTAMP_WIDTH, buf,
357 1, present ? "*" : "",
358 strna(exe ?: (comm ?: cmdline)));
363 static int print_info(FILE *file, sd_journal *j, bool need_space) {
365 *pid = NULL, *uid = NULL, *gid = NULL,
366 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
367 *unit = NULL, *user_unit = NULL, *session = NULL,
368 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
369 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
370 *message = NULL, *timestamp = NULL, *filename = NULL;
378 SD_JOURNAL_FOREACH_DATA(j, d, l) {
379 retrieve(d, l, "COREDUMP_PID", &pid);
380 retrieve(d, l, "COREDUMP_UID", &uid);
381 retrieve(d, l, "COREDUMP_GID", &gid);
382 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
383 retrieve(d, l, "COREDUMP_EXE", &exe);
384 retrieve(d, l, "COREDUMP_COMM", &comm);
385 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
386 retrieve(d, l, "COREDUMP_UNIT", &unit);
387 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
388 retrieve(d, l, "COREDUMP_SESSION", &session);
389 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
390 retrieve(d, l, "COREDUMP_SLICE", &slice);
391 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
392 retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp);
393 retrieve(d, l, "COREDUMP_FILENAME", &filename);
394 retrieve(d, l, "_BOOT_ID", &boot_id);
395 retrieve(d, l, "_MACHINE_ID", &machine_id);
396 retrieve(d, l, "_HOSTNAME", &hostname);
397 retrieve(d, l, "MESSAGE", &message);
405 " PID: %s%s%s (%s)\n",
406 ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
410 ansi_highlight(), strna(pid), ansi_highlight_off());
415 if (parse_uid(uid, &n) >= 0) {
416 _cleanup_free_ char *u = NULL;
432 if (parse_gid(gid, &n) >= 0) {
433 _cleanup_free_ char *g = NULL;
449 if (safe_atoi(sgnl, &sig) >= 0)
450 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
452 fprintf(file, " Signal: %s\n", sgnl);
458 r = safe_atou64(timestamp, &u);
460 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
463 " Timestamp: %s (%s)\n",
464 format_timestamp(absolute, sizeof(absolute), u),
465 format_timestamp_relative(relative, sizeof(relative), u));
468 fprintf(file, " Timestamp: %s\n", timestamp);
472 fprintf(file, " Command Line: %s\n", cmdline);
474 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
476 fprintf(file, " Control Group: %s\n", cgroup);
478 fprintf(file, " Unit: %s\n", unit);
480 fprintf(file, " User Unit: %s\n", unit);
482 fprintf(file, " Slice: %s\n", slice);
484 fprintf(file, " Session: %s\n", session);
488 if (parse_uid(owner_uid, &n) >= 0) {
489 _cleanup_free_ char *u = NULL;
493 " Owner UID: %s (%s)\n",
502 fprintf(file, " Boot ID: %s\n", boot_id);
504 fprintf(file, " Machine ID: %s\n", machine_id);
506 fprintf(file, " Hostname: %s\n", hostname);
508 if (filename && access(filename, F_OK) == 0)
509 fprintf(file, " Coredump: %s\n", filename);
512 _cleanup_free_ char *m = NULL;
514 m = strreplace(message, "\n", "\n ");
516 fprintf(file, " Message: %s\n", strstrip(m ?: message));
522 static int focus(sd_journal *j) {
525 r = sd_journal_seek_tail(j);
527 r = sd_journal_previous(j);
529 log_error("Failed to search journal: %s", strerror(-r));
533 log_error("No match found.");
539 static void print_entry(sd_journal *j, unsigned n_found) {
542 if (arg_action == ACTION_INFO)
543 print_info(stdout, j, n_found);
545 print_field(stdout, j);
547 print_list(stdout, j, n_found);
550 static int dump_list(sd_journal *j) {
551 unsigned n_found = 0;
556 /* The coredumps are likely to compressed, and for just
557 * listing them we don't need to decompress them, so let's
558 * pick a fairly low data threshold here */
559 sd_journal_set_data_threshold(j, 4096);
568 SD_JOURNAL_FOREACH(j)
569 print_entry(j, n_found++);
571 if (!arg_field && n_found <= 0) {
572 log_notice("No coredumps found.");
580 static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
582 _cleanup_free_ char *filename = NULL;
586 assert((fd >= 0) != !!path);
587 assert(!!path == !!unlink_temp);
589 /* Prefer uncompressed file to journal (probably cached) to
590 * compressed file (probably uncached). */
591 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
592 if (r < 0 && r != -ENOENT)
593 log_warning("Failed to retrieve COREDUMP_FILENAME: %s", strerror(-r));
595 retrieve(data, len, "COREDUMP_FILENAME", &filename);
597 if (filename && access(filename, R_OK) < 0) {
598 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
599 "File %s is not readable: %m", filename);
604 if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
612 _cleanup_close_ int fdt = -1;
616 temp = strdup("/var/tmp/coredump-XXXXXX");
620 fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
622 log_error("Failed to create temporary file: %m");
625 log_debug("Created temporary file %s", temp);
630 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
638 sz = write(fdt, data, len);
640 log_error("Failed to write temporary file: %m");
644 if (sz != (ssize_t) len) {
645 log_error("Short write to temporary file.");
649 } else if (filename) {
650 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
651 _cleanup_close_ int fdf;
653 fdf = open(filename, O_RDONLY | O_CLOEXEC);
655 log_error("Failed to open %s: %m", filename);
660 r = decompress_stream(filename, fdf, fd, -1);
662 log_error("Failed to decompress %s: %s", filename, strerror(-r));
666 log_error("Cannot decompress file. Compiled without compression support.");
672 log_error("Cannot retrieve coredump from journal nor disk.");
674 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
688 log_debug("Removed temporary file %s", temp);
694 static int dump_core(sd_journal* j) {
703 print_info(output ? stdout : stderr, j, false);
705 if (on_tty() && !output) {
706 log_error("Refusing to dump core to tty.");
710 r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
712 log_error("Coredump retrieval failed: %s", strerror(-r));
716 r = sd_journal_previous(j);
718 log_warning("More than one entry matches, ignoring rest.");
723 static int run_gdb(sd_journal *j) {
724 _cleanup_free_ char *exe = NULL, *path = NULL;
725 bool unlink_path = false;
738 print_info(stdout, j, false);
741 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
743 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
747 assert(len > strlen("COREDUMP_EXE="));
748 data += strlen("COREDUMP_EXE=");
749 len -= strlen("COREDUMP_EXE=");
751 exe = strndup(data, len);
755 if (endswith(exe, " (deleted)")) {
756 log_error("Binary already deleted.");
760 if (!path_is_absolute(exe)) {
761 log_error("Binary is not an absolute path.");
765 r = save_core(j, -1, &path, &unlink_path);
767 log_error("Failed to retrieve core: %s", strerror(-r));
773 log_error("Failed to fork(): %m");
778 execlp("gdb", "gdb", exe, path, NULL);
780 log_error("Failed to invoke gdb: %m");
784 r = wait_for_terminate(pid, &st);
786 log_error("Failed to wait for gdb: %m");
790 r = st.si_code == CLD_EXITED ? st.si_status : 255;
794 log_debug("Removed temporary file %s", path);
801 int main(int argc, char *argv[]) {
802 _cleanup_journal_close_ sd_journal*j = NULL;
806 _cleanup_set_free_free_ Set *matches = NULL;
808 setlocale(LC_ALL, "");
809 log_parse_environment();
812 matches = new_matches();
818 r = parse_argv(argc, argv, matches);
822 if (arg_action == ACTION_NONE)
825 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
827 log_error("Failed to open journal: %s", strerror(-r));
831 /* We want full data, nothing truncated. */
832 sd_journal_set_data_threshold(j, 0);
834 SET_FOREACH(match, matches, it) {
835 r = sd_journal_add_match(j, match, strlen(match));
837 log_error("Failed to add match '%s': %s",
838 match, strerror(-r));
843 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
844 _cleanup_free_ char *filter;
846 filter = journal_make_match_string(j);
847 log_debug("Journal filter: %s", filter);
869 assert_not_reached("Shouldn't be here");
878 return r >= 0 ? r : EXIT_FAILURE;