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 " -h --help Show this help\n"
89 " --version Print version string\n"
90 " --no-pager Do not pipe output into a pager\n"
91 " --no-legend Do not print the column headers.\n"
92 " -1 Show information about most recent entry only\n"
93 " -F --field=FIELD List all values a certain field takes\n"
94 " -o --output=FILE Write output to FILE\n\n"
97 " list [MATCHES...] List available coredumps (default)\n"
98 " info [MATCHES...] Show detailed information about one or more coredumps\n"
99 " dump [MATCHES...] Print first matching coredump to stdout\n"
100 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
101 , program_invocation_short_name
);
106 static int add_match(Set
*set
, const char *match
) {
110 char *pattern
= NULL
;
111 _cleanup_free_
char *p
= NULL
;
113 if (strchr(match
, '='))
115 else if (strchr(match
, '/')) {
116 p
= path_make_absolute_cwd(match
);
121 prefix
= "COREDUMP_EXE=";
123 else if (safe_atou(match
, &pid
) == 0)
124 prefix
= "COREDUMP_PID=";
126 prefix
= "COREDUMP_COMM=";
128 pattern
= strjoin(prefix
, match
, NULL
);
132 log_debug("Adding pattern: %s", pattern
);
133 r
= set_put(set
, pattern
);
135 log_error("Failed to add pattern '%s': %s",
136 pattern
, strerror(-r
));
143 log_error("Failed to add match: %s", strerror(-r
));
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' },
169 while ((c
= getopt_long(argc
, argv
, "ho:F:1", options
, NULL
)) >= 0)
173 arg_action
= ACTION_NONE
;
177 arg_action
= ACTION_NONE
;
178 puts(PACKAGE_STRING
);
179 puts(SYSTEMD_FEATURES
);
187 arg_no_legend
= true;
192 log_error("cannot set output more than once");
196 output
= fopen(optarg
, "we");
198 log_error("writing to '%s': %m", optarg
);
206 log_error("cannot use --field/-F more than once");
220 assert_not_reached("Unhandled option");
224 const char *cmd
= argv
[optind
++];
225 if (streq(cmd
, "list"))
226 arg_action
= ACTION_LIST
;
227 else if (streq(cmd
, "dump"))
228 arg_action
= ACTION_DUMP
;
229 else if (streq(cmd
, "gdb"))
230 arg_action
= ACTION_GDB
;
231 else if (streq(cmd
, "info"))
232 arg_action
= ACTION_INFO
;
234 log_error("Unknown action '%s'", cmd
);
239 if (arg_field
&& arg_action
!= ACTION_LIST
) {
240 log_error("Option --field/-F only makes sense with list");
244 while (optind
< argc
) {
245 r
= add_match(matches
, argv
[optind
]);
254 static int retrieve(const void *data
,
262 ident
= strlen(name
) + 1; /* name + "=" */
267 if (memcmp(data
, name
, ident
- 1) != 0)
270 if (((const char*) data
)[ident
- 1] != '=')
273 v
= strndup((const char*)data
+ ident
, len
- ident
);
283 #define filename_escape(s) xescape((s), "./ ")
285 static int make_coredump_path(sd_journal
*j
, char **ret
) {
287 *pid
= NULL
, *boot_id
= NULL
, *tstamp
= NULL
, *comm
= NULL
,
288 *p
= NULL
, *b
= NULL
, *t
= NULL
, *c
= NULL
;
296 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
297 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
298 retrieve(d
, l
, "COREDUMP_PID", &pid
);
299 retrieve(d
, l
, "COREDUMP_TIMESTAMP", &tstamp
);
300 retrieve(d
, l
, "_BOOT_ID", &boot_id
);
303 if (!pid
|| !comm
|| !tstamp
|| !boot_id
) {
304 log_error("Failed to retrieve necessary fields to find coredump on disk.");
308 p
= filename_escape(pid
);
312 t
= filename_escape(tstamp
);
316 c
= filename_escape(comm
);
320 b
= filename_escape(boot_id
);
324 fn
= strjoin("/var/lib/systemd/coredump/core.", c
, ".", b
, ".", p
, ".", t
, NULL
);
332 static void print_field(FILE* file
, sd_journal
*j
) {
333 _cleanup_free_
char *value
= NULL
;
342 SD_JOURNAL_FOREACH_DATA(j
, d
, l
)
343 retrieve(d
, l
, arg_field
, &value
);
346 fprintf(file
, "%s\n", value
);
349 static int print_list(FILE* file
, sd_journal
*j
, int had_legend
) {
351 *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
352 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
;
356 char buf
[FORMAT_TIMESTAMP_MAX
];
362 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
363 retrieve(d
, l
, "COREDUMP_PID", &pid
);
364 retrieve(d
, l
, "COREDUMP_UID", &uid
);
365 retrieve(d
, l
, "COREDUMP_GID", &gid
);
366 retrieve(d
, l
, "COREDUMP_SIGNAL", &sgnl
);
367 retrieve(d
, l
, "COREDUMP_EXE", &exe
);
368 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
369 retrieve(d
, l
, "COREDUMP_CMDLINE", &cmdline
);
372 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
) {
373 log_warning("Empty coredump log entry");
377 r
= sd_journal_get_realtime_usec(j
, &t
);
379 log_error("Failed to get realtime timestamp: %s", strerror(-r
));
383 format_timestamp(buf
, sizeof(buf
), t
);
385 if (!had_legend
&& !arg_no_legend
)
386 fprintf(file
, "%-*s %*s %*s %*s %*s %s\n",
387 FORMAT_TIMESTAMP_WIDTH
, "TIME",
394 fprintf(file
, "%-*s %*s %*s %*s %*s %s\n",
395 FORMAT_TIMESTAMP_WIDTH
, buf
,
400 strna(exe
?: (comm
?: cmdline
)));
405 static int print_info(FILE *file
, sd_journal
*j
, bool need_space
) {
407 *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
408 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
409 *unit
= NULL
, *user_unit
= NULL
, *session
= NULL
,
410 *boot_id
= NULL
, *machine_id
= NULL
, *hostname
= NULL
,
411 *coredump
= NULL
, *slice
= NULL
, *cgroup
= NULL
,
412 *owner_uid
= NULL
, *message
= NULL
, *timestamp
= NULL
;
420 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
421 retrieve(d
, l
, "COREDUMP_PID", &pid
);
422 retrieve(d
, l
, "COREDUMP_UID", &uid
);
423 retrieve(d
, l
, "COREDUMP_GID", &gid
);
424 retrieve(d
, l
, "COREDUMP_SIGNAL", &sgnl
);
425 retrieve(d
, l
, "COREDUMP_EXE", &exe
);
426 retrieve(d
, l
, "COREDUMP_COMM", &comm
);
427 retrieve(d
, l
, "COREDUMP_CMDLINE", &cmdline
);
428 retrieve(d
, l
, "COREDUMP_UNIT", &unit
);
429 retrieve(d
, l
, "COREDUMP_USER_UNIT", &user_unit
);
430 retrieve(d
, l
, "COREDUMP_SESSION", &session
);
431 retrieve(d
, l
, "COREDUMP_OWNER_UID", &owner_uid
);
432 retrieve(d
, l
, "COREDUMP_SLICE", &slice
);
433 retrieve(d
, l
, "COREDUMP_CGROUP", &cgroup
);
434 retrieve(d
, l
, "COREDUMP_TIMESTAMP", ×tamp
);
435 retrieve(d
, l
, "_BOOT_ID", &boot_id
);
436 retrieve(d
, l
, "_MACHINE_ID", &machine_id
);
437 retrieve(d
, l
, "_HOSTNAME", &hostname
);
438 retrieve(d
, l
, "MESSAGE", &message
);
446 " PID: %s%s%s (%s)\n",
447 ansi_highlight(), strna(pid
), ansi_highlight_off(), comm
);
451 ansi_highlight(), strna(pid
), ansi_highlight_off());
456 if (parse_uid(uid
, &n
) >= 0) {
457 _cleanup_free_
char *u
= NULL
;
473 if (parse_gid(gid
, &n
) >= 0) {
474 _cleanup_free_
char *g
= NULL
;
490 if (safe_atoi(sgnl
, &sig
) >= 0)
491 fprintf(file
, " Signal: %s (%s)\n", sgnl
, signal_to_string(sig
));
493 fprintf(file
, " Signal: %s\n", sgnl
);
499 r
= safe_atou64(timestamp
, &u
);
501 char absolute
[FORMAT_TIMESTAMP_MAX
], relative
[FORMAT_TIMESPAN_MAX
];
504 " Timestamp: %s (%s)\n",
505 format_timestamp(absolute
, sizeof(absolute
), u
),
506 format_timestamp_relative(relative
, sizeof(relative
), u
));
509 fprintf(file
, " Timestamp: %s\n", timestamp
);
513 fprintf(file
, " Command Line: %s\n", cmdline
);
515 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_highlight_off());
517 fprintf(file
, " Control Group: %s\n", cgroup
);
519 fprintf(file
, " Unit: %s\n", unit
);
521 fprintf(file
, " User Unit: %s\n", unit
);
523 fprintf(file
, " Slice: %s\n", slice
);
525 fprintf(file
, " Session: %s\n", session
);
529 if (parse_uid(owner_uid
, &n
) >= 0) {
530 _cleanup_free_
char *u
= NULL
;
534 " Owner UID: %s (%s)\n",
543 fprintf(file
, " Boot ID: %s\n", boot_id
);
545 fprintf(file
, " Machine ID: %s\n", machine_id
);
547 fprintf(file
, " Hostname: %s\n", hostname
);
549 if (make_coredump_path(j
, &coredump
) >= 0)
550 if (access(coredump
, F_OK
) >= 0)
551 fprintf(file
, " Coredump: %s\n", coredump
);
554 _cleanup_free_
char *m
= NULL
;
556 m
= strreplace(message
, "\n", "\n ");
558 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
564 static int focus(sd_journal
*j
) {
567 r
= sd_journal_seek_tail(j
);
569 r
= sd_journal_previous(j
);
571 log_error("Failed to search journal: %s", strerror(-r
));
575 log_error("No match found.");
581 static void print_entry(sd_journal
*j
, unsigned n_found
) {
584 if (arg_action
== ACTION_INFO
)
585 print_info(stdout
, j
, n_found
);
587 print_field(stdout
, j
);
589 print_list(stdout
, j
, n_found
);
592 static int dump_list(sd_journal
*j
) {
593 unsigned n_found
= 0;
598 /* The coredumps are likely to compressed, and for just
599 * listing them we don't need to decompress them, so let's
600 * pick a fairly low data threshold here */
601 sd_journal_set_data_threshold(j
, 4096);
610 SD_JOURNAL_FOREACH(j
)
611 print_entry(j
, n_found
++);
613 if (!arg_field
&& n_found
<= 0) {
614 log_notice("No coredumps found.");
622 static int dump_core(sd_journal
* j
) {
629 /* We want full data, nothing truncated. */
630 sd_journal_set_data_threshold(j
, 0);
636 print_info(output
? stdout
: stderr
, j
, false);
638 if (on_tty() && !output
) {
639 log_error("Refusing to dump core to tty.");
643 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
645 _cleanup_free_
char *fn
= NULL
;
646 _cleanup_close_
int fd
= -1;
648 r
= make_coredump_path(j
, &fn
);
652 fd
= open(fn
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
655 log_error("Coredump neither in journal file nor stored externally on disk.");
657 log_error("Failed to open coredump file: %s", strerror(-r
));
662 r
= copy_bytes(fd
, output
? fileno(output
) : STDOUT_FILENO
, (off_t
) -1);
664 log_error("Failed to stream coredump: %s", strerror(-r
));
669 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
674 data
= (const uint8_t*) data
+ 9;
677 ret
= fwrite(data
, len
, 1, output
?: stdout
);
679 log_error("Dumping coredump failed: %m (%zu)", ret
);
684 r
= sd_journal_previous(j
);
686 log_warning("More than one entry matches, ignoring rest.");
691 static int run_gdb(sd_journal
*j
) {
693 _cleanup_free_
char *exe
= NULL
, *coredump
= NULL
;
694 char temp
[] = "/var/tmp/coredump-XXXXXX";
695 bool unlink_temp
= false;
705 sd_journal_set_data_threshold(j
, 0);
711 print_info(stdout
, j
, false);
714 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
716 log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r
));
721 data
= (const uint8_t*) data
+ 13;
724 exe
= strndup(data
, len
);
728 if (endswith(exe
, " (deleted)")) {
729 log_error("Binary already deleted.");
733 if (!path_is_absolute(exe
)) {
734 log_error("Binary is not an absolute path.");
738 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
741 r
= make_coredump_path(j
, &coredump
);
745 if (access(coredump
, R_OK
) < 0) {
747 log_error("Coredump neither in journal file nor stored externally on disk.");
749 log_error("Failed to access coredump file: %m");
757 log_error("Failed to retrieve COREDUMP field: %s", strerror(-r
));
761 _cleanup_close_
int fd
= -1;
765 data
= (const uint8_t*) data
+ 9;
768 fd
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
770 log_error("Failed to create temporary file: %m");
776 sz
= write(fd
, data
, len
);
778 log_error("Failed to write temporary file: %m");
782 if (sz
!= (ssize_t
) len
) {
783 log_error("Short write to temporary file.");
793 log_error("Failed to fork(): %m");
798 execlp("gdb", "gdb", exe
, path
, NULL
);
800 log_error("Failed to invoke gdb: %m");
804 r
= wait_for_terminate(pid
, &st
);
806 log_error("Failed to wait for gdb: %m");
810 r
= st
.si_code
== CLD_EXITED
? st
.si_status
: 255;
819 int main(int argc
, char *argv
[]) {
820 _cleanup_journal_close_ sd_journal
*j
= NULL
;
824 _cleanup_set_free_free_ Set
*matches
= NULL
;
826 setlocale(LC_ALL
, "");
827 log_parse_environment();
830 matches
= new_matches();
836 r
= parse_argv(argc
, argv
, matches
);
840 if (arg_action
== ACTION_NONE
)
843 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
845 log_error("Failed to open journal: %s", strerror(-r
));
849 SET_FOREACH(match
, matches
, it
) {
850 r
= sd_journal_add_match(j
, match
, strlen(match
));
852 log_error("Failed to add match '%s': %s",
853 match
, strerror(-r
));
858 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG
))) {
859 _cleanup_free_
char *filter
;
861 filter
= journal_make_match_string(j
);
862 log_debug("Journal filter: %s", filter
);
884 assert_not_reached("Shouldn't be here");
893 return r
>= 0 ? r
: EXIT_FAILURE
;