blob: a063340dbf887f4e896c0a966577b80870a94a9a [file] [log] [blame]
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
Richard Hughes8645ec92015-03-19 10:14:32 +000024#include <fwupd.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000025#include <appstream-glib.h>
26#include <glib/gstdio.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000027#include <gio/gio.h>
28#include <gio/gunixfdlist.h>
Richard Hughesae0efdc2015-06-24 16:18:29 +010029#include <gio/gunixinputstream.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000030#include <glib/gi18n.h>
31#include <locale.h>
Richard Hughesf508e762015-02-27 12:49:36 +000032#include <polkit/polkit.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000033#include <stdlib.h>
34#include <fcntl.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000035
Richard Hughescccc7752015-03-06 11:13:19 +000036#include "fu-cab.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000037#include "fu-cleanup.h"
Richard Hughes8bbfdf42015-02-26 22:28:09 +000038#include "fu-debug.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000039#include "fu-device.h"
Richard Hughesae0efdc2015-06-24 16:18:29 +010040#include "fu-keyring.h"
Richard Hughes0e883ee2015-03-18 17:22:33 +000041#include "fu-pending.h"
Richard Hughes3c99ba42015-03-05 12:17:48 +000042#include "fu-provider.h"
Richard Hughesebb58a32015-05-29 15:35:37 +010043#include "fu-provider-udev.h"
Richard Hughesc89c1b02015-05-05 15:21:18 +010044#include "fu-provider-usb.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000045#include "fu-resources.h"
46
Richard Hughes3c99ba42015-03-05 12:17:48 +000047#ifdef HAVE_COLORHUG
48 #include "fu-provider-chug.h"
49#endif
50#ifdef HAVE_UEFI
51 #include "fu-provider-uefi.h"
52#endif
53
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000054typedef struct {
55 GDBusConnection *connection;
56 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000057 GDBusProxy *proxy_uid;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000058 GMainLoop *loop;
59 GPtrArray *devices;
Richard Hughes8bbfdf42015-02-26 22:28:09 +000060 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000061 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000062 FwupdStatus status;
Richard Hughes0e883ee2015-03-18 17:22:33 +000063 FuPending *pending;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000064} FuMainPrivate;
65
Richard Hughesf508e762015-02-27 12:49:36 +000066typedef struct {
67 FuDevice *device;
68 FuProvider *provider;
69} FuDeviceItem;
70
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000071/**
Richard Hughesd7022b52015-03-11 19:47:06 +000072 * fu_main_emit_changed:
73 **/
74static void
75fu_main_emit_changed (FuMainPrivate *priv)
76{
77 /* not yet connected */
78 if (priv->connection == NULL)
79 return;
80 g_dbus_connection_emit_signal (priv->connection,
81 NULL,
82 FWUPD_DBUS_PATH,
83 FWUPD_DBUS_INTERFACE,
84 "Changed",
85 NULL, NULL);
86}
87
88/**
Richard Hughes773ce982015-03-09 22:40:57 +000089 * fu_main_emit_property_changed:
90 **/
91static void
92fu_main_emit_property_changed (FuMainPrivate *priv,
93 const gchar *property_name,
94 GVariant *property_value)
95{
96 GVariantBuilder builder;
97 GVariantBuilder invalidated_builder;
98
99 /* not yet connected */
100 if (priv->connection == NULL)
101 return;
102
103 /* build the dict */
104 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
105 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
106 g_variant_builder_add (&builder,
107 "{sv}",
108 property_name,
109 property_value);
110 g_dbus_connection_emit_signal (priv->connection,
111 NULL,
112 FWUPD_DBUS_PATH,
113 "org.freedesktop.DBus.Properties",
114 "PropertiesChanged",
115 g_variant_new ("(sa{sv}as)",
116 FWUPD_DBUS_INTERFACE,
117 &builder,
118 &invalidated_builder),
119 NULL);
120 g_variant_builder_clear (&builder);
121 g_variant_builder_clear (&invalidated_builder);
122}
123
124/**
125 * fu_main_set_status:
126 **/
127static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000128fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000129{
Richard Hughes773ce982015-03-09 22:40:57 +0000130 if (priv->status == status)
131 return;
132 priv->status = status;
133
134 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000135 g_debug ("Emitting PropertyChanged('Status'='%s')",
136 fwupd_status_to_string (status));
137 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000138}
139
140/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000141 * fu_main_device_array_to_variant:
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000142 **/
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000143static GVariant *
Richard Hughes9a38c032015-03-17 20:58:46 +0000144fu_main_device_array_to_variant (FuMainPrivate *priv, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000145{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000146 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000147 guint i;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000148
Richard Hughes9a38c032015-03-17 20:58:46 +0000149 /* no devices */
150 if (priv->devices->len == 0) {
151 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000152 FWUPD_ERROR,
153 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9a38c032015-03-17 20:58:46 +0000154 "no supported devices");
155 return NULL;
156 }
157
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000158 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000159 for (i = 0; i < priv->devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000160 GVariant *tmp;
161 FuDeviceItem *item;
162 item = g_ptr_array_index (priv->devices, i);
163 tmp = fu_device_to_variant (item->device);
164 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000165 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000166 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000167}
168
169/**
Richard Hughesf508e762015-02-27 12:49:36 +0000170 * fu_main_item_free:
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000171 **/
Richard Hughesf508e762015-02-27 12:49:36 +0000172static void
173fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000174{
Richard Hughesf508e762015-02-27 12:49:36 +0000175 g_object_unref (item->device);
176 g_object_unref (item->provider);
177 g_free (item);
178}
179
180/**
181 * fu_main_get_item_by_id:
182 **/
183static FuDeviceItem *
184fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
185{
186 FuDeviceItem *item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000187 guint i;
188
189 for (i = 0; i < priv->devices->len; i++) {
Richard Hughesf508e762015-02-27 12:49:36 +0000190 item = g_ptr_array_index (priv->devices, i);
191 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
192 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000193 }
194 return NULL;
195}
196
Richard Hughesd079b1a2015-03-06 10:09:55 +0000197/**
198 * fu_main_get_item_by_guid:
199 **/
200static FuDeviceItem *
201fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid)
202{
203 FuDeviceItem *item;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000204 guint i;
205
206 for (i = 0; i < priv->devices->len; i++) {
207 item = g_ptr_array_index (priv->devices, i);
Richard Hughes0e883ee2015-03-18 17:22:33 +0000208 if (g_strcmp0 (fu_device_get_guid (item->device), guid) == 0)
Richard Hughesd079b1a2015-03-06 10:09:55 +0000209 return item;
210 }
211 return NULL;
212}
213
Richard Hughes0e883ee2015-03-18 17:22:33 +0000214/**
215 * fu_main_get_provider_by_name:
216 **/
217static FuProvider *
218fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
219{
220 FuProvider *provider;
221 guint i;
222
223 for (i = 0; i < priv->providers->len; i++) {
224 provider = g_ptr_array_index (priv->providers, i);
225 if (g_strcmp0 (fu_provider_get_name (provider), name) == 0)
226 return provider;
227 }
228 return NULL;
229}
230
Richard Hughesf508e762015-02-27 12:49:36 +0000231typedef struct {
232 GDBusMethodInvocation *invocation;
Richard Hughescccc7752015-03-06 11:13:19 +0000233 FuCab *cab;
Richard Hughes67ec8982015-03-03 11:39:27 +0000234 FuDevice *device;
Richard Hughes74cc2172015-02-27 13:19:46 +0000235 FuProviderFlags flags;
Richard Hughesf508e762015-02-27 12:49:36 +0000236 gchar *id;
Richard Hughes67ec8982015-03-03 11:39:27 +0000237 gint firmware_fd;
Richard Hughes67ec8982015-03-03 11:39:27 +0000238 gint cab_fd;
Richard Hughes63bbbf52015-04-14 16:12:16 +0100239 gint vercmp;
Richard Hughes67ec8982015-03-03 11:39:27 +0000240 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000241} FuMainAuthHelper;
242
243/**
244 * fu_main_helper_free:
245 **/
246static void
247fu_main_helper_free (FuMainAuthHelper *helper)
248{
Richard Hughescccc7752015-03-06 11:13:19 +0000249 /* delete temp files */
250 fu_cab_delete_temp_files (helper->cab, NULL);
251 g_object_unref (helper->cab);
Richard Hughes67ec8982015-03-03 11:39:27 +0000252
253 /* close any open files */
254 if (helper->cab_fd > 0)
255 close (helper->cab_fd);
256 if (helper->firmware_fd > 0)
257 close (helper->firmware_fd);
258
259 /* free */
Richard Hughesd1e823c2015-03-04 22:18:30 +0000260 g_free (helper->id);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000261 if (helper->device != NULL)
262 g_object_unref (helper->device);
Richard Hughes67ec8982015-03-03 11:39:27 +0000263 g_object_unref (helper->invocation);
Richard Hughesf508e762015-02-27 12:49:36 +0000264 g_free (helper);
265}
266
267/**
Richard Hughes18423292015-03-09 17:10:50 +0000268 * fu_main_provider_update_authenticated:
269 **/
270static gboolean
271fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
272{
273 FuDeviceItem *item;
274
275 /* check the device still exists */
276 item = fu_main_get_item_by_id (helper->priv, fu_device_get_id (helper->device));
277 if (item == NULL) {
278 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000279 FWUPD_ERROR,
280 FWUPD_ERROR_INVALID_FILE,
Richard Hughes18423292015-03-09 17:10:50 +0000281 "device %s was removed",
282 fu_device_get_id (helper->device));
283 return FALSE;
284 }
285
286 /* run the correct provider that added this */
287 return fu_provider_update (item->provider,
288 item->device,
289 fu_cab_get_stream (helper->cab),
290 helper->firmware_fd,
291 helper->flags,
292 error);
293}
294
295/**
Richard Hughesf508e762015-02-27 12:49:36 +0000296 * fu_main_check_authorization_cb:
297 **/
298static void
299fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
300{
301 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +0000302 _cleanup_error_free_ GError *error = NULL;
303 _cleanup_object_unref_ PolkitAuthorizationResult *auth = NULL;
304
305 /* get result */
306 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
307 res, &error);
308 if (auth == NULL) {
309 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000310 FWUPD_ERROR,
311 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000312 "could not check for auth: %s",
313 error->message);
314 fu_main_helper_free (helper);
315 return;
316 }
317
318 /* did not auth */
319 if (!polkit_authorization_result_get_is_authorized (auth)) {
320 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000321 FWUPD_ERROR,
322 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000323 "failed to obtain auth");
324 fu_main_helper_free (helper);
325 return;
326 }
327
Richard Hughes18423292015-03-09 17:10:50 +0000328 /* we're good to go */
329 if (!fu_main_provider_update_authenticated (helper, &error)) {
330 g_dbus_method_invocation_return_gerror (helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000331 fu_main_helper_free (helper);
332 return;
333 }
334
335 /* success */
336 g_dbus_method_invocation_return_value (helper->invocation, NULL);
337 fu_main_helper_free (helper);
338}
339
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000340/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000341 * fu_main_update_helper:
342 **/
343static gboolean
344fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
345{
Richard Hughescccc7752015-03-06 11:13:19 +0000346 const gchar *guid;
Richard Hughesdef31752015-03-04 19:26:54 +0000347 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000348 const gchar *version;
Richard Hughes67ec8982015-03-03 11:39:27 +0000349
Richard Hughescccc7752015-03-06 11:13:19 +0000350 /* load cab file */
Richard Hughesf910ac92015-03-19 10:43:42 +0000351 fu_main_set_status (helper->priv, FWUPD_STATUS_LOADING);
Richard Hughescccc7752015-03-06 11:13:19 +0000352 if (!fu_cab_load_fd (helper->cab, helper->cab_fd, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000353 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000354
355 /* are we matching *any* hardware */
Richard Hughescccc7752015-03-06 11:13:19 +0000356 guid = fu_cab_get_guid (helper->cab);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000357 if (helper->device == NULL) {
358 FuDeviceItem *item;
359 item = fu_main_get_item_by_guid (helper->priv, guid);
360 if (item == NULL) {
361 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000362 FWUPD_ERROR,
363 FWUPD_ERROR_INVALID_FILE,
Richard Hughesd079b1a2015-03-06 10:09:55 +0000364 "no hardware matched %s",
365 guid);
366 return FALSE;
367 }
368 helper->device = g_object_ref (item->device);
369 }
370
Richard Hughes0e883ee2015-03-18 17:22:33 +0000371 tmp = fu_device_get_guid (helper->device);
Richard Hughesdef31752015-03-04 19:26:54 +0000372 if (g_strcmp0 (guid, tmp) != 0) {
Richard Hughes67ec8982015-03-03 11:39:27 +0000373 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000374 FWUPD_ERROR,
375 FWUPD_ERROR_INVALID_FILE,
Richard Hughes67ec8982015-03-03 11:39:27 +0000376 "firmware is not for this hw: required %s got %s",
Richard Hughesdef31752015-03-04 19:26:54 +0000377 tmp, guid);
378 return FALSE;
379 }
380
Richard Hughesdef31752015-03-04 19:26:54 +0000381 /* parse the DriverVer */
Richard Hughescccc7752015-03-06 11:13:19 +0000382 version = fu_cab_get_version (helper->cab);
Richard Hughesd1e823c2015-03-04 22:18:30 +0000383 fu_device_set_metadata (helper->device, FU_DEVICE_KEY_VERSION_NEW, version);
Richard Hughesdef31752015-03-04 19:26:54 +0000384
Richard Hughes18e75912015-03-05 13:17:17 +0000385 /* compare to the lowest supported version, if it exists */
386 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION_LOWEST);
387 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
388 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000389 FWUPD_ERROR,
390 FWUPD_ERROR_VERSION_NEWER,
Richard Hughes18e75912015-03-05 13:17:17 +0000391 "Specified firmware is older than the minimum "
392 "required version '%s < %s'", tmp, version);
393 return FALSE;
394 }
395
Richard Hughesdef31752015-03-04 19:26:54 +0000396 /* compare the versions of what we have installed */
397 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION);
Richard Hughesd1e823c2015-03-04 22:18:30 +0000398 if (tmp == NULL) {
399 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000400 FWUPD_ERROR,
401 FWUPD_ERROR_INTERNAL,
Richard Hughesd1e823c2015-03-04 22:18:30 +0000402 "Device %s does not yet have a current version",
403 fu_device_get_id (helper->device));
404 return FALSE;
405 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100406 helper->vercmp = as_utils_vercmp (tmp, version);
407 if (helper->vercmp == 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000408 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000409 FWUPD_ERROR,
410 FWUPD_ERROR_VERSION_SAME,
Richard Hughesdef31752015-03-04 19:26:54 +0000411 "Specified firmware is already installed '%s'",
412 tmp);
413 return FALSE;
414 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100415 if (helper->vercmp > 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000416 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000417 FWUPD_ERROR,
418 FWUPD_ERROR_VERSION_NEWER,
Richard Hughesdef31752015-03-04 19:26:54 +0000419 "Specified firmware is older than installed '%s < %s'",
420 tmp, version);
Richard Hughes67ec8982015-03-03 11:39:27 +0000421 return FALSE;
422 }
423
Richard Hughes67ec8982015-03-03 11:39:27 +0000424 /* now extract the firmware */
Richard Hughesf910ac92015-03-19 10:43:42 +0000425 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
Richard Hughescccc7752015-03-06 11:13:19 +0000426 if (!fu_cab_extract_firmware (helper->cab, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000427 return FALSE;
Richard Hughes67ec8982015-03-03 11:39:27 +0000428
429 /* and open it */
Richard Hughescccc7752015-03-06 11:13:19 +0000430 helper->firmware_fd = g_open (fu_cab_get_filename_firmware (helper->cab),
431 O_CLOEXEC, 0);
Richard Hughes67ec8982015-03-03 11:39:27 +0000432 if (helper->firmware_fd < 0) {
433 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000434 FWUPD_ERROR,
435 FWUPD_ERROR_READ,
Richard Hughes67ec8982015-03-03 11:39:27 +0000436 "failed to open %s",
Richard Hughescccc7752015-03-06 11:13:19 +0000437 fu_cab_get_filename_firmware (helper->cab));
Richard Hughes67ec8982015-03-03 11:39:27 +0000438 return FALSE;
439 }
440 return TRUE;
441}
442
443/**
Richard Hughes18423292015-03-09 17:10:50 +0000444 * fu_main_dbus_get_uid:
445 *
446 * Return value: the UID, or %G_MAXUINT if it could not be obtained
447 **/
448static guint
449fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
450{
451 guint uid;
452 _cleanup_error_free_ GError *error = NULL;
453 _cleanup_variant_unref_ GVariant *value = NULL;
454
455 if (priv->proxy_uid == NULL)
456 return G_MAXUINT;
457 value = g_dbus_proxy_call_sync (priv->proxy_uid,
458 "GetConnectionUnixUser",
459 g_variant_new ("(s)", sender),
460 G_DBUS_CALL_FLAGS_NONE,
461 -1,
462 NULL,
463 &error);
464 if (value == NULL) {
465 g_warning ("Failed to get uid for %s: %s",
466 sender, error->message);
467 return G_MAXUINT;
468 }
469 g_variant_get (value, "(u)", &uid);
470 return uid;
471}
472
473/**
Richard Hughes0e883ee2015-03-18 17:22:33 +0000474 * fu_main_get_item_by_id_fallback_pending:
475 **/
476static FuDeviceItem *
477fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
478{
479 FuDevice *dev;
480 FuProvider *provider;
481 FuDeviceItem *item = NULL;
482 const gchar *tmp;
483 guint i;
484 _cleanup_ptrarray_unref_ GPtrArray *devices = NULL;
485
486 /* not a wildcard */
487 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
488 item = fu_main_get_item_by_id (priv, id);
489 if (item == NULL) {
490 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000491 FWUPD_ERROR,
492 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000493 "no suitable device found for %s", id);
494 }
495 return item;
496 }
497
498 /* allow '*' for any */
499 devices = fu_pending_get_devices (priv->pending, error);
500 if (devices == NULL)
501 return NULL;
502 for (i = 0; i < devices->len; i++) {
503 dev = g_ptr_array_index (devices, i);
504 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PENDING_STATE);
505 if (tmp == NULL)
506 continue;
507 if (g_strcmp0 (tmp, "scheduled") == 0)
508 continue;
509
510 /* if the device is not still connected, fake a FuDeviceItem */
511 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
512 if (item == NULL) {
513 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PROVIDER);
514 provider = fu_main_get_provider_by_name (priv, tmp);
515 if (provider == NULL) {
516 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000517 FWUPD_ERROR,
518 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000519 "no provider %s found", tmp);
520 }
521 item = g_new0 (FuDeviceItem, 1);
522 item->device = g_object_ref (dev);
523 item->provider = g_object_ref (provider);
524 g_ptr_array_add (priv->devices, item);
525
526 /* FIXME: just a boolean on FuDeviceItem? */
527 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
528 }
529 break;
530 }
531
532 /* no device found */
533 if (item == NULL) {
534 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000535 FWUPD_ERROR,
536 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000537 "no suitable devices found");
538 }
539 return item;
540}
541
542/**
Richard Hughes63bbbf52015-04-14 16:12:16 +0100543 * fu_main_get_action_id_for_device:
544 **/
545static const gchar *
546fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
547{
Richard Hughes63bbbf52015-04-14 16:12:16 +0100548 gboolean is_trusted;
549 gboolean is_downgrade;
550
551 /* only test the payload */
552 is_trusted = (fu_cab_get_trust_flags (helper->cab) & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
553 is_downgrade = helper->vercmp > 0;
554
555 /* relax authentication checks for removable devices */
Richard Hughesd7dba982015-05-05 16:02:17 +0100556 if ((fu_device_get_flags (helper->device) & FU_DEVICE_FLAG_INTERNAL) == 0) {
Richard Hughes63bbbf52015-04-14 16:12:16 +0100557 if (is_downgrade)
558 return "org.freedesktop.fwupd.downgrade-hotplug";
559 if (is_trusted)
560 return "org.freedesktop.fwupd.update-hotplug-trusted";
561 return "org.freedesktop.fwupd.update-hotplug";
562 }
563
564 /* internal device */
565 if (is_downgrade)
566 return "org.freedesktop.fwupd.downgrade-internal";
567 if (is_trusted)
568 return "org.freedesktop.fwupd.update-internal-trusted";
569 return "org.freedesktop.fwupd.update-internal";
570}
571
572/**
Richard Hughesae0efdc2015-06-24 16:18:29 +0100573 * fu_main_daemon_update_metadata:
574 *
575 * Supports optionally GZipped AppStream files up to 1MiB in size.
576 **/
577static gboolean
578fu_main_daemon_update_metadata (gint fd, gint fd_sig, GError **error)
579{
580 guint8 magic[2];
581 _cleanup_bytes_unref_ GBytes *bytes = NULL;
582 _cleanup_bytes_unref_ GBytes *bytes_raw = NULL;
583 _cleanup_bytes_unref_ GBytes *bytes_sig = NULL;
584 _cleanup_object_unref_ AsStore *store = NULL;
585 _cleanup_object_unref_ FuKeyring *kr = NULL;
586 _cleanup_object_unref_ GConverter *converter = NULL;
587 _cleanup_object_unref_ GFile *file = NULL;
588 _cleanup_object_unref_ GInputStream *stream_buf = NULL;
589 _cleanup_object_unref_ GInputStream *stream_fd = NULL;
590 _cleanup_object_unref_ GInputStream *stream = NULL;
591 _cleanup_object_unref_ GInputStream *stream_sig = NULL;
592
593 /* open existing file if it exists */
594 store = as_store_new ();
595 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
596 if (g_file_query_exists (file, NULL)) {
597 if (!as_store_from_file (store, file, NULL, NULL, error))
598 return FALSE;
599 }
600
601 /* read the entire file into memory */
602 stream_fd = g_unix_input_stream_new (fd, TRUE);
603 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
604 if (bytes_raw == NULL)
605 return FALSE;
606 stream_buf = g_memory_input_stream_new ();
607 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
608
609 /* peek the file type and get data */
610 if (g_input_stream_read (stream_buf, magic, 2, NULL, error) == -1)
611 return FALSE;
612 if (magic[0] == 0x1f && magic[1] == 0x8b) {
613 g_debug ("using GZip decompressor for data");
614 if (!g_seekable_seek (G_SEEKABLE (stream_buf), 0, G_SEEK_SET, NULL, error))
615 return FALSE;
616 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
617 stream = g_converter_input_stream_new (stream_buf, converter);
618 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
619 if (bytes == NULL)
620 return FALSE;
621 } else if (magic[0] == '<' && magic[1] == '?') {
622 g_debug ("using no decompressor for data");
623 bytes = g_bytes_ref (bytes_raw);
624 } else {
625 g_set_error (error,
626 FWUPD_ERROR,
627 FWUPD_ERROR_INVALID_FILE,
628 "file type '0x%02x,0x%02x' not supported",
629 magic[0], magic[1]);
630 return FALSE;
631 }
632
633 /* read signature */
634 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
635 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
636 if (bytes_sig == NULL)
637 return FALSE;
638
639 /* verify file */
640 kr = fu_keyring_new ();
641 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
642 return FALSE;
643 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
644 return FALSE;
645
646 /* merge in the new contents */
647 g_debug ("Store was %i size", as_store_get_size (store));
648 if (!as_store_from_xml (store,
649 g_bytes_get_data (bytes, NULL), -1,
650 NULL, error))
651 return FALSE;
652 g_debug ("Store now %i size", as_store_get_size (store));
653
654 /* save the new file */
655 as_store_set_api_version (store, 0.9);
656 if (!as_store_to_file (store, file,
657 AS_NODE_TO_XML_FLAG_ADD_HEADER |
658 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
659 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
660 NULL, error)) {
661 return FALSE;
662 }
663
664 return TRUE;
665}
666
667/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000668 * fu_main_daemon_method_call:
669 **/
670static void
671fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
672 const gchar *object_path, const gchar *interface_name,
673 const gchar *method_name, GVariant *parameters,
674 GDBusMethodInvocation *invocation, gpointer user_data)
675{
676 FuMainPrivate *priv = (FuMainPrivate *) user_data;
677 GVariant *val;
678
679 /* return 'as' */
680 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000681 _cleanup_error_free_ GError *error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000682 _cleanup_strv_free_ gchar **devices = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000683 g_debug ("Called %s()", method_name);
Richard Hughes9a38c032015-03-17 20:58:46 +0000684 val = fu_main_device_array_to_variant (priv, &error);
685 if (val == NULL) {
686 g_dbus_method_invocation_return_gerror (invocation, error);
687 return;
688 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000689 g_dbus_method_invocation_return_value (invocation, val);
Richard Hughesf910ac92015-03-19 10:43:42 +0000690 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000691 return;
692 }
693
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000694 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +0000695 if (g_strcmp0 (method_name, "ClearResults") == 0) {
696 FuDeviceItem *item = NULL;
697 const gchar *id = NULL;
698 _cleanup_error_free_ GError *error = NULL;
699
700 g_variant_get (parameters, "(&s)", &id);
701 g_debug ("Called %s(%s)", method_name, id);
702
703 /* find device */
704 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
705 if (item == NULL) {
706 g_dbus_method_invocation_return_gerror (invocation, error);
707 return;
708 }
709
710 /* call into the provider */
711 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
712 g_dbus_method_invocation_return_gerror (invocation, error);
713 return;
714 }
715
716 /* success */
717 g_dbus_method_invocation_return_value (invocation, NULL);
718 return;
719 }
720
721 /* return 'a{sv}' */
722 if (g_strcmp0 (method_name, "GetResults") == 0) {
723 FuDeviceItem *item = NULL;
724 const gchar *id = NULL;
725 _cleanup_error_free_ GError *error = NULL;
726
727 g_variant_get (parameters, "(&s)", &id);
728 g_debug ("Called %s(%s)", method_name, id);
729
730 /* find device */
731 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
732 if (item == NULL) {
733 g_dbus_method_invocation_return_gerror (invocation, error);
734 return;
735 }
736
737 /* call into the provider */
738 if (!fu_provider_get_results (item->provider, item->device, &error)) {
739 g_dbus_method_invocation_return_gerror (invocation, error);
740 return;
741 }
742
743 /* success */
744 val = fu_device_get_metadata_as_variant (item->device);
745 g_dbus_method_invocation_return_value (invocation, val);
746 return;
747 }
748
749 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +0100750 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
751 GDBusMessage *message;
752 GUnixFDList *fd_list;
753 gint fd_data;
754 gint fd_sig;
755 _cleanup_error_free_ GError *error = NULL;
756
757 message = g_dbus_method_invocation_get_message (invocation);
758 fd_list = g_dbus_message_get_unix_fd_list (message);
759 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
760 g_dbus_method_invocation_return_error (invocation,
761 FWUPD_ERROR,
762 FWUPD_ERROR_INTERNAL,
763 "invalid handle");
764 return;
765 }
766 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
767 if (fd_data < 0) {
768 g_dbus_method_invocation_return_gerror (invocation, error);
769 return;
770 }
771 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
772 if (fd_sig < 0) {
773 g_dbus_method_invocation_return_gerror (invocation, error);
774 return;
775 }
776 if (!fu_main_daemon_update_metadata (fd_data, fd_sig, &error)) {
777 g_prefix_error (&error, "failed to update metadata: ");
778 g_dbus_method_invocation_return_gerror (invocation, error);
779 return;
780 }
781 g_dbus_method_invocation_return_value (invocation, NULL);
782 return;
783 }
784
Richard Hughesa043c2e2015-06-29 08:43:18 +0100785 /* return 's' */
786 if (g_strcmp0 (method_name, "Verify") == 0) {
787 FuDeviceItem *item = NULL;
788 const gchar *id = NULL;
789 _cleanup_error_free_ GError *error = NULL;
790 const gchar *hash = NULL;
791
792 /* check the id exists */
793 g_variant_get (parameters, "(&s)", &id);
794 g_debug ("Called %s(%s)", method_name, id);
795 item = fu_main_get_item_by_id (priv, id);
796 if (item == NULL) {
797 g_dbus_method_invocation_return_error (invocation,
798 FWUPD_ERROR,
799 FWUPD_ERROR_NOT_FOUND,
800 "no such device %s",
801 id);
802 return;
803 }
804 if (!fu_provider_verify (item->provider, item->device,
805 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
806 g_dbus_method_invocation_return_gerror (invocation, error);
807 return;
808 }
809 hash = fu_device_get_metadata (item->device, FU_DEVICE_KEY_FIRMWARE_HASH);
810 g_dbus_method_invocation_return_value (invocation,
811 g_variant_new ("(s)", hash));
812 return;
813 }
814
Richard Hughesae0efdc2015-06-24 16:18:29 +0100815 /* return '' */
Richard Hughes74cc2172015-02-27 13:19:46 +0000816 if (g_strcmp0 (method_name, "Update") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +0000817 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000818 FuMainAuthHelper *helper;
Richard Hughes74cc2172015-02-27 13:19:46 +0000819 FuProviderFlags flags = FU_PROVIDER_UPDATE_FLAG_NONE;
820 GDBusMessage *message;
821 GUnixFDList *fd_list;
822 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +0000823 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000824 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +0000825 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000826 gint32 fd_handle = 0;
827 gint fd;
828 _cleanup_error_free_ GError *error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000829 _cleanup_object_unref_ PolkitSubject *subject = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +0000830 _cleanup_variant_iter_free_ GVariantIter *iter = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000831
832 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +0000833 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +0000834 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000835 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
836 item = fu_main_get_item_by_id (priv, id);
837 if (item == NULL) {
838 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000839 FWUPD_ERROR,
840 FWUPD_ERROR_NOT_FOUND,
Richard Hughesd079b1a2015-03-06 10:09:55 +0000841 "no such device %s",
842 id);
843 return;
844 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000845 }
846
Richard Hughes74cc2172015-02-27 13:19:46 +0000847 /* get options */
848 while (g_variant_iter_next (iter, "{&sv}",
849 &prop_key, &prop_value)) {
850 g_debug ("got option %s", prop_key);
851 if (g_strcmp0 (prop_key, "offline") == 0 &&
852 g_variant_get_boolean (prop_value) == TRUE)
853 flags |= FU_PROVIDER_UPDATE_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +0000854 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
855 g_variant_get_boolean (prop_value) == TRUE)
856 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER;
857 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
858 g_variant_get_boolean (prop_value) == TRUE)
859 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL;
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000860 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +0000861 }
862
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000863 /* get the fd */
864 message = g_dbus_method_invocation_get_message (invocation);
865 fd_list = g_dbus_message_get_unix_fd_list (message);
866 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
867 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000868 FWUPD_ERROR,
869 FWUPD_ERROR_INTERNAL,
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000870 "invalid handle");
871 return;
872 }
873 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
874 if (fd < 0) {
875 g_dbus_method_invocation_return_gerror (invocation,
876 error);
877 return;
878 }
879
Richard Hughes67ec8982015-03-03 11:39:27 +0000880 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +0000881 helper = g_new0 (FuMainAuthHelper, 1);
882 helper->invocation = g_object_ref (invocation);
Richard Hughes67ec8982015-03-03 11:39:27 +0000883 helper->cab_fd = fd;
Richard Hughesf508e762015-02-27 12:49:36 +0000884 helper->id = g_strdup (id);
Richard Hughes74cc2172015-02-27 13:19:46 +0000885 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +0000886 helper->priv = priv;
Richard Hughescccc7752015-03-06 11:13:19 +0000887 helper->cab = fu_cab_new ();
Richard Hughesd079b1a2015-03-06 10:09:55 +0000888 if (item != NULL)
889 helper->device = g_object_ref (item->device);
Richard Hughes67ec8982015-03-03 11:39:27 +0000890 if (!fu_main_update_helper (helper, &error)) {
891 g_dbus_method_invocation_return_gerror (helper->invocation,
892 error);
Richard Hughesf910ac92015-03-19 10:43:42 +0000893 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes67ec8982015-03-03 11:39:27 +0000894 fu_main_helper_free (helper);
895 return;
896 }
897
Richard Hughes18423292015-03-09 17:10:50 +0000898 /* is root */
899 if (fu_main_dbus_get_uid (priv, sender) == 0) {
900 if (!fu_main_provider_update_authenticated (helper, &error)) {
901 g_dbus_method_invocation_return_gerror (invocation, error);
902 } else {
903 g_dbus_method_invocation_return_value (invocation, NULL);
904 }
Richard Hughesf910ac92015-03-19 10:43:42 +0000905 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes18423292015-03-09 17:10:50 +0000906 fu_main_helper_free (helper);
907 return;
908 }
909
Richard Hughes67ec8982015-03-03 11:39:27 +0000910 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +0100911 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +0000912 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +0000913 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +0000914 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +0000915 NULL,
916 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
917 NULL,
918 fu_main_check_authorization_cb,
919 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000920 return;
921 }
922
Richard Hughescccc7752015-03-06 11:13:19 +0000923 /* return 'a{sv}' */
924 if (g_strcmp0 (method_name, "GetDetails") == 0) {
925 GDBusMessage *message;
926 GUnixFDList *fd_list;
927 GVariantBuilder builder;
Richard Hughesd8fa0492015-04-14 15:56:56 +0100928 FwupdTrustFlags trust_flags;
Richard Hughes3bf94802015-03-10 15:57:30 +0000929 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000930 gint32 fd_handle = 0;
931 gint fd;
932 _cleanup_error_free_ GError *error = NULL;
933 _cleanup_object_unref_ FuCab *cab = NULL;
934 _cleanup_variant_iter_free_ GVariantIter *iter = NULL;
935
936 /* check the id exists */
937 g_variant_get (parameters, "(h)", &fd_handle);
938 g_debug ("Called %s(%i)", method_name, fd_handle);
939
940 /* get the fd */
941 message = g_dbus_method_invocation_get_message (invocation);
942 fd_list = g_dbus_message_get_unix_fd_list (message);
943 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
944 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000945 FWUPD_ERROR,
946 FWUPD_ERROR_INTERNAL,
Richard Hughescccc7752015-03-06 11:13:19 +0000947 "invalid handle");
948 return;
949 }
950 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
951 if (fd < 0) {
952 g_dbus_method_invocation_return_gerror (invocation,
953 error);
954 return;
955 }
956
957 /* load file */
958 cab = fu_cab_new ();
959 if (!fu_cab_load_fd (cab, fd, NULL, &error)) {
960 g_dbus_method_invocation_return_gerror (invocation, error);
961 return;
962 }
963 if (!fu_cab_delete_temp_files (cab, &error)) {
964 g_dbus_method_invocation_return_gerror (invocation, error);
965 return;
966 }
967
Richard Hughesd8fa0492015-04-14 15:56:56 +0100968 /* we have to extract the file to check the signature */
969 if (!fu_cab_extract_firmware (cab, &error)) {
970 g_dbus_method_invocation_return_gerror (invocation, error);
971 return;
972 }
973
Richard Hughescccc7752015-03-06 11:13:19 +0000974 /* create an array with all the metadata in */
975 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
976 g_variant_builder_add (&builder, "{sv}",
977 FU_DEVICE_KEY_VERSION,
978 g_variant_new_string (fu_cab_get_version (cab)));
979 g_variant_builder_add (&builder, "{sv}",
980 FU_DEVICE_KEY_GUID,
981 g_variant_new_string (fu_cab_get_guid (cab)));
Richard Hughesbbac6d72015-03-11 22:09:44 +0000982 g_variant_builder_add (&builder, "{sv}",
983 FU_DEVICE_KEY_SIZE,
984 g_variant_new_uint64 (fu_cab_get_size (cab)));
Richard Hughes3bf94802015-03-10 15:57:30 +0000985
986 /* optional properties */
987 tmp = fu_cab_get_vendor (cab);
988 if (tmp != NULL) {
989 g_variant_builder_add (&builder, "{sv}",
990 FU_DEVICE_KEY_VENDOR,
991 g_variant_new_string (tmp));
992 }
993 tmp = fu_cab_get_name (cab);
994 if (tmp != NULL) {
995 g_variant_builder_add (&builder, "{sv}",
996 FU_DEVICE_KEY_NAME,
997 g_variant_new_string (tmp));
998 }
999 tmp = fu_cab_get_summary (cab);
1000 if (tmp != NULL) {
1001 g_variant_builder_add (&builder, "{sv}",
1002 FU_DEVICE_KEY_SUMMARY,
1003 g_variant_new_string (tmp));
1004 }
Richard Hughesbbac6d72015-03-11 22:09:44 +00001005 tmp = fu_cab_get_description (cab);
1006 if (tmp != NULL) {
1007 g_variant_builder_add (&builder, "{sv}",
1008 FU_DEVICE_KEY_DESCRIPTION,
1009 g_variant_new_string (tmp));
1010 }
1011 tmp = fu_cab_get_url_homepage (cab);
1012 if (tmp != NULL) {
1013 g_variant_builder_add (&builder, "{sv}",
1014 FU_DEVICE_KEY_URL_HOMEPAGE,
1015 g_variant_new_string (tmp));
1016 }
1017 tmp = fu_cab_get_license (cab);
1018 if (tmp != NULL) {
1019 g_variant_builder_add (&builder, "{sv}",
1020 FU_DEVICE_KEY_LICENSE,
1021 g_variant_new_string (tmp));
1022 }
Richard Hughesd8fa0492015-04-14 15:56:56 +01001023 trust_flags = fu_cab_get_trust_flags (cab);
1024 g_variant_builder_add (&builder, "{sv}",
1025 FU_DEVICE_KEY_TRUSTED,
1026 g_variant_new_uint64 (trust_flags));
Richard Hughescccc7752015-03-06 11:13:19 +00001027
1028 /* return whole array */
1029 val = g_variant_new ("(a{sv})", &builder);
1030 g_dbus_method_invocation_return_value (invocation, val);
1031 return;
1032 }
1033
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001034 /* we suck */
1035 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001036 G_DBUS_ERROR,
1037 G_DBUS_ERROR_UNKNOWN_METHOD,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001038 "no such method %s",
1039 method_name);
1040}
1041
1042/**
1043 * fu_main_daemon_get_property:
1044 **/
1045static GVariant *
1046fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
1047 const gchar *object_path, const gchar *interface_name,
1048 const gchar *property_name, GError **error,
1049 gpointer user_data)
1050{
Richard Hughes773ce982015-03-09 22:40:57 +00001051 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1052
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001053 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
1054 return g_variant_new_string (VERSION);
1055
Richard Hughes773ce982015-03-09 22:40:57 +00001056 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughesf910ac92015-03-19 10:43:42 +00001057 return g_variant_new_string (fwupd_status_to_string (priv->status));
Richard Hughes773ce982015-03-09 22:40:57 +00001058
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001059 /* return an error */
1060 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001061 G_DBUS_ERROR,
1062 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001063 "failed to get daemon property %s",
1064 property_name);
1065 return NULL;
1066}
1067
1068/**
Richard Hughesfd468842015-04-22 16:44:08 +01001069 * fu_main_providers_coldplug:
1070 **/
1071static void
1072fu_main_providers_coldplug (FuMainPrivate *priv)
1073{
1074 FuProvider *provider;
1075 guint i;
1076
1077 for (i = 0; i < priv->providers->len; i++) {
1078 _cleanup_error_free_ GError *error = NULL;
1079 provider = g_ptr_array_index (priv->providers, i);
1080 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
1081 g_warning ("Failed to coldplug: %s", error->message);
1082 }
1083}
1084
1085/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001086 * fu_main_on_bus_acquired_cb:
1087 **/
1088static void
1089fu_main_on_bus_acquired_cb (GDBusConnection *connection,
1090 const gchar *name,
1091 gpointer user_data)
1092{
1093 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1094 guint registration_id;
Richard Hughes18423292015-03-09 17:10:50 +00001095 _cleanup_error_free_ GError *error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001096 static const GDBusInterfaceVTable interface_vtable = {
1097 fu_main_daemon_method_call,
1098 fu_main_daemon_get_property,
1099 NULL
1100 };
1101
1102 priv->connection = g_object_ref (connection);
1103 registration_id = g_dbus_connection_register_object (connection,
1104 FWUPD_DBUS_PATH,
1105 priv->introspection_daemon->interfaces[0],
1106 &interface_vtable,
1107 priv, /* user_data */
1108 NULL, /* user_data_free_func */
1109 NULL); /* GError** */
1110 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00001111
Richard Hughesfd468842015-04-22 16:44:08 +01001112 /* add devices */
1113 fu_main_providers_coldplug (priv);
1114
Richard Hughes18423292015-03-09 17:10:50 +00001115 /* connect to D-Bus directly */
1116 priv->proxy_uid =
1117 g_dbus_proxy_new_sync (priv->connection,
1118 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1119 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1120 NULL,
1121 "org.freedesktop.DBus",
1122 "/org/freedesktop/DBus",
1123 "org.freedesktop.DBus",
1124 NULL,
1125 &error);
1126 if (priv->proxy_uid == NULL) {
1127 g_warning ("cannot connect to DBus: %s", error->message);
1128 return;
1129 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001130}
1131
1132/**
1133 * fu_main_on_name_acquired_cb:
1134 **/
1135static void
1136fu_main_on_name_acquired_cb (GDBusConnection *connection,
1137 const gchar *name,
1138 gpointer user_data)
1139{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001140 g_debug ("FuMain: acquired name: %s", name);
1141}
1142
1143/**
1144 * fu_main_on_name_lost_cb:
1145 **/
1146static void
1147fu_main_on_name_lost_cb (GDBusConnection *connection,
1148 const gchar *name,
1149 gpointer user_data)
1150{
1151 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1152 g_debug ("FuMain: lost name: %s", name);
1153 g_main_loop_quit (priv->loop);
1154}
1155
1156/**
1157 * fu_main_timed_exit_cb:
1158 **/
1159static gboolean
1160fu_main_timed_exit_cb (gpointer user_data)
1161{
1162 GMainLoop *loop = (GMainLoop *) user_data;
1163 g_main_loop_quit (loop);
1164 return G_SOURCE_REMOVE;
1165}
1166
1167/**
1168 * fu_main_load_introspection:
1169 **/
1170static GDBusNodeInfo *
1171fu_main_load_introspection (const gchar *filename, GError **error)
1172{
1173 _cleanup_bytes_unref_ GBytes *data = NULL;
1174 _cleanup_free_ gchar *path = NULL;
1175
1176 /* lookup data */
1177 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
1178 data = g_resource_lookup_data (fu_get_resource (),
1179 path,
1180 G_RESOURCE_LOOKUP_FLAGS_NONE,
1181 error);
1182 if (data == NULL)
1183 return NULL;
1184
1185 /* build introspection from XML */
1186 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
1187}
1188
1189/**
1190 * cd_main_provider_device_added_cb:
1191 **/
1192static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001193cd_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001194 FuDevice *device,
1195 gpointer user_data)
1196{
1197 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001198 FuDeviceItem *item;
1199
Richard Hughes0e883ee2015-03-18 17:22:33 +00001200 /* remove any fake device */
1201 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
1202 if (item != NULL)
1203 g_ptr_array_remove (priv->devices, item);
1204
1205 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00001206 item = g_new0 (FuDeviceItem, 1);
1207 item->device = g_object_ref (device);
1208 item->provider = g_object_ref (provider);
1209 g_ptr_array_add (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001210 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001211}
1212
1213/**
1214 * cd_main_provider_device_removed_cb:
1215 **/
1216static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001217cd_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001218 FuDevice *device,
1219 gpointer user_data)
1220{
1221 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001222 FuDeviceItem *item;
1223
1224 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
1225 if (item == NULL) {
1226 g_warning ("can't remove device %s", fu_device_get_id (device));
1227 return;
1228 }
1229 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001230 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001231}
1232
1233/**
Richard Hughes773ce982015-03-09 22:40:57 +00001234 * cd_main_provider_status_changed_cb:
1235 **/
1236static void
1237cd_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00001238 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00001239 gpointer user_data)
1240{
1241 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1242 fu_main_set_status (priv, status);
1243}
1244
1245/**
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001246 * fu_main_add_provider:
1247 **/
1248static void
1249fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
1250{
1251 g_signal_connect (provider, "device-added",
1252 G_CALLBACK (cd_main_provider_device_added_cb),
1253 priv);
1254 g_signal_connect (provider, "device-removed",
1255 G_CALLBACK (cd_main_provider_device_removed_cb),
1256 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00001257 g_signal_connect (provider, "status-changed",
1258 G_CALLBACK (cd_main_provider_status_changed_cb),
1259 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001260 g_ptr_array_add (priv->providers, provider);
1261}
1262
1263/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001264 * main:
1265 **/
1266int
1267main (int argc, char *argv[])
1268{
1269 FuMainPrivate *priv = NULL;
1270 gboolean immediate_exit = FALSE;
1271 gboolean ret;
1272 gboolean timed_exit = FALSE;
1273 GOptionContext *context;
1274 guint owner_id = 0;
1275 guint retval = 1;
1276 const GOptionEntry options[] = {
1277 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
1278 /* TRANSLATORS: exit after we've started up, used for user profiling */
1279 _("Exit after a small delay"), NULL },
1280 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
1281 /* TRANSLATORS: exit straight away, used for automatic profiling */
1282 _("Exit after the engine has loaded"), NULL },
1283 { NULL}
1284 };
1285 _cleanup_error_free_ GError *error = NULL;
1286
1287 setlocale (LC_ALL, "");
1288
1289 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1290 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1291 textdomain (GETTEXT_PACKAGE);
1292
1293 /* TRANSLATORS: program name */
1294 g_set_application_name (_("Firmware Update"));
1295 context = g_option_context_new (NULL);
1296 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001297 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00001298 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001299 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
1300 ret = g_option_context_parse (context, &argc, &argv, &error);
1301 if (!ret) {
1302 g_warning ("FuMain: failed to parse command line arguments: %s",
1303 error->message);
1304 goto out;
1305 }
1306
1307 /* create new objects */
1308 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00001309 priv->status = FWUPD_STATUS_IDLE;
Richard Hughesf508e762015-02-27 12:49:36 +00001310 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001311 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001312 priv->pending = fu_pending_new ();
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001313
1314 /* add providers */
1315 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughesebb58a32015-05-29 15:35:37 +01001316 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughesc89c1b02015-05-05 15:21:18 +01001317 fu_main_add_provider (priv, fu_provider_usb_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001318#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00001319 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001320#endif
1321#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001322 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001323#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001324
1325 /* load introspection from file */
1326 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
1327 &error);
1328 if (priv->introspection_daemon == NULL) {
1329 g_warning ("FuMain: failed to load daemon introspection: %s",
1330 error->message);
1331 goto out;
1332 }
1333
Richard Hughesf508e762015-02-27 12:49:36 +00001334 /* get authority */
1335 priv->authority = polkit_authority_get_sync (NULL, &error);
1336 if (priv->authority == NULL) {
1337 g_warning ("FuMain: failed to load polkit authority: %s",
1338 error->message);
1339 goto out;
1340 }
1341
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001342 /* own the object */
1343 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
1344 FWUPD_DBUS_SERVICE,
1345 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
1346 G_BUS_NAME_OWNER_FLAGS_REPLACE,
1347 fu_main_on_bus_acquired_cb,
1348 fu_main_on_name_acquired_cb,
1349 fu_main_on_name_lost_cb,
1350 priv, NULL);
1351
1352 /* Only timeout and close the mainloop if we have specified it
1353 * on the command line */
1354 if (immediate_exit)
1355 g_idle_add (fu_main_timed_exit_cb, priv->loop);
1356 else if (timed_exit)
1357 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
1358
1359 /* wait */
1360 g_info ("Daemon ready for requests");
1361 g_main_loop_run (priv->loop);
1362
1363 /* success */
1364 retval = 0;
1365out:
1366 g_option_context_free (context);
1367 if (owner_id > 0)
1368 g_bus_unown_name (owner_id);
1369 if (priv != NULL) {
1370 if (priv->loop != NULL)
1371 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00001372 if (priv->proxy_uid != NULL)
1373 g_object_unref (priv->proxy_uid);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001374 if (priv->connection != NULL)
1375 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00001376 if (priv->authority != NULL)
1377 g_object_unref (priv->authority);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001378 if (priv->introspection_daemon != NULL)
1379 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001380 g_object_unref (priv->pending);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001381 g_ptr_array_unref (priv->providers);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001382 g_ptr_array_unref (priv->devices);
1383 g_free (priv);
1384 }
1385 return retval;
1386}
1387