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;
49 static FILE* output = NULL;
50 static const char* arg_field = NULL;
52 static int arg_no_pager = false;
53 static int arg_no_legend = false;
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:", 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");
215 assert_not_reached("Unhandled option");
219 const char *cmd = argv[optind++];
220 if (streq(cmd, "list"))
221 arg_action = ACTION_LIST;
222 else if (streq(cmd, "dump"))
223 arg_action = ACTION_DUMP;
224 else if (streq(cmd, "gdb"))
225 arg_action = ACTION_GDB;
226 else if (streq(cmd, "info"))
227 arg_action = ACTION_INFO;
229 log_error("Unknown action '%s'", cmd);
234 if (arg_field && arg_action != ACTION_LIST) {
235 log_error("Option --field/-F only makes sense with list");
239 while (optind < argc) {
240 r = add_match(matches, argv[optind]);
249 static int retrieve(const void *data,
257 ident = strlen(name) + 1; /* name + "=" */
262 if (memcmp(data, name, ident - 1) != 0)
265 if (((const char*) data)[ident - 1] != '=')
268 v = strndup((const char*)data + ident, len - ident);
278 #define filename_escape(s) xescape((s), "./")
280 static int make_coredump_path(sd_journal *j, char **ret) {
282 *pid = NULL, *boot_id = NULL, *tstamp = NULL, *comm = NULL,
283 *p = NULL, *b = NULL, *t = NULL, *c = NULL;
291 SD_JOURNAL_FOREACH_DATA(j, d, l) {
292 retrieve(d, l, "COREDUMP_COMM", &comm);
293 retrieve(d, l, "COREDUMP_PID", &pid);
294 retrieve(d, l, "COREDUMP_TIMESTAMP", &tstamp);
295 retrieve(d, l, "_BOOT_ID", &boot_id);
298 if (!pid || !comm || !tstamp || !boot_id) {
299 log_error("Failed to retrieve necessary fields to find coredump on disk.");
303 p = filename_escape(pid);
307 t = filename_escape(tstamp);
311 c = filename_escape(comm);
315 b = filename_escape(boot_id);
319 fn = strjoin("/var/lib/systemd/coredump/core.", c, ".", b, ".", p, ".", t, NULL);
327 static void print_field(FILE* file, sd_journal *j) {
328 _cleanup_free_ char *value = NULL;
337 SD_JOURNAL_FOREACH_DATA(j, d, l)
338 retrieve(d, l, arg_field, &value);
341 fprintf(file, "%s\n", value);
344 static int print_list(FILE* file, sd_journal *j, int had_legend) {
346 *pid = NULL, *uid = NULL, *gid = NULL,
347 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL;
351 char buf[FORMAT_TIMESTAMP_MAX];
357 SD_JOURNAL_FOREACH_DATA(j, d, l) {
358 retrieve(d, l, "COREDUMP_PID", &pid);
359 retrieve(d, l, "COREDUMP_UID", &uid);
360 retrieve(d, l, "COREDUMP_GID", &gid);
361 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
362 retrieve(d, l, "COREDUMP_EXE", &exe);
363 retrieve(d, l, "COREDUMP_COMM", &comm);
364 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
367 if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline) {
368 log_warning("Empty coredump log entry");
372 r = sd_journal_get_realtime_usec(j, &t);
374 log_error("Failed to get realtime timestamp: %s", strerror(-r));
378 format_timestamp(buf, sizeof(buf), t);
380 if (!had_legend && !arg_no_legend)
381 fprintf(file, "%-*s %*s %*s %*s %*s %s\n",
382 FORMAT_TIMESTAMP_MAX-1, "TIME",
389 fprintf(file, "%*s %*s %*s %*s %*s %s\n",
390 FORMAT_TIMESTAMP_MAX-1, buf,
395 strna(exe ?: (comm ?: cmdline)));
400 static int print_info(FILE *file, sd_journal *j, bool need_space) {
402 *pid = NULL, *uid = NULL, *gid = NULL,
403 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
404 *unit = NULL, *user_unit = NULL, *session = NULL,
405 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
406 *coredump = NULL, *slice = NULL, *cgroup = NULL, *owner_uid = NULL;
413 SD_JOURNAL_FOREACH_DATA(j, d, l) {
414 retrieve(d, l, "COREDUMP_PID", &pid);
415 retrieve(d, l, "COREDUMP_UID", &uid);
416 retrieve(d, l, "COREDUMP_GID", &gid);
417 retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
418 retrieve(d, l, "COREDUMP_EXE", &exe);
419 retrieve(d, l, "COREDUMP_COMM", &comm);
420 retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
421 retrieve(d, l, "COREDUMP_UNIT", &unit);
422 retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
423 retrieve(d, l, "COREDUMP_SESSION", &session);
424 retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
425 retrieve(d, l, "COREDUMP_SLICE", &slice);
426 retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
427 retrieve(d, l, "_BOOT_ID", &boot_id);
428 retrieve(d, l, "_MACHINE_ID", &machine_id);
429 retrieve(d, l, "_HOSTNAME", &hostname);
437 ansi_highlight(), strna(pid), ansi_highlight_off());
442 if (parse_uid(uid, &n) >= 0) {
443 _cleanup_free_ char *u = NULL;
459 if (parse_gid(gid, &n) >= 0) {
460 _cleanup_free_ char *g = NULL;
476 if (safe_atoi(sgnl, &sig) >= 0)
477 fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig));
479 fprintf(file, " Signal: %s\n", sgnl);
483 fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_highlight_off());
485 fprintf(file, " Comm: %s\n", comm);
487 fprintf(file, " Command Line: %s\n", cmdline);
489 fprintf(file, " Control Group: %s\n", cgroup);
491 fprintf(file, " Unit: %s\n", unit);
493 fprintf(file, " User Unit: %s\n", unit);
495 fprintf(file, " Slice: %s\n", slice);
497 fprintf(file, " Session: %s\n", session);
501 if (parse_uid(owner_uid, &n) >= 0) {
502 _cleanup_free_ char *u = NULL;
506 " Owner UID: %s (%s)\n",
515 fprintf(file, " Boot ID: %s\n", boot_id);
517 fprintf(file, " Machine ID: %s\n", machine_id);
519 fprintf(file, " Hostname: %s\n", hostname);
521 if (make_coredump_path(j, &coredump) >= 0)
522 if (access(coredump, F_OK) >= 0)
523 fprintf(file, " Coredump: %s\n", coredump);
528 static int dump_list(sd_journal *j) {
533 /* The coredumps are likely to compressed, and for just
534 * listing them we don't need to decompress them, so let's
535 * pick a fairly low data threshold here */
536 sd_journal_set_data_threshold(j, 4096);
538 SD_JOURNAL_FOREACH(j) {
539 if (arg_action == ACTION_INFO)
540 print_info(stdout, j, found++);
542 print_field(stdout, j);
544 print_list(stdout, j, found++);
547 if (!arg_field && !found) {
548 log_notice("No coredumps found");
555 static int focus(sd_journal *j) {
558 r = sd_journal_seek_tail(j);
560 r = sd_journal_previous(j);
562 log_error("Failed to search journal: %s", strerror(-r));
566 log_error("No match found");
572 static int dump_core(sd_journal* j) {
579 /* We want full data, nothing truncated. */
580 sd_journal_set_data_threshold(j, 0);
586 print_info(output ? stdout : stderr, j, false);
588 if (on_tty() && !output) {
589 log_error("Refusing to dump core to tty.");
593 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
595 _cleanup_free_ char *fn = NULL;
596 _cleanup_close_ int fd = -1;
598 r = make_coredump_path(j, &fn);
602 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY);
605 log_error("Coredump neither in journal file nor stored externally on disk.");
607 log_error("Failed to open coredump file: %s", strerror(-r));
612 r = copy_bytes(fd, output ? fileno(output) : STDOUT_FILENO);
614 log_error("Failed to stream coredump: %s", strerror(-r));
619 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
624 data = (const uint8_t*) data + 9;
627 ret = fwrite(data, len, 1, output ?: stdout);
629 log_error("Dumping coredump failed: %m (%zu)", ret);
634 r = sd_journal_previous(j);
636 log_warning("More than one entry matches, ignoring rest.");
641 static int run_gdb(sd_journal *j) {
643 _cleanup_free_ char *exe = NULL, *coredump = NULL;
644 char temp[] = "/var/tmp/coredump-XXXXXX";
645 bool unlink_temp = false;
655 sd_journal_set_data_threshold(j, 0);
661 print_info(stdout, j, false);
664 r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len);
666 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r));
671 data = (const uint8_t*) data + 13;
674 exe = strndup(data, len);
678 if (endswith(exe, " (deleted)")) {
679 log_error("Binary already deleted.");
683 if (!path_is_absolute(exe)) {
684 log_error("Binary is not an absolute path.");
688 r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
691 r = make_coredump_path(j, &coredump);
695 if (access(coredump, R_OK) < 0) {
697 log_error("Coredump neither in journal file nor stored externally on disk.");
699 log_error("Failed to access coredump fiile: %s", strerror(-r));
707 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r));
711 _cleanup_close_ int fd = -1;
715 data = (const uint8_t*) data + 9;
718 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
720 log_error("Failed to create temporary file: %m");
726 sz = write(fd, data, len);
728 log_error("Failed to write temporary file: %m");
732 if (sz != (ssize_t) len) {
733 log_error("Short write to temporary file.");
743 log_error("Failed to fork(): %m");
748 execlp("gdb", "gdb", exe, path, NULL);
750 log_error("Failed to invoke gdb: %m");
754 r = wait_for_terminate(pid, &st);
756 log_error("Failed to wait for gdb: %m");
760 r = st.si_code == CLD_EXITED ? st.si_status : 255;
769 int main(int argc, char *argv[]) {
770 _cleanup_journal_close_ sd_journal*j = NULL;
774 _cleanup_set_free_free_ Set *matches = NULL;
776 setlocale(LC_ALL, "");
777 log_parse_environment();
780 matches = new_matches();
786 r = parse_argv(argc, argv, matches);
790 if (arg_action == ACTION_NONE)
793 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
795 log_error("Failed to open journal: %s", strerror(-r));
799 SET_FOREACH(match, matches, it) {
800 r = sd_journal_add_match(j, match, strlen(match));
802 log_error("Failed to add match '%s': %s",
803 match, strerror(-r));
808 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
809 _cleanup_free_ char *filter;
811 filter = journal_make_match_string(j);
812 log_debug("Journal filter: %s", filter);
834 assert_not_reached("Shouldn't be here");
843 return r >= 0 ? r : EXIT_FAILURE;