diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml
index 3695d13323b..8300c51b454 100644
--- a/man/coredumpctl.xml
+++ b/man/coredumpctl.xml
@@ -26,7 +26,7 @@
coredumpctl
OPTIONS
COMMAND
- PID|COMM|EXE|MATCH
+ ID|PID|COMM|EXE|MATCH
@@ -55,6 +55,14 @@
The output is designed to be human-readable and contains a table with the following
columns:
+
+ ID
+ The UUID of the crash assigned by systemd-coredump.
+
+
+
+
+
TIME
The timestamp of the crash, as reported by the kernel.
diff --git a/shell-completion/zsh/_coredumpctl b/shell-completion/zsh/_coredumpctl
index ad4ba107f53..92d3593bf14 100644
--- a/shell-completion/zsh/_coredumpctl
+++ b/shell-completion/zsh/_coredumpctl
@@ -17,7 +17,8 @@ _coredumpctl_commands(){
local -a _dumps
cmd="${${_coredumpctl_cmds[(r)$words[1]:*]%%:*}}"
if (( $#cmd )); then
- _dumps=( "${(f)$(coredumpctl list -q --no-legend 2>/dev/null | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $4,$0}')}" )
+ # $5 is PID: columns are ID($1), DATE($2), TIME($3), TIMEZONE($4), PID($5)
+ _dumps=( "${(f)$(coredumpctl list -q --no-legend 2>/dev/null | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $5,$0}')}" )
if [[ -n "$_dumps" ]]; then
_describe -V -t pids 'coredumps' _dumps
else
diff --git a/src/coredump/coredump-backtrace.c b/src/coredump/coredump-backtrace.c
index 9af7b5adb96..6396c8d28bc 100644
--- a/src/coredump/coredump-backtrace.c
+++ b/src/coredump/coredump-backtrace.c
@@ -2,6 +2,7 @@
#include
+#include "sd-id128.h"
#include "sd-journal.h"
#include "sd-messages.h"
@@ -32,6 +33,10 @@ int coredump_backtrace(int argc, char *argv[]) {
(void) iovw_replace_string_field(&context.iovw, "MESSAGE_ID=", SD_MESSAGE_BACKTRACE_STR);
+ sd_id128_t id;
+ if (sd_id128_randomize(&id) >= 0)
+ (void) iovw_replace_string_field(&context.iovw, "COREDUMP_ID=", SD_ID128_TO_STRING(id));
+
for (;;) {
r = journal_importer_process_data(&importer);
if (r < 0)
diff --git a/src/coredump/coredump-submit.c b/src/coredump/coredump-submit.c
index 55f8b6e9e19..c0a1dadcca3 100644
--- a/src/coredump/coredump-submit.c
+++ b/src/coredump/coredump-submit.c
@@ -726,6 +726,10 @@ int coredump_submit(const CoredumpConfig *config, CoredumpContext *context) {
(void) iovw_put_string_field(&context->iovw, "MESSAGE=", core_message);
+ sd_id128_t id;
+ if (sd_id128_randomize(&id) >= 0)
+ (void) iovw_put_string_field(&context->iovw, "COREDUMP_ID=", SD_ID128_TO_STRING(id));
+
if (filename)
(void) iovw_put_string_field(&context->iovw, "COREDUMP_FILENAME=", filename);
if (truncated)
diff --git a/src/coredump/coredumpctl-info.c b/src/coredump/coredumpctl-info.c
index 8d98271d2d5..30d455bf262 100644
--- a/src/coredump/coredumpctl-info.c
+++ b/src/coredump/coredumpctl-info.c
@@ -117,7 +117,7 @@ error:
static int print_list(FILE *file, sd_journal *j, Table *t) {
_cleanup_free_ char
- *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
+ *mid = NULL, *cid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
*sgnl = NULL, *exe = NULL, *comm = NULL,
*filename = NULL, *truncated = NULL;
const void *d;
@@ -127,6 +127,7 @@ static int print_list(FILE *file, sd_journal *j, Table *t) {
const char *present = NULL, *color = NULL;
uint64_t size = UINT64_MAX;
bool normal_coredump, has_inline_coredump;
+ sd_id128_t coredump_id = SD_ID128_NULL;
uid_t uid_as_int = UID_INVALID;
gid_t gid_as_int = GID_INVALID;
pid_t pid_as_int = 0;
@@ -137,6 +138,7 @@ static int print_list(FILE *file, sd_journal *j, Table *t) {
SD_JOURNAL_FOREACH_DATA(j, d, l) {
RETRIEVE(d, l, "MESSAGE_ID", mid);
+ RETRIEVE(d, l, "COREDUMP_ID", cid);
RETRIEVE(d, l, "COREDUMP_PID", pid);
RETRIEVE(d, l, "COREDUMP_UID", uid);
RETRIEVE(d, l, "COREDUMP_GID", gid);
@@ -156,6 +158,8 @@ static int print_list(FILE *file, sd_journal *j, Table *t) {
return 0;
}
+ if (cid)
+ (void) sd_id128_from_string(cid, &coredump_id);
(void) parse_uid(uid, &uid_as_int);
(void) parse_gid(gid, &gid_as_int);
(void) parse_pid(pid, &pid_as_int);
@@ -184,6 +188,13 @@ static int print_list(FILE *file, sd_journal *j, Table *t) {
if (STRPTR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
present = "truncated";
+ if (sd_id128_is_null(coredump_id))
+ r = table_add_cell(t, /* ret_cell= */ NULL, TABLE_EMPTY, /* data= */ NULL);
+ else
+ r = table_add_cell(t, /* ret_cell= */ NULL, TABLE_ID128, &coredump_id);
+ if (r < 0)
+ return table_log_add_error(r);
+
r = table_add_many(
t,
TABLE_TIMESTAMP, ts,
@@ -203,6 +214,7 @@ static int print_list(FILE *file, sd_journal *j, Table *t) {
typedef enum CoredumpField {
COREDUMP_FIELD_MID,
+ COREDUMP_FIELD_ID,
COREDUMP_FIELD_PID,
COREDUMP_FIELD_UID,
COREDUMP_FIELD_GID,
@@ -234,6 +246,7 @@ typedef enum CoredumpField {
static const char* const coredump_field_table[_COREDUMP_FIELD_MAX] = {
[COREDUMP_FIELD_MID] = "MESSAGE_ID",
+ [COREDUMP_FIELD_ID] = "COREDUMP_ID",
[COREDUMP_FIELD_PID] = "COREDUMP_PID",
[COREDUMP_FIELD_UID] = "COREDUMP_UID",
[COREDUMP_FIELD_GID] = "COREDUMP_GID",
@@ -265,6 +278,7 @@ static const char* const coredump_field_table[_COREDUMP_FIELD_MAX] = {
typedef struct CoredumpFields {
char *fields[_COREDUMP_FIELD_MAX];
+ sd_id128_t coredump_id;
bool normal_coredump;
const char *storage_state; /* points to a static string, not owned */
const char *storage_color; /* points to a static string, not owned */
@@ -298,6 +312,12 @@ static int coredump_fields_load(sd_journal *j, CoredumpFields *f) {
f->normal_coredump = streq_ptr(f->fields[COREDUMP_FIELD_MID], SD_MESSAGE_COREDUMP_STR);
+ if (f->fields[COREDUMP_FIELD_ID]) {
+ r = sd_id128_from_string(f->fields[COREDUMP_FIELD_ID], &f->coredump_id);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse coredump ID '%s', ignoring: %m", f->fields[COREDUMP_FIELD_ID]);
+ }
+
if (f->fields[COREDUMP_FIELD_FILENAME]) {
r = resolve_filename(arg_root, &f->fields[COREDUMP_FIELD_FILENAME]);
if (r < 0)
@@ -453,6 +473,14 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (arg_full)
table_set_width(table, 0);
+ if (!sd_id128_is_null(f.coredump_id)) {
+ r = table_add_many(table,
+ TABLE_FIELD, "ID",
+ TABLE_ID128, f.coredump_id);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
if (f.fields[COREDUMP_FIELD_COMM])
r = table_add_string_fieldf_full(
table, "PID", TABLE_STRING_WITH_ANSI, "%s%s%s (%s)",
@@ -672,6 +700,7 @@ static int print_info_json(FILE *file, sd_journal *j) {
(void) safe_atou64(f.fields[COREDUMP_FIELD_TIMESTAMP], &ts);
r = sd_json_build(&v, SD_JSON_BUILD_OBJECT(
+ SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(f.coredump_id), "ID", SD_JSON_BUILD_ID128(f.coredump_id)),
SD_JSON_BUILD_PAIR_CONDITION(pid_is_valid(pid_as_int), "PID", SD_JSON_BUILD_UNSIGNED(pid_as_int)),
SD_JSON_BUILD_PAIR_CONDITION(!pid_is_valid(pid_as_int) && !!f.fields[COREDUMP_FIELD_PID], "PID", SD_JSON_BUILD_STRING(f.fields[COREDUMP_FIELD_PID])),
SD_JSON_BUILD_PAIR_CONDITION(pid_is_valid(tid_as_int), "TID", SD_JSON_BUILD_UNSIGNED(tid_as_int)),
diff --git a/src/coredump/coredumpctl-journal.c b/src/coredump/coredumpctl-journal.c
index a2c71a7b844..ac07ce6b609 100644
--- a/src/coredump/coredumpctl-journal.c
+++ b/src/coredump/coredumpctl-journal.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "sd-id128.h"
#include "sd-messages.h"
#include "coredumpctl.h"
@@ -15,11 +16,16 @@
static int add_match(sd_journal *j, const char *match) {
_cleanup_free_ char *p = NULL;
const char *field;
+ char id_str[SD_ID128_STRING_MAX];
+ sd_id128_t id;
int r;
if (strchr(match, '='))
field = NULL;
- else if (is_path(match)) {
+ else if (sd_id128_from_string(match, &id) >= 0) {
+ match = sd_id128_to_string(id, id_str);
+ field = "COREDUMP_ID";
+ } else if (is_path(match)) {
r = path_make_absolute_cwd(match, &p);
if (r < 0)
return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match);
diff --git a/src/coredump/coredumpctl-list.c b/src/coredump/coredumpctl-list.c
index 63cf9db1f84..a3f21a878fd 100644
--- a/src/coredump/coredumpctl-list.c
+++ b/src/coredump/coredumpctl-list.c
@@ -25,17 +25,19 @@ int verb_dump_list(int argc, char *argv[], uintptr_t _data, void *userdata) {
return r;
if (!verb_is_info && !arg_field) {
- t = table_new("time", "pid", "uid", "gid", "sig", "corefile", "exe", "size");
+ t = table_new("id", "time", "pid", "uid", "gid", "sig", "corefile", "exe", "size");
if (!t)
return log_oom();
if (arg_full)
table_set_width(t, 0);
- (void) table_set_align_percent(t, TABLE_HEADER_CELL(1), 100);
+ (void) table_set_ellipsize_percent(t, TABLE_HEADER_CELL(1), 100);
+
(void) table_set_align_percent(t, TABLE_HEADER_CELL(2), 100);
(void) table_set_align_percent(t, TABLE_HEADER_CELL(3), 100);
- (void) table_set_align_percent(t, TABLE_HEADER_CELL(7), 100);
+ (void) table_set_align_percent(t, TABLE_HEADER_CELL(4), 100);
+ (void) table_set_align_percent(t, TABLE_HEADER_CELL(8), 100);
table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
} else if (!sd_json_format_enabled(arg_json_format_flags))
diff --git a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
index cc0e2db01a2..8389a448a6c 100755
--- a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
+++ b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
@@ -199,6 +199,23 @@ coredumpctl info "$CORE_TEST_BIN" | grep "TID:" >/dev/null
# Check the field is queryable in the journal
coredumpctl -F COREDUMP_TID
+# Test cases for Coredump ID
+LATEST_ID=$(coredumpctl list -1 --json=short "$CORE_TEST_BIN" | jq -r '.[0].id')
+[[ -n "$LATEST_ID" ]]
+[[ "$LATEST_ID" != null ]]
+# Test with explicit COREDUMP_ID= field
+coredumpctl list COREDUMP_ID="$LATEST_ID" | grep "$LATEST_ID" >/dev/null
+coredumpctl info COREDUMP_ID="$LATEST_ID" | grep --fixed-strings "ID: $LATEST_ID" >/dev/null
+coredumpctl info --json=short COREDUMP_ID="$LATEST_ID" | jq -e ".ID == \"$LATEST_ID\""
+coredumpctl info --json=pretty COREDUMP_ID="$LATEST_ID" | jq -e ".ID == \"$LATEST_ID\""
+# Test with bare UUID (without COREDUMP_ID= prefix)
+coredumpctl info "$LATEST_ID" | grep --fixed-strings "ID: $LATEST_ID" >/dev/null
+coredumpctl info --json=short "$LATEST_ID" | jq -e ".ID == \"$LATEST_ID\""
+# Test with dashed UUID format
+LATEST_ID_DASHED="${LATEST_ID:0:8}-${LATEST_ID:8:4}-${LATEST_ID:12:4}-${LATEST_ID:16:4}-${LATEST_ID:20:12}"
+coredumpctl info "$LATEST_ID_DASHED" | grep --fixed-strings "ID: $LATEST_ID" >/dev/null
+coredumpctl info --json=short "$LATEST_ID_DASHED" | jq -e ".ID == \"$LATEST_ID\""
+
# If COREDUMP_CODE= is present, check that the expected code is SI_USER (0).
if coredumpctl -F COREDUMP_CODE | grep "^0$" >/dev/null; then
coredumpctl info "$CORE_TEST_BIN" | grep --fixed-strings "Signal: 5 (TRAP) si_code: SI_USER" >/dev/null
@@ -282,6 +299,13 @@ coredumpctl info COREDUMP_HOSTNAME="mymachine"
coredumpctl info COREDUMP_TIMESTAMP=1679509902000000
coredumpctl info COREDUMP_HOSTNAME="youmachine"
coredumpctl info COREDUMP_DUMPABLE="1"
+# Verify that backtrace entries have COREDUMP_ID set
+coredumpctl info --json=short COREDUMP_TIMESTAMP=1679509900000000 | jq -e '.ID != null'
+coredumpctl info --json=short COREDUMP_TIMESTAMP=1679509901000000 | jq -e '.ID != null'
+coredumpctl info --json=short COREDUMP_TIMESTAMP=1679509902000000 | jq -e '.ID != null'
+# Verify that backtrace entries can be queried by ID
+BACKTRACE_ID=$(coredumpctl list -n 1 --json=short COREDUMP_TIMESTAMP=1679509900000000 | jq -r '.[0].id')
+coredumpctl info "$BACKTRACE_ID" | grep --fixed-strings "ID: $BACKTRACE_ID" >/dev/null
# This used to cause a stack overflow
systemd-run -t --property CoredumpFilter=all ls /tmp