mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-22 23:47:20 +00:00

Loosen the requirement of the user ID to match root for the following actions: * Install release * Activate firmware For install release action, reject CAB files not signed by LVFS
2034 lines
64 KiB
C
2034 lines
64 KiB
C
/*
|
|
* Copyright (C) 2015-2017 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>
|
|
#ifdef HAVE_POLKIT
|
|
#include <polkit/polkit.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);
|
|
|
|
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;
|
|
#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 */
|
|
|
|
/* do the next authentication action ID */
|
|
fu_main_authorize_install_queue (g_steal_pointer (&helper));
|
|
}
|
|
|
|
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;
|
|
|
|
/* still more things to to authenticate */
|
|
if (helper->action_ids->len > 0) {
|
|
#ifdef HAVE_POLKIT
|
|
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));
|
|
#else
|
|
g_ptr_array_remove_index (helper->action_ids, 0);
|
|
fu_main_authorize_install_cb (NULL, NULL, g_steal_pointer (&helper));
|
|
#endif /* HAVE_POLKIT */
|
|
return;
|
|
}
|
|
|
|
/* 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", 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 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) {
|
|
guint64 feature_flags = 0;
|
|
g_variant_get (parameters, "(t)", &feature_flags);
|
|
g_debug ("Called %s(%" G_GUINT64_FORMAT ")", method_name, feature_flags);
|
|
|
|
/* old flags for the same sender will be automatically destroyed */
|
|
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_NONE, &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);
|
|
|
|
/* wait */
|
|
g_message ("Daemon ready for requests (locale %s)", g_getenv ("LANG"));
|
|
g_main_loop_run (priv->loop);
|
|
|
|
/* success */
|
|
return EXIT_SUCCESS;
|
|
}
|