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_consume(set, pattern);
136 log_error("Failed to add pattern '%s': %s",
137 pattern, strerror(-r));
143 log_error("Failed to add match: %s", strerror(-r));
147 static int parse_argv(int argc, char *argv[], Set *matches) {
156 static const struct option options[] = {
157 { "help", no_argument, NULL, 'h' },
158 { "version" , no_argument, NULL, ARG_VERSION },
159 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
160 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
161 { "output", required_argument, NULL, 'o' },
162 { "field", required_argument, NULL, 'F' },
169 while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
173 arg_action = ACTION_NONE;
177 arg_action = ACTION_NONE;
178 puts(PACKAGE_STRING);
179 puts(SYSTEMD_FEATURES);
187 arg_no_legend = true;
192 log_error("cannot set output more than once");
196 output = fopen(optarg, "we");
198 log_error("writing to '%s': %m", optarg);
206 log_error("cannot use --field/-F more than once");
220 assert_not_reached("Unhandled option");
224 const char *cmd = argv[optind++];
225 if (streq(cmd, "list"))
226 arg_action = ACTION_LIST;
227 else if (streq(cmd, "dump"))
228 arg_action = ACTION_DUMP;
229 else if (streq(cmd, "gdb"))
230 arg_action = ACTION_GDB;
231 else if (streq(cmd, "info"))
232 arg_action = ACTION_INFO;
234 log_error("Unknown action '%s'", cmd);
239 if (arg_field && arg_action != ACTION_LIST) {
240 log_error("Option --field/-F only makes sense with list");
244 while (optind < argc) {
245 r = add_match(matches, argv[optind]);
254 static int retrieve(const void *data,
262 ident = strlen(name) + 1; /* name + "=" */
267 if (memcmp(data, name, ident - 1) != 0)
270 if (((const char*) data)[ident - 1] != '=')
273 v = strndup((const char*)data + ident, len - ident);
283 static void print_field(FILE* file, sd_journal *j) {
284 _cleanup_free_ char *value = NULL;
293 SD_JOURNAL_FOREACH_DATA(j, d, l)
294 retrieve(d, l, arg_field, &value);
297 fprintf(file, "%s\n", value);
300 static int print_list(FILE* file, sd_journal *j, int had_legend) {
302 *pid = NULL, *uid = NULL, *gid = NULL,
303 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
308 char buf[FORMAT_TIMESTAMP_MAX];
315 SD_JOURNAL_FOREACH_DATA(j, d, l) {
316 retrieve(d, l, "COREDUMP_PID", &pid);
317 retrieve(d, l, "COREDUMP_UID", &uid);
318 retrieve(d, l, "COREDUMP_GID", &gid);
319 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
320 retrieve(d, l, "COREDUMP_EXE", &exe);
321 retrieve(d, l, "COREDUMP_COMM", &comm);
322 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
323 retrieve(d, l, "COREDUMP_FILENAME", &filename);
326 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
327 log_warning("Empty coredump log entry");
331 r = sd_journal_get_realtime_usec(j, &t);
333 log_error("Failed to get realtime timestamp: %s", strerror(-r));
337 format_timestamp(buf, sizeof(buf), t);
338 present = filename && access(filename, F_OK) == 0;
340 if (!had_legend && !arg_no_legend)
341 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
342 FORMAT_TIMESTAMP_WIDTH, "TIME",
350 fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
351 FORMAT_TIMESTAMP_WIDTH, buf,
356 1, present ? "*" : "",
357 strna(exe ?: (comm ?: cmdline)));
362 static int print_info(FILE *file, sd_journal *j, bool need_space) {
364 *pid = NULL, *uid = NULL, *gid = NULL,
365 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
366 *unit = NULL, *user_unit = NULL, *session = NULL,
367 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
368 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
369 *message = NULL, *timestamp = NULL, *filename = NULL;
377 SD_JOURNAL_FOREACH_DATA(j, d, l) {
378 retrieve(d, l, "COREDUMP_PID", &pid);
379 retrieve(d, l, "COREDUMP_UID", &uid);
380 retrieve(d, l, "COREDUMP_GID", &gid);
381 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
382 retrieve(d, l, "COREDUMP_EXE", &exe);
383 retrieve(d, l, "COREDUMP_COMM", &comm);
384 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
385 retrieve(d, l, "COREDUMP_UNIT", &unit);
386 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
387 retrieve(d, l, "COREDUMP_SESSION", &session);
388 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
389 retrieve(d, l, "COREDUMP_SLICE", &slice);
390 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
391 retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp);
392 retrieve(d, l, "COREDUMP_FILENAME", &filename);
393 retrieve(d, l, "_BOOT_ID", &boot_id);
394 retrieve(d, l, "_MACHINE_ID", &machine_id);
395 retrieve(d, l, "_HOSTNAME", &hostname);
396 retrieve(d, l, "MESSAGE", &message);
404 " PID: %s%s%s (%s)\n",
405 ansi_highlight(), strna(pid), ansi_highlight_off(), comm);
409 ansi_highlight(), strna(pid), ansi_highlight_off());
414 if (parse_uid(uid, &n) >= 0) {
415 _cleanup_free_ char *u = NULL;
431 if (parse_gid(gid, &n) >= 0) {
432 _cleanup_free_ char *g = NULL;
448 if (safe_atoi(sgnl, &sig) >= 0)
449 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
451 fprintf(file, " Signal: %s\n", sgnl);
457 r = safe_atou64(timestamp, &u);
459 char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX];
462 " Timestamp: %s (%s)\n",
463 format_timestamp(absolute, sizeof(absolute), u),
464 format_timestamp_relative(relative, sizeof(relative), u));
467 fprintf(file, " Timestamp: %s\n", timestamp);
471 fprintf(file, " Command Line: %s\n", cmdline);
473 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
475 fprintf(file, " Control Group: %s\n", cgroup);
477 fprintf(file, " Unit: %s\n", unit);
479 fprintf(file, " User Unit: %s\n", unit);
481 fprintf(file, " Slice: %s\n", slice);
483 fprintf(file, " Session: %s\n", session);
487 if (parse_uid(owner_uid, &n) >= 0) {
488 _cleanup_free_ char *u = NULL;
492 " Owner UID: %s (%s)\n",
501 fprintf(file, " Boot ID: %s\n", boot_id);
503 fprintf(file, " Machine ID: %s\n", machine_id);
505 fprintf(file, " Hostname: %s\n", hostname);
507 if (filename && access(filename, F_OK) == 0)
508 fprintf(file, " Coredump: %s\n", filename);
511 _cleanup_free_ char *m = NULL;
513 m = strreplace(message, "\n", "\n ");
515 fprintf(file, " Message: %s\n", strstrip(m ?: message));
521 static int focus(sd_journal *j) {
524 r = sd_journal_seek_tail(j);
526 r = sd_journal_previous(j);
528 log_error("Failed to search journal: %s", strerror(-r));
532 log_error("No match found.");
538 static void print_entry(sd_journal *j, unsigned n_found) {
541 if (arg_action == ACTION_INFO)
542 print_info(stdout, j, n_found);
544 print_field(stdout, j);
546 print_list(stdout, j, n_found);
549 static int dump_list(sd_journal *j) {
550 unsigned n_found = 0;
555 /* The coredumps are likely to compressed, and for just
556 * listing them we don't need to decompress them, so let's
557 * pick a fairly low data threshold here */
558 sd_journal_set_data_threshold(j, 4096);
567 SD_JOURNAL_FOREACH(j)
568 print_entry(j, n_found++);
570 if (!arg_field && n_found <= 0) {
571 log_notice("No coredumps found.");
579 static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
581 _cleanup_free_ char *filename = NULL;
585 assert((fd >= 0) != !!path);
586 assert(!!path == !!unlink_temp);
588 /* Prefer uncompressed file to journal (probably cached) to
589 * compressed file (probably uncached). */
590 r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
591 if (r < 0 && r != -ENOENT)
592 log_warning("Failed to retrieve COREDUMP_FILENAME: %s", strerror(-r));
594 retrieve(data, len, "COREDUMP_FILENAME", &filename);
596 if (filename && access(filename, R_OK) < 0) {
597 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
598 "File %s is not readable: %m", filename);
603 if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
611 _cleanup_close_ int fdt = -1;
615 temp = strdup("/var/tmp/coredump-XXXXXX");
619 fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
621 log_error("Failed to create temporary file: %m");
624 log_debug("Created temporary file %s", temp);
629 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
637 sz = write(fdt, data, len);
639 log_error("Failed to write temporary file: %m");
643 if (sz != (ssize_t) len) {
644 log_error("Short write to temporary file.");
648 } else if (filename) {
649 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
650 _cleanup_close_ int fdf;
652 fdf = open(filename, O_RDONLY | O_CLOEXEC);
654 log_error("Failed to open %s: %m", filename);
659 r = decompress_stream(filename, fdf, fd, -1);
661 log_error("Failed to decompress %s: %s", filename, strerror(-r));
665 log_error("Cannot decompress file. Compiled without compression support.");
671 log_error("Cannot retrieve coredump from journal nor disk.");
673 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
687 log_debug("Removed temporary file %s", temp);
693 static int dump_core(sd_journal* j) {
702 print_info(output ? stdout : stderr, j, false);
704 if (on_tty() && !output) {
705 log_error("Refusing to dump core to tty.");
709 r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL);
711 log_error("Coredump retrieval failed: %s", strerror(-r));
715 r = sd_journal_previous(j);
717 log_warning("More than one entry matches, ignoring rest.");
722 static int run_gdb(sd_journal *j) {
723 _cleanup_free_ char *exe = NULL, *path = NULL;
724 bool unlink_path = false;
737 print_info(stdout, j, false);
740 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
742 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
746 assert(len > strlen("COREDUMP_EXE="));
747 data += strlen("COREDUMP_EXE=");
748 len -= strlen("COREDUMP_EXE=");
750 exe = strndup(data, len);
754 if (endswith(exe, " (deleted)")) {
755 log_error("Binary already deleted.");
759 if (!path_is_absolute(exe)) {
760 log_error("Binary is not an absolute path.");
764 r = save_core(j, -1, &path, &unlink_path);
766 log_error("Failed to retrieve core: %s", strerror(-r));
772 log_error("Failed to fork(): %m");
777 execlp("gdb", "gdb", exe, path, NULL);
779 log_error("Failed to invoke gdb: %m");
783 r = wait_for_terminate(pid, &st);
785 log_error("Failed to wait for gdb: %m");
789 r = st.si_code == CLD_EXITED ? st.si_status : 255;
793 log_debug("Removed temporary file %s", path);
800 int main(int argc, char *argv[]) {
801 _cleanup_journal_close_ sd_journal*j = NULL;
805 _cleanup_set_free_free_ Set *matches = NULL;
807 setlocale(LC_ALL, "");
808 log_parse_environment();
811 matches = new_matches();
817 r = parse_argv(argc, argv, matches);
821 if (arg_action == ACTION_NONE)
824 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
826 log_error("Failed to open journal: %s", strerror(-r));
830 /* We want full data, nothing truncated. */
831 sd_journal_set_data_threshold(j, 0);
833 SET_FOREACH(match, matches, it) {
834 r = sd_journal_add_match(j, match, strlen(match));
836 log_error("Failed to add match '%s': %s",
837 match, strerror(-r));
842 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
843 _cleanup_free_ char *filter;
845 filter = journal_make_match_string(j);
846 log_debug("Journal filter: %s", filter);
868 assert_not_reached("Shouldn't be here");
877 return r >= 0 ? r : EXIT_FAILURE;