blob: 0acbb12cc03be49bc2e07736dedba1af264c79c8 [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
Richard Hughes60f48c22015-10-08 20:25:51 +010062#ifndef PolkitAuthorizationResult_autoptr
63G_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 Hughesa4a2c182016-06-29 10:37:05 +0100471 if (helper->blob_fws > 0)
472 g_ptr_array_unref (helper->blob_fws);
Richard Hughes5d14def2015-10-07 17:43:10 +0100473 if (helper->blob_cab > 0)
474 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
552 /* The provider might have taken away update abilities */
553 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
554 !fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
555 g_set_error(error,
556 FWUPD_ERROR,
557 FWUPD_ERROR_INTERNAL,
Richard Hughes01b9a832016-08-16 17:59:32 +0100558 "Device %s does not now allow updates",
Richard Hughesfe5cc902016-06-29 10:00:00 +0100559 fu_device_get_id (device));
560 return FALSE;
561 }
562
563 /* can we only do this on AC power */
564 if (fu_device_has_flag (item->device, FU_DEVICE_FLAG_REQUIRE_AC)) {
565 if (fu_main_on_battery (helper->priv)) {
566 g_set_error_literal (error,
567 FWUPD_ERROR,
568 FWUPD_ERROR_NOT_SUPPORTED,
569 "Cannot install update "
570 "when not on AC power");
571 return FALSE;
572 }
573 }
Richard Hughesb75c92d2016-02-20 20:22:00 +0000574 }
575
Richard Hughesfe5cc902016-06-29 10:00:00 +0100576 /* run the correct providers for each device */
Richard Hughesf192bf02016-07-22 08:26:43 +0100577 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100578 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughesa4a2c182016-06-29 10:37:05 +0100579 GBytes *blob_fw = g_ptr_array_index (helper->blob_fws, i);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100580 item = fu_main_get_item_by_id (helper->priv,
581 fu_device_get_id (device));
582 plugin = fu_main_get_plugin_for_device (helper->priv->plugins,
583 item->device);
584 if (!fu_provider_update (item->provider,
585 item->device,
586 helper->blob_cab,
Richard Hughesa4a2c182016-06-29 10:37:05 +0100587 blob_fw,
Richard Hughesfe5cc902016-06-29 10:00:00 +0100588 plugin,
589 helper->flags,
590 error))
591 return FALSE;
592
593 /* make the UI update */
Richard Hughes33a518a2016-07-27 15:22:53 +0100594 fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100595 fu_main_emit_device_changed (helper->priv, item->device);
596 }
Richard Hughesa3a8f502015-11-24 12:31:59 +0000597
598 /* make the UI update */
599 fu_main_emit_changed (helper->priv);
600 return TRUE;
Richard Hughes18423292015-03-09 17:10:50 +0000601}
602
Richard Hughesf508e762015-02-27 12:49:36 +0000603static void
604fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
605{
606 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100607 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100608 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000609
610 /* get result */
611 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
612 res, &error);
613 if (auth == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +0100614 g_set_error (&error,
615 FWUPD_ERROR,
616 FWUPD_ERROR_AUTH_FAILED,
617 "could not check for auth: %s", error->message);
618 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000619 fu_main_helper_free (helper);
620 return;
621 }
622
623 /* did not auth */
624 if (!polkit_authorization_result_get_is_authorized (auth)) {
Richard Hughes060af612016-08-17 17:32:34 +0100625 g_set_error_literal (&error,
626 FWUPD_ERROR,
627 FWUPD_ERROR_AUTH_FAILED,
628 "failed to obtain auth");
629 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000630 fu_main_helper_free (helper);
631 return;
632 }
633
Richard Hughes18423292015-03-09 17:10:50 +0000634 /* we're good to go */
Richard Hughes9a410ce2016-02-28 15:58:54 +0000635 if (helper->auth_kind == FU_MAIN_AUTH_KIND_INSTALL) {
636 if (!fu_main_provider_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100637 fu_main_invocation_return_error (helper->priv,
638 helper->invocation,
639 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000640 fu_main_helper_free (helper);
641 return;
642 }
643 } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_UNLOCK) {
644 if (!fu_main_provider_unlock_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100645 fu_main_invocation_return_error (helper->priv,
646 helper->invocation,
647 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000648 fu_main_helper_free (helper);
649 return;
650 }
651 } else {
652 g_assert_not_reached ();
Richard Hughesf508e762015-02-27 12:49:36 +0000653 }
654
655 /* success */
Richard Hughes060af612016-08-17 17:32:34 +0100656 fu_main_invocation_return_value (helper->priv, helper->invocation, NULL);
Richard Hughesf508e762015-02-27 12:49:36 +0000657 fu_main_helper_free (helper);
658}
659
Richard Hughes5d14def2015-10-07 17:43:10 +0100660static gchar *
661fu_main_get_guids_from_store (AsStore *store)
662{
Richard Hughes5d14def2015-10-07 17:43:10 +0100663 AsProvide *prov;
664 GPtrArray *provides;
665 GPtrArray *apps;
666 GString *str = g_string_new ("");
Richard Hughes5d14def2015-10-07 17:43:10 +0100667
668 /* return a string with all the firmware apps in the store */
669 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +0100670 for (guint i = 0; i < apps->len; i++) {
671 AsApp *app = AS_APP (g_ptr_array_index (apps, i));
Richard Hughes5d14def2015-10-07 17:43:10 +0100672 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100673 for (guint j = 0; j < provides->len; j++) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100674 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
675 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
676 continue;
677 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
678 }
679 }
680 if (str->len == 0)
681 return NULL;
682 g_string_truncate (str, str->len - 1);
683 return g_string_free (str, FALSE);
684}
685
Richard Hughesdf7950b2016-01-31 10:18:40 +0000686static void
687fu_main_vendor_quirk_release_version (AsApp *app)
688{
689 AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET;
690 GPtrArray *releases;
Richard Hughesdf7950b2016-01-31 10:18:40 +0000691
692 /* no quirk required */
Richard Hughes65dfbfe2016-03-02 13:42:53 +0000693 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
Richard Hughesdf7950b2016-01-31 10:18:40 +0000694 return;
695
Richard Hughesf192bf02016-07-22 08:26:43 +0100696 for (guint i = 0; quirk_table[i].identifier != NULL; i++) {
Mario Limonciello918f3932016-02-03 12:47:23 -0600697 if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier))
698 flags = quirk_table[i].flags;
Richard Hughesf192bf02016-07-22 08:26:43 +0100699 }
Richard Hughesdf7950b2016-01-31 10:18:40 +0000700
701 /* fix each release */
702 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100703 for (guint i = 0; i < releases->len; i++) {
Richard Hughesdf7950b2016-01-31 10:18:40 +0000704 AsRelease *rel;
705 const gchar *version;
706 guint64 ver_uint32;
707 g_autofree gchar *version_new = NULL;
708
709 rel = g_ptr_array_index (releases, i);
710 version = as_release_get_version (rel);
711 if (version == NULL)
712 continue;
713 if (g_strstr_len (version, -1, ".") != NULL)
714 continue;
715
716 /* metainfo files use hex and the LVFS uses decimal */
717 if (g_str_has_prefix (version, "0x")) {
718 ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16);
719 } else {
720 ver_uint32 = g_ascii_strtoull (version, NULL, 10);
721 }
722 if (ver_uint32 == 0)
723 continue;
724
725 /* convert to dotted decimal */
Richard Hughes33a518a2016-07-27 15:22:53 +0100726 version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags);
Richard Hughesdf7950b2016-01-31 10:18:40 +0000727 as_release_set_version (rel, version_new);
728 }
729}
730
Richard Hughes99147f12016-05-17 09:35:04 +0100731static AsApp *
732fu_main_store_get_app_by_guids (AsStore *store, FuDevice *device)
733{
Richard Hughesf192bf02016-07-22 08:26:43 +0100734 GPtrArray *guids = fu_device_get_guids (device);
735 for (guint i = 0; i < guids->len; i++) {
Richard Hughes99147f12016-05-17 09:35:04 +0100736 AsApp *app = NULL;
737 app = as_store_get_app_by_provide (store,
738 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
739 g_ptr_array_index (guids, i));
740 if (app != NULL)
741 return app;
742 }
743 return NULL;
744}
745
Richard Hughes01b9a832016-08-16 17:59:32 +0100746static AsScreenshot *
747_as_app_get_screenshot_default (AsApp *app)
748{
749 GPtrArray *array = as_app_get_screenshots (app);
750 if (array->len == 0)
751 return NULL;
752 return g_ptr_array_index (array, 0);
753}
754
Richard Hughes67ec8982015-03-03 11:39:27 +0000755static gboolean
Richard Hughes3ab17e62016-07-04 12:37:22 +0100756fu_main_update_helper_for_device (FuMainAuthHelper *helper,
757 FuDevice *device,
758 GError **error)
Richard Hughes67ec8982015-03-03 11:39:27 +0000759{
Richard Hughes5d14def2015-10-07 17:43:10 +0100760 AsApp *app;
761 AsChecksum *csum_tmp;
762 AsRelease *rel;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100763 GBytes *blob_fw;
Richard Hughesdef31752015-03-04 19:26:54 +0000764 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000765 const gchar *version;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100766 gboolean is_downgrade;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100767 gint vercmp;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100768
769 /* find from guid */
770 app = fu_main_store_get_app_by_guids (helper->store, device);
771 if (app == NULL) {
772 g_autofree gchar *guid = NULL;
773 guid = fu_main_get_guids_from_store (helper->store);
774 g_set_error (error,
775 FWUPD_ERROR,
776 FWUPD_ERROR_INVALID_FILE,
777 "firmware is not for this hw: required %s got %s",
778 fu_device_get_guid_default (device), guid);
779 return FALSE;
780 }
781
782 /* parse the DriverVer */
783 rel = as_app_get_release_default (app);
784 if (rel == NULL) {
785 g_set_error_literal (error,
786 FWUPD_ERROR,
787 FWUPD_ERROR_INVALID_FILE,
788 "no releases in the firmware component");
789 return FALSE;
790 }
791
Richard Hughes01b9a832016-08-16 17:59:32 +0100792 /* no update abilities */
793 if (!fu_device_has_flag (device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
794 !fu_device_has_flag (device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +0100795 g_set_error (error,
796 FWUPD_ERROR,
797 FWUPD_ERROR_INTERNAL,
798 "Device %s does not currently allow updates",
799 fu_device_get_id (device));
800 return FALSE;
801 }
802
803 /* not in bootloader mode */
804 if (fu_device_has_flag (device, FU_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
Richard Hughes01b9a832016-08-16 17:59:32 +0100805 const gchar *caption = NULL;
806 AsScreenshot *ss = _as_app_get_screenshot_default (app);
807 if (ss != NULL)
808 caption = as_screenshot_get_caption (ss, NULL);
809 if (caption != NULL) {
810 g_set_error (error,
811 FWUPD_ERROR,
812 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +0100813 "Device %s needs to manually be put in update mode: %s",
814 fu_device_get_name (device), caption);
Richard Hughes01b9a832016-08-16 17:59:32 +0100815 } else {
816 g_set_error (error,
817 FWUPD_ERROR,
818 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +0100819 "Device %s needs to manually be put in update mode",
820 fu_device_get_name (device));
Richard Hughes01b9a832016-08-16 17:59:32 +0100821 }
822 return FALSE;
823 }
824
Richard Hughes3ab17e62016-07-04 12:37:22 +0100825 /* get the blob */
826 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
827 tmp = as_checksum_get_filename (csum_tmp);
828 if (tmp == NULL) {
829 g_set_error_literal (error,
830 FWUPD_ERROR,
831 FWUPD_ERROR_INVALID_FILE,
832 "no checksum filename");
833 return FALSE;
834 }
835
836 /* not all devices have to use the same blob */
837 blob_fw = as_release_get_blob (rel, tmp);
838 if (blob_fw == NULL) {
839 g_set_error_literal (error,
840 FWUPD_ERROR,
841 FWUPD_ERROR_READ,
842 "failed to get firmware blob");
843 return FALSE;
844 }
845
846 /* possibly convert the version from 0x to dotted */
847 fu_main_vendor_quirk_release_version (app);
848
849 version = as_release_get_version (rel);
850 fu_device_set_update_version (device, version);
851
852 /* compare to the lowest supported version, if it exists */
853 tmp = fu_device_get_version_lowest (device);
854 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
855 g_set_error (error,
856 FWUPD_ERROR,
857 FWUPD_ERROR_VERSION_NEWER,
858 "Specified firmware is older than the minimum "
859 "required version '%s < %s'", tmp, version);
860 return FALSE;
861 }
862
863 /* check the device is locked */
864 if (fu_device_has_flag (device, FU_DEVICE_FLAG_LOCKED)) {
865 g_set_error (error,
866 FWUPD_ERROR,
867 FWUPD_ERROR_INTERNAL,
868 "Device %s is locked",
869 fu_device_get_id (device));
870 return FALSE;
871 }
872
873 /* compare the versions of what we have installed */
874 tmp = fu_device_get_version (device);
875 if (tmp == NULL) {
876 g_set_error (error,
877 FWUPD_ERROR,
878 FWUPD_ERROR_INTERNAL,
879 "Device %s does not yet have a current version",
880 fu_device_get_id (device));
881 return FALSE;
882 }
883 vercmp = as_utils_vercmp (tmp, version);
884 if (vercmp == 0 && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
885 g_set_error (error,
886 FWUPD_ERROR,
887 FWUPD_ERROR_VERSION_SAME,
888 "Specified firmware is already installed '%s'",
889 tmp);
890 return FALSE;
891 }
892 is_downgrade = vercmp > 0;
893 if (is_downgrade && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
894 g_set_error (error,
895 FWUPD_ERROR,
896 FWUPD_ERROR_VERSION_NEWER,
897 "Specified firmware is older than installed '%s < %s'",
898 tmp, version);
899 return FALSE;
900 }
901
902 /* if any downgrade, we want the global to be true */
903 if (is_downgrade)
904 helper->is_downgrade = is_downgrade;
905
906 /* verify */
907 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
908 return FALSE;
909
910 /* success */
911 g_ptr_array_add (helper->blob_fws, g_bytes_ref (blob_fw));
912 return TRUE;
913}
914
915static gboolean
916fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
917{
Richard Hughesc8646af2016-07-04 13:04:27 +0100918 g_autoptr(GError) error_first = NULL;
Richard Hughes67ec8982015-03-03 11:39:27 +0000919
Richard Hughes5d14def2015-10-07 17:43:10 +0100920 /* load store file which also decompresses firmware */
921 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
922 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000923 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000924
Richard Hughesbd405282016-07-04 13:00:54 +0100925 /* we've specified a specific device; failure is critical */
926 if (helper->devices->len > 0) {
Richard Hughesf192bf02016-07-22 08:26:43 +0100927 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100928 FuDevice *device = g_ptr_array_index (helper->devices, i);
929 if (!fu_main_update_helper_for_device (helper, device, error))
930 return FALSE;
Richard Hughes5d14def2015-10-07 17:43:10 +0100931 }
Richard Hughesbd405282016-07-04 13:00:54 +0100932 return TRUE;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100933 }
Richard Hughes5d14def2015-10-07 17:43:10 +0100934
Richard Hughesbd405282016-07-04 13:00:54 +0100935 /* if we've not chosen a device, try and find anything in the
936 * cabinet 'store' that matches any installed device and is updatable */
Richard Hughesf192bf02016-07-22 08:26:43 +0100937 for (guint i = 0; i < helper->priv->devices->len; i++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100938 AsApp *app;
939 FuDeviceItem *item;
940 g_autoptr(GError) error_local = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +0100941
Richard Hughesbd405282016-07-04 13:00:54 +0100942 /* guid found */
943 item = g_ptr_array_index (helper->priv->devices, i);
944 app = fu_main_store_get_app_by_guids (helper->store, item->device);
945 if (app == NULL)
946 continue;
947
948 /* try this device, error not fatal */
949 if (!fu_main_update_helper_for_device (helper,
950 item->device,
951 &error_local)) {
952 g_debug ("failed to add %s: %s",
953 fu_device_get_id (item->device),
954 error_local->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100955
956 /* save this for later */
957 if (error_first == NULL)
958 error_first = g_error_copy (error_local);
Richard Hughesbd405282016-07-04 13:00:54 +0100959 continue;
960 }
961
962 /* success */
963 g_ptr_array_add (helper->devices, g_object_ref (item->device));
964 }
Richard Hughes346ea882016-07-04 12:31:06 +0100965 if (helper->devices->len == 0) {
Richard Hughesc8646af2016-07-04 13:04:27 +0100966 if (error_first != NULL) {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100967 g_set_error_literal (error,
968 FWUPD_ERROR,
969 FWUPD_ERROR_INVALID_FILE,
970 error_first->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100971 } else {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100972 g_autofree gchar *guid = NULL;
973 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesc8646af2016-07-04 13:04:27 +0100974 g_set_error (error,
975 FWUPD_ERROR,
976 FWUPD_ERROR_INVALID_FILE,
977 "no attached hardware matched %s",
978 guid);
979 }
Richard Hughes346ea882016-07-04 12:31:06 +0100980 return FALSE;
981 }
982
Richard Hughesa4a2c182016-06-29 10:37:05 +0100983 /* sanity check */
984 if (helper->devices->len != helper->blob_fws->len) {
985 g_set_error (error,
986 FWUPD_ERROR,
987 FWUPD_ERROR_INTERNAL,
Richard Hughes33a518a2016-07-27 15:22:53 +0100988 "not enough firmware blobs (%u) for devices (%u)",
Richard Hughesa4a2c182016-06-29 10:37:05 +0100989 helper->blob_fws->len,
990 helper->devices->len);
991 return FALSE;
992 }
993
Richard Hughes67ec8982015-03-03 11:39:27 +0000994 return TRUE;
995}
996
Richard Hughes18423292015-03-09 17:10:50 +0000997static guint
998fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
999{
1000 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +01001001 g_autoptr(GError) error = NULL;
1002 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +00001003
1004 if (priv->proxy_uid == NULL)
1005 return G_MAXUINT;
1006 value = g_dbus_proxy_call_sync (priv->proxy_uid,
1007 "GetConnectionUnixUser",
1008 g_variant_new ("(s)", sender),
1009 G_DBUS_CALL_FLAGS_NONE,
1010 -1,
1011 NULL,
1012 &error);
1013 if (value == NULL) {
1014 g_warning ("Failed to get uid for %s: %s",
1015 sender, error->message);
1016 return G_MAXUINT;
1017 }
1018 g_variant_get (value, "(u)", &uid);
1019 return uid;
1020}
1021
Richard Hughes0e883ee2015-03-18 17:22:33 +00001022static FuDeviceItem *
1023fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
1024{
1025 FuDevice *dev;
1026 FuProvider *provider;
1027 FuDeviceItem *item = NULL;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001028 FwupdUpdateState update_state;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001029 const gchar *tmp;
Richard Hughes46832432015-09-11 13:43:15 +01001030 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001031
1032 /* not a wildcard */
1033 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1034 item = fu_main_get_item_by_id (priv, id);
1035 if (item == NULL) {
1036 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001037 FWUPD_ERROR,
1038 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001039 "no suitable device found for %s", id);
1040 }
1041 return item;
1042 }
1043
1044 /* allow '*' for any */
1045 devices = fu_pending_get_devices (priv->pending, error);
1046 if (devices == NULL)
1047 return NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01001048 for (guint i = 0; i < devices->len; i++) {
Richard Hughes0e883ee2015-03-18 17:22:33 +00001049 dev = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +00001050 update_state = fu_device_get_update_state (dev);
1051 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001052 continue;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001053 if (update_state == FWUPD_UPDATE_STATE_PENDING)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001054 continue;
1055
1056 /* if the device is not still connected, fake a FuDeviceItem */
1057 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
1058 if (item == NULL) {
Richard Hughes8e9762d2016-03-17 10:14:15 +00001059 tmp = fu_device_get_provider (dev);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001060 provider = fu_main_get_provider_by_name (priv, tmp);
1061 if (provider == NULL) {
1062 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001063 FWUPD_ERROR,
1064 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001065 "no provider %s found", tmp);
1066 }
1067 item = g_new0 (FuDeviceItem, 1);
1068 item->device = g_object_ref (dev);
1069 item->provider = g_object_ref (provider);
1070 g_ptr_array_add (priv->devices, item);
1071
1072 /* FIXME: just a boolean on FuDeviceItem? */
1073 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
1074 }
1075 break;
1076 }
1077
1078 /* no device found */
1079 if (item == NULL) {
1080 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001081 FWUPD_ERROR,
1082 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001083 "no suitable devices found");
1084 }
1085 return item;
1086}
1087
Richard Hughes63bbbf52015-04-14 16:12:16 +01001088static const gchar *
1089fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
1090{
Richard Hughesfe5cc902016-06-29 10:00:00 +01001091 gboolean all_removable = TRUE;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001092 gboolean is_trusted;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001093
1094 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +01001095 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001096
Richard Hughesfe5cc902016-06-29 10:00:00 +01001097 /* any non-removable means false */
Richard Hughesf192bf02016-07-22 08:26:43 +01001098 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001099 FuDevice *device = g_ptr_array_index (helper->devices, i);
1100 if (fu_device_has_flag (device, FU_DEVICE_FLAG_INTERNAL)) {
1101 all_removable = FALSE;
1102 break;
1103 }
1104 }
1105
Richard Hughes63bbbf52015-04-14 16:12:16 +01001106 /* relax authentication checks for removable devices */
Richard Hughesfe5cc902016-06-29 10:00:00 +01001107 if (all_removable) {
Richard Hughesdb468ee2016-06-29 10:10:47 +01001108 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001109 return "org.freedesktop.fwupd.downgrade-hotplug";
1110 if (is_trusted)
1111 return "org.freedesktop.fwupd.update-hotplug-trusted";
1112 return "org.freedesktop.fwupd.update-hotplug";
1113 }
1114
1115 /* internal device */
Richard Hughesdb468ee2016-06-29 10:10:47 +01001116 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001117 return "org.freedesktop.fwupd.downgrade-internal";
1118 if (is_trusted)
1119 return "org.freedesktop.fwupd.update-internal-trusted";
1120 return "org.freedesktop.fwupd.update-internal";
1121}
1122
Richard Hughesae0efdc2015-06-24 16:18:29 +01001123static gboolean
Mario Limonciello3ed54472015-07-23 13:19:39 -05001124fu_main_daemon_update_metadata (FuMainPrivate *priv, gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +01001125{
Richard Hughesf2fca012015-10-30 08:44:44 +00001126 const guint8 *data;
Richard Hughesf2fca012015-10-30 08:44:44 +00001127 gsize size;
Richard Hughes727664f2015-10-27 09:56:04 +00001128 GPtrArray *apps;
Richard Hughesbb840ce2015-10-30 08:47:24 +00001129 g_autofree gchar *xml = NULL;
Richard Hughes727664f2015-10-27 09:56:04 +00001130 g_autoptr(AsStore) store = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001131 g_autoptr(GBytes) bytes = NULL;
1132 g_autoptr(GBytes) bytes_raw = NULL;
1133 g_autoptr(GBytes) bytes_sig = NULL;
1134 g_autoptr(FuKeyring) kr = NULL;
1135 g_autoptr(GConverter) converter = NULL;
1136 g_autoptr(GFile) file = NULL;
1137 g_autoptr(GInputStream) stream_buf = NULL;
1138 g_autoptr(GInputStream) stream_fd = NULL;
1139 g_autoptr(GInputStream) stream = NULL;
1140 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001141
Richard Hughesae0efdc2015-06-24 16:18:29 +01001142 /* read the entire file into memory */
1143 stream_fd = g_unix_input_stream_new (fd, TRUE);
1144 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1145 if (bytes_raw == NULL)
1146 return FALSE;
1147 stream_buf = g_memory_input_stream_new ();
1148 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
1149
1150 /* peek the file type and get data */
Richard Hughesf2fca012015-10-30 08:44:44 +00001151 data = g_bytes_get_data (bytes_raw, &size);
1152 if (size < 2) {
1153 g_set_error_literal (error,
1154 FWUPD_ERROR,
1155 FWUPD_ERROR_INVALID_FILE,
1156 "file is too small");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001157 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001158 }
1159 if (data[0] == 0x1f && data[1] == 0x8b) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001160 g_debug ("using GZip decompressor for data");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001161 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
1162 stream = g_converter_input_stream_new (stream_buf, converter);
1163 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
1164 if (bytes == NULL)
1165 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001166 } else if (data[0] == '<' && data[1] == '?') {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001167 g_debug ("using no decompressor for data");
1168 bytes = g_bytes_ref (bytes_raw);
1169 } else {
1170 g_set_error (error,
1171 FWUPD_ERROR,
1172 FWUPD_ERROR_INVALID_FILE,
1173 "file type '0x%02x,0x%02x' not supported",
Richard Hughesf2fca012015-10-30 08:44:44 +00001174 data[0], data[1]);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001175 return FALSE;
1176 }
1177
1178 /* read signature */
1179 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1180 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1181 if (bytes_sig == NULL)
1182 return FALSE;
1183
1184 /* verify file */
1185 kr = fu_keyring_new ();
1186 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
1187 return FALSE;
1188 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
1189 return FALSE;
1190
Richard Hughes727664f2015-10-27 09:56:04 +00001191 /* load the store locally until we know it is valid */
1192 store = as_store_new ();
Richard Hughesbb840ce2015-10-30 08:47:24 +00001193 data = g_bytes_get_data (bytes, &size);
1194 xml = g_strndup ((const gchar *) data, size);
1195 if (!as_store_from_xml (store, xml, NULL, error))
Richard Hughesae0efdc2015-06-24 16:18:29 +01001196 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001197
Richard Hughes727664f2015-10-27 09:56:04 +00001198 /* add the new application from the store */
1199 as_store_remove_all (priv->store);
1200 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +01001201 for (guint i = 0; i < apps->len; i++) {
Richard Hughes727664f2015-10-27 09:56:04 +00001202 AsApp *app = g_ptr_array_index (apps, i);
1203 as_store_add_app (priv->store, app);
1204 }
1205
Richard Hughesae0efdc2015-06-24 16:18:29 +01001206 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +01001207 as_store_set_api_version (priv->store, 0.9);
1208 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
1209 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +01001210 AS_NODE_TO_XML_FLAG_ADD_HEADER |
1211 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
1212 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
1213 NULL, error)) {
1214 return FALSE;
1215 }
1216
1217 return TRUE;
1218}
1219
Richard Hughes033ccba2015-09-10 14:51:28 +01001220static gboolean
1221fu_main_store_delay_cb (gpointer user_data)
1222{
Richard Hughes033ccba2015-09-10 14:51:28 +01001223 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf192bf02016-07-22 08:26:43 +01001224 GPtrArray *apps;
Richard Hughes033ccba2015-09-10 14:51:28 +01001225
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001226 /* print what we've got */
Richard Hughes033ccba2015-09-10 14:51:28 +01001227 apps = as_store_get_apps (priv->store);
Richard Hughes0a36f442016-01-06 14:57:20 +00001228 if (apps->len == 0) {
1229 g_debug ("no devices in store");
1230 } else {
1231 g_debug ("devices now in store:");
Richard Hughesf192bf02016-07-22 08:26:43 +01001232 for (guint i = 0; i < apps->len; i++) {
1233 AsApp *app = g_ptr_array_index (apps, i);
Richard Hughes33a518a2016-07-27 15:22:53 +01001234 g_debug ("%u\t%s\t%s", i + 1,
Richard Hughes0a36f442016-01-06 14:57:20 +00001235 as_app_get_id (app),
1236 as_app_get_name (app, NULL));
1237 }
Richard Hughes033ccba2015-09-10 14:51:28 +01001238 }
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001239
1240 /* are any devices now supported? */
Richard Hughesf192bf02016-07-22 08:26:43 +01001241 for (guint i = 0; i < priv->devices->len; i++) {
1242 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001243 if (fu_main_get_updates_item_update (priv, item))
1244 fu_main_emit_device_changed (priv, item->device);
1245 }
1246
Richard Hughes033ccba2015-09-10 14:51:28 +01001247 priv->store_changed_id = 0;
1248 return G_SOURCE_REMOVE;
1249}
1250
Richard Hughes033ccba2015-09-10 14:51:28 +01001251static void
1252fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
1253{
1254 if (priv->store_changed_id != 0)
1255 return;
1256 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
1257}
1258
Richard Hughes654f6b82016-04-25 12:29:48 +01001259static gboolean
1260fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item)
1261{
1262 AsApp *app;
1263 AsChecksum *csum;
1264 AsRelease *rel;
1265 GPtrArray *releases;
1266 const gchar *tmp;
1267 const gchar *version;
Richard Hughes654f6b82016-04-25 12:29:48 +01001268 g_autoptr(GPtrArray) updates_list = NULL;
1269
1270 /* get device version */
1271 version = fu_device_get_version (item->device);
1272 if (version == NULL)
1273 return FALSE;
1274
Richard Hughes99147f12016-05-17 09:35:04 +01001275 /* match the GUIDs in the XML */
1276 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughes654f6b82016-04-25 12:29:48 +01001277 if (app == NULL)
1278 return FALSE;
1279
1280 /* possibly convert the version from 0x to dotted */
1281 fu_main_vendor_quirk_release_version (app);
1282
1283 /* get latest release */
1284 rel = as_app_get_release_default (app);
1285 if (rel == NULL) {
Richard Hughes14d17642016-08-17 12:03:03 +01001286 g_debug ("%s [%s] has no firmware update metadata",
1287 fu_device_get_id (item->device),
1288 fu_device_get_name (item->device));
Richard Hughes654f6b82016-04-25 12:29:48 +01001289 return FALSE;
1290 }
1291
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001292 /* supported in metadata */
1293 fwupd_result_add_device_flag (FWUPD_RESULT (item->device),
1294 FU_DEVICE_FLAG_SUPPORTED);
1295
Richard Hughes654f6b82016-04-25 12:29:48 +01001296 /* check if actually newer than what we have installed */
1297 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0) {
1298 g_debug ("%s has no firmware updates",
1299 fu_device_get_id (item->device));
1300 return FALSE;
1301 }
1302
Richard Hughes6561c8d2016-05-17 16:24:09 +01001303 /* only show devices that can be updated */
1304 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
1305 !fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +01001306 g_debug ("ignoring %s [%s] as not updatable live or offline",
1307 fu_device_get_id (item->device),
1308 fu_device_get_name (item->device));
Richard Hughes6561c8d2016-05-17 16:24:09 +01001309 return FALSE;
1310 }
1311
Richard Hughes654f6b82016-04-25 12:29:48 +01001312 /* can we only do this on AC power */
1313 if (fu_device_has_flag (item->device, FU_DEVICE_FLAG_REQUIRE_AC) &&
1314 fu_main_on_battery (priv)) {
1315 g_debug ("ignoring update for %s as not on AC power",
1316 fu_device_get_id (item->device));
1317 return FALSE;
1318 }
1319
1320 /* add application metadata */
1321 fu_device_set_update_id (item->device, as_app_get_id (app));
1322 tmp = as_app_get_developer_name (app, NULL);
1323 if (tmp != NULL)
1324 fu_device_set_update_vendor (item->device, tmp);
1325 tmp = as_app_get_name (app, NULL);
1326 if (tmp != NULL)
1327 fu_device_set_update_name (item->device, tmp);
1328 tmp = as_app_get_comment (app, NULL);
1329 if (tmp != NULL)
1330 fu_device_set_update_summary (item->device, tmp);
1331 tmp = as_app_get_description (app, NULL);
1332 if (tmp != NULL)
1333 fu_device_set_description (item->device, tmp);
1334 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
1335 if (tmp != NULL)
1336 fu_device_set_update_homepage (item->device, tmp);
1337 tmp = as_app_get_project_license (app);
1338 if (tmp != NULL)
1339 fu_device_set_update_license (item->device, tmp);
1340
1341 /* add release information */
1342 tmp = as_release_get_version (rel);
1343 if (tmp != NULL)
1344 fu_device_set_update_version (item->device, tmp);
1345 csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTAINER);
1346 if (csum != NULL) {
1347 fu_device_set_update_checksum (item->device,
1348 as_checksum_get_value (csum));
1349 }
1350 tmp = as_release_get_location_default (rel);
1351 if (tmp != NULL)
1352 fu_device_set_update_uri (item->device, tmp);
1353
1354 /* get the list of releases newer than the one installed */
1355 updates_list = g_ptr_array_new ();
1356 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001357 for (guint i = 0; i < releases->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001358 rel = g_ptr_array_index (releases, i);
Richard Hughesae874382016-08-17 14:23:05 +01001359 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0)
Richard Hughes654f6b82016-04-25 12:29:48 +01001360 continue;
1361 tmp = as_release_get_description (rel, NULL);
1362 if (tmp == NULL)
1363 continue;
1364 g_ptr_array_add (updates_list, rel);
1365 }
1366
1367 /* no prefix on each release */
1368 if (updates_list->len == 1) {
1369 rel = g_ptr_array_index (updates_list, 0);
1370 fu_device_set_update_description (item->device,
1371 as_release_get_description (rel, NULL));
1372 } else {
1373 g_autoptr(GString) update_desc = NULL;
1374 update_desc = g_string_new ("");
1375
1376 /* get the descriptions with a version prefix */
Richard Hughesf192bf02016-07-22 08:26:43 +01001377 for (guint i = 0; i < updates_list->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001378 rel = g_ptr_array_index (updates_list, i);
1379 g_string_append_printf (update_desc,
1380 "<p>%s:</p>%s",
1381 as_release_get_version (rel),
1382 as_release_get_description (rel, NULL));
1383 }
1384 if (update_desc->len > 0)
1385 fu_device_set_update_description (item->device, update_desc->str);
1386 }
1387
1388 /* success */
1389 return TRUE;
1390}
1391
Richard Hughesf192bf02016-07-22 08:26:43 +01001392/* find any updates using the AppStream metadata */
Richard Hughes7708a0f2015-07-21 08:41:22 +01001393static GPtrArray *
1394fu_main_get_updates (FuMainPrivate *priv, GError **error)
1395{
Richard Hughesf192bf02016-07-22 08:26:43 +01001396 GPtrArray *updates = g_ptr_array_new ();
1397 for (guint i = 0; i < priv->devices->len; i++) {
1398 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes654f6b82016-04-25 12:29:48 +01001399 if (fu_main_get_updates_item_update (priv, item))
1400 g_ptr_array_add (updates, item);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001401 }
Richard Hughes7708a0f2015-07-21 08:41:22 +01001402 return updates;
1403}
1404
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001405static AsStore *
1406fu_main_get_store_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1407{
1408 g_autoptr(AsStore) store = NULL;
1409 g_autoptr(GBytes) blob_cab = NULL;
1410 g_autoptr(GError) error_local = NULL;
1411 g_autoptr(GInputStream) stream = NULL;
1412
1413 /* read the entire fd to a data blob */
1414 stream = g_unix_input_stream_new (fd, TRUE);
1415 blob_cab = g_input_stream_read_bytes (stream,
1416 FU_MAIN_FIRMWARE_SIZE_MAX,
1417 NULL, &error_local);
1418 if (blob_cab == NULL){
1419 g_set_error_literal (error,
1420 FWUPD_ERROR,
1421 FWUPD_ERROR_INVALID_FILE,
1422 error_local->message);
1423 return NULL;
1424 }
1425
1426 /* load file */
1427 store = as_store_new ();
1428 if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) {
1429 g_set_error_literal (error,
1430 FWUPD_ERROR,
1431 FWUPD_ERROR_INVALID_FILE,
1432 error_local->message);
1433 return NULL;
1434 }
1435 return g_steal_pointer (&store);
1436}
1437
1438static FwupdResult *
1439fu_main_get_result_from_app (FuMainPrivate *priv, AsApp *app, GError **error)
1440{
1441 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
1442 AsRelease *rel;
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001443 AsChecksum * csum_tmp;
1444 const gchar *fn;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001445 GPtrArray *provides;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001446 g_autoptr(FwupdResult) res = NULL;
1447
1448 res = fwupd_result_new ();
1449 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001450 for (guint i = 0; i < provides->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001451 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1452 FuDeviceItem *item;
1453 const gchar *guid;
1454
1455 /* not firmware */
1456 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
1457 continue;
1458
1459 /* is a online or offline update appropriate */
1460 guid = as_provide_get_value (prov);
1461 if (guid == NULL)
1462 continue;
1463 item = fu_main_get_item_by_guid (priv, guid);
1464 if (item != NULL) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001465 fwupd_result_set_device_flags (res, fu_device_get_flags (item->device));
1466 fwupd_result_set_device_id (res, fu_device_get_id (item->device));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001467 }
1468
1469 /* add GUID */
1470 fwupd_result_add_guid (res, guid);
1471 }
1472 if (fwupd_result_get_guids(res)->len == 0) {
1473 g_set_error_literal (error,
1474 FWUPD_ERROR,
1475 FWUPD_ERROR_INTERNAL,
1476 "component has no GUIDs");
1477 return NULL;
1478 }
1479
1480 /* verify trust */
1481 rel = as_app_get_release_default (app);
1482 if (!fu_main_get_release_trust_flags (rel, &trust_flags, error))
1483 return NULL;
1484
1485 /* possibly convert the version from 0x to dotted */
1486 fu_main_vendor_quirk_release_version (app);
1487
1488 /* create a result with all the metadata in */
1489 fwupd_result_set_device_description (res, as_app_get_description (app, NULL));
Richard Hughes4921bd92016-06-27 11:17:43 +01001490 fwupd_result_set_update_id (res, as_app_get_id (app));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001491 fwupd_result_set_update_description (res, as_release_get_description (rel, NULL));
1492 fwupd_result_set_update_homepage (res, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
1493 fwupd_result_set_update_license (res, as_app_get_project_license (app));
1494 fwupd_result_set_update_name (res, as_app_get_name (app, NULL));
1495 fwupd_result_set_update_size (res, as_release_get_size (rel, AS_SIZE_KIND_INSTALLED));
1496 fwupd_result_set_update_summary (res, as_app_get_comment (app, NULL));
1497 fwupd_result_set_update_trust_flags (res, trust_flags);
1498 fwupd_result_set_update_vendor (res, as_app_get_developer_name (app, NULL));
1499 fwupd_result_set_update_version (res, as_release_get_version (rel));
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001500 csum_tmp = as_release_get_checksum_by_target (rel,
1501 AS_CHECKSUM_TARGET_CONTENT);
1502 fn = as_checksum_get_filename (csum_tmp);
1503 if (fn != NULL)
1504 fwupd_result_set_update_filename (res, fn);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001505 return g_steal_pointer (&res);
1506}
1507
1508static GVariant *
1509fu_main_get_details_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1510{
1511 AsApp *app = NULL;
1512 GPtrArray *apps;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001513 g_autoptr(AsStore) store = NULL;
1514 g_autoptr(FwupdResult) res = NULL;
1515
1516 store = fu_main_get_store_from_fd (priv, fd, error);
1517 if (store == NULL)
1518 return NULL;
1519
1520 /* get all apps */
1521 apps = as_store_get_apps (store);
1522 if (apps->len == 0) {
1523 g_set_error_literal (error,
1524 FWUPD_ERROR,
1525 FWUPD_ERROR_INVALID_FILE,
1526 "no components");
1527 return NULL;
1528 }
1529 if (apps->len > 1) {
1530 /* we've got a .cab file with multiple components,
1531 * so try to find the first thing that's installed */
Richard Hughesf192bf02016-07-22 08:26:43 +01001532 for (guint i = 0; i < priv->devices->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001533 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
1534 app = fu_main_store_get_app_by_guids (store, item->device);
1535 if (app != NULL)
1536 break;
1537 }
1538 }
1539
1540 /* well, we've tried our best, just show the first entry */
1541 if (app == NULL)
1542 app = AS_APP (g_ptr_array_index (apps, 0));
1543
1544 /* create a result with all the metadata in */
1545 res = fu_main_get_result_from_app (priv, app, error);
1546 if (res == NULL)
1547 return NULL;
1548 return fwupd_result_to_data (res, "(a{sv})");
1549}
1550
Richard Hughes7289a6b2016-05-29 09:27:47 +01001551static GVariant *
1552fu_main_get_details_local_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1553{
1554 GPtrArray *apps;
1555 GVariantBuilder builder;
Richard Hughes7289a6b2016-05-29 09:27:47 +01001556 g_autoptr(AsStore) store = NULL;
1557
1558 store = fu_main_get_store_from_fd (priv, fd, error);
1559 if (store == NULL)
1560 return NULL;
1561
1562 /* get all apps */
1563 apps = as_store_get_apps (store);
1564 if (apps->len == 0) {
1565 g_set_error_literal (error,
1566 FWUPD_ERROR,
1567 FWUPD_ERROR_INVALID_FILE,
1568 "no components");
1569 return NULL;
1570 }
1571
1572 /* create results with all the metadata in */
1573 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +01001574 for (guint i = 0; i < apps->len; i++) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001575 g_autoptr(FwupdResult) res = NULL;
1576 AsApp *app = g_ptr_array_index (apps, i);
1577 GVariant *tmp;
1578 res = fu_main_get_result_from_app (priv, app, error);
1579 if (res == NULL)
1580 return NULL;
1581 tmp = fwupd_result_to_data (res, "{sa{sv}}");
1582 g_variant_builder_add_value (&builder, tmp);
1583 }
1584 return g_variant_new ("(a{sa{sv}})", &builder);
1585}
1586
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001587static void
1588fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
1589 const gchar *object_path, const gchar *interface_name,
1590 const gchar *method_name, GVariant *parameters,
1591 GDBusMethodInvocation *invocation, gpointer user_data)
1592{
1593 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1594 GVariant *val;
Richard Hughes060af612016-08-17 17:32:34 +01001595 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001596
1597 /* return 'as' */
1598 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughesf508e762015-02-27 12:49:36 +00001599 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001600 val = fu_main_device_array_to_variant (priv->devices, &error);
1601 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001602 if (g_error_matches (error,
1603 FWUPD_ERROR,
1604 FWUPD_ERROR_NOTHING_TO_DO)) {
1605 g_prefix_error (&error, "No detected devices: ");
1606 }
Richard Hughes060af612016-08-17 17:32:34 +01001607 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001608 return;
1609 }
Richard Hughes060af612016-08-17 17:32:34 +01001610 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001611 return;
1612 }
1613
1614 /* return 'as' */
1615 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +01001616 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +01001617 g_debug ("Called %s()", method_name);
1618 updates = fu_main_get_updates (priv, &error);
1619 if (updates == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001620 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001621 return;
1622 }
1623 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001624 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001625 if (g_error_matches (error,
1626 FWUPD_ERROR,
1627 FWUPD_ERROR_NOTHING_TO_DO)) {
1628 g_prefix_error (&error, "No devices can be updated: ");
1629 }
Richard Hughes060af612016-08-17 17:32:34 +01001630 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001631 return;
1632 }
Richard Hughes060af612016-08-17 17:32:34 +01001633 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001634 return;
1635 }
1636
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001637 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +00001638 if (g_strcmp0 (method_name, "ClearResults") == 0) {
1639 FuDeviceItem *item = NULL;
1640 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001641
1642 g_variant_get (parameters, "(&s)", &id);
1643 g_debug ("Called %s(%s)", method_name, id);
1644
1645 /* find device */
1646 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1647 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001648 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001649 return;
1650 }
1651
1652 /* call into the provider */
1653 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001654 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001655 return;
1656 }
1657
1658 /* success */
Richard Hughes060af612016-08-17 17:32:34 +01001659 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001660 return;
1661 }
1662
1663 /* return 'a{sv}' */
1664 if (g_strcmp0 (method_name, "GetResults") == 0) {
1665 FuDeviceItem *item = NULL;
1666 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001667
1668 g_variant_get (parameters, "(&s)", &id);
1669 g_debug ("Called %s(%s)", method_name, id);
1670
1671 /* find device */
1672 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1673 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001674 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001675 return;
1676 }
1677
1678 /* call into the provider */
1679 if (!fu_provider_get_results (item->provider, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001680 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001681 return;
1682 }
1683
1684 /* success */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001685 val = fwupd_result_to_data (FWUPD_RESULT (item->device), "(a{sv})");
Richard Hughes060af612016-08-17 17:32:34 +01001686 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001687 return;
1688 }
1689
1690 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01001691 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
1692 GDBusMessage *message;
1693 GUnixFDList *fd_list;
1694 gint fd_data;
1695 gint fd_sig;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001696
1697 message = g_dbus_method_invocation_get_message (invocation);
1698 fd_list = g_dbus_message_get_unix_fd_list (message);
1699 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
Richard Hughes060af612016-08-17 17:32:34 +01001700 g_set_error (&error,
1701 FWUPD_ERROR,
1702 FWUPD_ERROR_INTERNAL,
1703 "invalid handle");
1704 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001705 return;
1706 }
1707 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
1708 if (fd_data < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001709 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001710 return;
1711 }
1712 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
1713 if (fd_sig < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001714 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001715 return;
1716 }
Mario Limonciello3ed54472015-07-23 13:19:39 -05001717 if (!fu_main_daemon_update_metadata (priv, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001718 g_prefix_error (&error, "failed to update metadata: ");
Richard Hughes060af612016-08-17 17:32:34 +01001719 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001720 return;
1721 }
Richard Hughes060af612016-08-17 17:32:34 +01001722 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001723 return;
1724 }
1725
Richard Hughesa043c2e2015-06-29 08:43:18 +01001726 /* return 's' */
Richard Hughes9a410ce2016-02-28 15:58:54 +00001727 if (g_strcmp0 (method_name, "Unlock") == 0) {
1728 FuDeviceItem *item = NULL;
1729 FuMainAuthHelper *helper;
1730 const gchar *id = NULL;
1731 g_autoptr(PolkitSubject) subject = NULL;
1732
1733 /* check the id exists */
1734 g_variant_get (parameters, "(&s)", &id);
1735 g_debug ("Called %s(%s)", method_name, id);
1736 item = fu_main_get_item_by_id (priv, id);
1737 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001738 g_set_error (&error,
1739 FWUPD_ERROR,
1740 FWUPD_ERROR_NOT_FOUND,
1741 "No such device %s", id);
1742 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001743 return;
1744 }
1745
1746 /* check the device is locked */
Richard Hughes5bf28372016-03-29 18:43:54 +01001747 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_LOCKED)) {
Richard Hughes060af612016-08-17 17:32:34 +01001748 g_set_error (&error,
1749 FWUPD_ERROR,
1750 FWUPD_ERROR_NOT_FOUND,
1751 "Device %s is not locked", id);
1752 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001753 return;
1754 }
1755
1756 /* process the firmware */
1757 helper = g_new0 (FuMainAuthHelper, 1);
1758 helper->auth_kind = FU_MAIN_AUTH_KIND_UNLOCK;
1759 helper->invocation = g_object_ref (invocation);
Richard Hughesfe5cc902016-06-29 10:00:00 +01001760 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001761 helper->priv = priv;
1762
Richard Hughesfe5cc902016-06-29 10:00:00 +01001763 /* FIXME: do we want to support "*"? */
1764 g_ptr_array_add (helper->devices, g_object_ref (item->device));
1765
Richard Hughes9a410ce2016-02-28 15:58:54 +00001766 /* authenticate */
1767 subject = polkit_system_bus_name_new (sender);
1768 polkit_authority_check_authorization (helper->priv->authority, subject,
1769 "org.freedesktop.fwupd.device-unlock",
1770 NULL,
1771 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1772 NULL,
1773 fu_main_check_authorization_cb,
1774 helper);
1775 return;
1776 }
1777
1778 /* return 's' */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001779 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001780 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001781 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001782 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001783 FuDeviceItem *item = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001784 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001785 const gchar *id = NULL;
1786 const gchar *version = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001787
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 Hughesa043c2e2015-06-29 08:43:18 +01001798 return;
1799 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001800
1801 /* set the device firmware hash */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001802 if (!fu_provider_verify (item->provider, item->device,
1803 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001804 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001805 return;
1806 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001807
1808 /* find component in metadata */
Richard Hughes99147f12016-05-17 09:35:04 +01001809 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001810 if (app == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001811 g_set_error_literal (&error,
1812 FWUPD_ERROR,
1813 FWUPD_ERROR_NOT_FOUND,
1814 "No metadata");
1815 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001816 return;
1817 }
1818
1819 /* find version in metadata */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001820 version = fu_device_get_version (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001821 release = as_app_get_release (app, version);
1822 if (release == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001823 g_set_error (&error,
1824 FWUPD_ERROR,
1825 FWUPD_ERROR_NOT_FOUND,
1826 "No version %s", version);
1827 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001828 return;
1829 }
1830
1831 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001832 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
1833 if (csum == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001834 g_set_error (&error,
1835 FWUPD_ERROR,
1836 FWUPD_ERROR_NOT_FOUND,
1837 "No content checksum for %s", version);
1838 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001839 return;
1840 }
Richard Hughes8e9762d2016-03-17 10:14:15 +00001841 hash = fu_device_get_checksum (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001842 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001843 g_set_error (&error,
1844 FWUPD_ERROR,
1845 FWUPD_ERROR_NOT_FOUND,
1846 "For v%s expected %s, got %s",
1847 version,
1848 as_checksum_get_value (csum),
1849 hash);
1850 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001851 return;
1852 }
Richard Hughes060af612016-08-17 17:32:34 +01001853 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001854 return;
1855 }
1856
Richard Hughesae0efdc2015-06-24 16:18:29 +01001857 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01001858 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00001859 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001860 FuMainAuthHelper *helper;
Richard Hughes2d6e1862016-03-18 09:20:37 +00001861 FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE;
Richard Hughes74cc2172015-02-27 13:19:46 +00001862 GDBusMessage *message;
1863 GUnixFDList *fd_list;
1864 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00001865 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001866 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00001867 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001868 gint32 fd_handle = 0;
1869 gint fd;
Richard Hughes60f48c22015-10-08 20:25:51 +01001870 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001871 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001872 g_autoptr(GBytes) blob_cab = NULL;
1873 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001874
1875 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00001876 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00001877 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001878 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1879 item = fu_main_get_item_by_id (priv, id);
1880 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001881 g_set_error (&error,
1882 FWUPD_ERROR,
1883 FWUPD_ERROR_NOT_FOUND,
1884 "no such device %s", id);
1885 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001886 return;
1887 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001888 }
1889
Richard Hughes74cc2172015-02-27 13:19:46 +00001890 /* get options */
1891 while (g_variant_iter_next (iter, "{&sv}",
1892 &prop_key, &prop_value)) {
1893 g_debug ("got option %s", prop_key);
1894 if (g_strcmp0 (prop_key, "offline") == 0 &&
1895 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001896 flags |= FWUPD_INSTALL_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00001897 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
1898 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001899 flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughese7c12642015-03-04 20:28:59 +00001900 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
1901 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001902 flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Mario Limonciello71a5b982016-05-10 15:38:53 -05001903 if (g_strcmp0 (prop_key, "force") == 0 &&
1904 g_variant_get_boolean (prop_value) == TRUE)
1905 flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00001906 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00001907 }
1908
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001909 /* get the fd */
1910 message = g_dbus_method_invocation_get_message (invocation);
1911 fd_list = g_dbus_message_get_unix_fd_list (message);
1912 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01001913 g_set_error (&error,
1914 FWUPD_ERROR,
1915 FWUPD_ERROR_INTERNAL,
1916 "invalid handle");
1917 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001918 return;
1919 }
1920 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1921 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01001922 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001923 return;
1924 }
1925
Richard Hughes5d14def2015-10-07 17:43:10 +01001926 /* read the entire fd to a data blob */
1927 stream = g_unix_input_stream_new (fd, TRUE);
1928 blob_cab = g_input_stream_read_bytes (stream,
1929 FU_MAIN_FIRMWARE_SIZE_MAX,
1930 NULL, &error);
1931 if (blob_cab == NULL){
Richard Hughes060af612016-08-17 17:32:34 +01001932 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes5d14def2015-10-07 17:43:10 +01001933 return;
1934 }
1935
Richard Hughes67ec8982015-03-03 11:39:27 +00001936 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00001937 helper = g_new0 (FuMainAuthHelper, 1);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001938 helper->auth_kind = FU_MAIN_AUTH_KIND_INSTALL;
Richard Hughesf508e762015-02-27 12:49:36 +00001939 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01001940 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
1941 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00001942 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00001943 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01001944 helper->store = as_store_new ();
Richard Hughesfe5cc902016-06-29 10:00:00 +01001945 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughesa4a2c182016-06-29 10:37:05 +01001946 helper->blob_fws = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001947 if (item != NULL)
Richard Hughesfe5cc902016-06-29 10:00:00 +01001948 g_ptr_array_add (helper->devices, g_object_ref (item->device));
Richard Hughes67ec8982015-03-03 11:39:27 +00001949 if (!fu_main_update_helper (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001950 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughes67ec8982015-03-03 11:39:27 +00001951 fu_main_helper_free (helper);
1952 return;
1953 }
1954
Richard Hughes18423292015-03-09 17:10:50 +00001955 /* is root */
1956 if (fu_main_dbus_get_uid (priv, sender) == 0) {
1957 if (!fu_main_provider_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001958 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes18423292015-03-09 17:10:50 +00001959 } else {
Richard Hughes060af612016-08-17 17:32:34 +01001960 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes18423292015-03-09 17:10:50 +00001961 }
1962 fu_main_helper_free (helper);
1963 return;
1964 }
1965
Richard Hughes67ec8982015-03-03 11:39:27 +00001966 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01001967 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00001968 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00001969 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00001970 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00001971 NULL,
1972 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1973 NULL,
1974 fu_main_check_authorization_cb,
1975 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001976 return;
1977 }
1978
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001979 /* get a single result object from a local file */
Richard Hughescccc7752015-03-06 11:13:19 +00001980 if (g_strcmp0 (method_name, "GetDetails") == 0) {
1981 GDBusMessage *message;
1982 GUnixFDList *fd_list;
Richard Hughescccc7752015-03-06 11:13:19 +00001983 gint32 fd_handle = 0;
1984 gint fd;
Richard Hughescccc7752015-03-06 11:13:19 +00001985
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001986 /* get parameters */
Richard Hughescccc7752015-03-06 11:13:19 +00001987 g_variant_get (parameters, "(h)", &fd_handle);
1988 g_debug ("Called %s(%i)", method_name, fd_handle);
1989
1990 /* get the fd */
1991 message = g_dbus_method_invocation_get_message (invocation);
1992 fd_list = g_dbus_message_get_unix_fd_list (message);
1993 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01001994 g_set_error (&error,
1995 FWUPD_ERROR,
1996 FWUPD_ERROR_INTERNAL,
1997 "invalid handle");
1998 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00001999 return;
2000 }
2001 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
2002 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002003 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002004 return;
2005 }
2006
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002007 /* get details about the file */
2008 val = fu_main_get_details_from_fd (priv, fd, &error);
2009 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002010 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002011 return;
2012 }
Richard Hughes060af612016-08-17 17:32:34 +01002013 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughescccc7752015-03-06 11:13:19 +00002014 return;
2015 }
2016
Richard Hughes7289a6b2016-05-29 09:27:47 +01002017 /* get multiple result objects from a local file */
2018 if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) {
2019 GDBusMessage *message;
2020 GUnixFDList *fd_list;
2021 gint32 fd_handle = 0;
2022 gint fd;
Richard Hughes7289a6b2016-05-29 09:27:47 +01002023
2024 /* get parameters */
2025 g_variant_get (parameters, "(h)", &fd_handle);
2026 g_debug ("Called %s(%i)", method_name, fd_handle);
2027
2028 /* get the fd */
2029 message = g_dbus_method_invocation_get_message (invocation);
2030 fd_list = g_dbus_message_get_unix_fd_list (message);
2031 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002032 g_set_error (&error,
2033 FWUPD_ERROR,
2034 FWUPD_ERROR_INTERNAL,
2035 "invalid handle");
2036 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002037 return;
2038 }
2039 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
2040 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002041 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002042 return;
2043 }
2044
2045 /* get details about the file */
2046 val = fu_main_get_details_local_from_fd (priv, fd, &error);
2047 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002048 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002049 return;
2050 }
Richard Hughes060af612016-08-17 17:32:34 +01002051 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002052 return;
2053 }
2054
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002055 /* we suck */
Richard Hughes060af612016-08-17 17:32:34 +01002056 g_set_error (&error,
2057 G_DBUS_ERROR,
2058 G_DBUS_ERROR_UNKNOWN_METHOD,
2059 "no such method %s", method_name);
2060 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002061}
2062
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002063static GVariant *
2064fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
2065 const gchar *object_path, const gchar *interface_name,
2066 const gchar *property_name, GError **error,
2067 gpointer user_data)
2068{
Richard Hughes773ce982015-03-09 22:40:57 +00002069 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2070
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002071 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
2072 return g_variant_new_string (VERSION);
2073
Richard Hughes773ce982015-03-09 22:40:57 +00002074 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughes37e9d772016-04-28 14:23:24 +01002075 return g_variant_new_uint32 (priv->status);
Richard Hughes773ce982015-03-09 22:40:57 +00002076
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002077 /* return an error */
2078 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00002079 G_DBUS_ERROR,
2080 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002081 "failed to get daemon property %s",
2082 property_name);
2083 return NULL;
2084}
2085
Richard Hughesfd468842015-04-22 16:44:08 +01002086static void
2087fu_main_providers_coldplug (FuMainPrivate *priv)
2088{
Richard Hughes3f236502015-09-24 15:43:02 +01002089 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01002090
Richard Hughes3f236502015-09-24 15:43:02 +01002091 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughesf192bf02016-07-22 08:26:43 +01002092 for (guint i = 0; i < priv->providers->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01002093 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01002094 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01002095 FuProvider *provider = g_ptr_array_index (priv->providers, i);
Richard Hughes3f236502015-09-24 15:43:02 +01002096 ptask2 = as_profile_start (priv->profile,
2097 "FuMain:coldplug{%s}",
2098 fu_provider_get_name (provider));
Richard Hughesfd468842015-04-22 16:44:08 +01002099 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
2100 g_warning ("Failed to coldplug: %s", error->message);
2101 }
2102}
2103
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002104static void
2105fu_main_on_bus_acquired_cb (GDBusConnection *connection,
2106 const gchar *name,
2107 gpointer user_data)
2108{
2109 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2110 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01002111 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002112 static const GDBusInterfaceVTable interface_vtable = {
2113 fu_main_daemon_method_call,
2114 fu_main_daemon_get_property,
2115 NULL
2116 };
2117
2118 priv->connection = g_object_ref (connection);
2119 registration_id = g_dbus_connection_register_object (connection,
2120 FWUPD_DBUS_PATH,
2121 priv->introspection_daemon->interfaces[0],
2122 &interface_vtable,
2123 priv, /* user_data */
2124 NULL, /* user_data_free_func */
2125 NULL); /* GError** */
2126 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00002127
Richard Hughesfd468842015-04-22 16:44:08 +01002128 /* add devices */
2129 fu_main_providers_coldplug (priv);
2130
Richard Hughes18423292015-03-09 17:10:50 +00002131 /* connect to D-Bus directly */
2132 priv->proxy_uid =
2133 g_dbus_proxy_new_sync (priv->connection,
2134 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
2135 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2136 NULL,
2137 "org.freedesktop.DBus",
2138 "/org/freedesktop/DBus",
2139 "org.freedesktop.DBus",
2140 NULL,
2141 &error);
2142 if (priv->proxy_uid == NULL) {
2143 g_warning ("cannot connect to DBus: %s", error->message);
2144 return;
2145 }
Richard Hughes3f236502015-09-24 15:43:02 +01002146
Richard Hughes9559bbe2016-03-29 18:54:20 +01002147 /* connect to UPower */
2148 priv->proxy_upower =
2149 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
2150 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2151 NULL,
2152 "org.freedesktop.UPower",
2153 "/org/freedesktop/UPower",
2154 "org.freedesktop.UPower",
2155 NULL,
2156 &error);
2157 if (priv->proxy_upower == NULL) {
2158 g_warning ("Failed to conect UPower: %s", error->message);
2159 return;
2160 }
2161
Richard Hughes3f236502015-09-24 15:43:02 +01002162 /* dump startup profile data */
Richard Hughes2a1e75d2015-12-18 17:42:53 +00002163 if (fu_debug_is_verbose ())
2164 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002165}
2166
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002167static void
2168fu_main_on_name_acquired_cb (GDBusConnection *connection,
2169 const gchar *name,
2170 gpointer user_data)
2171{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002172 g_debug ("FuMain: acquired name: %s", name);
2173}
2174
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002175static void
2176fu_main_on_name_lost_cb (GDBusConnection *connection,
2177 const gchar *name,
2178 gpointer user_data)
2179{
2180 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2181 g_debug ("FuMain: lost name: %s", name);
2182 g_main_loop_quit (priv->loop);
2183}
2184
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002185static gboolean
2186fu_main_timed_exit_cb (gpointer user_data)
2187{
2188 GMainLoop *loop = (GMainLoop *) user_data;
2189 g_main_loop_quit (loop);
2190 return G_SOURCE_REMOVE;
2191}
2192
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002193static GDBusNodeInfo *
2194fu_main_load_introspection (const gchar *filename, GError **error)
2195{
Richard Hughes46832432015-09-11 13:43:15 +01002196 g_autoptr(GBytes) data = NULL;
2197 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002198
2199 /* lookup data */
2200 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
2201 data = g_resource_lookup_data (fu_get_resource (),
2202 path,
2203 G_RESOURCE_LOOKUP_FLAGS_NONE,
2204 error);
2205 if (data == NULL)
2206 return NULL;
2207
2208 /* build introspection from XML */
2209 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
2210}
2211
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002212static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002213fu_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002214 FuDevice *device,
2215 gpointer user_data)
2216{
2217 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002218 FuDeviceItem *item;
Richard Hughesdad1e192016-03-13 09:56:54 +00002219 AsApp *app;
2220 FuPlugin *plugin;
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002221 g_auto(GStrv) guids = NULL;
Richard Hughesdad1e192016-03-13 09:56:54 +00002222 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00002223
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002224 /* is this GUID blacklisted */
2225 guids = g_key_file_get_string_list (priv->config,
2226 "fwupd",
2227 "BlacklistDevices",
2228 NULL, /* length */
2229 NULL);
2230 if (guids != NULL &&
2231 g_strv_contains ((const gchar * const *) guids,
2232 fu_device_get_guid_default (device))) {
2233 g_debug ("%s is blacklisted [%s], ignoring from %s",
2234 fu_device_get_id (device),
2235 fu_device_get_guid_default (device),
2236 fu_provider_get_name (provider));
2237 return;
2238 }
2239
Richard Hughes0e883ee2015-03-18 17:22:33 +00002240 /* remove any fake device */
2241 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
Richard Hughes5d057a82015-11-24 18:09:57 +00002242 if (item != NULL) {
2243 g_debug ("already added %s by %s, ignoring same device from %s",
2244 fu_device_get_id (item->device),
Richard Hughes8e9762d2016-03-17 10:14:15 +00002245 fu_device_get_provider (item->device),
Richard Hughes5d057a82015-11-24 18:09:57 +00002246 fu_provider_get_name (provider));
2247 return;
2248 }
Richard Hughes0e883ee2015-03-18 17:22:33 +00002249
2250 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00002251 item = g_new0 (FuDeviceItem, 1);
2252 item->device = g_object_ref (device);
2253 item->provider = g_object_ref (provider);
2254 g_ptr_array_add (priv->devices, item);
Richard Hughesdad1e192016-03-13 09:56:54 +00002255
2256 /* does this match anything in the AppStream data */
Richard Hughes99147f12016-05-17 09:35:04 +01002257 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesdad1e192016-03-13 09:56:54 +00002258 if (app != NULL) {
2259 const gchar *tmp;
2260 tmp = as_app_get_metadata_item (app, FU_DEVICE_KEY_FWUPD_PLUGIN);
2261 if (tmp != NULL) {
2262 g_debug ("setting plugin: %s", tmp);
2263 fu_device_set_metadata (item->device,
2264 FU_DEVICE_KEY_FWUPD_PLUGIN,
2265 tmp);
2266 }
2267 }
2268
2269 /* run any plugins */
2270 plugin = fu_main_get_plugin_for_device (priv->plugins, device);
2271 if (plugin != NULL) {
2272 if (!fu_plugin_run_device_probe (plugin, device, &error)) {
2273 g_warning ("failed to probe %s: %s",
2274 fu_device_get_id (item->device),
2275 error->message);
2276 }
2277 }
2278
Richard Hughes422e8662016-04-28 15:05:33 +01002279 /* match the metadata at this point so clients can tell if the
2280 * device is worthy */
2281 fu_main_get_updates_item_update (priv, item);
2282
Richard Hughes8ca33782016-04-28 15:04:31 +01002283 /* notify clients */
2284 fu_main_emit_device_added (priv, item->device);
Richard Hughesd7022b52015-03-11 19:47:06 +00002285 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002286}
2287
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002288static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002289fu_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002290 FuDevice *device,
2291 gpointer user_data)
2292{
2293 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002294 FuDeviceItem *item;
2295
2296 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
2297 if (item == NULL) {
Richard Hughes5d057a82015-11-24 18:09:57 +00002298 g_debug ("no device to remove %s", fu_device_get_id (device));
Richard Hughesf508e762015-02-27 12:49:36 +00002299 return;
2300 }
Richard Hughes5d057a82015-11-24 18:09:57 +00002301
2302 /* check this came from the same provider */
2303 if (g_strcmp0 (fu_provider_get_name (provider),
2304 fu_provider_get_name (item->provider)) != 0) {
2305 g_debug ("ignoring duplicate removal from %s",
2306 fu_provider_get_name (provider));
2307 return;
2308 }
2309
Richard Hughes8ca33782016-04-28 15:04:31 +01002310 /* make the UI update */
2311 fu_main_emit_device_removed (priv, device);
Richard Hughesf508e762015-02-27 12:49:36 +00002312 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00002313 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002314}
2315
Richard Hughes773ce982015-03-09 22:40:57 +00002316static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002317fu_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00002318 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00002319 gpointer user_data)
2320{
2321 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2322 fu_main_set_status (priv, status);
2323}
2324
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002325static void
Richard Hughes876c0072016-08-17 14:51:03 +01002326fu_main_provider_percentage_changed_cb (FuProvider *provider,
2327 guint percentage,
2328 gpointer user_data)
2329{
2330 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2331 fu_main_set_percentage (priv, percentage);
2332}
2333
2334static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002335fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
2336{
2337 g_signal_connect (provider, "device-added",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002338 G_CALLBACK (fu_main_provider_device_added_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002339 priv);
2340 g_signal_connect (provider, "device-removed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002341 G_CALLBACK (fu_main_provider_device_removed_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002342 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00002343 g_signal_connect (provider, "status-changed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002344 G_CALLBACK (fu_main_provider_status_changed_cb),
Richard Hughes773ce982015-03-09 22:40:57 +00002345 priv);
Richard Hughes876c0072016-08-17 14:51:03 +01002346 g_signal_connect (provider, "percentage-changed",
2347 G_CALLBACK (fu_main_provider_percentage_changed_cb),
2348 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002349 g_ptr_array_add (priv->providers, provider);
2350}
2351
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002352int
2353main (int argc, char *argv[])
2354{
2355 FuMainPrivate *priv = NULL;
2356 gboolean immediate_exit = FALSE;
2357 gboolean ret;
2358 gboolean timed_exit = FALSE;
2359 GOptionContext *context;
2360 guint owner_id = 0;
Richard Hughes33a518a2016-07-27 15:22:53 +01002361 gint retval = 1;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002362 const GOptionEntry options[] = {
2363 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
2364 /* TRANSLATORS: exit after we've started up, used for user profiling */
2365 _("Exit after a small delay"), NULL },
2366 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
2367 /* TRANSLATORS: exit straight away, used for automatic profiling */
2368 _("Exit after the engine has loaded"), NULL },
2369 { NULL}
2370 };
Richard Hughes46832432015-09-11 13:43:15 +01002371 g_autoptr(GError) error = NULL;
2372 g_autofree gchar *config_file = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002373
2374 setlocale (LC_ALL, "");
2375
2376 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
2377 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2378 textdomain (GETTEXT_PACKAGE);
2379
2380 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01002381 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002382 context = g_option_context_new (NULL);
2383 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002384 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00002385 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002386 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
2387 ret = g_option_context_parse (context, &argc, &argv, &error);
2388 if (!ret) {
2389 g_warning ("FuMain: failed to parse command line arguments: %s",
2390 error->message);
2391 goto out;
2392 }
2393
2394 /* create new objects */
2395 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00002396 priv->status = FWUPD_STATUS_IDLE;
Richard Hughes876c0072016-08-17 14:51:03 +01002397 priv->percentage = 0;
Richard Hughesf508e762015-02-27 12:49:36 +00002398 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002399 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002400 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01002401 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01002402 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01002403 g_signal_connect (priv->store, "changed",
2404 G_CALLBACK (fu_main_store_changed_cb), priv);
2405 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
2406 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002407
Richard Hughesd0905142016-03-13 09:46:49 +00002408 /* load plugin */
2409 priv->plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
2410 g_free, (GDestroyNotify) fu_plugin_free);
2411 if (!fu_main_load_plugins (priv->plugins, &error)) {
2412 g_print ("failed to load plugins: %s\n", error->message);
2413 retval = EXIT_FAILURE;
2414 goto out;
2415 }
2416
Richard Hughes7708a0f2015-07-21 08:41:22 +01002417 /* load AppStream */
Richard Hughes65dfbfe2016-03-02 13:42:53 +00002418 as_store_add_filter (priv->store, AS_APP_KIND_FIRMWARE);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002419 if (!as_store_load (priv->store,
2420 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
2421 NULL, &error)){
2422 g_warning ("FuMain: failed to load AppStream data: %s",
2423 error->message);
2424 return FALSE;
2425 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002426
Richard Hughes804c0752015-08-04 14:53:52 +01002427 /* read config file */
Richard Hughes804c0752015-08-04 14:53:52 +01002428 config_file = g_build_filename (SYSCONFDIR, "fwupd.conf", NULL);
2429 g_debug ("Loading fallback values from %s", config_file);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002430 priv->config = g_key_file_new ();
2431 if (!g_key_file_load_from_file (priv->config, config_file,
Richard Hughes804c0752015-08-04 14:53:52 +01002432 G_KEY_FILE_NONE, &error)) {
2433 g_print ("failed to load config file %s: %s\n",
2434 config_file, error->message);
2435 retval = EXIT_FAILURE;
2436 goto out;
2437 }
2438
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002439 /* add providers */
2440 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002441 if (g_key_file_get_boolean (priv->config, "fwupd", "EnableOptionROM", NULL))
Richard Hughes804c0752015-08-04 14:53:52 +01002442 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughes5d057a82015-11-24 18:09:57 +00002443 fu_main_add_provider (priv, fu_provider_dfu_new ());
Richard Hughes25cf6ab2015-08-04 21:34:12 +01002444 fu_main_add_provider (priv, fu_provider_rpi_new ());
Richard Hughes14d17642016-08-17 12:03:03 +01002445 fu_main_add_provider (priv, fu_provider_ebitdo_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002446#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00002447 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002448#endif
2449#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002450 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002451#endif
Mario Limonciello958ead62016-05-14 00:10:25 -05002452#ifdef HAVE_DELL
2453 fu_main_add_provider (priv, fu_provider_dell_new ());
2454#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002455
Richard Hughes5d057a82015-11-24 18:09:57 +00002456 /* last as least priority */
2457 fu_main_add_provider (priv, fu_provider_usb_new ());
2458
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002459 /* load introspection from file */
2460 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
2461 &error);
2462 if (priv->introspection_daemon == NULL) {
2463 g_warning ("FuMain: failed to load daemon introspection: %s",
2464 error->message);
2465 goto out;
2466 }
2467
Richard Hughesf508e762015-02-27 12:49:36 +00002468 /* get authority */
2469 priv->authority = polkit_authority_get_sync (NULL, &error);
2470 if (priv->authority == NULL) {
2471 g_warning ("FuMain: failed to load polkit authority: %s",
2472 error->message);
2473 goto out;
2474 }
2475
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002476 /* own the object */
2477 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
2478 FWUPD_DBUS_SERVICE,
2479 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
2480 G_BUS_NAME_OWNER_FLAGS_REPLACE,
2481 fu_main_on_bus_acquired_cb,
2482 fu_main_on_name_acquired_cb,
2483 fu_main_on_name_lost_cb,
2484 priv, NULL);
2485
2486 /* Only timeout and close the mainloop if we have specified it
2487 * on the command line */
2488 if (immediate_exit)
2489 g_idle_add (fu_main_timed_exit_cb, priv->loop);
2490 else if (timed_exit)
2491 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
2492
2493 /* wait */
2494 g_info ("Daemon ready for requests");
2495 g_main_loop_run (priv->loop);
2496
2497 /* success */
2498 retval = 0;
2499out:
2500 g_option_context_free (context);
2501 if (owner_id > 0)
2502 g_bus_unown_name (owner_id);
2503 if (priv != NULL) {
2504 if (priv->loop != NULL)
2505 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00002506 if (priv->proxy_uid != NULL)
2507 g_object_unref (priv->proxy_uid);
Richard Hughes9559bbe2016-03-29 18:54:20 +01002508 if (priv->proxy_upower != NULL)
2509 g_object_unref (priv->proxy_upower);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002510 if (priv->config != NULL)
2511 g_object_unref (priv->config);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002512 if (priv->connection != NULL)
2513 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00002514 if (priv->authority != NULL)
2515 g_object_unref (priv->authority);
Richard Hughes3f236502015-09-24 15:43:02 +01002516 if (priv->profile != NULL)
2517 g_object_unref (priv->profile);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002518 if (priv->store != NULL)
2519 g_object_unref (priv->store);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002520 if (priv->introspection_daemon != NULL)
2521 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes033ccba2015-09-10 14:51:28 +01002522 if (priv->store_changed_id != 0)
2523 g_source_remove (priv->store_changed_id);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002524 g_object_unref (priv->pending);
Richard Hughes804c0752015-08-04 14:53:52 +01002525 if (priv->providers != NULL)
2526 g_ptr_array_unref (priv->providers);
Richard Hughesd0905142016-03-13 09:46:49 +00002527 if (priv->plugins != NULL)
2528 g_hash_table_unref (priv->plugins);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002529 g_ptr_array_unref (priv->devices);
2530 g_free (priv);
2531 }
2532 return retval;
2533}
2534