1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "sd-journal.h"
11 #include "sd-messages.h"
13 #include "alloc-util.h"
14 #include "bus-error.h"
20 #include "glob-util.h"
21 #include "journal-internal.h"
22 #include "journal-util.h"
25 #include "main-func.h"
27 #include "parse-util.h"
28 #include "path-util.h"
29 #include "pretty-print.h"
30 #include "process-util.h"
31 #include "rlimit-util.h"
33 #include "signal-util.h"
34 #include "string-util.h"
36 #include "terminal-util.h"
37 #include "tmpfile-util.h"
38 #include "user-util.h"
42 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
44 static usec_t arg_since
= USEC_INFINITY
, arg_until
= USEC_INFINITY
;
45 static const char* arg_field
= NULL
;
46 static const char *arg_debugger
= NULL
;
47 static const char *arg_directory
= NULL
;
48 static char **arg_file
= NULL
;
49 static PagerFlags arg_pager_flags
= 0;
50 static int arg_no_legend
= false;
51 static int arg_one
= false;
52 static const char* arg_output
= NULL
;
53 static bool arg_reverse
= false;
54 static bool arg_quiet
= false;
56 STATIC_DESTRUCTOR_REGISTER(arg_file
, strv_freep
);
58 static int add_match(sd_journal
*j
, const char *match
) {
59 _cleanup_free_
char *p
= NULL
;
60 const char* prefix
, *pattern
;
64 if (strchr(match
, '='))
66 else if (strchr(match
, '/')) {
67 r
= path_make_absolute_cwd(match
, &p
);
69 return log_error_errno(r
, "path_make_absolute_cwd(\"%s\"): %m", match
);
72 prefix
= "COREDUMP_EXE=";
73 } else if (parse_pid(match
, &pid
) >= 0)
74 prefix
= "COREDUMP_PID=";
76 prefix
= "COREDUMP_COMM=";
78 pattern
= strjoina(prefix
, match
);
79 log_debug("Adding match: %s", pattern
);
80 r
= sd_journal_add_match(j
, pattern
, 0);
82 return log_error_errno(r
, "Failed to add match \"%s\": %m", match
);
87 static int add_matches(sd_journal
*j
, char **matches
) {
91 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
, 0);
93 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
);
95 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
, 0);
97 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
);
99 STRV_FOREACH(match
, matches
) {
100 r
= add_match(j
, *match
);
108 static int acquire_journal(sd_journal
**ret
, char **matches
) {
109 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
115 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
117 return log_error_errno(r
, "Failed to open journals in directory: %s: %m", arg_directory
);
118 } else if (arg_file
) {
119 r
= sd_journal_open_files(&j
, (const char**)arg_file
, 0);
121 return log_error_errno(r
, "Failed to open journal files: %m");
123 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
125 return log_error_errno(r
, "Failed to open journal: %m");
128 r
= journal_access_check_and_warn(j
, arg_quiet
, true);
132 r
= add_matches(j
, matches
);
137 _cleanup_free_
char *filter
;
139 filter
= journal_make_match_string(j
);
140 log_debug("Journal filter: %s", filter
);
148 static int help(void) {
149 _cleanup_free_
char *link
= NULL
;
152 r
= terminal_urlify_man("coredumpctl", "1", &link
);
156 printf("%s [OPTIONS...] COMMAND ...\n\n"
157 "%sList or retrieve coredumps from the journal.%s\n"
159 " list [MATCHES...] List available coredumps (default)\n"
160 " info [MATCHES...] Show detailed information about one or more coredumps\n"
161 " dump [MATCHES...] Print first matching coredump to stdout\n"
162 " debug [MATCHES...] Start a debugger for the first matching coredump\n"
164 " -h --help Show this help\n"
165 " --version Print version string\n"
166 " --no-pager Do not pipe output into a pager\n"
167 " --no-legend Do not print the column headers\n"
168 " --debugger=DEBUGGER Use the given debugger\n"
169 " -1 Show information about most recent entry only\n"
170 " -S --since=DATE Only print coredumps since the date\n"
171 " -U --until=DATE Only print coredumps until the date\n"
172 " -r --reverse Show the newest entries first\n"
173 " -F --field=FIELD List all values a certain field takes\n"
174 " -o --output=FILE Write output to FILE\n"
175 " --file=PATH Use journal file\n"
176 " -D --directory=DIR Use journal files from directory\n\n"
177 " -q --quiet Do not show info messages and privilege warning\n"
178 "\nSee the %s for details.\n"
179 , program_invocation_short_name
188 static int parse_argv(int argc
, char *argv
[]) {
199 static const struct option options
[] = {
200 { "help", no_argument
, NULL
, 'h' },
201 { "version" , no_argument
, NULL
, ARG_VERSION
},
202 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
203 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
204 { "debugger", required_argument
, NULL
, ARG_DEBUGGER
},
205 { "output", required_argument
, NULL
, 'o' },
206 { "field", required_argument
, NULL
, 'F' },
207 { "file", required_argument
, NULL
, ARG_FILE
},
208 { "directory", required_argument
, NULL
, 'D' },
209 { "reverse", no_argument
, NULL
, 'r' },
210 { "since", required_argument
, NULL
, 'S' },
211 { "until", required_argument
, NULL
, 'U' },
212 { "quiet", no_argument
, NULL
, 'q' },
219 while ((c
= getopt_long(argc
, argv
, "ho:F:1D:rS:U:q", options
, NULL
)) >= 0)
228 arg_pager_flags
|= PAGER_DISABLE
;
232 arg_no_legend
= true;
236 arg_debugger
= optarg
;
240 r
= glob_extend(&arg_file
, optarg
, GLOB_NOCHECK
);
242 return log_error_errno(r
, "Failed to add paths: %m");
247 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
248 "Cannot set output more than once.");
254 r
= parse_timestamp(optarg
, &arg_since
);
256 return log_error_errno(r
, "Failed to parse timestamp '%s': %m", optarg
);
260 r
= parse_timestamp(optarg
, &arg_until
);
262 return log_error_errno(r
, "Failed to parse timestamp '%s': %m", optarg
);
267 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
268 "Cannot use --field/-F more than once.");
277 arg_directory
= optarg
;
292 assert_not_reached("Unhandled option");
295 if (arg_since
!= USEC_INFINITY
&& arg_until
!= USEC_INFINITY
&&
296 arg_since
> arg_until
)
297 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
298 "--since= must be before --until=.");
303 static int retrieve(const void *data
,
311 ident
= strlen(name
) + 1; /* name + "=" */
316 if (memcmp(data
, name
, ident
- 1) != 0)
319 if (((const char*) data
)[ident
- 1] != '=')
322 v
= strndup((const char*)data
+ ident
, len
- ident
);
326 free_and_replace(*var
, v
);
330 static int print_field(FILE* file
, sd_journal
*j
) {
339 /* A (user-specified) field may appear more than once for a given entry.
340 * We will print all of the occurrences.
341 * This is different below for fields that systemd-coredump uses,
342 * because they cannot meaningfully appear more than once.
344 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
345 _cleanup_free_
char *value
= NULL
;
348 r
= retrieve(d
, l
, arg_field
, &value
);
352 fprintf(file
, "%s\n", value
);
358 #define RETRIEVE(d, l, name, arg) \
360 int _r = retrieve(d, l, name, &arg); \
367 static int print_list(FILE* file
, sd_journal
*j
, int had_legend
) {
369 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
370 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
371 *filename
= NULL
, *truncated
= NULL
, *coredump
= NULL
;
375 char buf
[FORMAT_TIMESTAMP_MAX
];
378 bool normal_coredump
;
383 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
384 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
385 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
386 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
387 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
388 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
389 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
390 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
391 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
392 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
393 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
394 RETRIEVE(d
, l
, "COREDUMP", coredump
);
397 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
&& !filename
) {
398 log_warning("Empty coredump log entry");
402 r
= sd_journal_get_realtime_usec(j
, &t
);
404 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
406 format_timestamp(buf
, sizeof(buf
), t
);
408 if (!had_legend
&& !arg_no_legend
)
409 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
410 FORMAT_TIMESTAMP_WIDTH
, "TIME",
418 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
421 if (access(filename
, R_OK
) == 0)
423 else if (errno
== ENOENT
)
429 else if (normal_coredump
)
434 if (STR_IN_SET(present
, "present", "journal") && truncated
&& parse_boolean(truncated
) > 0)
435 present
= "truncated";
437 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
438 FORMAT_TIMESTAMP_WIDTH
, buf
,
442 3, normal_coredump
? strna(sgnl
) : "-",
444 strna(exe
?: (comm
?: cmdline
)));
449 static int print_info(FILE *file
, sd_journal
*j
, bool need_space
) {
451 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
452 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
453 *unit
= NULL
, *user_unit
= NULL
, *session
= NULL
,
454 *boot_id
= NULL
, *machine_id
= NULL
, *hostname
= NULL
,
455 *slice
= NULL
, *cgroup
= NULL
, *owner_uid
= NULL
,
456 *message
= NULL
, *timestamp
= NULL
, *filename
= NULL
,
457 *truncated
= NULL
, *coredump
= NULL
;
460 bool normal_coredump
;
466 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
467 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
468 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
469 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
470 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
471 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
472 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
473 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
474 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
475 RETRIEVE(d
, l
, "COREDUMP_UNIT", unit
);
476 RETRIEVE(d
, l
, "COREDUMP_USER_UNIT", user_unit
);
477 RETRIEVE(d
, l
, "COREDUMP_SESSION", session
);
478 RETRIEVE(d
, l
, "COREDUMP_OWNER_UID", owner_uid
);
479 RETRIEVE(d
, l
, "COREDUMP_SLICE", slice
);
480 RETRIEVE(d
, l
, "COREDUMP_CGROUP", cgroup
);
481 RETRIEVE(d
, l
, "COREDUMP_TIMESTAMP", timestamp
);
482 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
483 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
484 RETRIEVE(d
, l
, "COREDUMP", coredump
);
485 RETRIEVE(d
, l
, "_BOOT_ID", boot_id
);
486 RETRIEVE(d
, l
, "_MACHINE_ID", machine_id
);
487 RETRIEVE(d
, l
, "_HOSTNAME", hostname
);
488 RETRIEVE(d
, l
, "MESSAGE", message
);
494 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
498 " PID: %s%s%s (%s)\n",
499 ansi_highlight(), strna(pid
), ansi_normal(), comm
);
503 ansi_highlight(), strna(pid
), ansi_normal());
508 if (parse_uid(uid
, &n
) >= 0) {
509 _cleanup_free_
char *u
= NULL
;
525 if (parse_gid(gid
, &n
) >= 0) {
526 _cleanup_free_
char *g
= NULL
;
541 const char *name
= normal_coredump
? "Signal" : "Reason";
543 if (normal_coredump
&& safe_atoi(sgnl
, &sig
) >= 0)
544 fprintf(file
, " %s: %s (%s)\n", name
, sgnl
, signal_to_string(sig
));
546 fprintf(file
, " %s: %s\n", name
, sgnl
);
552 r
= safe_atou64(timestamp
, &u
);
554 char absolute
[FORMAT_TIMESTAMP_MAX
], relative
[FORMAT_TIMESPAN_MAX
];
557 " Timestamp: %s (%s)\n",
558 format_timestamp(absolute
, sizeof(absolute
), u
),
559 format_timestamp_relative(relative
, sizeof(relative
), u
));
562 fprintf(file
, " Timestamp: %s\n", timestamp
);
566 fprintf(file
, " Command Line: %s\n", cmdline
);
568 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_normal());
570 fprintf(file
, " Control Group: %s\n", cgroup
);
572 fprintf(file
, " Unit: %s\n", unit
);
574 fprintf(file
, " User Unit: %s\n", user_unit
);
576 fprintf(file
, " Slice: %s\n", slice
);
578 fprintf(file
, " Session: %s\n", session
);
582 if (parse_uid(owner_uid
, &n
) >= 0) {
583 _cleanup_free_
char *u
= NULL
;
587 " Owner UID: %s (%s)\n",
596 fprintf(file
, " Boot ID: %s\n", boot_id
);
598 fprintf(file
, " Machine ID: %s\n", machine_id
);
600 fprintf(file
, " Hostname: %s\n", hostname
);
605 inacc
= access(filename
, R_OK
) < 0;
606 trunc
= truncated
&& parse_boolean(truncated
) > 0;
609 fprintf(file
, " Storage: %s%s (%s%s%s)%s\n",
610 ansi_highlight_red(),
612 inacc
? "inaccessible" : "",
613 inacc
&& trunc
? ", " : "",
614 trunc
? "truncated" : "",
617 fprintf(file
, " Storage: %s\n", filename
);
621 fprintf(file
, " Storage: journal\n");
623 fprintf(file
, " Storage: none\n");
626 _cleanup_free_
char *m
= NULL
;
628 m
= strreplace(message
, "\n", "\n ");
630 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
636 static int focus(sd_journal
*j
) {
639 r
= sd_journal_seek_tail(j
);
641 r
= sd_journal_previous(j
);
643 return log_error_errno(r
, "Failed to search journal: %m");
645 return log_error_errno(SYNTHETIC_ERRNO(ESRCH
),
650 static int print_entry(sd_journal
*j
, unsigned n_found
, bool verb_is_info
) {
654 return print_info(stdout
, j
, n_found
);
656 return print_field(stdout
, j
);
658 return print_list(stdout
, j
, n_found
);
661 static int dump_list(int argc
, char **argv
, void *userdata
) {
662 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
663 unsigned n_found
= 0;
667 verb_is_info
= (argc
>= 1 && streq(argv
[0], "info"));
669 r
= acquire_journal(&j
, argv
+ 1);
673 (void) pager_open(arg_pager_flags
);
675 /* The coredumps are likely to compressed, and for just
676 * listing them we don't need to decompress them, so let's
677 * pick a fairly low data threshold here */
678 sd_journal_set_data_threshold(j
, 4096);
680 /* "info" without pattern implies "-1" */
681 if (arg_one
|| (verb_is_info
&& argc
== 1)) {
686 return print_entry(j
, 0, verb_is_info
);
688 if (arg_since
!= USEC_INFINITY
&& !arg_reverse
)
689 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
690 else if (arg_until
!= USEC_INFINITY
&& arg_reverse
)
691 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
692 else if (arg_reverse
)
693 r
= sd_journal_seek_tail(j
);
695 r
= sd_journal_seek_head(j
);
697 return log_error_errno(r
, "Failed to seek to date: %m");
701 r
= sd_journal_next(j
);
703 r
= sd_journal_previous(j
);
706 return log_error_errno(r
, "Failed to iterate through journal: %m");
711 if (arg_until
!= USEC_INFINITY
&& !arg_reverse
) {
714 r
= sd_journal_get_realtime_usec(j
, &usec
);
716 return log_error_errno(r
, "Failed to determine timestamp: %m");
717 if (usec
> arg_until
)
721 if (arg_since
!= USEC_INFINITY
&& arg_reverse
) {
724 r
= sd_journal_get_realtime_usec(j
, &usec
);
726 return log_error_errno(r
, "Failed to determine timestamp: %m");
727 if (usec
< arg_since
)
731 r
= print_entry(j
, n_found
++, verb_is_info
);
736 if (!arg_field
&& n_found
<= 0) {
738 log_notice("No coredumps found.");
746 static int save_core(sd_journal
*j
, FILE *file
, char **path
, bool *unlink_temp
) {
748 _cleanup_free_
char *filename
= NULL
;
751 _cleanup_close_
int fdt
= -1;
754 assert(!(file
&& path
)); /* At most one can be specified */
755 assert(!!path
== !!unlink_temp
); /* Those must be specified together */
757 /* Look for a coredump on disk first. */
758 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
760 r
= retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
765 if (access(filename
, R_OK
) < 0)
766 return log_error_errno(errno
, "File \"%s\" is not readable: %m", filename
);
768 if (path
&& !ENDSWITH_SET(filename
, ".xz", ".lz4", ".zst")) {
769 *path
= TAKE_PTR(filename
);
776 return log_error_errno(r
, "Failed to retrieve COREDUMP_FILENAME field: %m");
777 /* Check that we can have a COREDUMP field. We still haven't set a high
778 * data threshold, so we'll get a few kilobytes at most.
781 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
783 return log_error_errno(r
, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
785 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
791 /* Create a temporary file to write the uncompressed core to. */
793 r
= var_tmp_dir(&vt
);
795 return log_error_errno(r
, "Failed to acquire temporary directory path: %m");
797 temp
= path_join(vt
, "coredump-XXXXXX");
801 fdt
= mkostemp_safe(temp
);
803 return log_error_errno(fdt
, "Failed to create temporary file: %m");
804 log_debug("Created temporary file %s", temp
);
808 /* If neither path or file are specified, we will write to stdout. Let's now check
809 * if stdout is connected to a tty. We checked that the file exists, or that the
810 * core might be stored in the journal. In this second case, if we found the entry,
811 * in all likelihood we will be able to access the COREDUMP= field. In either case,
812 * we stop before doing any "real" work, i.e. before starting decompression or
813 * reading from the file or creating temporary files.
817 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY
),
818 "Refusing to dump core to tty"
819 " (use shell redirection or specify --output).");
828 _cleanup_close_
int fdf
;
830 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
832 r
= log_error_errno(errno
, "Failed to open %s: %m", filename
);
836 r
= decompress_stream(filename
, fdf
, fd
, -1);
838 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
842 log_error("Cannot decompress file. Compiled without compression support.");
849 /* We want full data, nothing truncated. */
850 sd_journal_set_data_threshold(j
, 0);
852 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
854 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
860 sz
= write(fd
, data
, len
);
862 r
= log_error_errno(errno
, "Failed to write output: %m");
865 if (sz
!= (ssize_t
) len
) {
866 log_error("Short write to output.");
881 log_debug("Removed temporary file %s", temp
);
886 static int dump_core(int argc
, char **argv
, void *userdata
) {
887 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
888 _cleanup_fclose_
FILE *f
= NULL
;
892 log_error("Option --field/-F only makes sense with list");
896 r
= acquire_journal(&j
, argv
+ 1);
905 f
= fopen(arg_output
, "we");
907 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", arg_output
);
910 print_info(f
? stdout
: stderr
, j
, false);
912 r
= save_core(j
, f
, NULL
, NULL
);
916 r
= sd_journal_previous(j
);
917 if (r
> 0 && !arg_quiet
)
918 log_notice("More than one entry matches, ignoring rest.");
923 static int run_debug(int argc
, char **argv
, void *userdata
) {
924 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
925 _cleanup_free_
char *exe
= NULL
, *path
= NULL
, *debugger
= NULL
;
926 bool unlink_path
= false;
927 const char *data
, *fork_name
;
935 env_debugger
= getenv("SYSTEMD_DEBUGGER");
937 arg_debugger
= env_debugger
;
939 arg_debugger
= "gdb";
942 debugger
= strdup(arg_debugger
);
947 log_error("Option --field/-F only makes sense with list");
951 r
= acquire_journal(&j
, argv
+ 1);
959 print_info(stdout
, j
, false);
962 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
964 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
966 assert(len
> STRLEN("COREDUMP_EXE="));
967 data
+= STRLEN("COREDUMP_EXE=");
968 len
-= STRLEN("COREDUMP_EXE=");
970 exe
= strndup(data
, len
);
974 if (endswith(exe
, " (deleted)")) {
975 log_error("Binary already deleted.");
979 if (!path_is_absolute(exe
)) {
980 log_error("Binary is not an absolute path.");
984 r
= save_core(j
, NULL
, &path
, &unlink_path
);
988 /* Don't interfere with gdb and its handling of SIGINT. */
989 (void) ignore_signals(SIGINT
, -1);
991 fork_name
= strjoina("(", debugger
, ")");
993 r
= safe_fork(fork_name
, FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_CLOSE_ALL_FDS
|FORK_RLIMIT_NOFILE_SAFE
|FORK_LOG
, &pid
);
997 execlp(debugger
, debugger
, exe
, "-c", path
, NULL
);
999 log_error_errno(errno
, "Failed to invoke %s: %m", debugger
);
1000 _exit(EXIT_FAILURE
);
1003 r
= wait_for_terminate_and_check(debugger
, pid
, WAIT_LOG_ABNORMAL
);
1006 (void) default_signals(SIGINT
, -1);
1009 log_debug("Removed temporary file %s", path
);
1010 (void) unlink(path
);
1016 static int check_units_active(void) {
1017 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1018 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1019 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1020 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1022 const char *id
, *state
, *substate
;
1027 r
= sd_bus_default_system(&bus
);
1029 return log_error_errno(r
, "Failed to acquire bus: %m");
1031 r
= sd_bus_message_new_method_call(
1034 "org.freedesktop.systemd1",
1035 "/org/freedesktop/systemd1",
1036 "org.freedesktop.systemd1.Manager",
1037 "ListUnitsByPatterns");
1039 return bus_log_create_error(r
);
1041 r
= sd_bus_message_append_strv(m
, NULL
);
1043 return bus_log_create_error(r
);
1045 r
= sd_bus_message_append_strv(m
, STRV_MAKE("systemd-coredump@*.service"));
1047 return bus_log_create_error(r
);
1049 r
= sd_bus_call(bus
, m
, SHORT_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1051 return log_error_errno(r
, "Failed to check if any systemd-coredump@.service units are running: %s",
1052 bus_error_message(&error
, r
));
1054 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1056 return bus_log_parse_error(r
);
1058 while ((r
= sd_bus_message_read(
1059 reply
, "(ssssssouso)",
1060 &id
, NULL
, NULL
, &state
, &substate
,
1061 NULL
, NULL
, NULL
, NULL
, NULL
)) > 0) {
1062 bool found
= !STR_IN_SET(state
, "inactive", "dead", "failed");
1063 log_debug("Unit %s is %s/%s, %scounting it.", id
, state
, substate
, found
? "" : "not ");
1067 return bus_log_parse_error(r
);
1069 r
= sd_bus_message_exit_container(reply
);
1071 return bus_log_parse_error(r
);
1076 static int coredumpctl_main(int argc
, char *argv
[]) {
1078 static const Verb verbs
[] = {
1079 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, dump_list
},
1080 { "info", VERB_ANY
, VERB_ANY
, 0, dump_list
},
1081 { "dump", VERB_ANY
, VERB_ANY
, 0, dump_core
},
1082 { "debug", VERB_ANY
, VERB_ANY
, 0, run_debug
},
1083 { "gdb", VERB_ANY
, VERB_ANY
, 0, run_debug
},
1087 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1090 static int run(int argc
, char *argv
[]) {
1091 int r
, units_active
;
1093 setlocale(LC_ALL
, "");
1096 /* The journal merging logic potentially needs a lot of fds. */
1097 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE
);
1099 r
= parse_argv(argc
, argv
);
1105 units_active
= check_units_active(); /* error is treated the same as 0 */
1107 r
= coredumpctl_main(argc
, argv
);
1109 if (units_active
> 0)
1110 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1111 ansi_highlight_red(),
1112 units_active
, units_active
== 1 ? "unit is running" : "units are running",
1118 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);