blob: d48f553f7c8e36f1ba243574abfc3e1d38243f24 [file] [log] [blame]
/*
* Copyright 2015 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#define G_LOG_DOMAIN "FuMain"
#include "config.h"
#include <fwupdplugin.h>
#ifdef HAVE_GIO_UNIX
#include <gio/gunixfdlist.h>
#include <gio/gunixoutputstream.h>
#endif
#include <glib/gstdio.h>
#include <jcat.h>
#include "fwupd-enums-private.h"
#include "fu-client-list.h"
#include "fu-context-private.h"
#include "fu-dbus-daemon.h"
#include "fu-device-private.h"
#include "fu-engine-helper.h"
#include "fu-engine-requirements.h"
#include "fu-polkit-authority.h"
#include "fu-release.h"
#include "fu-security-attrs-private.h"
#ifdef HAVE_GIO_UNIX
#include "fu-unix-seekable-input-stream.h"
#endif
struct _FuDbusDaemon {
FuDaemon parent_instance;
GDBusConnection *connection;
GDBusNodeInfo *introspection_daemon;
GDBusProxy *proxy_uid;
FuClientList *client_list;
guint32 clients_inhibit_id;
FuPolkitAuthority *authority;
FwupdStatus status; /* last emitted */
guint percentage; /* last emitted */
guint owner_id;
GPtrArray *system_inhibits;
};
G_DEFINE_TYPE(FuDbusDaemon, fu_dbus_daemon, FU_TYPE_DAEMON)
static void
fu_dbus_daemon_engine_changed_cb(FuEngine *engine, FuDbusDaemon *self)
{
/* not yet connected */
if (self->connection == NULL)
return;
g_dbus_connection_emit_signal(self->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"Changed",
NULL,
NULL);
fu_daemon_schedule_housekeeping(FU_DAEMON(self));
}
static void
fu_dbus_daemon_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self)
{
GVariant *val;
/* not yet connected */
if (self->connection == NULL)
return;
val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE);
g_dbus_connection_emit_signal(self->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"DeviceAdded",
g_variant_new_tuple(&val, 1),
NULL);
fu_daemon_schedule_housekeeping(FU_DAEMON(self));
}
static void
fu_dbus_daemon_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self)
{
GVariant *val;
/* not yet connected */
if (self->connection == NULL)
return;
val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE);
g_dbus_connection_emit_signal(self->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"DeviceRemoved",
g_variant_new_tuple(&val, 1),
NULL);
fu_daemon_schedule_housekeeping(FU_DAEMON(self));
}
static void
fu_dbus_daemon_engine_device_changed_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self)
{
GVariant *val;
/* not yet connected */
if (self->connection == NULL)
return;
val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE);
g_dbus_connection_emit_signal(self->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"DeviceChanged",
g_variant_new_tuple(&val, 1),
NULL);
fu_daemon_schedule_housekeeping(FU_DAEMON(self));
}
static void
fu_dbus_daemon_engine_device_request_cb(FuEngine *engine, FwupdRequest *request, FuDbusDaemon *self)
{
GVariant *val;
/* not yet connected */
if (self->connection == NULL)
return;
val = fwupd_codec_to_variant(FWUPD_CODEC(request), FWUPD_CODEC_FLAG_NONE);
g_dbus_connection_emit_signal(self->connection,
NULL,
FWUPD_DBUS_PATH,
FWUPD_DBUS_INTERFACE,
"DeviceRequest",
g_variant_new_tuple(&val, 1),
NULL);
}
static void
fu_dbus_daemon_emit_property_changed(FuDbusDaemon *self,
const gchar *property_name,
GVariant *property_value)
{
GVariantBuilder builder;
GVariantBuilder invalidated_builder;
/* not yet connected */
if (self->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(
self->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_dbus_daemon_set_status(FuDbusDaemon *self, FwupdStatus status)
{
/* sanity check */
if (self->status == status)
return;
self->status = status;
g_debug("emitting PropertyChanged('Status'='%s')", fwupd_status_to_string(status));
fu_dbus_daemon_emit_property_changed(self, "Status", g_variant_new_uint32(status));
}
static void
fu_dbus_daemon_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuDbusDaemon *self)
{
fu_dbus_daemon_set_status(self, status);
/* engine has gone idle */
if (status == FWUPD_STATUS_SHUTDOWN)
fu_daemon_stop(FU_DAEMON(self), NULL);
}
static FuEngineRequest *
fu_dbus_daemon_create_request(FuDbusDaemon *self, const gchar *sender, GError **error)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
FwupdCodecFlags converter_flags = FWUPD_CODEC_FLAG_NONE;
guint calling_uid = 0;
g_autoptr(FuClient) client = NULL;
g_autoptr(FuEngineRequest) request = fu_engine_request_new(sender);
g_autoptr(GVariant) value = NULL;
/* if using FWUPD_DBUS_SOCKET... */
if (sender == NULL) {
fu_engine_request_set_converter_flags(request, FWUPD_CODEC_FLAG_TRUSTED);
return g_object_ref(request);
}
/* did the client set the list of supported features or any hints */
client = fu_client_list_get_by_sender(self->client_list, sender);
if (client != NULL) {
const gchar *locale = fu_client_lookup_hint(client, "locale");
if (locale != NULL)
fu_engine_request_set_locale(request, locale);
fu_engine_request_set_feature_flags(request, fu_client_get_feature_flags(client));
}
/* are we root and therefore trusted? */
if (self->proxy_uid == NULL) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"org.freedesktop.DBus is not available");
return NULL;
}
value = g_dbus_proxy_call_sync(self->proxy_uid,
"GetConnectionUnixUser",
g_variant_new("(s)", sender),
G_DBUS_CALL_FLAGS_NONE,
2000,
NULL,
error);
if (value == NULL) {
g_prefix_error_literal(error, "failed to read user id of caller: ");
return NULL;
}
g_variant_get(value, "(u)", &calling_uid);
if (fu_engine_is_uid_trusted(engine, calling_uid))
converter_flags |= FWUPD_CODEC_FLAG_TRUSTED;
fu_engine_request_set_converter_flags(request, converter_flags);
/* success */
return g_object_ref(request);
}
static GVariant *
fu_dbus_daemon_device_array_to_variant(FuDbusDaemon *self,
FuEngineRequest *request,
GPtrArray *devices,
GError **error)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
FwupdCodecFlags flags = fu_engine_request_get_converter_flags(request);
if (fu_engine_config_get_show_device_private(fu_engine_get_config(engine)))
flags |= FWUPD_CODEC_FLAG_TRUSTED;
return fwupd_codec_array_to_variant(devices, flags);
}
typedef struct {
GDBusMethodInvocation *invocation;
FuEngineRequest *request;
FuProgress *progress;
FuClient *client;
glong client_sender_changed_id;
GPtrArray *releases;
GPtrArray *action_ids;
GPtrArray *checksums;
GPtrArray *errors;
guint64 flags;
GInputStream *stream;
FuDbusDaemon *self;
gchar *device_id;
gchar *remote_id;
gchar *section;
gchar *key;
gchar *value;
gint32 handle;
FuCabinet *cabinet;
GHashTable *bios_settings; /* str:str */
gboolean is_fix;
} FuMainAuthHelper;
static void
fu_dbus_daemon_auth_helper_free(FuMainAuthHelper *helper)
{
/* always return to IDLE even in event of an auth error */
fu_dbus_daemon_set_status(helper->self, FWUPD_STATUS_IDLE);
if (helper->cabinet != NULL)
g_object_unref(helper->cabinet);
if (helper->stream != NULL)
g_object_unref(helper->stream);
if (helper->request != NULL)
g_object_unref(helper->request);
if (helper->progress != NULL)
g_object_unref(helper->progress);
if (helper->releases != NULL)
g_ptr_array_unref(helper->releases);
if (helper->action_ids != NULL)
g_ptr_array_unref(helper->action_ids);
if (helper->checksums != NULL)
g_ptr_array_unref(helper->checksums);
if (helper->errors != NULL)
g_ptr_array_unref(helper->errors);
if (helper->client_sender_changed_id > 0)
g_signal_handler_disconnect(helper->client, helper->client_sender_changed_id);
if (helper->client != NULL)
g_object_unref(helper->client);
g_free(helper->device_id);
g_free(helper->remote_id);
g_free(helper->section);
g_free(helper->key);
g_free(helper->value);
g_object_unref(helper->invocation);
if (helper->bios_settings != NULL)
g_hash_table_unref(helper->bios_settings);
g_free(helper);
}
static void
fu_dbus_daemon_method_invocation_return_gerror(GDBusMethodInvocation *invocation, GError *error)
{
fwupd_error_convert(&error);
g_dbus_method_invocation_return_gerror(invocation, error);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_dbus_daemon_auth_helper_free)
#pragma clang diagnostic pop
static void
fu_dbus_daemon_authorize_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
if (!fu_engine_unlock(engine, helper->device_id, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_get_bios_settings_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(FuBiosSettings) attrs = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
FuContext *ctx = fu_engine_get_context(engine);
GVariant *val = NULL;
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
attrs = fu_context_get_bios_settings(ctx);
val = fwupd_codec_to_variant(FWUPD_CODEC(attrs), FWUPD_CODEC_FLAG_TRUSTED);
g_dbus_method_invocation_return_value(helper->invocation, val);
}
static void
fu_dbus_daemon_authorize_set_bios_settings_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
if (!fu_engine_modify_bios_settings(engine, helper->bios_settings, FALSE, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_set_approved_firmware_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* 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(engine, csum);
}
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_set_blocked_firmware_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
if (!fu_engine_set_blocked_firmware(engine, helper->checksums, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_fix_host_security_attr_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
if (!fu_engine_fix_host_security_attr(engine, helper->key, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_undo_host_security_attr_cb(GObject *source,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
if (!fu_engine_undo_host_security_attr(engine, helper->key, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_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;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
sig = fu_engine_self_sign(engine, helper->value, helper->flags, &error);
if (sig == NULL) {
fu_dbus_daemon_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_dbus_daemon_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
if (!fu_engine_modify_config(engine, helper->section, helper->key, helper->value, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_reset_config_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
if (!fu_engine_reset_config(engine, helper->section, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_progress_percentage_changed_cb(FuProgress *progress,
guint percentage,
FuDbusDaemon *self)
{
/* sanity check */
if (self->percentage == percentage)
return;
self->percentage = percentage;
g_debug("emitting PropertyChanged('Percentage'='%u%%')", percentage);
fu_dbus_daemon_emit_property_changed(self, "Percentage", g_variant_new_uint32(percentage));
}
static void
fu_dbus_daemon_progress_status_changed_cb(FuProgress *progress,
FwupdStatus status,
FuDbusDaemon *self)
{
fu_dbus_daemon_set_status(self, status);
}
static void
fu_dbus_daemon_authorize_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* progress */
fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL);
g_signal_connect(FU_PROGRESS(progress),
"percentage-changed",
G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb),
helper->self);
g_signal_connect(FU_PROGRESS(progress),
"status-changed",
G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb),
helper->self);
/* authenticated */
if (!fu_engine_activate(engine, helper->device_id, progress, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* progress */
fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL);
g_signal_connect(FU_PROGRESS(progress),
"percentage-changed",
G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb),
helper->self);
g_signal_connect(FU_PROGRESS(progress),
"status-changed",
G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb),
helper->self);
/* authenticated */
if (!fu_engine_verify_update(engine, helper->device_id, progress, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
if (!fu_engine_modify_remote(engine,
helper->remote_id,
helper->key,
helper->value,
&error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_authorize_clean_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* authenticated */
if (!fu_engine_clean_remote(engine, helper->remote_id, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static FuPolkitAuthorityCheckFlags
fu_dbus_daemon_engine_request_get_authority_check_flags(FuEngineRequest *request)
{
FuPolkitAuthorityCheckFlags auth_flags =
FU_POLKIT_AUTHORITY_CHECK_FLAG_ALLOW_USER_INTERACTION;
if (fu_engine_request_has_converter_flag(request, FWUPD_CODEC_FLAG_TRUSTED))
auth_flags |= FU_POLKIT_AUTHORITY_CHECK_FLAG_USER_IS_TRUSTED;
return auth_flags;
}
#ifdef HAVE_GIO_UNIX
static void
fu_dbus_daemon_authorize_install_queue(FuMainAuthHelper *helper);
static void
fu_dbus_daemon_authorize_install_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* do the next authentication action ID */
fu_dbus_daemon_authorize_install_queue(g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_authorize_install_queue(FuMainAuthHelper *helper_ref)
{
FuDbusDaemon *self = helper_ref->self;
g_autoptr(FuMainAuthHelper) helper = helper_ref;
g_autoptr(GError) error = NULL;
gboolean ret;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* 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_autofree gchar *sender = g_strdup(fu_client_get_sender(helper->client));
g_autoptr(FuEngineRequest) request = g_object_ref(helper->request);
g_ptr_array_remove_index(helper->action_ids, 0);
fu_polkit_authority_check(
self->authority,
sender,
action_id,
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_install_cb,
g_steal_pointer(&helper));
return;
}
/* all authenticated, so install all the things */
fu_progress_set_profile(helper->progress, g_getenv("FWUPD_VERBOSE") != NULL);
g_signal_connect(FU_PROGRESS(helper->progress),
"percentage-changed",
G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb),
helper->self);
g_signal_connect(FU_PROGRESS(helper->progress),
"status-changed",
G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb),
helper->self);
/* all authenticated, so install all the things */
fu_daemon_set_update_in_progress(FU_DAEMON(self), TRUE);
ret = fu_engine_install_releases(engine,
helper->request,
helper->releases,
helper->cabinet,
helper->progress,
helper->flags,
&error);
fu_daemon_set_update_in_progress(FU_DAEMON(self), FALSE);
if (fu_daemon_get_pending_stop(FU_DAEMON(self))) {
g_set_error_literal(&error, /* nocheck:error-false-return */
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"daemon was stopped");
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
if (!ret) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
#endif /* HAVE_GIO_UNIX */
#ifdef HAVE_GIO_UNIX
static gint
fu_dbus_daemon_release_sort_cb(gconstpointer a, gconstpointer b)
{
FuRelease *release1 = *((FuRelease **)a);
FuRelease *release2 = *((FuRelease **)b);
return fu_release_compare(release1, release2);
}
static gboolean
fu_dbus_daemon_install_with_helper_device(FuMainAuthHelper *helper,
XbNode *component,
FuDevice *device,
GError **error)
{
FuDbusDaemon *self = helper->self;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) releases = NULL;
/* is this component valid for the device */
fu_release_set_device(release, device);
fu_release_set_request(release, helper->request);
if (helper->remote_id != NULL) {
fu_release_set_remote(release,
fu_engine_get_remote_by_id(engine, helper->remote_id, NULL));
}
if (!fu_release_load(release,
helper->cabinet,
component,
NULL,
helper->flags | FWUPD_INSTALL_FLAG_FORCE,
&error_local)) {
g_ptr_array_add(helper->errors, g_steal_pointer(&error_local));
return TRUE;
}
if (!fu_engine_requirements_check(engine,
release,
helper->flags | FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS,
&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(helper->errors, g_steal_pointer(&error_local));
return TRUE;
}
/* sync update message from CAB */
fu_device_ensure_from_component(device, component);
fu_device_incorporate_from_component(device, component);
/* post-ensure checks */
if (!fu_release_check_version(release, component, helper->flags, &error_local)) {
g_ptr_array_add(helper->errors, g_steal_pointer(&error_local));
return TRUE;
}
/* install each intermediate release */
releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) {
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(XbQuery) query = NULL;
/* we get this one "for free" */
g_ptr_array_add(releases, g_object_ref(release));
query = xb_query_new_full(xb_node_get_silo(component),
"releases/release",
XB_QUERY_FLAG_FORCE_NODE_CACHE,
error);
if (query == NULL)
return FALSE;
rels = xb_node_query_full(component, query, NULL);
/* add all but the first entry */
for (guint i = 1; i < rels->len; i++) {
XbNode *rel = g_ptr_array_index(rels, i);
g_autoptr(FuRelease) release2 = fu_release_new();
g_autoptr(GError) error_loop = NULL;
fu_release_set_device(release2, device);
fu_release_set_request(release2, helper->request);
if (!fu_release_load(release2,
helper->cabinet,
component,
rel,
helper->flags,
&error_loop)) {
g_ptr_array_add(helper->errors, g_steal_pointer(&error_loop));
continue;
}
g_ptr_array_add(releases, g_object_ref(release2));
}
} else {
g_ptr_array_add(releases, g_object_ref(release));
}
/* make a second pass */
for (guint i = 0; i < releases->len; i++) {
FuRelease *release_tmp = g_ptr_array_index(releases, i);
if (!fu_engine_requirements_check(engine,
release_tmp,
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(helper->errors, g_steal_pointer(&error_local));
continue;
}
if (!fu_engine_check_trust(engine, release_tmp, &error_local)) {
g_ptr_array_add(helper->errors, g_steal_pointer(&error_local));
continue;
}
/* get the action IDs for the valid device */
if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) {
const gchar *action_id = fu_release_get_action_id(release_tmp);
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->releases, g_object_ref(release_tmp));
}
/* success */
return TRUE;
}
static gboolean
fu_dbus_daemon_install_with_helper(FuMainAuthHelper *helper_ref, GError **error)
{
FuDbusDaemon *self = helper_ref->self;
g_autoptr(FuMainAuthHelper) helper = helper_ref;
g_autoptr(GPtrArray) components = NULL;
g_autoptr(GPtrArray) devices_possible = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* 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(engine, error);
if (devices_possible == NULL)
return FALSE;
} else {
g_autoptr(FuDevice) device = NULL;
device = fu_engine_get_device(engine, helper->device_id, error);
if (device == NULL)
return FALSE;
devices_possible =
fu_engine_get_devices_by_composite_id(engine,
fu_device_get_composite_id(device),
error);
if (devices_possible == NULL)
return FALSE;
}
/* parse silo */
helper->cabinet = fu_engine_build_cabinet_from_stream(engine, helper->stream, error);
if (helper->cabinet == NULL)
return FALSE;
/* for each component in the silo */
components = fu_cabinet_get_components(helper->cabinet, error);
if (components == NULL)
return FALSE;
helper->action_ids = g_ptr_array_new_with_free_func(g_free);
helper->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
helper->errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free);
helper->remote_id = fu_engine_get_remote_id_for_stream(engine, helper->stream);
/* do any devices pass the requirements */
for (guint i = 0; i < components->len; i++) {
XbNode *component = g_ptr_array_index(components, i);
for (guint j = 0; j < devices_possible->len; j++) {
FuDevice *device = g_ptr_array_index(devices_possible, j);
/* emulating */
if ((helper->flags & FWUPD_INSTALL_FLAG_ONLY_EMULATED) &&
!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) {
g_debug("skipping non-emulated %s", fu_device_get_id(device));
continue;
}
g_debug("testing device %u [%s] with component %u",
j,
fu_device_get_id(device),
i);
if (!fu_dbus_daemon_install_with_helper_device(helper,
component,
device,
error))
return FALSE;
}
}
/* order the install tasks by the device priority */
g_ptr_array_sort(helper->releases, fu_dbus_daemon_release_sort_cb);
/* nothing suitable */
if (helper->releases->len == 0) {
GError *error_tmp = fu_engine_error_array_get_best(helper->errors);
g_propagate_error(error, error_tmp);
return FALSE;
}
/* authenticate all things in the action_ids */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
fu_dbus_daemon_authorize_install_queue(g_steal_pointer(&helper));
return TRUE;
}
#endif /* HAVE_GIO_UNIX */
typedef struct {
gchar *id;
gchar *sender;
guint watcher_id;
} FuDbusDaemonSystemInhibit;
static void
fu_dbus_daemon_system_inhibit_free(FuDbusDaemonSystemInhibit *inhibit)
{
g_bus_unwatch_name(inhibit->watcher_id);
g_free(inhibit->id);
g_free(inhibit->sender);
g_free(inhibit);
}
static void
fu_dbus_daemon_ensure_system_inhibit(FuDbusDaemon *self)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
FuContext *ctx = fu_engine_get_context(engine);
if (self->system_inhibits->len > 0) {
fu_context_add_flag(ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT);
return;
}
fu_context_remove_flag(ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT);
}
static void
fu_dbus_daemon_inhibit_name_vanished_cb(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
for (guint i = 0; i < self->system_inhibits->len; i++) {
FuDbusDaemonSystemInhibit *inhibit = g_ptr_array_index(self->system_inhibits, i);
if (g_strcmp0(inhibit->sender, name) == 0) {
g_debug("removing %s as %s vanished without calling Uninhibit",
inhibit->id,
name);
g_ptr_array_remove_index(self->system_inhibits, i);
fu_dbus_daemon_ensure_system_inhibit(self);
break;
}
}
}
#ifdef HAVE_GIO_UNIX
static void
fu_dbus_daemon_client_flags_notify_cb(FuClient *client, GParamSpec *pspec, FuMainAuthHelper *helper)
{
if (!fu_client_has_flag(client, FU_CLIENT_FLAG_ACTIVE)) {
g_info("%s vanished before completion of install on %s",
fu_client_get_sender(client),
helper->device_id);
fu_progress_add_flag(helper->progress, FU_PROGRESS_FLAG_NO_SENDER);
}
}
#endif
static GInputStream *
fu_dbus_daemon_invocation_get_input_stream(GDBusMethodInvocation *invocation, GError **error)
{
#ifdef HAVE_GIO_UNIX
GDBusMessage *message;
GUnixFDList *fd_list;
gint fd;
g_autoptr(GInputStream) stream = NULL;
/* 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_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no file descriptors are associated");
return NULL;
}
if (g_unix_fd_list_get_length(fd_list) != 1) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"wrong number of file descriptors: %i",
g_unix_fd_list_get_length(fd_list));
return NULL;
}
fd = g_unix_fd_list_get(fd_list, 0, error);
if (fd < 0)
return NULL;
/* get details about the file (will close the fd when done) */
stream = fu_unix_seekable_input_stream_new(fd, TRUE);
if (stream == NULL) {
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid stream");
return NULL;
}
return g_steal_pointer(&stream);
#else
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature");
return NULL;
#endif
}
static GOutputStream *
fu_dbus_daemon_invocation_get_output_stream(GDBusMethodInvocation *invocation, GError **error)
{
#ifdef HAVE_GIO_UNIX
GDBusMessage *message;
GUnixFDList *fd_list;
gint fd;
g_autoptr(GOutputStream) stream = NULL;
/* 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_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no file descriptors are associated");
return NULL;
}
if (g_unix_fd_list_get_length(fd_list) != 1) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"wrong number of file descriptors: %i",
g_unix_fd_list_get_length(fd_list));
return NULL;
}
fd = g_unix_fd_list_get(fd_list, 0, error);
if (fd < 0)
return NULL;
/* get details about the file (will close the fd when done) */
stream = g_unix_output_stream_new(fd, TRUE);
if (stream == NULL) {
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid stream");
return NULL;
}
return g_steal_pointer(&stream);
#else
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature");
return NULL;
#endif
}
static gboolean
fu_dbus_daemon_hsi_supported(FuDbusDaemon *self, GError **error)
{
#ifdef HAVE_HSI
g_autofree gchar *sysfsfwdir = NULL;
g_autofree gchar *xen_privileged_fn = NULL;
if (g_getenv("UMOCKDEV_DIR") != NULL)
return TRUE;
if (fu_daemon_get_machine_kind(FU_DAEMON(self)) == FU_DAEMON_MACHINE_KIND_PHYSICAL)
return TRUE;
sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW_ATTRIB);
/* privileged xen can access most hardware */
xen_privileged_fn =
g_build_filename(sysfsfwdir, "hypervisor", "start_flags", "privileged", NULL);
if (g_file_test(xen_privileged_fn, G_FILE_TEST_EXISTS)) {
g_autofree gchar *contents = NULL;
if (g_file_get_contents(xen_privileged_fn, &contents, NULL, NULL)) {
if (g_strcmp0(contents, "1") == 0)
return TRUE;
}
}
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"HSI unavailable for hypervisor");
#else
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"HSI support not enabled");
#endif
return FALSE;
}
static void
fu_dbus_daemon_method_get_devices(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GVariant *val;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
devices = fu_engine_get_devices(engine, &error);
if (devices == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
val = fu_dbus_daemon_device_array_to_variant(self, request, devices, &error);
if (val == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, val);
}
static void
fu_dbus_daemon_method_get_plugins(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GVariant *val;
val = fwupd_codec_array_to_variant(fu_engine_get_plugins(engine), FWUPD_CODEC_FLAG_NONE);
g_dbus_method_invocation_return_value(invocation, val);
}
static void
fu_dbus_daemon_method_get_releases(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *device_id;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_variant_get(parameters, "(&s)", &device_id);
releases = fu_engine_get_releases(engine, request, device_id, &error);
if (releases == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE));
}
static void
fu_dbus_daemon_method_get_approved_firmware(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GPtrArray *checksums = fu_engine_get_approved_firmware(engine);
GVariantBuilder builder;
GVariant *val;
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));
}
static void
fu_dbus_daemon_method_get_blocked_firmware(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GPtrArray *checksums = fu_engine_get_blocked_firmware(engine);
GVariantBuilder builder;
GVariant *val;
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));
}
static void
fu_dbus_daemon_method_get_report_metadata(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *key;
const gchar *value;
GHashTableIter iter;
GVariantBuilder builder;
GVariant *val;
g_autoptr(GError) error = NULL;
g_autoptr(GHashTable) metadata = NULL;
metadata = fu_engine_get_report_metadata(engine, &error);
if (metadata == NULL) {
fu_dbus_daemon_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));
}
static void
fu_dbus_daemon_method_set_approved_firmware(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
g_autofree gchar *checksums_str = NULL;
g_auto(GStrv) checksums = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(^as)", &checksums);
checksums_str = g_strjoinv(",", checksums);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->flags = FU_FIRMWARE_PARSE_FLAG_NO_SEARCH;
helper->request = g_object_ref(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]));
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.set-approved-firmware",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_set_approved_firmware_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_set_blocked_firmware(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
g_autofree gchar *checksums_str = NULL;
g_auto(GStrv) checksums = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(^as)", &checksums);
checksums_str = g_strjoinv(",", checksums);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(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]));
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.set-approved-firmware",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_set_blocked_firmware_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_authorize_quit_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
fu_daemon_schedule_process_quit(FU_DAEMON(helper->self));
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_method_quit(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
g_autoptr(FuMainAuthHelper) helper = NULL;
/* is root */
if (fu_engine_request_has_converter_flag(request, FWUPD_CODEC_FLAG_TRUSTED)) {
fu_daemon_schedule_process_quit(FU_DAEMON(self));
g_dbus_method_invocation_return_value(invocation, NULL);
return;
}
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.quit",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_quit_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_self_sign(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
GVariant *prop_value;
const gchar *prop_key;
g_autofree gchar *value = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(GVariantIter) iter = NULL;
g_variant_get(parameters, "(sa{sv})", &value, &iter);
/* 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_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper->self = self;
helper->value = g_steal_pointer(&value);
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.self-sign",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_self_sign_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_get_downgrades(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *device_id;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_variant_get(parameters, "(&s)", &device_id);
releases = fu_engine_get_downgrades(engine, request, device_id, &error);
if (releases == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE));
}
static void
fu_dbus_daemon_method_get_upgrades(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *device_id;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_variant_get(parameters, "(&s)", &device_id);
releases = fu_engine_get_upgrades(engine, request, device_id, &error);
if (releases == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE));
}
static void
fu_dbus_daemon_method_get_remotes(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) remotes = NULL;
remotes = fu_engine_get_remotes(engine, &error);
if (remotes == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(remotes, FWUPD_CODEC_FLAG_NONE));
}
static void
fu_dbus_daemon_method_get_history(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GVariant *val;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
devices = fu_engine_get_history(engine, &error);
if (devices == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
val = fu_dbus_daemon_device_array_to_variant(self, request, devices, &error);
if (val == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, val);
}
static void
fu_dbus_daemon_method_get_host_security_attrs(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
g_autoptr(GError) error = NULL;
g_autoptr(FuSecurityAttrs) attrs = NULL;
if (!fu_dbus_daemon_hsi_supported(self, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
attrs = fu_engine_get_host_security_attrs(engine);
g_dbus_method_invocation_return_value(invocation, fu_security_attrs_to_variant(attrs));
}
static void
fu_dbus_daemon_method_get_host_security_events(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
#ifdef HAVE_HSI
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
guint limit = 0;
g_autoptr(FuSecurityAttrs) attrs = NULL;
g_autoptr(GError) error = NULL;
g_variant_get(parameters, "(u)", &limit);
attrs = fu_engine_get_host_security_events(engine, limit, &error);
if (attrs == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, fu_security_attrs_to_variant(attrs));
#else
g_dbus_method_invocation_return_error_literal(invocation,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"HSI support not enabled");
#endif
}
static void
fu_dbus_daemon_method_clear_results(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *device_id;
g_autoptr(GError) error = NULL;
g_variant_get(parameters, "(&s)", &device_id);
if (!fu_engine_clear_results(engine, device_id, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_method_search(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *token;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_variant_get(parameters, "(&s)", &token);
releases = fu_engine_search(engine, token, &error);
if (releases == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE));
}
static void
fu_dbus_daemon_authorize_emulation_load_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(GInputStream) stream = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* get stream */
stream = fu_dbus_daemon_invocation_get_input_stream(helper->invocation, &error);
if (stream == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* load data into engine */
if (!fu_engine_emulation_load(engine, stream, &error)) {
g_dbus_method_invocation_return_error(helper->invocation,
error->domain,
error->code,
"failed to load emulation data: %s",
error->message);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_method_emulation_load(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
gint32 fd_handle = 0;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(h)", &fd_handle);
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->handle = fd_handle;
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.emulation-load",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_emulation_load_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_authorize_emulation_save_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(GOutputStream) stream = NULL;
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self));
/* get result */
if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* get stream */
stream = fu_dbus_daemon_invocation_get_output_stream(helper->invocation, &error);
if (stream == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* save data from engine */
if (!fu_engine_emulation_save(engine, stream, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error);
return;
}
/* success */
g_dbus_method_invocation_return_value(helper->invocation, NULL);
}
static void
fu_dbus_daemon_method_emulation_save(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
gint32 fd_handle = 0;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(h)", &fd_handle);
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->handle = fd_handle;
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.emulation-save",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_emulation_save_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_authorize_modify_device_internal(FuEngine *engine,
const gchar *device_id,
const gchar *key,
const gchar *value,
GDBusMethodInvocation *invocation)
{
g_autoptr(GError) error = NULL;
if (!fu_engine_modify_device(engine, device_id, key, value, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_authorize_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data;
fu_dbus_daemon_authorize_modify_device_internal(
fu_daemon_get_engine(FU_DAEMON(helper->self)),
helper->device_id,
helper->key,
helper->value,
helper->invocation);
}
static gboolean
fu_dbus_daemon_method_modify_device_flag_needs_auth(const gchar *key, const gchar *value)
{
if (g_strcmp0(key, "Flags") != 0)
return FALSE;
if (g_strcmp0(value, "emulation-tag") == 0)
return TRUE;
if (g_strcmp0(value, "~emulation-tag") == 0)
return TRUE;
return FALSE;
}
static void
fu_dbus_daemon_method_modify_device(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *device_id;
const gchar *key;
const gchar *value;
g_variant_get(parameters, "(&s&s&s)", &device_id, &key, &value);
if (fu_dbus_daemon_method_modify_device_flag_needs_auth(key, value)) {
g_autoptr(FuMainAuthHelper) helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->device_id = g_strdup(device_id);
helper->key = g_strdup(key);
helper->value = g_strdup(value);
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
fu_polkit_authority_check(
self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.emulation-tag",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_modify_device_cb,
g_steal_pointer(&helper));
} else
fu_dbus_daemon_authorize_modify_device_internal(
fu_daemon_get_engine(FU_DAEMON(self)),
device_id,
key,
value,
invocation);
}
static void
fu_dbus_daemon_method_get_results(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GVariant *val;
const gchar *device_id = NULL;
g_autoptr(FwupdDevice) device = NULL;
g_autoptr(GError) error = NULL;
g_variant_get(parameters, "(&s)", &device_id);
device = fu_engine_get_results(engine, device_id, &error);
if (device == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_TRUSTED);
g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&val, 1));
}
static void
fu_dbus_daemon_method_update_metadata(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
#ifdef HAVE_GIO_UNIX
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GDBusMessage *message;
GUnixFDList *fd_list;
const gchar *remote_id = NULL;
gint fd_data;
gint fd_sig;
g_autoptr(GError) error = NULL;
g_variant_get(parameters, "(&shh)", &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_set_error_literal(&error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no file descriptors are associated");
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
if (g_unix_fd_list_get_length(fd_list) != 2) {
g_set_error(&error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"wrong number of file descriptors: %i",
g_unix_fd_list_get_length(fd_list));
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
fd_data = g_unix_fd_list_get(fd_list, 0, &error);
if (fd_data < 0) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
fd_sig = g_unix_fd_list_get(fd_list, 1, &error);
if (fd_sig < 0) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
/* store new metadata (will close the fds when done) */
if (!fu_engine_update_metadata(engine, remote_id, fd_data, fd_sig, &error)) {
g_prefix_error(&error, "Failed to update metadata for %s: ", remote_id);
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, NULL);
#else
g_dbus_method_invocation_return_error_literal(invocation,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"unsupported feature");
#endif /* HAVE_GIO_UNIX */
}
static void
fu_dbus_daemon_method_unlock(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *device_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(&s)", &device_id);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->device_id = g_strdup(device_id);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.device-unlock",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_unlock_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_activate(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *device_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(&s)", &device_id);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->device_id = g_strdup(device_id);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.device-activate",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_activate_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_modify_config(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
g_autofree gchar *key = NULL;
g_autofree gchar *section = NULL;
g_autofree gchar *value = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(sss)", &section, &key, &value);
/* authenticate */
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->section = g_steal_pointer(&section);
helper->key = g_steal_pointer(&key);
helper->value = g_steal_pointer(&value);
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.modify-config",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_modify_config_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_reset_config(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
g_autofree gchar *section = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(s)", &section);
/* authenticate */
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->section = g_steal_pointer(&section);
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.reset-config",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_reset_config_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_modify_remote(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *remote_id = NULL;
const gchar *key = NULL;
const gchar *value = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
/* check the id exists */
g_variant_get(parameters, "(&s&s&s)", &remote_id, &key, &value);
/* create helper object */
helper = g_new0(FuMainAuthHelper, 1);
helper->request = g_object_ref(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->self = self;
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.modify-remote",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_modify_remote_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_clean_remote(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *remote_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
/* check the id exists */
g_variant_get(parameters, "(&s)", &remote_id);
/* create helper object */
helper = g_new0(FuMainAuthHelper, 1);
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->remote_id = g_strdup(remote_id);
helper->self = self;
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.clean-remote",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_clean_remote_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_verify_update(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *device_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
/* check the id exists */
g_variant_get(parameters, "(&s)", &device_id);
/* create helper object */
helper = g_new0(FuMainAuthHelper, 1);
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->device_id = g_strdup(device_id);
helper->self = self;
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.verify-update",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_verify_update_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_verify(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
const gchar *device_id = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_variant_get(parameters, "(&s)", &device_id);
/* progress */
fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL);
g_signal_connect(FU_PROGRESS(progress),
"percentage-changed",
G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb),
self);
g_signal_connect(FU_PROGRESS(progress),
"status-changed",
G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb),
self);
if (!fu_engine_verify(engine, device_id, progress, &error)) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_method_set_feature_flags(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
guint64 feature_flags_u64 = 0;
g_autoptr(FuClient) client = NULL;
g_variant_get(parameters, "(t)", &feature_flags_u64);
/* old flags for the same sender will be automatically destroyed */
client = fu_client_list_register(self->client_list, fu_engine_request_get_sender(request));
fu_client_set_feature_flags(client, feature_flags_u64);
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_method_set_hints(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *prop_key;
const gchar *prop_value;
g_autoptr(FuClient) client = NULL;
g_autoptr(GVariantIter) iter = NULL;
g_variant_get(parameters, "(a{ss})", &iter);
client = fu_client_list_register(self->client_list, fu_engine_request_get_sender(request));
while (g_variant_iter_next(iter, "{&s&s}", &prop_key, &prop_value)) {
g_debug("got hint %s=%s", prop_key, prop_value);
fu_client_insert_hint(client, prop_key, prop_value);
}
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_method_inhibit(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuDbusDaemonSystemInhibit *inhibit;
const gchar *reason = NULL;
g_variant_get(parameters, "(&s)", &reason);
/* watch */
inhibit = g_new0(FuDbusDaemonSystemInhibit, 1);
inhibit->sender = g_strdup(fu_engine_request_get_sender(request));
inhibit->id =
g_strdup_printf("dbus-%i", g_random_int_range(1, G_MAXINT - 1)); /* nocheck:blocked */
inhibit->watcher_id =
g_bus_watch_name_on_connection(self->connection,
fu_engine_request_get_sender(request),
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
fu_dbus_daemon_inhibit_name_vanished_cb,
self,
NULL);
g_ptr_array_add(self->system_inhibits, inhibit);
fu_dbus_daemon_ensure_system_inhibit(self);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", inhibit->id));
}
static void
fu_dbus_daemon_method_install(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
#ifdef HAVE_GIO_UNIX
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
GVariant *prop_value;
const gchar *device_id = NULL;
const gchar *prop_key;
gint32 fd_handle = 0;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GVariantIter) iter = NULL;
/* check the id exists */
g_variant_get(parameters, "(&sha{sv})", &device_id, &fd_handle, &iter);
/* create helper object */
helper = g_new0(FuMainAuthHelper, 1);
helper->request = g_object_ref(request);
helper->progress = fu_progress_new(G_STRLOC);
helper->invocation = g_object_ref(invocation);
helper->device_id = g_strdup(device_id);
helper->self = self;
/* 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, "install-flags") == 0)
helper->flags = g_variant_get_uint64(prop_value);
/* these are all set by libfwupd < 2.0.x; parse for compatibility */
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;
g_variant_unref(prop_value);
}
/* get stream */
helper->stream = fu_dbus_daemon_invocation_get_input_stream(invocation, &error);
if (helper->stream == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
/* relax these */
if (fu_engine_config_get_ignore_requirements(fu_engine_get_config(engine)))
helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS;
/* install all the things in the store */
helper->client =
fu_client_list_register(self->client_list, fu_engine_request_get_sender(request));
helper->client_sender_changed_id =
g_signal_connect(FU_CLIENT(helper->client),
"notify::flags",
G_CALLBACK(fu_dbus_daemon_client_flags_notify_cb),
helper);
if (!fu_dbus_daemon_install_with_helper(g_steal_pointer(&helper), &error)) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
#else
g_dbus_method_invocation_return_error_literal(invocation,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"unsupported feature");
#endif /* HAVE_GIO_UNIX */
}
static void
fu_dbus_daemon_method_uninhibit(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *inhibit_id = NULL;
gboolean found = FALSE;
g_variant_get(parameters, "(&s)", &inhibit_id);
/* find by id, then uninhibit device */
for (guint i = 0; i < self->system_inhibits->len; i++) {
FuDbusDaemonSystemInhibit *inhibit = g_ptr_array_index(self->system_inhibits, i);
if (g_strcmp0(inhibit->id, inhibit_id) == 0) {
g_ptr_array_remove_index(self->system_inhibits, i);
fu_dbus_daemon_ensure_system_inhibit(self);
found = TRUE;
break;
}
}
if (!found) {
g_dbus_method_invocation_return_error_literal(invocation,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"Cannot find inhibit ID");
return;
}
g_dbus_method_invocation_return_value(invocation, NULL);
}
static void
fu_dbus_daemon_method_get_details(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
#ifdef HAVE_GIO_UNIX
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
gint32 fd_handle = 0;
g_autoptr(GError) error = NULL;
g_autoptr(GInputStream) stream = NULL;
g_autoptr(GPtrArray) results = NULL;
/* get parameters */
g_variant_get(parameters, "(h)", &fd_handle);
/* get stream */
stream = fu_dbus_daemon_invocation_get_input_stream(invocation, &error);
if (stream == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
/* get details about the file */
results = fu_engine_get_details(engine, request, stream, &error);
if (results == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_array_to_variant(results, FWUPD_CODEC_FLAG_TRUSTED));
#else
g_dbus_method_invocation_return_error_literal(invocation,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"unsupported feature");
#endif /* HAVE_GIO_UNIX */
}
static void
fu_dbus_daemon_method_get_bios_settings(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
gboolean authenticate =
fu_engine_request_get_feature_flags(request) & FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION;
if (!authenticate) {
/* if we cannot authenticate and the peer is not
* inherently trusted, only return a non-sensitive
* subset of the settings */
g_autoptr(FuBiosSettings) attrs =
fu_context_get_bios_settings(fu_engine_get_context(engine));
g_dbus_method_invocation_return_value(
invocation,
fwupd_codec_to_variant(FWUPD_CODEC(attrs),
fu_engine_request_get_converter_flags(request)));
} else {
g_autoptr(FuMainAuthHelper) helper = NULL;
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
fu_polkit_authority_check(
self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.get-bios-settings",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_get_bios_settings_cb,
g_steal_pointer(&helper));
}
}
static void
fu_dbus_daemon_method_set_bios_settings(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *key;
const gchar *value;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(GVariantIter) iter = NULL;
g_variant_get(parameters, "(a{ss})", &iter);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->bios_settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
while (g_variant_iter_next(iter, "{&s&s}", &key, &value)) {
g_debug("got setting %s=%s", key, value);
g_hash_table_insert(helper->bios_settings, g_strdup(key), g_strdup(value));
}
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.set-bios-settings",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_set_bios_settings_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_fix_host_security_attr(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *appstream_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(&s)", &appstream_id);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->key = g_strdup(appstream_id);
helper->is_fix = TRUE;
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.fix-host-security-attr",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_fix_host_security_attr_cb,
g_steal_pointer(&helper));
}
static void
fu_dbus_daemon_method_undo_host_security_attr(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation)
{
const gchar *appstream_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_variant_get(parameters, "(&s)", &appstream_id);
/* authenticate */
fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0(FuMainAuthHelper, 1);
helper->self = self;
helper->request = g_object_ref(request);
helper->invocation = g_object_ref(invocation);
helper->key = g_strdup(appstream_id);
helper->is_fix = FALSE;
fu_polkit_authority_check(self->authority,
fu_engine_request_get_sender(request),
"org.freedesktop.fwupd.undo-host-security-attr",
fu_dbus_daemon_engine_request_get_authority_check_flags(request),
NULL,
fu_dbus_daemon_authorize_undo_host_security_attr_cb,
g_steal_pointer(&helper));
}
typedef void (*FuDbusDaemonMethodFunc)(FuDbusDaemon *self,
GVariant *parameters,
FuEngineRequest *request,
GDBusMethodInvocation *invocation);
static void
fu_dbus_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)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
g_autoptr(FuEngineRequest) request = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GString) parameters_str = NULL;
struct {
const gchar *name;
FuDbusDaemonMethodFunc func;
} method_funcs[] = {
{"GetDevices", fu_dbus_daemon_method_get_devices},
{"GetPlugins", fu_dbus_daemon_method_get_plugins},
{"GetReleases", fu_dbus_daemon_method_get_releases},
{"GetApprovedFirmware", fu_dbus_daemon_method_get_approved_firmware},
{"GetBlockedFirmware", fu_dbus_daemon_method_get_blocked_firmware},
{"GetReportMetadata", fu_dbus_daemon_method_get_report_metadata},
{"SetApprovedFirmware", fu_dbus_daemon_method_set_approved_firmware},
{"SetBlockedFirmware", fu_dbus_daemon_method_set_blocked_firmware},
{"Quit", fu_dbus_daemon_method_quit},
{"SelfSign", fu_dbus_daemon_method_self_sign},
{"GetDowngrades", fu_dbus_daemon_method_get_downgrades},
{"GetUpgrades", fu_dbus_daemon_method_get_upgrades},
{"GetRemotes", fu_dbus_daemon_method_get_remotes},
{"GetHistory", fu_dbus_daemon_method_get_history},
{"GetHostSecurityAttrs", fu_dbus_daemon_method_get_host_security_attrs},
{"GetHostSecurityEvents", fu_dbus_daemon_method_get_host_security_events},
{"ClearResults", fu_dbus_daemon_method_clear_results},
{"EmulationLoad", fu_dbus_daemon_method_emulation_load},
{"EmulationSave", fu_dbus_daemon_method_emulation_save},
{"Search", fu_dbus_daemon_method_search},
{"ModifyDevice", fu_dbus_daemon_method_modify_device},
{"GetResults", fu_dbus_daemon_method_get_results},
{"UpdateMetadata", fu_dbus_daemon_method_update_metadata},
{"Unlock", fu_dbus_daemon_method_unlock},
{"Activate", fu_dbus_daemon_method_activate},
{"ModifyConfig", fu_dbus_daemon_method_modify_config},
{"ResetConfig", fu_dbus_daemon_method_reset_config},
{"ModifyRemote", fu_dbus_daemon_method_modify_remote},
{"CleanRemote", fu_dbus_daemon_method_clean_remote},
{"VerifyUpdate", fu_dbus_daemon_method_verify_update},
{"Verify", fu_dbus_daemon_method_verify},
{"SetFeatureFlags", fu_dbus_daemon_method_set_feature_flags},
{"SetHints", fu_dbus_daemon_method_set_hints},
{"Inhibit", fu_dbus_daemon_method_inhibit},
{"Uninhibit", fu_dbus_daemon_method_uninhibit},
{"Install", fu_dbus_daemon_method_install},
{"GetDetails", fu_dbus_daemon_method_get_details},
{"GetBiosSettings", fu_dbus_daemon_method_get_bios_settings},
{"SetBiosSettings", fu_dbus_daemon_method_set_bios_settings},
{"FixHostSecurityAttr", fu_dbus_daemon_method_fix_host_security_attr},
{"UndoHostSecurityAttr", fu_dbus_daemon_method_undo_host_security_attr},
};
/* build request */
request = fu_dbus_daemon_create_request(self, sender, &error);
if (request == NULL) {
fu_dbus_daemon_method_invocation_return_gerror(invocation, error);
return;
}
/* activity */
fu_engine_idle_reset(engine);
/* be helpful */
parameters_str = g_variant_print_string(parameters, NULL, TRUE);
g_debug("called %s%s", method_name, parameters_str->str);
/* call the correct vfunc */
for (guint i = 0; i < G_N_ELEMENTS(method_funcs); i++) {
if (g_strcmp0(method_name, method_funcs[i].name) == 0) {
method_funcs[i].func(self, parameters, request, invocation);
return;
}
}
g_dbus_method_invocation_return_error(invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"no such method %s",
method_name);
}
static GVariant *
fu_dbus_daemon_get_property_hwids(FuDbusDaemon *self)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
FuContext *ctx = fu_engine_get_context(engine);
FuHwids *hwids = fu_context_get_hwids(ctx);
GVariantBuilder builder;
g_autoptr(GPtrArray) chid_keys = fu_hwids_get_chid_keys(hwids);
g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids);
g_variant_builder_init(&builder, G_VARIANT_TYPE("a(ss)"));
for (guint i = 0; i < hwid_keys->len; i++) {
const gchar *hwid_key = g_ptr_array_index(hwid_keys, i);
const gchar *value = fu_hwids_get_value(hwids, hwid_key);
if (value == NULL)
continue;
g_variant_builder_add(&builder, "(ss)", hwid_key, value);
}
for (guint i = 0; i < chid_keys->len; i++) {
const gchar *key = g_ptr_array_index(chid_keys, i);
const gchar *keys = NULL;
g_autofree gchar *guid = NULL;
/* get the GUID */
keys = fu_hwids_get_replace_keys(hwids, key);
if (keys == NULL)
continue;
guid = fu_hwids_get_guid(hwids, key, NULL);
if (guid == NULL)
continue;
g_variant_builder_add(&builder, "(ss)", keys, guid);
}
/* done */
return g_variant_builder_end(&builder);
}
static GVariant *
fu_dbus_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)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
/* activity */
fu_engine_idle_reset(engine);
if (g_strcmp0(property_name, "DaemonVersion") == 0)
return g_variant_new_string(PACKAGE_VERSION);
if (g_strcmp0(property_name, "HostBkc") == 0)
return g_variant_new_string(fu_engine_get_host_bkc(engine));
if (g_strcmp0(property_name, "Tainted") == 0)
return g_variant_new_boolean(FALSE);
if (g_strcmp0(property_name, "Status") == 0)
return g_variant_new_uint32(self->status);
if (g_strcmp0(property_name, "Percentage") == 0)
return g_variant_new_uint32(self->percentage);
if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_LEVEL) == 0) {
FuContext *ctx = fu_engine_get_context(engine);
return g_variant_new_uint32(fu_context_get_battery_level(ctx));
}
if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_THRESHOLD) == 0) {
FuContext *ctx = fu_engine_get_context(engine);
return g_variant_new_uint32(fu_context_get_battery_threshold(ctx));
}
if (g_strcmp0(property_name, "HostVendor") == 0)
return g_variant_new_string(fu_engine_get_host_vendor(engine));
if (g_strcmp0(property_name, "HostProduct") == 0)
return g_variant_new_string(fu_engine_get_host_product(engine));
if (g_strcmp0(property_name, "HostMachineId") == 0) {
const gchar *tmp = fu_engine_get_host_machine_id(engine);
if (tmp == NULL) {
g_set_error(error, /* nocheck:error */
G_DBUS_ERROR,
G_DBUS_ERROR_NOT_SUPPORTED,
"failed to get daemon property %s",
property_name);
return NULL;
}
return g_variant_new_string(tmp);
}
if (g_strcmp0(property_name, "HostSecurityId") == 0) {
#ifdef HAVE_HSI
g_autofree gchar *tmp = fu_engine_get_host_security_id(engine, NULL);
return g_variant_new_string(tmp);
#else
g_set_error(error, /* nocheck:error */
G_DBUS_ERROR,
G_DBUS_ERROR_NOT_SUPPORTED,
"failed to get daemon property %s",
property_name);
return NULL;
#endif
}
if (g_strcmp0(property_name, "Interactive") == 0)
return g_variant_new_boolean(isatty(fileno(stdout)) != 0);
if (g_strcmp0(property_name, "OnlyTrusted") == 0) {
return g_variant_new_boolean(
fu_engine_config_get_only_trusted(fu_engine_get_config(engine)));
}
if (g_strcmp0(property_name, "Hwids") == 0)
return fu_dbus_daemon_get_property_hwids(self);
/* return an error */
g_set_error(error, /* nocheck:error */
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_PROPERTY,
"failed to get daemon property %s",
property_name);
return NULL;
}
static gboolean
fu_dbus_daemon_register_object(FuDbusDaemon *self, GError **error)
{
guint registration_id;
static const GDBusInterfaceVTable interface_vtable = {fu_dbus_daemon_method_call,
fu_dbus_daemon_get_property,
NULL};
registration_id =
g_dbus_connection_register_object(self->connection,
FWUPD_DBUS_PATH,
self->introspection_daemon->interfaces[0],
&interface_vtable,
self, /* user_data */
NULL, /* user_data_free_func */
NULL); /* GError** */
if (registration_id == 0) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"unspecified failure");
return FALSE;
}
/* success */
return TRUE;
}
static void
fu_dbus_daemon_client_list_ensure_inhibit(FuDbusDaemon *self)
{
FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self));
g_autoptr(GPtrArray) clients = fu_client_list_get_all(self->client_list);
g_debug("connected clients: %u", clients->len);
if (clients->len > 0 && self->clients_inhibit_id == 0) {
self->clients_inhibit_id =
fu_engine_idle_inhibit(engine, FU_IDLE_INHIBIT_TIMEOUT, "connected-clients");
} else if (clients->len == 0 && self->clients_inhibit_id != 0) {
fu_engine_idle_uninhibit(engine, self->clients_inhibit_id);
self->clients_inhibit_id = 0;
}
}
static void
fu_dbus_daemon_client_list_added_cb(FuClientList *client_list, FuClient *client, gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
fu_dbus_daemon_client_list_ensure_inhibit(self);
}
static void
fu_dbus_daemon_client_list_removed_cb(FuClientList *client_list,
FuClient *client,
gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
fu_dbus_daemon_client_list_ensure_inhibit(self);
}
static void
fu_dbus_daemon_set_connection(FuDbusDaemon *self, GDBusConnection *connection)
{
g_set_object(&self->connection, connection);
if (connection != NULL) {
g_autoptr(FuClientList) client_list = fu_client_list_new(connection);
g_signal_connect(client_list,
"added",
G_CALLBACK(fu_dbus_daemon_client_list_added_cb),
self);
g_signal_connect(client_list,
"removed",
G_CALLBACK(fu_dbus_daemon_client_list_removed_cb),
self);
g_set_object(&self->client_list, client_list);
}
}
static void
fu_dbus_daemon_dbus_bus_acquired_cb(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
g_autoptr(GError) error = NULL;
/* connect to D-Bus directly */
self->proxy_uid = g_dbus_proxy_new_sync(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 (self->proxy_uid == NULL) {
g_warning("cannot connect to DBus: %s", error->message);
return;
}
fu_dbus_daemon_set_connection(self, connection);
if (!fu_dbus_daemon_register_object(self, &error)) {
g_warning("cannot register object: %s", error->message);
return;
}
}
static void
fu_dbus_daemon_dbus_name_acquired_cb(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_debug("acquired name: %s", name);
}
static void
fu_dbus_daemon_dbus_name_lost_cb(GDBusConnection *connection, const gchar *name, gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
g_warning("another service has claimed the dbus name %s", name);
fu_daemon_stop(FU_DAEMON(self), NULL);
}
static void
fu_dbus_daemon_dbus_connection_closed_cb(GDBusConnection *connection,
gboolean remote_peer_vanished,
GError *error,
gpointer user_data)
{
if (remote_peer_vanished)
g_info("client connection closed: %s", error != NULL ? error->message : "unknown");
}
static gboolean
fu_dbus_daemon_dbus_new_connection_cb(GDBusServer *server,
GDBusConnection *connection,
gpointer user_data)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(user_data);
fu_dbus_daemon_set_connection(self, connection);
g_signal_connect(connection,
"closed",
G_CALLBACK(fu_dbus_daemon_dbus_connection_closed_cb),
self);
return fu_dbus_daemon_register_object(self, NULL);
}
static GDBusNodeInfo *
fu_dbus_daemon_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_resources_lookup_data(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_dbus_daemon_setup(FuDaemon *daemon,
const gchar *socket_address,
FuProgress *progress,
GError **error)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(daemon);
FuEngine *engine = fu_daemon_get_engine(daemon);
/* progress */
fu_progress_set_id(progress, G_STRLOC);
fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL);
fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "load-engine");
fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-introspection");
fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-authority");
fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "own-name");
/* load engine */
g_signal_connect(FU_ENGINE(engine),
"changed",
G_CALLBACK(fu_dbus_daemon_engine_changed_cb),
self);
g_signal_connect(FU_ENGINE(engine),
"device-added",
G_CALLBACK(fu_dbus_daemon_engine_device_added_cb),
self);
g_signal_connect(FU_ENGINE(engine),
"device-removed",
G_CALLBACK(fu_dbus_daemon_engine_device_removed_cb),
self);
g_signal_connect(FU_ENGINE(engine),
"device-changed",
G_CALLBACK(fu_dbus_daemon_engine_device_changed_cb),
self);
g_signal_connect(FU_ENGINE(engine),
"device-request",
G_CALLBACK(fu_dbus_daemon_engine_device_request_cb),
self);
g_signal_connect(FU_ENGINE(engine),
"status-changed",
G_CALLBACK(fu_dbus_daemon_engine_status_changed_cb),
self);
if (!fu_engine_load(engine,
FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO |
FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS |
FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS |
FU_ENGINE_LOAD_FLAG_ENSURE_CLIENT_CERT |
FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG,
fu_progress_get_child(progress),
error)) {
g_prefix_error_literal(error, "failed to load engine: ");
return FALSE;
}
fu_progress_step_done(progress);
/* load introspection from file */
self->introspection_daemon =
fu_dbus_daemon_load_introspection(FWUPD_DBUS_INTERFACE ".xml", error);
if (self->introspection_daemon == NULL) {
g_prefix_error_literal(error, "failed to load introspection: ");
return FALSE;
}
fu_progress_step_done(progress);
/* get authority */
self->authority = fu_polkit_authority_new();
if (!fu_polkit_authority_load(self->authority, error))
return FALSE;
fu_progress_step_done(progress);
/* own the object */
if (socket_address != NULL) {
g_autofree gchar *guid = g_dbus_generate_guid();
g_autoptr(GDBusServer) server = NULL;
server = g_dbus_server_new_sync(socket_address,
G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
guid,
NULL,
NULL,
error);
if (server == NULL) {
g_prefix_error_literal(error, "failed to create D-Bus server: ");
return FALSE;
}
g_message("using socket address: %s", g_dbus_server_get_client_address(server));
g_dbus_server_start(server);
g_signal_connect(server,
"new-connection",
G_CALLBACK(fu_dbus_daemon_dbus_new_connection_cb),
self);
} else {
self->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_dbus_daemon_dbus_bus_acquired_cb,
fu_dbus_daemon_dbus_name_acquired_cb,
fu_dbus_daemon_dbus_name_lost_cb,
self,
NULL);
}
fu_progress_step_done(progress);
/* success */
return TRUE;
}
static void
fu_dbus_daemon_init(FuDbusDaemon *self)
{
self->status = FWUPD_STATUS_IDLE;
self->system_inhibits =
g_ptr_array_new_with_free_func((GDestroyNotify)fu_dbus_daemon_system_inhibit_free);
}
static void
fu_dbus_daemon_finalize(GObject *obj)
{
FuDbusDaemon *self = FU_DBUS_DAEMON(obj);
g_ptr_array_unref(self->system_inhibits);
if (self->client_list != NULL)
g_object_unref(self->client_list);
if (self->owner_id > 0)
g_bus_unown_name(self->owner_id);
if (self->proxy_uid != NULL)
g_object_unref(self->proxy_uid);
if (self->connection != NULL)
g_object_unref(self->connection);
if (self->authority != NULL)
g_object_unref(self->authority);
if (self->introspection_daemon != NULL)
g_dbus_node_info_unref(self->introspection_daemon);
G_OBJECT_CLASS(fu_dbus_daemon_parent_class)->finalize(obj);
}
static void
fu_dbus_daemon_class_init(FuDbusDaemonClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
FuDaemonClass *daemon_class = FU_DAEMON_CLASS(klass);
object_class->finalize = fu_dbus_daemon_finalize;
daemon_class->setup = fu_dbus_daemon_setup;
}
FuDaemon *
fu_daemon_new(void)
{
return FU_DAEMON(g_object_new(FU_TYPE_DBUS_DAEMON, NULL));
}