1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "sd-journal.h"
30 #include "sd-messages.h"
32 #include "alloc-util.h"
33 #include "bus-error.h"
39 #include "journal-internal.h"
40 #include "journal-util.h"
44 #include "parse-util.h"
45 #include "path-util.h"
46 #include "process-util.h"
48 #include "signal-util.h"
49 #include "string-util.h"
51 #include "terminal-util.h"
52 #include "user-util.h"
56 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
58 static usec_t arg_since
= USEC_INFINITY
, arg_until
= USEC_INFINITY
;
59 static const char* arg_field
= NULL
;
60 static const char *arg_directory
= NULL
;
61 static bool arg_no_pager
= false;
62 static int arg_no_legend
= false;
63 static int arg_one
= false;
64 static FILE* arg_output
= NULL
;
65 static bool arg_reverse
= false;
66 static bool arg_quiet
= false;
68 static int add_match(sd_journal
*j
, const char *match
) {
69 _cleanup_free_
char *p
= NULL
;
70 const char* prefix
, *pattern
;
74 if (strchr(match
, '='))
76 else if (strchr(match
, '/')) {
77 r
= path_make_absolute_cwd(match
, &p
);
79 return log_error_errno(r
, "path_make_absolute_cwd(\"%s\"): %m", match
);
82 prefix
= "COREDUMP_EXE=";
83 } else if (parse_pid(match
, &pid
) >= 0)
84 prefix
= "COREDUMP_PID=";
86 prefix
= "COREDUMP_COMM=";
88 pattern
= strjoina(prefix
, match
);
89 log_debug("Adding match: %s", pattern
);
90 r
= sd_journal_add_match(j
, pattern
, 0);
92 return log_error_errno(r
, "Failed to add match \"%s\": %m", match
);
97 static int add_matches(sd_journal
*j
, char **matches
) {
101 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
, 0);
103 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
);
105 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
, 0);
107 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
);
109 STRV_FOREACH(match
, matches
) {
110 r
= add_match(j
, *match
);
118 static int acquire_journal(sd_journal
**ret
, char **matches
) {
119 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
125 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
127 return log_error_errno(r
, "Failed to open journals in directory: %s: %m", arg_directory
);
129 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
131 return log_error_errno(r
, "Failed to open journal: %m");
134 r
= journal_access_check_and_warn(j
, arg_quiet
, true);
138 r
= add_matches(j
, matches
);
143 _cleanup_free_
char *filter
;
145 filter
= journal_make_match_string(j
);
146 log_debug("Journal filter: %s", filter
);
154 static int help(void) {
155 printf("%s [OPTIONS...]\n\n"
156 "List or retrieve coredumps from the journal.\n\n"
158 " -h --help Show this help\n"
159 " --version Print version string\n"
160 " --no-pager Do not pipe output into a pager\n"
161 " --no-legend Do not print the column headers.\n"
162 " -1 Show information about most recent entry only\n"
163 " -S --since=DATE Only print coredumps since the date\n"
164 " -U --until=DATE Only print coredumps until the date\n"
165 " -r --reverse Show the newest entries first\n"
166 " -F --field=FIELD List all values a certain field takes\n"
167 " -o --output=FILE Write output to FILE\n"
168 " -D --directory=DIR Use journal files from directory\n\n"
169 " -q --quiet Do not show info messages and privilege warning\n"
171 " list [MATCHES...] List available coredumps (default)\n"
172 " info [MATCHES...] Show detailed information about one or more coredumps\n"
173 " dump [MATCHES...] Print first matching coredump to stdout\n"
174 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
175 , program_invocation_short_name
);
180 static int parse_argv(int argc
, char *argv
[]) {
189 static const struct option options
[] = {
190 { "help", no_argument
, NULL
, 'h' },
191 { "version" , no_argument
, NULL
, ARG_VERSION
},
192 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
193 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
194 { "output", required_argument
, NULL
, 'o' },
195 { "field", required_argument
, NULL
, 'F' },
196 { "directory", required_argument
, NULL
, 'D' },
197 { "reverse", no_argument
, NULL
, 'r' },
198 { "since", required_argument
, NULL
, 'S' },
199 { "until", required_argument
, NULL
, 'U' },
200 { "quiet", no_argument
, NULL
, 'q' },
207 while ((c
= getopt_long(argc
, argv
, "ho:F:1D:rS:U:q", options
, NULL
)) >= 0)
220 arg_no_legend
= true;
225 log_error("cannot set output more than once");
229 arg_output
= fopen(optarg
, "we");
231 return log_error_errno(errno
, "writing to '%s': %m", optarg
);
236 r
= parse_timestamp(optarg
, &arg_since
);
238 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
242 r
= parse_timestamp(optarg
, &arg_until
);
244 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
249 log_error("cannot use --field/-F more than once");
260 arg_directory
= optarg
;
275 assert_not_reached("Unhandled option");
278 if (arg_since
!= USEC_INFINITY
&& arg_until
!= USEC_INFINITY
&&
279 arg_since
> arg_until
) {
280 log_error("--since= must be before --until=.");
287 static int retrieve(const void *data
,
295 ident
= strlen(name
) + 1; /* name + "=" */
300 if (memcmp(data
, name
, ident
- 1) != 0)
303 if (((const char*) data
)[ident
- 1] != '=')
306 v
= strndup((const char*)data
+ ident
, len
- ident
);
316 static int print_field(FILE* file
, sd_journal
*j
) {
325 /* A (user-specified) field may appear more than once for a given entry.
326 * We will print all of the occurences.
327 * This is different below for fields that systemd-coredump uses,
328 * because they cannot meaningfully appear more than once.
330 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
331 _cleanup_free_
char *value
= NULL
;
334 r
= retrieve(d
, l
, arg_field
, &value
);
338 fprintf(file
, "%s\n", value
);
344 #define RETRIEVE(d, l, name, arg) \
346 int _r = retrieve(d, l, name, &arg); \
353 static int print_list(FILE* file
, sd_journal
*j
, int had_legend
) {
355 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
356 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
357 *filename
= NULL
, *truncated
= NULL
, *coredump
= NULL
;
361 char buf
[FORMAT_TIMESTAMP_MAX
];
364 bool normal_coredump
;
369 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
370 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
371 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
372 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
373 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
374 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
375 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
376 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
377 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
378 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
379 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
380 RETRIEVE(d
, l
, "COREDUMP", coredump
);
383 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
&& !filename
) {
384 log_warning("Empty coredump log entry");
388 r
= sd_journal_get_realtime_usec(j
, &t
);
390 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
392 format_timestamp(buf
, sizeof(buf
), t
);
394 if (!had_legend
&& !arg_no_legend
)
395 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
396 FORMAT_TIMESTAMP_WIDTH
, "TIME",
404 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
407 if (access(filename
, R_OK
) == 0)
409 else if (errno
== ENOENT
)
415 else if (normal_coredump
)
420 if (STR_IN_SET(present
, "present", "journal") && truncated
&& parse_boolean(truncated
) > 0)
421 present
= "truncated";
423 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
424 FORMAT_TIMESTAMP_WIDTH
, buf
,
428 3, normal_coredump
? strna(sgnl
) : "-",
430 strna(exe
?: (comm
?: cmdline
)));
435 static int print_info(FILE *file
, sd_journal
*j
, bool need_space
) {
437 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
438 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
439 *unit
= NULL
, *user_unit
= NULL
, *session
= NULL
,
440 *boot_id
= NULL
, *machine_id
= NULL
, *hostname
= NULL
,
441 *slice
= NULL
, *cgroup
= NULL
, *owner_uid
= NULL
,
442 *message
= NULL
, *timestamp
= NULL
, *filename
= NULL
,
443 *truncated
= NULL
, *coredump
= NULL
;
446 bool normal_coredump
;
452 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
453 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
454 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
455 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
456 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
457 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
458 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
459 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
460 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
461 RETRIEVE(d
, l
, "COREDUMP_UNIT", unit
);
462 RETRIEVE(d
, l
, "COREDUMP_USER_UNIT", user_unit
);
463 RETRIEVE(d
, l
, "COREDUMP_SESSION", session
);
464 RETRIEVE(d
, l
, "COREDUMP_OWNER_UID", owner_uid
);
465 RETRIEVE(d
, l
, "COREDUMP_SLICE", slice
);
466 RETRIEVE(d
, l
, "COREDUMP_CGROUP", cgroup
);
467 RETRIEVE(d
, l
, "COREDUMP_TIMESTAMP", timestamp
);
468 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
469 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
470 RETRIEVE(d
, l
, "COREDUMP", coredump
);
471 RETRIEVE(d
, l
, "_BOOT_ID", boot_id
);
472 RETRIEVE(d
, l
, "_MACHINE_ID", machine_id
);
473 RETRIEVE(d
, l
, "_HOSTNAME", hostname
);
474 RETRIEVE(d
, l
, "MESSAGE", message
);
480 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
484 " PID: %s%s%s (%s)\n",
485 ansi_highlight(), strna(pid
), ansi_normal(), comm
);
489 ansi_highlight(), strna(pid
), ansi_normal());
494 if (parse_uid(uid
, &n
) >= 0) {
495 _cleanup_free_
char *u
= NULL
;
511 if (parse_gid(gid
, &n
) >= 0) {
512 _cleanup_free_
char *g
= NULL
;
527 const char *name
= normal_coredump
? "Signal" : "Reason";
529 if (normal_coredump
&& safe_atoi(sgnl
, &sig
) >= 0)
530 fprintf(file
, " %s: %s (%s)\n", name
, sgnl
, signal_to_string(sig
));
532 fprintf(file
, " %s: %s\n", name
, sgnl
);
538 r
= safe_atou64(timestamp
, &u
);
540 char absolute
[FORMAT_TIMESTAMP_MAX
], relative
[FORMAT_TIMESPAN_MAX
];
543 " Timestamp: %s (%s)\n",
544 format_timestamp(absolute
, sizeof(absolute
), u
),
545 format_timestamp_relative(relative
, sizeof(relative
), u
));
548 fprintf(file
, " Timestamp: %s\n", timestamp
);
552 fprintf(file
, " Command Line: %s\n", cmdline
);
554 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_normal());
556 fprintf(file
, " Control Group: %s\n", cgroup
);
558 fprintf(file
, " Unit: %s\n", unit
);
560 fprintf(file
, " User Unit: %s\n", user_unit
);
562 fprintf(file
, " Slice: %s\n", slice
);
564 fprintf(file
, " Session: %s\n", session
);
568 if (parse_uid(owner_uid
, &n
) >= 0) {
569 _cleanup_free_
char *u
= NULL
;
573 " Owner UID: %s (%s)\n",
582 fprintf(file
, " Boot ID: %s\n", boot_id
);
584 fprintf(file
, " Machine ID: %s\n", machine_id
);
586 fprintf(file
, " Hostname: %s\n", hostname
);
591 inacc
= access(filename
, R_OK
) < 0;
592 trunc
= truncated
&& parse_boolean(truncated
) > 0;
595 fprintf(file
, " Storage: %s%s (%s%s%s)%s\n",
596 ansi_highlight_red(),
598 inacc
? "inaccessible" : "",
599 inacc
&& trunc
? ", " : "",
600 trunc
? "truncated" : "",
603 fprintf(file
, " Storage: %s\n", filename
);
607 fprintf(file
, " Storage: journal\n");
609 fprintf(file
, " Storage: none\n");
612 _cleanup_free_
char *m
= NULL
;
614 m
= strreplace(message
, "\n", "\n ");
616 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
622 static int focus(sd_journal
*j
) {
625 r
= sd_journal_seek_tail(j
);
627 r
= sd_journal_previous(j
);
629 return log_error_errno(r
, "Failed to search journal: %m");
631 log_error("No match found.");
637 static int print_entry(sd_journal
*j
, unsigned n_found
, bool verb_is_info
) {
641 return print_info(stdout
, j
, n_found
);
643 return print_field(stdout
, j
);
645 return print_list(stdout
, j
, n_found
);
648 static int dump_list(int argc
, char **argv
, void *userdata
) {
649 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
650 unsigned n_found
= 0;
654 verb_is_info
= (argc
>= 1 && streq(argv
[0], "info"));
656 r
= acquire_journal(&j
, argv
+ 1);
660 (void) pager_open(arg_no_pager
, false);
662 /* The coredumps are likely to compressed, and for just
663 * listing them we don't need to decompress them, so let's
664 * pick a fairly low data threshold here */
665 sd_journal_set_data_threshold(j
, 4096);
672 return print_entry(j
, 0, verb_is_info
);
674 if (arg_since
!= USEC_INFINITY
&& !arg_reverse
)
675 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
676 else if (arg_until
!= USEC_INFINITY
&& arg_reverse
)
677 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
678 else if (arg_reverse
)
679 r
= sd_journal_seek_tail(j
);
681 r
= sd_journal_seek_head(j
);
683 return log_error_errno(r
, "Failed to seek to date: %m");
687 r
= sd_journal_next(j
);
689 r
= sd_journal_previous(j
);
692 return log_error_errno(r
, "Failed to iterate through journal: %m");
697 if (arg_until
!= USEC_INFINITY
&& !arg_reverse
) {
700 r
= sd_journal_get_realtime_usec(j
, &usec
);
702 return log_error_errno(r
, "Failed to determine timestamp: %m");
703 if (usec
> arg_until
)
707 if (arg_since
!= USEC_INFINITY
&& arg_reverse
) {
710 r
= sd_journal_get_realtime_usec(j
, &usec
);
712 return log_error_errno(r
, "Failed to determine timestamp: %m");
713 if (usec
< arg_since
)
717 r
= print_entry(j
, n_found
++, verb_is_info
);
722 if (!arg_field
&& n_found
<= 0) {
724 log_notice("No coredumps found.");
732 static int save_core(sd_journal
*j
, FILE *file
, char **path
, bool *unlink_temp
) {
734 _cleanup_free_
char *filename
= NULL
;
737 _cleanup_close_
int fdt
= -1;
740 assert(!(file
&& path
)); /* At most one can be specified */
741 assert(!!path
== !!unlink_temp
); /* Those must be specified together */
743 /* Look for a coredump on disk first. */
744 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
746 retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
749 return log_error_errno(r
, "Failed to retrieve COREDUMP_FILENAME field: %m");
750 /* Check that we can have a COREDUMP field. We still haven't set a high
751 * data threshold, so we'll get a few kilobytes at most.
754 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
756 return log_error_errno(r
, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
758 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
762 if (access(filename
, R_OK
) < 0)
763 return log_error_errno(errno
, "File \"%s\" is not readable: %m", filename
);
765 if (path
&& !endswith(filename
, ".xz") && !endswith(filename
, ".lz4")) {
766 *path
= TAKE_PTR(filename
);
775 /* Create a temporary file to write the uncompressed core to. */
777 r
= var_tmp_dir(&vt
);
779 return log_error_errno(r
, "Failed to acquire temporary directory path: %m");
781 temp
= strjoin(vt
, "/coredump-XXXXXX");
785 fdt
= mkostemp_safe(temp
);
787 return log_error_errno(fdt
, "Failed to create temporary file: %m");
788 log_debug("Created temporary file %s", temp
);
792 /* If neither path or file are specified, we will write to stdout. Let's now check
793 * if stdout is connected to a tty. We checked that the file exists, or that the
794 * core might be stored in the journal. In this second case, if we found the entry,
795 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
796 * we stop before doing any "real" work, i.e. before starting decompression or
797 * reading from the file or creating temporary files.
801 return log_error_errno(ENOTTY
, "Refusing to dump core to tty"
802 " (use shell redirection or specify --output).");
810 #if HAVE_XZ || HAVE_LZ4
811 _cleanup_close_
int fdf
;
813 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
815 r
= log_error_errno(errno
, "Failed to open %s: %m", filename
);
819 r
= decompress_stream(filename
, fdf
, fd
, -1);
821 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
825 log_error("Cannot decompress file. Compiled without compression support.");
832 /* We want full data, nothing truncated. */
833 sd_journal_set_data_threshold(j
, 0);
835 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
837 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
843 sz
= write(fd
, data
, len
);
845 r
= log_error_errno(errno
, "Failed to write output: %m");
848 if (sz
!= (ssize_t
) len
) {
849 log_error("Short write to output.");
864 log_debug("Removed temporary file %s", temp
);
869 static int dump_core(int argc
, char **argv
, void *userdata
) {
870 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
874 log_error("Option --field/-F only makes sense with list");
878 r
= acquire_journal(&j
, argv
+ 1);
886 print_info(arg_output
? stdout
: stderr
, j
, false);
888 r
= save_core(j
, arg_output
, NULL
, NULL
);
892 r
= sd_journal_previous(j
);
893 if (r
> 0 && !arg_quiet
)
894 log_notice("More than one entry matches, ignoring rest.");
899 static int run_gdb(int argc
, char **argv
, void *userdata
) {
900 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
901 _cleanup_free_
char *exe
= NULL
, *path
= NULL
;
902 bool unlink_path
= false;
909 log_error("Option --field/-F only makes sense with list");
913 r
= acquire_journal(&j
, argv
+ 1);
921 print_info(stdout
, j
, false);
924 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
926 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
928 assert(len
> STRLEN("COREDUMP_EXE="));
929 data
+= STRLEN("COREDUMP_EXE=");
930 len
-= STRLEN("COREDUMP_EXE=");
932 exe
= strndup(data
, len
);
936 if (endswith(exe
, " (deleted)")) {
937 log_error("Binary already deleted.");
941 if (!path_is_absolute(exe
)) {
942 log_error("Binary is not an absolute path.");
946 r
= save_core(j
, NULL
, &path
, &unlink_path
);
950 /* Don't interfere with gdb and its handling of SIGINT. */
951 (void) ignore_signals(SIGINT
, -1);
953 r
= safe_fork("(gdb)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
957 execlp("gdb", "gdb", exe
, path
, NULL
);
959 log_error_errno(errno
, "Failed to invoke gdb: %m");
963 r
= wait_for_terminate_and_check("gdb", pid
, WAIT_LOG_ABNORMAL
);
966 (void) default_signals(SIGINT
, -1);
969 log_debug("Removed temporary file %s", path
);
976 static int check_units_active(void) {
977 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
978 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
979 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
980 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
982 const char *id
, *state
, *substate
;
987 r
= sd_bus_default_system(&bus
);
989 return log_error_errno(r
, "Failed to acquire bus: %m");
991 r
= sd_bus_message_new_method_call(
994 "org.freedesktop.systemd1",
995 "/org/freedesktop/systemd1",
996 "org.freedesktop.systemd1.Manager",
997 "ListUnitsByPatterns");
999 return bus_log_create_error(r
);
1001 r
= sd_bus_message_append_strv(m
, NULL
);
1003 return bus_log_create_error(r
);
1005 r
= sd_bus_message_append_strv(m
, STRV_MAKE("systemd-coredump@*.service"));
1007 return bus_log_create_error(r
);
1009 r
= sd_bus_call(bus
, m
, SHORT_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1011 return log_error_errno(r
, "Failed to check if any systemd-coredump@.service units are running: %s",
1012 bus_error_message(&error
, r
));
1014 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1016 return bus_log_parse_error(r
);
1018 while ((r
= sd_bus_message_read(
1019 reply
, "(ssssssouso)",
1020 &id
, NULL
, NULL
, &state
, &substate
,
1021 NULL
, NULL
, NULL
, NULL
, NULL
)) > 0) {
1022 bool found
= !STR_IN_SET(state
, "inactive", "dead", "failed");
1023 log_debug("Unit %s is %s/%s, %scounting it.", id
, state
, substate
, found
? "" : "not ");
1027 return bus_log_parse_error(r
);
1029 r
= sd_bus_message_exit_container(reply
);
1031 return bus_log_parse_error(r
);
1036 static int coredumpctl_main(int argc
, char *argv
[]) {
1038 static const Verb verbs
[] = {
1039 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, dump_list
},
1040 { "info", VERB_ANY
, VERB_ANY
, 0, dump_list
},
1041 { "dump", VERB_ANY
, VERB_ANY
, 0, dump_core
},
1042 { "gdb", VERB_ANY
, VERB_ANY
, 0, run_gdb
},
1046 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1049 int main(int argc
, char *argv
[]) {
1050 int r
, units_active
;
1052 setlocale(LC_ALL
, "");
1053 log_parse_environment();
1056 r
= parse_argv(argc
, argv
);
1062 units_active
= check_units_active(); /* error is treated the same as 0 */
1064 r
= coredumpctl_main(argc
, argv
);
1066 if (units_active
> 0)
1067 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1068 ansi_highlight_red(),
1069 units_active
, units_active
== 1 ? "unit is running" : "units are running",
1074 safe_fclose(arg_output
);
1076 return r
>= 0 ? r
: EXIT_FAILURE
;