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
;
75 if (strchr(match
, '='))
77 else if (strchr(match
, '/')) {
78 r
= path_make_absolute_cwd(match
, &p
);
80 return log_error_errno(r
, "path_make_absolute_cwd(\"%s\"): %m", match
);
83 prefix
= "COREDUMP_EXE=";
84 } else if (parse_pid(match
, &pid
) >= 0)
85 prefix
= "COREDUMP_PID=";
87 prefix
= "COREDUMP_COMM=";
89 pattern
= strjoina(prefix
, match
);
90 log_debug("Adding match: %s", pattern
);
91 r
= sd_journal_add_match(j
, pattern
, 0);
93 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
);
155 static int help(void) {
156 printf("%s [OPTIONS...]\n\n"
157 "List or retrieve coredumps from the journal.\n\n"
159 " -h --help Show this help\n"
160 " --version Print version string\n"
161 " --no-pager Do not pipe output into a pager\n"
162 " --no-legend Do not print the column headers.\n"
163 " -1 Show information about most recent entry only\n"
164 " -S --since=DATE Only print coredumps since the date\n"
165 " -U --until=DATE Only print coredumps until the date\n"
166 " -r --reverse Show the newest entries first\n"
167 " -F --field=FIELD List all values a certain field takes\n"
168 " -o --output=FILE Write output to FILE\n"
169 " -D --directory=DIR Use journal files from directory\n\n"
170 " -q --quiet Do not show info messages and privilege warning\n"
172 " list [MATCHES...] List available coredumps (default)\n"
173 " info [MATCHES...] Show detailed information about one or more coredumps\n"
174 " dump [MATCHES...] Print first matching coredump to stdout\n"
175 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
176 , program_invocation_short_name
);
181 static int parse_argv(int argc
, char *argv
[]) {
190 static const struct option options
[] = {
191 { "help", no_argument
, NULL
, 'h' },
192 { "version" , no_argument
, NULL
, ARG_VERSION
},
193 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
194 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
195 { "output", required_argument
, NULL
, 'o' },
196 { "field", required_argument
, NULL
, 'F' },
197 { "directory", required_argument
, NULL
, 'D' },
198 { "reverse", no_argument
, NULL
, 'r' },
199 { "since", required_argument
, NULL
, 'S' },
200 { "until", required_argument
, NULL
, 'U' },
201 { "quiet", no_argument
, NULL
, 'q' },
208 while ((c
= getopt_long(argc
, argv
, "ho:F:1D:rS:U:q", options
, NULL
)) >= 0)
221 arg_no_legend
= true;
226 log_error("cannot set output more than once");
230 arg_output
= fopen(optarg
, "we");
232 return log_error_errno(errno
, "writing to '%s': %m", optarg
);
237 r
= parse_timestamp(optarg
, &arg_since
);
239 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
243 r
= parse_timestamp(optarg
, &arg_until
);
245 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
250 log_error("cannot use --field/-F more than once");
261 arg_directory
= optarg
;
276 assert_not_reached("Unhandled option");
279 if (arg_since
!= USEC_INFINITY
&& arg_until
!= USEC_INFINITY
&&
280 arg_since
> arg_until
) {
281 log_error("--since= must be before --until=.");
288 static int retrieve(const void *data
,
296 ident
= strlen(name
) + 1; /* name + "=" */
301 if (memcmp(data
, name
, ident
- 1) != 0)
304 if (((const char*) data
)[ident
- 1] != '=')
307 v
= strndup((const char*)data
+ ident
, len
- ident
);
317 static int print_field(FILE* file
, sd_journal
*j
) {
326 /* A (user-specified) field may appear more than once for a given entry.
327 * We will print all of the occurences.
328 * This is different below for fields that systemd-coredump uses,
329 * because they cannot meaningfully appear more than once.
331 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
332 _cleanup_free_
char *value
= NULL
;
335 r
= retrieve(d
, l
, arg_field
, &value
);
339 fprintf(file
, "%s\n", value
);
345 #define RETRIEVE(d, l, name, arg) \
347 int _r = retrieve(d, l, name, &arg); \
354 static int print_list(FILE* file
, sd_journal
*j
, int had_legend
) {
356 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
357 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
358 *filename
= NULL
, *truncated
= NULL
, *coredump
= NULL
;
362 char buf
[FORMAT_TIMESTAMP_MAX
];
365 bool normal_coredump
;
370 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
371 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
372 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
373 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
374 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
375 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
376 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
377 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
378 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
379 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
380 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
381 RETRIEVE(d
, l
, "COREDUMP", coredump
);
384 if (!pid
&& !uid
&& !gid
&& !sgnl
&& !exe
&& !comm
&& !cmdline
&& !filename
) {
385 log_warning("Empty coredump log entry");
389 r
= sd_journal_get_realtime_usec(j
, &t
);
391 return log_error_errno(r
, "Failed to get realtime timestamp: %m");
393 format_timestamp(buf
, sizeof(buf
), t
);
395 if (!had_legend
&& !arg_no_legend
)
396 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
397 FORMAT_TIMESTAMP_WIDTH
, "TIME",
405 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
408 if (access(filename
, R_OK
) == 0)
410 else if (errno
== ENOENT
)
416 else if (normal_coredump
)
421 if (STR_IN_SET(present
, "present", "journal") && truncated
&& parse_boolean(truncated
) > 0)
422 present
= "truncated";
424 fprintf(file
, "%-*s %*s %*s %*s %*s %-*s %s\n",
425 FORMAT_TIMESTAMP_WIDTH
, buf
,
429 3, normal_coredump
? strna(sgnl
) : "-",
431 strna(exe
?: (comm
?: cmdline
)));
436 static int print_info(FILE *file
, sd_journal
*j
, bool need_space
) {
438 *mid
= NULL
, *pid
= NULL
, *uid
= NULL
, *gid
= NULL
,
439 *sgnl
= NULL
, *exe
= NULL
, *comm
= NULL
, *cmdline
= NULL
,
440 *unit
= NULL
, *user_unit
= NULL
, *session
= NULL
,
441 *boot_id
= NULL
, *machine_id
= NULL
, *hostname
= NULL
,
442 *slice
= NULL
, *cgroup
= NULL
, *owner_uid
= NULL
,
443 *message
= NULL
, *timestamp
= NULL
, *filename
= NULL
,
444 *truncated
= NULL
, *coredump
= NULL
;
447 bool normal_coredump
;
453 SD_JOURNAL_FOREACH_DATA(j
, d
, l
) {
454 RETRIEVE(d
, l
, "MESSAGE_ID", mid
);
455 RETRIEVE(d
, l
, "COREDUMP_PID", pid
);
456 RETRIEVE(d
, l
, "COREDUMP_UID", uid
);
457 RETRIEVE(d
, l
, "COREDUMP_GID", gid
);
458 RETRIEVE(d
, l
, "COREDUMP_SIGNAL", sgnl
);
459 RETRIEVE(d
, l
, "COREDUMP_EXE", exe
);
460 RETRIEVE(d
, l
, "COREDUMP_COMM", comm
);
461 RETRIEVE(d
, l
, "COREDUMP_CMDLINE", cmdline
);
462 RETRIEVE(d
, l
, "COREDUMP_UNIT", unit
);
463 RETRIEVE(d
, l
, "COREDUMP_USER_UNIT", user_unit
);
464 RETRIEVE(d
, l
, "COREDUMP_SESSION", session
);
465 RETRIEVE(d
, l
, "COREDUMP_OWNER_UID", owner_uid
);
466 RETRIEVE(d
, l
, "COREDUMP_SLICE", slice
);
467 RETRIEVE(d
, l
, "COREDUMP_CGROUP", cgroup
);
468 RETRIEVE(d
, l
, "COREDUMP_TIMESTAMP", timestamp
);
469 RETRIEVE(d
, l
, "COREDUMP_FILENAME", filename
);
470 RETRIEVE(d
, l
, "COREDUMP_TRUNCATED", truncated
);
471 RETRIEVE(d
, l
, "COREDUMP", coredump
);
472 RETRIEVE(d
, l
, "_BOOT_ID", boot_id
);
473 RETRIEVE(d
, l
, "_MACHINE_ID", machine_id
);
474 RETRIEVE(d
, l
, "_HOSTNAME", hostname
);
475 RETRIEVE(d
, l
, "MESSAGE", message
);
481 normal_coredump
= streq_ptr(mid
, SD_MESSAGE_COREDUMP_STR
);
485 " PID: %s%s%s (%s)\n",
486 ansi_highlight(), strna(pid
), ansi_normal(), comm
);
490 ansi_highlight(), strna(pid
), ansi_normal());
495 if (parse_uid(uid
, &n
) >= 0) {
496 _cleanup_free_
char *u
= NULL
;
512 if (parse_gid(gid
, &n
) >= 0) {
513 _cleanup_free_
char *g
= NULL
;
528 const char *name
= normal_coredump
? "Signal" : "Reason";
530 if (normal_coredump
&& safe_atoi(sgnl
, &sig
) >= 0)
531 fprintf(file
, " %s: %s (%s)\n", name
, sgnl
, signal_to_string(sig
));
533 fprintf(file
, " %s: %s\n", name
, sgnl
);
539 r
= safe_atou64(timestamp
, &u
);
541 char absolute
[FORMAT_TIMESTAMP_MAX
], relative
[FORMAT_TIMESPAN_MAX
];
544 " Timestamp: %s (%s)\n",
545 format_timestamp(absolute
, sizeof(absolute
), u
),
546 format_timestamp_relative(relative
, sizeof(relative
), u
));
549 fprintf(file
, " Timestamp: %s\n", timestamp
);
553 fprintf(file
, " Command Line: %s\n", cmdline
);
555 fprintf(file
, " Executable: %s%s%s\n", ansi_highlight(), exe
, ansi_normal());
557 fprintf(file
, " Control Group: %s\n", cgroup
);
559 fprintf(file
, " Unit: %s\n", unit
);
561 fprintf(file
, " User Unit: %s\n", user_unit
);
563 fprintf(file
, " Slice: %s\n", slice
);
565 fprintf(file
, " Session: %s\n", session
);
569 if (parse_uid(owner_uid
, &n
) >= 0) {
570 _cleanup_free_
char *u
= NULL
;
574 " Owner UID: %s (%s)\n",
583 fprintf(file
, " Boot ID: %s\n", boot_id
);
585 fprintf(file
, " Machine ID: %s\n", machine_id
);
587 fprintf(file
, " Hostname: %s\n", hostname
);
592 inacc
= access(filename
, R_OK
) < 0;
593 trunc
= truncated
&& parse_boolean(truncated
) > 0;
596 fprintf(file
, " Storage: %s%s (%s%s%s)%s\n",
597 ansi_highlight_red(),
599 inacc
? "inaccessible" : "",
600 inacc
&& trunc
? ", " : "",
601 trunc
? "truncated" : "",
604 fprintf(file
, " Storage: %s\n", filename
);
608 fprintf(file
, " Storage: journal\n");
610 fprintf(file
, " Storage: none\n");
613 _cleanup_free_
char *m
= NULL
;
615 m
= strreplace(message
, "\n", "\n ");
617 fprintf(file
, " Message: %s\n", strstrip(m
?: message
));
623 static int focus(sd_journal
*j
) {
626 r
= sd_journal_seek_tail(j
);
628 r
= sd_journal_previous(j
);
630 return log_error_errno(r
, "Failed to search journal: %m");
632 log_error("No match found.");
638 static int print_entry(sd_journal
*j
, unsigned n_found
, bool verb_is_info
) {
642 return print_info(stdout
, j
, n_found
);
644 return print_field(stdout
, j
);
646 return print_list(stdout
, j
, n_found
);
649 static int dump_list(int argc
, char **argv
, void *userdata
) {
650 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
651 unsigned n_found
= 0;
655 verb_is_info
= (argc
>= 1 && streq(argv
[0], "info"));
657 r
= acquire_journal(&j
, argv
+ 1);
661 (void) pager_open(arg_no_pager
, false);
663 /* The coredumps are likely to compressed, and for just
664 * listing them we don't need to decompress them, so let's
665 * pick a fairly low data threshold here */
666 sd_journal_set_data_threshold(j
, 4096);
673 return print_entry(j
, 0, verb_is_info
);
675 if (arg_since
!= USEC_INFINITY
&& !arg_reverse
)
676 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
677 else if (arg_until
!= USEC_INFINITY
&& arg_reverse
)
678 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
679 else if (arg_reverse
)
680 r
= sd_journal_seek_tail(j
);
682 r
= sd_journal_seek_head(j
);
684 return log_error_errno(r
, "Failed to seek to date: %m");
688 r
= sd_journal_next(j
);
690 r
= sd_journal_previous(j
);
693 return log_error_errno(r
, "Failed to iterate through journal: %m");
698 if (arg_until
!= USEC_INFINITY
&& !arg_reverse
) {
701 r
= sd_journal_get_realtime_usec(j
, &usec
);
703 return log_error_errno(r
, "Failed to determine timestamp: %m");
704 if (usec
> arg_until
)
708 if (arg_since
!= USEC_INFINITY
&& arg_reverse
) {
711 r
= sd_journal_get_realtime_usec(j
, &usec
);
713 return log_error_errno(r
, "Failed to determine timestamp: %m");
714 if (usec
< arg_since
)
718 r
= print_entry(j
, n_found
++, verb_is_info
);
723 if (!arg_field
&& n_found
<= 0) {
725 log_notice("No coredumps found.");
733 static int save_core(sd_journal
*j
, FILE *file
, char **path
, bool *unlink_temp
) {
735 _cleanup_free_
char *filename
= NULL
;
738 _cleanup_close_
int fdt
= -1;
741 assert(!(file
&& path
)); /* At most one can be specified */
742 assert(!!path
== !!unlink_temp
); /* Those must be specified together */
744 /* Look for a coredump on disk first. */
745 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
747 retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
750 return log_error_errno(r
, "Failed to retrieve COREDUMP_FILENAME field: %m");
751 /* Check that we can have a COREDUMP field. We still haven't set a high
752 * data threshold, so we'll get a few kilobytes at most.
755 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
757 return log_error_errno(r
, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
759 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
763 if (access(filename
, R_OK
) < 0)
764 return log_error_errno(errno
, "File \"%s\" is not readable: %m", filename
);
766 if (path
&& !endswith(filename
, ".xz") && !endswith(filename
, ".lz4")) {
777 /* Create a temporary file to write the uncompressed core to. */
779 r
= var_tmp_dir(&vt
);
781 return log_error_errno(r
, "Failed to acquire temporary directory path: %m");
783 temp
= strjoin(vt
, "/coredump-XXXXXX");
787 fdt
= mkostemp_safe(temp
);
789 return log_error_errno(fdt
, "Failed to create temporary file: %m");
790 log_debug("Created temporary file %s", temp
);
794 /* If neither path or file are specified, we will write to stdout. Let's now check
795 * if stdout is connected to a tty. We checked that the file exists, or that the
796 * core might be stored in the journal. In this second case, if we found the entry,
797 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
798 * we stop before doing any "real" work, i.e. before starting decompression or
799 * reading from the file or creating temporary files.
803 return log_error_errno(ENOTTY
, "Refusing to dump core to tty"
804 " (use shell redirection or specify --output).");
812 #if HAVE_XZ || HAVE_LZ4
813 _cleanup_close_
int fdf
;
815 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
817 r
= log_error_errno(errno
, "Failed to open %s: %m", filename
);
821 r
= decompress_stream(filename
, fdf
, fd
, -1);
823 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
827 log_error("Cannot decompress file. Compiled without compression support.");
834 /* We want full data, nothing truncated. */
835 sd_journal_set_data_threshold(j
, 0);
837 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
839 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
845 sz
= write(fd
, data
, len
);
847 r
= log_error_errno(errno
, "Failed to write output: %m");
850 if (sz
!= (ssize_t
) len
) {
851 log_error("Short write to output.");
866 log_debug("Removed temporary file %s", temp
);
871 static int dump_core(int argc
, char **argv
, void *userdata
) {
872 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
876 log_error("Option --field/-F only makes sense with list");
880 r
= acquire_journal(&j
, argv
+ 1);
888 print_info(arg_output
? stdout
: stderr
, j
, false);
890 r
= save_core(j
, arg_output
, NULL
, NULL
);
894 r
= sd_journal_previous(j
);
895 if (r
> 0 && !arg_quiet
)
896 log_notice("More than one entry matches, ignoring rest.");
901 static int run_gdb(int argc
, char **argv
, void *userdata
) {
902 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
903 _cleanup_free_
char *exe
= NULL
, *path
= NULL
;
904 bool unlink_path
= false;
911 log_error("Option --field/-F only makes sense with list");
915 r
= acquire_journal(&j
, argv
+ 1);
923 print_info(stdout
, j
, false);
926 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
928 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
930 assert(len
> STRLEN("COREDUMP_EXE="));
931 data
+= STRLEN("COREDUMP_EXE=");
932 len
-= STRLEN("COREDUMP_EXE=");
934 exe
= strndup(data
, len
);
938 if (endswith(exe
, " (deleted)")) {
939 log_error("Binary already deleted.");
943 if (!path_is_absolute(exe
)) {
944 log_error("Binary is not an absolute path.");
948 r
= save_core(j
, NULL
, &path
, &unlink_path
);
952 /* Don't interfere with gdb and its handling of SIGINT. */
953 (void) ignore_signals(SIGINT
, -1);
955 r
= safe_fork("(gdb)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
959 execlp("gdb", "gdb", exe
, path
, NULL
);
961 log_error_errno(errno
, "Failed to invoke gdb: %m");
965 r
= wait_for_terminate_and_check("gdb", pid
, WAIT_LOG_ABNORMAL
);
968 (void) default_signals(SIGINT
, -1);
971 log_debug("Removed temporary file %s", path
);
978 static int check_units_active(void) {
979 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
980 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
981 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
982 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
984 const char *id
, *state
, *substate
;
989 r
= sd_bus_default_system(&bus
);
991 return log_error_errno(r
, "Failed to acquire bus: %m");
993 r
= sd_bus_message_new_method_call(
996 "org.freedesktop.systemd1",
997 "/org/freedesktop/systemd1",
998 "org.freedesktop.systemd1.Manager",
999 "ListUnitsByPatterns");
1001 return bus_log_create_error(r
);
1003 r
= sd_bus_message_append_strv(m
, NULL
);
1005 return bus_log_create_error(r
);
1007 r
= sd_bus_message_append_strv(m
, STRV_MAKE("systemd-coredump@*.service"));
1009 return bus_log_create_error(r
);
1011 r
= sd_bus_call(bus
, m
, SHORT_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1013 return log_error_errno(r
, "Failed to check if any systemd-coredump@.service units are running: %s",
1014 bus_error_message(&error
, r
));
1016 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1018 return bus_log_parse_error(r
);
1020 while ((r
= sd_bus_message_read(
1021 reply
, "(ssssssouso)",
1022 &id
, NULL
, NULL
, &state
, &substate
,
1023 NULL
, NULL
, NULL
, NULL
, NULL
)) > 0) {
1024 bool found
= !STR_IN_SET(state
, "inactive", "dead", "failed");
1025 log_debug("Unit %s is %s/%s, %scounting it.", id
, state
, substate
, found
? "" : "not ");
1029 return bus_log_parse_error(r
);
1031 r
= sd_bus_message_exit_container(reply
);
1033 return bus_log_parse_error(r
);
1038 static int coredumpctl_main(int argc
, char *argv
[]) {
1040 static const Verb verbs
[] = {
1041 { "list", VERB_ANY
, VERB_ANY
, VERB_DEFAULT
, dump_list
},
1042 { "info", VERB_ANY
, VERB_ANY
, 0, dump_list
},
1043 { "dump", VERB_ANY
, VERB_ANY
, 0, dump_core
},
1044 { "gdb", VERB_ANY
, VERB_ANY
, 0, run_gdb
},
1048 return dispatch_verb(argc
, argv
, verbs
, NULL
);
1051 int main(int argc
, char *argv
[]) {
1052 int r
, units_active
;
1054 setlocale(LC_ALL
, "");
1055 log_parse_environment();
1058 r
= parse_argv(argc
, argv
);
1064 units_active
= check_units_active(); /* error is treated the same as 0 */
1066 r
= coredumpctl_main(argc
, argv
);
1068 if (units_active
> 0)
1069 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1070 ansi_highlight_red(),
1071 units_active
, units_active
== 1 ? "unit is running" : "units are running",
1079 return r
>= 0 ? r
: EXIT_FAILURE
;