blob: 3f2e2c093ab32528e6e1b54622353d764ee187f7 [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 Hughesf29a6ee2017-06-02 19:50:37 +01003 * Copyright (C) 2015-2017 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 Hughes68cc00c2017-06-06 16:59:54 +010036#include "fwupd-common-private.h"
Richard Hughes8e9762d2016-03-17 10:14:15 +000037#include "fwupd-enums-private.h"
Richard Hughes1642b3b2017-06-05 17:40:08 +010038#include "fwupd-release-private.h"
Richard Hughesd6db6b42017-04-12 15:03:10 +010039#include "fwupd-resources.h"
Richard Hughes8e9762d2016-03-17 10:14:15 +000040
Richard Hughes8bbfdf42015-02-26 22:28:09 +000041#include "fu-debug.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000042#include "fu-device.h"
Richard Hughesb8f8db22017-04-25 15:56:00 +010043#include "fu-hwids.h"
Richard Hughescff38bc2016-12-12 12:03:37 +000044#include "fu-plugin-private.h"
Richard Hughesae0efdc2015-06-24 16:18:29 +010045#include "fu-keyring.h"
Richard Hughes0e883ee2015-03-18 17:22:33 +000046#include "fu-pending.h"
Richard Hughescff38bc2016-12-12 12:03:37 +000047#include "fu-plugin.h"
Mario Limonciello918f3932016-02-03 12:47:23 -060048#include "fu-quirks.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000049
Philip Withnallbc339aa2016-11-22 16:13:22 +000050#ifndef HAVE_POLKIT_0_114
Richard Hughes60f48c22015-10-08 20:25:51 +010051G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
52G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
53#endif
54
Richard Hughes5d14def2015-10-07 17:43:10 +010055#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
56
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000057typedef struct {
58 GDBusConnection *connection;
59 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000060 GDBusProxy *proxy_uid;
Richard Hughesbc93e4a2016-12-08 17:29:51 +000061 GUsbContext *usb_ctx;
Richard Hughes9a52c5e2016-07-18 09:17:02 +010062 GKeyFile *config;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000063 GMainLoop *loop;
Richard Hughes5d14def2015-10-07 17:43:10 +010064 GPtrArray *devices; /* of FuDeviceItem */
Richard Hughesf508e762015-02-27 12:49:36 +000065 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000066 FwupdStatus status;
Richard Hughes876c0072016-08-17 14:51:03 +010067 guint percentage;
Richard Hughes0e883ee2015-03-18 17:22:33 +000068 FuPending *pending;
Richard Hughes3f236502015-09-24 15:43:02 +010069 AsProfile *profile;
Richard Hughes7708a0f2015-07-21 08:41:22 +010070 AsStore *store;
Richard Hughes033ccba2015-09-10 14:51:28 +010071 guint store_changed_id;
Richard Hughesf0a799e2017-01-17 20:13:30 +000072 guint owner_id;
Richard Hughesf028ba22017-01-08 10:45:12 +000073 gboolean coldplug_running;
Richard Hughesf3605832017-01-08 10:50:46 +000074 guint coldplug_id;
Richard Hughesb0829032017-01-10 09:27:08 +000075 guint coldplug_delay;
Richard Hughescff38bc2016-12-12 12:03:37 +000076 GPtrArray *plugins; /* of FuPlugin */
77 GHashTable *plugins_hash; /* of name : FuPlugin */
Richard Hughesb8f8db22017-04-25 15:56:00 +010078 GHashTable *hwids; /* of hwid : 1 */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000079} FuMainPrivate;
80
Richard Hughesf508e762015-02-27 12:49:36 +000081typedef struct {
82 FuDevice *device;
Richard Hughescff38bc2016-12-12 12:03:37 +000083 FuPlugin *plugin;
Richard Hughesf508e762015-02-27 12:49:36 +000084} FuDeviceItem;
85
Richard Hughesdde7a2f2016-04-28 15:06:52 +010086static gboolean fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item);
87
Richard Hughesd7022b52015-03-11 19:47:06 +000088static void
89fu_main_emit_changed (FuMainPrivate *priv)
90{
91 /* not yet connected */
92 if (priv->connection == NULL)
93 return;
94 g_dbus_connection_emit_signal (priv->connection,
95 NULL,
96 FWUPD_DBUS_PATH,
97 FWUPD_DBUS_INTERFACE,
98 "Changed",
99 NULL, NULL);
100}
101
Richard Hughes8ca33782016-04-28 15:04:31 +0100102static void
103fu_main_emit_device_added (FuMainPrivate *priv, FuDevice *device)
104{
105 GVariant *val;
106
107 /* not yet connected */
108 if (priv->connection == NULL)
109 return;
110 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
111 g_dbus_connection_emit_signal (priv->connection,
112 NULL,
113 FWUPD_DBUS_PATH,
114 FWUPD_DBUS_INTERFACE,
115 "DeviceAdded",
116 val, NULL);
117}
118
Richard Hughes8ca33782016-04-28 15:04:31 +0100119static void
120fu_main_emit_device_removed (FuMainPrivate *priv, FuDevice *device)
121{
122 GVariant *val;
123
124 /* not yet connected */
125 if (priv->connection == NULL)
126 return;
127 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
128 g_dbus_connection_emit_signal (priv->connection,
129 NULL,
130 FWUPD_DBUS_PATH,
131 FWUPD_DBUS_INTERFACE,
132 "DeviceRemoved",
133 val, NULL);
134}
135
Richard Hughes8ca33782016-04-28 15:04:31 +0100136static void
137fu_main_emit_device_changed (FuMainPrivate *priv, FuDevice *device)
138{
139 GVariant *val;
140
141 /* not yet connected */
142 if (priv->connection == NULL)
143 return;
144 val = fwupd_result_to_data (FWUPD_RESULT (device), "(a{sv})");
145 g_dbus_connection_emit_signal (priv->connection,
146 NULL,
147 FWUPD_DBUS_PATH,
148 FWUPD_DBUS_INTERFACE,
149 "DeviceChanged",
150 val, NULL);
151}
152
Richard Hughes773ce982015-03-09 22:40:57 +0000153static void
154fu_main_emit_property_changed (FuMainPrivate *priv,
155 const gchar *property_name,
156 GVariant *property_value)
157{
158 GVariantBuilder builder;
159 GVariantBuilder invalidated_builder;
160
161 /* not yet connected */
162 if (priv->connection == NULL)
163 return;
164
165 /* build the dict */
166 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
167 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
168 g_variant_builder_add (&builder,
169 "{sv}",
170 property_name,
171 property_value);
172 g_dbus_connection_emit_signal (priv->connection,
173 NULL,
174 FWUPD_DBUS_PATH,
175 "org.freedesktop.DBus.Properties",
176 "PropertiesChanged",
177 g_variant_new ("(sa{sv}as)",
178 FWUPD_DBUS_INTERFACE,
179 &builder,
180 &invalidated_builder),
181 NULL);
182 g_variant_builder_clear (&builder);
183 g_variant_builder_clear (&invalidated_builder);
184}
185
Richard Hughes773ce982015-03-09 22:40:57 +0000186static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000187fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000188{
Richard Hughes773ce982015-03-09 22:40:57 +0000189 if (priv->status == status)
190 return;
191 priv->status = status;
192
193 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000194 g_debug ("Emitting PropertyChanged('Status'='%s')",
195 fwupd_status_to_string (status));
196 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000197}
198
Richard Hughes876c0072016-08-17 14:51:03 +0100199static void
200fu_main_set_percentage (FuMainPrivate *priv, guint percentage)
201{
202 if (priv->percentage == percentage)
203 return;
204 priv->percentage = percentage;
205
206 /* emit changed */
207 g_debug ("Emitting PropertyChanged('Percentage'='%u%%')", percentage);
208 fu_main_emit_property_changed (priv, "Percentage",
209 g_variant_new_uint32 (percentage));
210}
211
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000212static GVariant *
Richard Hughes7708a0f2015-07-21 08:41:22 +0100213fu_main_device_array_to_variant (GPtrArray *devices, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000214{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000215 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000216
Richard Hughes9a38c032015-03-17 20:58:46 +0000217 /* no devices */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100218 if (devices->len == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000219 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000220 FWUPD_ERROR,
221 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9d76a872015-09-17 12:49:07 +0100222 "Nothing to do");
Richard Hughes9a38c032015-03-17 20:58:46 +0000223 return NULL;
224 }
225
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000226 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +0100227 for (guint i = 0; i < devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000228 GVariant *tmp;
229 FuDeviceItem *item;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100230 item = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +0000231 tmp = fwupd_result_to_data (FWUPD_RESULT (item->device), "{sa{sv}}");
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000232 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000233 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000234 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000235}
236
Richard Hughes060af612016-08-17 17:32:34 +0100237static void
238fu_main_invocation_return_value (FuMainPrivate *priv,
239 GDBusMethodInvocation *invocation,
240 GVariant *parameters)
241{
242 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
243 g_dbus_method_invocation_return_value (invocation, parameters);
244}
245
246static void
247fu_main_invocation_return_error (FuMainPrivate *priv,
248 GDBusMethodInvocation *invocation,
249 const GError *error)
250{
251 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
252 g_dbus_method_invocation_return_gerror (invocation, error);
253}
254
Richard Hughesf508e762015-02-27 12:49:36 +0000255static void
256fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000257{
Richard Hughesf508e762015-02-27 12:49:36 +0000258 g_object_unref (item->device);
Richard Hughescff38bc2016-12-12 12:03:37 +0000259 g_object_unref (item->plugin);
Richard Hughesf508e762015-02-27 12:49:36 +0000260 g_free (item);
261}
262
Richard Hughesf508e762015-02-27 12:49:36 +0000263static FuDeviceItem *
264fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
265{
Richard Hughesf192bf02016-07-22 08:26:43 +0100266 for (guint i = 0; i < priv->devices->len; i++) {
267 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesf508e762015-02-27 12:49:36 +0000268 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
269 return item;
Richard Hughesb1b59d82016-01-06 13:20:53 +0000270 if (g_strcmp0 (fu_device_get_equivalent_id (item->device), id) == 0)
271 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000272 }
273 return NULL;
274}
275
Richard Hughes16d8f622016-03-15 16:34:20 +0000276static FuDeviceItem *
277fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid)
278{
Richard Hughesf192bf02016-07-22 08:26:43 +0100279 for (guint i = 0; i < priv->devices->len; i++) {
280 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes99147f12016-05-17 09:35:04 +0100281 if (fu_device_has_guid (item->device, guid))
Richard Hughes16d8f622016-03-15 16:34:20 +0000282 return item;
283 }
284 return NULL;
285}
286
Richard Hughescff38bc2016-12-12 12:03:37 +0000287static FuPlugin *
288fu_main_get_plugin_by_name (FuMainPrivate *priv, const gchar *name)
Richard Hughes0e883ee2015-03-18 17:22:33 +0000289{
Richard Hughescff38bc2016-12-12 12:03:37 +0000290 for (guint i = 0; i < priv->plugins->len; i++) {
291 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
292 if (g_strcmp0 (fu_plugin_get_name (plugin), name) == 0)
293 return plugin;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000294 }
295 return NULL;
296}
297
Richard Hughes01c13d12017-06-05 09:10:17 +0100298static const gchar *
299fu_main_get_sysconfig_dir (void)
300{
301 if (g_file_test (SYSCONFDIR, G_FILE_TEST_EXISTS))
302 return SYSCONFDIR;
303 return "/etc";
304}
305
Richard Hughes1642b3b2017-06-05 17:40:08 +0100306static void
307fu_main_set_release_from_item (FwupdRelease *rel, AsRelease *release)
308{
309 AsChecksum *csum;
310 const gchar *tmp;
311
312 tmp = as_release_get_version (release);
313 if (tmp != NULL)
314 fwupd_release_set_version (rel, tmp);
315 tmp = as_release_get_description (release, NULL);
316 if (tmp != NULL)
317 fwupd_release_set_description (rel, tmp);
318 tmp = as_release_get_location_default (release);
319 if (tmp != NULL)
320 fwupd_release_set_uri (rel, tmp);
321 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
322 if (csum != NULL) {
323 tmp = as_checksum_get_filename (csum);
324 if (tmp != NULL)
325 fwupd_release_set_filename (rel, tmp);
326 }
327 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTAINER);
328 if (csum != NULL) {
329 tmp = as_checksum_get_value (csum);
330 if (tmp != NULL)
Richard Hughes68cc00c2017-06-06 16:59:54 +0100331 fwupd_release_add_checksum (rel, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +0100332 }
333 fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED));
334}
335
Richard Hughes5d14def2015-10-07 17:43:10 +0100336static gboolean
337fu_main_get_release_trust_flags (AsRelease *release,
338 FwupdTrustFlags *trust_flags,
339 GError **error)
340{
341 AsChecksum *csum_tmp;
342 GBytes *blob_payload;
343 GBytes *blob_signature;
344 const gchar *fn;
345 g_autofree gchar *pki_dir = NULL;
346 g_autofree gchar *fn_signature = NULL;
347 g_autoptr(GError) error_local = NULL;
348 g_autoptr(FuKeyring) kr = NULL;
349
350 /* no filename? */
351 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
352 fn = as_checksum_get_filename (csum_tmp);
353 if (fn == NULL) {
354 g_set_error_literal (error,
355 FWUPD_ERROR,
356 FWUPD_ERROR_INVALID_FILE,
357 "no filename");
358 return FALSE;
359 }
360
361 /* no signature == no trust */
362 fn_signature = g_strdup_printf ("%s.asc", fn);
363 blob_signature = as_release_get_blob (release, fn_signature);
364 if (blob_signature == NULL) {
365 g_debug ("firmware archive contained no GPG signature");
366 return TRUE;
367 }
368
369 /* get payload */
370 blob_payload = as_release_get_blob (release, fn);
371 if (blob_payload == NULL) {
372 g_set_error_literal (error,
373 FWUPD_ERROR,
374 FWUPD_ERROR_INVALID_FILE,
375 "no payload");
376 return FALSE;
377 }
378
379 /* check we were installed correctly */
Richard Hughes01c13d12017-06-05 09:10:17 +0100380 pki_dir = g_build_filename (fu_main_get_sysconfig_dir (), "pki", "fwupd", NULL);
Richard Hughes5d14def2015-10-07 17:43:10 +0100381 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
382 g_set_error (error,
383 FWUPD_ERROR,
384 FWUPD_ERROR_NOT_FOUND,
385 "PKI directory %s not found", pki_dir);
386 return FALSE;
387 }
388
389 /* verify against the system trusted keys */
390 kr = fu_keyring_new ();
391 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
392 return FALSE;
393 if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
394 g_warning ("untrusted as failed to verify: %s",
395 error_local->message);
396 return TRUE;
397 }
398
399 /* awesome! */
400 g_debug ("marking payload as trusted");
401 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
402 return TRUE;
403}
404
Richard Hughes9a410ce2016-02-28 15:58:54 +0000405typedef enum {
406 FU_MAIN_AUTH_KIND_UNKNOWN,
407 FU_MAIN_AUTH_KIND_INSTALL,
408 FU_MAIN_AUTH_KIND_UNLOCK,
Richard Hughes29c220d2016-12-14 17:09:54 +0000409 FU_MAIN_AUTH_KIND_VERIFY_UPDATE,
Richard Hughes9a410ce2016-02-28 15:58:54 +0000410 FU_MAIN_AUTH_KIND_LAST
411} FuMainAuthKind;
412
Richard Hughesf508e762015-02-27 12:49:36 +0000413typedef struct {
414 GDBusMethodInvocation *invocation;
Richard Hughes5d14def2015-10-07 17:43:10 +0100415 AsStore *store;
416 FwupdTrustFlags trust_flags;
Richard Hughesfe5cc902016-06-29 10:00:00 +0100417 GPtrArray *devices; /* of FuDevice */
Richard Hughesa4a2c182016-06-29 10:37:05 +0100418 GPtrArray *blob_fws; /* of GBytes */
Richard Hughes2d6e1862016-03-18 09:20:37 +0000419 FwupdInstallFlags flags;
Richard Hughes5d14def2015-10-07 17:43:10 +0100420 GBytes *blob_cab;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100421 gboolean is_downgrade;
Richard Hughes9a410ce2016-02-28 15:58:54 +0000422 FuMainAuthKind auth_kind;
Richard Hughes67ec8982015-03-03 11:39:27 +0000423 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000424} FuMainAuthHelper;
425
Richard Hughesf508e762015-02-27 12:49:36 +0000426static void
427fu_main_helper_free (FuMainAuthHelper *helper)
428{
Richard Hughes67ec8982015-03-03 11:39:27 +0000429 /* free */
Richard Hughesfe5cc902016-06-29 10:00:00 +0100430 if (helper->devices != NULL)
431 g_ptr_array_unref (helper->devices);
Richard Hughes4ced4662016-08-26 11:02:31 +0100432 if (helper->blob_fws != NULL)
Richard Hughesa4a2c182016-06-29 10:37:05 +0100433 g_ptr_array_unref (helper->blob_fws);
Richard Hughes4ced4662016-08-26 11:02:31 +0100434 if (helper->blob_cab != NULL)
Richard Hughes5d14def2015-10-07 17:43:10 +0100435 g_bytes_unref (helper->blob_cab);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000436 if (helper->store != NULL)
437 g_object_unref (helper->store);
Richard Hughes67ec8982015-03-03 11:39:27 +0000438 g_object_unref (helper->invocation);
Richard Hughesf508e762015-02-27 12:49:36 +0000439 g_free (helper);
440}
441
Richard Hughesb75c92d2016-02-20 20:22:00 +0000442static gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000443fu_main_plugin_unlock_authenticated (FuMainAuthHelper *helper, GError **error)
Richard Hughes9a410ce2016-02-28 15:58:54 +0000444{
Richard Hughesfe5cc902016-06-29 10:00:00 +0100445 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100446 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100447 FuDeviceItem *item;
448 FuDevice *device = g_ptr_array_index (helper->devices, i);
449
450 item = fu_main_get_item_by_id (helper->priv,
451 fu_device_get_id (device));
452 if (item == NULL) {
453 g_set_error (error,
454 FWUPD_ERROR,
455 FWUPD_ERROR_INVALID_FILE,
456 "device %s was removed",
457 fu_device_get_id (device));
458 return FALSE;
459 }
460
Richard Hughescff38bc2016-12-12 12:03:37 +0000461 /* run the correct plugin that added this */
462 if (!fu_plugin_runner_unlock (item->plugin,
463 item->device,
464 error))
Richard Hughesfe5cc902016-06-29 10:00:00 +0100465 return FALSE;
466
467 /* make the UI update */
468 fu_main_emit_device_changed (helper->priv, item->device);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000469 }
470
Richard Hughes9a410ce2016-02-28 15:58:54 +0000471 /* make the UI update */
472 fu_main_emit_changed (helper->priv);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100473
Richard Hughes9a410ce2016-02-28 15:58:54 +0000474 return TRUE;
475}
476
Richard Hughes29c220d2016-12-14 17:09:54 +0000477static AsApp *
478fu_main_verify_update_device_to_app (FuDevice *device)
479{
480 AsApp *app = NULL;
Richard Hughes68cc00c2017-06-06 16:59:54 +0100481 GPtrArray *checksums;
Richard Hughes29c220d2016-12-14 17:09:54 +0000482 g_autofree gchar *id = NULL;
Richard Hughes29c220d2016-12-14 17:09:54 +0000483 g_autoptr(AsProvide) prov = NULL;
484 g_autoptr(AsRelease) rel = NULL;
485
486 /* make a plausible ID */
487 id = g_strdup_printf ("%s.firmware", fu_device_get_guid_default (device));
488
489 /* add app to store */
490 app = as_app_new ();
491 as_app_set_id (app, id);
492 as_app_set_kind (app, AS_APP_KIND_FIRMWARE);
Richard Hughes29c220d2016-12-14 17:09:54 +0000493 rel = as_release_new ();
494 as_release_set_version (rel, fu_device_get_version (device));
Richard Hughes68cc00c2017-06-06 16:59:54 +0100495 checksums = fu_device_get_checksums (device);
496 for (guint j = 0; j < checksums->len; j++) {
497 const gchar *checksum = g_ptr_array_index (checksums, j);
498 g_autoptr(AsChecksum) csum = as_checksum_new ();
499 as_checksum_set_kind (csum, fwupd_checksum_guess_kind (checksum));
500 as_checksum_set_value (csum, checksum);
501 as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT);
502 as_release_add_checksum (rel, csum);
503 }
Richard Hughes29c220d2016-12-14 17:09:54 +0000504 as_app_add_release (app, rel);
505 prov = as_provide_new ();
506 as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED);
507 as_provide_set_value (prov, fu_device_get_guid_default (device));
508 as_app_add_provide (app, prov);
509 return app;
510}
511
512static gboolean
513fu_main_plugin_verify_update_authenticated (FuMainAuthHelper *helper, GError **error)
514{
515 const gchar *fn = "/var/cache/app-info/xmls/fwupd-verify.xml";
516 g_autoptr(AsStore) store = NULL;
517 g_autoptr(GFile) xml_file = NULL;
518
519 /* load existing store */
520 store = as_store_new ();
521 as_store_set_api_version (store, 0.9);
522 xml_file = g_file_new_for_path (fn);
523 if (g_file_query_exists (xml_file, NULL)) {
524 if (!as_store_from_file (store, xml_file, NULL, NULL, error))
525 return FALSE;
526 }
527
528 /* check the devices still exists */
529 for (guint i = 0; i < helper->devices->len; i ++) {
530 FuDevice *device = g_ptr_array_index (helper->devices, i);
531 FuDeviceItem *item;
Richard Hughes68cc00c2017-06-06 16:59:54 +0100532 GPtrArray *checksums;
Richard Hughes29c220d2016-12-14 17:09:54 +0000533 g_autoptr(AsApp) app = NULL;
534
535 item = fu_main_get_item_by_id (helper->priv,
536 fu_device_get_id (device));
537 if (item == NULL) {
538 g_set_error (error,
539 FWUPD_ERROR,
540 FWUPD_ERROR_INVALID_FILE,
541 "device %s was removed",
542 fu_device_get_id (device));
543 return FALSE;
544 }
545
546 /* unlock device if required */
547 if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) {
548 if (!fu_plugin_runner_unlock (item->plugin,
549 item->device,
550 error))
551 return FALSE;
552 fu_main_emit_device_changed (helper->priv, item->device);
553 }
554
555 /* get the checksum */
Richard Hughes68cc00c2017-06-06 16:59:54 +0100556 checksums = fu_device_get_checksums (item->device);
557 if (checksums->len == 0) {
Richard Hughes29c220d2016-12-14 17:09:54 +0000558 if (!fu_plugin_runner_verify (item->plugin,
559 item->device,
560 FU_PLUGIN_VERIFY_FLAG_NONE,
561 error))
562 return FALSE;
563 fu_main_emit_device_changed (helper->priv, item->device);
564 }
565
566 /* we got nothing */
Richard Hughes68cc00c2017-06-06 16:59:54 +0100567 if (checksums->len == 0) {
Richard Hughes29c220d2016-12-14 17:09:54 +0000568 g_set_error_literal (error,
569 FWUPD_ERROR,
570 FWUPD_ERROR_NOT_SUPPORTED,
571 "device verification not supported");
572 return FALSE;
573 }
574
575 /* add to store */
576 app = fu_main_verify_update_device_to_app (item->device);
577 as_store_add_app (store, app);
578 }
579
580 /* write */
581 g_debug ("writing %s", fn);
582 return as_store_to_file (store, xml_file,
583 AS_NODE_TO_XML_FLAG_ADD_HEADER |
584 AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
585 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
586 NULL, error);
587}
588
Richard Hughes18423292015-03-09 17:10:50 +0000589static gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000590fu_main_plugin_update_authenticated (FuMainAuthHelper *helper, GError **error)
Richard Hughes18423292015-03-09 17:10:50 +0000591{
Richard Hughes7b8b2022016-12-12 16:15:03 +0000592 FuMainPrivate *priv = helper->priv;
Richard Hughes18423292015-03-09 17:10:50 +0000593 FuDeviceItem *item;
594
Richard Hughesfe5cc902016-06-29 10:00:00 +0100595 /* check the devices still exists */
Richard Hughesf192bf02016-07-22 08:26:43 +0100596 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100597 FuDevice *device = g_ptr_array_index (helper->devices, i);
598 item = fu_main_get_item_by_id (helper->priv,
599 fu_device_get_id (device));
600 if (item == NULL) {
601 g_set_error (error,
602 FWUPD_ERROR,
603 FWUPD_ERROR_INVALID_FILE,
604 "device %s was removed",
605 fu_device_get_id (device));
Richard Hughesb75c92d2016-02-20 20:22:00 +0000606 return FALSE;
607 }
Richard Hughesfe5cc902016-06-29 10:00:00 +0100608
Mario Limonciello3ef952f2016-09-20 12:28:59 -0500609 /* Called with online update, test if device is supposed to allow this */
610 if (!(helper->flags & FWUPD_INSTALL_FLAG_OFFLINE) &&
Richard Hughes644562e2016-08-22 10:30:24 +0100611 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100612 g_set_error(error,
613 FWUPD_ERROR,
Mario Limonciello3ef952f2016-09-20 12:28:59 -0500614 FWUPD_ERROR_NOT_SUPPORTED,
615 "Device %s does not allow online updates",
616 fu_device_get_id (device));
617 return FALSE;
618 }
619 /* Called with offline update, test if device is supposed to allow this */
620 if (helper->flags & FWUPD_INSTALL_FLAG_OFFLINE &&
621 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE)) {
622 g_set_error(error,
623 FWUPD_ERROR,
624 FWUPD_ERROR_NOT_SUPPORTED,
625 "Device %s does not allow offline updates",
Richard Hughesfe5cc902016-06-29 10:00:00 +0100626 fu_device_get_id (device));
627 return FALSE;
628 }
Richard Hughesb75c92d2016-02-20 20:22:00 +0000629 }
630
Richard Hughes7b8b2022016-12-12 16:15:03 +0000631 /* run the correct plugin for each device */
Richard Hughesf192bf02016-07-22 08:26:43 +0100632 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +0100633 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughesa4a2c182016-06-29 10:37:05 +0100634 GBytes *blob_fw = g_ptr_array_index (helper->blob_fws, i);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100635 item = fu_main_get_item_by_id (helper->priv,
636 fu_device_get_id (device));
Richard Hughes7b8b2022016-12-12 16:15:03 +0000637
638 /* signal to all the plugins the update is about to happen */
639 for (guint j = 0; j < priv->plugins->len; j++) {
640 FuPlugin *plugin = g_ptr_array_index (priv->plugins, j);
641 if (!fu_plugin_runner_update_prepare (plugin, device, error))
642 return FALSE;
643 }
644
645 /* do the update */
Richard Hughescff38bc2016-12-12 12:03:37 +0000646 if (!fu_plugin_runner_update (item->plugin,
Richard Hughes7b8b2022016-12-12 16:15:03 +0000647 item->device,
648 helper->blob_cab,
649 blob_fw,
650 helper->flags,
Richard Hughesb21e3242017-01-11 08:27:23 +0000651 error)) {
652 for (guint j = 0; j < priv->plugins->len; j++) {
653 FuPlugin *plugin = g_ptr_array_index (priv->plugins, j);
654 g_autoptr(GError) error_local = NULL;
655 if (!fu_plugin_runner_update_cleanup (plugin,
656 device,
657 &error_local)) {
658 g_warning ("failed to update-cleanup "
659 "after failed update: %s",
660 error_local->message);
661 }
662 }
Richard Hughesfe5cc902016-06-29 10:00:00 +0100663 return FALSE;
Richard Hughesb21e3242017-01-11 08:27:23 +0000664 }
Richard Hughesfe5cc902016-06-29 10:00:00 +0100665
Richard Hughes7b8b2022016-12-12 16:15:03 +0000666 /* signal to all the plugins the update has happened */
667 for (guint j = 0; j < priv->plugins->len; j++) {
668 FuPlugin *plugin = g_ptr_array_index (priv->plugins, j);
Richard Hughesb21e3242017-01-11 08:27:23 +0000669 g_autoptr(GError) error_local = NULL;
670 if (!fu_plugin_runner_update_cleanup (plugin, device, &error_local)) {
671 g_warning ("failed to update-cleanup: %s",
672 error_local->message);
673 }
Richard Hughes7b8b2022016-12-12 16:15:03 +0000674 }
675
Richard Hughesfe5cc902016-06-29 10:00:00 +0100676 /* make the UI update */
Richard Hughes33a518a2016-07-27 15:22:53 +0100677 fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
Richard Hughesfe5cc902016-06-29 10:00:00 +0100678 fu_main_emit_device_changed (helper->priv, item->device);
679 }
Richard Hughesa3a8f502015-11-24 12:31:59 +0000680
681 /* make the UI update */
682 fu_main_emit_changed (helper->priv);
683 return TRUE;
Richard Hughes18423292015-03-09 17:10:50 +0000684}
685
Richard Hughesf508e762015-02-27 12:49:36 +0000686static void
687fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
688{
689 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100690 g_autoptr(GError) error = NULL;
Richard Hughes2d744f52016-12-20 09:07:37 +0000691 g_autoptr(GError) error_local = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100692 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000693
694 /* get result */
695 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
Richard Hughes2d744f52016-12-20 09:07:37 +0000696 res, &error_local);
Richard Hughesf508e762015-02-27 12:49:36 +0000697 if (auth == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +0100698 g_set_error (&error,
699 FWUPD_ERROR,
700 FWUPD_ERROR_AUTH_FAILED,
Richard Hughes2d744f52016-12-20 09:07:37 +0000701 "could not check for auth: %s",
702 error_local->message);
Richard Hughes060af612016-08-17 17:32:34 +0100703 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000704 fu_main_helper_free (helper);
705 return;
706 }
707
708 /* did not auth */
709 if (!polkit_authorization_result_get_is_authorized (auth)) {
Richard Hughes060af612016-08-17 17:32:34 +0100710 g_set_error_literal (&error,
711 FWUPD_ERROR,
712 FWUPD_ERROR_AUTH_FAILED,
713 "failed to obtain auth");
714 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000715 fu_main_helper_free (helper);
716 return;
717 }
718
Richard Hughes18423292015-03-09 17:10:50 +0000719 /* we're good to go */
Richard Hughes9a410ce2016-02-28 15:58:54 +0000720 if (helper->auth_kind == FU_MAIN_AUTH_KIND_INSTALL) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000721 if (!fu_main_plugin_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100722 fu_main_invocation_return_error (helper->priv,
723 helper->invocation,
724 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000725 fu_main_helper_free (helper);
726 return;
727 }
728 } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_UNLOCK) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000729 if (!fu_main_plugin_unlock_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +0100730 fu_main_invocation_return_error (helper->priv,
731 helper->invocation,
732 error);
Richard Hughes9a410ce2016-02-28 15:58:54 +0000733 fu_main_helper_free (helper);
734 return;
735 }
Richard Hughes29c220d2016-12-14 17:09:54 +0000736 } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_VERIFY_UPDATE) {
737 if (!fu_main_plugin_verify_update_authenticated (helper, &error)) {
738 fu_main_invocation_return_error (helper->priv,
739 helper->invocation,
740 error);
741 fu_main_helper_free (helper);
742 return;
743 }
Richard Hughes9a410ce2016-02-28 15:58:54 +0000744 } else {
745 g_assert_not_reached ();
Richard Hughesf508e762015-02-27 12:49:36 +0000746 }
747
748 /* success */
Richard Hughes060af612016-08-17 17:32:34 +0100749 fu_main_invocation_return_value (helper->priv, helper->invocation, NULL);
Richard Hughesf508e762015-02-27 12:49:36 +0000750 fu_main_helper_free (helper);
751}
752
Richard Hughes5d14def2015-10-07 17:43:10 +0100753static gchar *
754fu_main_get_guids_from_store (AsStore *store)
755{
Richard Hughes5d14def2015-10-07 17:43:10 +0100756 AsProvide *prov;
757 GPtrArray *provides;
758 GPtrArray *apps;
759 GString *str = g_string_new ("");
Richard Hughes5d14def2015-10-07 17:43:10 +0100760
761 /* return a string with all the firmware apps in the store */
762 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +0100763 for (guint i = 0; i < apps->len; i++) {
764 AsApp *app = AS_APP (g_ptr_array_index (apps, i));
Richard Hughes5d14def2015-10-07 17:43:10 +0100765 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100766 for (guint j = 0; j < provides->len; j++) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100767 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
768 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
769 continue;
770 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
771 }
772 }
773 if (str->len == 0)
774 return NULL;
775 g_string_truncate (str, str->len - 1);
776 return g_string_free (str, FALSE);
777}
778
Richard Hughesdf7950b2016-01-31 10:18:40 +0000779static void
Richard Hughes3d2fc1e2017-06-08 14:26:31 +0100780fu_main_vendor_fixup_provide_value (AsApp *app)
781{
782 GPtrArray *provides;
783
784 /* no quirk required */
785 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
786 return;
787
788 /* fix each provide to be a GUID */
789 provides = as_app_get_provides (app);
790 for (guint i = 0; i < provides->len; i++) {
791 AsProvide *prov = g_ptr_array_index (provides, i);
792 const gchar *value = as_provide_get_value (prov);
793 g_autofree gchar *guid = NULL;
794 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
795 continue;
796 if (as_utils_guid_is_valid (value))
797 continue;
798 guid = as_utils_guid_from_string (value);
799 as_provide_set_value (prov, guid);
800 }
801}
802
803static void
Richard Hughesdf7950b2016-01-31 10:18:40 +0000804fu_main_vendor_quirk_release_version (AsApp *app)
805{
806 AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET;
807 GPtrArray *releases;
Richard Hughesdf7950b2016-01-31 10:18:40 +0000808
809 /* no quirk required */
Richard Hughes65dfbfe2016-03-02 13:42:53 +0000810 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
Richard Hughesdf7950b2016-01-31 10:18:40 +0000811 return;
812
Richard Hughesf192bf02016-07-22 08:26:43 +0100813 for (guint i = 0; quirk_table[i].identifier != NULL; i++) {
Mario Limonciello918f3932016-02-03 12:47:23 -0600814 if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier))
815 flags = quirk_table[i].flags;
Richard Hughesf192bf02016-07-22 08:26:43 +0100816 }
Richard Hughesdf7950b2016-01-31 10:18:40 +0000817
818 /* fix each release */
819 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +0100820 for (guint i = 0; i < releases->len; i++) {
Richard Hughesdf7950b2016-01-31 10:18:40 +0000821 AsRelease *rel;
822 const gchar *version;
823 guint64 ver_uint32;
824 g_autofree gchar *version_new = NULL;
825
826 rel = g_ptr_array_index (releases, i);
827 version = as_release_get_version (rel);
828 if (version == NULL)
829 continue;
830 if (g_strstr_len (version, -1, ".") != NULL)
831 continue;
832
833 /* metainfo files use hex and the LVFS uses decimal */
834 if (g_str_has_prefix (version, "0x")) {
835 ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16);
836 } else {
837 ver_uint32 = g_ascii_strtoull (version, NULL, 10);
838 }
839 if (ver_uint32 == 0)
840 continue;
841
842 /* convert to dotted decimal */
Richard Hughes33a518a2016-07-27 15:22:53 +0100843 version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags);
Richard Hughesdf7950b2016-01-31 10:18:40 +0000844 as_release_set_version (rel, version_new);
845 }
846}
847
Richard Hughes4852ff82017-01-04 20:38:29 +0000848#if AS_CHECK_VERSION(0,6,7)
Richard Hughes404cc512016-12-21 16:09:48 +0000849static gboolean
850fu_main_check_version_requirement (AsApp *app,
851 AsRequireKind kind,
852 const gchar *id,
853 const gchar *version,
854 GError **error)
855{
Richard Hughes404cc512016-12-21 16:09:48 +0000856 AsRequire *req;
857
858 /* check args */
859 if (version == NULL) {
860 g_debug ("no paramater given for %s{%s}",
861 as_require_kind_to_string (kind), id);
862 return TRUE;
863 }
864
865 /* does requirement exist */
866 req = as_app_get_require_by_value (app, kind, id);
867 if (req == NULL) {
868 g_debug ("no requirement on %s{%s}",
869 as_require_kind_to_string (kind), id);
870 return TRUE;
871 }
872
873 /* check version */
874 if (!as_require_version_compare (req, version, error)) {
875 g_prefix_error (error, "version of %s incorrect: ", id);
876 return FALSE;
877 }
878
879 /* success */
880 g_debug ("requirement %s %s %s on %s passed",
881 as_require_get_version (req),
882 as_require_compare_to_string (as_require_get_compare (req)),
883 version, id);
Richard Hughes404cc512016-12-21 16:09:48 +0000884 return TRUE;
885}
Richard Hughes4852ff82017-01-04 20:38:29 +0000886#endif
Richard Hughes404cc512016-12-21 16:09:48 +0000887
Richard Hughes99147f12016-05-17 09:35:04 +0100888static AsApp *
889fu_main_store_get_app_by_guids (AsStore *store, FuDevice *device)
890{
Richard Hughesf192bf02016-07-22 08:26:43 +0100891 GPtrArray *guids = fu_device_get_guids (device);
892 for (guint i = 0; i < guids->len; i++) {
Richard Hughes99147f12016-05-17 09:35:04 +0100893 AsApp *app = NULL;
894 app = as_store_get_app_by_provide (store,
895 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
896 g_ptr_array_index (guids, i));
897 if (app != NULL)
898 return app;
899 }
900 return NULL;
901}
902
Richard Hughes404cc512016-12-21 16:09:48 +0000903static gboolean
904fu_main_check_app_versions (AsApp *app, FuDevice *device, GError **error)
905{
Richard Hughes4852ff82017-01-04 20:38:29 +0000906#if AS_CHECK_VERSION(0,6,7)
Richard Hughes404cc512016-12-21 16:09:48 +0000907 /* make sure requirements are satisfied */
908 if (!fu_main_check_version_requirement (app,
909 AS_REQUIRE_KIND_ID,
910 "org.freedesktop.fwupd",
911 VERSION,
912 error)) {
913 return FALSE;
914 }
915
916 if (device != NULL) {
917 if (!fu_main_check_version_requirement (app,
918 AS_REQUIRE_KIND_FIRMWARE,
919 NULL,
920 fu_device_get_version (device),
921 error)) {
922 return FALSE;
923 }
924 if (!fu_main_check_version_requirement (app,
925 AS_REQUIRE_KIND_FIRMWARE,
926 "bootloader",
Richard Hughes7ceb3182016-12-23 11:19:20 +0000927 fu_device_get_version_bootloader (device),
Richard Hughes404cc512016-12-21 16:09:48 +0000928 error)) {
929 return FALSE;
930 }
931 }
Richard Hughes4852ff82017-01-04 20:38:29 +0000932#endif
Richard Hughes404cc512016-12-21 16:09:48 +0000933
934 /* success */
935 return TRUE;
936}
937
Richard Hughes01b9a832016-08-16 17:59:32 +0100938static AsScreenshot *
939_as_app_get_screenshot_default (AsApp *app)
940{
941 GPtrArray *array = as_app_get_screenshots (app);
942 if (array->len == 0)
943 return NULL;
944 return g_ptr_array_index (array, 0);
945}
946
Richard Hughes67ec8982015-03-03 11:39:27 +0000947static gboolean
Richard Hughes3ab17e62016-07-04 12:37:22 +0100948fu_main_update_helper_for_device (FuMainAuthHelper *helper,
949 FuDevice *device,
950 GError **error)
Richard Hughes67ec8982015-03-03 11:39:27 +0000951{
Richard Hughes5d14def2015-10-07 17:43:10 +0100952 AsApp *app;
953 AsChecksum *csum_tmp;
954 AsRelease *rel;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100955 GBytes *blob_fw;
Richard Hughesdef31752015-03-04 19:26:54 +0000956 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000957 const gchar *version;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100958 gboolean is_downgrade;
Richard Hughesdb468ee2016-06-29 10:10:47 +0100959 gint vercmp;
Richard Hughes3ab17e62016-07-04 12:37:22 +0100960
961 /* find from guid */
962 app = fu_main_store_get_app_by_guids (helper->store, device);
963 if (app == NULL) {
964 g_autofree gchar *guid = NULL;
965 guid = fu_main_get_guids_from_store (helper->store);
966 g_set_error (error,
967 FWUPD_ERROR,
968 FWUPD_ERROR_INVALID_FILE,
969 "firmware is not for this hw: required %s got %s",
970 fu_device_get_guid_default (device), guid);
971 return FALSE;
972 }
973
Richard Hughes404cc512016-12-21 16:09:48 +0000974 /* check we can install it */
975 if (!fu_main_check_app_versions (app, device, error))
976 return FALSE;
977
Richard Hughes3ab17e62016-07-04 12:37:22 +0100978 /* parse the DriverVer */
979 rel = as_app_get_release_default (app);
980 if (rel == NULL) {
981 g_set_error_literal (error,
982 FWUPD_ERROR,
983 FWUPD_ERROR_INVALID_FILE,
984 "no releases in the firmware component");
985 return FALSE;
986 }
987
Richard Hughes01b9a832016-08-16 17:59:32 +0100988 /* no update abilities */
Richard Hughes644562e2016-08-22 10:30:24 +0100989 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
990 !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +0100991 g_set_error (error,
992 FWUPD_ERROR,
993 FWUPD_ERROR_INTERNAL,
994 "Device %s does not currently allow updates",
995 fu_device_get_id (device));
996 return FALSE;
997 }
998
999 /* not in bootloader mode */
Richard Hughes644562e2016-08-22 10:30:24 +01001000 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
Richard Hughes01b9a832016-08-16 17:59:32 +01001001 const gchar *caption = NULL;
1002 AsScreenshot *ss = _as_app_get_screenshot_default (app);
1003 if (ss != NULL)
1004 caption = as_screenshot_get_caption (ss, NULL);
1005 if (caption != NULL) {
1006 g_set_error (error,
1007 FWUPD_ERROR,
1008 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +01001009 "Device %s needs to manually be put in update mode: %s",
1010 fu_device_get_name (device), caption);
Richard Hughes01b9a832016-08-16 17:59:32 +01001011 } else {
1012 g_set_error (error,
1013 FWUPD_ERROR,
1014 FWUPD_ERROR_INTERNAL,
Richard Hughes14d17642016-08-17 12:03:03 +01001015 "Device %s needs to manually be put in update mode",
1016 fu_device_get_name (device));
Richard Hughes01b9a832016-08-16 17:59:32 +01001017 }
1018 return FALSE;
1019 }
1020
Richard Hughes3ab17e62016-07-04 12:37:22 +01001021 /* get the blob */
1022 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
1023 tmp = as_checksum_get_filename (csum_tmp);
1024 if (tmp == NULL) {
1025 g_set_error_literal (error,
1026 FWUPD_ERROR,
1027 FWUPD_ERROR_INVALID_FILE,
1028 "no checksum filename");
1029 return FALSE;
1030 }
1031
1032 /* not all devices have to use the same blob */
1033 blob_fw = as_release_get_blob (rel, tmp);
1034 if (blob_fw == NULL) {
1035 g_set_error_literal (error,
1036 FWUPD_ERROR,
1037 FWUPD_ERROR_READ,
1038 "failed to get firmware blob");
1039 return FALSE;
1040 }
1041
1042 /* possibly convert the version from 0x to dotted */
1043 fu_main_vendor_quirk_release_version (app);
1044
Richard Hughes3d2fc1e2017-06-08 14:26:31 +01001045 /* possibly convert the flashed provide to a GUID */
1046 fu_main_vendor_fixup_provide_value (app);
1047
Richard Hughes3ab17e62016-07-04 12:37:22 +01001048 version = as_release_get_version (rel);
1049 fu_device_set_update_version (device, version);
1050
1051 /* compare to the lowest supported version, if it exists */
1052 tmp = fu_device_get_version_lowest (device);
1053 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
1054 g_set_error (error,
1055 FWUPD_ERROR,
1056 FWUPD_ERROR_VERSION_NEWER,
1057 "Specified firmware is older than the minimum "
1058 "required version '%s < %s'", tmp, version);
1059 return FALSE;
1060 }
1061
1062 /* check the device is locked */
Richard Hughes644562e2016-08-22 10:30:24 +01001063 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes3ab17e62016-07-04 12:37:22 +01001064 g_set_error (error,
1065 FWUPD_ERROR,
1066 FWUPD_ERROR_INTERNAL,
1067 "Device %s is locked",
1068 fu_device_get_id (device));
1069 return FALSE;
1070 }
1071
1072 /* compare the versions of what we have installed */
1073 tmp = fu_device_get_version (device);
1074 if (tmp == NULL) {
1075 g_set_error (error,
1076 FWUPD_ERROR,
1077 FWUPD_ERROR_INTERNAL,
1078 "Device %s does not yet have a current version",
1079 fu_device_get_id (device));
1080 return FALSE;
1081 }
1082 vercmp = as_utils_vercmp (tmp, version);
1083 if (vercmp == 0 && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
1084 g_set_error (error,
1085 FWUPD_ERROR,
1086 FWUPD_ERROR_VERSION_SAME,
1087 "Specified firmware is already installed '%s'",
1088 tmp);
1089 return FALSE;
1090 }
1091 is_downgrade = vercmp > 0;
1092 if (is_downgrade && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
1093 g_set_error (error,
1094 FWUPD_ERROR,
1095 FWUPD_ERROR_VERSION_NEWER,
1096 "Specified firmware is older than installed '%s < %s'",
1097 tmp, version);
1098 return FALSE;
1099 }
1100
1101 /* if any downgrade, we want the global to be true */
1102 if (is_downgrade)
1103 helper->is_downgrade = is_downgrade;
1104
1105 /* verify */
1106 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
1107 return FALSE;
1108
1109 /* success */
1110 g_ptr_array_add (helper->blob_fws, g_bytes_ref (blob_fw));
1111 return TRUE;
1112}
1113
1114static gboolean
1115fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
1116{
Richard Hughesc8646af2016-07-04 13:04:27 +01001117 g_autoptr(GError) error_first = NULL;
Richard Hughes67ec8982015-03-03 11:39:27 +00001118
Richard Hughes5d14def2015-10-07 17:43:10 +01001119 /* load store file which also decompresses firmware */
1120 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
1121 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +00001122 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +00001123
Richard Hughesbd405282016-07-04 13:00:54 +01001124 /* we've specified a specific device; failure is critical */
1125 if (helper->devices->len > 0) {
Richard Hughesf192bf02016-07-22 08:26:43 +01001126 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesbd405282016-07-04 13:00:54 +01001127 FuDevice *device = g_ptr_array_index (helper->devices, i);
1128 if (!fu_main_update_helper_for_device (helper, device, error))
1129 return FALSE;
Richard Hughes5d14def2015-10-07 17:43:10 +01001130 }
Richard Hughesbd405282016-07-04 13:00:54 +01001131 return TRUE;
Richard Hughesfe5cc902016-06-29 10:00:00 +01001132 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001133
Richard Hughesbd405282016-07-04 13:00:54 +01001134 /* if we've not chosen a device, try and find anything in the
1135 * cabinet 'store' that matches any installed device and is updatable */
Richard Hughesf192bf02016-07-22 08:26:43 +01001136 for (guint i = 0; i < helper->priv->devices->len; i++) {
Richard Hughesbd405282016-07-04 13:00:54 +01001137 AsApp *app;
1138 FuDeviceItem *item;
1139 g_autoptr(GError) error_local = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001140
Richard Hughesbd405282016-07-04 13:00:54 +01001141 /* guid found */
1142 item = g_ptr_array_index (helper->priv->devices, i);
1143 app = fu_main_store_get_app_by_guids (helper->store, item->device);
1144 if (app == NULL)
1145 continue;
1146
Richard Hughes404cc512016-12-21 16:09:48 +00001147 /* check we can install it */
1148 if (!fu_main_check_app_versions (app, item->device, &error_local)) {
1149 if (error_first == NULL)
1150 error_first = g_error_copy (error_local);
1151 continue;
1152 }
1153
Richard Hughesbd405282016-07-04 13:00:54 +01001154 /* try this device, error not fatal */
1155 if (!fu_main_update_helper_for_device (helper,
1156 item->device,
1157 &error_local)) {
1158 g_debug ("failed to add %s: %s",
1159 fu_device_get_id (item->device),
1160 error_local->message);
Richard Hughesc8646af2016-07-04 13:04:27 +01001161
1162 /* save this for later */
1163 if (error_first == NULL)
1164 error_first = g_error_copy (error_local);
Richard Hughesbd405282016-07-04 13:00:54 +01001165 continue;
1166 }
1167
1168 /* success */
1169 g_ptr_array_add (helper->devices, g_object_ref (item->device));
1170 }
Richard Hughes346ea882016-07-04 12:31:06 +01001171 if (helper->devices->len == 0) {
Richard Hughesc8646af2016-07-04 13:04:27 +01001172 if (error_first != NULL) {
Richard Hughes4fd38c82016-08-16 17:57:30 +01001173 g_set_error_literal (error,
1174 FWUPD_ERROR,
1175 FWUPD_ERROR_INVALID_FILE,
1176 error_first->message);
Richard Hughesc8646af2016-07-04 13:04:27 +01001177 } else {
Richard Hughes4fd38c82016-08-16 17:57:30 +01001178 g_autofree gchar *guid = NULL;
1179 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesc8646af2016-07-04 13:04:27 +01001180 g_set_error (error,
1181 FWUPD_ERROR,
1182 FWUPD_ERROR_INVALID_FILE,
1183 "no attached hardware matched %s",
1184 guid);
1185 }
Richard Hughes346ea882016-07-04 12:31:06 +01001186 return FALSE;
1187 }
1188
Richard Hughesa4a2c182016-06-29 10:37:05 +01001189 /* sanity check */
1190 if (helper->devices->len != helper->blob_fws->len) {
1191 g_set_error (error,
1192 FWUPD_ERROR,
1193 FWUPD_ERROR_INTERNAL,
Richard Hughes33a518a2016-07-27 15:22:53 +01001194 "not enough firmware blobs (%u) for devices (%u)",
Richard Hughesa4a2c182016-06-29 10:37:05 +01001195 helper->blob_fws->len,
1196 helper->devices->len);
1197 return FALSE;
1198 }
1199
Richard Hughes67ec8982015-03-03 11:39:27 +00001200 return TRUE;
1201}
1202
Richard Hughes18423292015-03-09 17:10:50 +00001203static guint
1204fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
1205{
1206 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +01001207 g_autoptr(GError) error = NULL;
1208 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +00001209
1210 if (priv->proxy_uid == NULL)
1211 return G_MAXUINT;
1212 value = g_dbus_proxy_call_sync (priv->proxy_uid,
1213 "GetConnectionUnixUser",
1214 g_variant_new ("(s)", sender),
1215 G_DBUS_CALL_FLAGS_NONE,
1216 -1,
1217 NULL,
1218 &error);
1219 if (value == NULL) {
1220 g_warning ("Failed to get uid for %s: %s",
1221 sender, error->message);
1222 return G_MAXUINT;
1223 }
1224 g_variant_get (value, "(u)", &uid);
1225 return uid;
1226}
1227
Richard Hughes0e883ee2015-03-18 17:22:33 +00001228static FuDeviceItem *
1229fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
1230{
1231 FuDevice *dev;
Richard Hughescff38bc2016-12-12 12:03:37 +00001232 FuPlugin *plugin;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001233 FuDeviceItem *item = NULL;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001234 FwupdUpdateState update_state;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001235 const gchar *tmp;
Richard Hughes46832432015-09-11 13:43:15 +01001236 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001237
1238 /* not a wildcard */
1239 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1240 item = fu_main_get_item_by_id (priv, id);
1241 if (item == NULL) {
1242 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001243 FWUPD_ERROR,
1244 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001245 "no suitable device found for %s", id);
1246 }
1247 return item;
1248 }
1249
1250 /* allow '*' for any */
1251 devices = fu_pending_get_devices (priv->pending, error);
1252 if (devices == NULL)
1253 return NULL;
Richard Hughesf192bf02016-07-22 08:26:43 +01001254 for (guint i = 0; i < devices->len; i++) {
Richard Hughes0e883ee2015-03-18 17:22:33 +00001255 dev = g_ptr_array_index (devices, i);
Richard Hughes8e9762d2016-03-17 10:14:15 +00001256 update_state = fu_device_get_update_state (dev);
1257 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001258 continue;
Richard Hughes8e9762d2016-03-17 10:14:15 +00001259 if (update_state == FWUPD_UPDATE_STATE_PENDING)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001260 continue;
1261
1262 /* if the device is not still connected, fake a FuDeviceItem */
1263 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
1264 if (item == NULL) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001265 tmp = fu_device_get_plugin (dev);
1266 plugin = fu_main_get_plugin_by_name (priv, tmp);
1267 if (plugin == NULL) {
Richard Hughes0e883ee2015-03-18 17:22:33 +00001268 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001269 FWUPD_ERROR,
1270 FWUPD_ERROR_NOT_FOUND,
Richard Hughescff38bc2016-12-12 12:03:37 +00001271 "no plugin %s found", tmp);
Richard Hughes31bfba32016-12-12 14:29:21 +00001272 return NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001273 }
1274 item = g_new0 (FuDeviceItem, 1);
1275 item->device = g_object_ref (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001276 item->plugin = g_object_ref (plugin);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001277 g_ptr_array_add (priv->devices, item);
1278
1279 /* FIXME: just a boolean on FuDeviceItem? */
1280 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
1281 }
1282 break;
1283 }
1284
1285 /* no device found */
1286 if (item == NULL) {
1287 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001288 FWUPD_ERROR,
1289 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +00001290 "no suitable devices found");
1291 }
1292 return item;
1293}
1294
Richard Hughes63bbbf52015-04-14 16:12:16 +01001295static const gchar *
1296fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
1297{
Richard Hughesfe5cc902016-06-29 10:00:00 +01001298 gboolean all_removable = TRUE;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001299 gboolean is_trusted;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001300
1301 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +01001302 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +01001303
Richard Hughesfe5cc902016-06-29 10:00:00 +01001304 /* any non-removable means false */
Richard Hughesf192bf02016-07-22 08:26:43 +01001305 for (guint i = 0; i < helper->devices->len; i ++) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001306 FuDevice *device = g_ptr_array_index (helper->devices, i);
Richard Hughes644562e2016-08-22 10:30:24 +01001307 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INTERNAL)) {
Richard Hughesfe5cc902016-06-29 10:00:00 +01001308 all_removable = FALSE;
1309 break;
1310 }
1311 }
1312
Richard Hughes63bbbf52015-04-14 16:12:16 +01001313 /* relax authentication checks for removable devices */
Richard Hughesfe5cc902016-06-29 10:00:00 +01001314 if (all_removable) {
Richard Hughesdb468ee2016-06-29 10:10:47 +01001315 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001316 return "org.freedesktop.fwupd.downgrade-hotplug";
1317 if (is_trusted)
1318 return "org.freedesktop.fwupd.update-hotplug-trusted";
1319 return "org.freedesktop.fwupd.update-hotplug";
1320 }
1321
1322 /* internal device */
Richard Hughesdb468ee2016-06-29 10:10:47 +01001323 if (helper->is_downgrade)
Richard Hughes63bbbf52015-04-14 16:12:16 +01001324 return "org.freedesktop.fwupd.downgrade-internal";
1325 if (is_trusted)
1326 return "org.freedesktop.fwupd.update-internal-trusted";
1327 return "org.freedesktop.fwupd.update-internal";
1328}
1329
Richard Hughesae0efdc2015-06-24 16:18:29 +01001330static gboolean
Richard Hughes1b50d962017-06-02 12:23:00 +01001331fu_main_daemon_update_metadata (FuMainPrivate *priv, const gchar *remote_id,
1332 gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +01001333{
Richard Hughesf2fca012015-10-30 08:44:44 +00001334 const guint8 *data;
Richard Hughesf2fca012015-10-30 08:44:44 +00001335 gsize size;
Richard Hughes727664f2015-10-27 09:56:04 +00001336 GPtrArray *apps;
Richard Hughesbb840ce2015-10-30 08:47:24 +00001337 g_autofree gchar *xml = NULL;
Richard Hughes727664f2015-10-27 09:56:04 +00001338 g_autoptr(AsStore) store = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001339 g_autoptr(GBytes) bytes = NULL;
1340 g_autoptr(GBytes) bytes_raw = NULL;
1341 g_autoptr(GBytes) bytes_sig = NULL;
1342 g_autoptr(FuKeyring) kr = NULL;
1343 g_autoptr(GConverter) converter = NULL;
1344 g_autoptr(GFile) file = NULL;
Richard Hughese643fb22017-04-12 14:09:04 +01001345 g_autoptr(GFile) file_parent = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001346 g_autoptr(GInputStream) stream_fd = NULL;
1347 g_autoptr(GInputStream) stream = NULL;
1348 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001349
Richard Hughesae0efdc2015-06-24 16:18:29 +01001350 /* read the entire file into memory */
1351 stream_fd = g_unix_input_stream_new (fd, TRUE);
1352 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1353 if (bytes_raw == NULL)
1354 return FALSE;
Richard Hughes985ac072017-06-06 17:07:41 +01001355
1356 /* read signature */
1357 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1358 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1359 if (bytes_sig == NULL)
1360 return FALSE;
1361
1362 /* verify file */
1363 kr = fu_keyring_new ();
1364 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
1365 return FALSE;
1366 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
1367 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001368
1369 /* peek the file type and get data */
Richard Hughesf2fca012015-10-30 08:44:44 +00001370 data = g_bytes_get_data (bytes_raw, &size);
1371 if (size < 2) {
1372 g_set_error_literal (error,
1373 FWUPD_ERROR,
1374 FWUPD_ERROR_INVALID_FILE,
1375 "file is too small");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001376 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001377 }
1378 if (data[0] == 0x1f && data[1] == 0x8b) {
Richard Hughes985ac072017-06-06 17:07:41 +01001379 g_autoptr(GInputStream) stream_buf = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001380 g_debug ("using GZip decompressor for data");
Richard Hughesae0efdc2015-06-24 16:18:29 +01001381 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
Richard Hughes985ac072017-06-06 17:07:41 +01001382 stream_buf = g_memory_input_stream_new ();
1383 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001384 stream = g_converter_input_stream_new (stream_buf, converter);
1385 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
1386 if (bytes == NULL)
1387 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +00001388 } else if (data[0] == '<' && data[1] == '?') {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001389 g_debug ("using no decompressor for data");
1390 bytes = g_bytes_ref (bytes_raw);
1391 } else {
1392 g_set_error (error,
1393 FWUPD_ERROR,
1394 FWUPD_ERROR_INVALID_FILE,
1395 "file type '0x%02x,0x%02x' not supported",
Richard Hughesf2fca012015-10-30 08:44:44 +00001396 data[0], data[1]);
Richard Hughesae0efdc2015-06-24 16:18:29 +01001397 return FALSE;
1398 }
1399
Richard Hughes727664f2015-10-27 09:56:04 +00001400 /* load the store locally until we know it is valid */
1401 store = as_store_new ();
Richard Hughesbb840ce2015-10-30 08:47:24 +00001402 data = g_bytes_get_data (bytes, &size);
1403 xml = g_strndup ((const gchar *) data, size);
1404 if (!as_store_from_xml (store, xml, NULL, error))
Richard Hughesae0efdc2015-06-24 16:18:29 +01001405 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001406
Richard Hughes727664f2015-10-27 09:56:04 +00001407 /* add the new application from the store */
1408 as_store_remove_all (priv->store);
1409 apps = as_store_get_apps (store);
Richard Hughesf192bf02016-07-22 08:26:43 +01001410 for (guint i = 0; i < apps->len; i++) {
Richard Hughes727664f2015-10-27 09:56:04 +00001411 AsApp *app = g_ptr_array_index (apps, i);
Richard Hughes1b50d962017-06-02 12:23:00 +01001412 if (remote_id != NULL && remote_id[0] != '\0')
1413 as_app_add_metadata (app, "fwupd::RemoteID", remote_id);
Richard Hughes727664f2015-10-27 09:56:04 +00001414 as_store_add_app (priv->store, app);
1415 }
1416
Richard Hughese643fb22017-04-12 14:09:04 +01001417 /* ensure directory exists */
1418 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
1419 file_parent = g_file_get_parent (file);
1420 if (!g_file_query_exists (file_parent, NULL)) {
1421 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
1422 return FALSE;
1423 }
1424
Richard Hughesae0efdc2015-06-24 16:18:29 +01001425 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +01001426 as_store_set_api_version (priv->store, 0.9);
Richard Hughesd4bdfd62017-06-02 12:25:52 +01001427 as_store_set_origin (priv->store, NULL);
Richard Hughes033ccba2015-09-10 14:51:28 +01001428 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +01001429 AS_NODE_TO_XML_FLAG_ADD_HEADER |
1430 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
1431 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
1432 NULL, error)) {
1433 return FALSE;
1434 }
1435
1436 return TRUE;
1437}
1438
Richard Hughes033ccba2015-09-10 14:51:28 +01001439static gboolean
1440fu_main_store_delay_cb (gpointer user_data)
1441{
Richard Hughes033ccba2015-09-10 14:51:28 +01001442 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf192bf02016-07-22 08:26:43 +01001443 GPtrArray *apps;
Richard Hughes033ccba2015-09-10 14:51:28 +01001444
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001445 /* print what we've got */
Richard Hughes033ccba2015-09-10 14:51:28 +01001446 apps = as_store_get_apps (priv->store);
Richard Hughes0a36f442016-01-06 14:57:20 +00001447 if (apps->len == 0) {
1448 g_debug ("no devices in store");
1449 } else {
1450 g_debug ("devices now in store:");
Richard Hughesf192bf02016-07-22 08:26:43 +01001451 for (guint i = 0; i < apps->len; i++) {
1452 AsApp *app = g_ptr_array_index (apps, i);
Richard Hughes33a518a2016-07-27 15:22:53 +01001453 g_debug ("%u\t%s\t%s", i + 1,
Richard Hughes0a36f442016-01-06 14:57:20 +00001454 as_app_get_id (app),
1455 as_app_get_name (app, NULL));
1456 }
Richard Hughes033ccba2015-09-10 14:51:28 +01001457 }
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001458
1459 /* are any devices now supported? */
Richard Hughesf192bf02016-07-22 08:26:43 +01001460 for (guint i = 0; i < priv->devices->len; i++) {
1461 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughesdde7a2f2016-04-28 15:06:52 +01001462 if (fu_main_get_updates_item_update (priv, item))
1463 fu_main_emit_device_changed (priv, item->device);
1464 }
1465
Richard Hughes033ccba2015-09-10 14:51:28 +01001466 priv->store_changed_id = 0;
1467 return G_SOURCE_REMOVE;
1468}
1469
Richard Hughes033ccba2015-09-10 14:51:28 +01001470static void
1471fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
1472{
1473 if (priv->store_changed_id != 0)
1474 return;
1475 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
1476}
1477
Richard Hughes654f6b82016-04-25 12:29:48 +01001478static gboolean
1479fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item)
1480{
1481 AsApp *app;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001482 AsRelease *release;
Richard Hughes654f6b82016-04-25 12:29:48 +01001483 GPtrArray *releases;
1484 const gchar *tmp;
1485 const gchar *version;
Richard Hughes404cc512016-12-21 16:09:48 +00001486 g_autoptr(GError) error = NULL;
Richard Hughes654f6b82016-04-25 12:29:48 +01001487 g_autoptr(GPtrArray) updates_list = NULL;
1488
1489 /* get device version */
1490 version = fu_device_get_version (item->device);
1491 if (version == NULL)
1492 return FALSE;
1493
Richard Hughes99147f12016-05-17 09:35:04 +01001494 /* match the GUIDs in the XML */
1495 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughes654f6b82016-04-25 12:29:48 +01001496 if (app == NULL)
1497 return FALSE;
1498
1499 /* possibly convert the version from 0x to dotted */
1500 fu_main_vendor_quirk_release_version (app);
1501
Richard Hughes3d2fc1e2017-06-08 14:26:31 +01001502 /* possibly convert the flashed provide to a GUID */
1503 fu_main_vendor_fixup_provide_value (app);
1504
Richard Hughes654f6b82016-04-25 12:29:48 +01001505 /* get latest release */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001506 release = as_app_get_release_default (app);
1507 if (release == NULL) {
Richard Hughes14d17642016-08-17 12:03:03 +01001508 g_debug ("%s [%s] has no firmware update metadata",
1509 fu_device_get_id (item->device),
1510 fu_device_get_name (item->device));
Richard Hughes654f6b82016-04-25 12:29:48 +01001511 return FALSE;
1512 }
1513
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001514 /* supported in metadata */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001515 fwupd_device_add_flag (fwupd_result_get_device (FWUPD_RESULT (item->device)),
1516 FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes4f4e1f32016-04-28 14:29:29 +01001517
Richard Hughes654f6b82016-04-25 12:29:48 +01001518 /* check if actually newer than what we have installed */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001519 if (as_utils_vercmp (as_release_get_version (release), version) <= 0) {
Richard Hughes654f6b82016-04-25 12:29:48 +01001520 g_debug ("%s has no firmware updates",
1521 fu_device_get_id (item->device));
1522 return FALSE;
1523 }
1524
Richard Hughes404cc512016-12-21 16:09:48 +00001525 /* check we can install it */
1526 if (!fu_main_check_app_versions (app, item->device, &error)) {
1527 g_debug ("can not be installed: %s", error->message);
1528 return FALSE;
1529 }
1530
Richard Hughes6561c8d2016-05-17 16:24:09 +01001531 /* only show devices that can be updated */
Richard Hughes644562e2016-08-22 10:30:24 +01001532 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
1533 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
Richard Hughes14d17642016-08-17 12:03:03 +01001534 g_debug ("ignoring %s [%s] as not updatable live or offline",
1535 fu_device_get_id (item->device),
1536 fu_device_get_name (item->device));
Richard Hughes6561c8d2016-05-17 16:24:09 +01001537 return FALSE;
1538 }
1539
Richard Hughes654f6b82016-04-25 12:29:48 +01001540 /* add application metadata */
1541 fu_device_set_update_id (item->device, as_app_get_id (app));
1542 tmp = as_app_get_developer_name (app, NULL);
1543 if (tmp != NULL)
1544 fu_device_set_update_vendor (item->device, tmp);
1545 tmp = as_app_get_name (app, NULL);
1546 if (tmp != NULL)
1547 fu_device_set_update_name (item->device, tmp);
1548 tmp = as_app_get_comment (app, NULL);
1549 if (tmp != NULL)
1550 fu_device_set_update_summary (item->device, tmp);
1551 tmp = as_app_get_description (app, NULL);
1552 if (tmp != NULL)
1553 fu_device_set_description (item->device, tmp);
1554 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
1555 if (tmp != NULL)
1556 fu_device_set_update_homepage (item->device, tmp);
1557 tmp = as_app_get_project_license (app);
1558 if (tmp != NULL)
1559 fu_device_set_update_license (item->device, tmp);
Richard Hughes90bcd052017-06-02 12:27:04 +01001560 tmp = as_app_get_metadata_item (app, "fwupd::RemoteID");
1561 if (tmp != NULL)
1562 fu_device_set_update_remote_id (item->device, tmp);
Richard Hughesfa782a32016-08-18 18:34:59 +01001563#if AS_CHECK_VERSION(0,6,1)
1564 tmp = as_app_get_unique_id (app);
1565 if (tmp != NULL)
1566 fu_device_set_unique_id (item->device, tmp);
1567#else
1568 fu_device_set_unique_id (item->device, as_app_get_id (app));
1569#endif
Richard Hughes654f6b82016-04-25 12:29:48 +01001570
1571 /* add release information */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001572 fu_main_set_release_from_item (fwupd_result_get_release (FWUPD_RESULT (item->device)), release);
Richard Hughes654f6b82016-04-25 12:29:48 +01001573
1574 /* get the list of releases newer than the one installed */
1575 updates_list = g_ptr_array_new ();
1576 releases = as_app_get_releases (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001577 for (guint i = 0; i < releases->len; i++) {
Richard Hughes1642b3b2017-06-05 17:40:08 +01001578 release = g_ptr_array_index (releases, i);
1579 if (as_utils_vercmp (as_release_get_version (release), version) <= 0)
Richard Hughes654f6b82016-04-25 12:29:48 +01001580 continue;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001581 tmp = as_release_get_description (release, NULL);
Richard Hughes654f6b82016-04-25 12:29:48 +01001582 if (tmp == NULL)
1583 continue;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001584 g_ptr_array_add (updates_list, release);
Richard Hughes654f6b82016-04-25 12:29:48 +01001585 }
1586
1587 /* no prefix on each release */
1588 if (updates_list->len == 1) {
Richard Hughes1642b3b2017-06-05 17:40:08 +01001589 release = g_ptr_array_index (updates_list, 0);
Richard Hughes654f6b82016-04-25 12:29:48 +01001590 fu_device_set_update_description (item->device,
Richard Hughes1642b3b2017-06-05 17:40:08 +01001591 as_release_get_description (release, NULL));
Richard Hughes654f6b82016-04-25 12:29:48 +01001592 } else {
1593 g_autoptr(GString) update_desc = NULL;
1594 update_desc = g_string_new ("");
1595
1596 /* get the descriptions with a version prefix */
Richard Hughesf192bf02016-07-22 08:26:43 +01001597 for (guint i = 0; i < updates_list->len; i++) {
Richard Hughes1642b3b2017-06-05 17:40:08 +01001598 release = g_ptr_array_index (updates_list, i);
Richard Hughes654f6b82016-04-25 12:29:48 +01001599 g_string_append_printf (update_desc,
1600 "<p>%s:</p>%s",
Richard Hughes1642b3b2017-06-05 17:40:08 +01001601 as_release_get_version (release),
1602 as_release_get_description (release, NULL));
Richard Hughes654f6b82016-04-25 12:29:48 +01001603 }
1604 if (update_desc->len > 0)
1605 fu_device_set_update_description (item->device, update_desc->str);
1606 }
1607
1608 /* success */
1609 return TRUE;
1610}
1611
Richard Hughesf192bf02016-07-22 08:26:43 +01001612/* find any updates using the AppStream metadata */
Richard Hughes7708a0f2015-07-21 08:41:22 +01001613static GPtrArray *
1614fu_main_get_updates (FuMainPrivate *priv, GError **error)
1615{
Richard Hughesf192bf02016-07-22 08:26:43 +01001616 GPtrArray *updates = g_ptr_array_new ();
1617 for (guint i = 0; i < priv->devices->len; i++) {
1618 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
Richard Hughes654f6b82016-04-25 12:29:48 +01001619 if (fu_main_get_updates_item_update (priv, item))
1620 g_ptr_array_add (updates, item);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001621 }
Richard Hughes7708a0f2015-07-21 08:41:22 +01001622 return updates;
1623}
1624
Richard Hughese4a100c2017-06-04 21:23:50 +01001625/* find releases for a device */
1626static GVariant *
1627fu_main_get_releases_to_variant (FuMainPrivate *priv, FuDeviceItem *item, GError **error)
1628{
1629 GPtrArray *device_guids;
1630 GVariantBuilder builder;
1631 g_autoptr(GPtrArray) results = NULL;
1632
1633 /* get all the results for the device */
1634 results = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1635 device_guids = fu_device_get_guids (item->device);
1636 for (guint i = 0; i < device_guids->len; i++) {
1637 GPtrArray *releases;
1638 const gchar *guid = g_ptr_array_index (device_guids, i);
1639 AsApp *app = as_store_get_app_by_provide (priv->store,
1640 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
1641 guid);
1642 if (app == NULL)
1643 continue;
1644 releases = as_app_get_releases (app);
1645 for (guint j = 0; j < releases->len; j++) {
1646 AsRelease *release = g_ptr_array_index (releases, j);
1647 FwupdRelease *rel = fwupd_release_new ();
1648 fu_main_set_release_from_item (rel, release);
1649 g_ptr_array_add (results, rel);
1650 }
1651 }
1652
1653 /* no devices */
1654 if (results->len == 0) {
1655 g_set_error_literal (error,
1656 FWUPD_ERROR,
1657 FWUPD_ERROR_NOTHING_TO_DO,
1658 "No releases for device");
1659 return NULL;
1660 }
1661
1662 /* convert the objects to a variant */
1663 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1664 for (guint i = 0; i < results->len; i++) {
1665 GVariant *tmp;
1666 FwupdRelease *rel = g_ptr_array_index (results, i);
1667 tmp = fwupd_release_to_data (rel, "a{sv}"); //FIXME?
1668 g_variant_builder_add_value (&builder, tmp);
1669 }
1670 return g_variant_new ("(aa{sv})", &builder);
1671}
1672
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001673static AsStore *
1674fu_main_get_store_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1675{
Richard Hughes5c066ad2016-08-18 18:38:06 +01001676 g_autofree gchar *checksum = NULL;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001677 g_autoptr(AsStore) store = NULL;
1678 g_autoptr(GBytes) blob_cab = NULL;
1679 g_autoptr(GError) error_local = NULL;
1680 g_autoptr(GInputStream) stream = NULL;
1681
1682 /* read the entire fd to a data blob */
1683 stream = g_unix_input_stream_new (fd, TRUE);
1684 blob_cab = g_input_stream_read_bytes (stream,
1685 FU_MAIN_FIRMWARE_SIZE_MAX,
1686 NULL, &error_local);
1687 if (blob_cab == NULL){
1688 g_set_error_literal (error,
1689 FWUPD_ERROR,
1690 FWUPD_ERROR_INVALID_FILE,
1691 error_local->message);
1692 return NULL;
1693 }
1694
1695 /* load file */
1696 store = as_store_new ();
1697 if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) {
1698 g_set_error_literal (error,
1699 FWUPD_ERROR,
1700 FWUPD_ERROR_INVALID_FILE,
1701 error_local->message);
1702 return NULL;
1703 }
Richard Hughes5c066ad2016-08-18 18:38:06 +01001704
1705 /* get a checksum of the file and use it as the origin */
Richard Hughesfe461b62017-06-06 14:18:14 +01001706 checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
Richard Hughes5c066ad2016-08-18 18:38:06 +01001707 g_bytes_get_data (blob_cab, NULL),
1708 g_bytes_get_size (blob_cab));
1709 as_store_set_origin (store, checksum);
1710
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001711 return g_steal_pointer (&store);
1712}
1713
1714static FwupdResult *
1715fu_main_get_result_from_app (FuMainPrivate *priv, AsApp *app, GError **error)
1716{
1717 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001718 AsRelease *release;
1719 FwupdDevice *dev;
1720 FwupdRelease *rel;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001721 GPtrArray *provides;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001722 g_autoptr(FwupdResult) res = NULL;
1723
1724 res = fwupd_result_new ();
Richard Hughes1642b3b2017-06-05 17:40:08 +01001725 dev = fwupd_result_get_device (res);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001726 provides = as_app_get_provides (app);
Richard Hughesf192bf02016-07-22 08:26:43 +01001727 for (guint i = 0; i < provides->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001728 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1729 FuDeviceItem *item;
1730 const gchar *guid;
1731
1732 /* not firmware */
1733 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
1734 continue;
1735
1736 /* is a online or offline update appropriate */
1737 guid = as_provide_get_value (prov);
1738 if (guid == NULL)
1739 continue;
1740 item = fu_main_get_item_by_guid (priv, guid);
1741 if (item != NULL) {
Richard Hughes1642b3b2017-06-05 17:40:08 +01001742 fwupd_device_set_flags (dev, fu_device_get_flags (item->device));
1743 fwupd_device_set_id (dev, fu_device_get_id (item->device));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001744 }
1745
1746 /* add GUID */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001747 fwupd_device_add_guid (dev, guid);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001748 }
Richard Hughes1642b3b2017-06-05 17:40:08 +01001749 if (fwupd_device_get_guids(dev)->len == 0) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001750 g_set_error_literal (error,
1751 FWUPD_ERROR,
1752 FWUPD_ERROR_INTERNAL,
1753 "component has no GUIDs");
1754 return NULL;
1755 }
1756
Richard Hughes404cc512016-12-21 16:09:48 +00001757 /* check we can install it */
1758 if (!fu_main_check_app_versions (app, NULL, error))
1759 return NULL;
1760
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001761 /* verify trust */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001762 release = as_app_get_release_default (app);
1763 if (!fu_main_get_release_trust_flags (release, &trust_flags, error))
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001764 return NULL;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001765 fwupd_result_set_update_trust_flags (res, trust_flags);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001766
1767 /* possibly convert the version from 0x to dotted */
1768 fu_main_vendor_quirk_release_version (app);
1769
Richard Hughes3d2fc1e2017-06-08 14:26:31 +01001770 /* possibly convert the flashed provide to a GUID */
1771 fu_main_vendor_fixup_provide_value (app);
1772
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001773 /* create a result with all the metadata in */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001774 fwupd_device_set_description (dev, as_app_get_description (app, NULL));
1775 rel = fwupd_result_get_release (res);
1776 fwupd_release_set_homepage (rel, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
1777 fwupd_release_set_license (rel, as_app_get_project_license (app));
1778 fwupd_release_set_name (rel, as_app_get_name (app, NULL));
1779 fwupd_release_set_summary (rel, as_app_get_comment (app, NULL));
1780 fwupd_release_set_vendor (rel, as_app_get_developer_name (app, NULL));
Richard Hughesfa782a32016-08-18 18:34:59 +01001781#if AS_CHECK_VERSION(0,6,1)
1782 fwupd_result_set_unique_id (res, as_app_get_unique_id (app));
1783#else
1784 fwupd_result_set_unique_id (res, as_app_get_id (app));
1785#endif
Richard Hughes1642b3b2017-06-05 17:40:08 +01001786 fwupd_release_set_appstream_id (rel, as_app_get_id (app));
1787 fu_main_set_release_from_item (rel, release);
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001788 return g_steal_pointer (&res);
1789}
1790
1791static GVariant *
1792fu_main_get_details_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1793{
1794 AsApp *app = NULL;
1795 GPtrArray *apps;
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001796 g_autoptr(AsStore) store = NULL;
1797 g_autoptr(FwupdResult) res = NULL;
1798
1799 store = fu_main_get_store_from_fd (priv, fd, error);
1800 if (store == NULL)
1801 return NULL;
1802
1803 /* get all apps */
1804 apps = as_store_get_apps (store);
1805 if (apps->len == 0) {
1806 g_set_error_literal (error,
1807 FWUPD_ERROR,
1808 FWUPD_ERROR_INVALID_FILE,
1809 "no components");
1810 return NULL;
1811 }
1812 if (apps->len > 1) {
1813 /* we've got a .cab file with multiple components,
1814 * so try to find the first thing that's installed */
Richard Hughesf192bf02016-07-22 08:26:43 +01001815 for (guint i = 0; i < priv->devices->len; i++) {
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001816 FuDeviceItem *item = g_ptr_array_index (priv->devices, i);
1817 app = fu_main_store_get_app_by_guids (store, item->device);
1818 if (app != NULL)
1819 break;
1820 }
1821 }
1822
1823 /* well, we've tried our best, just show the first entry */
1824 if (app == NULL)
1825 app = AS_APP (g_ptr_array_index (apps, 0));
1826
Richard Hughes404cc512016-12-21 16:09:48 +00001827 /* check we can install it */
1828 if (!fu_main_check_app_versions (app, NULL, error))
1829 return FALSE;
1830
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001831 /* create a result with all the metadata in */
Richard Hughes5c066ad2016-08-18 18:38:06 +01001832 as_app_set_origin (app, as_store_get_origin (store));
Richard Hughes8ac07dc2016-05-29 08:32:55 +01001833 res = fu_main_get_result_from_app (priv, app, error);
1834 if (res == NULL)
1835 return NULL;
1836 return fwupd_result_to_data (res, "(a{sv})");
1837}
1838
Richard Hughes7289a6b2016-05-29 09:27:47 +01001839static GVariant *
1840fu_main_get_details_local_from_fd (FuMainPrivate *priv, gint fd, GError **error)
1841{
1842 GPtrArray *apps;
1843 GVariantBuilder builder;
Richard Hughes7289a6b2016-05-29 09:27:47 +01001844 g_autoptr(AsStore) store = NULL;
1845
1846 store = fu_main_get_store_from_fd (priv, fd, error);
1847 if (store == NULL)
1848 return NULL;
1849
1850 /* get all apps */
1851 apps = as_store_get_apps (store);
1852 if (apps->len == 0) {
1853 g_set_error_literal (error,
1854 FWUPD_ERROR,
1855 FWUPD_ERROR_INVALID_FILE,
1856 "no components");
1857 return NULL;
1858 }
1859
1860 /* create results with all the metadata in */
1861 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughesf192bf02016-07-22 08:26:43 +01001862 for (guint i = 0; i < apps->len; i++) {
Richard Hughes7289a6b2016-05-29 09:27:47 +01001863 g_autoptr(FwupdResult) res = NULL;
1864 AsApp *app = g_ptr_array_index (apps, i);
1865 GVariant *tmp;
Richard Hughes404cc512016-12-21 16:09:48 +00001866
1867 /* check we can install it */
1868 if (!fu_main_check_app_versions (app, NULL, error))
1869 return NULL;
1870
Richard Hughes5c066ad2016-08-18 18:38:06 +01001871 as_app_set_origin (app, as_store_get_origin (store));
Richard Hughes7289a6b2016-05-29 09:27:47 +01001872 res = fu_main_get_result_from_app (priv, app, error);
1873 if (res == NULL)
1874 return NULL;
1875 tmp = fwupd_result_to_data (res, "{sa{sv}}");
1876 g_variant_builder_add_value (&builder, tmp);
1877 }
1878 return g_variant_new ("(a{sa{sv}})", &builder);
1879}
1880
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001881static void
1882fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
1883 const gchar *object_path, const gchar *interface_name,
1884 const gchar *method_name, GVariant *parameters,
1885 GDBusMethodInvocation *invocation, gpointer user_data)
1886{
1887 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1888 GVariant *val;
Richard Hughes060af612016-08-17 17:32:34 +01001889 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001890
1891 /* return 'as' */
1892 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughesf508e762015-02-27 12:49:36 +00001893 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001894 val = fu_main_device_array_to_variant (priv->devices, &error);
1895 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001896 if (g_error_matches (error,
1897 FWUPD_ERROR,
1898 FWUPD_ERROR_NOTHING_TO_DO)) {
1899 g_prefix_error (&error, "No detected devices: ");
1900 }
Richard Hughes060af612016-08-17 17:32:34 +01001901 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001902 return;
1903 }
Richard Hughes060af612016-08-17 17:32:34 +01001904 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001905 return;
1906 }
1907
1908 /* return 'as' */
1909 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +01001910 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +01001911 g_debug ("Called %s()", method_name);
1912 updates = fu_main_get_updates (priv, &error);
1913 if (updates == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001914 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001915 return;
1916 }
1917 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001918 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +01001919 if (g_error_matches (error,
1920 FWUPD_ERROR,
1921 FWUPD_ERROR_NOTHING_TO_DO)) {
Richard Hughese8fb74a2016-12-12 16:16:35 +00001922 g_prefix_error (&error, "No devices can be updated: ");
Richard Hughes9d76a872015-09-17 12:49:07 +01001923 }
Richard Hughes060af612016-08-17 17:32:34 +01001924 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a38c032015-03-17 20:58:46 +00001925 return;
1926 }
Richard Hughes060af612016-08-17 17:32:34 +01001927 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001928 return;
1929 }
1930
Richard Hughese4a100c2017-06-04 21:23:50 +01001931 /* return variant */
1932 if (g_strcmp0 (method_name, "GetReleases") == 0) {
1933 FuDeviceItem *item;
1934 const gchar *device_id = NULL;
1935 g_autoptr(GPtrArray) releases = NULL;
1936
1937 g_variant_get (parameters, "(&s)", &device_id);
1938 g_debug ("Called %s(%s)", method_name, device_id);
1939
1940 /* find the device */
1941 item = fu_main_get_item_by_id (priv, device_id);
1942 if (item == NULL) {
1943 g_set_error (&error,
1944 FWUPD_ERROR,
1945 FWUPD_ERROR_INTERNAL,
1946 "no device with ID %s",
1947 device_id);
1948 fu_main_invocation_return_error (priv, invocation, error);
1949 return;
1950 }
1951 val = fu_main_get_releases_to_variant (priv, item, &error);
1952 if (val == NULL) {
1953 if (g_error_matches (error,
1954 FWUPD_ERROR,
1955 FWUPD_ERROR_NOTHING_TO_DO)) {
1956 g_prefix_error (&error, "No releases found: ");
1957 }
1958 fu_main_invocation_return_error (priv, invocation, error);
1959 return;
1960 }
1961 fu_main_invocation_return_value (priv, invocation, val);
1962 return;
1963 }
1964
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001965 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +00001966 if (g_strcmp0 (method_name, "ClearResults") == 0) {
1967 FuDeviceItem *item = NULL;
1968 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001969
1970 g_variant_get (parameters, "(&s)", &id);
1971 g_debug ("Called %s(%s)", method_name, id);
1972
1973 /* find device */
1974 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1975 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01001976 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001977 return;
1978 }
1979
Richard Hughescff38bc2016-12-12 12:03:37 +00001980 /* call into the plugin */
1981 if (!fu_plugin_runner_clear_results (item->plugin, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01001982 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001983 return;
1984 }
1985
1986 /* success */
Richard Hughes060af612016-08-17 17:32:34 +01001987 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001988 return;
1989 }
1990
1991 /* return 'a{sv}' */
1992 if (g_strcmp0 (method_name, "GetResults") == 0) {
1993 FuDeviceItem *item = NULL;
1994 const gchar *id = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001995
1996 g_variant_get (parameters, "(&s)", &id);
1997 g_debug ("Called %s(%s)", method_name, id);
1998
1999 /* find device */
2000 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
2001 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002002 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002003 return;
2004 }
2005
Richard Hughescff38bc2016-12-12 12:03:37 +00002006 /* call into the plugin */
2007 if (!fu_plugin_runner_get_results (item->plugin, item->device, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002008 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002009 return;
2010 }
2011
Richard Hughes2db526d2016-08-25 15:07:23 +01002012 /* ensure the unique ID is set */
2013 if (fwupd_result_get_unique_id (FWUPD_RESULT (item->device)) == NULL) {
Richard Hughes4ced4662016-08-26 11:02:31 +01002014 g_autofree gchar *id2 = NULL;
Richard Hughes2db526d2016-08-25 15:07:23 +01002015 FwupdResult *res = FWUPD_RESULT (item->device);
Richard Hughes1642b3b2017-06-05 17:40:08 +01002016 FwupdDevice *dev = fwupd_result_get_device (res);
Richard Hughes27aad5a2016-08-29 16:12:23 +01002017#if AS_CHECK_VERSION(0,6,1)
Richard Hughes4ced4662016-08-26 11:02:31 +01002018 id2 = as_utils_unique_id_build (AS_APP_SCOPE_SYSTEM,
2019 AS_BUNDLE_KIND_UNKNOWN,
2020 NULL,
2021 AS_APP_KIND_FIRMWARE,
Richard Hughes1642b3b2017-06-05 17:40:08 +01002022 fwupd_device_get_name (dev),
2023 fwupd_device_get_version (dev));
Richard Hughes27aad5a2016-08-29 16:12:23 +01002024#else
2025 id2 = g_strdup_printf ("system/*/*/firmware/%s/%s",
Richard Hughes1642b3b2017-06-05 17:40:08 +01002026 fwupd_device_get_name (dev),
2027 fwupd_device_get_version (dev));
Richard Hughes27aad5a2016-08-29 16:12:23 +01002028#endif
Richard Hughes4ced4662016-08-26 11:02:31 +01002029 fwupd_result_set_unique_id (res, id2);
Richard Hughes2db526d2016-08-25 15:07:23 +01002030 }
2031
Richard Hughes0e883ee2015-03-18 17:22:33 +00002032 /* success */
Richard Hughes8e9762d2016-03-17 10:14:15 +00002033 val = fwupd_result_to_data (FWUPD_RESULT (item->device), "(a{sv})");
Richard Hughes060af612016-08-17 17:32:34 +01002034 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes0e883ee2015-03-18 17:22:33 +00002035 return;
2036 }
2037
2038 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01002039 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
2040 GDBusMessage *message;
2041 GUnixFDList *fd_list;
2042 gint fd_data;
2043 gint fd_sig;
Richard Hughesae0efdc2015-06-24 16:18:29 +01002044
Richard Hughes1b50d962017-06-02 12:23:00 +01002045 g_debug ("Called %s()", method_name);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002046 message = g_dbus_method_invocation_get_message (invocation);
2047 fd_list = g_dbus_message_get_unix_fd_list (message);
2048 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
Richard Hughes060af612016-08-17 17:32:34 +01002049 g_set_error (&error,
2050 FWUPD_ERROR,
2051 FWUPD_ERROR_INTERNAL,
2052 "invalid handle");
2053 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002054 return;
2055 }
2056 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
2057 if (fd_data < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002058 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002059 return;
2060 }
2061 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
2062 if (fd_sig < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002063 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002064 return;
2065 }
Richard Hughes1b50d962017-06-02 12:23:00 +01002066 if (!fu_main_daemon_update_metadata (priv, NULL, fd_data, fd_sig, &error)) {
2067 g_prefix_error (&error, "failed to update metadata: ");
2068 fu_main_invocation_return_error (priv, invocation, error);
2069 return;
2070 }
2071 fu_main_invocation_return_value (priv, invocation, NULL);
2072 return;
2073 }
2074
2075 /* return '' */
2076 if (g_strcmp0 (method_name, "UpdateMetadataWithId") == 0) {
2077 GDBusMessage *message;
2078 GUnixFDList *fd_list;
2079 const gchar *id = NULL;
2080 gint fd_data;
2081 gint fd_sig;
2082
2083 g_variant_get (parameters, "(&shh)", &id);
2084 g_debug ("Called %s(%s)", method_name, id);
2085
2086 message = g_dbus_method_invocation_get_message (invocation);
2087 fd_list = g_dbus_message_get_unix_fd_list (message);
2088 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
2089 g_set_error (&error,
2090 FWUPD_ERROR,
2091 FWUPD_ERROR_INTERNAL,
2092 "invalid handle");
2093 fu_main_invocation_return_error (priv, invocation, error);
2094 return;
2095 }
2096 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
2097 if (fd_data < 0) {
2098 fu_main_invocation_return_error (priv, invocation, error);
2099 return;
2100 }
2101 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
2102 if (fd_sig < 0) {
2103 fu_main_invocation_return_error (priv, invocation, error);
2104 return;
2105 }
2106 if (!fu_main_daemon_update_metadata (priv, id, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01002107 g_prefix_error (&error, "failed to update metadata: ");
Richard Hughes060af612016-08-17 17:32:34 +01002108 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002109 return;
2110 }
Richard Hughes060af612016-08-17 17:32:34 +01002111 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesae0efdc2015-06-24 16:18:29 +01002112 return;
2113 }
2114
Richard Hughesa043c2e2015-06-29 08:43:18 +01002115 /* return 's' */
Richard Hughes9a410ce2016-02-28 15:58:54 +00002116 if (g_strcmp0 (method_name, "Unlock") == 0) {
2117 FuDeviceItem *item = NULL;
2118 FuMainAuthHelper *helper;
2119 const gchar *id = NULL;
2120 g_autoptr(PolkitSubject) subject = NULL;
2121
2122 /* check the id exists */
2123 g_variant_get (parameters, "(&s)", &id);
2124 g_debug ("Called %s(%s)", method_name, id);
2125 item = fu_main_get_item_by_id (priv, id);
2126 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002127 g_set_error (&error,
2128 FWUPD_ERROR,
2129 FWUPD_ERROR_NOT_FOUND,
2130 "No such device %s", id);
2131 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00002132 return;
2133 }
2134
2135 /* check the device is locked */
Richard Hughes644562e2016-08-22 10:30:24 +01002136 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes060af612016-08-17 17:32:34 +01002137 g_set_error (&error,
2138 FWUPD_ERROR,
2139 FWUPD_ERROR_NOT_FOUND,
2140 "Device %s is not locked", id);
2141 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00002142 return;
2143 }
2144
2145 /* process the firmware */
2146 helper = g_new0 (FuMainAuthHelper, 1);
2147 helper->auth_kind = FU_MAIN_AUTH_KIND_UNLOCK;
2148 helper->invocation = g_object_ref (invocation);
Richard Hughesfe5cc902016-06-29 10:00:00 +01002149 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes9a410ce2016-02-28 15:58:54 +00002150 helper->priv = priv;
2151
Richard Hughesfe5cc902016-06-29 10:00:00 +01002152 /* FIXME: do we want to support "*"? */
2153 g_ptr_array_add (helper->devices, g_object_ref (item->device));
2154
Richard Hughes9a410ce2016-02-28 15:58:54 +00002155 /* authenticate */
2156 subject = polkit_system_bus_name_new (sender);
2157 polkit_authority_check_authorization (helper->priv->authority, subject,
2158 "org.freedesktop.fwupd.device-unlock",
2159 NULL,
2160 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
2161 NULL,
2162 fu_main_check_authorization_cb,
2163 helper);
2164 return;
2165 }
2166
Richard Hughes29c220d2016-12-14 17:09:54 +00002167 /* return 'b' */
2168 if (g_strcmp0 (method_name, "VerifyUpdate") == 0) {
2169 FuDeviceItem *item = NULL;
2170 FuMainAuthHelper *helper;
2171 const gchar *id = NULL;
2172 g_autoptr(PolkitSubject) subject = NULL;
2173
2174 /* check the id exists */
2175 g_variant_get (parameters, "(&s)", &id);
2176 g_debug ("Called %s(%s)", method_name, id);
2177 item = fu_main_get_item_by_id (priv, id);
2178 if (item == NULL) {
2179 g_set_error (&error,
2180 FWUPD_ERROR,
2181 FWUPD_ERROR_NOT_FOUND,
2182 "No such device %s", id);
2183 fu_main_invocation_return_error (priv, invocation, error);
2184 return;
2185 }
2186
2187 /* process the firmware */
2188 helper = g_new0 (FuMainAuthHelper, 1);
2189 helper->auth_kind = FU_MAIN_AUTH_KIND_VERIFY_UPDATE;
2190 helper->invocation = g_object_ref (invocation);
2191 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2192 helper->priv = priv;
2193 g_ptr_array_add (helper->devices, g_object_ref (item->device));
2194
2195 /* authenticate */
2196 subject = polkit_system_bus_name_new (sender);
2197 polkit_authority_check_authorization (helper->priv->authority, subject,
2198 "org.freedesktop.fwupd.verify-update",
2199 NULL,
2200 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
2201 NULL,
2202 fu_main_check_authorization_cb,
2203 helper);
2204 return;
2205 }
2206
Richard Hughes9a410ce2016-02-28 15:58:54 +00002207 /* return 's' */
Richard Hughesa043c2e2015-06-29 08:43:18 +01002208 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002209 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002210 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002211 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01002212 FuDeviceItem *item = NULL;
Richard Hughes68cc00c2017-06-06 16:59:54 +01002213 GPtrArray *checksums;
Richard Hughesa043c2e2015-06-29 08:43:18 +01002214 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002215 const gchar *id = NULL;
2216 const gchar *version = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01002217
2218 /* check the id exists */
2219 g_variant_get (parameters, "(&s)", &id);
2220 g_debug ("Called %s(%s)", method_name, id);
2221 item = fu_main_get_item_by_id (priv, id);
2222 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002223 g_set_error (&error,
2224 FWUPD_ERROR,
2225 FWUPD_ERROR_NOT_FOUND,
2226 "No such device %s", id);
2227 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesa043c2e2015-06-29 08:43:18 +01002228 return;
2229 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002230
2231 /* set the device firmware hash */
Richard Hughescff38bc2016-12-12 12:03:37 +00002232 if (!fu_plugin_runner_verify (item->plugin, item->device,
2233 FU_PLUGIN_VERIFY_FLAG_NONE, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002234 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesa043c2e2015-06-29 08:43:18 +01002235 return;
2236 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002237
2238 /* find component in metadata */
Richard Hughes99147f12016-05-17 09:35:04 +01002239 app = fu_main_store_get_app_by_guids (priv->store, item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002240 if (app == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002241 g_set_error_literal (&error,
2242 FWUPD_ERROR,
2243 FWUPD_ERROR_NOT_FOUND,
2244 "No metadata");
2245 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002246 return;
2247 }
2248
2249 /* find version in metadata */
Richard Hughes8e9762d2016-03-17 10:14:15 +00002250 version = fu_device_get_version (item->device);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002251 release = as_app_get_release (app, version);
Richard Hughes74d95ab2016-12-14 17:08:14 +00002252 if (release == NULL)
2253 release = as_app_get_release_default (app);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002254 if (release == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002255 g_set_error (&error,
2256 FWUPD_ERROR,
2257 FWUPD_ERROR_NOT_FOUND,
2258 "No version %s", version);
2259 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002260 return;
2261 }
2262
2263 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002264 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
2265 if (csum == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002266 g_set_error (&error,
2267 FWUPD_ERROR,
2268 FWUPD_ERROR_NOT_FOUND,
2269 "No content checksum for %s", version);
2270 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002271 return;
2272 }
Richard Hughes68cc00c2017-06-06 16:59:54 +01002273
2274 /* get the matching checksum */
2275 checksums = fu_device_get_checksums (item->device);
2276 if (checksums->len == 0) {
2277 g_set_error (&error,
2278 FWUPD_ERROR,
2279 FWUPD_ERROR_NOT_FOUND,
2280 "No device checksums for %s", version);
2281 fu_main_invocation_return_error (priv, invocation, error);
2282 return;
2283 }
2284 for (guint j = 0; j < checksums->len; j++) {
2285 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
2286 GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp);
2287 if (as_checksum_get_kind (csum) == hash_kind) {
2288 hash = hash_tmp;
2289 break;
2290 }
2291 }
2292 if (hash == NULL) {
2293 g_set_error (&error,
2294 FWUPD_ERROR,
2295 FWUPD_ERROR_NOT_FOUND,
2296 "No matching hash kind for %s", version);
2297 fu_main_invocation_return_error (priv, invocation, error);
2298 return;
2299 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002300 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002301 g_set_error (&error,
2302 FWUPD_ERROR,
2303 FWUPD_ERROR_NOT_FOUND,
2304 "For v%s expected %s, got %s",
2305 version,
2306 as_checksum_get_value (csum),
2307 hash);
2308 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01002309 return;
2310 }
Richard Hughes060af612016-08-17 17:32:34 +01002311 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01002312 return;
2313 }
2314
Richard Hughesae0efdc2015-06-24 16:18:29 +01002315 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01002316 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00002317 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00002318 FuMainAuthHelper *helper;
Richard Hughes2d6e1862016-03-18 09:20:37 +00002319 FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE;
Richard Hughes74cc2172015-02-27 13:19:46 +00002320 GDBusMessage *message;
2321 GUnixFDList *fd_list;
2322 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00002323 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002324 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00002325 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002326 gint32 fd_handle = 0;
2327 gint fd;
Richard Hughes60f48c22015-10-08 20:25:51 +01002328 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01002329 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01002330 g_autoptr(GBytes) blob_cab = NULL;
2331 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002332
2333 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00002334 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00002335 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00002336 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
2337 item = fu_main_get_item_by_id (priv, id);
2338 if (item == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002339 g_set_error (&error,
2340 FWUPD_ERROR,
2341 FWUPD_ERROR_NOT_FOUND,
2342 "no such device %s", id);
2343 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughesd079b1a2015-03-06 10:09:55 +00002344 return;
2345 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002346 }
2347
Richard Hughes74cc2172015-02-27 13:19:46 +00002348 /* get options */
2349 while (g_variant_iter_next (iter, "{&sv}",
2350 &prop_key, &prop_value)) {
2351 g_debug ("got option %s", prop_key);
2352 if (g_strcmp0 (prop_key, "offline") == 0 &&
2353 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00002354 flags |= FWUPD_INSTALL_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00002355 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
2356 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00002357 flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughese7c12642015-03-04 20:28:59 +00002358 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
2359 g_variant_get_boolean (prop_value) == TRUE)
Richard Hughes2d6e1862016-03-18 09:20:37 +00002360 flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Mario Limonciello71a5b982016-05-10 15:38:53 -05002361 if (g_strcmp0 (prop_key, "force") == 0 &&
2362 g_variant_get_boolean (prop_value) == TRUE)
2363 flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00002364 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00002365 }
2366
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002367 /* get the fd */
2368 message = g_dbus_method_invocation_get_message (invocation);
2369 fd_list = g_dbus_message_get_unix_fd_list (message);
2370 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002371 g_set_error (&error,
2372 FWUPD_ERROR,
2373 FWUPD_ERROR_INTERNAL,
2374 "invalid handle");
2375 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002376 return;
2377 }
Richard Hughes7419e962016-11-22 19:48:06 +00002378 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002379 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002380 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002381 return;
2382 }
2383
Richard Hughes5d14def2015-10-07 17:43:10 +01002384 /* read the entire fd to a data blob */
2385 stream = g_unix_input_stream_new (fd, TRUE);
2386 blob_cab = g_input_stream_read_bytes (stream,
2387 FU_MAIN_FIRMWARE_SIZE_MAX,
2388 NULL, &error);
2389 if (blob_cab == NULL){
Richard Hughes060af612016-08-17 17:32:34 +01002390 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes5d14def2015-10-07 17:43:10 +01002391 return;
2392 }
2393
Richard Hughes67ec8982015-03-03 11:39:27 +00002394 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00002395 helper = g_new0 (FuMainAuthHelper, 1);
Richard Hughes9a410ce2016-02-28 15:58:54 +00002396 helper->auth_kind = FU_MAIN_AUTH_KIND_INSTALL;
Richard Hughesf508e762015-02-27 12:49:36 +00002397 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01002398 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
2399 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00002400 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00002401 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01002402 helper->store = as_store_new ();
Richard Hughesfe5cc902016-06-29 10:00:00 +01002403 helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughesa4a2c182016-06-29 10:37:05 +01002404 helper->blob_fws = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
Richard Hughesd079b1a2015-03-06 10:09:55 +00002405 if (item != NULL)
Richard Hughesfe5cc902016-06-29 10:00:00 +01002406 g_ptr_array_add (helper->devices, g_object_ref (item->device));
Richard Hughes67ec8982015-03-03 11:39:27 +00002407 if (!fu_main_update_helper (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002408 fu_main_invocation_return_error (helper->priv, helper->invocation, error);
Richard Hughes67ec8982015-03-03 11:39:27 +00002409 fu_main_helper_free (helper);
2410 return;
2411 }
2412
Richard Hughes18423292015-03-09 17:10:50 +00002413 /* is root */
2414 if (fu_main_dbus_get_uid (priv, sender) == 0) {
Richard Hughescff38bc2016-12-12 12:03:37 +00002415 if (!fu_main_plugin_update_authenticated (helper, &error)) {
Richard Hughes060af612016-08-17 17:32:34 +01002416 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes18423292015-03-09 17:10:50 +00002417 } else {
Richard Hughes060af612016-08-17 17:32:34 +01002418 fu_main_invocation_return_value (priv, invocation, NULL);
Richard Hughes18423292015-03-09 17:10:50 +00002419 }
2420 fu_main_helper_free (helper);
2421 return;
2422 }
2423
Richard Hughes67ec8982015-03-03 11:39:27 +00002424 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01002425 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00002426 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00002427 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00002428 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00002429 NULL,
2430 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
2431 NULL,
2432 fu_main_check_authorization_cb,
2433 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002434 return;
2435 }
2436
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002437 /* get a single result object from a local file */
Richard Hughescccc7752015-03-06 11:13:19 +00002438 if (g_strcmp0 (method_name, "GetDetails") == 0) {
2439 GDBusMessage *message;
2440 GUnixFDList *fd_list;
Richard Hughescccc7752015-03-06 11:13:19 +00002441 gint32 fd_handle = 0;
2442 gint fd;
Richard Hughescccc7752015-03-06 11:13:19 +00002443
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002444 /* get parameters */
Richard Hughescccc7752015-03-06 11:13:19 +00002445 g_variant_get (parameters, "(h)", &fd_handle);
2446 g_debug ("Called %s(%i)", method_name, fd_handle);
2447
2448 /* get the fd */
2449 message = g_dbus_method_invocation_get_message (invocation);
2450 fd_list = g_dbus_message_get_unix_fd_list (message);
2451 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002452 g_set_error (&error,
2453 FWUPD_ERROR,
2454 FWUPD_ERROR_INTERNAL,
2455 "invalid handle");
2456 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002457 return;
2458 }
Richard Hughes7419e962016-11-22 19:48:06 +00002459 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughescccc7752015-03-06 11:13:19 +00002460 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002461 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002462 return;
2463 }
2464
Richard Hughes8ac07dc2016-05-29 08:32:55 +01002465 /* get details about the file */
2466 val = fu_main_get_details_from_fd (priv, fd, &error);
2467 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002468 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughescccc7752015-03-06 11:13:19 +00002469 return;
2470 }
Richard Hughes060af612016-08-17 17:32:34 +01002471 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughescccc7752015-03-06 11:13:19 +00002472 return;
2473 }
2474
Richard Hughes7289a6b2016-05-29 09:27:47 +01002475 /* get multiple result objects from a local file */
2476 if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) {
2477 GDBusMessage *message;
2478 GUnixFDList *fd_list;
2479 gint32 fd_handle = 0;
2480 gint fd;
Richard Hughes7289a6b2016-05-29 09:27:47 +01002481
2482 /* get parameters */
2483 g_variant_get (parameters, "(h)", &fd_handle);
2484 g_debug ("Called %s(%i)", method_name, fd_handle);
2485
2486 /* get the fd */
2487 message = g_dbus_method_invocation_get_message (invocation);
2488 fd_list = g_dbus_message_get_unix_fd_list (message);
2489 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
Richard Hughes060af612016-08-17 17:32:34 +01002490 g_set_error (&error,
2491 FWUPD_ERROR,
2492 FWUPD_ERROR_INTERNAL,
2493 "invalid handle");
2494 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002495 return;
2496 }
Richard Hughes7419e962016-11-22 19:48:06 +00002497 fd = g_unix_fd_list_get (fd_list, 0, &error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002498 if (fd < 0) {
Richard Hughes060af612016-08-17 17:32:34 +01002499 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002500 return;
2501 }
2502
2503 /* get details about the file */
2504 val = fu_main_get_details_local_from_fd (priv, fd, &error);
2505 if (val == NULL) {
Richard Hughes060af612016-08-17 17:32:34 +01002506 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002507 return;
2508 }
Richard Hughes060af612016-08-17 17:32:34 +01002509 fu_main_invocation_return_value (priv, invocation, val);
Richard Hughes7289a6b2016-05-29 09:27:47 +01002510 return;
2511 }
2512
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002513 /* we suck */
Richard Hughes060af612016-08-17 17:32:34 +01002514 g_set_error (&error,
2515 G_DBUS_ERROR,
2516 G_DBUS_ERROR_UNKNOWN_METHOD,
2517 "no such method %s", method_name);
2518 fu_main_invocation_return_error (priv, invocation, error);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002519}
2520
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002521static GVariant *
2522fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
2523 const gchar *object_path, const gchar *interface_name,
2524 const gchar *property_name, GError **error,
2525 gpointer user_data)
2526{
Richard Hughes773ce982015-03-09 22:40:57 +00002527 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2528
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002529 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
2530 return g_variant_new_string (VERSION);
2531
Richard Hughes773ce982015-03-09 22:40:57 +00002532 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughes37e9d772016-04-28 14:23:24 +01002533 return g_variant_new_uint32 (priv->status);
Richard Hughes773ce982015-03-09 22:40:57 +00002534
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002535 /* return an error */
2536 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00002537 G_DBUS_ERROR,
2538 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002539 "failed to get daemon property %s",
2540 property_name);
2541 return NULL;
2542}
2543
Richard Hughesfd468842015-04-22 16:44:08 +01002544static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002545fu_main_plugins_setup (FuMainPrivate *priv)
Richard Hughesbc93e4a2016-12-08 17:29:51 +00002546{
2547 g_autoptr(AsProfileTask) ptask = NULL;
2548
2549 ptask = as_profile_start_literal (priv->profile, "FuMain:setup");
Richard Hughes42321852016-12-13 16:01:52 +00002550 g_assert (ptask != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002551 for (guint i = 0; i < priv->plugins->len; i++) {
Richard Hughesbc93e4a2016-12-08 17:29:51 +00002552 g_autoptr(GError) error = NULL;
2553 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00002554 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
Richard Hughesbc93e4a2016-12-08 17:29:51 +00002555 ptask2 = as_profile_start (priv->profile,
2556 "FuMain:setup{%s}",
Richard Hughescff38bc2016-12-12 12:03:37 +00002557 fu_plugin_get_name (plugin));
Richard Hughes42321852016-12-13 16:01:52 +00002558 g_assert (ptask2 != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002559 if (!fu_plugin_runner_startup (plugin, &error)) {
2560 fu_plugin_set_enabled (plugin, FALSE);
2561 g_warning ("disabling plugin because: %s", error->message);
2562 }
Richard Hughesbc93e4a2016-12-08 17:29:51 +00002563 }
2564}
2565
2566static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002567fu_main_plugins_coldplug (FuMainPrivate *priv)
Richard Hughesfd468842015-04-22 16:44:08 +01002568{
Richard Hughes3f236502015-09-24 15:43:02 +01002569 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01002570
Richard Hughesf028ba22017-01-08 10:45:12 +00002571 /* don't allow coldplug to be scheduled when in coldplug */
2572 priv->coldplug_running = TRUE;
2573
Richard Hughes46487c92017-01-07 21:26:34 +00002574 /* prepare */
2575 for (guint i = 0; i < priv->plugins->len; i++) {
2576 g_autoptr(GError) error = NULL;
2577 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
2578 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
2579 g_warning ("failed to prepare coldplug: %s", error->message);
2580 }
2581
Richard Hughesb0829032017-01-10 09:27:08 +00002582 /* do this in one place */
2583 if (priv->coldplug_delay > 0) {
2584 g_debug ("sleeping for %ums", priv->coldplug_delay);
2585 g_usleep (priv->coldplug_delay * 1000);
2586 }
2587
Richard Hughes46487c92017-01-07 21:26:34 +00002588 /* exec */
Richard Hughes3f236502015-09-24 15:43:02 +01002589 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughes42321852016-12-13 16:01:52 +00002590 g_assert (ptask != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002591 for (guint i = 0; i < priv->plugins->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01002592 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01002593 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00002594 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
Richard Hughes3f236502015-09-24 15:43:02 +01002595 ptask2 = as_profile_start (priv->profile,
2596 "FuMain:coldplug{%s}",
Richard Hughescff38bc2016-12-12 12:03:37 +00002597 fu_plugin_get_name (plugin));
Richard Hughes42321852016-12-13 16:01:52 +00002598 g_assert (ptask2 != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002599 if (!fu_plugin_runner_coldplug (plugin, &error)) {
2600 fu_plugin_set_enabled (plugin, FALSE);
2601 g_warning ("disabling plugin because: %s", error->message);
2602 }
Richard Hughesfd468842015-04-22 16:44:08 +01002603 }
Richard Hughes46487c92017-01-07 21:26:34 +00002604
2605 /* cleanup */
2606 for (guint i = 0; i < priv->plugins->len; i++) {
2607 g_autoptr(GError) error = NULL;
2608 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
2609 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
2610 g_warning ("failed to cleanup coldplug: %s", error->message);
2611 }
Richard Hughesf028ba22017-01-08 10:45:12 +00002612
2613 /* we can recoldplug from this point on */
2614 priv->coldplug_running = FALSE;
Richard Hughesfd468842015-04-22 16:44:08 +01002615}
2616
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002617static void
2618fu_main_on_bus_acquired_cb (GDBusConnection *connection,
2619 const gchar *name,
2620 gpointer user_data)
2621{
2622 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2623 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01002624 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002625 static const GDBusInterfaceVTable interface_vtable = {
2626 fu_main_daemon_method_call,
2627 fu_main_daemon_get_property,
2628 NULL
2629 };
2630
2631 priv->connection = g_object_ref (connection);
2632 registration_id = g_dbus_connection_register_object (connection,
2633 FWUPD_DBUS_PATH,
2634 priv->introspection_daemon->interfaces[0],
2635 &interface_vtable,
2636 priv, /* user_data */
2637 NULL, /* user_data_free_func */
2638 NULL); /* GError** */
2639 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00002640
2641 /* connect to D-Bus directly */
2642 priv->proxy_uid =
2643 g_dbus_proxy_new_sync (priv->connection,
2644 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
2645 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
2646 NULL,
2647 "org.freedesktop.DBus",
2648 "/org/freedesktop/DBus",
2649 "org.freedesktop.DBus",
2650 NULL,
2651 &error);
2652 if (priv->proxy_uid == NULL) {
2653 g_warning ("cannot connect to DBus: %s", error->message);
2654 return;
2655 }
Richard Hughes3f236502015-09-24 15:43:02 +01002656
2657 /* dump startup profile data */
Richard Hughes2a1e75d2015-12-18 17:42:53 +00002658 if (fu_debug_is_verbose ())
2659 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002660}
2661
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002662static void
2663fu_main_on_name_acquired_cb (GDBusConnection *connection,
2664 const gchar *name,
2665 gpointer user_data)
2666{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002667 g_debug ("FuMain: acquired name: %s", name);
2668}
2669
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002670static void
2671fu_main_on_name_lost_cb (GDBusConnection *connection,
2672 const gchar *name,
2673 gpointer user_data)
2674{
2675 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2676 g_debug ("FuMain: lost name: %s", name);
2677 g_main_loop_quit (priv->loop);
2678}
2679
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002680static gboolean
2681fu_main_timed_exit_cb (gpointer user_data)
2682{
2683 GMainLoop *loop = (GMainLoop *) user_data;
2684 g_main_loop_quit (loop);
2685 return G_SOURCE_REMOVE;
2686}
2687
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002688static GDBusNodeInfo *
2689fu_main_load_introspection (const gchar *filename, GError **error)
2690{
Richard Hughes46832432015-09-11 13:43:15 +01002691 g_autoptr(GBytes) data = NULL;
2692 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002693
2694 /* lookup data */
2695 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
2696 data = g_resource_lookup_data (fu_get_resource (),
2697 path,
2698 G_RESOURCE_LOOKUP_FLAGS_NONE,
2699 error);
2700 if (data == NULL)
2701 return NULL;
2702
2703 /* build introspection from XML */
2704 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
2705}
2706
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002707static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002708fu_main_plugin_device_added_cb (FuPlugin *plugin,
2709 FuDevice *device,
2710 gpointer user_data)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002711{
2712 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002713 FuDeviceItem *item;
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002714 g_auto(GStrv) guids = NULL;
Richard Hughesdad1e192016-03-13 09:56:54 +00002715 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00002716
Richard Hughesfdf46162016-08-25 13:05:18 +01002717 /* device has no GUIDs set! */
2718 if (fu_device_get_guid_default (device) == NULL) {
2719 g_warning ("no GUIDs for device %s [%s]",
2720 fu_device_get_id (device),
2721 fu_device_get_name (device));
2722 return;
2723 }
2724
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002725 /* is this GUID blacklisted */
2726 guids = g_key_file_get_string_list (priv->config,
2727 "fwupd",
2728 "BlacklistDevices",
2729 NULL, /* length */
2730 NULL);
2731 if (guids != NULL &&
2732 g_strv_contains ((const gchar * const *) guids,
2733 fu_device_get_guid_default (device))) {
2734 g_debug ("%s is blacklisted [%s], ignoring from %s",
2735 fu_device_get_id (device),
2736 fu_device_get_guid_default (device),
Richard Hughescff38bc2016-12-12 12:03:37 +00002737 fu_plugin_get_name (plugin));
Richard Hughes9a52c5e2016-07-18 09:17:02 +01002738 return;
2739 }
2740
Richard Hughes0e883ee2015-03-18 17:22:33 +00002741 /* remove any fake device */
2742 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
Richard Hughes5d057a82015-11-24 18:09:57 +00002743 if (item != NULL) {
2744 g_debug ("already added %s by %s, ignoring same device from %s",
2745 fu_device_get_id (item->device),
Richard Hughescff38bc2016-12-12 12:03:37 +00002746 fu_device_get_plugin (item->device),
2747 fu_plugin_get_name (plugin));
Richard Hughes5d057a82015-11-24 18:09:57 +00002748 return;
2749 }
Richard Hughes0e883ee2015-03-18 17:22:33 +00002750
2751 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00002752 item = g_new0 (FuDeviceItem, 1);
2753 item->device = g_object_ref (device);
Richard Hughescff38bc2016-12-12 12:03:37 +00002754 item->plugin = g_object_ref (plugin);
Richard Hughesf508e762015-02-27 12:49:36 +00002755 g_ptr_array_add (priv->devices, item);
Richard Hughesdad1e192016-03-13 09:56:54 +00002756
Richard Hughes422e8662016-04-28 15:05:33 +01002757 /* match the metadata at this point so clients can tell if the
2758 * device is worthy */
2759 fu_main_get_updates_item_update (priv, item);
2760
Richard Hughes8ca33782016-04-28 15:04:31 +01002761 /* notify clients */
2762 fu_main_emit_device_added (priv, item->device);
Richard Hughesd7022b52015-03-11 19:47:06 +00002763 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002764}
2765
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002766static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002767fu_main_plugin_device_removed_cb (FuPlugin *plugin,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002768 FuDevice *device,
2769 gpointer user_data)
2770{
2771 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00002772 FuDeviceItem *item;
2773
2774 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
2775 if (item == NULL) {
Richard Hughes5d057a82015-11-24 18:09:57 +00002776 g_debug ("no device to remove %s", fu_device_get_id (device));
Richard Hughesf508e762015-02-27 12:49:36 +00002777 return;
2778 }
Richard Hughes5d057a82015-11-24 18:09:57 +00002779
Richard Hughescff38bc2016-12-12 12:03:37 +00002780 /* check this came from the same plugin */
2781 if (g_strcmp0 (fu_plugin_get_name (plugin),
2782 fu_plugin_get_name (item->plugin)) != 0) {
Richard Hughes5d057a82015-11-24 18:09:57 +00002783 g_debug ("ignoring duplicate removal from %s",
Richard Hughescff38bc2016-12-12 12:03:37 +00002784 fu_plugin_get_name (plugin));
Richard Hughes5d057a82015-11-24 18:09:57 +00002785 return;
2786 }
2787
Richard Hughes8ca33782016-04-28 15:04:31 +01002788 /* make the UI update */
2789 fu_main_emit_device_removed (priv, device);
Richard Hughesf508e762015-02-27 12:49:36 +00002790 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00002791 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002792}
2793
Richard Hughes773ce982015-03-09 22:40:57 +00002794static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002795fu_main_plugin_status_changed_cb (FuPlugin *plugin,
Richard Hughesf910ac92015-03-19 10:43:42 +00002796 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00002797 gpointer user_data)
2798{
2799 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2800 fu_main_set_status (priv, status);
2801}
2802
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002803static void
Richard Hughescff38bc2016-12-12 12:03:37 +00002804fu_main_plugin_percentage_changed_cb (FuPlugin *plugin,
Richard Hughes876c0072016-08-17 14:51:03 +01002805 guint percentage,
2806 gpointer user_data)
2807{
2808 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2809 fu_main_set_percentage (priv, percentage);
2810}
2811
Richard Hughesf3605832017-01-08 10:50:46 +00002812static gboolean
2813fu_main_recoldplug_delay_cb (gpointer user_data)
2814{
2815 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2816 g_debug ("performing a recoldplug");
2817 fu_main_plugins_coldplug (priv);
2818 priv->coldplug_id = 0;
2819 return FALSE;
2820}
2821
Richard Hughes362d6d72017-01-07 21:42:14 +00002822static void
2823fu_main_plugin_recoldplug_cb (FuPlugin *plugin, FuMainPrivate *priv)
2824{
Richard Hughesf028ba22017-01-08 10:45:12 +00002825 if (priv->coldplug_running) {
2826 g_warning ("coldplug already running, cannot recoldplug");
2827 return;
2828 }
Richard Hughesf3605832017-01-08 10:50:46 +00002829 g_debug ("scheduling a recoldplug");
2830 if (priv->coldplug_id != 0)
2831 g_source_remove (priv->coldplug_id);
2832 priv->coldplug_id = g_timeout_add (1500, fu_main_recoldplug_delay_cb, priv);
Richard Hughes362d6d72017-01-07 21:42:14 +00002833}
2834
Richard Hughesb0829032017-01-10 09:27:08 +00002835static void
2836fu_main_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuMainPrivate *priv)
2837{
2838 priv->coldplug_delay = MAX (priv->coldplug_delay, duration);
2839 g_debug ("got coldplug delay of %ums, global maximum is now %ums",
2840 duration, priv->coldplug_delay);
2841}
2842
Richard Hughescff38bc2016-12-12 12:03:37 +00002843static gboolean
Richard Hughesb8f8db22017-04-25 15:56:00 +01002844fu_main_load_hwids (FuMainPrivate *priv, GError **error)
2845{
2846 g_autoptr(FuHwids) hwids = fu_hwids_new ();
2847
2848 /* read files in /sys */
2849 if (!fu_hwids_setup (hwids, NULL, error))
2850 return FALSE;
2851
2852 /* add GUIDs */
2853 for (guint i = 0; i < 15; i++) {
2854 g_autofree gchar *guid = NULL;
2855 g_autofree gchar *key = NULL;
2856 g_autofree gchar *values = NULL;
2857 g_autoptr(GError) error_local = NULL;
2858
2859 /* get the GUID and add to hash */
2860 key = g_strdup_printf ("HardwareID-%u", i);
2861 guid = fu_hwids_get_guid (hwids, key, &error_local);
2862 if (guid == NULL) {
2863 g_debug ("%s is not available, %s", key, error_local->message);
2864 continue;
2865 }
2866 g_hash_table_insert (priv->hwids,
2867 g_strdup (guid),
2868 GUINT_TO_POINTER (1));
2869
2870 /* show what makes up the GUID */
2871 values = fu_hwids_get_replace_values (hwids, key, NULL);
2872 g_debug ("{%s} <- %s", guid, values);
2873 }
2874
2875 return TRUE;
2876}
2877
2878static gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +00002879fu_main_load_plugins (FuMainPrivate *priv, GError **error)
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002880{
Richard Hughescff38bc2016-12-12 12:03:37 +00002881 const gchar *fn;
Richard Hughescff38bc2016-12-12 12:03:37 +00002882 g_autoptr(GDir) dir = NULL;
Richard Hughes05ede652017-04-16 09:11:39 +01002883 g_auto(GStrv) blacklist = NULL;
2884
2885 /* get plugin blacklist */
2886 blacklist = g_key_file_get_string_list (priv->config,
2887 "fwupd",
2888 "BlacklistPlugins",
2889 NULL, /* length */
2890 NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002891
2892 /* search */
Richard Hughes4af05392017-04-12 14:18:15 +01002893 dir = g_dir_open (PLUGINDIR, 0, error);
Richard Hughescff38bc2016-12-12 12:03:37 +00002894 if (dir == NULL)
2895 return FALSE;
2896 while ((fn = g_dir_read_name (dir)) != NULL) {
2897 g_autofree gchar *filename = NULL;
2898 g_autoptr(FuPlugin) plugin = NULL;
2899 g_autoptr(GError) error_local = NULL;
2900
2901 /* ignore non-plugins */
2902 if (!g_str_has_suffix (fn, ".so"))
2903 continue;
2904
2905 /* open module */
Richard Hughes4af05392017-04-12 14:18:15 +01002906 filename = g_build_filename (PLUGINDIR, fn, NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +00002907 plugin = fu_plugin_new ();
2908 fu_plugin_set_usb_context (plugin, priv->usb_ctx);
Richard Hughesb8f8db22017-04-25 15:56:00 +01002909 fu_plugin_set_hwids (plugin, priv->hwids);
Richard Hughescff38bc2016-12-12 12:03:37 +00002910 g_debug ("adding plugin %s", filename);
2911 if (!fu_plugin_open (plugin, filename, &error_local)) {
2912 g_warning ("failed to open plugin %s: %s",
2913 filename, error_local->message);
2914 continue;
2915 }
Richard Hughes05ede652017-04-16 09:11:39 +01002916
2917 /* is blacklisted */
2918 if (blacklist != NULL &&
2919 g_strv_contains ((const gchar * const *) blacklist,
2920 fu_plugin_get_name (plugin))) {
2921 fu_plugin_set_enabled (plugin, FALSE);
2922 g_debug ("%s blacklisted by config",
2923 fu_plugin_get_name (plugin));
2924 continue;
2925 }
2926
2927 /* watch for changes */
Richard Hughescff38bc2016-12-12 12:03:37 +00002928 g_signal_connect (plugin, "device-added",
2929 G_CALLBACK (fu_main_plugin_device_added_cb),
2930 priv);
2931 g_signal_connect (plugin, "device-removed",
2932 G_CALLBACK (fu_main_plugin_device_removed_cb),
2933 priv);
2934 g_signal_connect (plugin, "status-changed",
2935 G_CALLBACK (fu_main_plugin_status_changed_cb),
2936 priv);
2937 g_signal_connect (plugin, "percentage-changed",
2938 G_CALLBACK (fu_main_plugin_percentage_changed_cb),
2939 priv);
Richard Hughes362d6d72017-01-07 21:42:14 +00002940 g_signal_connect (plugin, "recoldplug",
2941 G_CALLBACK (fu_main_plugin_recoldplug_cb),
2942 priv);
Richard Hughesb0829032017-01-10 09:27:08 +00002943 g_signal_connect (plugin, "set-coldplug-delay",
2944 G_CALLBACK (fu_main_plugin_set_coldplug_delay_cb),
2945 priv);
Richard Hughescff38bc2016-12-12 12:03:37 +00002946
2947 /* add */
2948 g_ptr_array_add (priv->plugins, g_object_ref (plugin));
2949 g_hash_table_insert (priv->plugins_hash,
2950 g_strdup (fu_plugin_get_name (plugin)),
2951 g_object_ref (plugin));
2952 }
2953
2954 return TRUE;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00002955}
2956
Richard Hughesf0a799e2017-01-17 20:13:30 +00002957/* returns FALSE if any plugins have pending devices to be added */
2958static gboolean
2959fu_main_check_plugins_pending (FuMainPrivate *priv, GError **error)
2960{
2961 for (guint i = 0; i < priv->plugins->len; i++) {
2962 FuPlugin *plugin = g_ptr_array_index (priv->plugins, i);
2963 if (fu_plugin_has_device_delay (plugin)) {
2964 g_set_error (error,
2965 FWUPD_ERROR,
2966 FWUPD_ERROR_INTERNAL,
2967 "%s pending",
2968 fu_plugin_get_name (plugin));
2969 return FALSE;
2970 }
2971 }
2972 return TRUE;
2973}
2974
2975static gboolean
2976fu_main_perhaps_own_name (gpointer user_data)
2977{
2978 FuMainPrivate *priv = (FuMainPrivate *) user_data;
2979 g_autoptr(GError) error = NULL;
2980
2981 /* are any plugins pending */
2982 if (!fu_main_check_plugins_pending (priv, &error)) {
2983 g_debug ("trying again: %s", error->message);
2984 return G_SOURCE_CONTINUE;
2985 }
2986
2987 /* own the object */
2988 g_debug ("registering D-Bus service");
2989 priv->owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
2990 FWUPD_DBUS_SERVICE,
2991 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
2992 G_BUS_NAME_OWNER_FLAGS_REPLACE,
2993 fu_main_on_bus_acquired_cb,
2994 fu_main_on_name_acquired_cb,
2995 fu_main_on_name_lost_cb,
2996 priv, NULL);
2997 return G_SOURCE_REMOVE;
2998}
2999
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003000static void
3001fu_main_private_free (FuMainPrivate *priv)
3002{
3003 if (priv->loop != NULL)
3004 g_main_loop_unref (priv->loop);
3005 if (priv->owner_id > 0)
3006 g_bus_unown_name (priv->owner_id);
3007 if (priv->proxy_uid != NULL)
3008 g_object_unref (priv->proxy_uid);
3009 if (priv->usb_ctx != NULL)
3010 g_object_unref (priv->usb_ctx);
3011 if (priv->config != NULL)
3012 g_key_file_unref (priv->config);
3013 if (priv->connection != NULL)
3014 g_object_unref (priv->connection);
3015 if (priv->authority != NULL)
3016 g_object_unref (priv->authority);
3017 if (priv->profile != NULL)
3018 g_object_unref (priv->profile);
3019 if (priv->store != NULL)
3020 g_object_unref (priv->store);
3021 if (priv->introspection_daemon != NULL)
3022 g_dbus_node_info_unref (priv->introspection_daemon);
3023 if (priv->store_changed_id != 0)
3024 g_source_remove (priv->store_changed_id);
3025 if (priv->pending != NULL)
3026 g_object_unref (priv->pending);
3027 if (priv->coldplug_id != 0)
3028 g_source_remove (priv->coldplug_id);
3029 if (priv->plugins != NULL)
3030 g_ptr_array_unref (priv->plugins);
Richard Hughesb8f8db22017-04-25 15:56:00 +01003031 if (priv->hwids != NULL)
3032 g_hash_table_unref (priv->hwids);
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003033 if (priv->plugins_hash != NULL)
3034 g_hash_table_unref (priv->plugins_hash);
3035 g_ptr_array_unref (priv->devices);
3036 g_free (priv);
3037}
3038
3039G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainPrivate, fu_main_private_free)
3040
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003041int
3042main (int argc, char *argv[])
3043{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003044 gboolean immediate_exit = FALSE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003045 gboolean timed_exit = FALSE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003046 const GOptionEntry options[] = {
3047 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
3048 /* TRANSLATORS: exit after we've started up, used for user profiling */
3049 _("Exit after a small delay"), NULL },
3050 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
3051 /* TRANSLATORS: exit straight away, used for automatic profiling */
3052 _("Exit after the engine has loaded"), NULL },
3053 { NULL}
3054 };
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003055 g_autoptr(FuMainPrivate) priv = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01003056 g_autoptr(GError) error = NULL;
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003057 g_autoptr(GOptionContext) context = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01003058 g_autofree gchar *config_file = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003059
3060 setlocale (LC_ALL, "");
3061
3062 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3063 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3064 textdomain (GETTEXT_PACKAGE);
3065
3066 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01003067 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003068 context = g_option_context_new (NULL);
3069 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00003070 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00003071 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003072 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003073 if (!g_option_context_parse (context, &argc, &argv, &error)) {
3074 g_printerr ("Failed to parse command line: %s\n", error->message);
3075 return EXIT_FAILURE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003076 }
3077
3078 /* create new objects */
3079 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00003080 priv->status = FWUPD_STATUS_IDLE;
Richard Hughes876c0072016-08-17 14:51:03 +01003081 priv->percentage = 0;
Richard Hughesf508e762015-02-27 12:49:36 +00003082 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003083 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00003084 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01003085 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01003086 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01003087 g_signal_connect (priv->store, "changed",
3088 G_CALLBACK (fu_main_store_changed_cb), priv);
3089 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
3090 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01003091
3092 /* load AppStream */
Richard Hughes65dfbfe2016-03-02 13:42:53 +00003093 as_store_add_filter (priv->store, AS_APP_KIND_FIRMWARE);
Richard Hughes7708a0f2015-07-21 08:41:22 +01003094 if (!as_store_load (priv->store,
Richard Hughesf5f400f2017-05-15 10:52:49 +01003095 AS_STORE_LOAD_FLAG_IGNORE_INVALID |
Richard Hughes7708a0f2015-07-21 08:41:22 +01003096 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
3097 NULL, &error)){
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003098 g_printerr ("Failed to load AppStream data: %s\n", error->message);
3099 return EXIT_FAILURE;
Richard Hughes7708a0f2015-07-21 08:41:22 +01003100 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00003101
Richard Hughes804c0752015-08-04 14:53:52 +01003102 /* read config file */
Richard Hughes01c13d12017-06-05 09:10:17 +01003103 config_file = g_build_filename (fu_main_get_sysconfig_dir (), "fwupd.conf", NULL);
Richard Hughesc7f61b62017-04-26 10:18:13 +01003104 g_debug ("loading config values from %s", config_file);
Richard Hughes9a52c5e2016-07-18 09:17:02 +01003105 priv->config = g_key_file_new ();
3106 if (!g_key_file_load_from_file (priv->config, config_file,
Richard Hughes804c0752015-08-04 14:53:52 +01003107 G_KEY_FILE_NONE, &error)) {
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003108 g_printerr ("Failed to load config file %s: %s\n",
3109 config_file, error->message);
3110 return EXIT_FAILURE;
Richard Hughes804c0752015-08-04 14:53:52 +01003111 }
3112
Richard Hughesbc93e4a2016-12-08 17:29:51 +00003113 /* set shared USB context */
3114 priv->usb_ctx = g_usb_context_new (&error);
3115 if (priv->usb_ctx == NULL) {
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003116 g_printerr ("Failed to get USB context: %s\n", error->message);
3117 return EXIT_FAILURE;
Richard Hughesbc93e4a2016-12-08 17:29:51 +00003118 }
Richard Hughescff38bc2016-12-12 12:03:37 +00003119
Richard Hughesb8f8db22017-04-25 15:56:00 +01003120 /* load the hwids */
3121 priv->hwids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3122 if (!fu_main_load_hwids (priv, &error)) {
3123 g_printerr ("Failed to load hwids: %s\n", error->message);
3124 return EXIT_FAILURE;
3125 }
3126
Richard Hughescff38bc2016-12-12 12:03:37 +00003127 /* load plugin */
3128 priv->plugins = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
3129 priv->plugins_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
3130 g_free, (GDestroyNotify) g_object_unref);
3131 if (!fu_main_load_plugins (priv, &error)) {
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003132 g_printerr ("Failed to load plugins: %s\n", error->message);
3133 return EXIT_FAILURE;
Richard Hughescff38bc2016-12-12 12:03:37 +00003134 }
3135
3136 /* disable udev? */
3137 if (!g_key_file_get_boolean (priv->config, "fwupd", "EnableOptionROM", NULL)) {
3138 FuPlugin *plugin = g_hash_table_lookup (priv->plugins_hash, "udev");
3139 if (plugin != NULL)
3140 fu_plugin_set_enabled (plugin, FALSE);
Richard Hughesbc93e4a2016-12-08 17:29:51 +00003141 }
3142
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003143 /* load introspection from file */
3144 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
3145 &error);
3146 if (priv->introspection_daemon == NULL) {
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003147 g_printerr ("Failed to load introspection: %s\n", error->message);
3148 return EXIT_FAILURE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003149 }
3150
Richard Hughesf508e762015-02-27 12:49:36 +00003151 /* get authority */
3152 priv->authority = polkit_authority_get_sync (NULL, &error);
3153 if (priv->authority == NULL) {
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003154 g_printerr ("Failed to load authority: %s\n", error->message);
3155 return EXIT_FAILURE;
Richard Hughesf508e762015-02-27 12:49:36 +00003156 }
3157
Richard Hughesf0a799e2017-01-17 20:13:30 +00003158 /* add devices */
3159 fu_main_plugins_setup (priv);
3160 g_usb_context_enumerate (priv->usb_ctx);
3161 fu_main_plugins_coldplug (priv);
3162
3163 /* keep polling until all the plugins are ready */
3164 g_timeout_add (200, fu_main_perhaps_own_name, priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003165
3166 /* Only timeout and close the mainloop if we have specified it
3167 * on the command line */
3168 if (immediate_exit)
3169 g_idle_add (fu_main_timed_exit_cb, priv->loop);
3170 else if (timed_exit)
3171 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
3172
3173 /* wait */
3174 g_info ("Daemon ready for requests");
3175 g_main_loop_run (priv->loop);
3176
3177 /* success */
Richard Hughesf29a6ee2017-06-02 19:50:37 +01003178 return EXIT_SUCCESS;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003179}
3180