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
, *coredump
= NULL
;
412 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
413 retrieve(d
, l
, "COREDUMP_PID", &pid
);
414 retrieve(d
, l
, "COREDUMP_UID", &uid
);
415 retrieve(d
, l
, "COREDUMP_GID", &gid
);
416 retrieve(d
, l
, "COREDUMP_SIGNAL", &sgnl
);
417 retrieve(d
, l
, "COREDUMP_EXE", &exe
);
418 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
419 retrieve(d
, l
, "COREDUMP_CMDLINE", &cmdline
);
420 retrieve(d
, l
, "COREDUMP_UNIT", &unit
);
421 retrieve(d
, l
, "COREDUMP_USER_UNIT", &user_unit
);
422 retrieve(d
, l
, "COREDUMP_SESSION", &session
);
423 retrieve(d
, l
, "_BOOT_ID", &boot_id
);
424 retrieve(d
, l
, "_MACHINE_ID", &machine_id
);
425 retrieve(d
, l
, "_HOSTNAME", &hostname
);
442 if (safe_atoi(sgnl
, &sig
) >= 0)
443 fprintf(file
, " Signal: %s (%s)\n", sgnl
, signal_to_string(sig
));
445 fprintf(file
, " Signal: %s\n", sgnl
);
449 fprintf(file
, " Executable: %s\n", exe
);
451 fprintf(file
, " Comm: %s\n", comm
);
453 fprintf(file
, " Command Line: %s\n", cmdline
);
455 fprintf(file
, " Unit: %s\n", unit
);
457 fprintf(file
, " User Unit: %s\n", unit
);
459 fprintf(file
, " Session: %s\n", session
);
461 fprintf(file
, " Boot ID: %s\n", boot_id
);
463 fprintf(file
, " Machine ID: %s\n", machine_id
);
465 fprintf(file
, " Hostname: %s\n", hostname
);
467 if (make_coredump_path(j
, &coredump
) >= 0)
468 if (access(coredump
, F_OK
) >= 0)
469 fprintf(file
, " Coredump: %s\n", coredump
);
474 static int dump_list(sd_journal
*j
) {
479 /* The coredumps are likely to compressed, and for just
480 * listing them we don't need to decompress them, so let's
481 * pick a fairly low data threshold here */
482 sd_journal_set_data_threshold(j
, 4096);
484 SD_JOURNAL_FOREACH(j
) {
485 if (arg_action
== ACTION_INFO
)
486 print_info(stdout
, j
, found
++);
488 print_field(stdout
, j
);
490 print_list(stdout
, j
, found
++);
493 if (!arg_field
&& !found
) {
494 log_notice("No coredumps found");
501 static int focus(sd_journal
*j
) {
504 r
= sd_journal_seek_tail(j
);
506 r
= sd_journal_previous(j
);
508 log_error("Failed to search journal: %s", strerror(-r
));
512 log_error("No match found");
518 static int dump_core(sd_journal
* j
) {
525 /* We want full data, nothing truncated. */
526 sd_journal_set_data_threshold(j
, 0);
532 print_info(output
? stdout
: stderr
, j
, false);
534 if (on_tty() && !output
) {
535 log_error("Refusing to dump core to tty.");
539 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
541 _cleanup_free_
char *fn
= NULL
;
542 _cleanup_close_
int fd
= -1;
544 r
= make_coredump_path(j
, &fn
);
548 fd
= open(fn
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
551 log_error("Coredump neither in journal file nor stored externally on disk.");
553 log_error("Failed to open coredump file: %s", strerror(-r
));
558 r
= copy_bytes(fd
, output
? fileno(output
) : STDOUT_FILENO
);
560 log_error("Failed to stream coredump: %s", strerror(-r
));
565 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
570 data
= (const uint8_t*) data
+ 9;
573 ret
= fwrite(data
, len
, 1, output
?: stdout
);
575 log_error("Dumping coredump failed: %m (%zu)", ret
);
580 r
= sd_journal_previous(j
);
582 log_warning("More than one entry matches, ignoring rest.");
587 static int run_gdb(sd_journal
*j
) {
589 _cleanup_free_
char *exe
= NULL
, *coredump
= NULL
;
590 char temp
[] = "/var/tmp/coredump-XXXXXX";
591 bool unlink_temp
= false;
601 sd_journal_set_data_threshold(j
, 0);
607 print_info(stdout
, j
, false);
610 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
612 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r
));
617 data
= (const uint8_t*) data
+ 13;
620 exe
= strndup(data
, len
);
624 if (endswith(exe
, " (deleted)")) {
625 log_error("Binary already deleted.");
629 if (!path_is_absolute(exe
)) {
630 log_error("Binary is not an absolute path.");
634 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
637 r
= make_coredump_path(j
, &coredump
);
641 if (access(coredump
, R_OK
) < 0) {
643 log_error("Coredump neither in journal file nor stored externally on disk.");
645 log_error("Failed to access coredump fiile: %s", strerror(-r
));
653 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
657 _cleanup_close_
int fd
= -1;
661 data
= (const uint8_t*) data
+ 9;
664 fd
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
666 log_error("Failed to create temporary file: %m");
672 sz
= write(fd
, data
, len
);
674 log_error("Failed to write temporary file: %m");
678 if (sz
!= (ssize_t
) len
) {
679 log_error("Short write to temporary file.");
689 log_error("Failed to fork(): %m");
694 execlp("gdb", "gdb", exe
, path
, NULL
);
696 log_error("Failed to invoke gdb: %m");
700 r
= wait_for_terminate(pid
, &st
);
702 log_error("Failed to wait for gdb: %m");
706 r
= st
.si_code
== CLD_EXITED
? st
.si_status
: 255;
715 int main(int argc
, char *argv
[]) {
716 _cleanup_journal_close_ sd_journal
*j
= NULL
;
720 _cleanup_set_free_free_ Set
*matches
= NULL
;
722 setlocale(LC_ALL
, "");
723 log_parse_environment();
726 matches
= new_matches();
732 r
= parse_argv(argc
, argv
, matches
);
736 if (arg_action
== ACTION_NONE
)
739 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
741 log_error("Failed to open journal: %s", strerror(-r
));
745 SET_FOREACH(match
, matches
, it
) {
746 r
= sd_journal_add_match(j
, match
, strlen(match
));
748 log_error("Failed to add match '%s': %s",
749 match
, strerror(-r
));
754 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG
))) {
755 _cleanup_free_
char *filter
;
757 filter
= journal_make_match_string(j
);
758 log_debug("Journal filter: %s", filter
);
780 assert_not_reached("Shouldn't be here");
789 return r
>= 0 ? r
: EXIT_FAILURE
;