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
,
407 *owner_uid
= NULL
, *message
= NULL
;
414 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
415 retrieve(d
, l
, "COREDUMP_PID", &pid
);
416 retrieve(d
, l
, "COREDUMP_UID", &uid
);
417 retrieve(d
, l
, "COREDUMP_GID", &gid
);
418 retrieve(d
, l
, "COREDUMP_SIGNAL", &sgnl
);
419 retrieve(d
, l
, "COREDUMP_EXE", &exe
);
420 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
421 retrieve(d
, l
, "COREDUMP_CMDLINE", &cmdline
);
422 retrieve(d
, l
, "COREDUMP_UNIT", &unit
);
423 retrieve(d
, l
, "COREDUMP_USER_UNIT", &user_unit
);
424 retrieve(d
, l
, "COREDUMP_SESSION", &session
);
425 retrieve(d
, l
, "COREDUMP_OWNER_UID", &owner_uid
);
426 retrieve(d
, l
, "COREDUMP_SLICE", &slice
);
427 retrieve(d
, l
, "COREDUMP_CGROUP", &cgroup
);
428 retrieve(d
, l
, "_BOOT_ID", &boot_id
);
429 retrieve(d
, l
, "_MACHINE_ID", &machine_id
);
430 retrieve(d
, l
, "_HOSTNAME", &hostname
);
431 retrieve(d
, l
, "MESSAGE", &message
);
439 ansi_highlight(), strna(pid
), ansi_highlight_off());
444 if (parse_uid(uid
, &n
) >= 0) {
445 _cleanup_free_
char *u
= NULL
;
461 if (parse_gid(gid
, &n
) >= 0) {
462 _cleanup_free_
char *g
= NULL
;
478 if (safe_atoi(sgnl
, &sig
) >= 0)
479 fprintf(file
, " Signal: %s (%s)\n", sgnl
, signal_to_string(sig
));
481 fprintf(file
, " Signal: %s\n", sgnl
);
485 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_highlight_off());
487 fprintf(file
, " Comm: %s\n", comm
);
489 fprintf(file
, " Command Line: %s\n", cmdline
);
491 fprintf(file
, " Control Group: %s\n", cgroup
);
493 fprintf(file
, " Unit: %s\n", unit
);
495 fprintf(file
, " User Unit: %s\n", unit
);
497 fprintf(file
, " Slice: %s\n", slice
);
499 fprintf(file
, " Session: %s\n", session
);
503 if (parse_uid(owner_uid
, &n
) >= 0) {
504 _cleanup_free_
char *u
= NULL
;
508 " Owner UID: %s (%s)\n",
517 fprintf(file
, " Boot ID: %s\n", boot_id
);
519 fprintf(file
, " Machine ID: %s\n", machine_id
);
521 fprintf(file
, " Hostname: %s\n", hostname
);
523 if (make_coredump_path(j
, &coredump
) >= 0)
524 if (access(coredump
, F_OK
) >= 0)
525 fprintf(file
, " Coredump: %s\n", coredump
);
528 _cleanup_free_
char *m
= NULL
;
530 m
= strreplace(message
, "\n", "\n ");
532 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
538 static int dump_list(sd_journal
*j
) {
543 /* The coredumps are likely to compressed, and for just
544 * listing them we don't need to decompress them, so let's
545 * pick a fairly low data threshold here */
546 sd_journal_set_data_threshold(j
, 4096);
548 SD_JOURNAL_FOREACH(j
) {
549 if (arg_action
== ACTION_INFO
)
550 print_info(stdout
, j
, found
++);
552 print_field(stdout
, j
);
554 print_list(stdout
, j
, found
++);
557 if (!arg_field
&& !found
) {
558 log_notice("No coredumps found");
565 static int focus(sd_journal
*j
) {
568 r
= sd_journal_seek_tail(j
);
570 r
= sd_journal_previous(j
);
572 log_error("Failed to search journal: %s", strerror(-r
));
576 log_error("No match found");
582 static int dump_core(sd_journal
* j
) {
589 /* We want full data, nothing truncated. */
590 sd_journal_set_data_threshold(j
, 0);
596 print_info(output
? stdout
: stderr
, j
, false);
598 if (on_tty() && !output
) {
599 log_error("Refusing to dump core to tty.");
603 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
605 _cleanup_free_
char *fn
= NULL
;
606 _cleanup_close_
int fd
= -1;
608 r
= make_coredump_path(j
, &fn
);
612 fd
= open(fn
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
615 log_error("Coredump neither in journal file nor stored externally on disk.");
617 log_error("Failed to open coredump file: %s", strerror(-r
));
622 r
= copy_bytes(fd
, output
? fileno(output
) : STDOUT_FILENO
);
624 log_error("Failed to stream coredump: %s", strerror(-r
));
629 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
634 data
= (const uint8_t*) data
+ 9;
637 ret
= fwrite(data
, len
, 1, output
?: stdout
);
639 log_error("Dumping coredump failed: %m (%zu)", ret
);
644 r
= sd_journal_previous(j
);
646 log_warning("More than one entry matches, ignoring rest.");
651 static int run_gdb(sd_journal
*j
) {
653 _cleanup_free_
char *exe
= NULL
, *coredump
= NULL
;
654 char temp
[] = "/var/tmp/coredump-XXXXXX";
655 bool unlink_temp
= false;
665 sd_journal_set_data_threshold(j
, 0);
671 print_info(stdout
, j
, false);
674 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
676 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r
));
681 data
= (const uint8_t*) data
+ 13;
684 exe
= strndup(data
, len
);
688 if (endswith(exe
, " (deleted)")) {
689 log_error("Binary already deleted.");
693 if (!path_is_absolute(exe
)) {
694 log_error("Binary is not an absolute path.");
698 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
701 r
= make_coredump_path(j
, &coredump
);
705 if (access(coredump
, R_OK
) < 0) {
707 log_error("Coredump neither in journal file nor stored externally on disk.");
709 log_error("Failed to access coredump file: %m");
717 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
721 _cleanup_close_
int fd
= -1;
725 data
= (const uint8_t*) data
+ 9;
728 fd
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
730 log_error("Failed to create temporary file: %m");
736 sz
= write(fd
, data
, len
);
738 log_error("Failed to write temporary file: %m");
742 if (sz
!= (ssize_t
) len
) {
743 log_error("Short write to temporary file.");
753 log_error("Failed to fork(): %m");
758 execlp("gdb", "gdb", exe
, path
, NULL
);
760 log_error("Failed to invoke gdb: %m");
764 r
= wait_for_terminate(pid
, &st
);
766 log_error("Failed to wait for gdb: %m");
770 r
= st
.si_code
== CLD_EXITED
? st
.si_status
: 255;
779 int main(int argc
, char *argv
[]) {
780 _cleanup_journal_close_ sd_journal
*j
= NULL
;
784 _cleanup_set_free_free_ Set
*matches
= NULL
;
786 setlocale(LC_ALL
, "");
787 log_parse_environment();
790 matches
= new_matches();
796 r
= parse_argv(argc
, argv
, matches
);
800 if (arg_action
== ACTION_NONE
)
803 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
805 log_error("Failed to open journal: %s", strerror(-r
));
809 SET_FOREACH(match
, matches
, it
) {
810 r
= sd_journal_add_match(j
, match
, strlen(match
));
812 log_error("Failed to add match '%s': %s",
813 match
, strerror(-r
));
818 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG
))) {
819 _cleanup_free_
char *filter
;
821 filter
= journal_make_match_string(j
);
822 log_debug("Journal filter: %s", filter
);
844 assert_not_reached("Shouldn't be here");
853 return r
>= 0 ? r
: EXIT_FAILURE
;