1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "sd-journal.h"
11 #include "sd-messages.h"
13 #include "alloc-util.h"
15 #include "bus-error.h"
16 #include "bus-locator.h"
18 #include "chase-symlinks.h"
20 #include "constants.h"
21 #include "dissect-image.h"
24 #include "format-table.h"
26 #include "glob-util.h"
27 #include "journal-internal.h"
28 #include "journal-util.h"
31 #include "main-func.h"
32 #include "mount-util.h"
34 #include "parse-argument.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "pretty-print.h"
38 #include "process-util.h"
39 #include "rlimit-util.h"
41 #include "signal-util.h"
42 #include "string-util.h"
44 #include "terminal-util.h"
45 #include "tmpfile-util.h"
46 #include "user-util.h"
49 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
51 static usec_t arg_since
= USEC_INFINITY
, arg_until
= USEC_INFINITY
;
52 static const char* arg_field
= NULL
;
53 static const char *arg_debugger
= NULL
;
54 static char **arg_debugger_args
= NULL
;
55 static const char *arg_directory
= NULL
;
56 static char *arg_root
= NULL
;
57 static char *arg_image
= NULL
;
58 static char **arg_file
= NULL
;
59 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
60 static PagerFlags arg_pager_flags
= 0;
61 static int arg_legend
= true;
62 static size_t arg_rows_max
= SIZE_MAX
;
63 static const char* arg_output
= NULL
;
64 static bool arg_reverse
= false;
65 static bool arg_quiet
= false;
66 static bool arg_all
= false;
68 STATIC_DESTRUCTOR_REGISTER(arg_debugger_args
, strv_freep
);
69 STATIC_DESTRUCTOR_REGISTER(arg_file
, strv_freep
);
71 static int add_match(sd_journal
*j
, const char *match
) {
72 _cleanup_free_
char *p
= NULL
;
73 const char* prefix
, *pattern
;
77 if (strchr(match
, '='))
79 else if (strchr(match
, '/')) {
80 r
= path_make_absolute_cwd(match
, &p
);
82 return log_error_errno(r
, "path_make_absolute_cwd(\"%s\"): %m", match
);
85 prefix
= "COREDUMP_EXE=";
86 } else if (parse_pid(match
, &pid
) >= 0)
87 prefix
= "COREDUMP_PID=";
89 prefix
= "COREDUMP_COMM=";
91 pattern
= strjoina(prefix
, match
);
92 log_debug("Adding match: %s", pattern
);
93 r
= sd_journal_add_match(j
, pattern
, 0);
95 return log_error_errno(r
, "Failed to add match \"%s\": %m", match
);
100 static int add_matches(sd_journal
*j
, char **matches
) {
103 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
, 0);
105 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
);
107 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
, 0);
109 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
);
111 STRV_FOREACH(match
, matches
) {
112 r
= add_match(j
, *match
);
120 static int acquire_journal(sd_journal
**ret
, char **matches
) {
121 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
127 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
129 return log_error_errno(r
, "Failed to open journals in directory: %s: %m", arg_directory
);
130 } else if (arg_root
) {
131 r
= sd_journal_open_directory(&j
, arg_root
, SD_JOURNAL_OS_ROOT
);
133 return log_error_errno(r
, "Failed to open journals in root directory: %s: %m", arg_root
);
134 } else if (arg_file
) {
135 r
= sd_journal_open_files(&j
, (const char**)arg_file
, 0);
137 return log_error_errno(r
, "Failed to open journal files: %m");
139 r
= sd_journal_open(&j
, arg_all
? 0 : SD_JOURNAL_LOCAL_ONLY
);
141 return log_error_errno(r
, "Failed to open journal: %m");
144 r
= journal_access_check_and_warn(j
, arg_quiet
, true);
148 r
= add_matches(j
, matches
);
153 _cleanup_free_
char *filter
= NULL
;
155 filter
= journal_make_match_string(j
);
156 log_debug("Journal filter: %s", filter
);
164 static int verb_help(int argc
, char **argv
, void *userdata
) {
165 _cleanup_free_
char *link
= NULL
;
168 r
= terminal_urlify_man("coredumpctl", "1", &link
);
172 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
173 "%5$sList or retrieve coredumps from the journal.%6$s\n"
174 "\n%3$sCommands:%4$s\n"
175 " list [MATCHES...] List available coredumps (default)\n"
176 " info [MATCHES...] Show detailed information about one or more coredumps\n"
177 " dump [MATCHES...] Print first matching coredump to stdout\n"
178 " debug [MATCHES...] Start a debugger for the first matching coredump\n"
179 "\n%3$sOptions:%4$s\n"
180 " -h --help Show this help\n"
181 " --version Print version string\n"
182 " --no-pager Do not pipe output into a pager\n"
183 " --no-legend Do not print the column headers\n"
184 " --json=pretty|short|off\n"
185 " Generate JSON output\n"
186 " --debugger=DEBUGGER Use the given debugger\n"
187 " -A --debugger-arguments=ARGS Pass the given arguments to the debugger\n"
188 " -n INT Show maximum number of rows\n"
189 " -1 Show information about most recent entry only\n"
190 " -S --since=DATE Only print coredumps since the date\n"
191 " -U --until=DATE Only print coredumps until the date\n"
192 " -r --reverse Show the newest entries first\n"
193 " -F --field=FIELD List all values a certain field takes\n"
194 " -o --output=FILE Write output to FILE\n"
195 " --file=PATH Use journal file\n"
196 " -D --directory=DIR Use journal files from directory\n\n"
197 " -q --quiet Do not show info messages and privilege warning\n"
198 " --all Look at all journal files instead of local ones\n"
199 " --root=PATH Operate on an alternate filesystem root\n"
200 " --image=PATH Operate on disk image as filesystem root\n"
201 "\nSee the %2$s for details.\n",
202 program_invocation_short_name
,
212 static int parse_argv(int argc
, char *argv
[]) {
227 static const struct option options
[] = {
228 { "help", no_argument
, NULL
, 'h' },
229 { "version" , no_argument
, NULL
, ARG_VERSION
},
230 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
231 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
232 { "debugger", required_argument
, NULL
, ARG_DEBUGGER
},
233 { "debugger-arguments", required_argument
, NULL
, 'A' },
234 { "output", required_argument
, NULL
, 'o' },
235 { "field", required_argument
, NULL
, 'F' },
236 { "file", required_argument
, NULL
, ARG_FILE
},
237 { "directory", required_argument
, NULL
, 'D' },
238 { "reverse", no_argument
, NULL
, 'r' },
239 { "since", required_argument
, NULL
, 'S' },
240 { "until", required_argument
, NULL
, 'U' },
241 { "quiet", no_argument
, NULL
, 'q' },
242 { "json", required_argument
, NULL
, ARG_JSON
},
243 { "root", required_argument
, NULL
, ARG_ROOT
},
244 { "image", required_argument
, NULL
, ARG_IMAGE
},
245 { "all", no_argument
, NULL
, ARG_ALL
},
252 while ((c
= getopt_long(argc
, argv
, "hA:o:F:1D:rS:U:qn:", options
, NULL
)) >= 0)
255 return verb_help(0, NULL
, NULL
);
261 arg_pager_flags
|= PAGER_DISABLE
;
269 arg_debugger
= optarg
;
273 _cleanup_strv_free_
char **l
= NULL
;
274 r
= strv_split_full(&l
, optarg
, WHITESPACE
, EXTRACT_UNQUOTE
);
276 return log_error_errno(r
, "Failed to parse debugger arguments '%s': %m", optarg
);
277 strv_free_and_replace(arg_debugger_args
, l
);
282 r
= glob_extend(&arg_file
, optarg
, GLOB_NOCHECK
);
284 return log_error_errno(r
, "Failed to add paths: %m");
289 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
290 "Cannot set output more than once.");
296 r
= parse_timestamp(optarg
, &arg_since
);
298 return log_error_errno(r
, "Failed to parse timestamp '%s': %m", optarg
);
302 r
= parse_timestamp(optarg
, &arg_until
);
304 return log_error_errno(r
, "Failed to parse timestamp '%s': %m", optarg
);
309 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
310 "Cannot use --field/-F more than once.");
322 r
= safe_atou(optarg
, &n
);
324 return log_error_errno(r
< 0 ? r
: SYNTHETIC_ERRNO(EINVAL
),
325 "Invalid numeric parameter to -n: %s", optarg
);
332 arg_directory
= optarg
;
336 r
= parse_path_argument(optarg
, false, &arg_root
);
342 r
= parse_path_argument(optarg
, false, &arg_image
);
356 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
370 assert_not_reached();
373 if (arg_since
!= USEC_INFINITY
&& arg_until
!= USEC_INFINITY
&&
374 arg_since
> arg_until
)
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
376 "--since= must be before --until=.");
378 if ((!!arg_directory
+ !!arg_image
+ !!arg_root
) > 1)
379 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Please specify either --root=, --image= or -D/--directory=, the combination of these options is not supported.");
384 static int retrieve(const void *data
,
392 ident
= strlen(name
) + 1; /* name + "=" */
397 if (memcmp(data
, name
, ident
- 1) != 0)
400 if (((const char*) data
)[ident
- 1] != '=')
403 v
= strndup((const char*)data
+ ident
, len
- ident
);
407 free_and_replace(*var
, v
);
411 static int print_field(FILE* file
, sd_journal
*j
) {
420 /* A (user-specified) field may appear more than once for a given entry.
421 * We will print all of the occurrences.
422 * This is different below for fields that systemd-coredump uses,
423 * because they cannot meaningfully appear more than once.
425 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
426 _cleanup_free_
char *value
= NULL
;
429 r
= retrieve(d
, l
, arg_field
, &value
);
433 fprintf(file
, "%s\n", value
);
439 #define RETRIEVE(d, l, name, arg) \
441 int _r = retrieve(d, l, name, &arg); \
448 static void analyze_coredump_file(
450 const char **ret_state
,
451 const char **ret_color
,
452 uint64_t *ret_size
) {
454 _cleanup_close_
int fd
= -EBADF
;
463 fd
= open(path
, O_PATH
|O_CLOEXEC
);
465 if (errno
== ENOENT
) {
466 *ret_state
= "missing";
467 *ret_color
= ansi_grey();
468 *ret_size
= UINT64_MAX
;
474 r
= access_fd(fd
, R_OK
);
475 if (ERRNO_IS_PRIVILEGE(r
)) {
476 *ret_state
= "inaccessible";
477 *ret_color
= ansi_highlight_yellow();
478 *ret_size
= UINT64_MAX
;
484 if (fstat(fd
, &st
) < 0)
487 if (!S_ISREG(st
.st_mode
))
490 *ret_state
= "present";
492 *ret_size
= st
.st_size
;
496 *ret_state
= "error";
497 *ret_color
= ansi_highlight_red();
498 *ret_size
= UINT64_MAX
;
501 static int resolve_filename(const char *root
, char **p
) {
502 char *resolved
= NULL
;
508 r
= chase_symlinks(*p
, root
, CHASE_PREFIX_ROOT
|CHASE_NONEXISTENT
, &resolved
, NULL
);
510 return log_error_errno(r
, "Failed to resolve \"%s%s\": %m", strempty(root
), *p
);
512 free_and_replace(*p
, resolved
);
514 /* chase_symlinks() with flag CHASE_NONEXISTENT will return 0 if the file doesn't exist and 1 if it does.
515 * Return that to the caller
520 static int print_list(FILE* file
, sd_journal
*j
, Table
*t
) {
522 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
523 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
524 *filename
= NULL
, *truncated
= NULL
, *coredump
= NULL
;
528 int r
, signal_as_int
= 0;
529 const char *present
= NULL
, *color
= NULL
;
530 uint64_t size
= UINT64_MAX
;
531 bool normal_coredump
;
532 uid_t uid_as_int
= UID_INVALID
;
533 gid_t gid_as_int
= GID_INVALID
;
534 pid_t pid_as_int
= 0;
540 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
541 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
542 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
543 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
544 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
545 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
546 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
547 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
548 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
549 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
550 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
551 RETRIEVE(d
, l
, "COREDUMP", coredump
);
554 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
&& !filename
)
555 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL
), "Empty coredump log entry");
557 (void) parse_uid(uid
, &uid_as_int
);
558 (void) parse_gid(gid
, &gid_as_int
);
559 (void) parse_pid(pid
, &pid_as_int
);
560 signal_as_int
= signal_from_string(sgnl
);
562 r
= sd_journal_get_realtime_usec(j
, &ts
);
564 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
566 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
569 r
= resolve_filename(arg_root
, &filename
);
573 analyze_coredump_file(filename
, &present
, &color
, &size
);
576 else if (normal_coredump
) {
582 if (STRPTR_IN_SET(present
, "present", "journal") && truncated
&& parse_boolean(truncated
) > 0)
583 present
= "truncated";
588 TABLE_PID
, pid_as_int
,
589 TABLE_UID
, uid_as_int
,
590 TABLE_GID
, gid_as_int
,
591 TABLE_SIGNAL
, normal_coredump
? signal_as_int
: 0,
592 TABLE_STRING
, present
,
593 TABLE_SET_COLOR
, color
,
594 TABLE_STRING
, exe
?: comm
?: cmdline
,
597 return table_log_add_error(r
);
602 static int print_info(FILE *file
, sd_journal
*j
, bool need_space
) {
604 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
605 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
606 *unit
= NULL
, *user_unit
= NULL
, *session
= NULL
,
607 *boot_id
= NULL
, *machine_id
= NULL
, *hostname
= NULL
,
608 *slice
= NULL
, *cgroup
= NULL
, *owner_uid
= NULL
,
609 *message
= NULL
, *timestamp
= NULL
, *filename
= NULL
,
610 *truncated
= NULL
, *coredump
= NULL
,
611 *pkgmeta_name
= NULL
, *pkgmeta_version
= NULL
, *pkgmeta_json
= NULL
;
614 bool normal_coredump
;
620 (void) sd_journal_set_data_threshold(j
, 0);
622 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
623 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
624 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
625 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
626 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
627 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
628 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
629 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
630 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
631 RETRIEVE(d
, l
, "COREDUMP_HOSTNAME", hostname
);
632 RETRIEVE(d
, l
, "COREDUMP_UNIT", unit
);
633 RETRIEVE(d
, l
, "COREDUMP_USER_UNIT", user_unit
);
634 RETRIEVE(d
, l
, "COREDUMP_SESSION", session
);
635 RETRIEVE(d
, l
, "COREDUMP_OWNER_UID", owner_uid
);
636 RETRIEVE(d
, l
, "COREDUMP_SLICE", slice
);
637 RETRIEVE(d
, l
, "COREDUMP_CGROUP", cgroup
);
638 RETRIEVE(d
, l
, "COREDUMP_TIMESTAMP", timestamp
);
639 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
640 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
641 RETRIEVE(d
, l
, "COREDUMP", coredump
);
642 RETRIEVE(d
, l
, "COREDUMP_PACKAGE_NAME", pkgmeta_name
);
643 RETRIEVE(d
, l
, "COREDUMP_PACKAGE_VERSION", pkgmeta_version
);
644 RETRIEVE(d
, l
, "COREDUMP_PACKAGE_JSON", pkgmeta_json
);
645 RETRIEVE(d
, l
, "_BOOT_ID", boot_id
);
646 RETRIEVE(d
, l
, "_MACHINE_ID", machine_id
);
647 RETRIEVE(d
, l
, "MESSAGE", message
);
653 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
657 " PID: %s%s%s (%s)\n",
658 ansi_highlight(), strna(pid
), ansi_normal(), comm
);
662 ansi_highlight(), strna(pid
), ansi_normal());
667 if (parse_uid(uid
, &n
) >= 0) {
668 _cleanup_free_
char *u
= NULL
;
684 if (parse_gid(gid
, &n
) >= 0) {
685 _cleanup_free_
char *g
= NULL
;
700 const char *name
= normal_coredump
? "Signal" : "Reason";
702 if (normal_coredump
&& safe_atoi(sgnl
, &sig
) >= 0)
703 fprintf(file
, " %s: %s (%s)\n", name
, sgnl
, signal_to_string(sig
));
705 fprintf(file
, " %s: %s\n", name
, sgnl
);
711 r
= safe_atou64(timestamp
, &u
);
713 fprintf(file
, " Timestamp: %s (%s)\n",
714 FORMAT_TIMESTAMP(u
), FORMAT_TIMESTAMP_RELATIVE(u
));
717 fprintf(file
, " Timestamp: %s\n", timestamp
);
721 fprintf(file
, " Command Line: %s\n", cmdline
);
723 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_normal());
725 fprintf(file
, " Control Group: %s\n", cgroup
);
727 fprintf(file
, " Unit: %s\n", unit
);
729 fprintf(file
, " User Unit: %s\n", user_unit
);
731 fprintf(file
, " Slice: %s\n", slice
);
733 fprintf(file
, " Session: %s\n", session
);
737 if (parse_uid(owner_uid
, &n
) >= 0) {
738 _cleanup_free_
char *u
= NULL
;
742 " Owner UID: %s (%s)\n",
751 fprintf(file
, " Boot ID: %s\n", boot_id
);
753 fprintf(file
, " Machine ID: %s\n", machine_id
);
755 fprintf(file
, " Hostname: %s\n", hostname
);
758 r
= resolve_filename(arg_root
, &filename
);
762 const char *state
= NULL
, *color
= NULL
;
763 uint64_t size
= UINT64_MAX
;
765 analyze_coredump_file(filename
, &state
, &color
, &size
);
767 if (STRPTR_IN_SET(state
, "present", "journal") && truncated
&& parse_boolean(truncated
) > 0)
771 " Storage: %s%s (%s)%s\n",
777 if (size
!= UINT64_MAX
)
778 fprintf(file
, " Size on Disk: %s\n", FORMAT_BYTES(size
));
781 fprintf(file
, " Storage: journal\n");
783 fprintf(file
, " Storage: none\n");
785 if (pkgmeta_name
&& pkgmeta_version
)
786 fprintf(file
, " Package: %s/%s\n", pkgmeta_name
, pkgmeta_version
);
788 /* Print out the build-id of the 'main' ELF module, by matching the JSON key
789 * with the 'exe' field. */
790 if (exe
&& pkgmeta_json
) {
791 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
793 r
= json_parse(pkgmeta_json
, 0, &v
, NULL
, NULL
);
795 _cleanup_free_
char *esc
= cescape(pkgmeta_json
);
796 log_warning_errno(r
, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc
));
798 const char *module_name
;
799 JsonVariant
*module_json
;
801 JSON_VARIANT_OBJECT_FOREACH(module_name
, module_json
, v
) {
802 JsonVariant
*build_id
;
804 /* We only print the build-id for the 'main' ELF module */
805 if (!path_equal_filename(module_name
, exe
))
808 build_id
= json_variant_by_key(module_json
, "buildId");
810 fprintf(file
, " build-id: %s\n", json_variant_string(build_id
));
818 _cleanup_free_
char *m
= NULL
;
820 m
= strreplace(message
, "\n", "\n ");
822 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
828 static int focus(sd_journal
*j
) {
831 r
= sd_journal_seek_tail(j
);
833 r
= sd_journal_previous(j
);
835 return log_error_errno(r
, "Failed to search journal: %m");
837 return log_error_errno(SYNTHETIC_ERRNO(ESRCH
),
842 static int print_entry(
850 return print_list(stdout
, j
, t
);
852 return print_field(stdout
, j
);
854 return print_info(stdout
, j
, n_found
> 0);
857 static int dump_list(int argc
, char **argv
, void *userdata
) {
858 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
859 _cleanup_(table_unrefp
) Table
*t
= NULL
;
864 verb_is_info
= argc
>= 1 && streq(argv
[0], "info");
866 r
= acquire_journal(&j
, argv
+ 1);
870 /* The coredumps are likely compressed, and for just listing them we don't need to decompress them,
871 * so let's pick a fairly low data threshold here */
872 (void) sd_journal_set_data_threshold(j
, 4096);
874 if (!verb_is_info
&& !arg_field
) {
875 t
= table_new("time", "pid", "uid", "gid", "sig", "corefile", "exe", "size");
879 (void) table_set_align_percent(t
, TABLE_HEADER_CELL(1), 100);
880 (void) table_set_align_percent(t
, TABLE_HEADER_CELL(2), 100);
881 (void) table_set_align_percent(t
, TABLE_HEADER_CELL(3), 100);
882 (void) table_set_align_percent(t
, TABLE_HEADER_CELL(7), 100);
884 table_set_ersatz_string(t
, TABLE_ERSATZ_DASH
);
886 pager_open(arg_pager_flags
);
888 /* "info" without pattern implies "-1" */
889 if ((arg_rows_max
== 1 && arg_reverse
) || (verb_is_info
&& argc
== 1)) {
894 r
= print_entry(j
, 0, t
);
898 if (arg_since
!= USEC_INFINITY
&& !arg_reverse
)
899 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
900 else if (arg_until
!= USEC_INFINITY
&& arg_reverse
)
901 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
902 else if (arg_reverse
)
903 r
= sd_journal_seek_tail(j
);
905 r
= sd_journal_seek_head(j
);
907 return log_error_errno(r
, "Failed to seek to date: %m");
911 r
= sd_journal_next(j
);
913 r
= sd_journal_previous(j
);
915 return log_error_errno(r
, "Failed to iterate through journal: %m");
919 if (arg_until
!= USEC_INFINITY
&& !arg_reverse
) {
922 r
= sd_journal_get_realtime_usec(j
, &usec
);
924 return log_error_errno(r
, "Failed to determine timestamp: %m");
925 if (usec
> arg_until
)
929 if (arg_since
!= USEC_INFINITY
&& arg_reverse
) {
932 r
= sd_journal_get_realtime_usec(j
, &usec
);
934 return log_error_errno(r
, "Failed to determine timestamp: %m");
935 if (usec
< arg_since
)
939 r
= print_entry(j
, n_found
++, t
);
943 if (arg_rows_max
!= SIZE_MAX
&& n_found
>= arg_rows_max
)
947 if (!arg_field
&& n_found
<= 0) {
949 log_notice("No coredumps found.");
955 r
= table_print_with_pager(t
, arg_json_format_flags
, arg_pager_flags
, arg_legend
);
963 static int save_core(sd_journal
*j
, FILE *file
, char **path
, bool *unlink_temp
) {
965 _cleanup_free_
char *filename
= NULL
;
968 _cleanup_close_
int fdt
= -EBADF
;
971 assert(!(file
&& path
)); /* At most one can be specified */
972 assert(!!path
== !!unlink_temp
); /* Those must be specified together */
974 /* Look for a coredump on disk first. */
975 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
977 _cleanup_free_
char *resolved
= NULL
;
979 r
= retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
984 r
= chase_symlinks_and_access(filename
, arg_root
, CHASE_PREFIX_ROOT
, F_OK
, &resolved
, NULL
);
986 return log_error_errno(r
, "Cannot access \"%s%s\": %m", strempty(arg_root
), filename
);
988 free_and_replace(filename
, resolved
);
990 if (path
&& !ENDSWITH_SET(filename
, ".xz", ".lz4", ".zst")) {
991 *path
= TAKE_PTR(filename
);
998 return log_error_errno(r
, "Failed to retrieve COREDUMP_FILENAME field: %m");
999 /* Check that we can have a COREDUMP field. We still haven't set a high
1000 * data threshold, so we'll get a few kilobytes at most.
1003 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
1005 return log_error_errno(r
, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
1007 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
1013 /* Create a temporary file to write the uncompressed core to. */
1015 r
= var_tmp_dir(&vt
);
1017 return log_error_errno(r
, "Failed to acquire temporary directory path: %m");
1019 temp
= path_join(vt
, "coredump-XXXXXX");
1023 fdt
= mkostemp_safe(temp
);
1025 return log_error_errno(fdt
, "Failed to create temporary file: %m");
1026 log_debug("Created temporary file %s", temp
);
1030 /* If neither path or file are specified, we will write to stdout. Let's now check
1031 * if stdout is connected to a tty. We checked that the file exists, or that the
1032 * core might be stored in the journal. In this second case, if we found the entry,
1033 * in all likelihood we will be able to access the COREDUMP= field. In either case,
1034 * we stop before doing any "real" work, i.e. before starting decompression or
1035 * reading from the file or creating temporary files.
1039 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY
),
1040 "Refusing to dump core to tty"
1041 " (use shell redirection or specify --output).");
1049 #if HAVE_COMPRESSION
1050 _cleanup_close_
int fdf
= -EBADF
;
1052 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
1054 r
= log_error_errno(errno
, "Failed to open %s: %m", filename
);
1058 r
= decompress_stream(filename
, fdf
, fd
, -1);
1060 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
1064 r
= log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
1065 "Cannot decompress file. Compiled without compression support.");
1071 /* We want full data, nothing truncated. */
1072 sd_journal_set_data_threshold(j
, 0);
1074 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
1076 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
1082 sz
= write(fd
, data
, len
);
1084 r
= log_error_errno(errno
, "Failed to write output: %m");
1087 if (sz
!= (ssize_t
) len
) {
1088 log_error("Short write to output.");
1096 *unlink_temp
= true;
1102 (void) unlink(temp
);
1103 log_debug("Removed temporary file %s", temp
);
1108 static int dump_core(int argc
, char **argv
, void *userdata
) {
1109 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1110 _cleanup_fclose_
FILE *f
= NULL
;
1114 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1115 "Option --field/-F only makes sense with list");
1117 r
= acquire_journal(&j
, argv
+ 1);
1126 f
= fopen(arg_output
, "we");
1128 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", arg_output
);
1131 print_info(f
? stdout
: stderr
, j
, false);
1133 r
= save_core(j
, f
, NULL
, NULL
);
1137 r
= sd_journal_previous(j
);
1138 if (r
> 0 && !arg_quiet
)
1139 log_notice("More than one entry matches, ignoring rest.");
1144 static int run_debug(int argc
, char **argv
, void *userdata
) {
1145 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1146 _cleanup_free_
char *exe
= NULL
, *path
= NULL
;
1147 _cleanup_strv_free_
char **debugger_call
= NULL
;
1148 bool unlink_path
= false;
1149 const char *data
, *fork_name
;
1154 if (!arg_debugger
) {
1157 env_debugger
= getenv("SYSTEMD_DEBUGGER");
1159 arg_debugger
= env_debugger
;
1161 arg_debugger
= "gdb";
1164 r
= strv_extend(&debugger_call
, arg_debugger
);
1168 r
= strv_extend_strv(&debugger_call
, arg_debugger_args
, false);
1173 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1174 "Option --field/-F only makes sense with list");
1176 r
= acquire_journal(&j
, argv
+ 1);
1185 print_info(stdout
, j
, false);
1186 fputs("\n", stdout
);
1189 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
1191 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
1193 assert(len
> STRLEN("COREDUMP_EXE="));
1194 data
+= STRLEN("COREDUMP_EXE=");
1195 len
-= STRLEN("COREDUMP_EXE=");
1197 exe
= strndup(data
, len
);
1201 if (endswith(exe
, " (deleted)"))
1202 return log_error_errno(SYNTHETIC_ERRNO(ENOENT
),
1203 "Binary already deleted.");
1205 if (!path_is_absolute(exe
))
1206 return log_error_errno(SYNTHETIC_ERRNO(ENOENT
),
1207 "Binary is not an absolute path.");
1209 r
= resolve_filename(arg_root
, &exe
);
1213 r
= save_core(j
, NULL
, &path
, &unlink_path
);
1217 r
= strv_extend_strv(&debugger_call
, STRV_MAKE(exe
, "-c", path
), false);
1222 if (streq(arg_debugger
, "gdb")) {
1223 const char *sysroot_cmd
;
1224 sysroot_cmd
= strjoina("set sysroot ", arg_root
);
1226 r
= strv_extend_strv(&debugger_call
, STRV_MAKE("-iex", sysroot_cmd
), false);
1229 } else if (streq(arg_debugger
, "lldb")) {
1230 const char *sysroot_cmd
;
1231 sysroot_cmd
= strjoina("platform select --sysroot ", arg_root
, " host");
1233 r
= strv_extend_strv(&debugger_call
, STRV_MAKE("-O", sysroot_cmd
), false);
1239 /* Don't interfere with gdb and its handling of SIGINT. */
1240 (void) ignore_signals(SIGINT
);
1242 fork_name
= strjoina("(", debugger_call
[0], ")");
1244 r
= safe_fork(fork_name
, FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_CLOSE_ALL_FDS
|FORK_RLIMIT_NOFILE_SAFE
|FORK_LOG
|FORK_FLUSH_STDIO
, &pid
);
1248 execvp(debugger_call
[0], debugger_call
);
1250 log_error_errno(errno
, "Failed to invoke %s: %m", debugger_call
[0]);
1251 _exit(EXIT_FAILURE
);
1254 r
= wait_for_terminate_and_check(debugger_call
[0], pid
, WAIT_LOG_ABNORMAL
);
1257 (void) default_signals(SIGINT
);
1260 log_debug("Removed temporary file %s", path
);
1261 (void) unlink(path
);
1267 static int check_units_active(void) {
1268 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1269 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1270 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1271 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1273 const char *id
, *state
, *substate
;
1278 r
= sd_bus_default_system(&bus
);
1280 log_debug("D-Bus is not running, skipping active unit check");
1284 return log_error_errno(r
, "Failed to acquire bus: %m");
1286 r
= bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "ListUnitsByPatterns");
1288 return bus_log_create_error(r
);
1290 r
= sd_bus_message_append_strv(m
, NULL
);
1292 return bus_log_create_error(r
);
1294 r
= sd_bus_message_append_strv(m
, STRV_MAKE("systemd-coredump@*.service"));
1296 return bus_log_create_error(r
);
1298 r
= sd_bus_call(bus
, m
, SHORT_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1300 return log_error_errno(r
, "Failed to check if any systemd-coredump@.service units are running: %s",
1301 bus_error_message(&error
, r
));
1303 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1305 return bus_log_parse_error(r
);
1307 while ((r
= sd_bus_message_read(
1308 reply
, "(ssssssouso)",
1309 &id
, NULL
, NULL
, &state
, &substate
,
1310 NULL
, NULL
, NULL
, NULL
, NULL
)) > 0) {
1311 bool found
= !STR_IN_SET(state
, "inactive", "dead", "failed");
1312 log_debug("Unit %s is %s/%s, %scounting it.", id
, state
, substate
, found
? "" : "not ");
1316 return bus_log_parse_error(r
);
1318 r
= sd_bus_message_exit_container(reply
);
1320 return bus_log_parse_error(r
);
1325 static int coredumpctl_main(int argc
, char *argv
[]) {
1327 static const Verb verbs
[] = {
1328 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, dump_list
},
1329 { "info", VERB_ANY
, VERB_ANY
, 0, dump_list
},
1330 { "dump", VERB_ANY
, VERB_ANY
, 0, dump_core
},
1331 { "debug", VERB_ANY
, VERB_ANY
, 0, run_debug
},
1332 { "gdb", VERB_ANY
, VERB_ANY
, 0, run_debug
},
1333 { "help", VERB_ANY
, 1, 0, verb_help
},
1337 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1340 static int run(int argc
, char *argv
[]) {
1341 _cleanup_(loop_device_unrefp
) LoopDevice
*loop_device
= NULL
;
1342 _cleanup_(umount_and_rmdir_and_freep
) char *mounted_dir
= NULL
;
1343 int r
, units_active
;
1345 setlocale(LC_ALL
, "");
1348 /* The journal merging logic potentially needs a lot of fds. */
1349 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE
);
1351 r
= parse_argv(argc
, argv
);
1357 units_active
= check_units_active(); /* error is treated the same as 0 */
1362 r
= mount_image_privately_interactively(
1364 DISSECT_IMAGE_GENERIC_ROOT
|
1365 DISSECT_IMAGE_REQUIRE_ROOT
|
1366 DISSECT_IMAGE_RELAX_VAR_CHECK
|
1367 DISSECT_IMAGE_VALIDATE_OS
,
1373 arg_root
= strdup(mounted_dir
);
1378 r
= coredumpctl_main(argc
, argv
);
1380 if (units_active
> 0)
1381 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1382 ansi_highlight_red(),
1383 units_active
, units_active
== 1 ? "unit is running" : "units are running",
1389 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);