From c546154a4448ddf9c22363c3f491313aeadd117e Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 6 Apr 2021 18:24:01 +0100 Subject: [PATCH] coredump: parse and append package metadata to journal message Append 'package' and 'packageVersion' to the journal as discrete fields COREDUMP_PKGMETA_PACKAGE and COREDUMP_PKGMETA_PACKAGEVERSION respectively, and the full json blurb as COREDUMP_PKGMETA_JSON. --- man/systemd-coredump.xml | 14 ++++++++ src/coredump/coredump.c | 67 +++++++++++++++++++++++++++++++++++++-- src/coredump/stacktrace.c | 8 +++-- src/coredump/stacktrace.h | 2 +- 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml index 117b9eb6d0..d994a21d9b 100644 --- a/man/systemd-coredump.xml +++ b/man/systemd-coredump.xml @@ -352,6 +352,20 @@ flags: ... + + COREDUMP_PKGMETA_PACKAGE= + COREDUMP_PKGMETA_PACKAGEVERSION= + COREDUMP_PKGMETA_JSON= + + If the executable contained .package metadata ELF notes, they will be + parsed and attached. The package and packageVersion + of the 'main' ELF module (ie: the excutable) will be appended individually. The + JSON-formatted content of all modules will be appended as a single JSON object, each with + the module name as the key. For more information about this metadata format and content, see + the coredump metadata spec. + + + MESSAGE= diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index b862a6d3d3..be813f57f1 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -703,14 +703,16 @@ static int submit_coredump( struct iovec_wrapper *iovw, int input_fd) { + _cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL; _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *filename = NULL, *coredump_data = NULL; _cleanup_free_ char *stacktrace = NULL; char *core_message; + const char *module_name; uint64_t coredump_size = UINT64_MAX; bool truncated = false; + JsonVariant *module_json; int r; - assert(context); assert(iovw); assert(input_fd >= 0); @@ -757,7 +759,7 @@ static int submit_coredump( "than %"PRIu64" (the configured maximum)", coredump_size, arg_process_size_max); } else - coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace); + coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata); #endif log: @@ -781,6 +783,67 @@ log: if (truncated) (void) iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1"); + /* If we managed to parse any ELF metadata (build-id, ELF package meta), + * attach it as journal metadata. */ + if (json_metadata) { + _cleanup_free_ char *formatted_json = NULL; + + r = json_variant_format(json_metadata, 0, &formatted_json); + if (r < 0) + return log_error_errno(r, "Failed to format JSON package metadata: %m"); + + (void) iovw_put_string_field(iovw, "COREDUMP_PKGMETA_JSON=", formatted_json); + } + + JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) { + _cleanup_free_ char *module_basename = NULL, *exe_basename = NULL; + const char *key; + JsonVariant *w; + + /* The module name, most likely parsed from the ELF core file, + * sometimes contains the full path and sometimes does not. */ + r = path_extract_filename(module_name, &module_basename); + if (r < 0) + return log_error_errno(r, "Failed to parse module basename: %m"); + r = path_extract_filename(context->meta[META_EXE], &exe_basename); + if (r < 0) + return log_error_errno(r, "Failed to parse executable basename: %m"); + + /* We only add structured fields for the 'main' ELF module */ + if (!streq(module_basename, exe_basename)) + continue; + + /* Cannot nest two JSON_VARIANT_OBJECT_FOREACH as they define the same + * iterator variable '_state' */ + for (struct json_variant_foreach_state _state2 = { (module_json), 0 }; \ + json_variant_is_object(_state2.variant) && \ + _state2.idx < json_variant_elements(_state2.variant) && \ + ({ key = json_variant_string(json_variant_by_index(_state2.variant, _state2.idx)); \ + w = json_variant_by_index(_state2.variant, _state2.idx + 1); \ + true; }); \ + _state2.idx += 2) { + _cleanup_free_ char *metadata_id = NULL, *key_upper = NULL; + + if (!json_variant_is_string(w)) + continue; + + if (!STR_IN_SET(key, "package", "packageVersion")) + continue; + + /* Journal metadata field names need to be upper case */ + key_upper = strdup(key); + if (!key_upper) + return log_oom(); + key_upper = ascii_strupper(key_upper); + + metadata_id = strjoin("COREDUMP_PKGMETA_", key_upper, "="); + if (!metadata_id) + return log_oom(); + + (void) iovw_put_string_field(iovw, metadata_id, json_variant_string(w)); + } + } + /* Optionally store the entire coredump in the journal */ if (arg_storage == COREDUMP_STORAGE_JOURNAL) { if (coredump_size <= arg_journal_size_max) { diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index ac404451d6..c16f12f09b 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -324,7 +324,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, return DWARF_CB_OK; } -static int parse_core(int fd, const char *executable, char **ret) { +static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) { static const Dwfl_Callbacks callbacks = { .find_elf = dwfl_build_id_find_elf, @@ -394,6 +394,8 @@ static int parse_core(int fd, const char *executable, char **ret) { c.f = safe_fclose(c.f); *ret = TAKE_PTR(buf); + if (ret_package_metadata) + *ret_package_metadata = TAKE_PTR(package_metadata); r = 0; @@ -411,10 +413,10 @@ finish: return r; } -void coredump_parse_core(int fd, const char *executable, char **ret) { +void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) { int r; - r = parse_core(fd, executable, ret); + r = parse_core(fd, executable, ret, ret_package_metadata); if (r == -EINVAL) log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else if (r < 0) diff --git a/src/coredump/stacktrace.h b/src/coredump/stacktrace.h index daeb38bf38..5039b934dd 100644 --- a/src/coredump/stacktrace.h +++ b/src/coredump/stacktrace.h @@ -3,4 +3,4 @@ #include "json.h" -void coredump_parse_core(int fd, const char *executable, char **ret); +void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata);