fwupd/src/fu-main.c
Richard Hughes f3dc1621bc Shut down the daemon if the on-disk binary is replaced
If we update fwupd 'live' rather than from an ostree or offline update then
kill the running instance unless a firmware update is ongoing.

When users update fwupd at runtime they often forget to restart the fwupd daemon
and say that the update didn't fix it when actually they using the the old code.
2019-03-27 09:54:06 -04:00

1598 lines
49 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>
#include <polkit/polkit.h>
#include <stdlib.h>
#include "fwupd-device-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"
#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
typedef struct {
GDBusConnection *connection;
GDBusNodeInfo *introspection_daemon;
GDBusProxy *proxy_uid;
GMainLoop *loop;
GFileMonitor *argv0_monitor;
PolkitAuthority *authority;
guint owner_id;
FuEngine *engine;
gboolean update_in_progress;
gboolean pending_sigterm;
} 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 gboolean
fu_main_get_device_flags_for_sender (FuMainPrivate *priv, const char *sender,
FwupdDeviceFlags *flags, GError **error)
{
uid_t calling_uid;
g_autoptr(GVariant) value = NULL;
g_return_val_if_fail (sender != NULL, FALSE);
g_return_val_if_fail (flags != NULL, FALSE);
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 FALSE;
}
g_variant_get (value, "(u)", &calling_uid);
if (calling_uid == 0)
*flags |= FWUPD_DEVICE_FLAG_TRUSTED;
return TRUE;
}
static GVariant *
fu_main_device_array_to_variant (FuMainPrivate *priv, const gchar *sender,
GPtrArray *devices, GError **error)
{
GVariantBuilder builder;
FwupdDeviceFlags flags = FWUPD_DEVICE_FLAG_NONE;
g_return_val_if_fail (devices->len > 0, NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
if (!fu_main_get_device_flags_for_sender (priv, sender, &flags, error))
return NULL;
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),
flags);
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;
PolkitSubject *subject;
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);
if (helper->subject != NULL)
g_object_unref (helper->subject);
if (helper->silo != NULL)
g_object_unref (helper->silo);
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
/* 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;
}
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;
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;
}
/* 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;
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;
}
/* 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_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;
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;
}
/* 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_authorize_activate_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
g_autoptr(GError) error = NULL;
g_autoptr(PolkitAuthorizationResult) auth = NULL;
/* get result */
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
res, &error);
if (!fu_main_authorization_is_valid (auth, &error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
/* 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;
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;
}
/* 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;
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;
}
/* 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;
g_autoptr(PolkitAuthorizationResult) auth = NULL;
/* get result */
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
res, &error);
if (!fu_main_authorization_is_valid (auth, &error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
/* do the next authentication action ID */
fu_main_authorize_install_queue (g_steal_pointer (&helper));
}
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) {
g_autofree gchar *action_id = g_strdup (g_ptr_array_index (helper->action_ids, 0));
g_autoptr(PolkitSubject) subject = g_object_ref (helper->subject);
g_ptr_array_remove_index (helper->action_ids, 0);
polkit_authority_check_authorization (priv->authority, subject,
action_id, NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
NULL,
fu_main_authorize_install_cb,
g_steal_pointer (&helper));
return;
}
/* all authenticated, so install all the things */
priv->update_in_progress = TRUE;
ret = fu_engine_install_tasks (helper->priv->engine,
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,
task,
helper->flags,
&error_local)) {
g_debug ("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(GError) error = NULL;
/* 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, sender, 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, "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, 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, "SetApprovedFirmware") == 0) {
g_autofree gchar *checksums_str = NULL;
g_auto(GStrv) checksums = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(PolkitSubject) subject = NULL;
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->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]));
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));
return;
}
if (g_strcmp0 (method_name, "SelfSign") == 0) {
GVariant *prop_value;
gchar *prop_key;
g_autofree gchar *value = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(PolkitSubject) subject = NULL;
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 |= FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP;
if (g_strcmp0 (prop_key, "add-cert") == 0 &&
g_variant_get_boolean (prop_value) == TRUE)
helper->flags |= FU_KEYRING_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->invocation = g_object_ref (invocation);
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));
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, 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, 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, sender, 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, "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;
g_autoptr(PolkitSubject) subject = 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;
}
/* authenticate */
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0 (FuMainAuthHelper, 1);
helper->priv = priv;
helper->invocation = g_object_ref (invocation);
helper->device_id = g_strdup (device_id);
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));
return;
}
if (g_strcmp0 (method_name, "Activate") == 0) {
const gchar *device_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(PolkitSubject) subject = 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;
}
/* authenticate */
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0 (FuMainAuthHelper, 1);
helper->priv = priv;
helper->invocation = g_object_ref (invocation);
helper->device_id = g_strdup (device_id);
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));
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;
g_autoptr(PolkitSubject) subject = NULL;
/* 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->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);
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));
return;
}
if (g_strcmp0 (method_name, "VerifyUpdate") == 0) {
const gchar *device_id = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(PolkitSubject) subject = NULL;
/* 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->invocation = g_object_ref (invocation);
helper->device_id = g_strdup (device_id);
helper->priv = priv;
/* authenticate */
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));
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, "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->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, "force") == 0 &&
g_variant_get_boolean (prop_value) == TRUE)
helper->flags |= FWUPD_INSTALL_FLAG_FORCE;
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 */
helper->subject = polkit_system_bus_name_new (sender);
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, 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 (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));
/* 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 gboolean
fu_main_is_running_offline_update (FuMainPrivate *priv)
{
const gchar *default_target = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) val = NULL;
val = g_dbus_connection_call_sync (priv->connection,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetDefaultTarget",
NULL, NULL,
G_DBUS_CALL_FLAGS_NONE,
1500, NULL, &error);
if (val == NULL) {
g_warning ("failed to get default.target: %s", error->message);
return FALSE;
}
g_variant_get (val, "(&s)", &default_target);
return g_strcmp0 (default_target, "system-update.target") == 0;
}
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);
/* are we running in the offline target */
if (fu_main_is_running_offline_update (priv))
fu_engine_add_app_flag (priv->engine, FU_APP_FLAGS_IS_OFFLINE);
/* 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 ("FuMain: 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_debug ("FuMain: lost 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);
}
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 void
fu_main_private_free (FuMainPrivate *priv)
{
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);
if (priv->authority != NULL)
g_object_unref (priv->authority);
if (priv->argv0_monitor != NULL)
g_object_unref (priv->argv0_monitor);
if (priv->introspection_daemon != NULL)
g_dbus_node_info_unref (priv->introspection_daemon);
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, 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->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);
/* 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;
}
/* 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;
}
/* 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);
g_debug ("Started with locale %s", g_getenv ("LANG"));
/* wait */
g_message ("Daemon ready for requests");
g_main_loop_run (priv->loop);
/* success */
return EXIT_SUCCESS;
}