]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
coredump: parse and append package metadata to journal message
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 6 Apr 2021 17:24:01 +0000 (18:24 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 6 Apr 2021 22:12:51 +0000 (23:12 +0100)
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
src/coredump/coredump.c
src/coredump/stacktrace.c
src/coredump/stacktrace.h

index 117b9eb6d08711e8d437d40094ea273d07749ab0..d994a21d9b5bef33d09cad3deafb08ed9170202b 100644 (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>
 
index b862a6d3d3d9554300bc6c9e50ed14c712dfbb07..be813f57f19ae78743103e05644286cdbe04f9fc 100644 (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) {
index ac404451d6311fe70329e97b3b6822ab5569f3b9..c16f12f09b46c4175a9bbcc2f2eab6045b78c14d 100644 (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)
index daeb38bf38ec961dfbbde9e8afe302a1953d1a7b..5039b934dda4ca30a51515f8bcb067b1b4f503fe 100644 (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);