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"
55 #define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
57 static usec_t arg_since
= USEC_INFINITY
, arg_until
= USEC_INFINITY
;
65 } arg_action
= ACTION_LIST
;
66 static const char* arg_field
= NULL
;
67 static const char *arg_directory
= NULL
;
68 static bool arg_no_pager
= false;
69 static int arg_no_legend
= false;
70 static int arg_one
= false;
71 static FILE* arg_output
= NULL
;
72 static bool arg_reverse
= false;
73 static char** arg_matches
= NULL
;
74 static bool arg_quiet
= false;
76 static int add_match(sd_journal
*j
, const char *match
) {
77 _cleanup_free_
char *p
= NULL
;
83 if (strchr(match
, '='))
85 else if (strchr(match
, '/')) {
86 r
= path_make_absolute_cwd(match
, &p
);
88 return log_error_errno(r
, "path_make_absolute_cwd(\"%s\"): %m", match
);
91 prefix
= "COREDUMP_EXE=";
92 } else if (parse_pid(match
, &pid
) >= 0)
93 prefix
= "COREDUMP_PID=";
95 prefix
= "COREDUMP_COMM=";
97 pattern
= strjoin(prefix
, match
);
101 log_debug("Adding match: %s", pattern
);
102 r
= sd_journal_add_match(j
, pattern
, 0);
104 return log_error_errno(r
, "Failed to add match \"%s\": %m", match
);
108 static int add_matches(sd_journal
*j
) {
112 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
, 0);
114 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR
);
116 r
= sd_journal_add_match(j
, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
, 0);
118 return log_error_errno(r
, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR
);
120 STRV_FOREACH(match
, arg_matches
) {
121 r
= add_match(j
, *match
);
129 static void help(void) {
130 printf("%s [OPTIONS...]\n\n"
131 "List or retrieve coredumps from the journal.\n\n"
133 " -h --help Show this help\n"
134 " --version Print version string\n"
135 " --no-pager Do not pipe output into a pager\n"
136 " --no-legend Do not print the column headers.\n"
137 " -1 Show information about most recent entry only\n"
138 " -S --since=DATE Only print coredumps since the date\n"
139 " -U --until=DATE Only print coredumps until the date\n"
140 " -r --reverse Show the newest entries first\n"
141 " -F --field=FIELD List all values a certain field takes\n"
142 " -o --output=FILE Write output to FILE\n"
143 " -D --directory=DIR Use journal files from directory\n\n"
144 " -q --quiet Do not show info messages and privilege warning\n"
146 " list [MATCHES...] List available coredumps (default)\n"
147 " info [MATCHES...] Show detailed information about one or more coredumps\n"
148 " dump [MATCHES...] Print first matching coredump to stdout\n"
149 " gdb [MATCHES...] Start gdb for the first matching coredump\n"
150 , program_invocation_short_name
);
153 static int parse_argv(int argc
, char *argv
[]) {
162 static const struct option options
[] = {
163 { "help", no_argument
, NULL
, 'h' },
164 { "version" , no_argument
, NULL
, ARG_VERSION
},
165 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
166 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
167 { "output", required_argument
, NULL
, 'o' },
168 { "field", required_argument
, NULL
, 'F' },
169 { "directory", required_argument
, NULL
, 'D' },
170 { "reverse", no_argument
, NULL
, 'r' },
171 { "since", required_argument
, NULL
, 'S' },
172 { "until", required_argument
, NULL
, 'U' },
173 { "quiet", no_argument
, NULL
, 'q' },
180 while ((c
= getopt_long(argc
, argv
, "ho:F:1D:rS:U:q", options
, NULL
)) >= 0)
183 arg_action
= ACTION_NONE
;
188 arg_action
= ACTION_NONE
;
196 arg_no_legend
= true;
201 log_error("cannot set output more than once");
205 arg_output
= fopen(optarg
, "we");
207 return log_error_errno(errno
, "writing to '%s': %m", optarg
);
212 r
= parse_timestamp(optarg
, &arg_since
);
214 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
218 r
= parse_timestamp(optarg
, &arg_until
);
220 return log_error_errno(r
, "Failed to parse timestamp: %s", optarg
);
225 log_error("cannot use --field/-F more than once");
236 arg_directory
= optarg
;
251 assert_not_reached("Unhandled option");
254 if (arg_since
!= USEC_INFINITY
&& arg_until
!= USEC_INFINITY
&&
255 arg_since
> arg_until
) {
256 log_error("--since= must be before --until=.");
261 const char *cmd
= argv
[optind
++];
262 if (streq(cmd
, "list"))
263 arg_action
= ACTION_LIST
;
264 else if (streq(cmd
, "dump"))
265 arg_action
= ACTION_DUMP
;
266 else if (streq(cmd
, "gdb"))
267 arg_action
= ACTION_GDB
;
268 else if (streq(cmd
, "info"))
269 arg_action
= ACTION_INFO
;
271 log_error("Unknown action '%s'", cmd
);
276 if (arg_field
&& arg_action
!= ACTION_LIST
) {
277 log_error("Option --field/-F only makes sense with list");
282 arg_matches
= argv
+ optind
;
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
) {
640 if (arg_action
== ACTION_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(sd_journal
*j
) {
649 unsigned n_found
= 0;
654 /* The coredumps are likely to compressed, and for just
655 * listing them we don't need to decompress them, so let's
656 * pick a fairly low data threshold here */
657 sd_journal_set_data_threshold(j
, 4096);
664 return print_entry(j
, 0);
666 if (arg_since
!= USEC_INFINITY
&& !arg_reverse
)
667 r
= sd_journal_seek_realtime_usec(j
, arg_since
);
668 else if (arg_until
!= USEC_INFINITY
&& arg_reverse
)
669 r
= sd_journal_seek_realtime_usec(j
, arg_until
);
670 else if (arg_reverse
)
671 r
= sd_journal_seek_tail(j
);
673 r
= sd_journal_seek_head(j
);
675 return log_error_errno(r
, "Failed to seek to date: %m");
679 r
= sd_journal_next(j
);
681 r
= sd_journal_previous(j
);
684 return log_error_errno(r
, "Failed to iterate through journal: %m");
689 if (arg_until
!= USEC_INFINITY
&& !arg_reverse
) {
692 r
= sd_journal_get_realtime_usec(j
, &usec
);
694 return log_error_errno(r
, "Failed to determine timestamp: %m");
695 if (usec
> arg_until
)
699 if (arg_since
!= USEC_INFINITY
&& arg_reverse
) {
702 r
= sd_journal_get_realtime_usec(j
, &usec
);
704 return log_error_errno(r
, "Failed to determine timestamp: %m");
705 if (usec
< arg_since
)
709 r
= print_entry(j
, n_found
++);
714 if (!arg_field
&& n_found
<= 0) {
716 log_notice("No coredumps found.");
724 static int save_core(sd_journal
*j
, FILE *file
, char **path
, bool *unlink_temp
) {
726 _cleanup_free_
char *filename
= NULL
;
729 _cleanup_close_
int fdt
= -1;
732 assert(!(file
&& path
)); /* At most one can be specified */
733 assert(!!path
== !!unlink_temp
); /* Those must be specified together */
735 /* Look for a coredump on disk first. */
736 r
= sd_journal_get_data(j
, "COREDUMP_FILENAME", (const void**) &data
, &len
);
738 retrieve(data
, len
, "COREDUMP_FILENAME", &filename
);
741 return log_error_errno(r
, "Failed to retrieve COREDUMP_FILENAME field: %m");
742 /* Check that we can have a COREDUMP field. We still haven't set a high
743 * data threshold, so we'll get a few kilobytes at most.
746 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
748 return log_error_errno(r
, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
750 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
754 if (access(filename
, R_OK
) < 0)
755 return log_error_errno(errno
, "File \"%s\" is not readable: %m", filename
);
757 if (path
&& !endswith(filename
, ".xz") && !endswith(filename
, ".lz4")) {
768 /* Create a temporary file to write the uncompressed core to. */
770 r
= var_tmp_dir(&vt
);
772 return log_error_errno(r
, "Failed to acquire temporary directory path: %m");
774 temp
= strjoin(vt
, "/coredump-XXXXXX");
778 fdt
= mkostemp_safe(temp
);
780 return log_error_errno(fdt
, "Failed to create temporary file: %m");
781 log_debug("Created temporary file %s", temp
);
785 /* If neither path or file are specified, we will write to stdout. Let's now check
786 * if stdout is connected to a tty. We checked that the file exists, or that the
787 * core might be stored in the journal. In this second case, if we found the entry,
788 * in all likelyhood we will be able to access the COREDUMP= field. In either case,
789 * we stop before doing any "real" work, i.e. before starting decompression or
790 * reading from the file or creating temporary files.
794 return log_error_errno(ENOTTY
, "Refusing to dump core to tty"
795 " (use shell redirection or specify --output).");
803 #if HAVE_XZ || HAVE_LZ4
804 _cleanup_close_
int fdf
;
806 fdf
= open(filename
, O_RDONLY
| O_CLOEXEC
);
808 r
= log_error_errno(errno
, "Failed to open %s: %m", filename
);
812 r
= decompress_stream(filename
, fdf
, fd
, -1);
814 log_error_errno(r
, "Failed to decompress %s: %m", filename
);
818 log_error("Cannot decompress file. Compiled without compression support.");
825 /* We want full data, nothing truncated. */
826 sd_journal_set_data_threshold(j
, 0);
828 r
= sd_journal_get_data(j
, "COREDUMP", (const void**) &data
, &len
);
830 return log_error_errno(r
, "Failed to retrieve COREDUMP field: %m");
836 sz
= write(fd
, data
, len
);
838 r
= log_error_errno(errno
, "Failed to write output: %m");
841 if (sz
!= (ssize_t
) len
) {
842 log_error("Short write to output.");
857 log_debug("Removed temporary file %s", temp
);
862 static int dump_core(sd_journal
* j
) {
871 print_info(arg_output
? stdout
: stderr
, j
, false);
873 r
= save_core(j
, arg_output
, NULL
, NULL
);
877 r
= sd_journal_previous(j
);
878 if (r
> 0 && !arg_quiet
)
879 log_notice("More than one entry matches, ignoring rest.");
884 static int run_gdb(sd_journal
*j
) {
885 _cleanup_free_
char *exe
= NULL
, *path
= NULL
;
886 bool unlink_path
= false;
898 print_info(stdout
, j
, false);
901 r
= sd_journal_get_data(j
, "COREDUMP_EXE", (const void**) &data
, &len
);
903 return log_error_errno(r
, "Failed to retrieve COREDUMP_EXE field: %m");
905 assert(len
> STRLEN("COREDUMP_EXE="));
906 data
+= STRLEN("COREDUMP_EXE=");
907 len
-= STRLEN("COREDUMP_EXE=");
909 exe
= strndup(data
, len
);
913 if (endswith(exe
, " (deleted)")) {
914 log_error("Binary already deleted.");
918 if (!path_is_absolute(exe
)) {
919 log_error("Binary is not an absolute path.");
923 r
= save_core(j
, NULL
, &path
, &unlink_path
);
927 /* Don't interfere with gdb and its handling of SIGINT. */
928 (void) ignore_signals(SIGINT
, -1);
930 r
= safe_fork("(gdb)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
934 execlp("gdb", "gdb", exe
, path
, NULL
);
935 log_error_errno(errno
, "Failed to invoke gdb: %m");
939 r
= wait_for_terminate_and_check("gdb", pid
, WAIT_LOG_ABNORMAL
);
942 (void) default_signals(SIGINT
, -1);
945 log_debug("Removed temporary file %s", path
);
952 static int check_units_active(void) {
953 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
954 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
955 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
956 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
958 const char *id
, *state
, *substate
;
963 r
= sd_bus_default_system(&bus
);
965 return log_error_errno(r
, "Failed to acquire bus: %m");
967 r
= sd_bus_message_new_method_call(
970 "org.freedesktop.systemd1",
971 "/org/freedesktop/systemd1",
972 "org.freedesktop.systemd1.Manager",
973 "ListUnitsByPatterns");
975 return bus_log_create_error(r
);
977 r
= sd_bus_message_append_strv(m
, NULL
);
979 return bus_log_create_error(r
);
981 r
= sd_bus_message_append_strv(m
, STRV_MAKE("systemd-coredump@*.service"));
983 return bus_log_create_error(r
);
985 r
= sd_bus_call(bus
, m
, SHORT_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
987 return log_error_errno(r
, "Failed to check if any systemd-coredump@.service units are running: %s",
988 bus_error_message(&error
, r
));
990 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
992 return bus_log_parse_error(r
);
994 while ((r
= sd_bus_message_read(
995 reply
, "(ssssssouso)",
996 &id
, NULL
, NULL
, &state
, &substate
,
997 NULL
, NULL
, NULL
, NULL
, NULL
)) > 0) {
998 bool found
= !STR_IN_SET(state
, "inactive", "dead", "failed");
999 log_debug("Unit %s is %s/%s, %scounting it.", id
, state
, substate
, found
? "" : "not ");
1003 return bus_log_parse_error(r
);
1005 r
= sd_bus_message_exit_container(reply
);
1007 return bus_log_parse_error(r
);
1012 int main(int argc
, char *argv
[]) {
1013 _cleanup_(sd_journal_closep
) sd_journal
*j
= NULL
;
1014 int r
= 0, units_active
;
1016 setlocale(LC_ALL
, "");
1017 log_parse_environment();
1020 r
= parse_argv(argc
, argv
);
1024 if (arg_action
== ACTION_NONE
)
1029 if (arg_directory
) {
1030 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
1032 log_error_errno(r
, "Failed to open journals in directory: %s: %m", arg_directory
);
1036 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
1038 log_error_errno(r
, "Failed to open journal: %m");
1043 r
= journal_access_check_and_warn(j
, arg_quiet
);
1051 if (DEBUG_LOGGING
) {
1052 _cleanup_free_
char *filter
;
1054 filter
= journal_make_match_string(j
);
1055 log_debug("Journal filter: %s", filter
);
1058 units_active
= check_units_active(); /* error is treated the same as 0 */
1060 switch(arg_action
) {
1064 pager_open(arg_no_pager
, false);
1077 assert_not_reached("Shouldn't be here");
1080 if (units_active
> 0)
1081 printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
1082 ansi_highlight_red(),
1083 units_active
, units_active
== 1 ? "unit is running" : "units are running",
1091 return r
>= 0 ? r
: EXIT_FAILURE
;