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"
47 } arg_action = ACTION_LIST;
48 static const char* arg_field = NULL;
49 static int arg_no_pager = false;
50 static int arg_no_legend = false;
51 static int arg_one = false;
53 static FILE* output = NULL;
55 static Set *new_matches(void) {
60 set = set_new(trivial_hash_func, trivial_compare_func);
66 tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
73 r = set_consume(set, tmp);
75 log_error("failed to add to set: %s", strerror(-r));
83 static int help(void) {
85 printf("%s [OPTIONS...]\n\n"
86 "List or retrieve coredumps from the journal.\n\n"
88 " -o --output=FILE Write output to FILE\n"
89 " --no-pager Do not pipe output into a pager\n"
90 " --no-legend Do not print the column headers.\n\n"
93 " -h --help Show this help\n"
94 " --version Print version string\n"
95 " -F --field=FIELD List all values a certain field takes\n"
96 " list [MATCHES...] List available coredumps\n"
97 " info [MATCHES...] Show detailed information about one or more coredumps\n"
98 " dump [MATCHES...] Print first matching coredump to stdout\n"
99 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
100 , program_invocation_short_name);
105 static int add_match(Set *set, const char *match) {
109 char *pattern = NULL;
110 _cleanup_free_ char *p = NULL;
112 if (strchr(match, '='))
114 else if (strchr(match, '/')) {
115 p = path_make_absolute_cwd(match);
120 prefix = "COREDUMP_EXE=";
122 else if (safe_atou(match, &pid) == 0)
123 prefix = "COREDUMP_PID=";
125 prefix = "COREDUMP_COMM=";
127 pattern = strjoin(prefix, match, NULL);
131 log_debug("Adding pattern: %s", pattern);
132 r = set_put(set, pattern);
134 log_error("Failed to add pattern '%s': %s",
135 pattern, strerror(-r));
142 log_error("Failed to add match: %s", strerror(-r));
146 static int parse_argv(int argc, char *argv[], Set *matches) {
155 static const struct option options[] = {
156 { "help", no_argument, NULL, 'h' },
157 { "version" , no_argument, NULL, ARG_VERSION },
158 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
159 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
160 { "output", required_argument, NULL, 'o' },
161 { "field", required_argument, NULL, 'F' },
168 while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
172 arg_action = ACTION_NONE;
176 arg_action = ACTION_NONE;
177 puts(PACKAGE_STRING);
178 puts(SYSTEMD_FEATURES);
186 arg_no_legend = true;
191 log_error("cannot set output more than once");
195 output = fopen(optarg, "we");
197 log_error("writing to '%s': %m", optarg);
205 log_error("cannot use --field/-F more than once");
219 assert_not_reached("Unhandled option");
223 const char *cmd = argv[optind++];
224 if (streq(cmd, "list"))
225 arg_action = ACTION_LIST;
226 else if (streq(cmd, "dump"))
227 arg_action = ACTION_DUMP;
228 else if (streq(cmd, "gdb"))
229 arg_action = ACTION_GDB;
230 else if (streq(cmd, "info"))
231 arg_action = ACTION_INFO;
233 log_error("Unknown action '%s'", cmd);
238 if (arg_field && arg_action != ACTION_LIST) {
239 log_error("Option --field/-F only makes sense with list");
243 while (optind < argc) {
244 r = add_match(matches, argv[optind]);
253 static int retrieve(const void *data,
261 ident = strlen(name) + 1; /* name + "=" */
266 if (memcmp(data, name, ident - 1) != 0)
269 if (((const char*) data)[ident - 1] != '=')
272 v = strndup((const char*)data + ident, len - ident);
282 #define filename_escape(s) xescape((s), "./")
284 static int make_coredump_path(sd_journal *j, char **ret) {
286 *pid = NULL, *boot_id = NULL, *tstamp = NULL, *comm = NULL,
287 *p = NULL, *b = NULL, *t = NULL, *c = NULL;
295 SD_JOURNAL_FOREACH_DATA(j, d, l) {
296 retrieve(d, l, "COREDUMP_COMM", &comm);
297 retrieve(d, l, "COREDUMP_PID", &pid);
298 retrieve(d, l, "COREDUMP_TIMESTAMP", &tstamp);
299 retrieve(d, l, "_BOOT_ID", &boot_id);
302 if (!pid || !comm || !tstamp || !boot_id) {
303 log_error("Failed to retrieve necessary fields to find coredump on disk.");
307 p = filename_escape(pid);
311 t = filename_escape(tstamp);
315 c = filename_escape(comm);
319 b = filename_escape(boot_id);
323 fn = strjoin("/var/lib/systemd/coredump/core.", c, ".", b, ".", p, ".", t, NULL);
331 static void print_field(FILE* file, sd_journal *j) {
332 _cleanup_free_ char *value = NULL;
341 SD_JOURNAL_FOREACH_DATA(j, d, l)
342 retrieve(d, l, arg_field, &value);
345 fprintf(file, "%s\n", value);
348 static int print_list(FILE* file, sd_journal *j, int had_legend) {
350 *pid = NULL, *uid = NULL, *gid = NULL,
351 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL;
355 char buf[FORMAT_TIMESTAMP_MAX];
361 SD_JOURNAL_FOREACH_DATA(j, d, l) {
362 retrieve(d, l, "COREDUMP_PID", &pid);
363 retrieve(d, l, "COREDUMP_UID", &uid);
364 retrieve(d, l, "COREDUMP_GID", &gid);
365 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
366 retrieve(d, l, "COREDUMP_EXE", &exe);
367 retrieve(d, l, "COREDUMP_COMM", &comm);
368 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
371 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline) {
372 log_warning("Empty coredump log entry");
376 r = sd_journal_get_realtime_usec(j, &t);
378 log_error("Failed to get realtime timestamp: %s", strerror(-r));
382 format_timestamp(buf, sizeof(buf), t);
384 if (!had_legend && !arg_no_legend)
385 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
386 FORMAT_TIMESTAMP_MAX-1, "TIME",
393 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
394 FORMAT_TIMESTAMP_MAX-1, buf,
399 strna(exe ?: (comm ?: cmdline)));
404 static int print_info(FILE *file, sd_journal *j, bool need_space) {
406 *pid = NULL, *uid = NULL, *gid = NULL,
407 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
408 *unit = NULL, *user_unit = NULL, *session = NULL,
409 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
410 *coredump = NULL, *slice = NULL, *cgroup = NULL,
411 *owner_uid = NULL, *message = NULL;
418 SD_JOURNAL_FOREACH_DATA(j, d, l) {
419 retrieve(d, l, "COREDUMP_PID", &pid);
420 retrieve(d, l, "COREDUMP_UID", &uid);
421 retrieve(d, l, "COREDUMP_GID", &gid);
422 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
423 retrieve(d, l, "COREDUMP_EXE", &exe);
424 retrieve(d, l, "COREDUMP_COMM", &comm);
425 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
426 retrieve(d, l, "COREDUMP_UNIT", &unit);
427 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
428 retrieve(d, l, "COREDUMP_SESSION", &session);
429 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
430 retrieve(d, l, "COREDUMP_SLICE", &slice);
431 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
432 retrieve(d, l, "_BOOT_ID", &boot_id);
433 retrieve(d, l, "_MACHINE_ID", &machine_id);
434 retrieve(d, l, "_HOSTNAME", &hostname);
435 retrieve(d, l, "MESSAGE", &message);
443 ansi_highlight(), strna(pid), ansi_highlight_off());
448 if (parse_uid(uid, &n) >= 0) {
449 _cleanup_free_ char *u = NULL;
465 if (parse_gid(gid, &n) >= 0) {
466 _cleanup_free_ char *g = NULL;
482 if (safe_atoi(sgnl, &sig) >= 0)
483 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
485 fprintf(file, " Signal: %s\n", sgnl);
489 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
491 fprintf(file, " Comm: %s\n", comm);
493 fprintf(file, " Command Line: %s\n", cmdline);
495 fprintf(file, " Control Group: %s\n", cgroup);
497 fprintf(file, " Unit: %s\n", unit);
499 fprintf(file, " User Unit: %s\n", unit);
501 fprintf(file, " Slice: %s\n", slice);
503 fprintf(file, " Session: %s\n", session);
507 if (parse_uid(owner_uid, &n) >= 0) {
508 _cleanup_free_ char *u = NULL;
512 " Owner UID: %s (%s)\n",
521 fprintf(file, " Boot ID: %s\n", boot_id);
523 fprintf(file, " Machine ID: %s\n", machine_id);
525 fprintf(file, " Hostname: %s\n", hostname);
527 if (make_coredump_path(j, &coredump) >= 0)
528 if (access(coredump, F_OK) >= 0)
529 fprintf(file, " Coredump: %s\n", coredump);
532 _cleanup_free_ char *m = NULL;
534 m = strreplace(message, "\n", "\n ");
536 fprintf(file, " Message: %s\n", strstrip(m ?: message));
542 static int focus(sd_journal *j) {
545 r = sd_journal_seek_tail(j);
547 r = sd_journal_previous(j);
549 log_error("Failed to search journal: %s", strerror(-r));
553 log_error("No match found.");
559 static void print_entry(sd_journal *j, unsigned n_found) {
562 if (arg_action == ACTION_INFO)
563 print_info(stdout, j, n_found);
565 print_field(stdout, j);
567 print_list(stdout, j, n_found);
570 static int dump_list(sd_journal *j) {
571 unsigned n_found = 0;
576 /* The coredumps are likely to compressed, and for just
577 * listing them we don't need to decompress them, so let's
578 * pick a fairly low data threshold here */
579 sd_journal_set_data_threshold(j, 4096);
588 SD_JOURNAL_FOREACH(j)
589 print_entry(j, n_found++);
591 if (!arg_field && n_found <= 0) {
592 log_notice("No coredumps found.");
600 static int dump_core(sd_journal* j) {
607 /* We want full data, nothing truncated. */
608 sd_journal_set_data_threshold(j, 0);
614 print_info(output ? stdout : stderr, j, false);
616 if (on_tty() && !output) {
617 log_error("Refusing to dump core to tty.");
621 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
623 _cleanup_free_ char *fn = NULL;
624 _cleanup_close_ int fd = -1;
626 r = make_coredump_path(j, &fn);
630 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY);
633 log_error("Coredump neither in journal file nor stored externally on disk.");
635 log_error("Failed to open coredump file: %s", strerror(-r));
640 r = copy_bytes(fd, output ? fileno(output) : STDOUT_FILENO);
642 log_error("Failed to stream coredump: %s", strerror(-r));
647 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
652 data = (const uint8_t*) data + 9;
655 ret = fwrite(data, len, 1, output ?: stdout);
657 log_error("Dumping coredump failed: %m (%zu)", ret);
662 r = sd_journal_previous(j);
664 log_warning("More than one entry matches, ignoring rest.");
669 static int run_gdb(sd_journal *j) {
671 _cleanup_free_ char *exe = NULL, *coredump = NULL;
672 char temp[] = "/var/tmp/coredump-XXXXXX";
673 bool unlink_temp = false;
683 sd_journal_set_data_threshold(j, 0);
689 print_info(stdout, j, false);
692 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
694 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
699 data = (const uint8_t*) data + 13;
702 exe = strndup(data, len);
706 if (endswith(exe, " (deleted)")) {
707 log_error("Binary already deleted.");
711 if (!path_is_absolute(exe)) {
712 log_error("Binary is not an absolute path.");
716 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
719 r = make_coredump_path(j, &coredump);
723 if (access(coredump, R_OK) < 0) {
725 log_error("Coredump neither in journal file nor stored externally on disk.");
727 log_error("Failed to access coredump file: %m");
735 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
739 _cleanup_close_ int fd = -1;
743 data = (const uint8_t*) data + 9;
746 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
748 log_error("Failed to create temporary file: %m");
754 sz = write(fd, data, len);
756 log_error("Failed to write temporary file: %m");
760 if (sz != (ssize_t) len) {
761 log_error("Short write to temporary file.");
771 log_error("Failed to fork(): %m");
776 execlp("gdb", "gdb", exe, path, NULL);
778 log_error("Failed to invoke gdb: %m");
782 r = wait_for_terminate(pid, &st);
784 log_error("Failed to wait for gdb: %m");
788 r = st.si_code == CLD_EXITED ? st.si_status : 255;
797 int main(int argc, char *argv[]) {
798 _cleanup_journal_close_ sd_journal*j = NULL;
802 _cleanup_set_free_free_ Set *matches = NULL;
804 setlocale(LC_ALL, "");
805 log_parse_environment();
808 matches = new_matches();
814 r = parse_argv(argc, argv, matches);
818 if (arg_action == ACTION_NONE)
821 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
823 log_error("Failed to open journal: %s", strerror(-r));
827 SET_FOREACH(match, matches, it) {
828 r = sd_journal_add_match(j, match, strlen(match));
830 log_error("Failed to add match '%s': %s",
831 match, strerror(-r));
836 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
837 _cleanup_free_ char *filter;
839 filter = journal_make_match_string(j);
840 log_debug("Journal filter: %s", filter);
862 assert_not_reached("Shouldn't be here");
871 return r >= 0 ? r : EXIT_FAILURE;