]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/coredump/coredumpctl.c
build-sys: use #if Y instead of #ifdef Y everywhere
[thirdparty/systemd.git] / src / coredump / coredumpctl.c
index 6abd99430ba3b71ae069985d78f517ad7e8121fc..0380df10d1be5206c2f0fa3867402ef736764a7b 100644 (file)
 #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 "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,
@@ -62,6 +70,7 @@ 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 int add_match(sd_journal *j, const char *match) {
         _cleanup_free_ char *p = NULL;
@@ -125,11 +134,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"
                "  -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,7 +156,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NO_LEGEND,
         };
 
-        int c;
+        int c, r;
 
         static const struct option options[] = {
                 { "help",         no_argument,       NULL, 'h'           },
@@ -156,15 +167,17 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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:r", 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();
@@ -194,6 +207,18 @@ static int parse_argv(int argc, char *argv[]) {
 
                         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");
@@ -214,6 +239,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_reverse = true;
                         break;
 
+                case 'q':
+                        arg_quiet = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -221,6 +250,12 @@ static int parse_argv(int argc, char *argv[]) {
                         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"))
@@ -318,7 +353,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
         _cleanup_free_ char
                 *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
-                *filename = NULL, *coredump = NULL;
+                *filename = NULL, *truncated = NULL, *coredump = NULL;
         const void *d;
         size_t l;
         usec_t t;
@@ -340,6 +375,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
                 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);
         }
 
@@ -355,13 +391,13 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
         format_timestamp(buf, sizeof(buf), t);
 
         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",
-                        8, "COREFILE",
+                        9, "COREFILE",
                            "EXE");
 
         normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
@@ -380,13 +416,16 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
         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, normal_coredump ? strna(sgnl) : "-",
-                8, present,
+                9, present,
                 strna(exe ?: (comm ?: cmdline)));
 
         return 0;
@@ -400,7 +439,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
                 *slice = NULL, *cgroup = NULL, *owner_uid = NULL,
                 *message = NULL, *timestamp = NULL, *filename = NULL,
-                *coredump = NULL;
+                *truncated = NULL, *coredump = NULL;
         const void *d;
         size_t l;
         bool normal_coredump;
@@ -426,6 +465,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 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);
@@ -544,9 +584,24 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
         if (hostname)
                 fprintf(file, "      Hostname: %s\n", hostname);
 
-        if (filename)
-                fprintf(file, "       Storage: %s%s\n", filename,
-                        access(filename, R_OK) < 0 ? " (inaccessible)" : "");
+        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
@@ -607,22 +662,57 @@ static int dump_list(sd_journal *j) {
 
                 return print_entry(j, 0);
         } else {
-                if (!arg_reverse) {
-                        SD_JOURNAL_FOREACH(j) {
-                                r = 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 r;
+                                        return log_error_errno(r, "Failed to determine timestamp: %m");
+                                if (usec > arg_until)
+                                        continue;
                         }
-                } else {
-                        SD_JOURNAL_FOREACH_BACKWARDS(j) {
-                                r = print_entry(j, n_found++);
+
+                        if (arg_since != USEC_INFINITY && arg_reverse) {
+                                usec_t usec;
+
+                                r = sd_journal_get_realtime_usec(j, &usec);
                                 if (r < 0)
-                                        return r;
+                                        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;
                 }
         }
@@ -709,7 +799,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
         }
 
         if (filename) {
-#if defined(HAVE_XZ) || defined(HAVE_LZ4)
+#if HAVE_XZ || HAVE_LZ4
                 _cleanup_close_ int fdf;
 
                 fdf = open(filename, O_RDONLY | O_CLOEXEC);
@@ -784,8 +874,8 @@ static int dump_core(sd_journal* j) {
                 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;
 }
@@ -871,9 +961,69 @@ 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;
-        int r = 0;
+        int r = 0, units_active;
 
         setlocale(LC_ALL, "");
         log_parse_environment();
@@ -902,6 +1052,10 @@ int main(int argc, char *argv[]) {
                 }
         }
 
+        r = journal_access_check_and_warn(j, arg_quiet);
+        if (r < 0)
+                goto end;
+
         r = add_matches(j);
         if (r < 0)
                 goto end;
@@ -913,6 +1067,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:
@@ -933,6 +1089,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();