2 This file is part of systemd.
4 Copyright 2012 Zbigniew Jędrzejewski-Szmek
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "sd-journal.h"
29 #include "alloc-util.h"
33 #include "journal-internal.h"
37 #include "parse-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
42 #include "signal-util.h"
43 #include "string-util.h"
44 #include "terminal-util.h"
45 #include "user-util.h"
54 } arg_action
= ACTION_LIST
;
55 static const char* arg_field
= NULL
;
56 static const char *arg_directory
= NULL
;
57 static bool arg_no_pager
= false;
58 static int arg_no_legend
= false;
59 static int arg_one
= false;
60 static FILE* arg_output
= NULL
;
62 static Set
*new_matches(void) {
73 tmp
= strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
80 r
= set_consume(set
, tmp
);
82 log_error_errno(r
, "failed to add to set: %m");
90 static int add_match(Set
*set
, const char *match
) {
91 _cleanup_free_
char *p
= NULL
;
97 if (strchr(match
, '='))
99 else if (strchr(match
, '/')) {
100 r
= path_make_absolute_cwd(match
, &p
);
104 prefix
= "COREDUMP_EXE=";
105 } else if (parse_pid(match
, &pid
) >= 0)
106 prefix
= "COREDUMP_PID=";
108 prefix
= "COREDUMP_COMM=";
110 pattern
= strjoin(prefix
, match
, NULL
);
116 log_debug("Adding pattern: %s", pattern
);
117 r
= set_consume(set
, pattern
);
123 return log_error_errno(r
, "Failed to add match: %m");
126 static void help(void) {
127 printf("%s [OPTIONS...]\n\n"
128 "List or retrieve coredumps from the journal.\n\n"
130 " -h --help Show this help\n"
131 " --version Print version string\n"
132 " --no-pager Do not pipe output into a pager\n"
133 " --no-legend Do not print the column headers.\n"
134 " -1 Show information about most recent entry only\n"
135 " -F --field=FIELD List all values a certain field takes\n"
136 " -o --output=FILE Write output to FILE\n\n"
137 " -D --directory=DIR Use journal files from directory\n\n"
140 " list [MATCHES...] List available coredumps (default)\n"
141 " info [MATCHES...] Show detailed information about one or more coredumps\n"
142 " dump [MATCHES...] Print first matching coredump to stdout\n"
143 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
144 , program_invocation_short_name
);
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' },
163 { "directory", required_argument
, NULL
, 'D' },
170 while ((c
= getopt_long(argc
, argv
, "ho:F:1D:", options
, NULL
)) >= 0)
174 arg_action
= ACTION_NONE
;
179 arg_action
= ACTION_NONE
;
187 arg_no_legend
= true;
192 log_error("cannot set output more than once");
196 arg_output
= fopen(optarg
, "we");
198 return log_error_errno(errno
, "writing to '%s': %m", optarg
);
204 log_error("cannot use --field/-F more than once");
215 arg_directory
= optarg
;
222 assert_not_reached("Unhandled option");
226 const char *cmd
= argv
[optind
++];
227 if (streq(cmd
, "list"))
228 arg_action
= ACTION_LIST
;
229 else if (streq(cmd
, "dump"))
230 arg_action
= ACTION_DUMP
;
231 else if (streq(cmd
, "gdb"))
232 arg_action
= ACTION_GDB
;
233 else if (streq(cmd
, "info"))
234 arg_action
= ACTION_INFO
;
236 log_error("Unknown action '%s'", cmd
);
241 if (arg_field
&& arg_action
!= ACTION_LIST
) {
242 log_error("Option --field/-F only makes sense with list");
246 while (optind
< argc
) {
247 r
= add_match(matches
, argv
[optind
]);
256 static int retrieve(const void *data
,
264 ident
= strlen(name
) + 1; /* name + "=" */
269 if (memcmp(data
, name
, ident
- 1) != 0)
272 if (((const char*) data
)[ident
- 1] != '=')
275 v
= strndup((const char*)data
+ ident
, len
- ident
);
285 static void print_field(FILE* file
, sd_journal
*j
) {
286 _cleanup_free_
char *value
= NULL
;
295 SD_JOURNAL_FOREACH_DATA(j
, d
, l
)
296 retrieve(d
, l
, arg_field
, &value
);
299 fprintf(file
, "%s\n", value
);
302 static int print_list(FILE* file
, sd_journal
*j
, int had_legend
) {
304 *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
305 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
310 char buf
[FORMAT_TIMESTAMP_MAX
];
317 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
318 retrieve(d
, l
, "COREDUMP_PID", &pid
);
319 retrieve(d
, l
, "COREDUMP_UID", &uid
);
320 retrieve(d
, l
, "COREDUMP_GID", &gid
);
321 retrieve(d
, l
, "COREDUMP_SIGNAL", &sgnl
);
322 retrieve(d
, l
, "COREDUMP_EXE", &exe
);
323 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
324 retrieve(d
, l
, "COREDUMP_CMDLINE", &cmdline
);
325 retrieve(d
, l
, "COREDUMP_FILENAME", &filename
);
328 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
&& !filename
) {
329 log_warning("Empty coredump log entry");
333 r
= sd_journal_get_realtime_usec(j
, &t
);
335 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
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_normal(), comm
);
409 ansi_highlight(), strna(pid
), ansi_normal());
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_normal());
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 return log_error_errno(r
, "Failed to search journal: %m");
530 log_error("No match found.");
536 static void print_entry(sd_journal
*j
, unsigned n_found
) {
539 if (arg_action
== ACTION_INFO
)
540 print_info(stdout
, j
, n_found
);
542 print_field(stdout
, j
);
544 print_list(stdout
, j
, n_found
);
547 static int dump_list(sd_journal
*j
) {
548 unsigned n_found
= 0;
553 /* The coredumps are likely to compressed, and for just
554 * listing them we don't need to decompress them, so let's
555 * pick a fairly low data threshold here */
556 sd_journal_set_data_threshold(j
, 4096);
565 SD_JOURNAL_FOREACH(j
)
566 print_entry(j
, n_found
++);
568 if (!arg_field
&& n_found
<= 0) {
569 log_notice("No coredumps found.");
577 static int save_core(sd_journal
*j
, int fd
, char **path
, bool *unlink_temp
) {
579 _cleanup_free_
char *filename
= NULL
;
583 assert((fd
>= 0) != !!path
);
584 assert(!!path
== !!unlink_temp
);
586 /* Prefer uncompressed file to journal (probably cached) to
587 * compressed file (probably uncached). */
588 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
589 if (r
< 0 && r
!= -ENOENT
)
590 log_warning_errno(r
, "Failed to retrieve COREDUMP_FILENAME: %m");
592 retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
594 if (filename
&& access(filename
, R_OK
) < 0) {
595 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
596 "File %s is not readable: %m", filename
);
597 filename
= mfree(filename
);
600 if (filename
&& !endswith(filename
, ".xz") && !endswith(filename
, ".lz4")) {
608 _cleanup_close_
int fdt
= -1;
612 temp
= strdup("/var/tmp/coredump-XXXXXX");
616 fdt
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
618 return log_error_errno(fdt
, "Failed to create temporary file: %m");
619 log_debug("Created temporary file %s", temp
);
624 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
632 sz
= write(fdt
, data
, len
);
634 r
= log_error_errno(errno
,
635 "Failed to write temporary file: %m");
638 if (sz
!= (ssize_t
) len
) {
639 log_error("Short write to temporary file.");
643 } else if (filename
) {
644 #if defined(HAVE_XZ) || defined(HAVE_LZ4)
645 _cleanup_close_
int fdf
;
647 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
649 r
= log_error_errno(errno
,
650 "Failed to open %s: %m",
655 r
= decompress_stream(filename
, fdf
, fd
, -1);
657 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
661 log_error("Cannot decompress file. Compiled without compression support.");
667 log_error("Cannot retrieve coredump from journal or disk.");
669 log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
683 log_debug("Removed temporary file %s", temp
);
689 static int dump_core(sd_journal
* j
) {
698 print_info(arg_output
? stdout
: stderr
, j
, false);
700 if (on_tty() && !arg_output
) {
701 log_error("Refusing to dump core to tty.");
705 r
= save_core(j
, arg_output
? fileno(arg_output
) : STDOUT_FILENO
, NULL
, NULL
);
707 return log_error_errno(r
, "Coredump retrieval failed: %m");
709 r
= sd_journal_previous(j
);
711 log_warning("More than one entry matches, ignoring rest.");
716 static int run_gdb(sd_journal
*j
) {
717 _cleanup_free_
char *exe
= NULL
, *path
= NULL
;
718 bool unlink_path
= false;
731 print_info(stdout
, j
, false);
734 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
736 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
738 assert(len
> strlen("COREDUMP_EXE="));
739 data
+= strlen("COREDUMP_EXE=");
740 len
-= strlen("COREDUMP_EXE=");
742 exe
= strndup(data
, len
);
746 if (endswith(exe
, " (deleted)")) {
747 log_error("Binary already deleted.");
751 if (!path_is_absolute(exe
)) {
752 log_error("Binary is not an absolute path.");
756 r
= save_core(j
, -1, &path
, &unlink_path
);
758 return log_error_errno(r
, "Failed to retrieve core: %m");
762 r
= log_error_errno(errno
, "Failed to fork(): %m");
766 (void) reset_all_signal_handlers();
767 (void) reset_signal_mask();
769 execlp("gdb", "gdb", exe
, path
, NULL
);
771 log_error_errno(errno
, "Failed to invoke gdb: %m");
775 r
= wait_for_terminate(pid
, &st
);
777 log_error_errno(r
, "Failed to wait for gdb: %m");
781 r
= st
.si_code
== CLD_EXITED
? st
.si_status
: 255;
785 log_debug("Removed temporary file %s", path
);
792 int main(int argc
, char *argv
[]) {
793 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
797 _cleanup_set_free_free_ Set
*matches
= NULL
;
799 setlocale(LC_ALL
, "");
800 log_parse_environment();
803 matches
= new_matches();
809 r
= parse_argv(argc
, argv
, matches
);
813 if (arg_action
== ACTION_NONE
)
819 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
821 log_error_errno(r
, "Failed to open journals in directory: %s: %m", arg_directory
);
825 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
827 log_error_errno(r
, "Failed to open journal: %m");
832 /* We want full data, nothing truncated. */
833 sd_journal_set_data_threshold(j
, 0);
835 SET_FOREACH(match
, matches
, it
) {
836 r
= sd_journal_add_match(j
, match
, strlen(match
));
838 log_error_errno(r
, "Failed to add match '%s': %m",
844 if (_unlikely_(log_get_max_level() >= LOG_DEBUG
)) {
845 _cleanup_free_
char *filter
;
847 filter
= journal_make_match_string(j
);
848 log_debug("Journal filter: %s", filter
);
855 pager_open(arg_no_pager
, false);
868 assert_not_reached("Shouldn't be here");
877 return r
>= 0 ? r
: EXIT_FAILURE
;