]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/coredump/coredumpctl.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / coredump / coredumpctl.c
index bbf8793e57f841618af63cdbee5ce557ee67812a..b92f5d330d81ee3622034085c18b50ee34f98fff 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #include <string.h>
 #include <unistd.h>
 
+#include "sd-bus.h"
 #include "sd-journal.h"
+#include "sd-messages.h"
 
 #include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
 #include "compress.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "journal-internal.h"
+#include "journal-util.h"
 #include "log.h"
 #include "macro.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
-#include "set.h"
 #include "sigbus.h"
 #include "signal-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "terminal-util.h"
 #include "user-util.h"
 #include "util.h"
 
+#define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC)
+
+static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
+
 static enum {
         ACTION_NONE,
         ACTION_INFO,
@@ -59,36 +69,11 @@ static bool arg_no_pager = false;
 static int arg_no_legend = false;
 static int arg_one = false;
 static FILE* arg_output = NULL;
+static bool arg_reverse = false;
+static char** arg_matches = NULL;
+static bool arg_quiet = false;
 
-static Set *new_matches(void) {
-        Set *set;
-        char *tmp;
-        int r;
-
-        set = set_new(NULL);
-        if (!set) {
-                log_oom();
-                return NULL;
-        }
-
-        tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
-        if (!tmp) {
-                log_oom();
-                set_free(set);
-                return NULL;
-        }
-
-        r = set_consume(set, tmp);
-        if (r < 0) {
-                log_error_errno(r, "failed to add to set: %m");
-                set_free(set);
-                return NULL;
-        }
-
-        return set;
-}
-
-static int add_match(Set *set, const char *match) {
+static int add_match(sd_journal *j, const char *match) {
         _cleanup_free_ char *p = NULL;
         char *pattern = NULL;
         const char* prefix;
@@ -100,7 +85,8 @@ static int add_match(Set *set, const char *match) {
         else if (strchr(match, '/')) {
                 r = path_make_absolute_cwd(match, &p);
                 if (r < 0)
-                        goto fail;
+                        return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
+
                 match = p;
                 prefix = "COREDUMP_EXE=";
         } else if (parse_pid(match, &pid) >= 0)
@@ -108,20 +94,36 @@ static int add_match(Set *set, const char *match) {
         else
                 prefix = "COREDUMP_COMM=";
 
-        pattern = strjoin(prefix, match, NULL);
-        if (!pattern) {
-                r = -ENOMEM;
-                goto fail;
-        }
+        pattern = strjoin(prefix, match);
+        if (!pattern)
+                return log_oom();
 
-        log_debug("Adding pattern: %s", pattern);
-        r = set_consume(set, pattern);
+        log_debug("Adding match: %s", pattern);
+        r = sd_journal_add_match(j, pattern, 0);
         if (r < 0)
-                goto fail;
+                return log_error_errno(r, "Failed to add match \"%s\": %m", match);
+        return 0;
+}
+
+static int add_matches(sd_journal *j) {
+        char **match;
+        int r;
+
+        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
+
+        r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
+
+        STRV_FOREACH(match, arg_matches) {
+                r = add_match(j, *match);
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
-fail:
-        return log_error_errno(r, "Failed to add match: %m");
 }
 
 static void help(void) {
@@ -133,10 +135,13 @@ static void help(void) {
                "     --no-pager      Do not pipe output into a pager\n"
                "     --no-legend     Do not print the column headers.\n"
                "  -1                 Show information about most recent entry only\n"
+               "  -S --since=DATE    Only print coredumps since the date\n"
+               "  -U --until=DATE    Only print coredumps until the date\n"
+               "  -r --reverse       Show the newest entries first\n"
                "  -F --field=FIELD   List all values a certain field takes\n"
-               "  -o --output=FILE   Write output to FILE\n\n"
+               "  -o --output=FILE   Write output to FILE\n"
                "  -D --directory=DIR Use journal files from directory\n\n"
-
+               "  -q --quiet         Do not show info messages and privilege warning\n"
                "Commands:\n"
                "  list [MATCHES...]  List available coredumps (default)\n"
                "  info [MATCHES...]  Show detailed information about one or more coredumps\n"
@@ -145,14 +150,14 @@ static void help(void) {
                , program_invocation_short_name);
 }
 
-static int parse_argv(int argc, char *argv[], Set *matches) {
+static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_NO_PAGER,
                 ARG_NO_LEGEND,
         };
 
-        int r, c;
+        int c, r;
 
         static const struct option options[] = {
                 { "help",         no_argument,       NULL, 'h'           },
@@ -162,15 +167,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
                 { "output",       required_argument, NULL, 'o'           },
                 { "field",        required_argument, NULL, 'F'           },
                 { "directory",    required_argument, NULL, 'D'           },
+                { "reverse",      no_argument,       NULL, 'r'           },
+                { "since",        required_argument, NULL, 'S'           },
+                { "until",        required_argument, NULL, 'U'           },
+                { "quiet",        no_argument,       NULL, 'q'           },
                 {}
         };
 
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
                 switch(c) {
-
                 case 'h':
                         arg_action = ACTION_NONE;
                         help();
@@ -200,6 +208,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
 
                         break;
 
+                case 'S':
+                        r = parse_timestamp(optarg, &arg_since);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+                        break;
+
+                case 'U':
+                        r = parse_timestamp(optarg, &arg_until);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse timestamp: %s", optarg);
+                        break;
+
                 case 'F':
                         if (arg_field) {
                                 log_error("cannot use --field/-F more than once");
@@ -216,6 +236,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
                         arg_directory = optarg;
                         break;
 
+                case 'r':
+                        arg_reverse = true;
+                        break;
+
+                case 'q':
+                        arg_quiet = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -223,6 +251,12 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
                         assert_not_reached("Unhandled option");
                 }
 
+        if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
+            arg_since > arg_until) {
+                log_error("--since= must be before --until=.");
+                return -EINVAL;
+        }
+
         if (optind < argc) {
                 const char *cmd = argv[optind++];
                 if (streq(cmd, "list"))
@@ -244,12 +278,8 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
                 return -EINVAL;
         }
 
-        while (optind < argc) {
-                r = add_match(matches, argv[optind]);
-                if (r != 0)
-                        return r;
-                optind++;
-        }
+        if (optind < argc)
+                arg_matches = argv + optind;
 
         return 0;
 }
@@ -280,11 +310,10 @@ static int retrieve(const void *data,
         free(*var);
         *var = v;
 
-        return 0;
+        return 1;
 }
 
-static void print_field(FILE* file, sd_journal *j) {
-        _cleanup_free_ char *value = NULL;
+static int print_field(FILE* file, sd_journal *j) {
         const void *d;
         size_t l;
 
@@ -293,37 +322,62 @@ static void print_field(FILE* file, sd_journal *j) {
 
         assert(arg_field);
 
-        SD_JOURNAL_FOREACH_DATA(j, d, l)
-                retrieve(d, l, arg_field, &value);
+        /* A (user-specified) field may appear more than once for a given entry.
+         * We will print all of the occurences.
+         * This is different below for fields that systemd-coredump uses,
+         * because they cannot meaningfully appear more than once.
+         */
+        SD_JOURNAL_FOREACH_DATA(j, d, l) {
+                _cleanup_free_ char *value = NULL;
+                int r;
 
-        if (value)
-                fprintf(file, "%s\n", value);
+                r = retrieve(d, l, arg_field, &value);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        fprintf(file, "%s\n", value);
+        }
+
+        return 0;
 }
 
+#define RETRIEVE(d, l, name, arg)                    \
+        {                                            \
+                int _r = retrieve(d, l, name, &arg); \
+                if (_r < 0)                          \
+                        return _r;                   \
+                if (_r > 0)                          \
+                        continue;                    \
+        }
+
 static int print_list(FILE* file, sd_journal *j, int had_legend) {
         _cleanup_free_ char
-                *pid = NULL, *uid = NULL, *gid = NULL,
+                *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
-                *filename = NULL;
+                *filename = NULL, *truncated = NULL, *coredump = NULL;
         const void *d;
         size_t l;
         usec_t t;
         char buf[FORMAT_TIMESTAMP_MAX];
         int r;
-        bool present;
+        const char *present;
+        bool normal_coredump;
 
         assert(file);
         assert(j);
 
         SD_JOURNAL_FOREACH_DATA(j, d, l) {
-                retrieve(d, l, "COREDUMP_PID", &pid);
-                retrieve(d, l, "COREDUMP_UID", &uid);
-                retrieve(d, l, "COREDUMP_GID", &gid);
-                retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
-                retrieve(d, l, "COREDUMP_EXE", &exe);
-                retrieve(d, l, "COREDUMP_COMM", &comm);
-                retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
-                retrieve(d, l, "COREDUMP_FILENAME", &filename);
+                RETRIEVE(d, l, "MESSAGE_ID", mid);
+                RETRIEVE(d, l, "COREDUMP_PID", pid);
+                RETRIEVE(d, l, "COREDUMP_UID", uid);
+                RETRIEVE(d, l, "COREDUMP_GID", gid);
+                RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+                RETRIEVE(d, l, "COREDUMP_EXE", exe);
+                RETRIEVE(d, l, "COREDUMP_COMM", comm);
+                RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+                RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+                RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
+                RETRIEVE(d, l, "COREDUMP", coredump);
         }
 
         if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
@@ -336,25 +390,43 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
                 return log_error_errno(r, "Failed to get realtime timestamp: %m");
 
         format_timestamp(buf, sizeof(buf), t);
-        present = filename && access(filename, F_OK) == 0;
 
         if (!had_legend && !arg_no_legend)
-                fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+                fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
                         FORMAT_TIMESTAMP_WIDTH, "TIME",
                         6, "PID",
                         5, "UID",
                         5, "GID",
                         3, "SIG",
-                        1, "PRESENT",
+                        9, "COREFILE",
                            "EXE");
 
-        fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n",
+        normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
+
+        if (filename)
+                if (access(filename, R_OK) == 0)
+                        present = "present";
+                else if (errno == ENOENT)
+                        present = "missing";
+                else
+                        present = "error";
+        else if (coredump)
+                present = "journal";
+        else if (normal_coredump)
+                present = "none";
+        else
+                present = "-";
+
+        if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
+                present = "truncated";
+
+        fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
                 FORMAT_TIMESTAMP_WIDTH, buf,
                 6, strna(pid),
                 5, strna(uid),
                 5, strna(gid),
-                3, strna(sgnl),
-                1, present ? "*" : "",
+                3, normal_coredump ? strna(sgnl) : "-",
+                9, present,
                 strna(exe ?: (comm ?: cmdline)));
 
         return 0;
@@ -362,44 +434,51 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
 
 static int print_info(FILE *file, sd_journal *j, bool need_space) {
         _cleanup_free_ char
-                *pid = NULL, *uid = NULL, *gid = NULL,
+                *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
                 *unit = NULL, *user_unit = NULL, *session = NULL,
                 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
                 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
-                *message = NULL, *timestamp = NULL, *filename = NULL;
+                *message = NULL, *timestamp = NULL, *filename = NULL,
+                *truncated = NULL, *coredump = NULL;
         const void *d;
         size_t l;
+        bool normal_coredump;
         int r;
 
         assert(file);
         assert(j);
 
         SD_JOURNAL_FOREACH_DATA(j, d, l) {
-                retrieve(d, l, "COREDUMP_PID", &pid);
-                retrieve(d, l, "COREDUMP_UID", &uid);
-                retrieve(d, l, "COREDUMP_GID", &gid);
-                retrieve(d, l, "COREDUMP_SIGNAL", &sgnl);
-                retrieve(d, l, "COREDUMP_EXE", &exe);
-                retrieve(d, l, "COREDUMP_COMM", &comm);
-                retrieve(d, l, "COREDUMP_CMDLINE", &cmdline);
-                retrieve(d, l, "COREDUMP_UNIT", &unit);
-                retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit);
-                retrieve(d, l, "COREDUMP_SESSION", &session);
-                retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid);
-                retrieve(d, l, "COREDUMP_SLICE", &slice);
-                retrieve(d, l, "COREDUMP_CGROUP", &cgroup);
-                retrieve(d, l, "COREDUMP_TIMESTAMP", &timestamp);
-                retrieve(d, l, "COREDUMP_FILENAME", &filename);
-                retrieve(d, l, "_BOOT_ID", &boot_id);
-                retrieve(d, l, "_MACHINE_ID", &machine_id);
-                retrieve(d, l, "_HOSTNAME", &hostname);
-                retrieve(d, l, "MESSAGE", &message);
+                RETRIEVE(d, l, "MESSAGE_ID", mid);
+                RETRIEVE(d, l, "COREDUMP_PID", pid);
+                RETRIEVE(d, l, "COREDUMP_UID", uid);
+                RETRIEVE(d, l, "COREDUMP_GID", gid);
+                RETRIEVE(d, l, "COREDUMP_SIGNAL", sgnl);
+                RETRIEVE(d, l, "COREDUMP_EXE", exe);
+                RETRIEVE(d, l, "COREDUMP_COMM", comm);
+                RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline);
+                RETRIEVE(d, l, "COREDUMP_UNIT", unit);
+                RETRIEVE(d, l, "COREDUMP_USER_UNIT", user_unit);
+                RETRIEVE(d, l, "COREDUMP_SESSION", session);
+                RETRIEVE(d, l, "COREDUMP_OWNER_UID", owner_uid);
+                RETRIEVE(d, l, "COREDUMP_SLICE", slice);
+                RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup);
+                RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp);
+                RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
+                RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
+                RETRIEVE(d, l, "COREDUMP", coredump);
+                RETRIEVE(d, l, "_BOOT_ID", boot_id);
+                RETRIEVE(d, l, "_MACHINE_ID", machine_id);
+                RETRIEVE(d, l, "_HOSTNAME", hostname);
+                RETRIEVE(d, l, "MESSAGE", message);
         }
 
         if (need_space)
                 fputs("\n", file);
 
+        normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
+
         if (comm)
                 fprintf(file,
                         "           PID: %s%s%s (%s)\n",
@@ -445,11 +524,12 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
 
         if (sgnl) {
                 int sig;
+                const char *name = normal_coredump ? "Signal" : "Reason";
 
-                if (safe_atoi(sgnl, &sig) >= 0)
-                        fprintf(file, "        Signal: %s (%s)\n", sgnl, signal_to_string(sig));
+                if (normal_coredump && safe_atoi(sgnl, &sig) >= 0)
+                        fprintf(file, "        %s: %s (%s)\n", name, sgnl, signal_to_string(sig));
                 else
-                        fprintf(file, "        Signal: %s\n", sgnl);
+                        fprintf(file, "        %s: %s\n", name, sgnl);
         }
 
         if (timestamp) {
@@ -477,7 +557,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
         if (unit)
                 fprintf(file, "          Unit: %s\n", unit);
         if (user_unit)
-                fprintf(file, "     User Unit: %s\n", unit);
+                fprintf(file, "     User Unit: %s\n", user_unit);
         if (slice)
                 fprintf(file, "         Slice: %s\n", slice);
         if (session)
@@ -505,8 +585,28 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
         if (hostname)
                 fprintf(file, "      Hostname: %s\n", hostname);
 
-        if (filename && access(filename, F_OK) == 0)
-                fprintf(file, "      Coredump: %s\n", filename);
+        if (filename) {
+                bool inacc, trunc;
+
+                inacc = access(filename, R_OK) < 0;
+                trunc = truncated && parse_boolean(truncated) > 0;
+
+                if (inacc || trunc)
+                        fprintf(file, "       Storage: %s%s (%s%s%s)%s\n",
+                                ansi_highlight_red(),
+                                filename,
+                                inacc ? "inaccessible" : "",
+                                inacc && trunc ? ", " : "",
+                                trunc ? "truncated" : "",
+                                ansi_normal());
+                else
+                        fprintf(file, "       Storage: %s\n", filename);
+        }
+
+        else if (coredump)
+                fprintf(file, "       Storage: journal\n");
+        else
+                fprintf(file, "       Storage: none\n");
 
         if (message) {
                 _cleanup_free_ char *m = NULL;
@@ -534,15 +634,15 @@ static int focus(sd_journal *j) {
         return r;
 }
 
-static void print_entry(sd_journal *j, unsigned n_found) {
+static int print_entry(sd_journal *j, unsigned n_found) {
         assert(j);
 
         if (arg_action == ACTION_INFO)
-                print_info(stdout, j, n_found);
+                return print_info(stdout, j, n_found);
         else if (arg_field)
-                print_field(stdout, j);
+                return print_field(stdout, j);
         else
-                print_list(stdout, j, n_found);
+                return print_list(stdout, j, n_found);
 }
 
 static int dump_list(sd_journal *j) {
@@ -561,13 +661,59 @@ static int dump_list(sd_journal *j) {
                 if (r < 0)
                         return r;
 
-                print_entry(j, 0);
+                return print_entry(j, 0);
         } else {
-                SD_JOURNAL_FOREACH(j)
-                        print_entry(j, n_found++);
+                if (arg_since != USEC_INFINITY && !arg_reverse)
+                        r = sd_journal_seek_realtime_usec(j, arg_since);
+                else if (arg_until != USEC_INFINITY && arg_reverse)
+                        r = sd_journal_seek_realtime_usec(j, arg_until);
+                else if (arg_reverse)
+                        r = sd_journal_seek_tail(j);
+                else
+                        r = sd_journal_seek_head(j);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to seek to date: %m");
+
+                for (;;) {
+                        if (!arg_reverse)
+                                r = sd_journal_next(j);
+                        else
+                                r = sd_journal_previous(j);
+
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to iterate through journal: %m");
+
+                        if (r == 0)
+                                break;
+
+                        if (arg_until != USEC_INFINITY && !arg_reverse) {
+                                usec_t usec;
+
+                                r = sd_journal_get_realtime_usec(j, &usec);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to determine timestamp: %m");
+                                if (usec > arg_until)
+                                        continue;
+                        }
+
+                        if (arg_since != USEC_INFINITY && arg_reverse) {
+                                usec_t usec;
+
+                                r = sd_journal_get_realtime_usec(j, &usec);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to determine timestamp: %m");
+                                if (usec < arg_since)
+                                        continue;
+                        }
+
+                        r = print_entry(j, n_found++);
+                        if (r < 0)
+                                return r;
+                }
 
                 if (!arg_field && n_found <= 0) {
-                        log_notice("No coredumps found.");
+                        if (!arg_quiet)
+                                log_notice("No coredumps found.");
                         return -ESRCH;
                 }
         }
@@ -575,122 +721,142 @@ static int dump_list(sd_journal *j) {
         return 0;
 }
 
-static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
+static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) {
         const char *data;
         _cleanup_free_ char *filename = NULL;
         size_t len;
-        int r;
+        int r, fd;
+        _cleanup_close_ int fdt = -1;
+        char *temp = NULL;
 
-        assert((fd >= 0) != !!path);
-        assert(!!path == !!unlink_temp);
+        assert(!(file && path));         /* At most one can be specified */
+        assert(!!path == !!unlink_temp); /* Those must be specified together */
 
-        /* Prefer uncompressed file to journal (probably cached) to
-         * compressed file (probably uncached). */
+        /* Look for a coredump on disk first. */
         r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len);
-        if (r < 0 && r != -ENOENT)
-                log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m");
-        else if (r == 0)
+        if (r == 0)
                 retrieve(data, len, "COREDUMP_FILENAME", &filename);
+        else {
+                if (r != -ENOENT)
+                        return log_error_errno(r, "Failed to retrieve COREDUMP_FILENAME field: %m");
+                /* Check that we can have a COREDUMP field. We still haven't set a high
+                 * data threshold, so we'll get a few kilobytes at most.
+                 */
 
-        if (filename && access(filename, R_OK) < 0) {
-                log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
-                         "File %s is not readable: %m", filename);
-                filename = mfree(filename);
+                r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+                if (r == -ENOENT)
+                        return log_error_errno(r, "Coredump entry has no core attached (neither internally in the journal nor externally on disk).");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
         }
 
-        if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
-                if (path) {
+        if (filename) {
+                if (access(filename, R_OK) < 0)
+                        return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
+
+                if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
                         *path = filename;
                         filename = NULL;
+
+                        return 0;
                 }
+        }
 
-                return 0;
-        } else {
-                _cleanup_close_ int fdt = -1;
-                char *temp = NULL;
+        if (path) {
+                const char *vt;
 
-                if (fd < 0) {
-                        const char *vt;
+                /* Create a temporary file to write the uncompressed core to. */
 
-                        r = var_tmp_dir(&vt);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to acquire temporary directory path: %m");
+                r = var_tmp_dir(&vt);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire temporary directory path: %m");
 
-                        temp = strjoin(vt, "/coredump-XXXXXX", NULL);
-                        if (!temp)
-                                return log_oom();
+                temp = strjoin(vt, "/coredump-XXXXXX");
+                if (!temp)
+                        return log_oom();
 
-                        fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
-                        if (fdt < 0)
-                                return log_error_errno(fdt, "Failed to create temporary file: %m");
-                        log_debug("Created temporary file %s", temp);
+                fdt = mkostemp_safe(temp);
+                if (fdt < 0)
+                        return log_error_errno(fdt, "Failed to create temporary file: %m");
+                log_debug("Created temporary file %s", temp);
 
-                        fd = fdt;
+                fd = fdt;
+        } else {
+                /* If neither path or file are specified, we will write to stdout. Let's now check
+                 * if stdout is connected to a tty. We checked that the file exists, or that the
+                 * core might be stored in the journal. In this second case, if we found the entry,
+                 * in all likelyhood we will be able to access the COREDUMP= field.  In either case,
+                 * we stop before doing any "real" work, i.e. before starting decompression or
+                 * reading from the file or creating temporary files.
+                 */
+                if (!file) {
+                        if (on_tty())
+                                return log_error_errno(ENOTTY, "Refusing to dump core to tty"
+                                                       " (use shell redirection or specify --output).");
+                        file = stdout;
                 }
 
-                r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
-                if (r == 0) {
-                        ssize_t sz;
-
-                        assert(len >= 9);
-                        data += 9;
-                        len -= 9;
-
-                        sz = write(fdt, data, len);
-                        if (sz < 0) {
-                                r = log_error_errno(errno,
-                                                    "Failed to write temporary file: %m");
-                                goto error;
-                        }
-                        if (sz != (ssize_t) len) {
-                                log_error("Short write to temporary file.");
-                                r = -EIO;
-                                goto error;
-                        }
-                } else if (filename) {
-#if defined(HAVE_XZ) || defined(HAVE_LZ4)
-                        _cleanup_close_ int fdf;
-
-                        fdf = open(filename, O_RDONLY | O_CLOEXEC);
-                        if (fdf < 0) {
-                                r = log_error_errno(errno,
-                                                    "Failed to open %s: %m",
-                                                    filename);
-                                goto error;
-                        }
+                fd = fileno(file);
+        }
 
-                        r = decompress_stream(filename, fdf, fd, -1);
-                        if (r < 0) {
-                                log_error_errno(r, "Failed to decompress %s: %m", filename);
-                                goto error;
-                        }
-#else
-                        log_error("Cannot decompress file. Compiled without compression support.");
-                        r = -EOPNOTSUPP;
+        if (filename) {
+#if HAVE_XZ || HAVE_LZ4
+                _cleanup_close_ int fdf;
+
+                fdf = open(filename, O_RDONLY | O_CLOEXEC);
+                if (fdf < 0) {
+                        r = log_error_errno(errno, "Failed to open %s: %m", filename);
                         goto error;
-#endif
-                } else {
-                        if (r == -ENOENT)
-                                log_error("Cannot retrieve coredump from journal or disk.");
-                        else
-                                log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
+                }
+
+                r = decompress_stream(filename, fdf, fd, -1);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to decompress %s: %m", filename);
                         goto error;
                 }
+#else
+                log_error("Cannot decompress file. Compiled without compression support.");
+                r = -EOPNOTSUPP;
+                goto error;
+#endif
+        } else {
+                ssize_t sz;
+
+                /* We want full data, nothing truncated. */
+                sd_journal_set_data_threshold(j, 0);
 
-                if (temp) {
-                        *path = temp;
-                        *unlink_temp = true;
+                r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to retrieve COREDUMP field: %m");
+
+                assert(len >= 9);
+                data += 9;
+                len -= 9;
+
+                sz = write(fd, data, len);
+                if (sz < 0) {
+                        r = log_error_errno(errno, "Failed to write output: %m");
+                        goto error;
                 }
+                if (sz != (ssize_t) len) {
+                        log_error("Short write to output.");
+                        r = -EIO;
+                        goto error;
+                }
+        }
 
-                return 0;
+        if (temp) {
+                *path = temp;
+                *unlink_temp = true;
+        }
+        return 0;
 
 error:
-                if (temp) {
-                        unlink(temp);
-                        log_debug("Removed temporary file %s", temp);
-                }
-                return r;
+        if (temp) {
+                unlink(temp);
+                log_debug("Removed temporary file %s", temp);
         }
+        return r;
 }
 
 static int dump_core(sd_journal* j) {
@@ -704,18 +870,13 @@ static int dump_core(sd_journal* j) {
 
         print_info(arg_output ? stdout : stderr, j, false);
 
-        if (on_tty() && !arg_output) {
-                log_error("Refusing to dump core to tty.");
-                return -ENOTTY;
-        }
-
-        r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL);
+        r = save_core(j, arg_output, NULL, NULL);
         if (r < 0)
-                return log_error_errno(r, "Coredump retrieval failed: %m");
+                return r;
 
         r = sd_journal_previous(j);
-        if (r >= 0)
-                log_warning("More than one entry matches, ignoring rest.");
+        if (r > 0 && !arg_quiet)
+                log_notice("More than one entry matches, ignoring rest.");
 
         return 0;
 }
@@ -760,9 +921,12 @@ static int run_gdb(sd_journal *j) {
                 return -ENOENT;
         }
 
-        r = save_core(j, -1, &path, &unlink_path);
+        r = save_core(j, NULL, &path, &unlink_path);
         if (r < 0)
-                return log_error_errno(r, "Failed to retrieve core: %m");
+                return r;
+
+        /* Don't interfere with gdb and its handling of SIGINT. */
+        (void) ignore_signals(SIGINT, -1);
 
         pid = fork();
         if (pid < 0) {
@@ -788,6 +952,8 @@ static int run_gdb(sd_journal *j) {
         r = st.si_code == CLD_EXITED ? st.si_status : 255;
 
 finish:
+        (void) default_signals(SIGINT, -1);
+
         if (unlink_path) {
                 log_debug("Removed temporary file %s", path);
                 unlink(path);
@@ -796,24 +962,75 @@ finish:
         return r;
 }
 
+static int check_units_active(void) {
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        int c = 0, r;
+        const char *id, *state, *substate;
+
+        if (arg_quiet)
+                return false;
+
+        r = sd_bus_default_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire bus: %m");
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "ListUnitsByPatterns");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, NULL);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service"));
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s",
+                                       bus_error_message(&error, r));
+
+        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        while ((r = sd_bus_message_read(
+                                reply, "(ssssssouso)",
+                                &id,  NULL,  NULL,  &state,  &substate,
+                                NULL,  NULL,  NULL,  NULL,  NULL)) > 0) {
+                bool found = !STR_IN_SET(state, "inactive", "dead", "failed");
+                log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not ");
+                c += found;
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        return c;
+}
+
 int main(int argc, char *argv[]) {
         _cleanup_(sd_journal_closep) sd_journal*j = NULL;
-        const char* match;
-        Iterator it;
-        int r = 0;
-        _cleanup_set_free_free_ Set *matches = NULL;
+        int r = 0, units_active;
 
         setlocale(LC_ALL, "");
         log_parse_environment();
         log_open();
 
-        matches = new_matches();
-        if (!matches) {
-                r = -ENOMEM;
-                goto end;
-        }
-
-        r = parse_argv(argc, argv, matches);
+        r = parse_argv(argc, argv);
         if (r < 0)
                 goto end;
 
@@ -836,17 +1053,13 @@ int main(int argc, char *argv[]) {
                 }
         }
 
-        /* We want full data, nothing truncated. */
-        sd_journal_set_data_threshold(j, 0);
+        r = journal_access_check_and_warn(j, arg_quiet);
+        if (r < 0)
+                goto end;
 
-        SET_FOREACH(match, matches, it) {
-                r = sd_journal_add_match(j, match, strlen(match));
-                if (r != 0) {
-                        log_error_errno(r, "Failed to add match '%s': %m",
-                                        match);
-                        goto end;
-                }
-        }
+        r = add_matches(j);
+        if (r < 0)
+                goto end;
 
         if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
                 _cleanup_free_ char *filter;
@@ -855,6 +1068,8 @@ int main(int argc, char *argv[]) {
                 log_debug("Journal filter: %s", filter);
         }
 
+        units_active = check_units_active(); /* error is treated the same as 0 */
+
         switch(arg_action) {
 
         case ACTION_LIST:
@@ -875,6 +1090,11 @@ int main(int argc, char *argv[]) {
                 assert_not_reached("Shouldn't be here");
         }
 
+        if (units_active > 0)
+                printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n",
+                       ansi_highlight_red(),
+                       units_active, units_active == 1 ? "unit is running" : "units are running",
+                       ansi_normal());
 end:
         pager_close();