blob: 8f40b81de4e5a08db6b8aaed202e3d2de17588b8 [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 *
Richard Hughes53ec7ea2016-03-29 18:45:08 +01003 * Copyright (C) 2015-2016 Richard Hughes <richard@hughsie.com>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00004 *
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 Hughes8e9762d2016-03-17 10:14:15 +000036#include "fwupd-enums-private.h"
37
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 Hughesd0905142016-03-13 09:46:49 +000040#include "fu-plugin.h"
Richard Hughesae0efdc2015-06-24 16:18:29 +010041#include "fu-keyring.h"
Richard Hughes0e883ee2015-03-18 17:22:33 +000042#include "fu-pending.h"
Richard Hughes3c99ba42015-03-05 12:17:48 +000043#include "fu-provider.h"
Richard Hughes5d057a82015-11-24 18:09:57 +000044#include "fu-provider-dfu.h"
Richard Hughes14d17642016-08-17 12:03:03 +010045#include "fu-provider-ebitdo.h"
Richard Hughes25cf6ab2015-08-04 21:34:12 +010046#include "fu-provider-rpi.h"
Richard Hughesebb58a32015-05-29 15:35:37 +010047#include "fu-provider-udev.h"
Richard Hughesc89c1b02015-05-05 15:21:18 +010048#include "fu-provider-usb.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000049#include "fu-resources.h"
Mario Limonciello918f3932016-02-03 12:47:23 -060050#include "fu-quirks.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000051
Richard Hughes3c99ba42015-03-05 12:17:48 +000052#ifdef HAVE_COLORHUG
53 #include "fu-provider-chug.h"
54#endif
55#ifdef HAVE_UEFI
56 #include "fu-provider-uefi.h"
57#endif
Mario Limonciello958ead62016-05-14 00:10:25 -050058#ifdef HAVE_DELL
59 #include "fu-provider-dell.h"
60#endif
Richard Hughes3c99ba42015-03-05 12:17:48 +000061
Philip Withnallbc339aa2016-11-22 16:13:22 +000062#ifndef HAVE_POLKIT_0_114
Richard Hughes60f48c22015-10-08 20:25:51 +010063G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
64G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
65#endif
66
Richard Hughes5d14def2015-10-07 17:43:10 +010067#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
68
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000069typedef struct {
70 GDBusConnection *connection;
71 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000072 GDBusProxy *proxy_uid;
Richard Hughes9559bbe2016-03-29 18:54:20 +010073 GDBusProxy *proxy_upower;
Richard Hughes9a52c5e2016-07-18 09:17:02 +010074 GKeyFile *config;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000075 GMainLoop *loop;
Richard Hughes5d14def2015-10-07 17:43:10 +010076 GPtrArray *devices; /* of FuDeviceItem */
Richard Hughes8bbfdf42015-02-26 22:28:09 +000077 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000078 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000079 FwupdStatus status;
Richard Hughes876c0072016-08-17 14:51:03 +010080 guint percentage;
Richard Hughes0e883ee2015-03-18 17:22:33 +000081 FuPending *pending;
Richard Hughes3f236502015-09-24 15:43:02 +010082 AsProfile *profile;
Richard Hughes7708a0f2015-07-21 08:41:22 +010083 AsStore *store;
Richard Hughes033ccba2015-09-10 14:51:28 +010084 guint store_changed_id;
Richard Hughesd0905142016-03-13 09:46:49 +000085 GHashTable *plugins; /* of name : FuPlugin */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000086} FuMainPrivate;
87
Richard Hughesf508e762015-02-27 12:49:36 +000088typedef struct {
89 FuDevice *device;
90 FuProvider *provider;
91} FuDeviceItem;
92
Richard Hughesdde7a2f2016-04-28 15:06:52 +010093static gboolean fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item);
94
Richard Hughesd7022b52015-03-11 19:47:06 +000095static void
96fu_main_emit_changed (FuMainPrivate *priv)
97{
98 /* not yet connected */
99 if (priv->connection == NULL)
100 return;
101 g_dbus_connection_emit_signal (priv->connection,
102 NULL,
103 FWUPD_DBUS_PATH,
104 FWUPD_DBUS_INTERFACE,
105 "Changed",
106 NULL, NULL);
107}
108
Richard Hughes8ca33782016-04-28 15:04:31 +0100109static void
110fu_main_emit_device_added (FuMainPrivate *priv, FuDevice *device)
111{
112 GVariant *val;
113
114 /* not yet connected */
115 if (priv->connection == NULL)
116 return;
117 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
118 g_dbus_connection_emit_signal (priv->connection,
119 NULL,
120 FWUPD_DBUS_PATH,
121 FWUPD_DBUS_INTERFACE,
122 "DeviceAdded",
123 val, NULL);
124}
125
Richard Hughes8ca33782016-04-28 15:04:31 +0100126static void
127fu_main_emit_device_removed (FuMainPrivate *priv, FuDevice *device)
128{
129 GVariant *val;
130
131 /* not yet connected */
132 if (priv->connection == NULL)
133 return;
134 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
135 g_dbus_connection_emit_signal (priv->connection,
136 NULL,
137 FWUPD_DBUS_PATH,
138 FWUPD_DBUS_INTERFACE,
139 "DeviceRemoved",
140 val, NULL);
141}
142
Richard Hughes8ca33782016-04-28 15:04:31 +0100143static void
144fu_main_emit_device_changed (FuMainPrivate *priv, FuDevice *device)
145{
146 GVariant *val;
147
148 /* not yet connected */
149 if (priv->connection == NULL)
150 return;
151 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
152 g_dbus_connection_emit_signal (priv->connection,
153 NULL,
154 FWUPD_DBUS_PATH,
155 FWUPD_DBUS_INTERFACE,
156 "DeviceChanged",
157 val, NULL);
158}
159
Richard Hughes773ce982015-03-09 22:40:57 +0000160static void
161fu_main_emit_property_changed (FuMainPrivate *priv,
162 const gchar *property_name,
163 GVariant *property_value)
164{
165 GVariantBuilder builder;
166 GVariantBuilder invalidated_builder;
167
168 /* not yet connected */
169 if (priv->connection == NULL)
170 return;
171
172 /* build the dict */
173 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
174 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
175 g_variant_builder_add (&builder,
176 "{sv}",
177 property_name,
178 property_value);
179 g_dbus_connection_emit_signal (priv->connection,
180 NULL,
181 FWUPD_DBUS_PATH,
182 "org.freedesktop.DBus.Properties",
183 "PropertiesChanged",
184 g_variant_new ("(sa{sv}as)",
185 FWUPD_DBUS_INTERFACE,
186 &builder,
187 &invalidated_builder),
188 NULL);
189 g_variant_builder_clear (&builder);
190 g_variant_builder_clear (&invalidated_builder);
191}
192
Richard Hughes773ce982015-03-09 22:40:57 +0000193static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000194fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000195{
Richard Hughes773ce982015-03-09 22:40:57 +0000196 if (priv->status == status)
197 return;
198 priv->status = status;
199
200 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000201 g_debug ("Emitting PropertyChanged('Status'='%s')",
202 fwupd_status_to_string (status));
203 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000204}
205
Richard Hughes876c0072016-08-17 14:51:03 +0100206static void
207fu_main_set_percentage (FuMainPrivate *priv, guint percentage)
208{
209 if (priv->percentage == percentage)
210 return;
211 priv->percentage = percentage;
212
213 /* emit changed */
214 g_debug ("Emitting PropertyChanged('Percentage'='%u%%')", percentage);
215 fu_main_emit_property_changed (priv, "Percentage",
216 g_variant_new_uint32 (percentage));
217}
218
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000219static GVariant *
Richard Hughes7708a0f2015-07-21 08:41:22 +0100220fu_main_device_array_to_variant (GPtrArray *devices, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000221{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000222 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000223
Richard Hughes9a38c032015-03-17 20:58:46 +0000224 /* no devices */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100225 if (devices->len == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000226 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000227 FWUPD_ERROR,
228 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9d76a872015-09-17 12:49:07 +0100229 "Nothing to do");
Richard Hughes9a38c032015-03-17 20:58:46 +0000230 return NULL;
231 }
232
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000233 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +0100234 for (guint i = 0; i < devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000235 GVariant *tmp;
236 FuDeviceItem *item;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100237 item = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +0000238 tmp = fwupd_result_to_data (FWUPD_RESULT (item->device), "{sa{sv}}");
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000239 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000240 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000241 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000242}
243
Richard Hughes060af612016-08-17 17:32:34 +0100244static void
245fu_main_invocation_return_value (FuMainPrivate *priv,
246 GDBusMethodInvocation *invocation,
247 GVariant *parameters)
248{
249 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
250 g_dbus_method_invocation_return_value (invocation, parameters);
251}
252
253static void
254fu_main_invocation_return_error (FuMainPrivate *priv,
255 GDBusMethodInvocation *invocation,
256 const GError *error)
257{
258 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
259 g_dbus_method_invocation_return_gerror (invocation, error);
260}
261
Richard Hughesd0905142016-03-13 09:46:49 +0000262static gboolean
263fu_main_load_plugins (GHashTable *plugins, GError **error)
264{
265 FuPlugin *plugin;
266 GModule *module;
Richard Hughesd0905142016-03-13 09:46:49 +0000267 const gchar *fn;
268 g_autofree gchar *plugin_dir = NULL;
269 g_autoptr(GDir) dir = NULL;
270 g_autoptr(GList) values = NULL;
271
272 /* search */
273 plugin_dir = g_build_filename (LIBDIR, "fwupd-plugins-1", NULL);
274 dir = g_dir_open (plugin_dir, 0, error);
275 if (dir == NULL)
276 return FALSE;
277 while ((fn = g_dir_read_name (dir)) != NULL) {
278 g_autofree gchar *filename = NULL;
279
280 /* ignore non-plugins */
281 if (!g_str_has_suffix (fn, ".so"))
282 continue;
283
284 /* open module */
285 filename = g_build_filename (plugin_dir, fn, NULL);
286 g_debug ("adding plugin %s", filename);
287 module = g_module_open (filename, 0);
288 if (module == NULL) {
289 g_warning ("failed to open plugin %s: %s",
290 filename, g_module_error ());
291 continue;
292 }
293 plugin = fu_plugin_new (module);
294 if (plugin == NULL) {
295 g_module_close (module);
296 g_warning ("plugin %s requires name", filename);
297 continue;
298 }
299
300 /* add */
301 g_hash_table_insert (plugins, g_strdup (plugin->name), plugin);
302 }
303
304 /* start them all up */
305 values = g_hash_table_get_values (plugins);
Richard Hughesf192bf02016-07-22 08:26:43 +0100306 for (GList *l = values; l != NULL; l = l->next) {
Richard Hughesd0905142016-03-13 09:46:49 +0000307 plugin = FU_PLUGIN (l->data);
308 if (!fu_plugin_run_startup (plugin, error))
309 return FALSE;
310 }
311
312 return TRUE;
313}
314
Richard Hughesdad1e192016-03-13 09:56:54 +0000315static FuPlugin *
316fu_main_get_plugin_for_device (GHashTable *plugins, FuDevice *device)
317{
318 const gchar *tmp;
Richard Hughes0bda0542016-08-11 15:35:30 +0100319 FuPlugin *plugin;
Richard Hughesdad1e192016-03-13 09:56:54 +0000320
321 /* does a vendor plugin exist */
322 tmp = fu_device_get_metadata (device, FU_DEVICE_KEY_FWUPD_PLUGIN);
323 if (tmp == NULL)
324 return NULL;
Richard Hughes0bda0542016-08-11 15:35:30 +0100325 plugin = g_hash_table_lookup (plugins, tmp);
326 if (plugin == NULL) {
327 g_warning ("requested plugin %s for %s, but not found",
328 tmp, fu_device_get_id (device));
329 }
330 return plugin;
Richard Hughesdad1e192016-03-13 09:56:54 +0000331}
332
Richard Hughesf508e762015-02-27 12:49:36 +0000333static void
334fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000335{
Richard Hughesf508e762015-02-27 12:49:36 +0000336 g_object_unref (item->device);
337 g_object_unref (item->provider);
338 g_free (item);
339}
340
Richard Hughesf508e762015-02-27 12:49:36 +0000341static FuDeviceItem *
342fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
343{
Richard Hughesf192bf02016-07-22 08:26:43 +0100344 for (guint i = 0; i < priv->devices->len; i++) {
345 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesf508e762015-02-27 12:49:36 +0000346 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
347 return item;
Richard Hughesb1b59d82016-01-06 13:20:53 +0000348 if (g_strcmp0 (fu_device_get_equivalent_id (item->device), id) == 0)
349 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000350 }
351 return NULL;
352}
353
Richard Hughes16d8f622016-03-15 16:34:20 +0000354static FuDeviceItem *
355fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid)
356{
Richard Hughesf192bf02016-07-22 08:26:43 +0100357 for (guint i = 0; i < priv->devices->len; i++) {
358 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes99147f12016-05-17 09:35:04 +0100359 if (fu_device_has_guid (item->device, guid))
Richard Hughes16d8f622016-03-15 16:34:20 +0000360 return item;
361 }
362 return NULL;
363}
364
Richard Hughes0e883ee2015-03-18 17:22:33 +0000365static FuProvider *
366fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
367{
Richard Hughesf192bf02016-07-22 08:26:43 +0100368 for (guint i = 0; i < priv->providers->len; i++) {
369 FuProvider *provider = g_ptr_array_index (priv->providers, i);
Richard Hughes0e883ee2015-03-18 17:22:33 +0000370 if (g_strcmp0 (fu_provider_get_name (provider), name) == 0)
371 return provider;
372 }
373 return NULL;
374}
375
Richard Hughes5d14def2015-10-07 17:43:10 +0100376static gboolean
377fu_main_get_release_trust_flags (AsRelease *release,
378 FwupdTrustFlags *trust_flags,
379 GError **error)
380{
381 AsChecksum *csum_tmp;
382 GBytes *blob_payload;
383 GBytes *blob_signature;
384 const gchar *fn;
385 g_autofree gchar *pki_dir = NULL;
386 g_autofree gchar *fn_signature = NULL;
387 g_autoptr(GError) error_local = NULL;
388 g_autoptr(FuKeyring) kr = NULL;
389
390 /* no filename? */
391 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
392 fn = as_checksum_get_filename (csum_tmp);
393 if (fn == NULL) {
394 g_set_error_literal (error,
395 FWUPD_ERROR,
396 FWUPD_ERROR_INVALID_FILE,
397 "no filename");
398 return FALSE;
399 }
400
401 /* no signature == no trust */
402 fn_signature = g_strdup_printf ("%s.asc", fn);
403 blob_signature = as_release_get_blob (release, fn_signature);
404 if (blob_signature == NULL) {
405 g_debug ("firmware archive contained no GPG signature");
406 return TRUE;
407 }
408
409 /* get payload */
410 blob_payload = as_release_get_blob (release, fn);
411 if (blob_payload == NULL) {
412 g_set_error_literal (error,
413 FWUPD_ERROR,
414 FWUPD_ERROR_INVALID_FILE,
415 "no payload");
416 return FALSE;
417 }
418
419 /* check we were installed correctly */
420 pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
421 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
422 g_set_error (error,
423 FWUPD_ERROR,
424 FWUPD_ERROR_NOT_FOUND,
425 "PKI directory %s not found", pki_dir);
426 return FALSE;
427 }
428
429 /* verify against the system trusted keys */
430 kr = fu_keyring_new ();
431 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
432 return FALSE;
433 if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
434 g_warning ("untrusted as failed to verify: %s",
435 error_local->message);
436 return TRUE;
437 }
438
439 /* awesome! */
440 g_debug ("marking payload as trusted");
441 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
442 return TRUE;
443}
444
Richard Hughes9a410ce2016-02-28 15:58:54 +0000445typedef enum {
446 FU_MAIN_AUTH_KIND_UNKNOWN,
447 FU_MAIN_AUTH_KIND_INSTALL,
448 FU_MAIN_AUTH_KIND_UNLOCK,
449 FU_MAIN_AUTH_KIND_LAST
450} FuMainAuthKind;
451
Richard Hughesf508e762015-02-27 12:49:36 +0000452typedef struct {
453 GDBusMethodInvocation *invocation;
Richard Hughes5d14def2015-10-07 17:43:10 +0100454 AsStore *store;
455 FwupdTrustFlags trust_flags;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100456 GPtrArray *devices; /* of FuDevice */
Richard Hughesa4a2c182016-06-29 10:37:05 +0100457 GPtrArray *blob_fws; /* of GBytes */
Richard Hughes2d6e1862016-03-18 09:20:37 +0000458 FwupdInstallFlags flags;
Richard Hughes5d14def2015-10-07 17:43:10 +0100459 GBytes *blob_cab;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100460 gboolean is_downgrade;
Richard Hughes9a410ce2016-02-28 15:58:54 +0000461 FuMainAuthKind auth_kind;
Richard Hughes67ec8982015-03-03 11:39:27 +0000462 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000463} FuMainAuthHelper;
464
Richard Hughesf508e762015-02-27 12:49:36 +0000465static void
466fu_main_helper_free (FuMainAuthHelper *helper)
467{
Richard Hughes67ec8982015-03-03 11:39:27 +0000468 /* free */
Richard Hughesfe5cc902016-06-29 10:00:00 +0100469 if (helper->devices != NULL)
470 g_ptr_array_unref (helper->devices);
Richard Hughes4ced4662016-08-26 11:02:31 +0100471 if (helper->blob_fws != NULL)
Richard Hughesa4a2c182016-06-29 10:37:05 +0100472 g_ptr_array_unref (helper->blob_fws);
Richard Hughes4ced4662016-08-26 11:02:31 +0100473 if (helper->blob_cab != NULL)
Richard Hughes5d14def2015-10-07 17:43:10 +0100474 g_bytes_unref (helper->blob_cab);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000475 if (helper->store != NULL)
476 g_object_unref (helper->store);
Richard Hughes67ec8982015-03-03 11:39:27 +0000477 g_object_unref (helper->invocation);
Richard Hughesf508e762015-02-27 12:49:36 +0000478 g_free (helper);
479}
480
Richard Hughesb75c92d2016-02-20 20:22:00 +0000481static gboolean
Richard Hughes9559bbe2016-03-29 18:54:20 +0100482fu_main_on_battery (FuMainPrivate *priv)
Richard Hughesb75c92d2016-02-20 20:22:00 +0000483{
Richard Hughesb75c92d2016-02-20 20:22:00 +0000484 g_autoptr(GVariant) value = NULL;
Richard Hughes9559bbe2016-03-29 18:54:20 +0100485 if (priv->proxy_upower == NULL) {
486 g_warning ("Failed to get OnBattery property as no UPower");
Richard Hughesb75c92d2016-02-20 20:22:00 +0000487 return FALSE;
488 }
Richard Hughes9559bbe2016-03-29 18:54:20 +0100489 value = g_dbus_proxy_get_cached_property (priv->proxy_upower, "OnBattery");
Richard Hughesb75c92d2016-02-20 20:22:00 +0000490 if (value == NULL) {
491 g_warning ("Failed to get OnBattery property value");
492 return FALSE;
493 }
494 return g_variant_get_boolean (value);
495}
496
Richard Hughes9a410ce2016-02-28 15:58:54 +0000497static gboolean
498fu_main_provider_unlock_authenticated (FuMainAuthHelper *helper, GError **error)
499{
Richard Hughesfe5cc902016-06-29 10:00:00 +0100500 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100501 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100502 FuDeviceItem *item;
503 FuDevice *device = g_ptr_array_index (helper->devices, i);
504
505 item = fu_main_get_item_by_id (helper->priv,
506 fu_device_get_id (device));
507 if (item == NULL) {
508 g_set_error (error,
509 FWUPD_ERROR,
510 FWUPD_ERROR_INVALID_FILE,
511 "device %s was removed",
512 fu_device_get_id (device));
513 return FALSE;
514 }
515
516 /* run the correct provider that added this */
517 if (!fu_provider_unlock (item->provider,
518 item->device,
519 error))
520 return FALSE;
521
522 /* make the UI update */
523 fu_main_emit_device_changed (helper->priv, item->device);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000524 }
525
Richard Hughes9a410ce2016-02-28 15:58:54 +0000526 /* make the UI update */
527 fu_main_emit_changed (helper->priv);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100528
Richard Hughes9a410ce2016-02-28 15:58:54 +0000529 return TRUE;
530}
531
Richard Hughes18423292015-03-09 17:10:50 +0000532static gboolean
533fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
534{
535 FuDeviceItem *item;
Richard Hughesdad1e192016-03-13 09:56:54 +0000536 FuPlugin *plugin;
Richard Hughes18423292015-03-09 17:10:50 +0000537
Richard Hughesfe5cc902016-06-29 10:00:00 +0100538 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100539 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100540 FuDevice *device = g_ptr_array_index (helper->devices, i);
541 item = fu_main_get_item_by_id (helper->priv,
542 fu_device_get_id (device));
543 if (item == NULL) {
544 g_set_error (error,
545 FWUPD_ERROR,
546 FWUPD_ERROR_INVALID_FILE,
547 "device %s was removed",
548 fu_device_get_id (device));
Richard Hughesb75c92d2016-02-20 20:22:00 +0000549 return FALSE;
550 }
Richard Hughesfe5cc902016-06-29 10:00:00 +0100551
Mario Limonciello3ef952f2016-09-20 12:28:59 -0500552 /* Called with online update, test if device is supposed to allow this */
553 if (!(helper->flags & FWUPD_INSTALL_FLAG_OFFLINE) &&
Richard Hughes644562e2016-08-22 10:30:24 +0100554 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100555 g_set_error(error,
556 FWUPD_ERROR,
Mario Limonciello3ef952f2016-09-20 12:28:59 -0500557 FWUPD_ERROR_NOT_SUPPORTED,
558 "Device %s does not allow online updates",
559 fu_device_get_id (device));
560 return FALSE;
561 }
562 /* Called with offline update, test if device is supposed to allow this */
563 if (helper->flags & FWUPD_INSTALL_FLAG_OFFLINE &&
564 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE)) {
565 g_set_error(error,
566 FWUPD_ERROR,
567 FWUPD_ERROR_NOT_SUPPORTED,
568 "Device %s does not allow offline updates",
Richard Hughesfe5cc902016-06-29 10:00:00 +0100569 fu_device_get_id (device));
570 return FALSE;
571 }
572
573 /* can we only do this on AC power */
Richard Hughes644562e2016-08-22 10:30:24 +0100574 if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_REQUIRE_AC)) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100575 if (fu_main_on_battery (helper->priv)) {
576 g_set_error_literal (error,
577 FWUPD_ERROR,
578 FWUPD_ERROR_NOT_SUPPORTED,
579 "Cannot install update "
580 "when not on AC power");
581 return FALSE;
582 }
583 }
Richard Hughesb75c92d2016-02-20 20:22:00 +0000584 }
585
Richard Hughesfe5cc902016-06-29 10:00:00 +0100586 /* run the correct providers for each device */
Richard Hughesf192bf02016-07-22 08:26:43 +0100587 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100588 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughesa4a2c182016-06-29 10:37:05 +0100589 GBytes *blob_fw = g_ptr_array_index (helper->blob_fws, i);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100590 item = fu_main_get_item_by_id (helper->priv,
591 fu_device_get_id (device));
592 plugin = fu_main_get_plugin_for_device (helper->priv->plugins,
593 item->device);
594 if (!fu_provider_update (item->provider,
595 item->device,
596 helper->blob_cab,
Richard Hughesa4a2c182016-06-29 10:37:05 +0100597 blob_fw,
Richard Hughesfe5cc902016-06-29 10:00:00 +0100598 plugin,
599 helper->flags,
600 error))
601 return FALSE;
602
603 /* make the UI update */
Richard Hughes33a518a2016-07-27 15:22:53 +0100604 fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100605 fu_main_emit_device_changed (helper->priv, item->device);
606 }
Richard Hughesa3a8f502015-11-24 12:31:59 +0000607
608 /* make the UI update */
609 fu_main_emit_changed (helper->priv);
610 return TRUE;
Richard Hughes18423292015-03-09 17:10:50 +0000611}
612
Richard Hughesf508e762015-02-27 12:49:36 +0000613static void
614fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
615{
616 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100617 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100618 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000619
620 /* get result */
621 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
622 res, &error);
623 if (auth == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +0100624 g_set_error (&error,
625 FWUPD_ERROR,
626 FWUPD_ERROR_AUTH_FAILED,
627 "could not check for auth: %s", error->message);
628 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000629 fu_main_helper_free (helper);
630 return;
631 }
632
633 /* did not auth */
634 if (!polkit_authorization_result_get_is_authorized (auth)) {
Richard Hughes060af612016-08-17 17:32:34 +0100635 g_set_error_literal (&error,
636 FWUPD_ERROR,
637 FWUPD_ERROR_AUTH_FAILED,
638 "failed to obtain auth");
639 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000640 fu_main_helper_free (helper);
641 return;
642 }
643
Richard Hughes18423292015-03-09 17:10:50 +0000644 /* we're good to go */
Richard Hughes9a410ce2016-02-28 15:58:54 +0000645 if (helper->auth_kind == FU_MAIN_AUTH_KIND_INSTALL) {
646 if (!fu_main_provider_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100647 fu_main_invocation_return_error (helper->priv,
648 helper->invocation,
649 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000650 fu_main_helper_free (helper);
651 return;
652 }
653 } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_UNLOCK) {
654 if (!fu_main_provider_unlock_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100655 fu_main_invocation_return_error (helper->priv,
656 helper->invocation,
657 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000658 fu_main_helper_free (helper);
659 return;
660 }
661 } else {
662 g_assert_not_reached ();
Richard Hughesf508e762015-02-27 12:49:36 +0000663 }
664
665 /* success */
Richard Hughes060af612016-08-17 17:32:34 +0100666 fu_main_invocation_return_value (helper->priv, helper->invocation, NULL);
Richard Hughesf508e762015-02-27 12:49:36 +0000667 fu_main_helper_free (helper);
668}
669
Richard Hughes5d14def2015-10-07 17:43:10 +0100670static gchar *
671fu_main_get_guids_from_store (AsStore *store)
672{
Richard Hughes5d14def2015-10-07 17:43:10 +0100673 AsProvide *prov;
674 GPtrArray *provides;
675 GPtrArray *apps;
676 GString *str = g_string_new ("");
Richard Hughes5d14def2015-10-07 17:43:10 +0100677
678 /* return a string with all the firmware apps in the store */
679 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +0100680 for (guint i = 0; i < apps->len; i++) {
681 AsApp *app = AS_APP (g_ptr_array_index (apps, i));
Richard Hughes5d14def2015-10-07 17:43:10 +0100682 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100683 for (guint j = 0; j < provides->len; j++) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100684 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
685 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
686 continue;
687 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
688 }
689 }
690 if (str->len == 0)
691 return NULL;
692 g_string_truncate (str, str->len - 1);
693 return g_string_free (str, FALSE);
694}
695
Richard Hughesdf7950b2016-01-31 10:18:40 +0000696static void
697fu_main_vendor_quirk_release_version (AsApp *app)
698{
699 AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET;
700 GPtrArray *releases;
Richard Hughesdf7950b2016-01-31 10:18:40 +0000701
702 /* no quirk required */
Richard Hughes65dfbfe2016-03-02 13:42:53 +0000703 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
Richard Hughesdf7950b2016-01-31 10:18:40 +0000704 return;
705
Richard Hughesf192bf02016-07-22 08:26:43 +0100706 for (guint i = 0; quirk_table[i].identifier != NULL; i++) {
Mario Limonciello918f3932016-02-03 12:47:23 -0600707 if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier))
708 flags = quirk_table[i].flags;
Richard Hughesf192bf02016-07-22 08:26:43 +0100709 }
Richard Hughesdf7950b2016-01-31 10:18:40 +0000710
711 /* fix each release */
712 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100713 for (guint i = 0; i < releases->len; i++) {
Richard Hughesdf7950b2016-01-31 10:18:40 +0000714 AsRelease *rel;
715 const gchar *version;
716 guint64 ver_uint32;
717 g_autofree gchar *version_new = NULL;
718
719 rel = g_ptr_array_index (releases, i);
720 version = as_release_get_version (rel);
721 if (version == NULL)
722 continue;
723 if (g_strstr_len (version, -1, ".") != NULL)
724 continue;
725
726 /* metainfo files use hex and the LVFS uses decimal */
727 if (g_str_has_prefix (version, "0x")) {
728 ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16);
729 } else {
730 ver_uint32 = g_ascii_strtoull (version, NULL, 10);
731 }
732 if (ver_uint32 == 0)
733 continue;
734
735 /* convert to dotted decimal */
Richard Hughes33a518a2016-07-27 15:22:53 +0100736 version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags);
Richard Hughesdf7950b2016-01-31 10:18:40 +0000737 as_release_set_version (rel, version_new);
738 }
739}
740
Richard Hughes99147f12016-05-17 09:35:04 +0100741static AsApp *
742fu_main_store_get_app_by_guids (AsStore *store, FuDevice *device)
743{
Richard Hughesf192bf02016-07-22 08:26:43 +0100744 GPtrArray *guids = fu_device_get_guids (device);
745 for (guint i = 0; i < guids->len; i++) {
Richard Hughes99147f12016-05-17 09:35:04 +0100746 AsApp *app = NULL;
747 app = as_store_get_app_by_provide (store,
748 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
749 g_ptr_array_index (guids, i));
750 if (app != NULL)
751 return app;
752 }
753 return NULL;
754}
755
Richard Hughes01b9a832016-08-16 17:59:32 +0100756static AsScreenshot *
757_as_app_get_screenshot_default (AsApp *app)
758{
759 GPtrArray *array = as_app_get_screenshots (app);
760 if (array->len == 0)
761 return NULL;
762 return g_ptr_array_index (array, 0);
763}
764
Richard Hughes67ec8982015-03-03 11:39:27 +0000765static gboolean
Richard Hughes3ab17e62016-07-04 12:37:22 +0100766fu_main_update_helper_for_device (FuMainAuthHelper *helper,
767 FuDevice *device,
768 GError **error)
Richard Hughes67ec8982015-03-03 11:39:27 +0000769{
Richard Hughes5d14def2015-10-07 17:43:10 +0100770 AsApp *app;
771 AsChecksum *csum_tmp;
772 AsRelease *rel;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100773 GBytes *blob_fw;
Richard Hughesdef31752015-03-04 19:26:54 +0000774 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000775 const gchar *version;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100776 gboolean is_downgrade;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100777 gint vercmp;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100778
779 /* find from guid */
780 app = fu_main_store_get_app_by_guids (helper->store, device);
781 if (app == NULL) {
782 g_autofree gchar *guid = NULL;
783 guid = fu_main_get_guids_from_store (helper->store);
784 g_set_error (error,
785 FWUPD_ERROR,
786 FWUPD_ERROR_INVALID_FILE,
787 "firmware is not for this hw: required %s got %s",
788 fu_device_get_guid_default (device), guid);
789 return FALSE;
790 }
791
792 /* parse the DriverVer */
793 rel = as_app_get_release_default (app);
794 if (rel == NULL) {
795 g_set_error_literal (error,
796 FWUPD_ERROR,
797 FWUPD_ERROR_INVALID_FILE,
798 "no releases in the firmware component");
799 return FALSE;
800 }
801
Richard Hughes01b9a832016-08-16 17:59:32 +0100802 /* no update abilities */
Richard Hughes644562e2016-08-22 10:30:24 +0100803 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
804 !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +0100805 g_set_error (error,
806 FWUPD_ERROR,
807 FWUPD_ERROR_INTERNAL,
808 "Device %s does not currently allow updates",
809 fu_device_get_id (device));
810 return FALSE;
811 }
812
813 /* not in bootloader mode */
Richard Hughes644562e2016-08-22 10:30:24 +0100814 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
Richard Hughes01b9a832016-08-16 17:59:32 +0100815 const gchar *caption = NULL;
816 AsScreenshot *ss = _as_app_get_screenshot_default (app);
817 if (ss != NULL)
818 caption = as_screenshot_get_caption (ss, NULL);
819 if (caption != NULL) {
820 g_set_error (error,
821 FWUPD_ERROR,
822 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +0100823 "Device %s needs to manually be put in update mode: %s",
824 fu_device_get_name (device), caption);
Richard Hughes01b9a832016-08-16 17:59:32 +0100825 } else {
826 g_set_error (error,
827 FWUPD_ERROR,
828 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +0100829 "Device %s needs to manually be put in update mode",
830 fu_device_get_name (device));
Richard Hughes01b9a832016-08-16 17:59:32 +0100831 }
832 return FALSE;
833 }
834
Richard Hughes3ab17e62016-07-04 12:37:22 +0100835 /* get the blob */
836 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
837 tmp = as_checksum_get_filename (csum_tmp);
838 if (tmp == NULL) {
839 g_set_error_literal (error,
840 FWUPD_ERROR,
841 FWUPD_ERROR_INVALID_FILE,
842 "no checksum filename");
843 return FALSE;
844 }
845
846 /* not all devices have to use the same blob */
847 blob_fw = as_release_get_blob (rel, tmp);
848 if (blob_fw == NULL) {
849 g_set_error_literal (error,
850 FWUPD_ERROR,
851 FWUPD_ERROR_READ,
852 "failed to get firmware blob");
853 return FALSE;
854 }
855
856 /* possibly convert the version from 0x to dotted */
857 fu_main_vendor_quirk_release_version (app);
858
859 version = as_release_get_version (rel);
860 fu_device_set_update_version (device, version);
861
862 /* compare to the lowest supported version, if it exists */
863 tmp = fu_device_get_version_lowest (device);
864 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
865 g_set_error (error,
866 FWUPD_ERROR,
867 FWUPD_ERROR_VERSION_NEWER,
868 "Specified firmware is older than the minimum "
869 "required version '%s < %s'", tmp, version);
870 return FALSE;
871 }
872
873 /* check the device is locked */
Richard Hughes644562e2016-08-22 10:30:24 +0100874 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes3ab17e62016-07-04 12:37:22 +0100875 g_set_error (error,
876 FWUPD_ERROR,
877 FWUPD_ERROR_INTERNAL,
878 "Device %s is locked",
879 fu_device_get_id (device));
880 return FALSE;
881 }
882
883 /* compare the versions of what we have installed */
884 tmp = fu_device_get_version (device);
885 if (tmp == NULL) {
886 g_set_error (error,
887 FWUPD_ERROR,
888 FWUPD_ERROR_INTERNAL,
889 "Device %s does not yet have a current version",
890 fu_device_get_id (device));
891 return FALSE;
892 }
893 vercmp = as_utils_vercmp (tmp, version);
894 if (vercmp == 0 && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
895 g_set_error (error,
896 FWUPD_ERROR,
897 FWUPD_ERROR_VERSION_SAME,
898 "Specified firmware is already installed '%s'",
899 tmp);
900 return FALSE;
901 }
902 is_downgrade = vercmp > 0;
903 if (is_downgrade && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
904 g_set_error (error,
905 FWUPD_ERROR,
906 FWUPD_ERROR_VERSION_NEWER,
907 "Specified firmware is older than installed '%s < %s'",
908 tmp, version);
909 return FALSE;
910 }
911
912 /* if any downgrade, we want the global to be true */
913 if (is_downgrade)
914 helper->is_downgrade = is_downgrade;
915
916 /* verify */
917 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
918 return FALSE;
919
920 /* success */
921 g_ptr_array_add (helper->blob_fws, g_bytes_ref (blob_fw));
922 return TRUE;
923}
924
925static gboolean
926fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
927{
Richard Hughesc8646af2016-07-04 13:04:27 +0100928 g_autoptr(GError) error_first = NULL;
Richard Hughes67ec8982015-03-03 11:39:27 +0000929
Richard Hughes5d14def2015-10-07 17:43:10 +0100930 /* load store file which also decompresses firmware */
931 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
932 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000933 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000934
Richard Hughesbd405282016-07-04 13:00:54 +0100935 /* we've specified a specific device; failure is critical */
936 if (helper->devices->len > 0) {
Richard Hughesf192bf02016-07-22 08:26:43 +0100937 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100938 FuDevice *device = g_ptr_array_index (helper->devices, i);
939 if (!fu_main_update_helper_for_device (helper, device, error))
940 return FALSE;
Richard Hughes5d14def2015-10-07 17:43:10 +0100941 }
Richard Hughesbd405282016-07-04 13:00:54 +0100942 return TRUE;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100943 }
Richard Hughes5d14def2015-10-07 17:43:10 +0100944
Richard Hughesbd405282016-07-04 13:00:54 +0100945 /* if we've not chosen a device, try and find anything in the
946 * cabinet 'store' that matches any installed device and is updatable */
Richard Hughesf192bf02016-07-22 08:26:43 +0100947 for (guint i = 0; i < helper->priv->devices->len; i++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100948 AsApp *app;
949 FuDeviceItem *item;
950 g_autoptr(GError) error_local = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +0100951
Richard Hughesbd405282016-07-04 13:00:54 +0100952 /* guid found */
953 item = g_ptr_array_index (helper->priv->devices, i);
954 app = fu_main_store_get_app_by_guids (helper->store, item->device);
955 if (app == NULL)
956 continue;
957
958 /* try this device, error not fatal */
959 if (!fu_main_update_helper_for_device (helper,
960 item->device,
961 &error_local)) {
962 g_debug ("failed to add %s: %s",
963 fu_device_get_id (item->device),
964 error_local->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100965
966 /* save this for later */
967 if (error_first == NULL)
968 error_first = g_error_copy (error_local);
Richard Hughesbd405282016-07-04 13:00:54 +0100969 continue;
970 }
971
972 /* success */
973 g_ptr_array_add (helper->devices, g_object_ref (item->device));
974 }
Richard Hughes346ea882016-07-04 12:31:06 +0100975 if (helper->devices->len == 0) {
Richard Hughesc8646af2016-07-04 13:04:27 +0100976 if (error_first != NULL) {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100977 g_set_error_literal (error,
978 FWUPD_ERROR,
979 FWUPD_ERROR_INVALID_FILE,
980 error_first->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100981 } else {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100982 g_autofree gchar *guid = NULL;
983 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesc8646af2016-07-04 13:04:27 +0100984 g_set_error (error,
985 FWUPD_ERROR,
986 FWUPD_ERROR_INVALID_FILE,
987 "no attached hardware matched %s",
988 guid);
989 }
Richard Hughes346ea882016-07-04 12:31:06 +0100990 return FALSE;
991 }
992
Richard Hughesa4a2c182016-06-29 10:37:05 +0100993 /* sanity check */
994 if (helper->devices->len != helper->blob_fws->len) {
995 g_set_error (error,
996 FWUPD_ERROR,
997 FWUPD_ERROR_INTERNAL,
Richard Hughes33a518a2016-07-27 15:22:53 +0100998 "not enough firmware blobs (%u) for devices (%u)",
Richard Hughesa4a2c182016-06-29 10:37:05 +0100999 helper->blob_fws->len,
1000 helper->devices->len);
1001 return FALSE;
1002 }
1003
Richard Hughes67ec8982015-03-03 11:39:27 +00001004 return TRUE;
1005}
1006
Richard Hughes18423292015-03-09 17:10:50 +00001007static guint
1008fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
1009{
1010 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +01001011 g_autoptr(GError) error = NULL;
1012 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +00001013
1014 if (priv->proxy_uid == NULL)
1015 return G_MAXUINT;
1016 value = g_dbus_proxy_call_sync (priv->proxy_uid,
1017 "GetConnectionUnixUser",
1018 g_variant_new ("(s)", sender),
1019 G_DBUS_CALL_FLAGS_NONE,
1020 -1,
1021 NULL,
1022 &error);
1023 if (value == NULL) {
1024 g_warning ("Failed to get uid for %s: %s",
1025 sender, error->message);
1026 return G_MAXUINT;
1027 }
1028 g_variant_get (value, "(u)", &uid);
1029 return uid;
1030}
1031
Richard Hughes0e883ee2015-03-18 17:22:33 +00001032static FuDeviceItem *
1033fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
1034{
1035 FuDevice *dev;
1036 FuProvider *provider;
1037 FuDeviceItem *item = NULL;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001038 FwupdUpdateState update_state;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001039 const gchar *tmp;
Richard Hughes46832432015-09-11 13:43:15 +01001040 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001041
1042 /* not a wildcard */
1043 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1044 item = fu_main_get_item_by_id (priv, id);
1045 if (item == NULL) {
1046 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001047 FWUPD_ERROR,
1048 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001049 "no suitable device found for %s", id);
1050 }
1051 return item;
1052 }
1053
1054 /* allow '*' for any */
1055 devices = fu_pending_get_devices (priv->pending, error);
1056 if (devices == NULL)
1057 return NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01001058 for (guint i = 0; i < devices->len; i++) {
Richard Hughes0e883ee2015-03-18 17:22:33 +00001059 dev = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +00001060 update_state = fu_device_get_update_state (dev);
1061 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001062 continue;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001063 if (update_state == FWUPD_UPDATE_STATE_PENDING)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001064 continue;
1065
1066 /* if the device is not still connected, fake a FuDeviceItem */
1067 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
1068 if (item == NULL) {
Richard Hughes8e9762d2016-03-17 10:14:15 +00001069 tmp = fu_device_get_provider (dev);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001070 provider = fu_main_get_provider_by_name (priv, tmp);
1071 if (provider == NULL) {
1072 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001073 FWUPD_ERROR,
1074 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001075 "no provider %s found", tmp);
1076 }
1077 item = g_new0 (FuDeviceItem, 1);
1078 item->device = g_object_ref (dev);
1079 item->provider = g_object_ref (provider);
1080 g_ptr_array_add (priv->devices, item);
1081
1082 /* FIXME: just a boolean on FuDeviceItem? */
1083 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
1084 }
1085 break;
1086 }
1087
1088 /* no device found */
1089 if (item == NULL) {
1090 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001091 FWUPD_ERROR,
1092 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001093 "no suitable devices found");
1094 }
1095 return item;
1096}
1097
Richard Hughes63bbbf52015-04-14 16:12:16 +01001098static const gchar *
1099fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
1100{
Richard Hughesfe5cc902016-06-29 10:00:00 +01001101 gboolean all_removable = TRUE;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001102 gboolean is_trusted;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001103
1104 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +01001105 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001106
Richard Hughesfe5cc902016-06-29 10:00:00 +01001107 /* any non-removable means false */
Richard Hughesf192bf02016-07-22 08:26:43 +01001108 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001109 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughes644562e2016-08-22 10:30:24 +01001110 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INTERNAL)) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001111 all_removable = FALSE;
1112 break;
1113 }
1114 }
1115
Richard Hughes63bbbf52015-04-14 16:12:16 +01001116 /* relax authentication checks for removable devices */
Richard Hughesfe5cc902016-06-29 10:00:00 +01001117 if (all_removable) {
Richard Hughesdb468ee2016-06-29 10:10:47 +01001118 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001119 return "org.freedesktop.fwupd.downgrade-hotplug";
1120 if (is_trusted)
1121 return "org.freedesktop.fwupd.update-hotplug-trusted";
1122 return "org.freedesktop.fwupd.update-hotplug";
1123 }
1124
1125 /* internal device */
Richard Hughesdb468ee2016-06-29 10:10:47 +01001126 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001127 return "org.freedesktop.fwupd.downgrade-internal";
1128 if (is_trusted)
1129 return "org.freedesktop.fwupd.update-internal-trusted";
1130 return "org.freedesktop.fwupd.update-internal";
1131}
1132
Richard Hughesae0efdc2015-06-24 16:18:29 +01001133static gboolean
Mario Limonciello3ed54472015-07-23 13:19:39 -05001134fu_main_daemon_update_metadata (FuMainPrivate *priv, gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +01001135{
Richard Hughesf2fca012015-10-30 08:44:44 +00001136 const guint8 *data;
Richard Hughesf2fca012015-10-30 08:44:44 +00001137 gsize size;
Richard Hughes727664f2015-10-27 09:56:04 +00001138 GPtrArray *apps;
Richard Hughesbb840ce2015-10-30 08:47:24 +00001139 g_autofree gchar *xml = NULL;
Richard Hughes727664f2015-10-27 09:56:04 +00001140 g_autoptr(AsStore) store = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001141 g_autoptr(GBytes) bytes = NULL;
1142 g_autoptr(GBytes) bytes_raw = NULL;
1143 g_autoptr(GBytes) bytes_sig = NULL;
1144 g_autoptr(FuKeyring) kr = NULL;
1145 g_autoptr(GConverter) converter = NULL;
1146 g_autoptr(GFile) file = NULL;
1147 g_autoptr(GInputStream) stream_buf = NULL;
1148 g_autoptr(GInputStream) stream_fd = NULL;
1149 g_autoptr(GInputStream) stream = NULL;
1150 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001151
Richard Hughesae0efdc2015-06-24 16:18:29 +01001152 /* read the entire file into memory */
1153 stream_fd = g_unix_input_stream_new (fd, TRUE);
1154 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1155 if (bytes_raw == NULL)
1156 return FALSE;
1157 stream_buf = g_memory_input_stream_new ();
1158 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
1159
1160 /* peek the file type and get data */
Richard Hughesf2fca012015-10-30 08:44:44 +00001161 data = g_bytes_get_data (bytes_raw, &size);
1162 if (size < 2) {
1163 g_set_error_literal (error,
1164 FWUPD_ERROR,
1165 FWUPD_ERROR_INVALID_FILE,
1166 "file is too small");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001167 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001168 }
1169 if (data[0] == 0x1f && data[1] == 0x8b) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001170 g_debug ("using GZip decompressor for data");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001171 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
1172 stream = g_converter_input_stream_new (stream_buf, converter);
1173 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
1174 if (bytes == NULL)
1175 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001176 } else if (data[0] == '<' && data[1] == '?') {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001177 g_debug ("using no decompressor for data");
1178 bytes = g_bytes_ref (bytes_raw);
1179 } else {
1180 g_set_error (error,
1181 FWUPD_ERROR,
1182 FWUPD_ERROR_INVALID_FILE,
1183 "file type '0x%02x,0x%02x' not supported",
Richard Hughesf2fca012015-10-30 08:44:44 +00001184 data[0], data[1]);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001185 return FALSE;
1186 }
1187
1188 /* read signature */
1189 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1190 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1191 if (bytes_sig == NULL)
1192 return FALSE;
1193
1194 /* verify file */
1195 kr = fu_keyring_new ();
1196 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
1197 return FALSE;
1198 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
1199 return FALSE;
1200
Richard Hughes727664f2015-10-27 09:56:04 +00001201 /* load the store locally until we know it is valid */
1202 store = as_store_new ();
Richard Hughesbb840ce2015-10-30 08:47:24 +00001203 data = g_bytes_get_data (bytes, &size);
1204 xml = g_strndup ((const gchar *) data, size);
1205 if (!as_store_from_xml (store, xml, NULL, error))
Richard Hughesae0efdc2015-06-24 16:18:29 +01001206 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001207
Richard Hughes727664f2015-10-27 09:56:04 +00001208 /* add the new application from the store */
1209 as_store_remove_all (priv->store);
1210 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +01001211 for (guint i = 0; i < apps->len; i++) {
Richard Hughes727664f2015-10-27 09:56:04 +00001212 AsApp *app = g_ptr_array_index (apps, i);
1213 as_store_add_app (priv->store, app);
1214 }
1215
Richard Hughesae0efdc2015-06-24 16:18:29 +01001216 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +01001217 as_store_set_api_version (priv->store, 0.9);
1218 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
1219 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +01001220 AS_NODE_TO_XML_FLAG_ADD_HEADER |
1221 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
1222 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
1223 NULL, error)) {
1224 return FALSE;
1225 }
1226
1227 return TRUE;
1228}
1229
Richard Hughes033ccba2015-09-10 14:51:28 +01001230static gboolean
1231fu_main_store_delay_cb (gpointer user_data)
1232{
Richard Hughes033ccba2015-09-10 14:51:28 +01001233 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf192bf02016-07-22 08:26:43 +01001234 GPtrArray *apps;
Richard Hughes033ccba2015-09-10 14:51:28 +01001235
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001236 /* print what we've got */
Richard Hughes033ccba2015-09-10 14:51:28 +01001237 apps = as_store_get_apps (priv->store);
Richard Hughes0a36f442016-01-06 14:57:20 +00001238 if (apps->len == 0) {
1239 g_debug ("no devices in store");
1240 } else {
1241 g_debug ("devices now in store:");
Richard Hughesf192bf02016-07-22 08:26:43 +01001242 for (guint i = 0; i < apps->len; i++) {
1243 AsApp *app = g_ptr_array_index (apps, i);
Richard Hughes33a518a2016-07-27 15:22:53 +01001244 g_debug ("%u\t%s\t%s", i + 1,
Richard Hughes0a36f442016-01-06 14:57:20 +00001245 as_app_get_id (app),
1246 as_app_get_name (app, NULL));
1247 }
Richard Hughes033ccba2015-09-10 14:51:28 +01001248 }
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001249
1250 /* are any devices now supported? */
Richard Hughesf192bf02016-07-22 08:26:43 +01001251 for (guint i = 0; i < priv->devices->len; i++) {
1252 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001253 if (fu_main_get_updates_item_update (priv, item))
1254 fu_main_emit_device_changed (priv, item->device);
1255 }
1256
Richard Hughes033ccba2015-09-10 14:51:28 +01001257 priv->store_changed_id = 0;
1258 return G_SOURCE_REMOVE;
1259}
1260
Richard Hughes033ccba2015-09-10 14:51:28 +01001261static void
1262fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
1263{
1264 if (priv->store_changed_id != 0)
1265 return;
1266 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
1267}
1268
Richard Hughes654f6b82016-04-25 12:29:48 +01001269static gboolean
1270fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item)
1271{
1272 AsApp *app;
1273 AsChecksum *csum;
1274 AsRelease *rel;
1275 GPtrArray *releases;
1276 const gchar *tmp;
1277 const gchar *version;
Richard Hughes654f6b82016-04-25 12:29:48 +01001278 g_autoptr(GPtrArray) updates_list = NULL;
1279
1280 /* get device version */
1281 version = fu_device_get_version (item->device);
1282 if (version == NULL)
1283 return FALSE;
1284
Richard Hughes99147f12016-05-17 09:35:04 +01001285 /* match the GUIDs in the XML */
1286 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughes654f6b82016-04-25 12:29:48 +01001287 if (app == NULL)
1288 return FALSE;
1289
1290 /* possibly convert the version from 0x to dotted */
1291 fu_main_vendor_quirk_release_version (app);
1292
1293 /* get latest release */
1294 rel = as_app_get_release_default (app);
1295 if (rel == NULL) {
Richard Hughes14d17642016-08-17 12:03:03 +01001296 g_debug ("%s [%s] has no firmware update metadata",
1297 fu_device_get_id (item->device),
1298 fu_device_get_name (item->device));
Richard Hughes654f6b82016-04-25 12:29:48 +01001299 return FALSE;
1300 }
1301
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001302 /* supported in metadata */
1303 fwupd_result_add_device_flag (FWUPD_RESULT (item->device),
Richard Hughes644562e2016-08-22 10:30:24 +01001304 FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001305
Richard Hughes654f6b82016-04-25 12:29:48 +01001306 /* check if actually newer than what we have installed */
1307 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0) {
1308 g_debug ("%s has no firmware updates",
1309 fu_device_get_id (item->device));
1310 return FALSE;
1311 }
1312
Richard Hughes6561c8d2016-05-17 16:24:09 +01001313 /* only show devices that can be updated */
Richard Hughes644562e2016-08-22 10:30:24 +01001314 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
1315 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +01001316 g_debug ("ignoring %s [%s] as not updatable live or offline",
1317 fu_device_get_id (item->device),
1318 fu_device_get_name (item->device));
Richard Hughes6561c8d2016-05-17 16:24:09 +01001319 return FALSE;
1320 }
1321
Richard Hughes654f6b82016-04-25 12:29:48 +01001322 /* can we only do this on AC power */
Richard Hughes644562e2016-08-22 10:30:24 +01001323 if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_REQUIRE_AC) &&
Richard Hughes654f6b82016-04-25 12:29:48 +01001324 fu_main_on_battery (priv)) {
1325 g_debug ("ignoring update for %s as not on AC power",
1326 fu_device_get_id (item->device));
1327 return FALSE;
1328 }
1329
1330 /* add application metadata */
1331 fu_device_set_update_id (item->device, as_app_get_id (app));
1332 tmp = as_app_get_developer_name (app, NULL);
1333 if (tmp != NULL)
1334 fu_device_set_update_vendor (item->device, tmp);
1335 tmp = as_app_get_name (app, NULL);
1336 if (tmp != NULL)
1337 fu_device_set_update_name (item->device, tmp);
1338 tmp = as_app_get_comment (app, NULL);
1339 if (tmp != NULL)
1340 fu_device_set_update_summary (item->device, tmp);
1341 tmp = as_app_get_description (app, NULL);
1342 if (tmp != NULL)
1343 fu_device_set_description (item->device, tmp);
1344 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
1345 if (tmp != NULL)
1346 fu_device_set_update_homepage (item->device, tmp);
1347 tmp = as_app_get_project_license (app);
1348 if (tmp != NULL)
1349 fu_device_set_update_license (item->device, tmp);
Richard Hughesfa782a32016-08-18 18:34:59 +01001350#if AS_CHECK_VERSION(0,6,1)
1351 tmp = as_app_get_unique_id (app);
1352 if (tmp != NULL)
1353 fu_device_set_unique_id (item->device, tmp);
1354#else
1355 fu_device_set_unique_id (item->device, as_app_get_id (app));
1356#endif
Richard Hughes654f6b82016-04-25 12:29:48 +01001357
1358 /* add release information */
1359 tmp = as_release_get_version (rel);
1360 if (tmp != NULL)
1361 fu_device_set_update_version (item->device, tmp);
1362 csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTAINER);
1363 if (csum != NULL) {
1364 fu_device_set_update_checksum (item->device,
1365 as_checksum_get_value (csum));
1366 }
1367 tmp = as_release_get_location_default (rel);
1368 if (tmp != NULL)
1369 fu_device_set_update_uri (item->device, tmp);
1370
1371 /* get the list of releases newer than the one installed */
1372 updates_list = g_ptr_array_new ();
1373 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001374 for (guint i = 0; i < releases->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001375 rel = g_ptr_array_index (releases, i);
Richard Hughesae874382016-08-17 14:23:05 +01001376 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0)
Richard Hughes654f6b82016-04-25 12:29:48 +01001377 continue;
1378 tmp = as_release_get_description (rel, NULL);
1379 if (tmp == NULL)
1380 continue;
1381 g_ptr_array_add (updates_list, rel);
1382 }
1383
1384 /* no prefix on each release */
1385 if (updates_list->len == 1) {
1386 rel = g_ptr_array_index (updates_list, 0);
1387 fu_device_set_update_description (item->device,
1388 as_release_get_description (rel, NULL));
1389 } else {
1390 g_autoptr(GString) update_desc = NULL;
1391 update_desc = g_string_new ("");
1392
1393 /* get the descriptions with a version prefix */
Richard Hughesf192bf02016-07-22 08:26:43 +01001394 for (guint i = 0; i < updates_list->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001395 rel = g_ptr_array_index (updates_list, i);
1396 g_string_append_printf (update_desc,
1397 "<p>%s:</p>%s",
1398 as_release_get_version (rel),
1399 as_release_get_description (rel, NULL));
1400 }
1401 if (update_desc->len > 0)
1402 fu_device_set_update_description (item->device, update_desc->str);
1403 }
1404
1405 /* success */
1406 return TRUE;
1407}
1408
Richard Hughesf192bf02016-07-22 08:26:43 +01001409/* find any updates using the AppStream metadata */
Richard Hughes7708a0f2015-07-21 08:41:22 +01001410static GPtrArray *
1411fu_main_get_updates (FuMainPrivate *priv, GError **error)
1412{
Richard Hughesf192bf02016-07-22 08:26:43 +01001413 GPtrArray *updates = g_ptr_array_new ();
1414 for (guint i = 0; i < priv->devices->len; i++) {
1415 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes654f6b82016-04-25 12:29:48 +01001416 if (fu_main_get_updates_item_update (priv, item))
1417 g_ptr_array_add (updates, item);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001418 }
Richard Hughes7708a0f2015-07-21 08:41:22 +01001419 return updates;
1420}
1421
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001422static AsStore *
1423fu_main_get_store_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1424{
Richard Hughes5c066ad2016-08-18 18:38:06 +01001425 g_autofree gchar *checksum = NULL;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001426 g_autoptr(AsStore) store = NULL;
1427 g_autoptr(GBytes) blob_cab = NULL;
1428 g_autoptr(GError) error_local = NULL;
1429 g_autoptr(GInputStream) stream = NULL;
1430
1431 /* read the entire fd to a data blob */
1432 stream = g_unix_input_stream_new (fd, TRUE);
1433 blob_cab = g_input_stream_read_bytes (stream,
1434 FU_MAIN_FIRMWARE_SIZE_MAX,
1435 NULL, &error_local);
1436 if (blob_cab == NULL){
1437 g_set_error_literal (error,
1438 FWUPD_ERROR,
1439 FWUPD_ERROR_INVALID_FILE,
1440 error_local->message);
1441 return NULL;
1442 }
1443
1444 /* load file */
1445 store = as_store_new ();
1446 if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) {
1447 g_set_error_literal (error,
1448 FWUPD_ERROR,
1449 FWUPD_ERROR_INVALID_FILE,
1450 error_local->message);
1451 return NULL;
1452 }
Richard Hughes5c066ad2016-08-18 18:38:06 +01001453
1454 /* get a checksum of the file and use it as the origin */
1455 checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
1456 g_bytes_get_data (blob_cab, NULL),
1457 g_bytes_get_size (blob_cab));
1458 as_store_set_origin (store, checksum);
1459
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001460 return g_steal_pointer (&store);
1461}
1462
1463static FwupdResult *
1464fu_main_get_result_from_app (FuMainPrivate *priv, AsApp *app, GError **error)
1465{
1466 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
1467 AsRelease *rel;
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001468 AsChecksum * csum_tmp;
1469 const gchar *fn;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001470 GPtrArray *provides;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001471 g_autoptr(FwupdResult) res = NULL;
1472
1473 res = fwupd_result_new ();
1474 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001475 for (guint i = 0; i < provides->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001476 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1477 FuDeviceItem *item;
1478 const gchar *guid;
1479
1480 /* not firmware */
1481 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
1482 continue;
1483
1484 /* is a online or offline update appropriate */
1485 guid = as_provide_get_value (prov);
1486 if (guid == NULL)
1487 continue;
1488 item = fu_main_get_item_by_guid (priv, guid);
1489 if (item != NULL) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001490 fwupd_result_set_device_flags (res, fu_device_get_flags (item->device));
1491 fwupd_result_set_device_id (res, fu_device_get_id (item->device));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001492 }
1493
1494 /* add GUID */
1495 fwupd_result_add_guid (res, guid);
1496 }
1497 if (fwupd_result_get_guids(res)->len == 0) {
1498 g_set_error_literal (error,
1499 FWUPD_ERROR,
1500 FWUPD_ERROR_INTERNAL,
1501 "component has no GUIDs");
1502 return NULL;
1503 }
1504
1505 /* verify trust */
1506 rel = as_app_get_release_default (app);
1507 if (!fu_main_get_release_trust_flags (rel, &trust_flags, error))
1508 return NULL;
1509
1510 /* possibly convert the version from 0x to dotted */
1511 fu_main_vendor_quirk_release_version (app);
1512
1513 /* create a result with all the metadata in */
1514 fwupd_result_set_device_description (res, as_app_get_description (app, NULL));
Richard Hughes4921bd92016-06-27 11:17:43 +01001515 fwupd_result_set_update_id (res, as_app_get_id (app));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001516 fwupd_result_set_update_description (res, as_release_get_description (rel, NULL));
1517 fwupd_result_set_update_homepage (res, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
1518 fwupd_result_set_update_license (res, as_app_get_project_license (app));
1519 fwupd_result_set_update_name (res, as_app_get_name (app, NULL));
1520 fwupd_result_set_update_size (res, as_release_get_size (rel, AS_SIZE_KIND_INSTALLED));
1521 fwupd_result_set_update_summary (res, as_app_get_comment (app, NULL));
1522 fwupd_result_set_update_trust_flags (res, trust_flags);
1523 fwupd_result_set_update_vendor (res, as_app_get_developer_name (app, NULL));
1524 fwupd_result_set_update_version (res, as_release_get_version (rel));
Richard Hughesfa782a32016-08-18 18:34:59 +01001525#if AS_CHECK_VERSION(0,6,1)
1526 fwupd_result_set_unique_id (res, as_app_get_unique_id (app));
1527#else
1528 fwupd_result_set_unique_id (res, as_app_get_id (app));
1529#endif
1530
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001531 csum_tmp = as_release_get_checksum_by_target (rel,
1532 AS_CHECKSUM_TARGET_CONTENT);
1533 fn = as_checksum_get_filename (csum_tmp);
1534 if (fn != NULL)
1535 fwupd_result_set_update_filename (res, fn);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001536 return g_steal_pointer (&res);
1537}
1538
1539static GVariant *
1540fu_main_get_details_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1541{
1542 AsApp *app = NULL;
1543 GPtrArray *apps;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001544 g_autoptr(AsStore) store = NULL;
1545 g_autoptr(FwupdResult) res = NULL;
1546
1547 store = fu_main_get_store_from_fd (priv, fd, error);
1548 if (store == NULL)
1549 return NULL;
1550
1551 /* get all apps */
1552 apps = as_store_get_apps (store);
1553 if (apps->len == 0) {
1554 g_set_error_literal (error,
1555 FWUPD_ERROR,
1556 FWUPD_ERROR_INVALID_FILE,
1557 "no components");
1558 return NULL;
1559 }
1560 if (apps->len > 1) {
1561 /* we've got a .cab file with multiple components,
1562 * so try to find the first thing that's installed */
Richard Hughesf192bf02016-07-22 08:26:43 +01001563 for (guint i = 0; i < priv->devices->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001564 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
1565 app = fu_main_store_get_app_by_guids (store, item->device);
1566 if (app != NULL)
1567 break;
1568 }
1569 }
1570
1571 /* well, we've tried our best, just show the first entry */
1572 if (app == NULL)
1573 app = AS_APP (g_ptr_array_index (apps, 0));
1574
1575 /* create a result with all the metadata in */
Richard Hughes5c066ad2016-08-18 18:38:06 +01001576 as_app_set_origin (app, as_store_get_origin (store));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001577 res = fu_main_get_result_from_app (priv, app, error);
1578 if (res == NULL)
1579 return NULL;
1580 return fwupd_result_to_data (res, "(a{sv})");
1581}
1582
Richard Hughes7289a6b2016-05-29 09:27:47 +01001583static GVariant *
1584fu_main_get_details_local_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1585{
1586 GPtrArray *apps;
1587 GVariantBuilder builder;
Richard Hughes7289a6b2016-05-29 09:27:47 +01001588 g_autoptr(AsStore) store = NULL;
1589
1590 store = fu_main_get_store_from_fd (priv, fd, error);
1591 if (store == NULL)
1592 return NULL;
1593
1594 /* get all apps */
1595 apps = as_store_get_apps (store);
1596 if (apps->len == 0) {
1597 g_set_error_literal (error,
1598 FWUPD_ERROR,
1599 FWUPD_ERROR_INVALID_FILE,
1600 "no components");
1601 return NULL;
1602 }
1603
1604 /* create results with all the metadata in */
1605 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +01001606 for (guint i = 0; i < apps->len; i++) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001607 g_autoptr(FwupdResult) res = NULL;
1608 AsApp *app = g_ptr_array_index (apps, i);
1609 GVariant *tmp;
Richard Hughes5c066ad2016-08-18 18:38:06 +01001610 as_app_set_origin (app, as_store_get_origin (store));
Richard Hughes7289a6b2016-05-29 09:27:47 +01001611 res = fu_main_get_result_from_app (priv, app, error);
1612 if (res == NULL)
1613 return NULL;
1614 tmp = fwupd_result_to_data (res, "{sa{sv}}");
1615 g_variant_builder_add_value (&builder, tmp);
1616 }
1617 return g_variant_new ("(a{sa{sv}})", &builder);
1618}
1619
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001620static void
1621fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
1622 const gchar *object_path, const gchar *interface_name,
1623 const gchar *method_name, GVariant *parameters,
1624 GDBusMethodInvocation *invocation, gpointer user_data)
1625{
1626 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1627 GVariant *val;
Richard Hughes060af612016-08-17 17:32:34 +01001628 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001629
1630 /* return 'as' */
1631 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughesf508e762015-02-27 12:49:36 +00001632 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001633 val = fu_main_device_array_to_variant (priv->devices, &error);
1634 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001635 if (g_error_matches (error,
1636 FWUPD_ERROR,
1637 FWUPD_ERROR_NOTHING_TO_DO)) {
1638 g_prefix_error (&error, "No detected devices: ");
1639 }
Richard Hughes060af612016-08-17 17:32:34 +01001640 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001641 return;
1642 }
Richard Hughes060af612016-08-17 17:32:34 +01001643 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001644 return;
1645 }
1646
1647 /* return 'as' */
1648 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +01001649 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +01001650 g_debug ("Called %s()", method_name);
1651 updates = fu_main_get_updates (priv, &error);
1652 if (updates == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001653 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001654 return;
1655 }
1656 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001657 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001658 if (g_error_matches (error,
1659 FWUPD_ERROR,
1660 FWUPD_ERROR_NOTHING_TO_DO)) {
Mario Limonciello54a5a212016-10-19 14:29:53 -05001661 if (fu_main_on_battery (priv))
1662 g_prefix_error (&error, "No devices can be updated while operating on battery power: ");
1663 else
1664 g_prefix_error (&error, "No devices can be updated: ");
Richard Hughes9d76a872015-09-17 12:49:07 +01001665 }
Richard Hughes060af612016-08-17 17:32:34 +01001666 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001667 return;
1668 }
Richard Hughes060af612016-08-17 17:32:34 +01001669 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001670 return;
1671 }
1672
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001673 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +00001674 if (g_strcmp0 (method_name, "ClearResults") == 0) {
1675 FuDeviceItem *item = NULL;
1676 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001677
1678 g_variant_get (parameters, "(&s)", &id);
1679 g_debug ("Called %s(%s)", method_name, id);
1680
1681 /* find device */
1682 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1683 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001684 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001685 return;
1686 }
1687
1688 /* call into the provider */
1689 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001690 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001691 return;
1692 }
1693
1694 /* success */
Richard Hughes060af612016-08-17 17:32:34 +01001695 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001696 return;
1697 }
1698
1699 /* return 'a{sv}' */
1700 if (g_strcmp0 (method_name, "GetResults") == 0) {
1701 FuDeviceItem *item = NULL;
1702 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001703
1704 g_variant_get (parameters, "(&s)", &id);
1705 g_debug ("Called %s(%s)", method_name, id);
1706
1707 /* find device */
1708 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1709 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001710 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001711 return;
1712 }
1713
1714 /* call into the provider */
1715 if (!fu_provider_get_results (item->provider, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001716 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001717 return;
1718 }
1719
Richard Hughes2db526d2016-08-25 15:07:23 +01001720 /* ensure the unique ID is set */
1721 if (fwupd_result_get_unique_id (FWUPD_RESULT (item->device)) == NULL) {
Richard Hughes4ced4662016-08-26 11:02:31 +01001722 g_autofree gchar *id2 = NULL;
Richard Hughes2db526d2016-08-25 15:07:23 +01001723 FwupdResult *res = FWUPD_RESULT (item->device);
Richard Hughes27aad5a2016-08-29 16:12:23 +01001724#if AS_CHECK_VERSION(0,6,1)
Richard Hughes4ced4662016-08-26 11:02:31 +01001725 id2 = as_utils_unique_id_build (AS_APP_SCOPE_SYSTEM,
1726 AS_BUNDLE_KIND_UNKNOWN,
1727 NULL,
1728 AS_APP_KIND_FIRMWARE,
1729 fwupd_result_get_device_name (res),
1730 fwupd_result_get_device_version (res));
Richard Hughes27aad5a2016-08-29 16:12:23 +01001731#else
1732 id2 = g_strdup_printf ("system/*/*/firmware/%s/%s",
1733 fwupd_result_get_device_name (res),
1734 fwupd_result_get_device_version (res));
1735#endif
Richard Hughes4ced4662016-08-26 11:02:31 +01001736 fwupd_result_set_unique_id (res, id2);
Richard Hughes2db526d2016-08-25 15:07:23 +01001737 }
1738
Richard Hughes0e883ee2015-03-18 17:22:33 +00001739 /* success */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001740 val = fwupd_result_to_data (FWUPD_RESULT (item->device), "(a{sv})");
Richard Hughes060af612016-08-17 17:32:34 +01001741 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001742 return;
1743 }
1744
1745 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01001746 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
1747 GDBusMessage *message;
1748 GUnixFDList *fd_list;
1749 gint fd_data;
1750 gint fd_sig;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001751
1752 message = g_dbus_method_invocation_get_message (invocation);
1753 fd_list = g_dbus_message_get_unix_fd_list (message);
1754 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
Richard Hughes060af612016-08-17 17:32:34 +01001755 g_set_error (&error,
1756 FWUPD_ERROR,
1757 FWUPD_ERROR_INTERNAL,
1758 "invalid handle");
1759 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001760 return;
1761 }
1762 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
1763 if (fd_data < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001764 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001765 return;
1766 }
1767 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
1768 if (fd_sig < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001769 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001770 return;
1771 }
Mario Limonciello3ed54472015-07-23 13:19:39 -05001772 if (!fu_main_daemon_update_metadata (priv, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001773 g_prefix_error (&error, "failed to update metadata: ");
Richard Hughes060af612016-08-17 17:32:34 +01001774 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001775 return;
1776 }
Richard Hughes060af612016-08-17 17:32:34 +01001777 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001778 return;
1779 }
1780
Richard Hughesa043c2e2015-06-29 08:43:18 +01001781 /* return 's' */
Richard Hughes9a410ce2016-02-28 15:58:54 +00001782 if (g_strcmp0 (method_name, "Unlock") == 0) {
1783 FuDeviceItem *item = NULL;
1784 FuMainAuthHelper *helper;
1785 const gchar *id = NULL;
1786 g_autoptr(PolkitSubject) subject = NULL;
1787
1788 /* check the id exists */
1789 g_variant_get (parameters, "(&s)", &id);
1790 g_debug ("Called %s(%s)", method_name, id);
1791 item = fu_main_get_item_by_id (priv, id);
1792 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001793 g_set_error (&error,
1794 FWUPD_ERROR,
1795 FWUPD_ERROR_NOT_FOUND,
1796 "No such device %s", id);
1797 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001798 return;
1799 }
1800
1801 /* check the device is locked */
Richard Hughes644562e2016-08-22 10:30:24 +01001802 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes060af612016-08-17 17:32:34 +01001803 g_set_error (&error,
1804 FWUPD_ERROR,
1805 FWUPD_ERROR_NOT_FOUND,
1806 "Device %s is not locked", id);
1807 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001808 return;
1809 }
1810
1811 /* process the firmware */
1812 helper = g_new0 (FuMainAuthHelper, 1);
1813 helper->auth_kind = FU_MAIN_AUTH_KIND_UNLOCK;
1814 helper->invocation = g_object_ref (invocation);
Richard Hughesfe5cc902016-06-29 10:00:00 +01001815 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001816 helper->priv = priv;
1817
Richard Hughesfe5cc902016-06-29 10:00:00 +01001818 /* FIXME: do we want to support "*"? */
1819 g_ptr_array_add (helper->devices, g_object_ref (item->device));
1820
Richard Hughes9a410ce2016-02-28 15:58:54 +00001821 /* authenticate */
1822 subject = polkit_system_bus_name_new (sender);
1823 polkit_authority_check_authorization (helper->priv->authority, subject,
1824 "org.freedesktop.fwupd.device-unlock",
1825 NULL,
1826 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1827 NULL,
1828 fu_main_check_authorization_cb,
1829 helper);
1830 return;
1831 }
1832
1833 /* return 's' */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001834 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001835 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001836 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001837 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001838 FuDeviceItem *item = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001839 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001840 const gchar *id = NULL;
1841 const gchar *version = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001842
1843 /* check the id exists */
1844 g_variant_get (parameters, "(&s)", &id);
1845 g_debug ("Called %s(%s)", method_name, id);
1846 item = fu_main_get_item_by_id (priv, id);
1847 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001848 g_set_error (&error,
1849 FWUPD_ERROR,
1850 FWUPD_ERROR_NOT_FOUND,
1851 "No such device %s", id);
1852 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001853 return;
1854 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001855
1856 /* set the device firmware hash */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001857 if (!fu_provider_verify (item->provider, item->device,
1858 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001859 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001860 return;
1861 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001862
1863 /* find component in metadata */
Richard Hughes99147f12016-05-17 09:35:04 +01001864 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001865 if (app == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001866 g_set_error_literal (&error,
1867 FWUPD_ERROR,
1868 FWUPD_ERROR_NOT_FOUND,
1869 "No metadata");
1870 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001871 return;
1872 }
1873
1874 /* find version in metadata */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001875 version = fu_device_get_version (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001876 release = as_app_get_release (app, version);
1877 if (release == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001878 g_set_error (&error,
1879 FWUPD_ERROR,
1880 FWUPD_ERROR_NOT_FOUND,
1881 "No version %s", version);
1882 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001883 return;
1884 }
1885
1886 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001887 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
1888 if (csum == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001889 g_set_error (&error,
1890 FWUPD_ERROR,
1891 FWUPD_ERROR_NOT_FOUND,
1892 "No content checksum for %s", version);
1893 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001894 return;
1895 }
Richard Hughes8e9762d2016-03-17 10:14:15 +00001896 hash = fu_device_get_checksum (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001897 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001898 g_set_error (&error,
1899 FWUPD_ERROR,
1900 FWUPD_ERROR_NOT_FOUND,
1901 "For v%s expected %s, got %s",
1902 version,
1903 as_checksum_get_value (csum),
1904 hash);
1905 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001906 return;
1907 }
Richard Hughes060af612016-08-17 17:32:34 +01001908 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001909 return;
1910 }
1911
Richard Hughesae0efdc2015-06-24 16:18:29 +01001912 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01001913 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00001914 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001915 FuMainAuthHelper *helper;
Richard Hughes2d6e1862016-03-18 09:20:37 +00001916 FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE;
Richard Hughes74cc2172015-02-27 13:19:46 +00001917 GDBusMessage *message;
1918 GUnixFDList *fd_list;
1919 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00001920 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001921 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00001922 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001923 gint32 fd_handle = 0;
1924 gint fd;
Richard Hughes60f48c22015-10-08 20:25:51 +01001925 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001926 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001927 g_autoptr(GBytes) blob_cab = NULL;
1928 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001929
1930 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00001931 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00001932 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001933 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1934 item = fu_main_get_item_by_id (priv, id);
1935 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001936 g_set_error (&error,
1937 FWUPD_ERROR,
1938 FWUPD_ERROR_NOT_FOUND,
1939 "no such device %s", id);
1940 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001941 return;
1942 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001943 }
1944
Richard Hughes74cc2172015-02-27 13:19:46 +00001945 /* get options */
1946 while (g_variant_iter_next (iter, "{&sv}",
1947 &prop_key, &prop_value)) {
1948 g_debug ("got option %s", prop_key);
1949 if (g_strcmp0 (prop_key, "offline") == 0 &&
1950 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001951 flags |= FWUPD_INSTALL_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00001952 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
1953 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001954 flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughese7c12642015-03-04 20:28:59 +00001955 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
1956 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001957 flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Mario Limonciello71a5b982016-05-10 15:38:53 -05001958 if (g_strcmp0 (prop_key, "force") == 0 &&
1959 g_variant_get_boolean (prop_value) == TRUE)
1960 flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00001961 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00001962 }
1963
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001964 /* get the fd */
1965 message = g_dbus_method_invocation_get_message (invocation);
1966 fd_list = g_dbus_message_get_unix_fd_list (message);
1967 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01001968 g_set_error (&error,
1969 FWUPD_ERROR,
1970 FWUPD_ERROR_INTERNAL,
1971 "invalid handle");
1972 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001973 return;
1974 }
Richard Hughes7419e962016-11-22 19:48:06 +00001975 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001976 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001977 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001978 return;
1979 }
1980
Richard Hughes5d14def2015-10-07 17:43:10 +01001981 /* read the entire fd to a data blob */
1982 stream = g_unix_input_stream_new (fd, TRUE);
1983 blob_cab = g_input_stream_read_bytes (stream,
1984 FU_MAIN_FIRMWARE_SIZE_MAX,
1985 NULL, &error);
1986 if (blob_cab == NULL){
Richard Hughes060af612016-08-17 17:32:34 +01001987 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes5d14def2015-10-07 17:43:10 +01001988 return;
1989 }
1990
Richard Hughes67ec8982015-03-03 11:39:27 +00001991 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00001992 helper = g_new0 (FuMainAuthHelper, 1);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001993 helper->auth_kind = FU_MAIN_AUTH_KIND_INSTALL;
Richard Hughesf508e762015-02-27 12:49:36 +00001994 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01001995 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
1996 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00001997 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00001998 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01001999 helper->store = as_store_new ();
Richard Hughesfe5cc902016-06-29 10:00:00 +01002000 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughesa4a2c182016-06-29 10:37:05 +01002001 helper->blob_fws = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
Richard Hughesd079b1a2015-03-06 10:09:55 +00002002 if (item != NULL)
Richard Hughesfe5cc902016-06-29 10:00:00 +01002003 g_ptr_array_add (helper->devices, g_object_ref (item->device));
Richard Hughes67ec8982015-03-03 11:39:27 +00002004 if (!fu_main_update_helper (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002005 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughes67ec8982015-03-03 11:39:27 +00002006 fu_main_helper_free (helper);
2007 return;
2008 }
2009
Richard Hughes18423292015-03-09 17:10:50 +00002010 /* is root */
2011 if (fu_main_dbus_get_uid (priv, sender) == 0) {
2012 if (!fu_main_provider_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002013 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes18423292015-03-09 17:10:50 +00002014 } else {
Richard Hughes060af612016-08-17 17:32:34 +01002015 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes18423292015-03-09 17:10:50 +00002016 }
2017 fu_main_helper_free (helper);
2018 return;
2019 }
2020
Richard Hughes67ec8982015-03-03 11:39:27 +00002021 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01002022 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00002023 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00002024 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00002025 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00002026 NULL,
2027 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
2028 NULL,
2029 fu_main_check_authorization_cb,
2030 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002031 return;
2032 }
2033
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002034 /* get a single result object from a local file */
Richard Hughescccc7752015-03-06 11:13:19 +00002035 if (g_strcmp0 (method_name, "GetDetails") == 0) {
2036 GDBusMessage *message;
2037 GUnixFDList *fd_list;
Richard Hughescccc7752015-03-06 11:13:19 +00002038 gint32 fd_handle = 0;
2039 gint fd;
Richard Hughescccc7752015-03-06 11:13:19 +00002040
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002041 /* get parameters */
Richard Hughescccc7752015-03-06 11:13:19 +00002042 g_variant_get (parameters, "(h)", &fd_handle);
2043 g_debug ("Called %s(%i)", method_name, fd_handle);
2044
2045 /* get the fd */
2046 message = g_dbus_method_invocation_get_message (invocation);
2047 fd_list = g_dbus_message_get_unix_fd_list (message);
2048 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002049 g_set_error (&error,
2050 FWUPD_ERROR,
2051 FWUPD_ERROR_INTERNAL,
2052 "invalid handle");
2053 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002054 return;
2055 }
Richard Hughes7419e962016-11-22 19:48:06 +00002056 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughescccc7752015-03-06 11:13:19 +00002057 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002058 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002059 return;
2060 }
2061
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002062 /* get details about the file */
2063 val = fu_main_get_details_from_fd (priv, fd, &error);
2064 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002065 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002066 return;
2067 }
Richard Hughes060af612016-08-17 17:32:34 +01002068 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughescccc7752015-03-06 11:13:19 +00002069 return;
2070 }
2071
Richard Hughes7289a6b2016-05-29 09:27:47 +01002072 /* get multiple result objects from a local file */
2073 if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) {
2074 GDBusMessage *message;
2075 GUnixFDList *fd_list;
2076 gint32 fd_handle = 0;
2077 gint fd;
Richard Hughes7289a6b2016-05-29 09:27:47 +01002078
2079 /* get parameters */
2080 g_variant_get (parameters, "(h)", &fd_handle);
2081 g_debug ("Called %s(%i)", method_name, fd_handle);
2082
2083 /* get the fd */
2084 message = g_dbus_method_invocation_get_message (invocation);
2085 fd_list = g_dbus_message_get_unix_fd_list (message);
2086 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002087 g_set_error (&error,
2088 FWUPD_ERROR,
2089 FWUPD_ERROR_INTERNAL,
2090 "invalid handle");
2091 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002092 return;
2093 }
Richard Hughes7419e962016-11-22 19:48:06 +00002094 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002095 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002096 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002097 return;
2098 }
2099
2100 /* get details about the file */
2101 val = fu_main_get_details_local_from_fd (priv, fd, &error);
2102 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002103 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002104 return;
2105 }
Richard Hughes060af612016-08-17 17:32:34 +01002106 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002107 return;
2108 }
2109
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002110 /* we suck */
Richard Hughes060af612016-08-17 17:32:34 +01002111 g_set_error (&error,
2112 G_DBUS_ERROR,
2113 G_DBUS_ERROR_UNKNOWN_METHOD,
2114 "no such method %s", method_name);
2115 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002116}
2117
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002118static GVariant *
2119fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
2120 const gchar *object_path, const gchar *interface_name,
2121 const gchar *property_name, GError **error,
2122 gpointer user_data)
2123{
Richard Hughes773ce982015-03-09 22:40:57 +00002124 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2125
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002126 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
2127 return g_variant_new_string (VERSION);
2128
Richard Hughes773ce982015-03-09 22:40:57 +00002129 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughes37e9d772016-04-28 14:23:24 +01002130 return g_variant_new_uint32 (priv->status);
Richard Hughes773ce982015-03-09 22:40:57 +00002131
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002132 /* return an error */
2133 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00002134 G_DBUS_ERROR,
2135 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002136 "failed to get daemon property %s",
2137 property_name);
2138 return NULL;
2139}
2140
Richard Hughesfd468842015-04-22 16:44:08 +01002141static void
2142fu_main_providers_coldplug (FuMainPrivate *priv)
2143{
Richard Hughes3f236502015-09-24 15:43:02 +01002144 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01002145
Richard Hughes3f236502015-09-24 15:43:02 +01002146 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughesf192bf02016-07-22 08:26:43 +01002147 for (guint i = 0; i < priv->providers->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01002148 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01002149 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01002150 FuProvider *provider = g_ptr_array_index (priv->providers, i);
Richard Hughes3f236502015-09-24 15:43:02 +01002151 ptask2 = as_profile_start (priv->profile,
2152 "FuMain:coldplug{%s}",
2153 fu_provider_get_name (provider));
Richard Hughesfd468842015-04-22 16:44:08 +01002154 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
2155 g_warning ("Failed to coldplug: %s", error->message);
2156 }
2157}
2158
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002159static void
2160fu_main_on_bus_acquired_cb (GDBusConnection *connection,
2161 const gchar *name,
2162 gpointer user_data)
2163{
2164 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2165 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01002166 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002167 static const GDBusInterfaceVTable interface_vtable = {
2168 fu_main_daemon_method_call,
2169 fu_main_daemon_get_property,
2170 NULL
2171 };
2172
2173 priv->connection = g_object_ref (connection);
2174 registration_id = g_dbus_connection_register_object (connection,
2175 FWUPD_DBUS_PATH,
2176 priv->introspection_daemon->interfaces[0],
2177 &interface_vtable,
2178 priv, /* user_data */
2179 NULL, /* user_data_free_func */
2180 NULL); /* GError** */
2181 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00002182
Richard Hughesfd468842015-04-22 16:44:08 +01002183 /* add devices */
2184 fu_main_providers_coldplug (priv);
2185
Richard Hughes18423292015-03-09 17:10:50 +00002186 /* connect to D-Bus directly */
2187 priv->proxy_uid =
2188 g_dbus_proxy_new_sync (priv->connection,
2189 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
2190 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2191 NULL,
2192 "org.freedesktop.DBus",
2193 "/org/freedesktop/DBus",
2194 "org.freedesktop.DBus",
2195 NULL,
2196 &error);
2197 if (priv->proxy_uid == NULL) {
2198 g_warning ("cannot connect to DBus: %s", error->message);
2199 return;
2200 }
Richard Hughes3f236502015-09-24 15:43:02 +01002201
Richard Hughes9559bbe2016-03-29 18:54:20 +01002202 /* connect to UPower */
2203 priv->proxy_upower =
2204 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
2205 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2206 NULL,
2207 "org.freedesktop.UPower",
2208 "/org/freedesktop/UPower",
2209 "org.freedesktop.UPower",
2210 NULL,
2211 &error);
2212 if (priv->proxy_upower == NULL) {
2213 g_warning ("Failed to conect UPower: %s", error->message);
2214 return;
2215 }
2216
Richard Hughes3f236502015-09-24 15:43:02 +01002217 /* dump startup profile data */
Richard Hughes2a1e75d2015-12-18 17:42:53 +00002218 if (fu_debug_is_verbose ())
2219 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002220}
2221
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002222static void
2223fu_main_on_name_acquired_cb (GDBusConnection *connection,
2224 const gchar *name,
2225 gpointer user_data)
2226{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002227 g_debug ("FuMain: acquired name: %s", name);
2228}
2229
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002230static void
2231fu_main_on_name_lost_cb (GDBusConnection *connection,
2232 const gchar *name,
2233 gpointer user_data)
2234{
2235 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2236 g_debug ("FuMain: lost name: %s", name);
2237 g_main_loop_quit (priv->loop);
2238}
2239
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002240static gboolean
2241fu_main_timed_exit_cb (gpointer user_data)
2242{
2243 GMainLoop *loop = (GMainLoop *) user_data;
2244 g_main_loop_quit (loop);
2245 return G_SOURCE_REMOVE;
2246}
2247
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002248static GDBusNodeInfo *
2249fu_main_load_introspection (const gchar *filename, GError **error)
2250{
Richard Hughes46832432015-09-11 13:43:15 +01002251 g_autoptr(GBytes) data = NULL;
2252 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002253
2254 /* lookup data */
2255 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
2256 data = g_resource_lookup_data (fu_get_resource (),
2257 path,
2258 G_RESOURCE_LOOKUP_FLAGS_NONE,
2259 error);
2260 if (data == NULL)
2261 return NULL;
2262
2263 /* build introspection from XML */
2264 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
2265}
2266
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002267static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002268fu_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002269 FuDevice *device,
2270 gpointer user_data)
2271{
2272 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002273 FuDeviceItem *item;
Richard Hughesdad1e192016-03-13 09:56:54 +00002274 AsApp *app;
2275 FuPlugin *plugin;
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002276 g_auto(GStrv) guids = NULL;
Richard Hughesdad1e192016-03-13 09:56:54 +00002277 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00002278
Richard Hughesfdf46162016-08-25 13:05:18 +01002279 /* device has no GUIDs set! */
2280 if (fu_device_get_guid_default (device) == NULL) {
2281 g_warning ("no GUIDs for device %s [%s]",
2282 fu_device_get_id (device),
2283 fu_device_get_name (device));
2284 return;
2285 }
2286
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002287 /* is this GUID blacklisted */
2288 guids = g_key_file_get_string_list (priv->config,
2289 "fwupd",
2290 "BlacklistDevices",
2291 NULL, /* length */
2292 NULL);
2293 if (guids != NULL &&
2294 g_strv_contains ((const gchar * const *) guids,
2295 fu_device_get_guid_default (device))) {
2296 g_debug ("%s is blacklisted [%s], ignoring from %s",
2297 fu_device_get_id (device),
2298 fu_device_get_guid_default (device),
2299 fu_provider_get_name (provider));
2300 return;
2301 }
2302
Richard Hughes0e883ee2015-03-18 17:22:33 +00002303 /* remove any fake device */
2304 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
Richard Hughes5d057a82015-11-24 18:09:57 +00002305 if (item != NULL) {
2306 g_debug ("already added %s by %s, ignoring same device from %s",
2307 fu_device_get_id (item->device),
Richard Hughes8e9762d2016-03-17 10:14:15 +00002308 fu_device_get_provider (item->device),
Richard Hughes5d057a82015-11-24 18:09:57 +00002309 fu_provider_get_name (provider));
2310 return;
2311 }
Richard Hughes0e883ee2015-03-18 17:22:33 +00002312
2313 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00002314 item = g_new0 (FuDeviceItem, 1);
2315 item->device = g_object_ref (device);
2316 item->provider = g_object_ref (provider);
2317 g_ptr_array_add (priv->devices, item);
Richard Hughesdad1e192016-03-13 09:56:54 +00002318
2319 /* does this match anything in the AppStream data */
Richard Hughes99147f12016-05-17 09:35:04 +01002320 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesdad1e192016-03-13 09:56:54 +00002321 if (app != NULL) {
2322 const gchar *tmp;
2323 tmp = as_app_get_metadata_item (app, FU_DEVICE_KEY_FWUPD_PLUGIN);
2324 if (tmp != NULL) {
2325 g_debug ("setting plugin: %s", tmp);
2326 fu_device_set_metadata (item->device,
2327 FU_DEVICE_KEY_FWUPD_PLUGIN,
2328 tmp);
2329 }
2330 }
2331
2332 /* run any plugins */
2333 plugin = fu_main_get_plugin_for_device (priv->plugins, device);
2334 if (plugin != NULL) {
2335 if (!fu_plugin_run_device_probe (plugin, device, &error)) {
2336 g_warning ("failed to probe %s: %s",
2337 fu_device_get_id (item->device),
2338 error->message);
2339 }
2340 }
2341
Richard Hughes422e8662016-04-28 15:05:33 +01002342 /* match the metadata at this point so clients can tell if the
2343 * device is worthy */
2344 fu_main_get_updates_item_update (priv, item);
2345
Richard Hughes8ca33782016-04-28 15:04:31 +01002346 /* notify clients */
2347 fu_main_emit_device_added (priv, item->device);
Richard Hughesd7022b52015-03-11 19:47:06 +00002348 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002349}
2350
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002351static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002352fu_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002353 FuDevice *device,
2354 gpointer user_data)
2355{
2356 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002357 FuDeviceItem *item;
2358
2359 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
2360 if (item == NULL) {
Richard Hughes5d057a82015-11-24 18:09:57 +00002361 g_debug ("no device to remove %s", fu_device_get_id (device));
Richard Hughesf508e762015-02-27 12:49:36 +00002362 return;
2363 }
Richard Hughes5d057a82015-11-24 18:09:57 +00002364
2365 /* check this came from the same provider */
2366 if (g_strcmp0 (fu_provider_get_name (provider),
2367 fu_provider_get_name (item->provider)) != 0) {
2368 g_debug ("ignoring duplicate removal from %s",
2369 fu_provider_get_name (provider));
2370 return;
2371 }
2372
Richard Hughes8ca33782016-04-28 15:04:31 +01002373 /* make the UI update */
2374 fu_main_emit_device_removed (priv, device);
Richard Hughesf508e762015-02-27 12:49:36 +00002375 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00002376 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002377}
2378
Richard Hughes773ce982015-03-09 22:40:57 +00002379static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002380fu_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00002381 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00002382 gpointer user_data)
2383{
2384 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2385 fu_main_set_status (priv, status);
2386}
2387
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002388static void
Richard Hughes876c0072016-08-17 14:51:03 +01002389fu_main_provider_percentage_changed_cb (FuProvider *provider,
2390 guint percentage,
2391 gpointer user_data)
2392{
2393 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2394 fu_main_set_percentage (priv, percentage);
2395}
2396
2397static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002398fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
2399{
2400 g_signal_connect (provider, "device-added",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002401 G_CALLBACK (fu_main_provider_device_added_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002402 priv);
2403 g_signal_connect (provider, "device-removed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002404 G_CALLBACK (fu_main_provider_device_removed_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002405 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00002406 g_signal_connect (provider, "status-changed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002407 G_CALLBACK (fu_main_provider_status_changed_cb),
Richard Hughes773ce982015-03-09 22:40:57 +00002408 priv);
Richard Hughes876c0072016-08-17 14:51:03 +01002409 g_signal_connect (provider, "percentage-changed",
2410 G_CALLBACK (fu_main_provider_percentage_changed_cb),
2411 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002412 g_ptr_array_add (priv->providers, provider);
2413}
2414
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002415int
2416main (int argc, char *argv[])
2417{
2418 FuMainPrivate *priv = NULL;
2419 gboolean immediate_exit = FALSE;
2420 gboolean ret;
2421 gboolean timed_exit = FALSE;
2422 GOptionContext *context;
2423 guint owner_id = 0;
Richard Hughes33a518a2016-07-27 15:22:53 +01002424 gint retval = 1;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002425 const GOptionEntry options[] = {
2426 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
2427 /* TRANSLATORS: exit after we've started up, used for user profiling */
2428 _("Exit after a small delay"), NULL },
2429 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
2430 /* TRANSLATORS: exit straight away, used for automatic profiling */
2431 _("Exit after the engine has loaded"), NULL },
2432 { NULL}
2433 };
Richard Hughes46832432015-09-11 13:43:15 +01002434 g_autoptr(GError) error = NULL;
2435 g_autofree gchar *config_file = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002436
2437 setlocale (LC_ALL, "");
2438
2439 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
2440 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2441 textdomain (GETTEXT_PACKAGE);
2442
2443 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01002444 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002445 context = g_option_context_new (NULL);
2446 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002447 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00002448 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002449 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
2450 ret = g_option_context_parse (context, &argc, &argv, &error);
2451 if (!ret) {
2452 g_warning ("FuMain: failed to parse command line arguments: %s",
2453 error->message);
2454 goto out;
2455 }
2456
2457 /* create new objects */
2458 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00002459 priv->status = FWUPD_STATUS_IDLE;
Richard Hughes876c0072016-08-17 14:51:03 +01002460 priv->percentage = 0;
Richard Hughesf508e762015-02-27 12:49:36 +00002461 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002462 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002463 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01002464 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01002465 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01002466 g_signal_connect (priv->store, "changed",
2467 G_CALLBACK (fu_main_store_changed_cb), priv);
2468 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
2469 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002470
Richard Hughesd0905142016-03-13 09:46:49 +00002471 /* load plugin */
2472 priv->plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
2473 g_free, (GDestroyNotify) fu_plugin_free);
2474 if (!fu_main_load_plugins (priv->plugins, &error)) {
2475 g_print ("failed to load plugins: %s\n", error->message);
2476 retval = EXIT_FAILURE;
2477 goto out;
2478 }
2479
Richard Hughes7708a0f2015-07-21 08:41:22 +01002480 /* load AppStream */
Richard Hughes65dfbfe2016-03-02 13:42:53 +00002481 as_store_add_filter (priv->store, AS_APP_KIND_FIRMWARE);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002482 if (!as_store_load (priv->store,
2483 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
2484 NULL, &error)){
2485 g_warning ("FuMain: failed to load AppStream data: %s",
2486 error->message);
2487 return FALSE;
2488 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002489
Richard Hughes804c0752015-08-04 14:53:52 +01002490 /* read config file */
Richard Hughes804c0752015-08-04 14:53:52 +01002491 config_file = g_build_filename (SYSCONFDIR, "fwupd.conf", NULL);
2492 g_debug ("Loading fallback values from %s", config_file);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002493 priv->config = g_key_file_new ();
2494 if (!g_key_file_load_from_file (priv->config, config_file,
Richard Hughes804c0752015-08-04 14:53:52 +01002495 G_KEY_FILE_NONE, &error)) {
2496 g_print ("failed to load config file %s: %s\n",
2497 config_file, error->message);
2498 retval = EXIT_FAILURE;
2499 goto out;
2500 }
2501
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002502 /* add providers */
2503 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002504 if (g_key_file_get_boolean (priv->config, "fwupd", "EnableOptionROM", NULL))
Richard Hughes804c0752015-08-04 14:53:52 +01002505 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughes5d057a82015-11-24 18:09:57 +00002506 fu_main_add_provider (priv, fu_provider_dfu_new ());
Richard Hughes25cf6ab2015-08-04 21:34:12 +01002507 fu_main_add_provider (priv, fu_provider_rpi_new ());
Richard Hughes14d17642016-08-17 12:03:03 +01002508 fu_main_add_provider (priv, fu_provider_ebitdo_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002509#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00002510 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002511#endif
2512#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002513 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002514#endif
Mario Limonciello958ead62016-05-14 00:10:25 -05002515#ifdef HAVE_DELL
2516 fu_main_add_provider (priv, fu_provider_dell_new ());
2517#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002518
Richard Hughes5d057a82015-11-24 18:09:57 +00002519 /* last as least priority */
2520 fu_main_add_provider (priv, fu_provider_usb_new ());
2521
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002522 /* load introspection from file */
2523 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
2524 &error);
2525 if (priv->introspection_daemon == NULL) {
2526 g_warning ("FuMain: failed to load daemon introspection: %s",
2527 error->message);
2528 goto out;
2529 }
2530
Richard Hughesf508e762015-02-27 12:49:36 +00002531 /* get authority */
2532 priv->authority = polkit_authority_get_sync (NULL, &error);
2533 if (priv->authority == NULL) {
2534 g_warning ("FuMain: failed to load polkit authority: %s",
2535 error->message);
2536 goto out;
2537 }
2538
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002539 /* own the object */
2540 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
2541 FWUPD_DBUS_SERVICE,
2542 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
2543 G_BUS_NAME_OWNER_FLAGS_REPLACE,
2544 fu_main_on_bus_acquired_cb,
2545 fu_main_on_name_acquired_cb,
2546 fu_main_on_name_lost_cb,
2547 priv, NULL);
2548
2549 /* Only timeout and close the mainloop if we have specified it
2550 * on the command line */
2551 if (immediate_exit)
2552 g_idle_add (fu_main_timed_exit_cb, priv->loop);
2553 else if (timed_exit)
2554 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
2555
2556 /* wait */
2557 g_info ("Daemon ready for requests");
2558 g_main_loop_run (priv->loop);
2559
2560 /* success */
2561 retval = 0;
2562out:
2563 g_option_context_free (context);
2564 if (owner_id > 0)
2565 g_bus_unown_name (owner_id);
2566 if (priv != NULL) {
2567 if (priv->loop != NULL)
2568 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00002569 if (priv->proxy_uid != NULL)
2570 g_object_unref (priv->proxy_uid);
Richard Hughes9559bbe2016-03-29 18:54:20 +01002571 if (priv->proxy_upower != NULL)
2572 g_object_unref (priv->proxy_upower);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002573 if (priv->config != NULL)
Richard Hughesfec24802016-11-04 09:30:14 +00002574 g_key_file_unref (priv->config);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002575 if (priv->connection != NULL)
2576 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00002577 if (priv->authority != NULL)
2578 g_object_unref (priv->authority);
Richard Hughes3f236502015-09-24 15:43:02 +01002579 if (priv->profile != NULL)
2580 g_object_unref (priv->profile);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002581 if (priv->store != NULL)
2582 g_object_unref (priv->store);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002583 if (priv->introspection_daemon != NULL)
2584 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes033ccba2015-09-10 14:51:28 +01002585 if (priv->store_changed_id != 0)
2586 g_source_remove (priv->store_changed_id);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002587 g_object_unref (priv->pending);
Richard Hughes804c0752015-08-04 14:53:52 +01002588 if (priv->providers != NULL)
2589 g_ptr_array_unref (priv->providers);
Richard Hughesd0905142016-03-13 09:46:49 +00002590 if (priv->plugins != NULL)
2591 g_hash_table_unref (priv->plugins);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002592 g_ptr_array_unref (priv->devices);
2593 g_free (priv);
2594 }
2595 return retval;
2596}
2597