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.
This commit is contained in:
Luca Boccassi 2021-04-06 18:24:01 +01:00
parent 95f7180773
commit c546154a44
4 changed files with 85 additions and 6 deletions

View file

@ -352,6 +352,20 @@ flags: ...
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>COREDUMP_PKGMETA_PACKAGE=</varname></term>
<term><varname>COREDUMP_PKGMETA_PACKAGEVERSION=</varname></term>
<term><varname>COREDUMP_PKGMETA_JSON=</varname></term>
<listitem><para>If the executable contained .package metadata ELF notes, they will be
parsed and attached. The <varname>package</varname> and <varname>packageVersion</varname>
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
<ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">the coredump metadata spec</ulink>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MESSAGE=</varname></term>

View file

@ -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) {

View file

@ -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)

View file

@ -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);