diff --git a/contrib/ci/dependencies.xml b/contrib/ci/dependencies.xml index d9175c8f4..54916d6fa 100644 --- a/contrib/ci/dependencies.xml +++ b/contrib/ci/dependencies.xml @@ -70,6 +70,28 @@ + + + json-glib + + + json-glib-devel + + + + (>= 1.1.1) + + + libjson-glib-dev:s390x + + + + + (>= 1.1.1) + + + + colord diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index cb73c6ca5..e24003a59 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -4,6 +4,7 @@ %global libsoup_version 2.51.92 %global colord_version 1.2.12 %global systemd_version 231 +%global json_glib_version 1.1.1 %define alphatag #ALPHATAG# @@ -53,6 +54,7 @@ BuildRequires: gnutls-devel BuildRequires: gnutls-utils BuildRequires: meson BuildRequires: help2man +BuildRequires: json-glib-devel >= %{json_glib_version} %if 0%{?have_uefi} BuildRequires: python3 python3-cairo python3-gobject python3-pillow diff --git a/libfwupd/fwupd-common.c b/libfwupd/fwupd-common.c index 2e4e6e43f..6ab6dac95 100644 --- a/libfwupd/fwupd-common.c +++ b/libfwupd/fwupd-common.c @@ -22,11 +22,14 @@ #include "config.h" #include "fwupd-common-private.h" +#include "fwupd-device.h" #include "fwupd-error.h" +#include "fwupd-release.h" #include #include #include +#include /** * fwupd_checksum_guess_kind: @@ -334,3 +337,171 @@ fwupd_build_machine_id (const gchar *salt, GError **error) g_checksum_update (csum, (const guchar *) buf, (gssize) sz); return g_strdup (g_checksum_get_string (csum)); } + +static void +fwupd_build_history_report_json_metadata_device (JsonBuilder *builder, FwupdDevice *dev) +{ + FwupdRelease *rel = fwupd_device_get_release_default (dev); + GHashTable *metadata = fwupd_release_get_metadata (rel); + g_autoptr(GList) keys = NULL; + + /* add each metadata value */ + keys = g_hash_table_get_keys (metadata); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup (metadata, key); + json_builder_set_member_name (builder, key); + json_builder_add_string_value (builder, value); + } +} + +static void +fwupd_build_history_report_json_device (JsonBuilder *builder, FwupdDevice *dev) +{ + FwupdRelease *rel = fwupd_device_get_release_default (dev); + GPtrArray *checksums; + + /* identify different devices */ + json_builder_set_member_name (builder, "DeviceId"); + json_builder_add_string_value (builder, fwupd_device_get_id (dev)); + + /* identify the firmware used */ + json_builder_set_member_name (builder, "Checksum"); + checksums = fwupd_release_get_checksums (rel); + json_builder_add_string_value (builder, fwupd_checksum_get_by_kind (checksums, G_CHECKSUM_SHA1)); + + /* set the error state of the report */ + json_builder_set_member_name (builder, "UpdateState"); + json_builder_add_int_value (builder, fwupd_device_get_update_state (dev)); + if (fwupd_device_get_update_error (dev) != NULL) { + json_builder_set_member_name (builder, "UpdateError"); + json_builder_add_string_value (builder, fwupd_device_get_update_error (dev)); + } + + /* map back to the dev type on the LVFS */ + json_builder_set_member_name (builder, "Guid"); + json_builder_add_string_value (builder, fwupd_device_get_guid_default (dev)); + + json_builder_set_member_name (builder, "Plugin"); + json_builder_add_string_value (builder, fwupd_device_get_plugin (dev)); + + /* report what we're trying to update *from* and *to* */ + json_builder_set_member_name (builder, "VersionOld"); + json_builder_add_string_value (builder, fwupd_device_get_version (dev)); + json_builder_set_member_name (builder, "VersionNew"); + json_builder_add_string_value (builder, fwupd_release_get_version (rel)); + + /* to know the state of the dev we're trying to update */ + json_builder_set_member_name (builder, "Flags"); + json_builder_add_int_value (builder, fwupd_device_get_flags (dev)); + + /* to know when the update tried to happen, and how soon after boot */ + json_builder_set_member_name (builder, "Created"); + json_builder_add_int_value (builder, fwupd_device_get_created (dev)); + json_builder_set_member_name (builder, "Modified"); + json_builder_add_int_value (builder, fwupd_device_get_modified (dev)); + + /* add saved metadata to the report */ + json_builder_set_member_name (builder, "Metadata"); + json_builder_begin_object (builder); + fwupd_build_history_report_json_metadata_device (builder, dev); + json_builder_end_object (builder); +} + +static gboolean +fwupd_build_history_report_json_metadata (JsonBuilder *builder, GError **error) +{ + g_autoptr(GHashTable) hash = NULL; + struct { + const gchar *key; + const gchar *val; + } distro_kv[] = { + { "ID", "DistroId" }, + { "VERSION_ID", "DistroVersion" }, + { "VARIANT_ID", "DistroVariant" }, + { NULL, NULL } + }; + + /* get all required os-release keys */ + hash = fwupd_build_distro_hash (error); + if (hash == NULL) + return FALSE; + for (guint i = 0; distro_kv[i].key != NULL; i++) { + const gchar *tmp = g_hash_table_lookup (hash, distro_kv[i].key); + if (tmp != NULL) { + json_builder_set_member_name (builder, distro_kv[i].val); + json_builder_add_string_value (builder, tmp); + } + } + return TRUE; +} + +/** + * fwupd_build_history_report_json: + * @devices: (element-type FwupdDevice): devices + * @error: A #GError or %NULL + * + * Builds a JSON report for the list of devices. No filtering is done on the + * @devices array, and it is expected that the caller will filter to something + * sane, e.g. %FWUPD_DEVICE_FLAG_REPORTED at the bare minimum. + * + * Returns: a string, or %NULL if the ID is not present + * + * Since: 1.0.4 + **/ +gchar * +fwupd_build_history_report_json (GPtrArray *devices, GError **error) +{ + gchar *data; + g_autofree gchar *machine_id = NULL; + g_autoptr(JsonBuilder) builder = NULL; + g_autoptr(JsonGenerator) json_generator = NULL; + g_autoptr(JsonNode) json_root = NULL; + + /* get a hash that represents the machine */ + machine_id = fwupd_build_machine_id ("fwupd", error); + if (machine_id == NULL) + return FALSE; + + /* create header */ + builder = json_builder_new (); + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "ReportVersion"); + json_builder_add_int_value (builder, 2); + json_builder_set_member_name (builder, "MachineId"); + json_builder_add_string_value (builder, machine_id); + + /* this is system metadata not stored in the database */ + json_builder_set_member_name (builder, "Metadata"); + json_builder_begin_object (builder); + if (!fwupd_build_history_report_json_metadata (builder, error)) + return FALSE; + json_builder_end_object (builder); + + /* add each device */ + json_builder_set_member_name (builder, "Reports"); + json_builder_begin_array (builder); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index (devices, i); + json_builder_begin_object (builder); + fwupd_build_history_report_json_device (builder, dev); + json_builder_end_object (builder); + } + json_builder_end_array (builder); + json_builder_end_object (builder); + + /* export as a string */ + json_root = json_builder_get_root (builder); + json_generator = json_generator_new (); + json_generator_set_pretty (json_generator, TRUE); + json_generator_set_root (json_generator, json_root); + data = json_generator_to_data (json_generator, NULL); + if (data == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to convert to JSON string"); + return NULL; + } + return data; +} diff --git a/libfwupd/fwupd-common.h b/libfwupd/fwupd-common.h index 51e00a5ac..192ec58aa 100644 --- a/libfwupd/fwupd-common.h +++ b/libfwupd/fwupd-common.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * - * Copyright (C) 2015-2017 Richard Hughes + * Copyright (C) 2015-2018 Richard Hughes * * Licensed under the GNU Lesser General Public License Version 2.1 * @@ -38,5 +38,7 @@ gchar *fwupd_build_user_agent (const gchar *package_name, const gchar *package_version); gchar *fwupd_build_machine_id (const gchar *salt, GError **error); +gchar *fwupd_build_history_report_json (GPtrArray *devices, + GError **error); #endif /* __FWUPD_COMMON_H */ diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map index fb6befa60..1ab99e8cc 100644 --- a/libfwupd/fwupd.map +++ b/libfwupd/fwupd.map @@ -224,6 +224,7 @@ LIBFWUPD_1.0.3 { LIBFWUPD_1.0.4 { global: + fwupd_build_history_report_json; fwupd_build_machine_id; fwupd_client_get_history; fwupd_client_modify_device; diff --git a/libfwupd/meson.build b/libfwupd/meson.build index f4252292e..48493fece 100644 --- a/libfwupd/meson.build +++ b/libfwupd/meson.build @@ -45,6 +45,7 @@ fwupd = shared_library( dependencies : [ giounix, soup, + libjsonglib, ], c_args : [ cargs, diff --git a/meson.build b/meson.build index 79212a471..2d957e680 100644 --- a/meson.build +++ b/meson.build @@ -145,6 +145,7 @@ appstream_glib = dependency('appstream-glib', version : '>= 0.6.13') gusb = dependency('gusb', version : '>= 0.2.9') sqlite = dependency('sqlite3') libarchive = dependency('libarchive') +libjsonglib = dependency('json-glib-1.0', version : '>= 1.1.1') if meson.version().version_compare('>0.41.0') valgrind = dependency('valgrind', required: false) else