blob: ed1a4062d574b7c97fa282380153fa653fbcdbb3 [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 Hughes25cf6ab2015-08-04 21:34:12 +010045#include "fu-provider-rpi.h"
Richard Hughesebb58a32015-05-29 15:35:37 +010046#include "fu-provider-udev.h"
Richard Hughesc89c1b02015-05-05 15:21:18 +010047#include "fu-provider-usb.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000048#include "fu-resources.h"
Mario Limonciello918f3932016-02-03 12:47:23 -060049#include "fu-quirks.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000050
Richard Hughes3c99ba42015-03-05 12:17:48 +000051#ifdef HAVE_COLORHUG
52 #include "fu-provider-chug.h"
53#endif
54#ifdef HAVE_UEFI
55 #include "fu-provider-uefi.h"
56#endif
Mario Limonciello958ead62016-05-14 00:10:25 -050057#ifdef HAVE_DELL
58 #include "fu-provider-dell.h"
59#endif
Richard Hughes3c99ba42015-03-05 12:17:48 +000060
Richard Hughes60f48c22015-10-08 20:25:51 +010061#ifndef PolkitAuthorizationResult_autoptr
62G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
63G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
64#endif
65
Richard Hughes5d14def2015-10-07 17:43:10 +010066#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
67
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000068typedef struct {
69 GDBusConnection *connection;
70 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000071 GDBusProxy *proxy_uid;
Richard Hughes9559bbe2016-03-29 18:54:20 +010072 GDBusProxy *proxy_upower;
Richard Hughes9a52c5e2016-07-18 09:17:02 +010073 GKeyFile *config;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000074 GMainLoop *loop;
Richard Hughes5d14def2015-10-07 17:43:10 +010075 GPtrArray *devices; /* of FuDeviceItem */
Richard Hughes8bbfdf42015-02-26 22:28:09 +000076 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000077 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000078 FwupdStatus status;
Richard Hughes876c0072016-08-17 14:51:03 +010079 guint percentage;
Richard Hughes0e883ee2015-03-18 17:22:33 +000080 FuPending *pending;
Richard Hughes3f236502015-09-24 15:43:02 +010081 AsProfile *profile;
Richard Hughes7708a0f2015-07-21 08:41:22 +010082 AsStore *store;
Richard Hughes033ccba2015-09-10 14:51:28 +010083 guint store_changed_id;
Richard Hughesd0905142016-03-13 09:46:49 +000084 GHashTable *plugins; /* of name : FuPlugin */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000085} FuMainPrivate;
86
Richard Hughesf508e762015-02-27 12:49:36 +000087typedef struct {
88 FuDevice *device;
89 FuProvider *provider;
90} FuDeviceItem;
91
Richard Hughesdde7a2f2016-04-28 15:06:52 +010092static gboolean fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item);
93
Richard Hughesd7022b52015-03-11 19:47:06 +000094static void
95fu_main_emit_changed (FuMainPrivate *priv)
96{
97 /* not yet connected */
98 if (priv->connection == NULL)
99 return;
100 g_dbus_connection_emit_signal (priv->connection,
101 NULL,
102 FWUPD_DBUS_PATH,
103 FWUPD_DBUS_INTERFACE,
104 "Changed",
105 NULL, NULL);
106}
107
Richard Hughes8ca33782016-04-28 15:04:31 +0100108static void
109fu_main_emit_device_added (FuMainPrivate *priv, FuDevice *device)
110{
111 GVariant *val;
112
113 /* not yet connected */
114 if (priv->connection == NULL)
115 return;
116 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
117 g_dbus_connection_emit_signal (priv->connection,
118 NULL,
119 FWUPD_DBUS_PATH,
120 FWUPD_DBUS_INTERFACE,
121 "DeviceAdded",
122 val, NULL);
123}
124
Richard Hughes8ca33782016-04-28 15:04:31 +0100125static void
126fu_main_emit_device_removed (FuMainPrivate *priv, FuDevice *device)
127{
128 GVariant *val;
129
130 /* not yet connected */
131 if (priv->connection == NULL)
132 return;
133 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
134 g_dbus_connection_emit_signal (priv->connection,
135 NULL,
136 FWUPD_DBUS_PATH,
137 FWUPD_DBUS_INTERFACE,
138 "DeviceRemoved",
139 val, NULL);
140}
141
Richard Hughes8ca33782016-04-28 15:04:31 +0100142static void
143fu_main_emit_device_changed (FuMainPrivate *priv, FuDevice *device)
144{
145 GVariant *val;
146
147 /* not yet connected */
148 if (priv->connection == NULL)
149 return;
150 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
151 g_dbus_connection_emit_signal (priv->connection,
152 NULL,
153 FWUPD_DBUS_PATH,
154 FWUPD_DBUS_INTERFACE,
155 "DeviceChanged",
156 val, NULL);
157}
158
Richard Hughes773ce982015-03-09 22:40:57 +0000159static void
160fu_main_emit_property_changed (FuMainPrivate *priv,
161 const gchar *property_name,
162 GVariant *property_value)
163{
164 GVariantBuilder builder;
165 GVariantBuilder invalidated_builder;
166
167 /* not yet connected */
168 if (priv->connection == NULL)
169 return;
170
171 /* build the dict */
172 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
173 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
174 g_variant_builder_add (&builder,
175 "{sv}",
176 property_name,
177 property_value);
178 g_dbus_connection_emit_signal (priv->connection,
179 NULL,
180 FWUPD_DBUS_PATH,
181 "org.freedesktop.DBus.Properties",
182 "PropertiesChanged",
183 g_variant_new ("(sa{sv}as)",
184 FWUPD_DBUS_INTERFACE,
185 &builder,
186 &invalidated_builder),
187 NULL);
188 g_variant_builder_clear (&builder);
189 g_variant_builder_clear (&invalidated_builder);
190}
191
Richard Hughes773ce982015-03-09 22:40:57 +0000192static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000193fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000194{
Richard Hughes773ce982015-03-09 22:40:57 +0000195 if (priv->status == status)
196 return;
197 priv->status = status;
198
199 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000200 g_debug ("Emitting PropertyChanged('Status'='%s')",
201 fwupd_status_to_string (status));
202 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000203}
204
Richard Hughes876c0072016-08-17 14:51:03 +0100205static void
206fu_main_set_percentage (FuMainPrivate *priv, guint percentage)
207{
208 if (priv->percentage == percentage)
209 return;
210 priv->percentage = percentage;
211
212 /* emit changed */
213 g_debug ("Emitting PropertyChanged('Percentage'='%u%%')", percentage);
214 fu_main_emit_property_changed (priv, "Percentage",
215 g_variant_new_uint32 (percentage));
216}
217
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000218static GVariant *
Richard Hughes7708a0f2015-07-21 08:41:22 +0100219fu_main_device_array_to_variant (GPtrArray *devices, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000220{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000221 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000222
Richard Hughes9a38c032015-03-17 20:58:46 +0000223 /* no devices */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100224 if (devices->len == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000225 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000226 FWUPD_ERROR,
227 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9d76a872015-09-17 12:49:07 +0100228 "Nothing to do");
Richard Hughes9a38c032015-03-17 20:58:46 +0000229 return NULL;
230 }
231
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000232 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +0100233 for (guint i = 0; i < devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000234 GVariant *tmp;
235 FuDeviceItem *item;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100236 item = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +0000237 tmp = fwupd_result_to_data (FWUPD_RESULT (item->device), "{sa{sv}}");
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000238 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000239 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000240 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000241}
242
Richard Hughesd0905142016-03-13 09:46:49 +0000243static gboolean
244fu_main_load_plugins (GHashTable *plugins, GError **error)
245{
246 FuPlugin *plugin;
247 GModule *module;
Richard Hughesd0905142016-03-13 09:46:49 +0000248 const gchar *fn;
249 g_autofree gchar *plugin_dir = NULL;
250 g_autoptr(GDir) dir = NULL;
251 g_autoptr(GList) values = NULL;
252
253 /* search */
254 plugin_dir = g_build_filename (LIBDIR, "fwupd-plugins-1", NULL);
255 dir = g_dir_open (plugin_dir, 0, error);
256 if (dir == NULL)
257 return FALSE;
258 while ((fn = g_dir_read_name (dir)) != NULL) {
259 g_autofree gchar *filename = NULL;
260
261 /* ignore non-plugins */
262 if (!g_str_has_suffix (fn, ".so"))
263 continue;
264
265 /* open module */
266 filename = g_build_filename (plugin_dir, fn, NULL);
267 g_debug ("adding plugin %s", filename);
268 module = g_module_open (filename, 0);
269 if (module == NULL) {
270 g_warning ("failed to open plugin %s: %s",
271 filename, g_module_error ());
272 continue;
273 }
274 plugin = fu_plugin_new (module);
275 if (plugin == NULL) {
276 g_module_close (module);
277 g_warning ("plugin %s requires name", filename);
278 continue;
279 }
280
281 /* add */
282 g_hash_table_insert (plugins, g_strdup (plugin->name), plugin);
283 }
284
285 /* start them all up */
286 values = g_hash_table_get_values (plugins);
Richard Hughesf192bf02016-07-22 08:26:43 +0100287 for (GList *l = values; l != NULL; l = l->next) {
Richard Hughesd0905142016-03-13 09:46:49 +0000288 plugin = FU_PLUGIN (l->data);
289 if (!fu_plugin_run_startup (plugin, error))
290 return FALSE;
291 }
292
293 return TRUE;
294}
295
Richard Hughesdad1e192016-03-13 09:56:54 +0000296static FuPlugin *
297fu_main_get_plugin_for_device (GHashTable *plugins, FuDevice *device)
298{
299 const gchar *tmp;
Richard Hughes0bda0542016-08-11 15:35:30 +0100300 FuPlugin *plugin;
Richard Hughesdad1e192016-03-13 09:56:54 +0000301
302 /* does a vendor plugin exist */
303 tmp = fu_device_get_metadata (device, FU_DEVICE_KEY_FWUPD_PLUGIN);
304 if (tmp == NULL)
305 return NULL;
Richard Hughes0bda0542016-08-11 15:35:30 +0100306 plugin = g_hash_table_lookup (plugins, tmp);
307 if (plugin == NULL) {
308 g_warning ("requested plugin %s for %s, but not found",
309 tmp, fu_device_get_id (device));
310 }
311 return plugin;
Richard Hughesdad1e192016-03-13 09:56:54 +0000312}
313
Richard Hughesf508e762015-02-27 12:49:36 +0000314static void
315fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000316{
Richard Hughesf508e762015-02-27 12:49:36 +0000317 g_object_unref (item->device);
318 g_object_unref (item->provider);
319 g_free (item);
320}
321
Richard Hughesf508e762015-02-27 12:49:36 +0000322static FuDeviceItem *
323fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
324{
Richard Hughesf192bf02016-07-22 08:26:43 +0100325 for (guint i = 0; i < priv->devices->len; i++) {
326 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesf508e762015-02-27 12:49:36 +0000327 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
328 return item;
Richard Hughesb1b59d82016-01-06 13:20:53 +0000329 if (g_strcmp0 (fu_device_get_equivalent_id (item->device), id) == 0)
330 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000331 }
332 return NULL;
333}
334
Richard Hughes16d8f622016-03-15 16:34:20 +0000335static FuDeviceItem *
336fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid)
337{
Richard Hughesf192bf02016-07-22 08:26:43 +0100338 for (guint i = 0; i < priv->devices->len; i++) {
339 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes99147f12016-05-17 09:35:04 +0100340 if (fu_device_has_guid (item->device, guid))
Richard Hughes16d8f622016-03-15 16:34:20 +0000341 return item;
342 }
343 return NULL;
344}
345
Richard Hughes0e883ee2015-03-18 17:22:33 +0000346static FuProvider *
347fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
348{
Richard Hughesf192bf02016-07-22 08:26:43 +0100349 for (guint i = 0; i < priv->providers->len; i++) {
350 FuProvider *provider = g_ptr_array_index (priv->providers, i);
Richard Hughes0e883ee2015-03-18 17:22:33 +0000351 if (g_strcmp0 (fu_provider_get_name (provider), name) == 0)
352 return provider;
353 }
354 return NULL;
355}
356
Richard Hughes5d14def2015-10-07 17:43:10 +0100357static gboolean
358fu_main_get_release_trust_flags (AsRelease *release,
359 FwupdTrustFlags *trust_flags,
360 GError **error)
361{
362 AsChecksum *csum_tmp;
363 GBytes *blob_payload;
364 GBytes *blob_signature;
365 const gchar *fn;
366 g_autofree gchar *pki_dir = NULL;
367 g_autofree gchar *fn_signature = NULL;
368 g_autoptr(GError) error_local = NULL;
369 g_autoptr(FuKeyring) kr = NULL;
370
371 /* no filename? */
372 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
373 fn = as_checksum_get_filename (csum_tmp);
374 if (fn == NULL) {
375 g_set_error_literal (error,
376 FWUPD_ERROR,
377 FWUPD_ERROR_INVALID_FILE,
378 "no filename");
379 return FALSE;
380 }
381
382 /* no signature == no trust */
383 fn_signature = g_strdup_printf ("%s.asc", fn);
384 blob_signature = as_release_get_blob (release, fn_signature);
385 if (blob_signature == NULL) {
386 g_debug ("firmware archive contained no GPG signature");
387 return TRUE;
388 }
389
390 /* get payload */
391 blob_payload = as_release_get_blob (release, fn);
392 if (blob_payload == NULL) {
393 g_set_error_literal (error,
394 FWUPD_ERROR,
395 FWUPD_ERROR_INVALID_FILE,
396 "no payload");
397 return FALSE;
398 }
399
400 /* check we were installed correctly */
401 pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
402 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
403 g_set_error (error,
404 FWUPD_ERROR,
405 FWUPD_ERROR_NOT_FOUND,
406 "PKI directory %s not found", pki_dir);
407 return FALSE;
408 }
409
410 /* verify against the system trusted keys */
411 kr = fu_keyring_new ();
412 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
413 return FALSE;
414 if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
415 g_warning ("untrusted as failed to verify: %s",
416 error_local->message);
417 return TRUE;
418 }
419
420 /* awesome! */
421 g_debug ("marking payload as trusted");
422 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
423 return TRUE;
424}
425
Richard Hughes9a410ce2016-02-28 15:58:54 +0000426typedef enum {
427 FU_MAIN_AUTH_KIND_UNKNOWN,
428 FU_MAIN_AUTH_KIND_INSTALL,
429 FU_MAIN_AUTH_KIND_UNLOCK,
430 FU_MAIN_AUTH_KIND_LAST
431} FuMainAuthKind;
432
Richard Hughesf508e762015-02-27 12:49:36 +0000433typedef struct {
434 GDBusMethodInvocation *invocation;
Richard Hughes5d14def2015-10-07 17:43:10 +0100435 AsStore *store;
436 FwupdTrustFlags trust_flags;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100437 GPtrArray *devices; /* of FuDevice */
Richard Hughesa4a2c182016-06-29 10:37:05 +0100438 GPtrArray *blob_fws; /* of GBytes */
Richard Hughes2d6e1862016-03-18 09:20:37 +0000439 FwupdInstallFlags flags;
Richard Hughes5d14def2015-10-07 17:43:10 +0100440 GBytes *blob_cab;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100441 gboolean is_downgrade;
Richard Hughes9a410ce2016-02-28 15:58:54 +0000442 FuMainAuthKind auth_kind;
Richard Hughes67ec8982015-03-03 11:39:27 +0000443 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000444} FuMainAuthHelper;
445
Richard Hughesf508e762015-02-27 12:49:36 +0000446static void
447fu_main_helper_free (FuMainAuthHelper *helper)
448{
Richard Hughes67ec8982015-03-03 11:39:27 +0000449 /* free */
Richard Hughesfe5cc902016-06-29 10:00:00 +0100450 if (helper->devices != NULL)
451 g_ptr_array_unref (helper->devices);
Richard Hughesa4a2c182016-06-29 10:37:05 +0100452 if (helper->blob_fws > 0)
453 g_ptr_array_unref (helper->blob_fws);
Richard Hughes5d14def2015-10-07 17:43:10 +0100454 if (helper->blob_cab > 0)
455 g_bytes_unref (helper->blob_cab);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000456 if (helper->store != NULL)
457 g_object_unref (helper->store);
Richard Hughes67ec8982015-03-03 11:39:27 +0000458 g_object_unref (helper->invocation);
Richard Hughesf508e762015-02-27 12:49:36 +0000459 g_free (helper);
460}
461
Richard Hughesb75c92d2016-02-20 20:22:00 +0000462static gboolean
Richard Hughes9559bbe2016-03-29 18:54:20 +0100463fu_main_on_battery (FuMainPrivate *priv)
Richard Hughesb75c92d2016-02-20 20:22:00 +0000464{
Richard Hughesb75c92d2016-02-20 20:22:00 +0000465 g_autoptr(GVariant) value = NULL;
Richard Hughes9559bbe2016-03-29 18:54:20 +0100466 if (priv->proxy_upower == NULL) {
467 g_warning ("Failed to get OnBattery property as no UPower");
Richard Hughesb75c92d2016-02-20 20:22:00 +0000468 return FALSE;
469 }
Richard Hughes9559bbe2016-03-29 18:54:20 +0100470 value = g_dbus_proxy_get_cached_property (priv->proxy_upower, "OnBattery");
Richard Hughesb75c92d2016-02-20 20:22:00 +0000471 if (value == NULL) {
472 g_warning ("Failed to get OnBattery property value");
473 return FALSE;
474 }
475 return g_variant_get_boolean (value);
476}
477
Richard Hughes9a410ce2016-02-28 15:58:54 +0000478static gboolean
479fu_main_provider_unlock_authenticated (FuMainAuthHelper *helper, GError **error)
480{
Richard Hughesfe5cc902016-06-29 10:00:00 +0100481 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100482 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100483 FuDeviceItem *item;
484 FuDevice *device = g_ptr_array_index (helper->devices, i);
485
486 item = fu_main_get_item_by_id (helper->priv,
487 fu_device_get_id (device));
488 if (item == NULL) {
489 g_set_error (error,
490 FWUPD_ERROR,
491 FWUPD_ERROR_INVALID_FILE,
492 "device %s was removed",
493 fu_device_get_id (device));
494 return FALSE;
495 }
496
497 /* run the correct provider that added this */
498 if (!fu_provider_unlock (item->provider,
499 item->device,
500 error))
501 return FALSE;
502
503 /* make the UI update */
504 fu_main_emit_device_changed (helper->priv, item->device);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000505 }
506
Richard Hughes9a410ce2016-02-28 15:58:54 +0000507 /* make the UI update */
508 fu_main_emit_changed (helper->priv);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100509
Richard Hughes9a410ce2016-02-28 15:58:54 +0000510 return TRUE;
511}
512
Richard Hughes18423292015-03-09 17:10:50 +0000513static gboolean
514fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
515{
516 FuDeviceItem *item;
Richard Hughesdad1e192016-03-13 09:56:54 +0000517 FuPlugin *plugin;
Richard Hughes18423292015-03-09 17:10:50 +0000518
Richard Hughesfe5cc902016-06-29 10:00:00 +0100519 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100520 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100521 FuDevice *device = g_ptr_array_index (helper->devices, i);
522 item = fu_main_get_item_by_id (helper->priv,
523 fu_device_get_id (device));
524 if (item == NULL) {
525 g_set_error (error,
526 FWUPD_ERROR,
527 FWUPD_ERROR_INVALID_FILE,
528 "device %s was removed",
529 fu_device_get_id (device));
Richard Hughesb75c92d2016-02-20 20:22:00 +0000530 return FALSE;
531 }
Richard Hughesfe5cc902016-06-29 10:00:00 +0100532
533 /* The provider might have taken away update abilities */
534 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
535 !fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
536 g_set_error(error,
537 FWUPD_ERROR,
538 FWUPD_ERROR_INTERNAL,
Richard Hughes01b9a832016-08-16 17:59:32 +0100539 "Device %s does not now allow updates",
Richard Hughesfe5cc902016-06-29 10:00:00 +0100540 fu_device_get_id (device));
541 return FALSE;
542 }
543
544 /* can we only do this on AC power */
545 if (fu_device_has_flag (item->device, FU_DEVICE_FLAG_REQUIRE_AC)) {
546 if (fu_main_on_battery (helper->priv)) {
547 g_set_error_literal (error,
548 FWUPD_ERROR,
549 FWUPD_ERROR_NOT_SUPPORTED,
550 "Cannot install update "
551 "when not on AC power");
552 return FALSE;
553 }
554 }
Richard Hughesb75c92d2016-02-20 20:22:00 +0000555 }
556
Richard Hughesfe5cc902016-06-29 10:00:00 +0100557 /* run the correct providers for each device */
Richard Hughesf192bf02016-07-22 08:26:43 +0100558 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100559 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughesa4a2c182016-06-29 10:37:05 +0100560 GBytes *blob_fw = g_ptr_array_index (helper->blob_fws, i);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100561 item = fu_main_get_item_by_id (helper->priv,
562 fu_device_get_id (device));
563 plugin = fu_main_get_plugin_for_device (helper->priv->plugins,
564 item->device);
565 if (!fu_provider_update (item->provider,
566 item->device,
567 helper->blob_cab,
Richard Hughesa4a2c182016-06-29 10:37:05 +0100568 blob_fw,
Richard Hughesfe5cc902016-06-29 10:00:00 +0100569 plugin,
570 helper->flags,
571 error))
572 return FALSE;
573
574 /* make the UI update */
Richard Hughes33a518a2016-07-27 15:22:53 +0100575 fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100576 fu_main_emit_device_changed (helper->priv, item->device);
577 }
Richard Hughesa3a8f502015-11-24 12:31:59 +0000578
579 /* make the UI update */
580 fu_main_emit_changed (helper->priv);
581 return TRUE;
Richard Hughes18423292015-03-09 17:10:50 +0000582}
583
Richard Hughesf508e762015-02-27 12:49:36 +0000584static void
585fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
586{
587 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100588 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100589 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000590
591 /* get result */
592 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
593 res, &error);
594 if (auth == NULL) {
595 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000596 FWUPD_ERROR,
597 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000598 "could not check for auth: %s",
599 error->message);
600 fu_main_helper_free (helper);
601 return;
602 }
603
604 /* did not auth */
605 if (!polkit_authorization_result_get_is_authorized (auth)) {
606 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000607 FWUPD_ERROR,
608 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000609 "failed to obtain auth");
610 fu_main_helper_free (helper);
611 return;
612 }
613
Richard Hughes18423292015-03-09 17:10:50 +0000614 /* we're good to go */
Richard Hughes9a410ce2016-02-28 15:58:54 +0000615 if (helper->auth_kind == FU_MAIN_AUTH_KIND_INSTALL) {
616 if (!fu_main_provider_update_authenticated (helper, &error)) {
617 g_dbus_method_invocation_return_gerror (helper->invocation, error);
618 fu_main_helper_free (helper);
619 return;
620 }
621 } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_UNLOCK) {
622 if (!fu_main_provider_unlock_authenticated (helper, &error)) {
623 g_dbus_method_invocation_return_gerror (helper->invocation, error);
624 fu_main_helper_free (helper);
625 return;
626 }
627 } else {
628 g_assert_not_reached ();
Richard Hughesf508e762015-02-27 12:49:36 +0000629 }
630
631 /* success */
632 g_dbus_method_invocation_return_value (helper->invocation, NULL);
633 fu_main_helper_free (helper);
634}
635
Richard Hughes5d14def2015-10-07 17:43:10 +0100636static gchar *
637fu_main_get_guids_from_store (AsStore *store)
638{
Richard Hughes5d14def2015-10-07 17:43:10 +0100639 AsProvide *prov;
640 GPtrArray *provides;
641 GPtrArray *apps;
642 GString *str = g_string_new ("");
Richard Hughes5d14def2015-10-07 17:43:10 +0100643
644 /* return a string with all the firmware apps in the store */
645 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +0100646 for (guint i = 0; i < apps->len; i++) {
647 AsApp *app = AS_APP (g_ptr_array_index (apps, i));
Richard Hughes5d14def2015-10-07 17:43:10 +0100648 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100649 for (guint j = 0; j < provides->len; j++) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100650 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
651 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
652 continue;
653 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
654 }
655 }
656 if (str->len == 0)
657 return NULL;
658 g_string_truncate (str, str->len - 1);
659 return g_string_free (str, FALSE);
660}
661
Richard Hughesdf7950b2016-01-31 10:18:40 +0000662static void
663fu_main_vendor_quirk_release_version (AsApp *app)
664{
665 AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET;
666 GPtrArray *releases;
Richard Hughesdf7950b2016-01-31 10:18:40 +0000667
668 /* no quirk required */
Richard Hughes65dfbfe2016-03-02 13:42:53 +0000669 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
Richard Hughesdf7950b2016-01-31 10:18:40 +0000670 return;
671
Richard Hughesf192bf02016-07-22 08:26:43 +0100672 for (guint i = 0; quirk_table[i].identifier != NULL; i++) {
Mario Limonciello918f3932016-02-03 12:47:23 -0600673 if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier))
674 flags = quirk_table[i].flags;
Richard Hughesf192bf02016-07-22 08:26:43 +0100675 }
Richard Hughesdf7950b2016-01-31 10:18:40 +0000676
677 /* fix each release */
678 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100679 for (guint i = 0; i < releases->len; i++) {
Richard Hughesdf7950b2016-01-31 10:18:40 +0000680 AsRelease *rel;
681 const gchar *version;
682 guint64 ver_uint32;
683 g_autofree gchar *version_new = NULL;
684
685 rel = g_ptr_array_index (releases, i);
686 version = as_release_get_version (rel);
687 if (version == NULL)
688 continue;
689 if (g_strstr_len (version, -1, ".") != NULL)
690 continue;
691
692 /* metainfo files use hex and the LVFS uses decimal */
693 if (g_str_has_prefix (version, "0x")) {
694 ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16);
695 } else {
696 ver_uint32 = g_ascii_strtoull (version, NULL, 10);
697 }
698 if (ver_uint32 == 0)
699 continue;
700
701 /* convert to dotted decimal */
Richard Hughes33a518a2016-07-27 15:22:53 +0100702 version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags);
Richard Hughesdf7950b2016-01-31 10:18:40 +0000703 as_release_set_version (rel, version_new);
704 }
705}
706
Richard Hughes99147f12016-05-17 09:35:04 +0100707static AsApp *
708fu_main_store_get_app_by_guids (AsStore *store, FuDevice *device)
709{
Richard Hughesf192bf02016-07-22 08:26:43 +0100710 GPtrArray *guids = fu_device_get_guids (device);
711 for (guint i = 0; i < guids->len; i++) {
Richard Hughes99147f12016-05-17 09:35:04 +0100712 AsApp *app = NULL;
713 app = as_store_get_app_by_provide (store,
714 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
715 g_ptr_array_index (guids, i));
716 if (app != NULL)
717 return app;
718 }
719 return NULL;
720}
721
Richard Hughes01b9a832016-08-16 17:59:32 +0100722static AsScreenshot *
723_as_app_get_screenshot_default (AsApp *app)
724{
725 GPtrArray *array = as_app_get_screenshots (app);
726 if (array->len == 0)
727 return NULL;
728 return g_ptr_array_index (array, 0);
729}
730
Richard Hughes67ec8982015-03-03 11:39:27 +0000731static gboolean
Richard Hughes3ab17e62016-07-04 12:37:22 +0100732fu_main_update_helper_for_device (FuMainAuthHelper *helper,
733 FuDevice *device,
734 GError **error)
Richard Hughes67ec8982015-03-03 11:39:27 +0000735{
Richard Hughes5d14def2015-10-07 17:43:10 +0100736 AsApp *app;
737 AsChecksum *csum_tmp;
738 AsRelease *rel;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100739 GBytes *blob_fw;
Richard Hughesdef31752015-03-04 19:26:54 +0000740 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000741 const gchar *version;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100742 gboolean is_downgrade;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100743 gint vercmp;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100744
745 /* find from guid */
746 app = fu_main_store_get_app_by_guids (helper->store, device);
747 if (app == NULL) {
748 g_autofree gchar *guid = NULL;
749 guid = fu_main_get_guids_from_store (helper->store);
750 g_set_error (error,
751 FWUPD_ERROR,
752 FWUPD_ERROR_INVALID_FILE,
753 "firmware is not for this hw: required %s got %s",
754 fu_device_get_guid_default (device), guid);
755 return FALSE;
756 }
757
758 /* parse the DriverVer */
759 rel = as_app_get_release_default (app);
760 if (rel == NULL) {
761 g_set_error_literal (error,
762 FWUPD_ERROR,
763 FWUPD_ERROR_INVALID_FILE,
764 "no releases in the firmware component");
765 return FALSE;
766 }
767
Richard Hughes01b9a832016-08-16 17:59:32 +0100768 /* no update abilities */
769 if (!fu_device_has_flag (device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
770 !fu_device_has_flag (device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
771 const gchar *caption = NULL;
772 AsScreenshot *ss = _as_app_get_screenshot_default (app);
773 if (ss != NULL)
774 caption = as_screenshot_get_caption (ss, NULL);
775 if (caption != NULL) {
776 g_set_error (error,
777 FWUPD_ERROR,
778 FWUPD_ERROR_INTERNAL,
779 "Device %s does not currently allow updates: %s",
780 fu_device_get_id (device), caption);
781 } else {
782 g_set_error (error,
783 FWUPD_ERROR,
784 FWUPD_ERROR_INTERNAL,
785 "Device %s does not currently allow updates",
786 fu_device_get_id (device));
787 }
788 return FALSE;
789 }
790
Richard Hughes3ab17e62016-07-04 12:37:22 +0100791 /* get the blob */
792 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
793 tmp = as_checksum_get_filename (csum_tmp);
794 if (tmp == NULL) {
795 g_set_error_literal (error,
796 FWUPD_ERROR,
797 FWUPD_ERROR_INVALID_FILE,
798 "no checksum filename");
799 return FALSE;
800 }
801
802 /* not all devices have to use the same blob */
803 blob_fw = as_release_get_blob (rel, tmp);
804 if (blob_fw == NULL) {
805 g_set_error_literal (error,
806 FWUPD_ERROR,
807 FWUPD_ERROR_READ,
808 "failed to get firmware blob");
809 return FALSE;
810 }
811
812 /* possibly convert the version from 0x to dotted */
813 fu_main_vendor_quirk_release_version (app);
814
815 version = as_release_get_version (rel);
816 fu_device_set_update_version (device, version);
817
818 /* compare to the lowest supported version, if it exists */
819 tmp = fu_device_get_version_lowest (device);
820 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
821 g_set_error (error,
822 FWUPD_ERROR,
823 FWUPD_ERROR_VERSION_NEWER,
824 "Specified firmware is older than the minimum "
825 "required version '%s < %s'", tmp, version);
826 return FALSE;
827 }
828
829 /* check the device is locked */
830 if (fu_device_has_flag (device, FU_DEVICE_FLAG_LOCKED)) {
831 g_set_error (error,
832 FWUPD_ERROR,
833 FWUPD_ERROR_INTERNAL,
834 "Device %s is locked",
835 fu_device_get_id (device));
836 return FALSE;
837 }
838
839 /* compare the versions of what we have installed */
840 tmp = fu_device_get_version (device);
841 if (tmp == NULL) {
842 g_set_error (error,
843 FWUPD_ERROR,
844 FWUPD_ERROR_INTERNAL,
845 "Device %s does not yet have a current version",
846 fu_device_get_id (device));
847 return FALSE;
848 }
849 vercmp = as_utils_vercmp (tmp, version);
850 if (vercmp == 0 && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
851 g_set_error (error,
852 FWUPD_ERROR,
853 FWUPD_ERROR_VERSION_SAME,
854 "Specified firmware is already installed '%s'",
855 tmp);
856 return FALSE;
857 }
858 is_downgrade = vercmp > 0;
859 if (is_downgrade && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
860 g_set_error (error,
861 FWUPD_ERROR,
862 FWUPD_ERROR_VERSION_NEWER,
863 "Specified firmware is older than installed '%s < %s'",
864 tmp, version);
865 return FALSE;
866 }
867
868 /* if any downgrade, we want the global to be true */
869 if (is_downgrade)
870 helper->is_downgrade = is_downgrade;
871
872 /* verify */
873 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
874 return FALSE;
875
876 /* success */
877 g_ptr_array_add (helper->blob_fws, g_bytes_ref (blob_fw));
878 return TRUE;
879}
880
881static gboolean
882fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
883{
Richard Hughesc8646af2016-07-04 13:04:27 +0100884 g_autoptr(GError) error_first = NULL;
Richard Hughes67ec8982015-03-03 11:39:27 +0000885
Richard Hughes5d14def2015-10-07 17:43:10 +0100886 /* load store file which also decompresses firmware */
887 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
888 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000889 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000890
Richard Hughesbd405282016-07-04 13:00:54 +0100891 /* we've specified a specific device; failure is critical */
892 if (helper->devices->len > 0) {
Richard Hughesf192bf02016-07-22 08:26:43 +0100893 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100894 FuDevice *device = g_ptr_array_index (helper->devices, i);
895 if (!fu_main_update_helper_for_device (helper, device, error))
896 return FALSE;
Richard Hughes5d14def2015-10-07 17:43:10 +0100897 }
Richard Hughesbd405282016-07-04 13:00:54 +0100898 return TRUE;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100899 }
Richard Hughes5d14def2015-10-07 17:43:10 +0100900
Richard Hughesbd405282016-07-04 13:00:54 +0100901 /* if we've not chosen a device, try and find anything in the
902 * cabinet 'store' that matches any installed device and is updatable */
Richard Hughesf192bf02016-07-22 08:26:43 +0100903 for (guint i = 0; i < helper->priv->devices->len; i++) {
Richard Hughesbd405282016-07-04 13:00:54 +0100904 AsApp *app;
905 FuDeviceItem *item;
906 g_autoptr(GError) error_local = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +0100907
Richard Hughesbd405282016-07-04 13:00:54 +0100908 /* guid found */
909 item = g_ptr_array_index (helper->priv->devices, i);
910 app = fu_main_store_get_app_by_guids (helper->store, item->device);
911 if (app == NULL)
912 continue;
913
914 /* try this device, error not fatal */
915 if (!fu_main_update_helper_for_device (helper,
916 item->device,
917 &error_local)) {
918 g_debug ("failed to add %s: %s",
919 fu_device_get_id (item->device),
920 error_local->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100921
922 /* save this for later */
923 if (error_first == NULL)
924 error_first = g_error_copy (error_local);
Richard Hughesbd405282016-07-04 13:00:54 +0100925 continue;
926 }
927
928 /* success */
929 g_ptr_array_add (helper->devices, g_object_ref (item->device));
930 }
Richard Hughes346ea882016-07-04 12:31:06 +0100931 if (helper->devices->len == 0) {
Richard Hughesc8646af2016-07-04 13:04:27 +0100932 if (error_first != NULL) {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100933 g_set_error_literal (error,
934 FWUPD_ERROR,
935 FWUPD_ERROR_INVALID_FILE,
936 error_first->message);
Richard Hughesc8646af2016-07-04 13:04:27 +0100937 } else {
Richard Hughes4fd38c82016-08-16 17:57:30 +0100938 g_autofree gchar *guid = NULL;
939 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesc8646af2016-07-04 13:04:27 +0100940 g_set_error (error,
941 FWUPD_ERROR,
942 FWUPD_ERROR_INVALID_FILE,
943 "no attached hardware matched %s",
944 guid);
945 }
Richard Hughes346ea882016-07-04 12:31:06 +0100946 return FALSE;
947 }
948
Richard Hughesa4a2c182016-06-29 10:37:05 +0100949 /* sanity check */
950 if (helper->devices->len != helper->blob_fws->len) {
951 g_set_error (error,
952 FWUPD_ERROR,
953 FWUPD_ERROR_INTERNAL,
Richard Hughes33a518a2016-07-27 15:22:53 +0100954 "not enough firmware blobs (%u) for devices (%u)",
Richard Hughesa4a2c182016-06-29 10:37:05 +0100955 helper->blob_fws->len,
956 helper->devices->len);
957 return FALSE;
958 }
959
Richard Hughes67ec8982015-03-03 11:39:27 +0000960 return TRUE;
961}
962
Richard Hughes18423292015-03-09 17:10:50 +0000963static guint
964fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
965{
966 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +0100967 g_autoptr(GError) error = NULL;
968 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +0000969
970 if (priv->proxy_uid == NULL)
971 return G_MAXUINT;
972 value = g_dbus_proxy_call_sync (priv->proxy_uid,
973 "GetConnectionUnixUser",
974 g_variant_new ("(s)", sender),
975 G_DBUS_CALL_FLAGS_NONE,
976 -1,
977 NULL,
978 &error);
979 if (value == NULL) {
980 g_warning ("Failed to get uid for %s: %s",
981 sender, error->message);
982 return G_MAXUINT;
983 }
984 g_variant_get (value, "(u)", &uid);
985 return uid;
986}
987
Richard Hughes0e883ee2015-03-18 17:22:33 +0000988static FuDeviceItem *
989fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
990{
991 FuDevice *dev;
992 FuProvider *provider;
993 FuDeviceItem *item = NULL;
Richard Hughes8e9762d2016-03-17 10:14:15 +0000994 FwupdUpdateState update_state;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000995 const gchar *tmp;
Richard Hughes46832432015-09-11 13:43:15 +0100996 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000997
998 /* not a wildcard */
999 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1000 item = fu_main_get_item_by_id (priv, id);
1001 if (item == NULL) {
1002 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001003 FWUPD_ERROR,
1004 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001005 "no suitable device found for %s", id);
1006 }
1007 return item;
1008 }
1009
1010 /* allow '*' for any */
1011 devices = fu_pending_get_devices (priv->pending, error);
1012 if (devices == NULL)
1013 return NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01001014 for (guint i = 0; i < devices->len; i++) {
Richard Hughes0e883ee2015-03-18 17:22:33 +00001015 dev = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +00001016 update_state = fu_device_get_update_state (dev);
1017 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001018 continue;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001019 if (update_state == FWUPD_UPDATE_STATE_PENDING)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001020 continue;
1021
1022 /* if the device is not still connected, fake a FuDeviceItem */
1023 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
1024 if (item == NULL) {
Richard Hughes8e9762d2016-03-17 10:14:15 +00001025 tmp = fu_device_get_provider (dev);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001026 provider = fu_main_get_provider_by_name (priv, tmp);
1027 if (provider == NULL) {
1028 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001029 FWUPD_ERROR,
1030 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001031 "no provider %s found", tmp);
1032 }
1033 item = g_new0 (FuDeviceItem, 1);
1034 item->device = g_object_ref (dev);
1035 item->provider = g_object_ref (provider);
1036 g_ptr_array_add (priv->devices, item);
1037
1038 /* FIXME: just a boolean on FuDeviceItem? */
1039 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
1040 }
1041 break;
1042 }
1043
1044 /* no device found */
1045 if (item == NULL) {
1046 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001047 FWUPD_ERROR,
1048 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001049 "no suitable devices found");
1050 }
1051 return item;
1052}
1053
Richard Hughes63bbbf52015-04-14 16:12:16 +01001054static const gchar *
1055fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
1056{
Richard Hughesfe5cc902016-06-29 10:00:00 +01001057 gboolean all_removable = TRUE;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001058 gboolean is_trusted;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001059
1060 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +01001061 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001062
Richard Hughesfe5cc902016-06-29 10:00:00 +01001063 /* any non-removable means false */
Richard Hughesf192bf02016-07-22 08:26:43 +01001064 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001065 FuDevice *device = g_ptr_array_index (helper->devices, i);
1066 if (fu_device_has_flag (device, FU_DEVICE_FLAG_INTERNAL)) {
1067 all_removable = FALSE;
1068 break;
1069 }
1070 }
1071
Richard Hughes63bbbf52015-04-14 16:12:16 +01001072 /* relax authentication checks for removable devices */
Richard Hughesfe5cc902016-06-29 10:00:00 +01001073 if (all_removable) {
Richard Hughesdb468ee2016-06-29 10:10:47 +01001074 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001075 return "org.freedesktop.fwupd.downgrade-hotplug";
1076 if (is_trusted)
1077 return "org.freedesktop.fwupd.update-hotplug-trusted";
1078 return "org.freedesktop.fwupd.update-hotplug";
1079 }
1080
1081 /* internal device */
Richard Hughesdb468ee2016-06-29 10:10:47 +01001082 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001083 return "org.freedesktop.fwupd.downgrade-internal";
1084 if (is_trusted)
1085 return "org.freedesktop.fwupd.update-internal-trusted";
1086 return "org.freedesktop.fwupd.update-internal";
1087}
1088
Richard Hughesae0efdc2015-06-24 16:18:29 +01001089static gboolean
Mario Limonciello3ed54472015-07-23 13:19:39 -05001090fu_main_daemon_update_metadata (FuMainPrivate *priv, gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +01001091{
Richard Hughesf2fca012015-10-30 08:44:44 +00001092 const guint8 *data;
Richard Hughesf2fca012015-10-30 08:44:44 +00001093 gsize size;
Richard Hughes727664f2015-10-27 09:56:04 +00001094 GPtrArray *apps;
Richard Hughesbb840ce2015-10-30 08:47:24 +00001095 g_autofree gchar *xml = NULL;
Richard Hughes727664f2015-10-27 09:56:04 +00001096 g_autoptr(AsStore) store = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001097 g_autoptr(GBytes) bytes = NULL;
1098 g_autoptr(GBytes) bytes_raw = NULL;
1099 g_autoptr(GBytes) bytes_sig = NULL;
1100 g_autoptr(FuKeyring) kr = NULL;
1101 g_autoptr(GConverter) converter = NULL;
1102 g_autoptr(GFile) file = NULL;
1103 g_autoptr(GInputStream) stream_buf = NULL;
1104 g_autoptr(GInputStream) stream_fd = NULL;
1105 g_autoptr(GInputStream) stream = NULL;
1106 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001107
Richard Hughesae0efdc2015-06-24 16:18:29 +01001108 /* read the entire file into memory */
1109 stream_fd = g_unix_input_stream_new (fd, TRUE);
1110 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1111 if (bytes_raw == NULL)
1112 return FALSE;
1113 stream_buf = g_memory_input_stream_new ();
1114 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
1115
1116 /* peek the file type and get data */
Richard Hughesf2fca012015-10-30 08:44:44 +00001117 data = g_bytes_get_data (bytes_raw, &size);
1118 if (size < 2) {
1119 g_set_error_literal (error,
1120 FWUPD_ERROR,
1121 FWUPD_ERROR_INVALID_FILE,
1122 "file is too small");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001123 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001124 }
1125 if (data[0] == 0x1f && data[1] == 0x8b) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001126 g_debug ("using GZip decompressor for data");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001127 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
1128 stream = g_converter_input_stream_new (stream_buf, converter);
1129 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
1130 if (bytes == NULL)
1131 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001132 } else if (data[0] == '<' && data[1] == '?') {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001133 g_debug ("using no decompressor for data");
1134 bytes = g_bytes_ref (bytes_raw);
1135 } else {
1136 g_set_error (error,
1137 FWUPD_ERROR,
1138 FWUPD_ERROR_INVALID_FILE,
1139 "file type '0x%02x,0x%02x' not supported",
Richard Hughesf2fca012015-10-30 08:44:44 +00001140 data[0], data[1]);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001141 return FALSE;
1142 }
1143
1144 /* read signature */
1145 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1146 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1147 if (bytes_sig == NULL)
1148 return FALSE;
1149
1150 /* verify file */
1151 kr = fu_keyring_new ();
1152 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
1153 return FALSE;
1154 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
1155 return FALSE;
1156
Richard Hughes727664f2015-10-27 09:56:04 +00001157 /* load the store locally until we know it is valid */
1158 store = as_store_new ();
Richard Hughesbb840ce2015-10-30 08:47:24 +00001159 data = g_bytes_get_data (bytes, &size);
1160 xml = g_strndup ((const gchar *) data, size);
1161 if (!as_store_from_xml (store, xml, NULL, error))
Richard Hughesae0efdc2015-06-24 16:18:29 +01001162 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001163
Richard Hughes727664f2015-10-27 09:56:04 +00001164 /* add the new application from the store */
1165 as_store_remove_all (priv->store);
1166 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +01001167 for (guint i = 0; i < apps->len; i++) {
Richard Hughes727664f2015-10-27 09:56:04 +00001168 AsApp *app = g_ptr_array_index (apps, i);
1169 as_store_add_app (priv->store, app);
1170 }
1171
Richard Hughesae0efdc2015-06-24 16:18:29 +01001172 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +01001173 as_store_set_api_version (priv->store, 0.9);
1174 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
1175 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +01001176 AS_NODE_TO_XML_FLAG_ADD_HEADER |
1177 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
1178 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
1179 NULL, error)) {
1180 return FALSE;
1181 }
1182
1183 return TRUE;
1184}
1185
Richard Hughes033ccba2015-09-10 14:51:28 +01001186static gboolean
1187fu_main_store_delay_cb (gpointer user_data)
1188{
Richard Hughes033ccba2015-09-10 14:51:28 +01001189 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf192bf02016-07-22 08:26:43 +01001190 GPtrArray *apps;
Richard Hughes033ccba2015-09-10 14:51:28 +01001191
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001192 /* print what we've got */
Richard Hughes033ccba2015-09-10 14:51:28 +01001193 apps = as_store_get_apps (priv->store);
Richard Hughes0a36f442016-01-06 14:57:20 +00001194 if (apps->len == 0) {
1195 g_debug ("no devices in store");
1196 } else {
1197 g_debug ("devices now in store:");
Richard Hughesf192bf02016-07-22 08:26:43 +01001198 for (guint i = 0; i < apps->len; i++) {
1199 AsApp *app = g_ptr_array_index (apps, i);
Richard Hughes33a518a2016-07-27 15:22:53 +01001200 g_debug ("%u\t%s\t%s", i + 1,
Richard Hughes0a36f442016-01-06 14:57:20 +00001201 as_app_get_id (app),
1202 as_app_get_name (app, NULL));
1203 }
Richard Hughes033ccba2015-09-10 14:51:28 +01001204 }
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001205
1206 /* are any devices now supported? */
Richard Hughesf192bf02016-07-22 08:26:43 +01001207 for (guint i = 0; i < priv->devices->len; i++) {
1208 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001209 if (fu_main_get_updates_item_update (priv, item))
1210 fu_main_emit_device_changed (priv, item->device);
1211 }
1212
Richard Hughes033ccba2015-09-10 14:51:28 +01001213 priv->store_changed_id = 0;
1214 return G_SOURCE_REMOVE;
1215}
1216
Richard Hughes033ccba2015-09-10 14:51:28 +01001217static void
1218fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
1219{
1220 if (priv->store_changed_id != 0)
1221 return;
1222 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
1223}
1224
Richard Hughes654f6b82016-04-25 12:29:48 +01001225static gboolean
1226fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item)
1227{
1228 AsApp *app;
1229 AsChecksum *csum;
1230 AsRelease *rel;
1231 GPtrArray *releases;
1232 const gchar *tmp;
1233 const gchar *version;
Richard Hughes654f6b82016-04-25 12:29:48 +01001234 g_autoptr(GPtrArray) updates_list = NULL;
1235
1236 /* get device version */
1237 version = fu_device_get_version (item->device);
1238 if (version == NULL)
1239 return FALSE;
1240
Richard Hughes99147f12016-05-17 09:35:04 +01001241 /* match the GUIDs in the XML */
1242 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughes654f6b82016-04-25 12:29:48 +01001243 if (app == NULL)
1244 return FALSE;
1245
1246 /* possibly convert the version from 0x to dotted */
1247 fu_main_vendor_quirk_release_version (app);
1248
1249 /* get latest release */
1250 rel = as_app_get_release_default (app);
1251 if (rel == NULL) {
1252 g_debug ("%s has no firmware update metadata",
1253 fu_device_get_id (item->device));
1254 return FALSE;
1255 }
1256
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001257 /* supported in metadata */
1258 fwupd_result_add_device_flag (FWUPD_RESULT (item->device),
1259 FU_DEVICE_FLAG_SUPPORTED);
1260
Richard Hughes654f6b82016-04-25 12:29:48 +01001261 /* check if actually newer than what we have installed */
1262 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0) {
1263 g_debug ("%s has no firmware updates",
1264 fu_device_get_id (item->device));
1265 return FALSE;
1266 }
1267
Richard Hughes6561c8d2016-05-17 16:24:09 +01001268 /* only show devices that can be updated */
1269 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_OFFLINE) &&
1270 !fu_device_has_flag (item->device, FU_DEVICE_FLAG_ALLOW_ONLINE)) {
1271 g_debug ("ignoring %s as not updatable live or offline",
1272 fu_device_get_id (item->device));
1273 return FALSE;
1274 }
1275
Richard Hughes654f6b82016-04-25 12:29:48 +01001276 /* can we only do this on AC power */
1277 if (fu_device_has_flag (item->device, FU_DEVICE_FLAG_REQUIRE_AC) &&
1278 fu_main_on_battery (priv)) {
1279 g_debug ("ignoring update for %s as not on AC power",
1280 fu_device_get_id (item->device));
1281 return FALSE;
1282 }
1283
1284 /* add application metadata */
1285 fu_device_set_update_id (item->device, as_app_get_id (app));
1286 tmp = as_app_get_developer_name (app, NULL);
1287 if (tmp != NULL)
1288 fu_device_set_update_vendor (item->device, tmp);
1289 tmp = as_app_get_name (app, NULL);
1290 if (tmp != NULL)
1291 fu_device_set_update_name (item->device, tmp);
1292 tmp = as_app_get_comment (app, NULL);
1293 if (tmp != NULL)
1294 fu_device_set_update_summary (item->device, tmp);
1295 tmp = as_app_get_description (app, NULL);
1296 if (tmp != NULL)
1297 fu_device_set_description (item->device, tmp);
1298 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
1299 if (tmp != NULL)
1300 fu_device_set_update_homepage (item->device, tmp);
1301 tmp = as_app_get_project_license (app);
1302 if (tmp != NULL)
1303 fu_device_set_update_license (item->device, tmp);
1304
1305 /* add release information */
1306 tmp = as_release_get_version (rel);
1307 if (tmp != NULL)
1308 fu_device_set_update_version (item->device, tmp);
1309 csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTAINER);
1310 if (csum != NULL) {
1311 fu_device_set_update_checksum (item->device,
1312 as_checksum_get_value (csum));
1313 }
1314 tmp = as_release_get_location_default (rel);
1315 if (tmp != NULL)
1316 fu_device_set_update_uri (item->device, tmp);
1317
1318 /* get the list of releases newer than the one installed */
1319 updates_list = g_ptr_array_new ();
1320 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001321 for (guint i = 0; i < releases->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001322 rel = g_ptr_array_index (releases, i);
Richard Hughesae874382016-08-17 14:23:05 +01001323 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0)
Richard Hughes654f6b82016-04-25 12:29:48 +01001324 continue;
1325 tmp = as_release_get_description (rel, NULL);
1326 if (tmp == NULL)
1327 continue;
1328 g_ptr_array_add (updates_list, rel);
1329 }
1330
1331 /* no prefix on each release */
1332 if (updates_list->len == 1) {
1333 rel = g_ptr_array_index (updates_list, 0);
1334 fu_device_set_update_description (item->device,
1335 as_release_get_description (rel, NULL));
1336 } else {
1337 g_autoptr(GString) update_desc = NULL;
1338 update_desc = g_string_new ("");
1339
1340 /* get the descriptions with a version prefix */
Richard Hughesf192bf02016-07-22 08:26:43 +01001341 for (guint i = 0; i < updates_list->len; i++) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001342 rel = g_ptr_array_index (updates_list, i);
1343 g_string_append_printf (update_desc,
1344 "<p>%s:</p>%s",
1345 as_release_get_version (rel),
1346 as_release_get_description (rel, NULL));
1347 }
1348 if (update_desc->len > 0)
1349 fu_device_set_update_description (item->device, update_desc->str);
1350 }
1351
1352 /* success */
1353 return TRUE;
1354}
1355
Richard Hughesf192bf02016-07-22 08:26:43 +01001356/* find any updates using the AppStream metadata */
Richard Hughes7708a0f2015-07-21 08:41:22 +01001357static GPtrArray *
1358fu_main_get_updates (FuMainPrivate *priv, GError **error)
1359{
Richard Hughesf192bf02016-07-22 08:26:43 +01001360 GPtrArray *updates = g_ptr_array_new ();
1361 for (guint i = 0; i < priv->devices->len; i++) {
1362 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes654f6b82016-04-25 12:29:48 +01001363 if (fu_main_get_updates_item_update (priv, item))
1364 g_ptr_array_add (updates, item);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001365 }
Richard Hughes7708a0f2015-07-21 08:41:22 +01001366 return updates;
1367}
1368
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001369static AsStore *
1370fu_main_get_store_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1371{
1372 g_autoptr(AsStore) store = NULL;
1373 g_autoptr(GBytes) blob_cab = NULL;
1374 g_autoptr(GError) error_local = NULL;
1375 g_autoptr(GInputStream) stream = NULL;
1376
1377 /* read the entire fd to a data blob */
1378 stream = g_unix_input_stream_new (fd, TRUE);
1379 blob_cab = g_input_stream_read_bytes (stream,
1380 FU_MAIN_FIRMWARE_SIZE_MAX,
1381 NULL, &error_local);
1382 if (blob_cab == NULL){
1383 g_set_error_literal (error,
1384 FWUPD_ERROR,
1385 FWUPD_ERROR_INVALID_FILE,
1386 error_local->message);
1387 return NULL;
1388 }
1389
1390 /* load file */
1391 store = as_store_new ();
1392 if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) {
1393 g_set_error_literal (error,
1394 FWUPD_ERROR,
1395 FWUPD_ERROR_INVALID_FILE,
1396 error_local->message);
1397 return NULL;
1398 }
1399 return g_steal_pointer (&store);
1400}
1401
1402static FwupdResult *
1403fu_main_get_result_from_app (FuMainPrivate *priv, AsApp *app, GError **error)
1404{
1405 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
1406 AsRelease *rel;
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001407 AsChecksum * csum_tmp;
1408 const gchar *fn;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001409 GPtrArray *provides;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001410 g_autoptr(FwupdResult) res = NULL;
1411
1412 res = fwupd_result_new ();
1413 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001414 for (guint i = 0; i < provides->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001415 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1416 FuDeviceItem *item;
1417 const gchar *guid;
1418
1419 /* not firmware */
1420 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
1421 continue;
1422
1423 /* is a online or offline update appropriate */
1424 guid = as_provide_get_value (prov);
1425 if (guid == NULL)
1426 continue;
1427 item = fu_main_get_item_by_guid (priv, guid);
1428 if (item != NULL) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001429 fwupd_result_set_device_flags (res, fu_device_get_flags (item->device));
1430 fwupd_result_set_device_id (res, fu_device_get_id (item->device));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001431 }
1432
1433 /* add GUID */
1434 fwupd_result_add_guid (res, guid);
1435 }
1436 if (fwupd_result_get_guids(res)->len == 0) {
1437 g_set_error_literal (error,
1438 FWUPD_ERROR,
1439 FWUPD_ERROR_INTERNAL,
1440 "component has no GUIDs");
1441 return NULL;
1442 }
1443
1444 /* verify trust */
1445 rel = as_app_get_release_default (app);
1446 if (!fu_main_get_release_trust_flags (rel, &trust_flags, error))
1447 return NULL;
1448
1449 /* possibly convert the version from 0x to dotted */
1450 fu_main_vendor_quirk_release_version (app);
1451
1452 /* create a result with all the metadata in */
1453 fwupd_result_set_device_description (res, as_app_get_description (app, NULL));
Richard Hughes4921bd92016-06-27 11:17:43 +01001454 fwupd_result_set_update_id (res, as_app_get_id (app));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001455 fwupd_result_set_update_description (res, as_release_get_description (rel, NULL));
1456 fwupd_result_set_update_homepage (res, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
1457 fwupd_result_set_update_license (res, as_app_get_project_license (app));
1458 fwupd_result_set_update_name (res, as_app_get_name (app, NULL));
1459 fwupd_result_set_update_size (res, as_release_get_size (rel, AS_SIZE_KIND_INSTALLED));
1460 fwupd_result_set_update_summary (res, as_app_get_comment (app, NULL));
1461 fwupd_result_set_update_trust_flags (res, trust_flags);
1462 fwupd_result_set_update_vendor (res, as_app_get_developer_name (app, NULL));
1463 fwupd_result_set_update_version (res, as_release_get_version (rel));
Mario Limonciello8eaadd02016-06-17 16:23:44 -05001464 csum_tmp = as_release_get_checksum_by_target (rel,
1465 AS_CHECKSUM_TARGET_CONTENT);
1466 fn = as_checksum_get_filename (csum_tmp);
1467 if (fn != NULL)
1468 fwupd_result_set_update_filename (res, fn);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001469 return g_steal_pointer (&res);
1470}
1471
1472static GVariant *
1473fu_main_get_details_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1474{
1475 AsApp *app = NULL;
1476 GPtrArray *apps;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001477 g_autoptr(AsStore) store = NULL;
1478 g_autoptr(FwupdResult) res = NULL;
1479
1480 store = fu_main_get_store_from_fd (priv, fd, error);
1481 if (store == NULL)
1482 return NULL;
1483
1484 /* get all apps */
1485 apps = as_store_get_apps (store);
1486 if (apps->len == 0) {
1487 g_set_error_literal (error,
1488 FWUPD_ERROR,
1489 FWUPD_ERROR_INVALID_FILE,
1490 "no components");
1491 return NULL;
1492 }
1493 if (apps->len > 1) {
1494 /* we've got a .cab file with multiple components,
1495 * so try to find the first thing that's installed */
Richard Hughesf192bf02016-07-22 08:26:43 +01001496 for (guint i = 0; i < priv->devices->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001497 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
1498 app = fu_main_store_get_app_by_guids (store, item->device);
1499 if (app != NULL)
1500 break;
1501 }
1502 }
1503
1504 /* well, we've tried our best, just show the first entry */
1505 if (app == NULL)
1506 app = AS_APP (g_ptr_array_index (apps, 0));
1507
1508 /* create a result with all the metadata in */
1509 res = fu_main_get_result_from_app (priv, app, error);
1510 if (res == NULL)
1511 return NULL;
1512 return fwupd_result_to_data (res, "(a{sv})");
1513}
1514
Richard Hughes7289a6b2016-05-29 09:27:47 +01001515static GVariant *
1516fu_main_get_details_local_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1517{
1518 GPtrArray *apps;
1519 GVariantBuilder builder;
Richard Hughes7289a6b2016-05-29 09:27:47 +01001520 g_autoptr(AsStore) store = NULL;
1521
1522 store = fu_main_get_store_from_fd (priv, fd, error);
1523 if (store == NULL)
1524 return NULL;
1525
1526 /* get all apps */
1527 apps = as_store_get_apps (store);
1528 if (apps->len == 0) {
1529 g_set_error_literal (error,
1530 FWUPD_ERROR,
1531 FWUPD_ERROR_INVALID_FILE,
1532 "no components");
1533 return NULL;
1534 }
1535
1536 /* create results with all the metadata in */
1537 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +01001538 for (guint i = 0; i < apps->len; i++) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001539 g_autoptr(FwupdResult) res = NULL;
1540 AsApp *app = g_ptr_array_index (apps, i);
1541 GVariant *tmp;
1542 res = fu_main_get_result_from_app (priv, app, error);
1543 if (res == NULL)
1544 return NULL;
1545 tmp = fwupd_result_to_data (res, "{sa{sv}}");
1546 g_variant_builder_add_value (&builder, tmp);
1547 }
1548 return g_variant_new ("(a{sa{sv}})", &builder);
1549}
1550
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001551static void
1552fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
1553 const gchar *object_path, const gchar *interface_name,
1554 const gchar *method_name, GVariant *parameters,
1555 GDBusMethodInvocation *invocation, gpointer user_data)
1556{
1557 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1558 GVariant *val;
1559
1560 /* return 'as' */
1561 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +01001562 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001563 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001564 val = fu_main_device_array_to_variant (priv->devices, &error);
1565 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001566 if (g_error_matches (error,
1567 FWUPD_ERROR,
1568 FWUPD_ERROR_NOTHING_TO_DO)) {
1569 g_prefix_error (&error, "No detected devices: ");
1570 }
Richard Hughes7708a0f2015-07-21 08:41:22 +01001571 g_dbus_method_invocation_return_gerror (invocation, error);
1572 return;
1573 }
1574 g_dbus_method_invocation_return_value (invocation, val);
1575 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
1576 return;
1577 }
1578
1579 /* return 'as' */
1580 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +01001581 g_autoptr(GError) error = NULL;
1582 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +01001583 g_debug ("Called %s()", method_name);
1584 updates = fu_main_get_updates (priv, &error);
1585 if (updates == NULL) {
1586 g_dbus_method_invocation_return_gerror (invocation, error);
1587 return;
1588 }
1589 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001590 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001591 if (g_error_matches (error,
1592 FWUPD_ERROR,
1593 FWUPD_ERROR_NOTHING_TO_DO)) {
1594 g_prefix_error (&error, "No devices can be updated: ");
1595 }
Richard Hughes9a38c032015-03-17 20:58:46 +00001596 g_dbus_method_invocation_return_gerror (invocation, error);
1597 return;
1598 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001599 g_dbus_method_invocation_return_value (invocation, val);
Richard Hughesf910ac92015-03-19 10:43:42 +00001600 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001601 return;
1602 }
1603
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001604 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +00001605 if (g_strcmp0 (method_name, "ClearResults") == 0) {
1606 FuDeviceItem *item = NULL;
1607 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001608 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001609
1610 g_variant_get (parameters, "(&s)", &id);
1611 g_debug ("Called %s(%s)", method_name, id);
1612
1613 /* find device */
1614 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1615 if (item == NULL) {
1616 g_dbus_method_invocation_return_gerror (invocation, error);
1617 return;
1618 }
1619
1620 /* call into the provider */
1621 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
1622 g_dbus_method_invocation_return_gerror (invocation, error);
1623 return;
1624 }
1625
1626 /* success */
1627 g_dbus_method_invocation_return_value (invocation, NULL);
1628 return;
1629 }
1630
1631 /* return 'a{sv}' */
1632 if (g_strcmp0 (method_name, "GetResults") == 0) {
1633 FuDeviceItem *item = NULL;
1634 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001635 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001636
1637 g_variant_get (parameters, "(&s)", &id);
1638 g_debug ("Called %s(%s)", method_name, id);
1639
1640 /* find device */
1641 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1642 if (item == NULL) {
1643 g_dbus_method_invocation_return_gerror (invocation, error);
1644 return;
1645 }
1646
1647 /* call into the provider */
1648 if (!fu_provider_get_results (item->provider, item->device, &error)) {
1649 g_dbus_method_invocation_return_gerror (invocation, error);
1650 return;
1651 }
1652
1653 /* success */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001654 val = fwupd_result_to_data (FWUPD_RESULT (item->device), "(a{sv})");
Richard Hughes0e883ee2015-03-18 17:22:33 +00001655 g_dbus_method_invocation_return_value (invocation, val);
1656 return;
1657 }
1658
1659 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01001660 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
1661 GDBusMessage *message;
1662 GUnixFDList *fd_list;
1663 gint fd_data;
1664 gint fd_sig;
Richard Hughes46832432015-09-11 13:43:15 +01001665 g_autoptr(GError) error = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001666
1667 message = g_dbus_method_invocation_get_message (invocation);
1668 fd_list = g_dbus_message_get_unix_fd_list (message);
1669 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
1670 g_dbus_method_invocation_return_error (invocation,
1671 FWUPD_ERROR,
1672 FWUPD_ERROR_INTERNAL,
1673 "invalid handle");
1674 return;
1675 }
1676 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
1677 if (fd_data < 0) {
1678 g_dbus_method_invocation_return_gerror (invocation, error);
1679 return;
1680 }
1681 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
1682 if (fd_sig < 0) {
1683 g_dbus_method_invocation_return_gerror (invocation, error);
1684 return;
1685 }
Mario Limonciello3ed54472015-07-23 13:19:39 -05001686 if (!fu_main_daemon_update_metadata (priv, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001687 g_prefix_error (&error, "failed to update metadata: ");
1688 g_dbus_method_invocation_return_gerror (invocation, error);
1689 return;
1690 }
1691 g_dbus_method_invocation_return_value (invocation, NULL);
1692 return;
1693 }
1694
Richard Hughesa043c2e2015-06-29 08:43:18 +01001695 /* return 's' */
Richard Hughes9a410ce2016-02-28 15:58:54 +00001696 if (g_strcmp0 (method_name, "Unlock") == 0) {
1697 FuDeviceItem *item = NULL;
1698 FuMainAuthHelper *helper;
1699 const gchar *id = NULL;
1700 g_autoptr(PolkitSubject) subject = NULL;
1701
1702 /* check the id exists */
1703 g_variant_get (parameters, "(&s)", &id);
1704 g_debug ("Called %s(%s)", method_name, id);
1705 item = fu_main_get_item_by_id (priv, id);
1706 if (item == NULL) {
1707 g_dbus_method_invocation_return_error (invocation,
1708 FWUPD_ERROR,
1709 FWUPD_ERROR_NOT_FOUND,
1710 "No such device %s",
1711 id);
1712 return;
1713 }
1714
1715 /* check the device is locked */
Richard Hughes5bf28372016-03-29 18:43:54 +01001716 if (!fu_device_has_flag (item->device, FU_DEVICE_FLAG_LOCKED)) {
Richard Hughes9a410ce2016-02-28 15:58:54 +00001717 g_dbus_method_invocation_return_error (invocation,
1718 FWUPD_ERROR,
1719 FWUPD_ERROR_NOT_FOUND,
1720 "Device %s is not locked",
1721 id);
1722 return;
1723 }
1724
1725 /* process the firmware */
1726 helper = g_new0 (FuMainAuthHelper, 1);
1727 helper->auth_kind = FU_MAIN_AUTH_KIND_UNLOCK;
1728 helper->invocation = g_object_ref (invocation);
Richard Hughesfe5cc902016-06-29 10:00:00 +01001729 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001730 helper->priv = priv;
1731
Richard Hughesfe5cc902016-06-29 10:00:00 +01001732 /* FIXME: do we want to support "*"? */
1733 g_ptr_array_add (helper->devices, g_object_ref (item->device));
1734
Richard Hughes9a410ce2016-02-28 15:58:54 +00001735 /* authenticate */
1736 subject = polkit_system_bus_name_new (sender);
1737 polkit_authority_check_authorization (helper->priv->authority, subject,
1738 "org.freedesktop.fwupd.device-unlock",
1739 NULL,
1740 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1741 NULL,
1742 fu_main_check_authorization_cb,
1743 helper);
1744 return;
1745 }
1746
1747 /* return 's' */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001748 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001749 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001750 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001751 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001752 FuDeviceItem *item = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001753 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001754 const gchar *id = NULL;
1755 const gchar *version = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001756 g_autoptr(GError) error = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001757
1758 /* check the id exists */
1759 g_variant_get (parameters, "(&s)", &id);
1760 g_debug ("Called %s(%s)", method_name, id);
1761 item = fu_main_get_item_by_id (priv, id);
1762 if (item == NULL) {
1763 g_dbus_method_invocation_return_error (invocation,
1764 FWUPD_ERROR,
1765 FWUPD_ERROR_NOT_FOUND,
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001766 "No such device %s",
Richard Hughesa043c2e2015-06-29 08:43:18 +01001767 id);
1768 return;
1769 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001770
1771 /* set the device firmware hash */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001772 if (!fu_provider_verify (item->provider, item->device,
1773 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
1774 g_dbus_method_invocation_return_gerror (invocation, error);
1775 return;
1776 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001777
1778 /* find component in metadata */
Richard Hughes99147f12016-05-17 09:35:04 +01001779 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001780 if (app == NULL) {
1781 g_dbus_method_invocation_return_error (invocation,
1782 FWUPD_ERROR,
1783 FWUPD_ERROR_NOT_SUPPORTED,
1784 "No metadata");
1785 return;
1786 }
1787
1788 /* find version in metadata */
Richard Hughes8e9762d2016-03-17 10:14:15 +00001789 version = fu_device_get_version (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001790 release = as_app_get_release (app, version);
1791 if (release == NULL) {
1792 g_dbus_method_invocation_return_error (invocation,
1793 FWUPD_ERROR,
1794 FWUPD_ERROR_NOT_SUPPORTED,
1795 "No version %s",
1796 version);
1797 return;
1798 }
1799
1800 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001801 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
1802 if (csum == NULL) {
1803 g_dbus_method_invocation_return_error (invocation,
1804 FWUPD_ERROR,
1805 FWUPD_ERROR_NOT_SUPPORTED,
1806 "No content checksum for %s",
1807 version);
1808 return;
1809 }
Richard Hughes8e9762d2016-03-17 10:14:15 +00001810 hash = fu_device_get_checksum (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001811 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
1812 g_dbus_method_invocation_return_error (invocation,
1813 FWUPD_ERROR,
1814 FWUPD_ERROR_NOT_SUPPORTED,
1815 "For v%s expected %s, got %s",
1816 version,
1817 as_checksum_get_value (csum),
1818 hash);
1819 return;
1820 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001821 g_dbus_method_invocation_return_value (invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001822 return;
1823 }
1824
Richard Hughesae0efdc2015-06-24 16:18:29 +01001825 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01001826 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00001827 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001828 FuMainAuthHelper *helper;
Richard Hughes2d6e1862016-03-18 09:20:37 +00001829 FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE;
Richard Hughes74cc2172015-02-27 13:19:46 +00001830 GDBusMessage *message;
1831 GUnixFDList *fd_list;
1832 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00001833 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001834 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00001835 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001836 gint32 fd_handle = 0;
1837 gint fd;
Richard Hughes46832432015-09-11 13:43:15 +01001838 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +01001839 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001840 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001841 g_autoptr(GBytes) blob_cab = NULL;
1842 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001843
1844 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00001845 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00001846 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001847 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1848 item = fu_main_get_item_by_id (priv, id);
1849 if (item == NULL) {
1850 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001851 FWUPD_ERROR,
1852 FWUPD_ERROR_NOT_FOUND,
Richard Hughesd079b1a2015-03-06 10:09:55 +00001853 "no such device %s",
1854 id);
1855 return;
1856 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001857 }
1858
Richard Hughes74cc2172015-02-27 13:19:46 +00001859 /* get options */
1860 while (g_variant_iter_next (iter, "{&sv}",
1861 &prop_key, &prop_value)) {
1862 g_debug ("got option %s", prop_key);
1863 if (g_strcmp0 (prop_key, "offline") == 0 &&
1864 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001865 flags |= FWUPD_INSTALL_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00001866 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
1867 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001868 flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughese7c12642015-03-04 20:28:59 +00001869 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
1870 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00001871 flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Mario Limonciello71a5b982016-05-10 15:38:53 -05001872 if (g_strcmp0 (prop_key, "force") == 0 &&
1873 g_variant_get_boolean (prop_value) == TRUE)
1874 flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00001875 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00001876 }
1877
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001878 /* get the fd */
1879 message = g_dbus_method_invocation_get_message (invocation);
1880 fd_list = g_dbus_message_get_unix_fd_list (message);
1881 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1882 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001883 FWUPD_ERROR,
1884 FWUPD_ERROR_INTERNAL,
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001885 "invalid handle");
1886 return;
1887 }
1888 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1889 if (fd < 0) {
1890 g_dbus_method_invocation_return_gerror (invocation,
1891 error);
1892 return;
1893 }
1894
Richard Hughes5d14def2015-10-07 17:43:10 +01001895 /* read the entire fd to a data blob */
1896 stream = g_unix_input_stream_new (fd, TRUE);
1897 blob_cab = g_input_stream_read_bytes (stream,
1898 FU_MAIN_FIRMWARE_SIZE_MAX,
1899 NULL, &error);
1900 if (blob_cab == NULL){
1901 g_dbus_method_invocation_return_gerror (invocation,
1902 error);
1903 return;
1904 }
1905
Richard Hughes67ec8982015-03-03 11:39:27 +00001906 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00001907 helper = g_new0 (FuMainAuthHelper, 1);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001908 helper->auth_kind = FU_MAIN_AUTH_KIND_INSTALL;
Richard Hughesf508e762015-02-27 12:49:36 +00001909 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01001910 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
1911 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00001912 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00001913 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01001914 helper->store = as_store_new ();
Richard Hughesfe5cc902016-06-29 10:00:00 +01001915 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughesa4a2c182016-06-29 10:37:05 +01001916 helper->blob_fws = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001917 if (item != NULL)
Richard Hughesfe5cc902016-06-29 10:00:00 +01001918 g_ptr_array_add (helper->devices, g_object_ref (item->device));
Richard Hughes67ec8982015-03-03 11:39:27 +00001919 if (!fu_main_update_helper (helper, &error)) {
1920 g_dbus_method_invocation_return_gerror (helper->invocation,
1921 error);
Richard Hughesf910ac92015-03-19 10:43:42 +00001922 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes67ec8982015-03-03 11:39:27 +00001923 fu_main_helper_free (helper);
1924 return;
1925 }
1926
Richard Hughes18423292015-03-09 17:10:50 +00001927 /* is root */
1928 if (fu_main_dbus_get_uid (priv, sender) == 0) {
1929 if (!fu_main_provider_update_authenticated (helper, &error)) {
1930 g_dbus_method_invocation_return_gerror (invocation, error);
1931 } else {
1932 g_dbus_method_invocation_return_value (invocation, NULL);
1933 }
Richard Hughesf910ac92015-03-19 10:43:42 +00001934 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes18423292015-03-09 17:10:50 +00001935 fu_main_helper_free (helper);
1936 return;
1937 }
1938
Richard Hughes67ec8982015-03-03 11:39:27 +00001939 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01001940 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00001941 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00001942 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00001943 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00001944 NULL,
1945 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1946 NULL,
1947 fu_main_check_authorization_cb,
1948 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001949 return;
1950 }
1951
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001952 /* get a single result object from a local file */
Richard Hughescccc7752015-03-06 11:13:19 +00001953 if (g_strcmp0 (method_name, "GetDetails") == 0) {
1954 GDBusMessage *message;
1955 GUnixFDList *fd_list;
Richard Hughescccc7752015-03-06 11:13:19 +00001956 gint32 fd_handle = 0;
1957 gint fd;
Richard Hughes46832432015-09-11 13:43:15 +01001958 g_autoptr(GError) error = NULL;
Richard Hughescccc7752015-03-06 11:13:19 +00001959
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001960 /* get parameters */
Richard Hughescccc7752015-03-06 11:13:19 +00001961 g_variant_get (parameters, "(h)", &fd_handle);
1962 g_debug ("Called %s(%i)", method_name, fd_handle);
1963
1964 /* get the fd */
1965 message = g_dbus_method_invocation_get_message (invocation);
1966 fd_list = g_dbus_message_get_unix_fd_list (message);
1967 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1968 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001969 FWUPD_ERROR,
1970 FWUPD_ERROR_INTERNAL,
Richard Hughescccc7752015-03-06 11:13:19 +00001971 "invalid handle");
1972 return;
1973 }
1974 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1975 if (fd < 0) {
1976 g_dbus_method_invocation_return_gerror (invocation,
1977 error);
1978 return;
1979 }
1980
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001981 /* get details about the file */
1982 val = fu_main_get_details_from_fd (priv, fd, &error);
1983 if (val == NULL) {
Richard Hughes5d14def2015-10-07 17:43:10 +01001984 g_dbus_method_invocation_return_gerror (invocation,
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001985 error);
1986 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughescccc7752015-03-06 11:13:19 +00001987 return;
1988 }
Richard Hughescccc7752015-03-06 11:13:19 +00001989 g_dbus_method_invocation_return_value (invocation, val);
1990 return;
1991 }
1992
Richard Hughes7289a6b2016-05-29 09:27:47 +01001993 /* get multiple result objects from a local file */
1994 if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) {
1995 GDBusMessage *message;
1996 GUnixFDList *fd_list;
1997 gint32 fd_handle = 0;
1998 gint fd;
1999 g_autoptr(GError) error = NULL;
2000
2001 /* get parameters */
2002 g_variant_get (parameters, "(h)", &fd_handle);
2003 g_debug ("Called %s(%i)", method_name, fd_handle);
2004
2005 /* get the fd */
2006 message = g_dbus_method_invocation_get_message (invocation);
2007 fd_list = g_dbus_message_get_unix_fd_list (message);
2008 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
2009 g_dbus_method_invocation_return_error (invocation,
2010 FWUPD_ERROR,
2011 FWUPD_ERROR_INTERNAL,
2012 "invalid handle");
2013 return;
2014 }
2015 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
2016 if (fd < 0) {
2017 g_dbus_method_invocation_return_gerror (invocation,
2018 error);
2019 return;
2020 }
2021
2022 /* get details about the file */
2023 val = fu_main_get_details_local_from_fd (priv, fd, &error);
2024 if (val == NULL) {
2025 g_dbus_method_invocation_return_gerror (invocation,
2026 error);
2027 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
2028 return;
2029 }
2030 g_dbus_method_invocation_return_value (invocation, val);
2031 return;
2032 }
2033
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002034 /* we suck */
2035 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00002036 G_DBUS_ERROR,
2037 G_DBUS_ERROR_UNKNOWN_METHOD,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002038 "no such method %s",
2039 method_name);
2040}
2041
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002042static GVariant *
2043fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
2044 const gchar *object_path, const gchar *interface_name,
2045 const gchar *property_name, GError **error,
2046 gpointer user_data)
2047{
Richard Hughes773ce982015-03-09 22:40:57 +00002048 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2049
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002050 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
2051 return g_variant_new_string (VERSION);
2052
Richard Hughes773ce982015-03-09 22:40:57 +00002053 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughes37e9d772016-04-28 14:23:24 +01002054 return g_variant_new_uint32 (priv->status);
Richard Hughes773ce982015-03-09 22:40:57 +00002055
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002056 /* return an error */
2057 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00002058 G_DBUS_ERROR,
2059 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002060 "failed to get daemon property %s",
2061 property_name);
2062 return NULL;
2063}
2064
Richard Hughesfd468842015-04-22 16:44:08 +01002065static void
2066fu_main_providers_coldplug (FuMainPrivate *priv)
2067{
Richard Hughes3f236502015-09-24 15:43:02 +01002068 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01002069
Richard Hughes3f236502015-09-24 15:43:02 +01002070 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughesf192bf02016-07-22 08:26:43 +01002071 for (guint i = 0; i < priv->providers->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01002072 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01002073 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01002074 FuProvider *provider = g_ptr_array_index (priv->providers, i);
Richard Hughes3f236502015-09-24 15:43:02 +01002075 ptask2 = as_profile_start (priv->profile,
2076 "FuMain:coldplug{%s}",
2077 fu_provider_get_name (provider));
Richard Hughesfd468842015-04-22 16:44:08 +01002078 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
2079 g_warning ("Failed to coldplug: %s", error->message);
2080 }
2081}
2082
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002083static void
2084fu_main_on_bus_acquired_cb (GDBusConnection *connection,
2085 const gchar *name,
2086 gpointer user_data)
2087{
2088 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2089 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01002090 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002091 static const GDBusInterfaceVTable interface_vtable = {
2092 fu_main_daemon_method_call,
2093 fu_main_daemon_get_property,
2094 NULL
2095 };
2096
2097 priv->connection = g_object_ref (connection);
2098 registration_id = g_dbus_connection_register_object (connection,
2099 FWUPD_DBUS_PATH,
2100 priv->introspection_daemon->interfaces[0],
2101 &interface_vtable,
2102 priv, /* user_data */
2103 NULL, /* user_data_free_func */
2104 NULL); /* GError** */
2105 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00002106
Richard Hughesfd468842015-04-22 16:44:08 +01002107 /* add devices */
2108 fu_main_providers_coldplug (priv);
2109
Richard Hughes18423292015-03-09 17:10:50 +00002110 /* connect to D-Bus directly */
2111 priv->proxy_uid =
2112 g_dbus_proxy_new_sync (priv->connection,
2113 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
2114 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2115 NULL,
2116 "org.freedesktop.DBus",
2117 "/org/freedesktop/DBus",
2118 "org.freedesktop.DBus",
2119 NULL,
2120 &error);
2121 if (priv->proxy_uid == NULL) {
2122 g_warning ("cannot connect to DBus: %s", error->message);
2123 return;
2124 }
Richard Hughes3f236502015-09-24 15:43:02 +01002125
Richard Hughes9559bbe2016-03-29 18:54:20 +01002126 /* connect to UPower */
2127 priv->proxy_upower =
2128 g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
2129 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2130 NULL,
2131 "org.freedesktop.UPower",
2132 "/org/freedesktop/UPower",
2133 "org.freedesktop.UPower",
2134 NULL,
2135 &error);
2136 if (priv->proxy_upower == NULL) {
2137 g_warning ("Failed to conect UPower: %s", error->message);
2138 return;
2139 }
2140
Richard Hughes3f236502015-09-24 15:43:02 +01002141 /* dump startup profile data */
Richard Hughes2a1e75d2015-12-18 17:42:53 +00002142 if (fu_debug_is_verbose ())
2143 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002144}
2145
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002146static void
2147fu_main_on_name_acquired_cb (GDBusConnection *connection,
2148 const gchar *name,
2149 gpointer user_data)
2150{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002151 g_debug ("FuMain: acquired name: %s", name);
2152}
2153
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002154static void
2155fu_main_on_name_lost_cb (GDBusConnection *connection,
2156 const gchar *name,
2157 gpointer user_data)
2158{
2159 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2160 g_debug ("FuMain: lost name: %s", name);
2161 g_main_loop_quit (priv->loop);
2162}
2163
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002164static gboolean
2165fu_main_timed_exit_cb (gpointer user_data)
2166{
2167 GMainLoop *loop = (GMainLoop *) user_data;
2168 g_main_loop_quit (loop);
2169 return G_SOURCE_REMOVE;
2170}
2171
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002172static GDBusNodeInfo *
2173fu_main_load_introspection (const gchar *filename, GError **error)
2174{
Richard Hughes46832432015-09-11 13:43:15 +01002175 g_autoptr(GBytes) data = NULL;
2176 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002177
2178 /* lookup data */
2179 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
2180 data = g_resource_lookup_data (fu_get_resource (),
2181 path,
2182 G_RESOURCE_LOOKUP_FLAGS_NONE,
2183 error);
2184 if (data == NULL)
2185 return NULL;
2186
2187 /* build introspection from XML */
2188 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
2189}
2190
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002191static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002192fu_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002193 FuDevice *device,
2194 gpointer user_data)
2195{
2196 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002197 FuDeviceItem *item;
Richard Hughesdad1e192016-03-13 09:56:54 +00002198 AsApp *app;
2199 FuPlugin *plugin;
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002200 g_auto(GStrv) guids = NULL;
Richard Hughesdad1e192016-03-13 09:56:54 +00002201 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00002202
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002203 /* is this GUID blacklisted */
2204 guids = g_key_file_get_string_list (priv->config,
2205 "fwupd",
2206 "BlacklistDevices",
2207 NULL, /* length */
2208 NULL);
2209 if (guids != NULL &&
2210 g_strv_contains ((const gchar * const *) guids,
2211 fu_device_get_guid_default (device))) {
2212 g_debug ("%s is blacklisted [%s], ignoring from %s",
2213 fu_device_get_id (device),
2214 fu_device_get_guid_default (device),
2215 fu_provider_get_name (provider));
2216 return;
2217 }
2218
Richard Hughes0e883ee2015-03-18 17:22:33 +00002219 /* remove any fake device */
2220 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
Richard Hughes5d057a82015-11-24 18:09:57 +00002221 if (item != NULL) {
2222 g_debug ("already added %s by %s, ignoring same device from %s",
2223 fu_device_get_id (item->device),
Richard Hughes8e9762d2016-03-17 10:14:15 +00002224 fu_device_get_provider (item->device),
Richard Hughes5d057a82015-11-24 18:09:57 +00002225 fu_provider_get_name (provider));
2226 return;
2227 }
Richard Hughes0e883ee2015-03-18 17:22:33 +00002228
2229 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00002230 item = g_new0 (FuDeviceItem, 1);
2231 item->device = g_object_ref (device);
2232 item->provider = g_object_ref (provider);
2233 g_ptr_array_add (priv->devices, item);
Richard Hughesdad1e192016-03-13 09:56:54 +00002234
2235 /* does this match anything in the AppStream data */
Richard Hughes99147f12016-05-17 09:35:04 +01002236 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesdad1e192016-03-13 09:56:54 +00002237 if (app != NULL) {
2238 const gchar *tmp;
2239 tmp = as_app_get_metadata_item (app, FU_DEVICE_KEY_FWUPD_PLUGIN);
2240 if (tmp != NULL) {
2241 g_debug ("setting plugin: %s", tmp);
2242 fu_device_set_metadata (item->device,
2243 FU_DEVICE_KEY_FWUPD_PLUGIN,
2244 tmp);
2245 }
2246 }
2247
2248 /* run any plugins */
2249 plugin = fu_main_get_plugin_for_device (priv->plugins, device);
2250 if (plugin != NULL) {
2251 if (!fu_plugin_run_device_probe (plugin, device, &error)) {
2252 g_warning ("failed to probe %s: %s",
2253 fu_device_get_id (item->device),
2254 error->message);
2255 }
2256 }
2257
Richard Hughes422e8662016-04-28 15:05:33 +01002258 /* match the metadata at this point so clients can tell if the
2259 * device is worthy */
2260 fu_main_get_updates_item_update (priv, item);
2261
Richard Hughes8ca33782016-04-28 15:04:31 +01002262 /* notify clients */
2263 fu_main_emit_device_added (priv, item->device);
Richard Hughesd7022b52015-03-11 19:47:06 +00002264 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002265}
2266
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002267static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002268fu_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002269 FuDevice *device,
2270 gpointer user_data)
2271{
2272 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002273 FuDeviceItem *item;
2274
2275 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
2276 if (item == NULL) {
Richard Hughes5d057a82015-11-24 18:09:57 +00002277 g_debug ("no device to remove %s", fu_device_get_id (device));
Richard Hughesf508e762015-02-27 12:49:36 +00002278 return;
2279 }
Richard Hughes5d057a82015-11-24 18:09:57 +00002280
2281 /* check this came from the same provider */
2282 if (g_strcmp0 (fu_provider_get_name (provider),
2283 fu_provider_get_name (item->provider)) != 0) {
2284 g_debug ("ignoring duplicate removal from %s",
2285 fu_provider_get_name (provider));
2286 return;
2287 }
2288
Richard Hughes8ca33782016-04-28 15:04:31 +01002289 /* make the UI update */
2290 fu_main_emit_device_removed (priv, device);
Richard Hughesf508e762015-02-27 12:49:36 +00002291 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00002292 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002293}
2294
Richard Hughes773ce982015-03-09 22:40:57 +00002295static void
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002296fu_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00002297 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00002298 gpointer user_data)
2299{
2300 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2301 fu_main_set_status (priv, status);
2302}
2303
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002304static void
Richard Hughes876c0072016-08-17 14:51:03 +01002305fu_main_provider_percentage_changed_cb (FuProvider *provider,
2306 guint percentage,
2307 gpointer user_data)
2308{
2309 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2310 fu_main_set_percentage (priv, percentage);
2311}
2312
2313static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002314fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
2315{
2316 g_signal_connect (provider, "device-added",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002317 G_CALLBACK (fu_main_provider_device_added_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002318 priv);
2319 g_signal_connect (provider, "device-removed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002320 G_CALLBACK (fu_main_provider_device_removed_cb),
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002321 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00002322 g_signal_connect (provider, "status-changed",
Mario Limoncielloc2cbd1a2016-06-22 14:59:29 -05002323 G_CALLBACK (fu_main_provider_status_changed_cb),
Richard Hughes773ce982015-03-09 22:40:57 +00002324 priv);
Richard Hughes876c0072016-08-17 14:51:03 +01002325 g_signal_connect (provider, "percentage-changed",
2326 G_CALLBACK (fu_main_provider_percentage_changed_cb),
2327 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002328 g_ptr_array_add (priv->providers, provider);
2329}
2330
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002331int
2332main (int argc, char *argv[])
2333{
2334 FuMainPrivate *priv = NULL;
2335 gboolean immediate_exit = FALSE;
2336 gboolean ret;
2337 gboolean timed_exit = FALSE;
2338 GOptionContext *context;
2339 guint owner_id = 0;
Richard Hughes33a518a2016-07-27 15:22:53 +01002340 gint retval = 1;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002341 const GOptionEntry options[] = {
2342 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
2343 /* TRANSLATORS: exit after we've started up, used for user profiling */
2344 _("Exit after a small delay"), NULL },
2345 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
2346 /* TRANSLATORS: exit straight away, used for automatic profiling */
2347 _("Exit after the engine has loaded"), NULL },
2348 { NULL}
2349 };
Richard Hughes46832432015-09-11 13:43:15 +01002350 g_autoptr(GError) error = NULL;
2351 g_autofree gchar *config_file = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002352
2353 setlocale (LC_ALL, "");
2354
2355 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
2356 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2357 textdomain (GETTEXT_PACKAGE);
2358
2359 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01002360 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002361 context = g_option_context_new (NULL);
2362 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002363 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00002364 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002365 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
2366 ret = g_option_context_parse (context, &argc, &argv, &error);
2367 if (!ret) {
2368 g_warning ("FuMain: failed to parse command line arguments: %s",
2369 error->message);
2370 goto out;
2371 }
2372
2373 /* create new objects */
2374 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00002375 priv->status = FWUPD_STATUS_IDLE;
Richard Hughes876c0072016-08-17 14:51:03 +01002376 priv->percentage = 0;
Richard Hughesf508e762015-02-27 12:49:36 +00002377 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002378 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002379 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01002380 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01002381 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01002382 g_signal_connect (priv->store, "changed",
2383 G_CALLBACK (fu_main_store_changed_cb), priv);
2384 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
2385 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002386
Richard Hughesd0905142016-03-13 09:46:49 +00002387 /* load plugin */
2388 priv->plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
2389 g_free, (GDestroyNotify) fu_plugin_free);
2390 if (!fu_main_load_plugins (priv->plugins, &error)) {
2391 g_print ("failed to load plugins: %s\n", error->message);
2392 retval = EXIT_FAILURE;
2393 goto out;
2394 }
2395
Richard Hughes7708a0f2015-07-21 08:41:22 +01002396 /* load AppStream */
Richard Hughes65dfbfe2016-03-02 13:42:53 +00002397 as_store_add_filter (priv->store, AS_APP_KIND_FIRMWARE);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002398 if (!as_store_load (priv->store,
2399 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
2400 NULL, &error)){
2401 g_warning ("FuMain: failed to load AppStream data: %s",
2402 error->message);
2403 return FALSE;
2404 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002405
Richard Hughes804c0752015-08-04 14:53:52 +01002406 /* read config file */
Richard Hughes804c0752015-08-04 14:53:52 +01002407 config_file = g_build_filename (SYSCONFDIR, "fwupd.conf", NULL);
2408 g_debug ("Loading fallback values from %s", config_file);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002409 priv->config = g_key_file_new ();
2410 if (!g_key_file_load_from_file (priv->config, config_file,
Richard Hughes804c0752015-08-04 14:53:52 +01002411 G_KEY_FILE_NONE, &error)) {
2412 g_print ("failed to load config file %s: %s\n",
2413 config_file, error->message);
2414 retval = EXIT_FAILURE;
2415 goto out;
2416 }
2417
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002418 /* add providers */
2419 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002420 if (g_key_file_get_boolean (priv->config, "fwupd", "EnableOptionROM", NULL))
Richard Hughes804c0752015-08-04 14:53:52 +01002421 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughes5d057a82015-11-24 18:09:57 +00002422 fu_main_add_provider (priv, fu_provider_dfu_new ());
Richard Hughes25cf6ab2015-08-04 21:34:12 +01002423 fu_main_add_provider (priv, fu_provider_rpi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002424#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00002425 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002426#endif
2427#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002428 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00002429#endif
Mario Limonciello958ead62016-05-14 00:10:25 -05002430#ifdef HAVE_DELL
2431 fu_main_add_provider (priv, fu_provider_dell_new ());
2432#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002433
Richard Hughes5d057a82015-11-24 18:09:57 +00002434 /* last as least priority */
2435 fu_main_add_provider (priv, fu_provider_usb_new ());
2436
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002437 /* load introspection from file */
2438 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
2439 &error);
2440 if (priv->introspection_daemon == NULL) {
2441 g_warning ("FuMain: failed to load daemon introspection: %s",
2442 error->message);
2443 goto out;
2444 }
2445
Richard Hughesf508e762015-02-27 12:49:36 +00002446 /* get authority */
2447 priv->authority = polkit_authority_get_sync (NULL, &error);
2448 if (priv->authority == NULL) {
2449 g_warning ("FuMain: failed to load polkit authority: %s",
2450 error->message);
2451 goto out;
2452 }
2453
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002454 /* own the object */
2455 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
2456 FWUPD_DBUS_SERVICE,
2457 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
2458 G_BUS_NAME_OWNER_FLAGS_REPLACE,
2459 fu_main_on_bus_acquired_cb,
2460 fu_main_on_name_acquired_cb,
2461 fu_main_on_name_lost_cb,
2462 priv, NULL);
2463
2464 /* Only timeout and close the mainloop if we have specified it
2465 * on the command line */
2466 if (immediate_exit)
2467 g_idle_add (fu_main_timed_exit_cb, priv->loop);
2468 else if (timed_exit)
2469 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
2470
2471 /* wait */
2472 g_info ("Daemon ready for requests");
2473 g_main_loop_run (priv->loop);
2474
2475 /* success */
2476 retval = 0;
2477out:
2478 g_option_context_free (context);
2479 if (owner_id > 0)
2480 g_bus_unown_name (owner_id);
2481 if (priv != NULL) {
2482 if (priv->loop != NULL)
2483 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00002484 if (priv->proxy_uid != NULL)
2485 g_object_unref (priv->proxy_uid);
Richard Hughes9559bbe2016-03-29 18:54:20 +01002486 if (priv->proxy_upower != NULL)
2487 g_object_unref (priv->proxy_upower);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002488 if (priv->config != NULL)
2489 g_object_unref (priv->config);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002490 if (priv->connection != NULL)
2491 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00002492 if (priv->authority != NULL)
2493 g_object_unref (priv->authority);
Richard Hughes3f236502015-09-24 15:43:02 +01002494 if (priv->profile != NULL)
2495 g_object_unref (priv->profile);
Richard Hughes7708a0f2015-07-21 08:41:22 +01002496 if (priv->store != NULL)
2497 g_object_unref (priv->store);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002498 if (priv->introspection_daemon != NULL)
2499 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes033ccba2015-09-10 14:51:28 +01002500 if (priv->store_changed_id != 0)
2501 g_source_remove (priv->store_changed_id);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002502 g_object_unref (priv->pending);
Richard Hughes804c0752015-08-04 14:53:52 +01002503 if (priv->providers != NULL)
2504 g_ptr_array_unref (priv->providers);
Richard Hughesd0905142016-03-13 09:46:49 +00002505 if (priv->plugins != NULL)
2506 g_hash_table_unref (priv->plugins);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002507 g_ptr_array_unref (priv->devices);
2508 g_free (priv);
2509 }
2510 return retval;
2511}
2512