mirror of
https://git.proxmox.com/git/fwupd
synced 2025-06-04 16:05:11 +00:00

Although glibc calls this when memory gets low automatically, we can invoke it manually when we've loaded all plugins and coldplugged all devices. This has no effect when using proper heap benchmarks like massif, but random Reddit users just looking at the RSS column in an 'easy-to-use memory profiler' will see a much lower numbers. Where this feature might actually be useful is after a libxmlb metadata rebuild where we dirtied a lot of heap just parsing XML.
2055 lines
64 KiB
C
2055 lines
64 KiB
C
/*
|
|
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuMain"
|
|
|
|
#include "config.h"
|
|
|
|
#include <xmlb.h>
|
|
#include <fwupd.h>
|
|
#include <gio/gunixfdlist.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib-unix.h>
|
|
#include <locale.h>
|
|
#include <malloc.h>
|
|
#ifdef HAVE_POLKIT
|
|
#include <polkit/polkit.h>
|
|
#endif
|
|
#ifdef HAVE_SYSTEMD
|
|
#include <systemd/sd-daemon.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <jcat.h>
|
|
|
|
#include "fwupd-device-private.h"
|
|
#include "fwupd-plugin-private.h"
|
|
#include "fwupd-security-attr-private.h"
|
|
#include "fwupd-release-private.h"
|
|
#include "fwupd-remote-private.h"
|
|
#include "fwupd-resources.h"
|
|
|
|
#include "fu-common.h"
|
|
#include "fu-debug.h"
|
|
#include "fu-device-private.h"
|
|
#include "fu-engine.h"
|
|
#include "fu-install-task.h"
|
|
#include "fu-security-attrs-private.h"
|
|
|
|
#ifdef HAVE_POLKIT
|
|
#ifndef HAVE_POLKIT_0_114
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
|
|
#pragma clang diagnostic pop
|
|
#endif /* HAVE_POLKIT_0_114 */
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
typedef enum {
|
|
FU_MAIN_MACHINE_KIND_PHYSICAL,
|
|
FU_MAIN_MACHINE_KIND_VIRTUAL,
|
|
FU_MAIN_MACHINE_KIND_CONTAINER,
|
|
} FuMainMachineKind;
|
|
|
|
typedef struct {
|
|
GDBusConnection *connection;
|
|
GDBusNodeInfo *introspection_daemon;
|
|
GDBusProxy *proxy_uid;
|
|
GMainLoop *loop;
|
|
GFileMonitor *argv0_monitor;
|
|
GHashTable *sender_features; /* sender:FwupdFeatureFlags */
|
|
#if GLIB_CHECK_VERSION(2,63,3)
|
|
GMemoryMonitor *memory_monitor;
|
|
#endif
|
|
#ifdef HAVE_POLKIT
|
|
PolkitAuthority *authority;
|
|
#endif
|
|
guint owner_id;
|
|
FuEngine *engine;
|
|
gboolean update_in_progress;
|
|
gboolean pending_sigterm;
|
|
FuMainMachineKind machine_kind;
|
|
} FuMainPrivate;
|
|
|
|
static gboolean
|
|
fu_main_sigterm_cb (gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
if (!priv->update_in_progress) {
|
|
g_main_loop_quit (priv->loop);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
g_warning ("Received SIGTERM during a firmware update, ignoring");
|
|
priv->pending_sigterm = TRUE;
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_changed_cb (FuEngine *engine, FuMainPrivate *priv)
|
|
{
|
|
/* not yet connected */
|
|
if (priv->connection == NULL)
|
|
return;
|
|
g_dbus_connection_emit_signal (priv->connection,
|
|
NULL,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"Changed",
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_device_added_cb (FuEngine *engine,
|
|
FuDevice *device,
|
|
FuMainPrivate *priv)
|
|
{
|
|
GVariant *val;
|
|
|
|
/* not yet connected */
|
|
if (priv->connection == NULL)
|
|
return;
|
|
val = fwupd_device_to_variant (FWUPD_DEVICE (device));
|
|
g_dbus_connection_emit_signal (priv->connection,
|
|
NULL,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"DeviceAdded",
|
|
g_variant_new_tuple (&val, 1), NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_device_removed_cb (FuEngine *engine,
|
|
FuDevice *device,
|
|
FuMainPrivate *priv)
|
|
{
|
|
GVariant *val;
|
|
|
|
/* not yet connected */
|
|
if (priv->connection == NULL)
|
|
return;
|
|
val = fwupd_device_to_variant (FWUPD_DEVICE (device));
|
|
g_dbus_connection_emit_signal (priv->connection,
|
|
NULL,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"DeviceRemoved",
|
|
g_variant_new_tuple (&val, 1), NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_device_changed_cb (FuEngine *engine,
|
|
FuDevice *device,
|
|
FuMainPrivate *priv)
|
|
{
|
|
GVariant *val;
|
|
|
|
/* not yet connected */
|
|
if (priv->connection == NULL)
|
|
return;
|
|
val = fwupd_device_to_variant (FWUPD_DEVICE (device));
|
|
g_dbus_connection_emit_signal (priv->connection,
|
|
NULL,
|
|
FWUPD_DBUS_PATH,
|
|
FWUPD_DBUS_INTERFACE,
|
|
"DeviceChanged",
|
|
g_variant_new_tuple (&val, 1), NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_emit_property_changed (FuMainPrivate *priv,
|
|
const gchar *property_name,
|
|
GVariant *property_value)
|
|
{
|
|
GVariantBuilder builder;
|
|
GVariantBuilder invalidated_builder;
|
|
|
|
/* not yet connected */
|
|
if (priv->connection == NULL) {
|
|
g_variant_unref (g_variant_ref_sink (property_value));
|
|
return;
|
|
}
|
|
|
|
/* build the dict */
|
|
g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
|
g_variant_builder_add (&builder,
|
|
"{sv}",
|
|
property_name,
|
|
property_value);
|
|
g_dbus_connection_emit_signal (priv->connection,
|
|
NULL,
|
|
FWUPD_DBUS_PATH,
|
|
"org.freedesktop.DBus.Properties",
|
|
"PropertiesChanged",
|
|
g_variant_new ("(sa{sv}as)",
|
|
FWUPD_DBUS_INTERFACE,
|
|
&builder,
|
|
&invalidated_builder),
|
|
NULL);
|
|
g_variant_builder_clear (&builder);
|
|
g_variant_builder_clear (&invalidated_builder);
|
|
}
|
|
|
|
static void
|
|
fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
|
|
{
|
|
g_debug ("Emitting PropertyChanged('Status'='%s')",
|
|
fwupd_status_to_string (status));
|
|
fu_main_emit_property_changed (priv, "Status",
|
|
g_variant_new_uint32 (status));
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_status_changed_cb (FuEngine *engine,
|
|
FwupdStatus status,
|
|
FuMainPrivate *priv)
|
|
{
|
|
fu_main_set_status (priv, status);
|
|
|
|
/* engine has gone idle */
|
|
if (status == FWUPD_STATUS_SHUTDOWN)
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
|
|
static void
|
|
fu_main_engine_percentage_changed_cb (FuEngine *engine,
|
|
guint percentage,
|
|
FuMainPrivate *priv)
|
|
{
|
|
g_debug ("Emitting PropertyChanged('Percentage'='%u%%')", percentage);
|
|
fu_main_emit_property_changed (priv, "Percentage",
|
|
g_variant_new_uint32 (percentage));
|
|
}
|
|
|
|
static FuEngineRequest *
|
|
fu_main_create_request (FuMainPrivate *priv, const gchar *sender, GError **error)
|
|
{
|
|
FwupdFeatureFlags *feature_flags;
|
|
FwupdDeviceFlags device_flags = FWUPD_DEVICE_FLAG_NONE;
|
|
uid_t calling_uid = 0;
|
|
g_autoptr(FuEngineRequest) request = fu_engine_request_new ();
|
|
g_autoptr(GVariant) value = NULL;
|
|
|
|
g_return_val_if_fail (sender != NULL, NULL);
|
|
|
|
/* did the client set the list of supported feature */
|
|
feature_flags = g_hash_table_lookup (priv->sender_features, sender);
|
|
if (feature_flags != NULL)
|
|
fu_engine_request_set_feature_flags (request, *feature_flags);
|
|
|
|
/* are we root and therefore trusted? */
|
|
value = g_dbus_proxy_call_sync (priv->proxy_uid,
|
|
"GetConnectionUnixUser",
|
|
g_variant_new ("(s)", sender),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
2000,
|
|
NULL,
|
|
error);
|
|
if (value == NULL) {
|
|
g_prefix_error (error, "failed to read user id of caller: ");
|
|
return NULL;
|
|
}
|
|
g_variant_get (value, "(u)", &calling_uid);
|
|
if (calling_uid == 0)
|
|
device_flags |= FWUPD_DEVICE_FLAG_TRUSTED;
|
|
fu_engine_request_set_device_flags (request, device_flags);
|
|
|
|
/* success */
|
|
return g_steal_pointer (&request);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_device_array_to_variant (FuMainPrivate *priv, FuEngineRequest *request,
|
|
GPtrArray *devices, GError **error)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_return_val_if_fail (devices->len > 0, NULL);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
|
|
for (guint i = 0; i < devices->len; i++) {
|
|
FuDevice *device = g_ptr_array_index (devices, i);
|
|
GVariant *tmp = fwupd_device_to_variant_full (FWUPD_DEVICE (device),
|
|
fu_engine_request_get_device_flags (request));
|
|
g_variant_builder_add_value (&builder, tmp);
|
|
}
|
|
return g_variant_new ("(aa{sv})", &builder);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_plugin_array_to_variant (GPtrArray *plugins)
|
|
{
|
|
GVariantBuilder builder;
|
|
|
|
g_return_val_if_fail (plugins->len > 0, NULL);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
|
|
for (guint i = 0; i < plugins->len; i++) {
|
|
FuDevice *plugin = g_ptr_array_index (plugins, i);
|
|
GVariant *tmp = fwupd_plugin_to_variant (FWUPD_PLUGIN (plugin));
|
|
g_variant_builder_add_value (&builder, tmp);
|
|
}
|
|
return g_variant_new ("(aa{sv})", &builder);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_release_array_to_variant (GPtrArray *results)
|
|
{
|
|
GVariantBuilder builder;
|
|
g_return_val_if_fail (results->len > 0, NULL);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
for (guint i = 0; i < results->len; i++) {
|
|
FwupdRelease *rel = g_ptr_array_index (results, i);
|
|
GVariant *tmp = fwupd_release_to_variant (rel);
|
|
g_variant_builder_add_value (&builder, tmp);
|
|
}
|
|
return g_variant_new ("(aa{sv})", &builder);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_remote_array_to_variant (GPtrArray *remotes)
|
|
{
|
|
GVariantBuilder builder;
|
|
g_return_val_if_fail (remotes->len > 0, NULL);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
for (guint i = 0; i < remotes->len; i++) {
|
|
FwupdRemote *remote = g_ptr_array_index (remotes, i);
|
|
GVariant *tmp = fwupd_remote_to_variant (remote);
|
|
g_variant_builder_add_value (&builder, tmp);
|
|
}
|
|
return g_variant_new ("(aa{sv})", &builder);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_result_array_to_variant (GPtrArray *results)
|
|
{
|
|
GVariantBuilder builder;
|
|
g_return_val_if_fail (results->len > 0, NULL);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
|
for (guint i = 0; i < results->len; i++) {
|
|
FwupdDevice *result = g_ptr_array_index (results, i);
|
|
GVariant *tmp = fwupd_device_to_variant (result);
|
|
g_variant_builder_add_value (&builder, tmp);
|
|
}
|
|
return g_variant_new ("(aa{sv})", &builder);
|
|
}
|
|
|
|
typedef struct {
|
|
GDBusMethodInvocation *invocation;
|
|
FuEngineRequest *request;
|
|
#ifdef HAVE_POLKIT
|
|
PolkitSubject *subject;
|
|
#endif
|
|
GPtrArray *install_tasks;
|
|
GPtrArray *action_ids;
|
|
GPtrArray *checksums;
|
|
guint64 flags;
|
|
GBytes *blob_cab;
|
|
FuMainPrivate *priv;
|
|
gchar *device_id;
|
|
gchar *remote_id;
|
|
gchar *key;
|
|
gchar *value;
|
|
XbSilo *silo;
|
|
} FuMainAuthHelper;
|
|
|
|
static void
|
|
fu_main_auth_helper_free (FuMainAuthHelper *helper)
|
|
{
|
|
if (helper->blob_cab != NULL)
|
|
g_bytes_unref (helper->blob_cab);
|
|
#ifdef HAVE_POLKIT
|
|
if (helper->subject != NULL)
|
|
g_object_unref (helper->subject);
|
|
#endif
|
|
if (helper->silo != NULL)
|
|
g_object_unref (helper->silo);
|
|
if (helper->request != NULL)
|
|
g_object_unref (helper->request);
|
|
if (helper->install_tasks != NULL)
|
|
g_ptr_array_unref (helper->install_tasks);
|
|
if (helper->action_ids != NULL)
|
|
g_ptr_array_unref (helper->action_ids);
|
|
if (helper->checksums != NULL)
|
|
g_ptr_array_unref (helper->checksums);
|
|
g_free (helper->device_id);
|
|
g_free (helper->remote_id);
|
|
g_free (helper->key);
|
|
g_free (helper->value);
|
|
g_object_unref (helper->invocation);
|
|
g_free (helper);
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_main_auth_helper_free)
|
|
#pragma clang diagnostic pop
|
|
|
|
#ifdef HAVE_POLKIT
|
|
/* error may or may not already have been set */
|
|
static gboolean
|
|
fu_main_authorization_is_valid (PolkitAuthorizationResult *auth, GError **error)
|
|
{
|
|
/* failed */
|
|
if (auth == NULL) {
|
|
g_autofree gchar *message = g_strdup ((*error)->message);
|
|
g_clear_error (error);
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_AUTH_FAILED,
|
|
"Could not check for auth: %s", message);
|
|
return FALSE;
|
|
}
|
|
|
|
/* did not auth */
|
|
if (!polkit_authorization_result_get_is_authorized (auth)) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_AUTH_FAILED,
|
|
"Failed to obtain auth");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
#else
|
|
static gboolean
|
|
fu_main_authorization_is_trusted (FuEngineRequest *request, GError **error)
|
|
{
|
|
FwupdDeviceFlags flags = fu_engine_request_get_device_flags (request);
|
|
if ((flags & FWUPD_DEVICE_FLAG_TRUSTED) == 0) {
|
|
g_set_error_literal (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_AUTH_FAILED,
|
|
"permission denied: untrusted client process");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
static void
|
|
fu_main_authorize_unlock_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* authenticated */
|
|
if (!fu_engine_unlock (helper->priv->engine, helper->device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* success */
|
|
for (guint i = 0; i < helper->checksums->len; i++) {
|
|
const gchar *csum = g_ptr_array_index (helper->checksums, i);
|
|
fu_engine_add_approved_firmware (helper->priv->engine, csum);
|
|
}
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_set_blocked_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* success */
|
|
if (!fu_engine_set_blocked_firmware (helper->priv->engine, helper->checksums, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autofree gchar *sig = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* authenticated */
|
|
sig = fu_engine_self_sign (helper->priv->engine, helper->value, helper->flags, &error);
|
|
if (sig == NULL) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, g_variant_new ("(s)", sig));
|
|
}
|
|
|
|
static void
|
|
fu_main_modify_config_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
if (!fu_engine_modify_config (helper->priv->engine, helper->key, helper->value, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_activate_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* authenticated */
|
|
if (!fu_engine_activate (helper->priv->engine, helper->device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* authenticated */
|
|
if (!fu_engine_verify_update (helper->priv->engine, helper->device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void
|
|
fu_main_authorize_modify_remote_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#else
|
|
if (!fu_main_authorization_is_trusted (helper->request, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* authenticated */
|
|
if (!fu_engine_modify_remote (helper->priv->engine,
|
|
helper->remote_id,
|
|
helper->key,
|
|
helper->value,
|
|
&error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
static void fu_main_authorize_install_queue (FuMainAuthHelper *helper);
|
|
|
|
#ifdef HAVE_POLKIT
|
|
static void
|
|
fu_main_authorize_install_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|
{
|
|
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(PolkitAuthorizationResult) auth = NULL;
|
|
|
|
/* get result */
|
|
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
|
|
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
|
|
res, &error);
|
|
if (!fu_main_authorization_is_valid (auth, &error)) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* do the next authentication action ID */
|
|
fu_main_authorize_install_queue (g_steal_pointer (&helper));
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
static void
|
|
fu_main_authorize_install_queue (FuMainAuthHelper *helper_ref)
|
|
{
|
|
FuMainPrivate *priv = helper_ref->priv;
|
|
g_autoptr(FuMainAuthHelper) helper = helper_ref;
|
|
g_autoptr(GError) error = NULL;
|
|
gboolean ret;
|
|
|
|
#ifdef HAVE_POLKIT
|
|
/* still more things to to authenticate */
|
|
if (helper->action_ids->len > 0) {
|
|
g_autofree gchar *action_id = g_strdup (g_ptr_array_index (helper->action_ids, 0));
|
|
g_autoptr(PolkitSubject) subject = g_object_ref (helper->subject);
|
|
g_ptr_array_remove_index (helper->action_ids, 0);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
action_id, NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_install_cb,
|
|
g_steal_pointer (&helper));
|
|
return;
|
|
}
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
/* all authenticated, so install all the things */
|
|
priv->update_in_progress = TRUE;
|
|
ret = fu_engine_install_tasks (helper->priv->engine,
|
|
helper->request,
|
|
helper->install_tasks,
|
|
helper->blob_cab,
|
|
helper->flags,
|
|
&error);
|
|
priv->update_in_progress = FALSE;
|
|
if (priv->pending_sigterm)
|
|
g_main_loop_quit (priv->loop);
|
|
if (!ret) {
|
|
g_dbus_method_invocation_return_gerror (helper->invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* success */
|
|
g_dbus_method_invocation_return_value (helper->invocation, NULL);
|
|
}
|
|
|
|
#if !GLIB_CHECK_VERSION(2,54,0)
|
|
static gboolean
|
|
g_ptr_array_find (GPtrArray *haystack, gconstpointer needle, guint *index_)
|
|
{
|
|
for (guint i = 0; i < haystack->len; i++) {
|
|
gconstpointer *tmp = g_ptr_array_index (haystack, i);
|
|
if (tmp == needle) {
|
|
if (index_ != NULL) {
|
|
*index_ = i;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static gint
|
|
fu_main_install_task_sort_cb (gconstpointer a, gconstpointer b)
|
|
{
|
|
FuInstallTask *task_a = *((FuInstallTask **) a);
|
|
FuInstallTask *task_b = *((FuInstallTask **) b);
|
|
return fu_install_task_compare (task_a, task_b);
|
|
}
|
|
|
|
static GPtrArray *
|
|
fu_main_get_device_family (FuMainAuthHelper *helper, GError **error)
|
|
{
|
|
FuDevice *parent;
|
|
GPtrArray *children;
|
|
g_autoptr(FuDevice) device = NULL;
|
|
g_autoptr(GPtrArray) devices_possible = NULL;
|
|
|
|
/* get the device */
|
|
device = fu_engine_get_device (helper->priv->engine, helper->device_id, error);
|
|
if (device == NULL)
|
|
return NULL;
|
|
|
|
/* device itself */
|
|
devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
g_ptr_array_add (devices_possible, g_object_ref (device));
|
|
|
|
/* add device children */
|
|
children = fu_device_get_children (device);
|
|
for (guint i = 0; i < children->len; i++) {
|
|
FuDevice *child = g_ptr_array_index (children, i);
|
|
g_ptr_array_add (devices_possible, g_object_ref (child));
|
|
}
|
|
|
|
/* add parent and siblings, not including the device itself */
|
|
parent = fu_device_get_parent (device);
|
|
if (parent != NULL) {
|
|
GPtrArray *siblings = fu_device_get_children (parent);
|
|
g_ptr_array_add (devices_possible, g_object_ref (parent));
|
|
for (guint i = 0; i < siblings->len; i++) {
|
|
FuDevice *sibling = g_ptr_array_index (siblings, i);
|
|
if (sibling == device)
|
|
continue;
|
|
g_ptr_array_add (devices_possible, g_object_ref (sibling));
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
return g_steal_pointer (&devices_possible);
|
|
}
|
|
|
|
static gboolean
|
|
fu_main_install_with_helper (FuMainAuthHelper *helper_ref, GError **error)
|
|
{
|
|
FuMainPrivate *priv = helper_ref->priv;
|
|
g_autoptr(FuMainAuthHelper) helper = helper_ref;
|
|
g_autoptr(GPtrArray) components = NULL;
|
|
g_autoptr(GPtrArray) devices_possible = NULL;
|
|
g_autoptr(GPtrArray) errors = NULL;
|
|
|
|
/* get a list of devices that in some way match the device_id */
|
|
if (g_strcmp0 (helper->device_id, FWUPD_DEVICE_ID_ANY) == 0) {
|
|
devices_possible = fu_engine_get_devices (priv->engine, error);
|
|
if (devices_possible == NULL)
|
|
return FALSE;
|
|
} else {
|
|
devices_possible = fu_main_get_device_family (helper, error);
|
|
if (devices_possible == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
/* parse silo */
|
|
helper->silo = fu_engine_get_silo_from_blob (priv->engine,
|
|
helper->blob_cab,
|
|
error);
|
|
if (helper->silo == NULL)
|
|
return FALSE;
|
|
|
|
/* for each component in the silo */
|
|
components = xb_silo_query (helper->silo,
|
|
"components/component[@type='firmware']",
|
|
0, error);
|
|
if (components == NULL)
|
|
return FALSE;
|
|
helper->action_ids = g_ptr_array_new_with_free_func (g_free);
|
|
helper->install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
|
errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
|
|
for (guint i = 0; i < components->len; i++) {
|
|
XbNode *component = g_ptr_array_index (components, i);
|
|
|
|
/* do any devices pass the requirements */
|
|
for (guint j = 0; j < devices_possible->len; j++) {
|
|
FuDevice *device = g_ptr_array_index (devices_possible, j);
|
|
const gchar *action_id;
|
|
g_autoptr(FuInstallTask) task = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
|
|
/* is this component valid for the device */
|
|
task = fu_install_task_new (device, component);
|
|
if (!fu_engine_check_requirements (priv->engine,
|
|
helper->request,
|
|
task,
|
|
helper->flags | FWUPD_INSTALL_FLAG_FORCE,
|
|
&error_local)) {
|
|
if (!g_error_matches (error_local,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_FOUND)) {
|
|
g_debug ("first pass requirement on %s:%s failed: %s",
|
|
fu_device_get_id (device),
|
|
xb_node_query_text (component, "id", NULL),
|
|
error_local->message);
|
|
}
|
|
g_ptr_array_add (errors, g_steal_pointer (&error_local));
|
|
continue;
|
|
}
|
|
|
|
/* make a second pass using possibly updated version format now */
|
|
fu_engine_md_refresh_device_from_component (priv->engine, device, component);
|
|
if (!fu_engine_check_requirements (priv->engine,
|
|
helper->request,
|
|
task,
|
|
helper->flags,
|
|
&error_local)) {
|
|
g_debug ("second pass requirement on %s:%s failed: %s",
|
|
fu_device_get_id (device),
|
|
xb_node_query_text (component, "id", NULL),
|
|
error_local->message);
|
|
g_ptr_array_add (errors, g_steal_pointer (&error_local));
|
|
continue;
|
|
}
|
|
if (!fu_engine_check_trust (task, &error_local)) {
|
|
g_ptr_array_add (errors, g_steal_pointer (&error_local));
|
|
continue;
|
|
}
|
|
|
|
/* if component should have an update message from CAB */
|
|
fu_device_incorporate_from_component (device, component);
|
|
|
|
/* get the action IDs for the valid device */
|
|
action_id = fu_install_task_get_action_id (task);
|
|
if (!g_ptr_array_find (helper->action_ids, action_id, NULL))
|
|
g_ptr_array_add (helper->action_ids, g_strdup (action_id));
|
|
g_ptr_array_add (helper->install_tasks, g_steal_pointer (&task));
|
|
}
|
|
}
|
|
|
|
/* order the install tasks by the device priority */
|
|
g_ptr_array_sort (helper->install_tasks, fu_main_install_task_sort_cb);
|
|
|
|
/* nothing suitable */
|
|
if (helper->install_tasks->len == 0) {
|
|
GError *error_tmp = fu_common_error_array_get_best (errors);
|
|
g_propagate_error (error, error_tmp);
|
|
return FALSE;
|
|
}
|
|
|
|
/* authenticate all things in the action_ids */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
fu_main_authorize_install_queue (g_steal_pointer (&helper));
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_main_device_id_valid (const gchar *device_id, GError **error)
|
|
{
|
|
if (g_strcmp0 (device_id, FWUPD_DEVICE_ID_ANY) == 0)
|
|
return TRUE;
|
|
if (device_id != NULL && strlen (device_id) >= 4)
|
|
return TRUE;
|
|
g_set_error (error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"invalid device ID: %s",
|
|
device_id);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
|
|
const gchar *object_path, const gchar *interface_name,
|
|
const gchar *method_name, GVariant *parameters,
|
|
GDBusMethodInvocation *invocation, gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
GVariant *val = NULL;
|
|
g_autoptr(FuEngineRequest) request = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
/* build request */
|
|
request = fu_main_create_request (priv, sender, &error);
|
|
if (request == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* activity */
|
|
fu_engine_idle_reset (priv->engine);
|
|
|
|
if (g_strcmp0 (method_name, "GetDevices") == 0) {
|
|
g_autoptr(GPtrArray) devices = NULL;
|
|
g_debug ("Called %s()", method_name);
|
|
devices = fu_engine_get_devices (priv->engine, &error);
|
|
if (devices == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_device_array_to_variant (priv, request, devices, &error);
|
|
if (val == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetPlugins") == 0) {
|
|
g_debug ("Called %s()", method_name);
|
|
val = fu_main_plugin_array_to_variant (fu_engine_get_plugins (priv->engine));
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetReleases") == 0) {
|
|
const gchar *device_id;
|
|
g_autoptr(GPtrArray) releases = NULL;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
releases = fu_engine_get_releases (priv->engine, request, device_id, &error);
|
|
if (releases == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_release_array_to_variant (releases);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetApprovedFirmware") == 0) {
|
|
GVariantBuilder builder;
|
|
GPtrArray *checksums = fu_engine_get_approved_firmware (priv->engine);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
|
|
for (guint i = 0; i < checksums->len; i++) {
|
|
const gchar *checksum = g_ptr_array_index (checksums, i);
|
|
g_variant_builder_add_value (&builder, g_variant_new_string (checksum));
|
|
}
|
|
val = g_variant_builder_end (&builder);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new_tuple (&val, 1));
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetBlockedFirmware") == 0) {
|
|
GVariantBuilder builder;
|
|
GPtrArray *checksums = fu_engine_get_blocked_firmware (priv->engine);
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
|
|
for (guint i = 0; i < checksums->len; i++) {
|
|
const gchar *checksum = g_ptr_array_index (checksums, i);
|
|
g_variant_builder_add_value (&builder, g_variant_new_string (checksum));
|
|
}
|
|
val = g_variant_builder_end (&builder);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new_tuple (&val, 1));
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetReportMetadata") == 0) {
|
|
GHashTableIter iter;
|
|
GVariantBuilder builder;
|
|
const gchar *key;
|
|
const gchar *value;
|
|
g_autoptr(GHashTable) metadata = NULL;
|
|
|
|
metadata = fu_engine_get_report_metadata (priv->engine, &error);
|
|
if (metadata == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
|
|
g_hash_table_iter_init (&iter, metadata);
|
|
while (g_hash_table_iter_next (&iter,
|
|
(gpointer *) &key,
|
|
(gpointer *) &value)) {
|
|
g_variant_builder_add_value (&builder,
|
|
g_variant_new ("{ss}", key, value));
|
|
}
|
|
val = g_variant_builder_end (&builder);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new_tuple (&val, 1));
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "SetApprovedFirmware") == 0) {
|
|
g_autofree gchar *checksums_str = NULL;
|
|
g_auto(GStrv) checksums = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif /* HAVE_POLKIT */
|
|
|
|
g_variant_get (parameters, "(^as)", &checksums);
|
|
checksums_str = g_strjoinv (",", checksums);
|
|
g_debug ("Called %s(%s)", method_name, checksums_str);
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->priv = priv;
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->checksums = g_ptr_array_new_with_free_func (g_free);
|
|
for (guint i = 0; checksums[i] != NULL; i++)
|
|
g_ptr_array_add (helper->checksums, g_strdup (checksums[i]));
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.set-approved-firmware",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_set_approved_firmware_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_set_approved_firmware_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "SetBlockedFirmware") == 0) {
|
|
g_autofree gchar *checksums_str = NULL;
|
|
g_auto(GStrv) checksums = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
g_variant_get (parameters, "(^as)", &checksums);
|
|
checksums_str = g_strjoinv (",", checksums);
|
|
g_debug ("Called %s(%s)", method_name, checksums_str);
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->priv = priv;
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->checksums = g_ptr_array_new_with_free_func (g_free);
|
|
for (guint i = 0; checksums[i] != NULL; i++)
|
|
g_ptr_array_add (helper->checksums, g_strdup (checksums[i]));
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.set-approved-firmware",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_set_blocked_firmware_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_set_blocked_firmware_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "SelfSign") == 0) {
|
|
GVariant *prop_value;
|
|
gchar *prop_key;
|
|
g_autofree gchar *value = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
g_autoptr(GVariantIter) iter = NULL;
|
|
|
|
g_variant_get (parameters, "(sa{sv})", &value, &iter);
|
|
g_debug ("Called %s(%s)", method_name, value);
|
|
|
|
/* get flags */
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
while (g_variant_iter_next (iter, "{&sv}", &prop_key, &prop_value)) {
|
|
g_debug ("got option %s", prop_key);
|
|
if (g_strcmp0 (prop_key, "add-timestamp") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= JCAT_SIGN_FLAG_ADD_TIMESTAMP;
|
|
if (g_strcmp0 (prop_key, "add-cert") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= JCAT_SIGN_FLAG_ADD_CERT;
|
|
g_variant_unref (prop_value);
|
|
}
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
helper->priv = priv;
|
|
helper->value = g_steal_pointer (&value);
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.self-sign",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_self_sign_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_self_sign_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetDowngrades") == 0) {
|
|
const gchar *device_id;
|
|
g_autoptr(GPtrArray) releases = NULL;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
releases = fu_engine_get_downgrades (priv->engine, request, device_id, &error);
|
|
if (releases == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_release_array_to_variant (releases);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetUpgrades") == 0) {
|
|
const gchar *device_id;
|
|
g_autoptr(GPtrArray) releases = NULL;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
releases = fu_engine_get_upgrades (priv->engine, request, device_id, &error);
|
|
if (releases == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_release_array_to_variant (releases);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetRemotes") == 0) {
|
|
g_autoptr(GPtrArray) remotes = NULL;
|
|
g_debug ("Called %s()", method_name);
|
|
remotes = fu_engine_get_remotes (priv->engine, &error);
|
|
if (remotes == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_remote_array_to_variant (remotes);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetHistory") == 0) {
|
|
g_autoptr(GPtrArray) devices = NULL;
|
|
g_debug ("Called %s()", method_name);
|
|
devices = fu_engine_get_history (priv->engine, &error);
|
|
if (devices == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_device_array_to_variant (priv, request, devices, &error);
|
|
if (val == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetHostSecurityAttrs") == 0) {
|
|
g_autoptr(FuSecurityAttrs) attrs = NULL;
|
|
g_debug ("Called %s()", method_name);
|
|
if (priv->machine_kind != FU_MAIN_MACHINE_KIND_PHYSICAL) {
|
|
g_dbus_method_invocation_return_error_literal (invocation,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"HSI unavailable for hypervisor");
|
|
return;
|
|
}
|
|
attrs = fu_engine_get_host_security_attrs (priv->engine);
|
|
val = fu_security_attrs_to_variant (attrs);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "ClearResults") == 0) {
|
|
const gchar *device_id;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_engine_clear_results (priv->engine, device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "ModifyDevice") == 0) {
|
|
const gchar *device_id;
|
|
const gchar *key = NULL;
|
|
const gchar *value = NULL;
|
|
|
|
/* check the id exists */
|
|
g_variant_get (parameters, "(&s&s&s)", &device_id, &key, &value);
|
|
g_debug ("Called %s(%s,%s=%s)", method_name, device_id, key, value);
|
|
if (!fu_engine_modify_device (priv->engine, device_id, key, value, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetResults") == 0) {
|
|
const gchar *device_id = NULL;
|
|
g_autoptr(FwupdDevice) result = NULL;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
result = fu_engine_get_results (priv->engine, device_id, &error);
|
|
if (result == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fwupd_device_to_variant (result);
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new_tuple (&val, 1));
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
|
|
GDBusMessage *message;
|
|
GUnixFDList *fd_list;
|
|
const gchar *remote_id = NULL;
|
|
gint fd_data;
|
|
gint fd_sig;
|
|
|
|
g_variant_get (parameters, "(&shh)", &remote_id, &fd_data, &fd_sig);
|
|
g_debug ("Called %s(%s,%i,%i)", method_name, remote_id, fd_data, fd_sig);
|
|
|
|
/* update the metadata store */
|
|
message = g_dbus_method_invocation_get_message (invocation);
|
|
fd_list = g_dbus_message_get_unix_fd_list (message);
|
|
if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
|
|
g_set_error (&error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"invalid handle");
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
fd_data = g_unix_fd_list_get (fd_list, 0, &error);
|
|
if (fd_data < 0) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
|
|
if (fd_sig < 0) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* store new metadata (will close the fds when done) */
|
|
if (!fu_engine_update_metadata (priv->engine, remote_id,
|
|
fd_data, fd_sig, &error)) {
|
|
g_prefix_error (&error, "Failed to update metadata for %s: ", remote_id);
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "Unlock") == 0) {
|
|
const gchar *device_id = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif /* HAVE_POLKIT */
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->priv = priv;
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->device_id = g_strdup (device_id);
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.device-unlock",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_unlock_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_unlock_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "Activate") == 0) {
|
|
const gchar *device_id = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->priv = priv;
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->device_id = g_strdup (device_id);
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.device-activate",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_activate_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_activate_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "ModifyConfig") == 0) {
|
|
g_autofree gchar *key = NULL;
|
|
g_autofree gchar *value = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
g_variant_get (parameters, "(ss)", &key, &value);
|
|
g_debug ("Called %s(%s=%s)", method_name, key, value);
|
|
|
|
/* authenticate */
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->priv = priv;
|
|
helper->key = g_steal_pointer (&key);
|
|
helper->value = g_steal_pointer (&value);
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.modify-config",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_modify_config_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_modify_config_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "ModifyRemote") == 0) {
|
|
const gchar *remote_id = NULL;
|
|
const gchar *key = NULL;
|
|
const gchar *value = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
/* check the id exists */
|
|
g_variant_get (parameters, "(&s&s&s)", &remote_id, &key, &value);
|
|
g_debug ("Called %s(%s,%s=%s)", method_name, remote_id, key, value);
|
|
|
|
/* create helper object */
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->remote_id = g_strdup (remote_id);
|
|
helper->key = g_strdup (key);
|
|
helper->value = g_strdup (value);
|
|
helper->priv = priv;
|
|
|
|
/* authenticate */
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
#ifdef HAVE_POLKIT
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.modify-remote",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_modify_remote_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_modify_remote_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "VerifyUpdate") == 0) {
|
|
const gchar *device_id = NULL;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
#ifdef HAVE_POLKIT
|
|
g_autoptr(PolkitSubject) subject = NULL;
|
|
#endif
|
|
|
|
/* check the id exists */
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* create helper object */
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->device_id = g_strdup (device_id);
|
|
helper->priv = priv;
|
|
|
|
/* authenticate */
|
|
#ifdef HAVE_POLKIT
|
|
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
|
|
subject = polkit_system_bus_name_new (sender);
|
|
polkit_authority_check_authorization (priv->authority, subject,
|
|
"org.freedesktop.fwupd.verify-update",
|
|
NULL,
|
|
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
|
|
NULL,
|
|
fu_main_authorize_verify_update_cb,
|
|
g_steal_pointer (&helper));
|
|
#else
|
|
fu_main_authorize_verify_update_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "Verify") == 0) {
|
|
const gchar *device_id = NULL;
|
|
g_variant_get (parameters, "(&s)", &device_id);
|
|
g_debug ("Called %s(%s)", method_name, device_id);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
if (!fu_engine_verify (priv->engine, device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "SetFeatureFlags") == 0) {
|
|
FwupdFeatureFlags feature_flags;
|
|
guint64 feature_flags_u64 = 0;
|
|
g_variant_get (parameters, "(t)", &feature_flags_u64);
|
|
g_debug ("Called %s(%" G_GUINT64_FORMAT ")", method_name, feature_flags_u64);
|
|
|
|
/* old flags for the same sender will be automatically destroyed */
|
|
feature_flags = feature_flags_u64;
|
|
g_hash_table_insert (priv->sender_features,
|
|
g_strdup (sender),
|
|
g_memdup (&feature_flags, sizeof(feature_flags)));
|
|
g_dbus_method_invocation_return_value (invocation, NULL);
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "Install") == 0) {
|
|
GVariant *prop_value;
|
|
const gchar *device_id = NULL;
|
|
gchar *prop_key;
|
|
gint32 fd_handle = 0;
|
|
gint fd;
|
|
guint64 archive_size_max;
|
|
GDBusMessage *message;
|
|
GUnixFDList *fd_list;
|
|
g_autoptr(FuMainAuthHelper) helper = NULL;
|
|
g_autoptr(GVariantIter) iter = NULL;
|
|
|
|
/* check the id exists */
|
|
g_variant_get (parameters, "(&sha{sv})", &device_id, &fd_handle, &iter);
|
|
g_debug ("Called %s(%s,%i)", method_name, device_id, fd_handle);
|
|
if (!fu_main_device_id_valid (device_id, &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* create helper object */
|
|
helper = g_new0 (FuMainAuthHelper, 1);
|
|
helper->request = g_steal_pointer (&request);
|
|
helper->invocation = g_object_ref (invocation);
|
|
helper->device_id = g_strdup (device_id);
|
|
helper->priv = priv;
|
|
|
|
/* get flags */
|
|
while (g_variant_iter_next (iter, "{&sv}", &prop_key, &prop_value)) {
|
|
g_debug ("got option %s", prop_key);
|
|
if (g_strcmp0 (prop_key, "offline") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_OFFLINE;
|
|
if (g_strcmp0 (prop_key, "allow-older") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
|
|
if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
|
|
if (g_strcmp0 (prop_key, "allow-branch-switch") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
|
|
if (g_strcmp0 (prop_key, "force") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE) {
|
|
helper->flags |= FWUPD_INSTALL_FLAG_FORCE;
|
|
helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
|
|
}
|
|
if (g_strcmp0 (prop_key, "ignore-power") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
|
|
if (g_strcmp0 (prop_key, "no-history") == 0 &&
|
|
g_variant_get_boolean (prop_value) == TRUE)
|
|
helper->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
|
|
g_variant_unref (prop_value);
|
|
}
|
|
|
|
|
|
/* get the fd */
|
|
message = g_dbus_method_invocation_get_message (invocation);
|
|
fd_list = g_dbus_message_get_unix_fd_list (message);
|
|
if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
|
|
g_set_error (&error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"invalid handle");
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
fd = g_unix_fd_list_get (fd_list, 0, &error);
|
|
if (fd < 0) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* parse the cab file before authenticating so we can work out
|
|
* what action ID to use, for instance, if this is trusted --
|
|
* this will also close the fd when done */
|
|
archive_size_max = fu_engine_get_archive_size_max (priv->engine);
|
|
helper->blob_cab = fu_common_get_contents_fd (fd, archive_size_max, &error);
|
|
if (helper->blob_cab == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* install all the things in the store */
|
|
#ifdef HAVE_POLKIT
|
|
helper->subject = polkit_system_bus_name_new (sender);
|
|
#endif /* HAVE_POLKIT */
|
|
if (!fu_main_install_with_helper (g_steal_pointer (&helper), &error)) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* async return */
|
|
return;
|
|
}
|
|
if (g_strcmp0 (method_name, "GetDetails") == 0) {
|
|
GDBusMessage *message;
|
|
GUnixFDList *fd_list;
|
|
gint32 fd_handle = 0;
|
|
gint fd;
|
|
g_autoptr(GPtrArray) results = NULL;
|
|
|
|
/* get parameters */
|
|
g_variant_get (parameters, "(h)", &fd_handle);
|
|
g_debug ("Called %s(%i)", method_name, fd_handle);
|
|
|
|
/* get the fd */
|
|
message = g_dbus_method_invocation_get_message (invocation);
|
|
fd_list = g_dbus_message_get_unix_fd_list (message);
|
|
if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
|
|
g_set_error (&error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"invalid handle");
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
fd = g_unix_fd_list_get (fd_list, 0, &error);
|
|
if (fd < 0) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
|
|
/* get details about the file (will close the fd when done) */
|
|
results = fu_engine_get_details (priv->engine, request, fd, &error);
|
|
if (results == NULL) {
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
return;
|
|
}
|
|
val = fu_main_result_array_to_variant (results);
|
|
g_dbus_method_invocation_return_value (invocation, val);
|
|
return;
|
|
}
|
|
g_set_error (&error,
|
|
G_DBUS_ERROR,
|
|
G_DBUS_ERROR_UNKNOWN_METHOD,
|
|
"no such method %s", method_name);
|
|
g_dbus_method_invocation_return_gerror (invocation, error);
|
|
}
|
|
|
|
static GVariant *
|
|
fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
|
|
const gchar *object_path, const gchar *interface_name,
|
|
const gchar *property_name, GError **error,
|
|
gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
|
|
/* activity */
|
|
fu_engine_idle_reset (priv->engine);
|
|
|
|
if (g_strcmp0 (property_name, "DaemonVersion") == 0)
|
|
return g_variant_new_string (SOURCE_VERSION);
|
|
|
|
if (g_strcmp0 (property_name, "Tainted") == 0)
|
|
return g_variant_new_boolean (fu_engine_get_tainted (priv->engine));
|
|
|
|
if (g_strcmp0 (property_name, "Status") == 0)
|
|
return g_variant_new_uint32 (fu_engine_get_status (priv->engine));
|
|
|
|
if (g_strcmp0 (property_name, "HostProduct") == 0)
|
|
return g_variant_new_string (fu_engine_get_host_product (priv->engine));
|
|
|
|
if (g_strcmp0 (property_name, "HostMachineId") == 0)
|
|
return g_variant_new_string (fu_engine_get_host_machine_id (priv->engine));
|
|
|
|
if (g_strcmp0 (property_name, "HostSecurityId") == 0)
|
|
return g_variant_new_string (fu_engine_get_host_security_id (priv->engine));
|
|
|
|
if (g_strcmp0 (property_name, "Interactive") == 0)
|
|
return g_variant_new_boolean (isatty (fileno (stdout)) != 0);
|
|
|
|
/* return an error */
|
|
g_set_error (error,
|
|
G_DBUS_ERROR,
|
|
G_DBUS_ERROR_UNKNOWN_PROPERTY,
|
|
"failed to get daemon property %s",
|
|
property_name);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
fu_main_on_bus_acquired_cb (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
guint registration_id;
|
|
g_autoptr(GError) error = NULL;
|
|
static const GDBusInterfaceVTable interface_vtable = {
|
|
fu_main_daemon_method_call,
|
|
fu_main_daemon_get_property,
|
|
NULL
|
|
};
|
|
|
|
priv->connection = g_object_ref (connection);
|
|
registration_id = g_dbus_connection_register_object (connection,
|
|
FWUPD_DBUS_PATH,
|
|
priv->introspection_daemon->interfaces[0],
|
|
&interface_vtable,
|
|
priv, /* user_data */
|
|
NULL, /* user_data_free_func */
|
|
NULL); /* GError** */
|
|
g_assert (registration_id > 0);
|
|
|
|
/* connect to D-Bus directly */
|
|
priv->proxy_uid =
|
|
g_dbus_proxy_new_sync (priv->connection,
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
|
|
NULL,
|
|
"org.freedesktop.DBus",
|
|
"/org/freedesktop/DBus",
|
|
"org.freedesktop.DBus",
|
|
NULL,
|
|
&error);
|
|
if (priv->proxy_uid == NULL) {
|
|
g_warning ("cannot connect to DBus: %s", error->message);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_main_on_name_acquired_cb (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
g_debug ("acquired name: %s", name);
|
|
}
|
|
|
|
static void
|
|
fu_main_on_name_lost_cb (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
g_warning ("another service has claimed the dbus name %s", name);
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
|
|
static gboolean
|
|
fu_main_timed_exit_cb (gpointer user_data)
|
|
{
|
|
GMainLoop *loop = (GMainLoop *) user_data;
|
|
g_main_loop_quit (loop);
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
fu_main_argv_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file,
|
|
GFileMonitorEvent event_type, gpointer user_data)
|
|
{
|
|
FuMainPrivate *priv = (FuMainPrivate *) user_data;
|
|
|
|
/* can do straight away? */
|
|
if (priv->update_in_progress) {
|
|
g_warning ("binary changed during a firmware update, ignoring");
|
|
return;
|
|
}
|
|
g_debug ("binary changed, shutting down");
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
|
|
#if GLIB_CHECK_VERSION(2,63,3)
|
|
static void
|
|
fu_main_memory_monitor_warning_cb (GMemoryMonitor *memory_monitor,
|
|
GMemoryMonitorWarningLevel level,
|
|
FuMainPrivate *priv)
|
|
{
|
|
/* can do straight away? */
|
|
if (priv->update_in_progress) {
|
|
g_warning ("OOM during a firmware update, ignoring");
|
|
priv->pending_sigterm = TRUE;
|
|
return;
|
|
}
|
|
g_debug ("OOM event, shutting down");
|
|
g_main_loop_quit (priv->loop);
|
|
}
|
|
#endif
|
|
|
|
static GDBusNodeInfo *
|
|
fu_main_load_introspection (const gchar *filename, GError **error)
|
|
{
|
|
g_autoptr(GBytes) data = NULL;
|
|
g_autofree gchar *path = NULL;
|
|
|
|
/* lookup data */
|
|
path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
|
|
data = g_resource_lookup_data (fu_get_resource (),
|
|
path,
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE,
|
|
error);
|
|
if (data == NULL)
|
|
return NULL;
|
|
|
|
/* build introspection from XML */
|
|
return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_main_is_hypervisor (void)
|
|
{
|
|
g_autofree gchar *buf = NULL;
|
|
gsize bufsz = 0;
|
|
if (!g_file_get_contents ("/proc/cpuinfo", &buf, &bufsz, NULL))
|
|
return FALSE;
|
|
return g_strstr_len (buf, (gssize) bufsz, "hypervisor") != NULL;
|
|
}
|
|
|
|
static gboolean
|
|
fu_main_is_container (void)
|
|
{
|
|
g_autofree gchar *buf = NULL;
|
|
gsize bufsz = 0;
|
|
if (!g_file_get_contents ("/proc/1/cgroup", &buf, &bufsz, NULL))
|
|
return FALSE;
|
|
if (g_strstr_len (buf, (gssize) bufsz, "docker") != NULL)
|
|
return TRUE;
|
|
if (g_strstr_len (buf, (gssize) bufsz, "lxc") != NULL)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_main_private_free (FuMainPrivate *priv)
|
|
{
|
|
g_hash_table_unref (priv->sender_features);
|
|
if (priv->loop != NULL)
|
|
g_main_loop_unref (priv->loop);
|
|
if (priv->owner_id > 0)
|
|
g_bus_unown_name (priv->owner_id);
|
|
if (priv->proxy_uid != NULL)
|
|
g_object_unref (priv->proxy_uid);
|
|
if (priv->engine != NULL)
|
|
g_object_unref (priv->engine);
|
|
if (priv->connection != NULL)
|
|
g_object_unref (priv->connection);
|
|
#ifdef HAVE_POLKIT
|
|
if (priv->authority != NULL)
|
|
g_object_unref (priv->authority);
|
|
#endif
|
|
if (priv->argv0_monitor != NULL) {
|
|
g_file_monitor_cancel (priv->argv0_monitor);
|
|
g_object_unref (priv->argv0_monitor);
|
|
}
|
|
if (priv->introspection_daemon != NULL)
|
|
g_dbus_node_info_unref (priv->introspection_daemon);
|
|
#if GLIB_CHECK_VERSION(2,63,3)
|
|
if (priv->memory_monitor != NULL)
|
|
g_object_unref (priv->memory_monitor);
|
|
#endif
|
|
g_free (priv);
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainPrivate, fu_main_private_free)
|
|
#pragma clang diagnostic pop
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
gboolean immediate_exit = FALSE;
|
|
gboolean timed_exit = FALSE;
|
|
const GOptionEntry options[] = {
|
|
{ "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
|
|
/* TRANSLATORS: exit after we've started up, used for user profiling */
|
|
_("Exit after a small delay"), NULL },
|
|
{ "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
|
|
/* TRANSLATORS: exit straight away, used for automatic profiling */
|
|
_("Exit after the engine has loaded"), NULL },
|
|
{ NULL}
|
|
};
|
|
g_autoptr(FuMainPrivate) priv = NULL;
|
|
g_autoptr(GError) error = NULL;
|
|
g_autoptr(GFile) argv0_file = g_file_new_for_path (argv[0]);
|
|
g_autoptr(GOptionContext) context = NULL;
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
textdomain (GETTEXT_PACKAGE);
|
|
|
|
/* TRANSLATORS: program name */
|
|
g_set_application_name (_("Firmware Update Daemon"));
|
|
context = g_option_context_new (NULL);
|
|
g_option_context_add_main_entries (context, options, NULL);
|
|
g_option_context_add_group (context, fu_debug_get_option_group ());
|
|
/* TRANSLATORS: program summary */
|
|
g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
|
|
if (!g_option_context_parse (context, &argc, &argv, &error)) {
|
|
g_printerr ("Failed to parse command line: %s\n", error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* create new objects */
|
|
priv = g_new0 (FuMainPrivate, 1);
|
|
priv->sender_features = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
priv->loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
/* load engine */
|
|
priv->engine = fu_engine_new (FU_APP_FLAGS_NONE);
|
|
g_signal_connect (priv->engine, "changed",
|
|
G_CALLBACK (fu_main_engine_changed_cb),
|
|
priv);
|
|
g_signal_connect (priv->engine, "device-added",
|
|
G_CALLBACK (fu_main_engine_device_added_cb),
|
|
priv);
|
|
g_signal_connect (priv->engine, "device-removed",
|
|
G_CALLBACK (fu_main_engine_device_removed_cb),
|
|
priv);
|
|
g_signal_connect (priv->engine, "device-changed",
|
|
G_CALLBACK (fu_main_engine_device_changed_cb),
|
|
priv);
|
|
g_signal_connect (priv->engine, "status-changed",
|
|
G_CALLBACK (fu_main_engine_status_changed_cb),
|
|
priv);
|
|
g_signal_connect (priv->engine, "percentage-changed",
|
|
G_CALLBACK (fu_main_engine_percentage_changed_cb),
|
|
priv);
|
|
if (!fu_engine_load (priv->engine,
|
|
FU_ENGINE_LOAD_FLAG_COLDPLUG |
|
|
FU_ENGINE_LOAD_FLAG_HWINFO |
|
|
FU_ENGINE_LOAD_FLAG_REMOTES,
|
|
&error)) {
|
|
g_printerr ("Failed to load engine: %s\n", error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
g_unix_signal_add_full (G_PRIORITY_DEFAULT,
|
|
SIGTERM, fu_main_sigterm_cb,
|
|
priv, NULL);
|
|
|
|
/* restart the daemon if the binary gets replaced */
|
|
priv->argv0_monitor = g_file_monitor_file (argv0_file, G_FILE_MONITOR_NONE,
|
|
NULL, &error);
|
|
g_signal_connect (priv->argv0_monitor, "changed",
|
|
G_CALLBACK (fu_main_argv_changed_cb), priv);
|
|
|
|
#if GLIB_CHECK_VERSION(2,63,3)
|
|
/* shut down on low memory event as we can just rescan hardware */
|
|
priv->memory_monitor = g_memory_monitor_dup_default ();
|
|
g_signal_connect (G_OBJECT (priv->memory_monitor), "low-memory-warning",
|
|
G_CALLBACK (fu_main_memory_monitor_warning_cb), priv);
|
|
#endif
|
|
|
|
/* load introspection from file */
|
|
priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
|
|
&error);
|
|
if (priv->introspection_daemon == NULL) {
|
|
g_printerr ("Failed to load introspection: %s\n", error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
#ifdef HAVE_POLKIT
|
|
/* get authority */
|
|
priv->authority = polkit_authority_get_sync (NULL, &error);
|
|
if (priv->authority == NULL) {
|
|
g_printerr ("Failed to load authority: %s\n", error->message);
|
|
return EXIT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
/* are we a VM? */
|
|
if (fu_main_is_hypervisor ()) {
|
|
priv->machine_kind = FU_MAIN_MACHINE_KIND_VIRTUAL;
|
|
} else if (fu_main_is_container ()) {
|
|
priv->machine_kind = FU_MAIN_MACHINE_KIND_CONTAINER;
|
|
}
|
|
|
|
/* own the object */
|
|
priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
|
|
FWUPD_DBUS_SERVICE,
|
|
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
|
|
G_BUS_NAME_OWNER_FLAGS_REPLACE,
|
|
fu_main_on_bus_acquired_cb,
|
|
fu_main_on_name_acquired_cb,
|
|
fu_main_on_name_lost_cb,
|
|
priv, NULL);
|
|
|
|
/* Only timeout and close the mainloop if we have specified it
|
|
* on the command line */
|
|
if (immediate_exit)
|
|
g_idle_add (fu_main_timed_exit_cb, priv->loop);
|
|
else if (timed_exit)
|
|
g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
|
|
|
|
/* drop heap except one page */
|
|
malloc_trim (4096);
|
|
|
|
/* wait */
|
|
g_message ("Daemon ready for requests (locale %s)", g_getenv ("LANG"));
|
|
g_main_loop_run (priv->loop);
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
/* notify the service manager */
|
|
sd_notify (0, "STOPPING=1");
|
|
#endif
|
|
|
|
/* success */
|
|
return EXIT_SUCCESS;
|
|
}
|