blob: 53f8c3c5013a8d19627e9f1ff7fbd5e431c7453e [file] [log] [blame]
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
Richard Hughes8645ec92015-03-19 10:14:32 +000024#include <fwupd.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000025#include <appstream-glib.h>
26#include <glib/gstdio.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000027#include <gio/gio.h>
28#include <gio/gunixfdlist.h>
Richard Hughesae0efdc2015-06-24 16:18:29 +010029#include <gio/gunixinputstream.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000030#include <glib/gi18n.h>
31#include <locale.h>
Richard Hughesf508e762015-02-27 12:49:36 +000032#include <polkit/polkit.h>
Richard Hughes67ec8982015-03-03 11:39:27 +000033#include <stdlib.h>
34#include <fcntl.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000035
Richard Hughes8bbfdf42015-02-26 22:28:09 +000036#include "fu-debug.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000037#include "fu-device.h"
Richard Hughesae0efdc2015-06-24 16:18:29 +010038#include "fu-keyring.h"
Richard Hughes0e883ee2015-03-18 17:22:33 +000039#include "fu-pending.h"
Richard Hughes3c99ba42015-03-05 12:17:48 +000040#include "fu-provider.h"
Richard Hughes25cf6ab2015-08-04 21:34:12 +010041#include "fu-provider-rpi.h"
Richard Hughesebb58a32015-05-29 15:35:37 +010042#include "fu-provider-udev.h"
Richard Hughesc89c1b02015-05-05 15:21:18 +010043#include "fu-provider-usb.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000044#include "fu-resources.h"
45
Richard Hughes3c99ba42015-03-05 12:17:48 +000046#ifdef HAVE_COLORHUG
47 #include "fu-provider-chug.h"
48#endif
49#ifdef HAVE_UEFI
50 #include "fu-provider-uefi.h"
51#endif
52
Richard Hughes60f48c22015-10-08 20:25:51 +010053#ifndef PolkitAuthorizationResult_autoptr
54G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
55G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
56#endif
57
Richard Hughes5d14def2015-10-07 17:43:10 +010058#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
59
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000060typedef struct {
61 GDBusConnection *connection;
62 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000063 GDBusProxy *proxy_uid;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000064 GMainLoop *loop;
Richard Hughes5d14def2015-10-07 17:43:10 +010065 GPtrArray *devices; /* of FuDeviceItem */
Richard Hughes8bbfdf42015-02-26 22:28:09 +000066 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000067 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000068 FwupdStatus status;
Richard Hughes0e883ee2015-03-18 17:22:33 +000069 FuPending *pending;
Richard Hughes3f236502015-09-24 15:43:02 +010070 AsProfile *profile;
Richard Hughes7708a0f2015-07-21 08:41:22 +010071 AsStore *store;
Richard Hughes033ccba2015-09-10 14:51:28 +010072 guint store_changed_id;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000073} FuMainPrivate;
74
Richard Hughesf508e762015-02-27 12:49:36 +000075typedef struct {
76 FuDevice *device;
77 FuProvider *provider;
78} FuDeviceItem;
79
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000080/**
Richard Hughesd7022b52015-03-11 19:47:06 +000081 * fu_main_emit_changed:
82 **/
83static void
84fu_main_emit_changed (FuMainPrivate *priv)
85{
86 /* not yet connected */
87 if (priv->connection == NULL)
88 return;
89 g_dbus_connection_emit_signal (priv->connection,
90 NULL,
91 FWUPD_DBUS_PATH,
92 FWUPD_DBUS_INTERFACE,
93 "Changed",
94 NULL, NULL);
95}
96
97/**
Richard Hughes773ce982015-03-09 22:40:57 +000098 * fu_main_emit_property_changed:
99 **/
100static void
101fu_main_emit_property_changed (FuMainPrivate *priv,
102 const gchar *property_name,
103 GVariant *property_value)
104{
105 GVariantBuilder builder;
106 GVariantBuilder invalidated_builder;
107
108 /* not yet connected */
109 if (priv->connection == NULL)
110 return;
111
112 /* build the dict */
113 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
114 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
115 g_variant_builder_add (&builder,
116 "{sv}",
117 property_name,
118 property_value);
119 g_dbus_connection_emit_signal (priv->connection,
120 NULL,
121 FWUPD_DBUS_PATH,
122 "org.freedesktop.DBus.Properties",
123 "PropertiesChanged",
124 g_variant_new ("(sa{sv}as)",
125 FWUPD_DBUS_INTERFACE,
126 &builder,
127 &invalidated_builder),
128 NULL);
129 g_variant_builder_clear (&builder);
130 g_variant_builder_clear (&invalidated_builder);
131}
132
133/**
134 * fu_main_set_status:
135 **/
136static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000137fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000138{
Richard Hughes773ce982015-03-09 22:40:57 +0000139 if (priv->status == status)
140 return;
141 priv->status = status;
142
143 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000144 g_debug ("Emitting PropertyChanged('Status'='%s')",
145 fwupd_status_to_string (status));
146 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000147}
148
149/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000150 * fu_main_device_array_to_variant:
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000151 **/
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000152static GVariant *
Richard Hughes7708a0f2015-07-21 08:41:22 +0100153fu_main_device_array_to_variant (GPtrArray *devices, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000154{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000155 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000156 guint i;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000157
Richard Hughes9a38c032015-03-17 20:58:46 +0000158 /* no devices */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100159 if (devices->len == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000160 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000161 FWUPD_ERROR,
162 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9d76a872015-09-17 12:49:07 +0100163 "Nothing to do");
Richard Hughes9a38c032015-03-17 20:58:46 +0000164 return NULL;
165 }
166
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000167 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100168 for (i = 0; i < devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000169 GVariant *tmp;
170 FuDeviceItem *item;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100171 item = g_ptr_array_index (devices, i);
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000172 tmp = fu_device_to_variant (item->device);
173 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000174 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000175 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000176}
177
178/**
Richard Hughesf508e762015-02-27 12:49:36 +0000179 * fu_main_item_free:
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000180 **/
Richard Hughesf508e762015-02-27 12:49:36 +0000181static void
182fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000183{
Richard Hughesf508e762015-02-27 12:49:36 +0000184 g_object_unref (item->device);
185 g_object_unref (item->provider);
186 g_free (item);
187}
188
189/**
190 * fu_main_get_item_by_id:
191 **/
192static FuDeviceItem *
193fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
194{
195 FuDeviceItem *item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000196 guint i;
197
198 for (i = 0; i < priv->devices->len; i++) {
Richard Hughesf508e762015-02-27 12:49:36 +0000199 item = g_ptr_array_index (priv->devices, i);
200 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
201 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000202 }
203 return NULL;
204}
205
Richard Hughesd079b1a2015-03-06 10:09:55 +0000206/**
Richard Hughes0e883ee2015-03-18 17:22:33 +0000207 * fu_main_get_provider_by_name:
208 **/
209static FuProvider *
210fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
211{
212 FuProvider *provider;
213 guint i;
214
215 for (i = 0; i < priv->providers->len; i++) {
216 provider = g_ptr_array_index (priv->providers, i);
217 if (g_strcmp0 (fu_provider_get_name (provider), name) == 0)
218 return provider;
219 }
220 return NULL;
221}
222
Richard Hughes5d14def2015-10-07 17:43:10 +0100223/**
224 * fu_main_get_release_trust_flags:
225 **/
226static gboolean
227fu_main_get_release_trust_flags (AsRelease *release,
228 FwupdTrustFlags *trust_flags,
229 GError **error)
230{
231 AsChecksum *csum_tmp;
232 GBytes *blob_payload;
233 GBytes *blob_signature;
234 const gchar *fn;
235 g_autofree gchar *pki_dir = NULL;
236 g_autofree gchar *fn_signature = NULL;
237 g_autoptr(GError) error_local = NULL;
238 g_autoptr(FuKeyring) kr = NULL;
239
240 /* no filename? */
241 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
242 fn = as_checksum_get_filename (csum_tmp);
243 if (fn == NULL) {
244 g_set_error_literal (error,
245 FWUPD_ERROR,
246 FWUPD_ERROR_INVALID_FILE,
247 "no filename");
248 return FALSE;
249 }
250
251 /* no signature == no trust */
252 fn_signature = g_strdup_printf ("%s.asc", fn);
253 blob_signature = as_release_get_blob (release, fn_signature);
254 if (blob_signature == NULL) {
255 g_debug ("firmware archive contained no GPG signature");
256 return TRUE;
257 }
258
259 /* get payload */
260 blob_payload = as_release_get_blob (release, fn);
261 if (blob_payload == NULL) {
262 g_set_error_literal (error,
263 FWUPD_ERROR,
264 FWUPD_ERROR_INVALID_FILE,
265 "no payload");
266 return FALSE;
267 }
268
269 /* check we were installed correctly */
270 pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
271 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
272 g_set_error (error,
273 FWUPD_ERROR,
274 FWUPD_ERROR_NOT_FOUND,
275 "PKI directory %s not found", pki_dir);
276 return FALSE;
277 }
278
279 /* verify against the system trusted keys */
280 kr = fu_keyring_new ();
281 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
282 return FALSE;
283 if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
284 g_warning ("untrusted as failed to verify: %s",
285 error_local->message);
286 return TRUE;
287 }
288
289 /* awesome! */
290 g_debug ("marking payload as trusted");
291 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
292 return TRUE;
293}
294
Richard Hughesf508e762015-02-27 12:49:36 +0000295typedef struct {
296 GDBusMethodInvocation *invocation;
Richard Hughes5d14def2015-10-07 17:43:10 +0100297 AsStore *store;
298 FwupdTrustFlags trust_flags;
Richard Hughes67ec8982015-03-03 11:39:27 +0000299 FuDevice *device;
Richard Hughes74cc2172015-02-27 13:19:46 +0000300 FuProviderFlags flags;
Richard Hughes5d14def2015-10-07 17:43:10 +0100301 GBytes *blob_fw;
302 GBytes *blob_cab;
Richard Hughes63bbbf52015-04-14 16:12:16 +0100303 gint vercmp;
Richard Hughes67ec8982015-03-03 11:39:27 +0000304 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000305} FuMainAuthHelper;
306
307/**
308 * fu_main_helper_free:
309 **/
310static void
311fu_main_helper_free (FuMainAuthHelper *helper)
312{
Richard Hughes67ec8982015-03-03 11:39:27 +0000313 /* free */
Richard Hughesd079b1a2015-03-06 10:09:55 +0000314 if (helper->device != NULL)
315 g_object_unref (helper->device);
Richard Hughes5d14def2015-10-07 17:43:10 +0100316 if (helper->blob_fw > 0)
317 g_bytes_unref (helper->blob_fw);
318 if (helper->blob_cab > 0)
319 g_bytes_unref (helper->blob_cab);
Richard Hughes67ec8982015-03-03 11:39:27 +0000320 g_object_unref (helper->invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +0100321 g_object_unref (helper->store);
Richard Hughesf508e762015-02-27 12:49:36 +0000322 g_free (helper);
323}
324
325/**
Richard Hughes18423292015-03-09 17:10:50 +0000326 * fu_main_provider_update_authenticated:
327 **/
328static gboolean
329fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
330{
331 FuDeviceItem *item;
332
333 /* check the device still exists */
334 item = fu_main_get_item_by_id (helper->priv, fu_device_get_id (helper->device));
335 if (item == NULL) {
336 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000337 FWUPD_ERROR,
338 FWUPD_ERROR_INVALID_FILE,
Richard Hughes18423292015-03-09 17:10:50 +0000339 "device %s was removed",
340 fu_device_get_id (helper->device));
341 return FALSE;
342 }
343
344 /* run the correct provider that added this */
345 return fu_provider_update (item->provider,
346 item->device,
Richard Hughes5d14def2015-10-07 17:43:10 +0100347 helper->blob_cab,
348 helper->blob_fw,
Richard Hughes18423292015-03-09 17:10:50 +0000349 helper->flags,
350 error);
351}
352
353/**
Richard Hughesf508e762015-02-27 12:49:36 +0000354 * fu_main_check_authorization_cb:
355 **/
356static void
357fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
358{
359 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100360 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100361 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000362
363 /* get result */
364 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
365 res, &error);
366 if (auth == NULL) {
367 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000368 FWUPD_ERROR,
369 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000370 "could not check for auth: %s",
371 error->message);
372 fu_main_helper_free (helper);
373 return;
374 }
375
376 /* did not auth */
377 if (!polkit_authorization_result_get_is_authorized (auth)) {
378 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000379 FWUPD_ERROR,
380 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000381 "failed to obtain auth");
382 fu_main_helper_free (helper);
383 return;
384 }
385
Richard Hughes18423292015-03-09 17:10:50 +0000386 /* we're good to go */
387 if (!fu_main_provider_update_authenticated (helper, &error)) {
388 g_dbus_method_invocation_return_gerror (helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000389 fu_main_helper_free (helper);
390 return;
391 }
392
393 /* success */
394 g_dbus_method_invocation_return_value (helper->invocation, NULL);
395 fu_main_helper_free (helper);
396}
397
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000398/**
Richard Hughes5d14def2015-10-07 17:43:10 +0100399 * fu_main_get_guids_from_store:
400 **/
401static gchar *
402fu_main_get_guids_from_store (AsStore *store)
403{
404 AsApp *app;
405 AsProvide *prov;
406 GPtrArray *provides;
407 GPtrArray *apps;
408 GString *str = g_string_new ("");
409 guint i;
410 guint j;
411
412 /* return a string with all the firmware apps in the store */
413 apps = as_store_get_apps (store);
414 for (i = 0; i < apps->len; i++) {
415 app = AS_APP (g_ptr_array_index (apps, i));
416 provides = as_app_get_provides (app);
417 for (j = 0; j < provides->len; j++) {
418 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
419 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
420 continue;
421 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
422 }
423 }
424 if (str->len == 0)
425 return NULL;
426 g_string_truncate (str, str->len - 1);
427 return g_string_free (str, FALSE);
428}
429
430/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000431 * fu_main_update_helper:
432 **/
433static gboolean
434fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
435{
Richard Hughes5d14def2015-10-07 17:43:10 +0100436 AsApp *app;
437 AsChecksum *csum_tmp;
438 AsRelease *rel;
Richard Hughesdef31752015-03-04 19:26:54 +0000439 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000440 const gchar *version;
Richard Hughes5d14def2015-10-07 17:43:10 +0100441 guint i;
Richard Hughes67ec8982015-03-03 11:39:27 +0000442
Richard Hughes5d14def2015-10-07 17:43:10 +0100443 /* load store file which also decompresses firmware */
444 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
445 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000446 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000447
Richard Hughes5d14def2015-10-07 17:43:10 +0100448 /* if we've not chosen a device, try and find anything in the
449 * cabinet 'store' that matches any installed device */
Richard Hughesd079b1a2015-03-06 10:09:55 +0000450 if (helper->device == NULL) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100451 for (i = 0; i < helper->priv->devices->len; i++) {
452 FuDeviceItem *item;
453 item = g_ptr_array_index (helper->priv->devices, i);
454 app = as_store_get_app_by_provide (helper->store,
455 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
456 fu_device_get_guid (item->device));
457 if (app != NULL) {
458 helper->device = g_object_ref (item->device);
459 break;
460 }
461 }
462
463 /* nothing found */
464 if (helper->device == NULL) {
465 g_autofree gchar *guid = NULL;
466 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000467 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000468 FWUPD_ERROR,
469 FWUPD_ERROR_INVALID_FILE,
Richard Hughes5d14def2015-10-07 17:43:10 +0100470 "no attached hardware matched %s",
Richard Hughesd079b1a2015-03-06 10:09:55 +0000471 guid);
472 return FALSE;
473 }
Richard Hughes5d14def2015-10-07 17:43:10 +0100474 } else {
475 /* find an application from the cabinet 'store' for the
476 * chosen device */
477 app = as_store_get_app_by_provide (helper->store,
478 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
479 fu_device_get_guid (helper->device));
480 if (app == NULL) {
481 g_autofree gchar *guid = NULL;
482 guid = fu_main_get_guids_from_store (helper->store);
483 g_set_error (error,
484 FWUPD_ERROR,
485 FWUPD_ERROR_INVALID_FILE,
486 "firmware is not for this hw: required %s got %s",
487 fu_device_get_guid (helper->device), guid);
488 return FALSE;
489 }
Richard Hughesdef31752015-03-04 19:26:54 +0000490 }
491
Richard Hughesdef31752015-03-04 19:26:54 +0000492 /* parse the DriverVer */
Richard Hughes5d14def2015-10-07 17:43:10 +0100493 rel = as_app_get_release_default (app);
494 if (rel == NULL) {
495 g_set_error_literal (error,
496 FWUPD_ERROR,
497 FWUPD_ERROR_INVALID_FILE,
498 "no releases in the firmware component");
499 return FALSE;
500 }
501
502 /* get the blob */
503 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
504 tmp = as_checksum_get_filename (csum_tmp);
505 g_assert (tmp != NULL);
506 helper->blob_fw = as_release_get_blob (rel, tmp);
507 if (helper->blob_fw == NULL) {
508 g_set_error_literal (error,
509 FWUPD_ERROR,
510 FWUPD_ERROR_READ,
511 "failed to get firmware blob");
512 return FALSE;
513 }
514
515 version = as_release_get_version (rel);
Richard Hughes2257cee2015-09-08 14:46:17 +0100516 fu_device_set_metadata (helper->device, FU_DEVICE_KEY_UPDATE_VERSION, version);
Richard Hughesdef31752015-03-04 19:26:54 +0000517
Richard Hughes18e75912015-03-05 13:17:17 +0000518 /* compare to the lowest supported version, if it exists */
519 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION_LOWEST);
520 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
521 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000522 FWUPD_ERROR,
523 FWUPD_ERROR_VERSION_NEWER,
Richard Hughes18e75912015-03-05 13:17:17 +0000524 "Specified firmware is older than the minimum "
525 "required version '%s < %s'", tmp, version);
526 return FALSE;
527 }
528
Richard Hughesdef31752015-03-04 19:26:54 +0000529 /* compare the versions of what we have installed */
530 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION);
Richard Hughesd1e823c2015-03-04 22:18:30 +0000531 if (tmp == NULL) {
532 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000533 FWUPD_ERROR,
534 FWUPD_ERROR_INTERNAL,
Richard Hughesd1e823c2015-03-04 22:18:30 +0000535 "Device %s does not yet have a current version",
536 fu_device_get_id (helper->device));
537 return FALSE;
538 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100539 helper->vercmp = as_utils_vercmp (tmp, version);
540 if (helper->vercmp == 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000541 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000542 FWUPD_ERROR,
543 FWUPD_ERROR_VERSION_SAME,
Richard Hughesdef31752015-03-04 19:26:54 +0000544 "Specified firmware is already installed '%s'",
545 tmp);
546 return FALSE;
547 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100548 if (helper->vercmp > 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000549 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000550 FWUPD_ERROR,
551 FWUPD_ERROR_VERSION_NEWER,
Richard Hughesdef31752015-03-04 19:26:54 +0000552 "Specified firmware is older than installed '%s < %s'",
553 tmp, version);
Richard Hughes67ec8982015-03-03 11:39:27 +0000554 return FALSE;
555 }
556
Richard Hughes5d14def2015-10-07 17:43:10 +0100557 /* verify */
558 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
Richard Hughes1a886b12015-07-15 17:22:10 +0100559 return FALSE;
Richard Hughes67ec8982015-03-03 11:39:27 +0000560 return TRUE;
561}
562
563/**
Richard Hughes18423292015-03-09 17:10:50 +0000564 * fu_main_dbus_get_uid:
565 *
566 * Return value: the UID, or %G_MAXUINT if it could not be obtained
567 **/
568static guint
569fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
570{
571 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +0100572 g_autoptr(GError) error = NULL;
573 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +0000574
575 if (priv->proxy_uid == NULL)
576 return G_MAXUINT;
577 value = g_dbus_proxy_call_sync (priv->proxy_uid,
578 "GetConnectionUnixUser",
579 g_variant_new ("(s)", sender),
580 G_DBUS_CALL_FLAGS_NONE,
581 -1,
582 NULL,
583 &error);
584 if (value == NULL) {
585 g_warning ("Failed to get uid for %s: %s",
586 sender, error->message);
587 return G_MAXUINT;
588 }
589 g_variant_get (value, "(u)", &uid);
590 return uid;
591}
592
593/**
Richard Hughes0e883ee2015-03-18 17:22:33 +0000594 * fu_main_get_item_by_id_fallback_pending:
595 **/
596static FuDeviceItem *
597fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
598{
599 FuDevice *dev;
600 FuProvider *provider;
601 FuDeviceItem *item = NULL;
602 const gchar *tmp;
603 guint i;
Richard Hughes46832432015-09-11 13:43:15 +0100604 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000605
606 /* not a wildcard */
607 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
608 item = fu_main_get_item_by_id (priv, id);
609 if (item == NULL) {
610 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000611 FWUPD_ERROR,
612 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000613 "no suitable device found for %s", id);
614 }
615 return item;
616 }
617
618 /* allow '*' for any */
619 devices = fu_pending_get_devices (priv->pending, error);
620 if (devices == NULL)
621 return NULL;
622 for (i = 0; i < devices->len; i++) {
623 dev = g_ptr_array_index (devices, i);
624 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PENDING_STATE);
625 if (tmp == NULL)
626 continue;
627 if (g_strcmp0 (tmp, "scheduled") == 0)
628 continue;
629
630 /* if the device is not still connected, fake a FuDeviceItem */
631 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
632 if (item == NULL) {
633 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PROVIDER);
634 provider = fu_main_get_provider_by_name (priv, tmp);
635 if (provider == NULL) {
636 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000637 FWUPD_ERROR,
638 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000639 "no provider %s found", tmp);
640 }
641 item = g_new0 (FuDeviceItem, 1);
642 item->device = g_object_ref (dev);
643 item->provider = g_object_ref (provider);
644 g_ptr_array_add (priv->devices, item);
645
646 /* FIXME: just a boolean on FuDeviceItem? */
647 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
648 }
649 break;
650 }
651
652 /* no device found */
653 if (item == NULL) {
654 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000655 FWUPD_ERROR,
656 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000657 "no suitable devices found");
658 }
659 return item;
660}
661
662/**
Richard Hughes63bbbf52015-04-14 16:12:16 +0100663 * fu_main_get_action_id_for_device:
664 **/
665static const gchar *
666fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
667{
Richard Hughes63bbbf52015-04-14 16:12:16 +0100668 gboolean is_trusted;
669 gboolean is_downgrade;
670
671 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +0100672 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +0100673 is_downgrade = helper->vercmp > 0;
674
675 /* relax authentication checks for removable devices */
Richard Hughesd7dba982015-05-05 16:02:17 +0100676 if ((fu_device_get_flags (helper->device) & FU_DEVICE_FLAG_INTERNAL) == 0) {
Richard Hughes63bbbf52015-04-14 16:12:16 +0100677 if (is_downgrade)
678 return "org.freedesktop.fwupd.downgrade-hotplug";
679 if (is_trusted)
680 return "org.freedesktop.fwupd.update-hotplug-trusted";
681 return "org.freedesktop.fwupd.update-hotplug";
682 }
683
684 /* internal device */
685 if (is_downgrade)
686 return "org.freedesktop.fwupd.downgrade-internal";
687 if (is_trusted)
688 return "org.freedesktop.fwupd.update-internal-trusted";
689 return "org.freedesktop.fwupd.update-internal";
690}
691
692/**
Richard Hughesae0efdc2015-06-24 16:18:29 +0100693 * fu_main_daemon_update_metadata:
694 *
695 * Supports optionally GZipped AppStream files up to 1MiB in size.
696 **/
697static gboolean
Mario Limonciello3ed54472015-07-23 13:19:39 -0500698fu_main_daemon_update_metadata (FuMainPrivate *priv, gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +0100699{
700 guint8 magic[2];
Richard Hughes46832432015-09-11 13:43:15 +0100701 g_autoptr(GBytes) bytes = NULL;
702 g_autoptr(GBytes) bytes_raw = NULL;
703 g_autoptr(GBytes) bytes_sig = NULL;
704 g_autoptr(FuKeyring) kr = NULL;
705 g_autoptr(GConverter) converter = NULL;
706 g_autoptr(GFile) file = NULL;
707 g_autoptr(GInputStream) stream_buf = NULL;
708 g_autoptr(GInputStream) stream_fd = NULL;
709 g_autoptr(GInputStream) stream = NULL;
710 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +0100711
Richard Hughesae0efdc2015-06-24 16:18:29 +0100712 /* read the entire file into memory */
713 stream_fd = g_unix_input_stream_new (fd, TRUE);
714 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
715 if (bytes_raw == NULL)
716 return FALSE;
717 stream_buf = g_memory_input_stream_new ();
718 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
719
720 /* peek the file type and get data */
721 if (g_input_stream_read (stream_buf, magic, 2, NULL, error) == -1)
722 return FALSE;
723 if (magic[0] == 0x1f && magic[1] == 0x8b) {
724 g_debug ("using GZip decompressor for data");
725 if (!g_seekable_seek (G_SEEKABLE (stream_buf), 0, G_SEEK_SET, NULL, error))
726 return FALSE;
727 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
728 stream = g_converter_input_stream_new (stream_buf, converter);
729 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
730 if (bytes == NULL)
731 return FALSE;
732 } else if (magic[0] == '<' && magic[1] == '?') {
733 g_debug ("using no decompressor for data");
734 bytes = g_bytes_ref (bytes_raw);
735 } else {
736 g_set_error (error,
737 FWUPD_ERROR,
738 FWUPD_ERROR_INVALID_FILE,
739 "file type '0x%02x,0x%02x' not supported",
740 magic[0], magic[1]);
741 return FALSE;
742 }
743
744 /* read signature */
745 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
746 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
747 if (bytes_sig == NULL)
748 return FALSE;
749
750 /* verify file */
751 kr = fu_keyring_new ();
752 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
753 return FALSE;
754 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
755 return FALSE;
756
757 /* merge in the new contents */
Richard Hughes033ccba2015-09-10 14:51:28 +0100758 as_store_remove_all (priv->store);
759 if (!as_store_from_xml (priv->store,
Richard Hughese00d70d2015-08-04 14:44:37 +0100760 g_bytes_get_data (bytes, NULL),
Richard Hughesae0efdc2015-06-24 16:18:29 +0100761 NULL, error))
762 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +0100763
764 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +0100765 as_store_set_api_version (priv->store, 0.9);
766 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
767 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +0100768 AS_NODE_TO_XML_FLAG_ADD_HEADER |
769 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
770 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
771 NULL, error)) {
772 return FALSE;
773 }
774
775 return TRUE;
776}
777
778/**
Richard Hughes033ccba2015-09-10 14:51:28 +0100779 * fu_main_store_delay_cb:
780 **/
781static gboolean
782fu_main_store_delay_cb (gpointer user_data)
783{
784 AsApp *app;
785 GPtrArray *apps;
786 guint i;
787 FuMainPrivate *priv = (FuMainPrivate *) user_data;
788
789 g_debug ("devices now in store:");
790 apps = as_store_get_apps (priv->store);
791 for (i = 0; i < apps->len; i++) {
792 app = g_ptr_array_index (apps, i);
793 g_debug ("%i\t%s\t%s", i + 1,
794 as_app_get_id (app),
795 as_app_get_name (app, NULL));
796 }
797 priv->store_changed_id = 0;
798 return G_SOURCE_REMOVE;
799}
800
801/**
802 * fu_main_store_changed_cb:
803 **/
804static void
805fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
806{
807 if (priv->store_changed_id != 0)
808 return;
809 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
810}
811
812/**
Richard Hughes7708a0f2015-07-21 08:41:22 +0100813 * fu_main_get_updates:
814 **/
815static GPtrArray *
816fu_main_get_updates (FuMainPrivate *priv, GError **error)
817{
818 AsApp *app;
819 AsRelease *rel;
820 FuDeviceItem *item;
821 GPtrArray *updates;
822 guint i;
823 const gchar *tmp;
824
825 /* find any updates using the AppStream metadata */
826 updates = g_ptr_array_new ();
827 for (i = 0; i < priv->devices->len; i++) {
828 const gchar *version;
Richard Hughes9985a242015-08-03 13:22:31 +0100829 AsChecksum *csum;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100830
831 item = g_ptr_array_index (priv->devices, i);
832
833 /* get device version */
834 version = fu_device_get_metadata (item->device, FU_DEVICE_KEY_VERSION);
835 if (version == NULL)
836 continue;
837
838 /* match the GUID in the XML */
Richard Hughesba145822015-08-11 14:34:08 +0100839 app = as_store_get_app_by_provide (priv->store,
840 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
841 fu_device_get_guid (item->device));
Richard Hughes7708a0f2015-07-21 08:41:22 +0100842 if (app == NULL)
843 continue;
844
845 /* get latest release */
846 rel = as_app_get_release_default (app);
847 if (rel == NULL) {
848 g_debug ("%s has no firmware update metadata",
849 fu_device_get_id (item->device));
850 continue;
851 }
852
853 /* check if actually newer than what we have installed */
854 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0) {
855 g_debug ("%s has no firmware updates",
856 fu_device_get_id (item->device));
857 continue;
858 }
859
Richard Hughes82856d92015-09-08 14:30:38 +0100860 /* add application metadata */
861 tmp = as_app_get_developer_name (app, NULL);
862 if (tmp != NULL) {
863 fu_device_set_metadata (item->device,
864 FU_DEVICE_KEY_VENDOR, tmp);
865 }
866 tmp = as_app_get_name (app, NULL);
867 if (tmp != NULL) {
868 fu_device_set_metadata (item->device,
869 FU_DEVICE_KEY_NAME, tmp);
870 }
871 tmp = as_app_get_comment (app, NULL);
872 if (tmp != NULL) {
873 fu_device_set_metadata (item->device,
874 FU_DEVICE_KEY_SUMMARY, tmp);
875 }
876 tmp = as_app_get_description (app, NULL);
877 if (tmp != NULL) {
878 fu_device_set_metadata (item->device,
879 FU_DEVICE_KEY_DESCRIPTION, tmp);
880 }
881 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
882 if (tmp != NULL) {
883 fu_device_set_metadata (item->device,
884 FU_DEVICE_KEY_URL_HOMEPAGE, tmp);
885 }
886 tmp = as_app_get_project_license (app);
887 if (tmp != NULL) {
888 fu_device_set_metadata (item->device,
889 FU_DEVICE_KEY_LICENSE, tmp);
890 }
891
892 /* add release information */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100893 tmp = as_release_get_version (rel);
894 if (tmp != NULL) {
895 fu_device_set_metadata (item->device,
896 FU_DEVICE_KEY_UPDATE_VERSION, tmp);
897 }
Richard Hughes9985a242015-08-03 13:22:31 +0100898 csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTAINER);
899 if (csum != NULL) {
900 fu_device_set_metadata (item->device,
901 FU_DEVICE_KEY_UPDATE_HASH,
902 as_checksum_get_value (csum));
903 }
Richard Hughes7708a0f2015-07-21 08:41:22 +0100904 tmp = as_release_get_location_default (rel);
905 if (tmp != NULL) {
906 fu_device_set_metadata (item->device,
907 FU_DEVICE_KEY_UPDATE_URI, tmp);
908 }
909 tmp = as_release_get_description (rel, NULL);
910 if (tmp != NULL) {
Richard Hughesfe8b96e2015-07-28 12:18:16 +0100911 fu_device_set_metadata (item->device,
912 FU_DEVICE_KEY_UPDATE_DESCRIPTION,
913 tmp);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100914 }
915 g_ptr_array_add (updates, item);
916 }
917
918 return updates;
919}
920
921/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000922 * fu_main_daemon_method_call:
923 **/
924static void
925fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
926 const gchar *object_path, const gchar *interface_name,
927 const gchar *method_name, GVariant *parameters,
928 GDBusMethodInvocation *invocation, gpointer user_data)
929{
930 FuMainPrivate *priv = (FuMainPrivate *) user_data;
931 GVariant *val;
932
933 /* return 'as' */
934 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +0100935 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000936 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100937 val = fu_main_device_array_to_variant (priv->devices, &error);
938 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +0100939 if (g_error_matches (error,
940 FWUPD_ERROR,
941 FWUPD_ERROR_NOTHING_TO_DO)) {
942 g_prefix_error (&error, "No detected devices: ");
943 }
Richard Hughes7708a0f2015-07-21 08:41:22 +0100944 g_dbus_method_invocation_return_gerror (invocation, error);
945 return;
946 }
947 g_dbus_method_invocation_return_value (invocation, val);
948 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
949 return;
950 }
951
952 /* return 'as' */
953 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +0100954 g_autoptr(GError) error = NULL;
955 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100956 g_debug ("Called %s()", method_name);
957 updates = fu_main_get_updates (priv, &error);
958 if (updates == NULL) {
959 g_dbus_method_invocation_return_gerror (invocation, error);
960 return;
961 }
962 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +0000963 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +0100964 if (g_error_matches (error,
965 FWUPD_ERROR,
966 FWUPD_ERROR_NOTHING_TO_DO)) {
967 g_prefix_error (&error, "No devices can be updated: ");
968 }
Richard Hughes9a38c032015-03-17 20:58:46 +0000969 g_dbus_method_invocation_return_gerror (invocation, error);
970 return;
971 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000972 g_dbus_method_invocation_return_value (invocation, val);
Richard Hughesf910ac92015-03-19 10:43:42 +0000973 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000974 return;
975 }
976
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000977 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +0000978 if (g_strcmp0 (method_name, "ClearResults") == 0) {
979 FuDeviceItem *item = NULL;
980 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +0100981 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000982
983 g_variant_get (parameters, "(&s)", &id);
984 g_debug ("Called %s(%s)", method_name, id);
985
986 /* find device */
987 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
988 if (item == NULL) {
989 g_dbus_method_invocation_return_gerror (invocation, error);
990 return;
991 }
992
993 /* call into the provider */
994 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
995 g_dbus_method_invocation_return_gerror (invocation, error);
996 return;
997 }
998
999 /* success */
1000 g_dbus_method_invocation_return_value (invocation, NULL);
1001 return;
1002 }
1003
1004 /* return 'a{sv}' */
1005 if (g_strcmp0 (method_name, "GetResults") == 0) {
1006 FuDeviceItem *item = NULL;
1007 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001008 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001009
1010 g_variant_get (parameters, "(&s)", &id);
1011 g_debug ("Called %s(%s)", method_name, id);
1012
1013 /* find device */
1014 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1015 if (item == NULL) {
1016 g_dbus_method_invocation_return_gerror (invocation, error);
1017 return;
1018 }
1019
1020 /* call into the provider */
1021 if (!fu_provider_get_results (item->provider, item->device, &error)) {
1022 g_dbus_method_invocation_return_gerror (invocation, error);
1023 return;
1024 }
1025
1026 /* success */
1027 val = fu_device_get_metadata_as_variant (item->device);
1028 g_dbus_method_invocation_return_value (invocation, val);
1029 return;
1030 }
1031
1032 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01001033 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
1034 GDBusMessage *message;
1035 GUnixFDList *fd_list;
1036 gint fd_data;
1037 gint fd_sig;
Richard Hughes46832432015-09-11 13:43:15 +01001038 g_autoptr(GError) error = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001039
1040 message = g_dbus_method_invocation_get_message (invocation);
1041 fd_list = g_dbus_message_get_unix_fd_list (message);
1042 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
1043 g_dbus_method_invocation_return_error (invocation,
1044 FWUPD_ERROR,
1045 FWUPD_ERROR_INTERNAL,
1046 "invalid handle");
1047 return;
1048 }
1049 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
1050 if (fd_data < 0) {
1051 g_dbus_method_invocation_return_gerror (invocation, error);
1052 return;
1053 }
1054 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
1055 if (fd_sig < 0) {
1056 g_dbus_method_invocation_return_gerror (invocation, error);
1057 return;
1058 }
Mario Limonciello3ed54472015-07-23 13:19:39 -05001059 if (!fu_main_daemon_update_metadata (priv, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001060 g_prefix_error (&error, "failed to update metadata: ");
1061 g_dbus_method_invocation_return_gerror (invocation, error);
1062 return;
1063 }
1064 g_dbus_method_invocation_return_value (invocation, NULL);
1065 return;
1066 }
1067
Richard Hughesa043c2e2015-06-29 08:43:18 +01001068 /* return 's' */
1069 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001070 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001071 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001072 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001073 FuDeviceItem *item = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001074 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001075 const gchar *id = NULL;
1076 const gchar *version = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001077 g_autoptr(GError) error = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001078
1079 /* check the id exists */
1080 g_variant_get (parameters, "(&s)", &id);
1081 g_debug ("Called %s(%s)", method_name, id);
1082 item = fu_main_get_item_by_id (priv, id);
1083 if (item == NULL) {
1084 g_dbus_method_invocation_return_error (invocation,
1085 FWUPD_ERROR,
1086 FWUPD_ERROR_NOT_FOUND,
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001087 "No such device %s",
Richard Hughesa043c2e2015-06-29 08:43:18 +01001088 id);
1089 return;
1090 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001091
1092 /* set the device firmware hash */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001093 if (!fu_provider_verify (item->provider, item->device,
1094 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
1095 g_dbus_method_invocation_return_gerror (invocation, error);
1096 return;
1097 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001098
1099 /* find component in metadata */
1100 app = as_store_get_app_by_id (priv->store, fu_device_get_guid (item->device));
1101 if (app == NULL) {
1102 g_dbus_method_invocation_return_error (invocation,
1103 FWUPD_ERROR,
1104 FWUPD_ERROR_NOT_SUPPORTED,
1105 "No metadata");
1106 return;
1107 }
1108
1109 /* find version in metadata */
1110 version = fu_device_get_metadata (item->device, FU_DEVICE_KEY_VERSION);
1111 release = as_app_get_release (app, version);
1112 if (release == NULL) {
1113 g_dbus_method_invocation_return_error (invocation,
1114 FWUPD_ERROR,
1115 FWUPD_ERROR_NOT_SUPPORTED,
1116 "No version %s",
1117 version);
1118 return;
1119 }
1120
1121 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001122 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
1123 if (csum == NULL) {
1124 g_dbus_method_invocation_return_error (invocation,
1125 FWUPD_ERROR,
1126 FWUPD_ERROR_NOT_SUPPORTED,
1127 "No content checksum for %s",
1128 version);
1129 return;
1130 }
Richard Hughesa043c2e2015-06-29 08:43:18 +01001131 hash = fu_device_get_metadata (item->device, FU_DEVICE_KEY_FIRMWARE_HASH);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001132 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
1133 g_dbus_method_invocation_return_error (invocation,
1134 FWUPD_ERROR,
1135 FWUPD_ERROR_NOT_SUPPORTED,
1136 "For v%s expected %s, got %s",
1137 version,
1138 as_checksum_get_value (csum),
1139 hash);
1140 return;
1141 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001142 g_dbus_method_invocation_return_value (invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001143 return;
1144 }
1145
Richard Hughesae0efdc2015-06-24 16:18:29 +01001146 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01001147 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00001148 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001149 FuMainAuthHelper *helper;
Richard Hughes74cc2172015-02-27 13:19:46 +00001150 FuProviderFlags flags = FU_PROVIDER_UPDATE_FLAG_NONE;
1151 GDBusMessage *message;
1152 GUnixFDList *fd_list;
1153 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00001154 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001155 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00001156 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001157 gint32 fd_handle = 0;
1158 gint fd;
Richard Hughes46832432015-09-11 13:43:15 +01001159 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +01001160 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001161 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001162 g_autoptr(GBytes) blob_cab = NULL;
1163 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001164
1165 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00001166 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00001167 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001168 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1169 item = fu_main_get_item_by_id (priv, id);
1170 if (item == NULL) {
1171 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001172 FWUPD_ERROR,
1173 FWUPD_ERROR_NOT_FOUND,
Richard Hughesd079b1a2015-03-06 10:09:55 +00001174 "no such device %s",
1175 id);
1176 return;
1177 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001178 }
1179
Richard Hughes74cc2172015-02-27 13:19:46 +00001180 /* get options */
1181 while (g_variant_iter_next (iter, "{&sv}",
1182 &prop_key, &prop_value)) {
1183 g_debug ("got option %s", prop_key);
1184 if (g_strcmp0 (prop_key, "offline") == 0 &&
1185 g_variant_get_boolean (prop_value) == TRUE)
1186 flags |= FU_PROVIDER_UPDATE_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00001187 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
1188 g_variant_get_boolean (prop_value) == TRUE)
1189 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER;
1190 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
1191 g_variant_get_boolean (prop_value) == TRUE)
1192 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00001193 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00001194 }
1195
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001196 /* get the fd */
1197 message = g_dbus_method_invocation_get_message (invocation);
1198 fd_list = g_dbus_message_get_unix_fd_list (message);
1199 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1200 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001201 FWUPD_ERROR,
1202 FWUPD_ERROR_INTERNAL,
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001203 "invalid handle");
1204 return;
1205 }
1206 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1207 if (fd < 0) {
1208 g_dbus_method_invocation_return_gerror (invocation,
1209 error);
1210 return;
1211 }
1212
Richard Hughes5d14def2015-10-07 17:43:10 +01001213 /* read the entire fd to a data blob */
1214 stream = g_unix_input_stream_new (fd, TRUE);
1215 blob_cab = g_input_stream_read_bytes (stream,
1216 FU_MAIN_FIRMWARE_SIZE_MAX,
1217 NULL, &error);
1218 if (blob_cab == NULL){
1219 g_dbus_method_invocation_return_gerror (invocation,
1220 error);
1221 return;
1222 }
1223
Richard Hughes67ec8982015-03-03 11:39:27 +00001224 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00001225 helper = g_new0 (FuMainAuthHelper, 1);
1226 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01001227 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
1228 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00001229 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00001230 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01001231 helper->store = as_store_new ();
Richard Hughesd079b1a2015-03-06 10:09:55 +00001232 if (item != NULL)
1233 helper->device = g_object_ref (item->device);
Richard Hughes67ec8982015-03-03 11:39:27 +00001234 if (!fu_main_update_helper (helper, &error)) {
1235 g_dbus_method_invocation_return_gerror (helper->invocation,
1236 error);
Richard Hughesf910ac92015-03-19 10:43:42 +00001237 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes67ec8982015-03-03 11:39:27 +00001238 fu_main_helper_free (helper);
1239 return;
1240 }
1241
Richard Hughes18423292015-03-09 17:10:50 +00001242 /* is root */
1243 if (fu_main_dbus_get_uid (priv, sender) == 0) {
1244 if (!fu_main_provider_update_authenticated (helper, &error)) {
1245 g_dbus_method_invocation_return_gerror (invocation, error);
1246 } else {
1247 g_dbus_method_invocation_return_value (invocation, NULL);
1248 }
Richard Hughesf910ac92015-03-19 10:43:42 +00001249 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes18423292015-03-09 17:10:50 +00001250 fu_main_helper_free (helper);
1251 return;
1252 }
1253
Richard Hughes67ec8982015-03-03 11:39:27 +00001254 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01001255 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00001256 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00001257 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00001258 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00001259 NULL,
1260 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1261 NULL,
1262 fu_main_check_authorization_cb,
1263 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001264 return;
1265 }
1266
Richard Hughescccc7752015-03-06 11:13:19 +00001267 /* return 'a{sv}' */
1268 if (g_strcmp0 (method_name, "GetDetails") == 0) {
Richard Hughes5d14def2015-10-07 17:43:10 +01001269 AsApp *app = NULL;
1270 AsRelease *rel;
Richard Hughescccc7752015-03-06 11:13:19 +00001271 GDBusMessage *message;
Richard Hughes5d14def2015-10-07 17:43:10 +01001272 GPtrArray *apps;
1273 GPtrArray *provides;
Richard Hughescccc7752015-03-06 11:13:19 +00001274 GUnixFDList *fd_list;
1275 GVariantBuilder builder;
Richard Hughes5d14def2015-10-07 17:43:10 +01001276 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
Richard Hughes3bf94802015-03-10 15:57:30 +00001277 const gchar *tmp;
Richard Hughes5d14def2015-10-07 17:43:10 +01001278 const gchar *guid = NULL;
Richard Hughescccc7752015-03-06 11:13:19 +00001279 gint32 fd_handle = 0;
Richard Hughes5d14def2015-10-07 17:43:10 +01001280 guint i;
Richard Hughescccc7752015-03-06 11:13:19 +00001281 gint fd;
Richard Hughes5d14def2015-10-07 17:43:10 +01001282 g_autoptr(AsStore) store = NULL;
1283 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001284 g_autoptr(GError) error = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001285 g_autoptr(GInputStream) stream = NULL;
Richard Hughescccc7752015-03-06 11:13:19 +00001286
1287 /* check the id exists */
1288 g_variant_get (parameters, "(h)", &fd_handle);
1289 g_debug ("Called %s(%i)", method_name, fd_handle);
1290
1291 /* get the fd */
1292 message = g_dbus_method_invocation_get_message (invocation);
1293 fd_list = g_dbus_message_get_unix_fd_list (message);
1294 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1295 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001296 FWUPD_ERROR,
1297 FWUPD_ERROR_INTERNAL,
Richard Hughescccc7752015-03-06 11:13:19 +00001298 "invalid handle");
1299 return;
1300 }
1301 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1302 if (fd < 0) {
1303 g_dbus_method_invocation_return_gerror (invocation,
1304 error);
1305 return;
1306 }
1307
Richard Hughes5d14def2015-10-07 17:43:10 +01001308 /* read the entire fd to a data blob */
1309 stream = g_unix_input_stream_new (fd, TRUE);
1310 blob_cab = g_input_stream_read_bytes (stream,
1311 FU_MAIN_FIRMWARE_SIZE_MAX,
1312 NULL, &error);
1313 if (blob_cab == NULL){
1314 g_dbus_method_invocation_return_gerror (invocation,
1315 error);
Richard Hughescccc7752015-03-06 11:13:19 +00001316 return;
1317 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001318
1319 /* load file */
1320 store = as_store_new ();
1321 if (!as_store_from_bytes (store, blob_cab, NULL, &error)) {
Richard Hughescccc7752015-03-06 11:13:19 +00001322 g_dbus_method_invocation_return_gerror (invocation, error);
1323 return;
1324 }
1325
Richard Hughes5d14def2015-10-07 17:43:10 +01001326 /* get default app */
1327 apps = as_store_get_apps (store);
1328 if (apps->len == 0) {
1329 g_dbus_method_invocation_return_error (invocation,
1330 FWUPD_ERROR,
1331 FWUPD_ERROR_INVALID_FILE,
1332 "no components");
1333 return;
1334 }
1335 if (apps->len > 1) {
1336 /* we've got a .cab file with multiple components,
1337 * so try to find the first thing that's installed */
1338 for (i = 0; i < priv->devices->len; i++) {
1339 FuDeviceItem *item;
1340 item = g_ptr_array_index (priv->devices, i);
1341 app = as_store_get_app_by_provide (store,
1342 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
1343 fu_device_get_guid (item->device));
1344 if (app != NULL)
1345 break;
1346 }
1347 }
1348
1349 /* well, we've tried our best, just show the first entry */
1350 if (app == NULL)
1351 app = AS_APP (g_ptr_array_index (apps, 0));
1352
1353 /* get guid */
1354 provides = as_app_get_provides (app);
1355 for (i = 0; i < provides->len; i++) {
1356 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1357 if (as_provide_get_kind (prov) == AS_PROVIDE_KIND_FIRMWARE_FLASHED) {
1358 guid = as_provide_get_value (prov);
1359 break;
1360 }
1361 }
1362 if (guid == NULL) {
1363 g_dbus_method_invocation_return_error (invocation,
1364 FWUPD_ERROR,
1365 FWUPD_ERROR_INTERNAL,
1366 "component has no GUID");
1367 return;
1368 }
1369
1370 /* verify trust */
1371 rel = as_app_get_release_default (app);
1372 if (!fu_main_get_release_trust_flags (rel, &trust_flags, &error)) {
Richard Hughesd8fa0492015-04-14 15:56:56 +01001373 g_dbus_method_invocation_return_gerror (invocation, error);
1374 return;
1375 }
1376
Richard Hughescccc7752015-03-06 11:13:19 +00001377 /* create an array with all the metadata in */
1378 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1379 g_variant_builder_add (&builder, "{sv}",
1380 FU_DEVICE_KEY_VERSION,
Richard Hughes5d14def2015-10-07 17:43:10 +01001381 g_variant_new_string (as_release_get_version (rel)));
Richard Hughescccc7752015-03-06 11:13:19 +00001382 g_variant_builder_add (&builder, "{sv}",
1383 FU_DEVICE_KEY_GUID,
Richard Hughes5d14def2015-10-07 17:43:10 +01001384 g_variant_new_string (guid));
Richard Hughesbbac6d72015-03-11 22:09:44 +00001385 g_variant_builder_add (&builder, "{sv}",
1386 FU_DEVICE_KEY_SIZE,
Richard Hughes5d14def2015-10-07 17:43:10 +01001387 g_variant_new_uint64 (as_release_get_size (rel, AS_SIZE_KIND_INSTALLED)));
Richard Hughes3bf94802015-03-10 15:57:30 +00001388
1389 /* optional properties */
Richard Hughes5d14def2015-10-07 17:43:10 +01001390 tmp = as_app_get_developer_name (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001391 if (tmp != NULL) {
1392 g_variant_builder_add (&builder, "{sv}",
1393 FU_DEVICE_KEY_VENDOR,
1394 g_variant_new_string (tmp));
1395 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001396 tmp = as_app_get_name (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001397 if (tmp != NULL) {
1398 g_variant_builder_add (&builder, "{sv}",
1399 FU_DEVICE_KEY_NAME,
1400 g_variant_new_string (tmp));
1401 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001402 tmp = as_app_get_comment (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001403 if (tmp != NULL) {
1404 g_variant_builder_add (&builder, "{sv}",
1405 FU_DEVICE_KEY_SUMMARY,
1406 g_variant_new_string (tmp));
1407 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001408 tmp = as_app_get_description (app, NULL);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001409 if (tmp != NULL) {
1410 g_variant_builder_add (&builder, "{sv}",
1411 FU_DEVICE_KEY_DESCRIPTION,
1412 g_variant_new_string (tmp));
1413 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001414 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001415 if (tmp != NULL) {
1416 g_variant_builder_add (&builder, "{sv}",
1417 FU_DEVICE_KEY_URL_HOMEPAGE,
1418 g_variant_new_string (tmp));
1419 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001420 tmp = as_app_get_project_license (app);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001421 if (tmp != NULL) {
1422 g_variant_builder_add (&builder, "{sv}",
1423 FU_DEVICE_KEY_LICENSE,
1424 g_variant_new_string (tmp));
1425 }
Richard Hughesd4494472015-10-08 20:10:44 +01001426 tmp = as_release_get_description (rel, NULL);
1427 if (tmp != NULL) {
1428 g_variant_builder_add (&builder, "{sv}",
1429 FU_DEVICE_KEY_UPDATE_DESCRIPTION,
1430 g_variant_new_string (tmp));
1431 }
Richard Hughesd8fa0492015-04-14 15:56:56 +01001432 g_variant_builder_add (&builder, "{sv}",
1433 FU_DEVICE_KEY_TRUSTED,
1434 g_variant_new_uint64 (trust_flags));
Richard Hughescccc7752015-03-06 11:13:19 +00001435
1436 /* return whole array */
1437 val = g_variant_new ("(a{sv})", &builder);
1438 g_dbus_method_invocation_return_value (invocation, val);
1439 return;
1440 }
1441
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001442 /* we suck */
1443 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001444 G_DBUS_ERROR,
1445 G_DBUS_ERROR_UNKNOWN_METHOD,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001446 "no such method %s",
1447 method_name);
1448}
1449
1450/**
1451 * fu_main_daemon_get_property:
1452 **/
1453static GVariant *
1454fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
1455 const gchar *object_path, const gchar *interface_name,
1456 const gchar *property_name, GError **error,
1457 gpointer user_data)
1458{
Richard Hughes773ce982015-03-09 22:40:57 +00001459 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1460
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001461 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
1462 return g_variant_new_string (VERSION);
1463
Richard Hughes773ce982015-03-09 22:40:57 +00001464 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughesf910ac92015-03-19 10:43:42 +00001465 return g_variant_new_string (fwupd_status_to_string (priv->status));
Richard Hughes773ce982015-03-09 22:40:57 +00001466
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001467 /* return an error */
1468 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001469 G_DBUS_ERROR,
1470 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001471 "failed to get daemon property %s",
1472 property_name);
1473 return NULL;
1474}
1475
1476/**
Richard Hughesfd468842015-04-22 16:44:08 +01001477 * fu_main_providers_coldplug:
1478 **/
1479static void
1480fu_main_providers_coldplug (FuMainPrivate *priv)
1481{
1482 FuProvider *provider;
1483 guint i;
Richard Hughes3f236502015-09-24 15:43:02 +01001484 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01001485
Richard Hughes3f236502015-09-24 15:43:02 +01001486 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughesfd468842015-04-22 16:44:08 +01001487 for (i = 0; i < priv->providers->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01001488 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01001489 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01001490 provider = g_ptr_array_index (priv->providers, i);
Richard Hughes3f236502015-09-24 15:43:02 +01001491 ptask2 = as_profile_start (priv->profile,
1492 "FuMain:coldplug{%s}",
1493 fu_provider_get_name (provider));
Richard Hughesfd468842015-04-22 16:44:08 +01001494 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
1495 g_warning ("Failed to coldplug: %s", error->message);
1496 }
1497}
1498
1499/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001500 * fu_main_on_bus_acquired_cb:
1501 **/
1502static void
1503fu_main_on_bus_acquired_cb (GDBusConnection *connection,
1504 const gchar *name,
1505 gpointer user_data)
1506{
1507 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1508 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01001509 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001510 static const GDBusInterfaceVTable interface_vtable = {
1511 fu_main_daemon_method_call,
1512 fu_main_daemon_get_property,
1513 NULL
1514 };
1515
1516 priv->connection = g_object_ref (connection);
1517 registration_id = g_dbus_connection_register_object (connection,
1518 FWUPD_DBUS_PATH,
1519 priv->introspection_daemon->interfaces[0],
1520 &interface_vtable,
1521 priv, /* user_data */
1522 NULL, /* user_data_free_func */
1523 NULL); /* GError** */
1524 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00001525
Richard Hughesfd468842015-04-22 16:44:08 +01001526 /* add devices */
1527 fu_main_providers_coldplug (priv);
1528
Richard Hughes18423292015-03-09 17:10:50 +00001529 /* connect to D-Bus directly */
1530 priv->proxy_uid =
1531 g_dbus_proxy_new_sync (priv->connection,
1532 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1533 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1534 NULL,
1535 "org.freedesktop.DBus",
1536 "/org/freedesktop/DBus",
1537 "org.freedesktop.DBus",
1538 NULL,
1539 &error);
1540 if (priv->proxy_uid == NULL) {
1541 g_warning ("cannot connect to DBus: %s", error->message);
1542 return;
1543 }
Richard Hughes3f236502015-09-24 15:43:02 +01001544
1545 /* dump startup profile data */
1546 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001547}
1548
1549/**
1550 * fu_main_on_name_acquired_cb:
1551 **/
1552static void
1553fu_main_on_name_acquired_cb (GDBusConnection *connection,
1554 const gchar *name,
1555 gpointer user_data)
1556{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001557 g_debug ("FuMain: acquired name: %s", name);
1558}
1559
1560/**
1561 * fu_main_on_name_lost_cb:
1562 **/
1563static void
1564fu_main_on_name_lost_cb (GDBusConnection *connection,
1565 const gchar *name,
1566 gpointer user_data)
1567{
1568 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1569 g_debug ("FuMain: lost name: %s", name);
1570 g_main_loop_quit (priv->loop);
1571}
1572
1573/**
1574 * fu_main_timed_exit_cb:
1575 **/
1576static gboolean
1577fu_main_timed_exit_cb (gpointer user_data)
1578{
1579 GMainLoop *loop = (GMainLoop *) user_data;
1580 g_main_loop_quit (loop);
1581 return G_SOURCE_REMOVE;
1582}
1583
1584/**
1585 * fu_main_load_introspection:
1586 **/
1587static GDBusNodeInfo *
1588fu_main_load_introspection (const gchar *filename, GError **error)
1589{
Richard Hughes46832432015-09-11 13:43:15 +01001590 g_autoptr(GBytes) data = NULL;
1591 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001592
1593 /* lookup data */
1594 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
1595 data = g_resource_lookup_data (fu_get_resource (),
1596 path,
1597 G_RESOURCE_LOOKUP_FLAGS_NONE,
1598 error);
1599 if (data == NULL)
1600 return NULL;
1601
1602 /* build introspection from XML */
1603 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
1604}
1605
1606/**
1607 * cd_main_provider_device_added_cb:
1608 **/
1609static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001610cd_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001611 FuDevice *device,
1612 gpointer user_data)
1613{
1614 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001615 FuDeviceItem *item;
1616
Richard Hughes0e883ee2015-03-18 17:22:33 +00001617 /* remove any fake device */
1618 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
1619 if (item != NULL)
1620 g_ptr_array_remove (priv->devices, item);
1621
1622 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00001623 item = g_new0 (FuDeviceItem, 1);
1624 item->device = g_object_ref (device);
1625 item->provider = g_object_ref (provider);
1626 g_ptr_array_add (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001627 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001628}
1629
1630/**
1631 * cd_main_provider_device_removed_cb:
1632 **/
1633static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001634cd_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001635 FuDevice *device,
1636 gpointer user_data)
1637{
1638 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001639 FuDeviceItem *item;
1640
1641 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
1642 if (item == NULL) {
1643 g_warning ("can't remove device %s", fu_device_get_id (device));
1644 return;
1645 }
1646 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001647 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001648}
1649
1650/**
Richard Hughes773ce982015-03-09 22:40:57 +00001651 * cd_main_provider_status_changed_cb:
1652 **/
1653static void
1654cd_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00001655 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00001656 gpointer user_data)
1657{
1658 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1659 fu_main_set_status (priv, status);
1660}
1661
1662/**
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001663 * fu_main_add_provider:
1664 **/
1665static void
1666fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
1667{
1668 g_signal_connect (provider, "device-added",
1669 G_CALLBACK (cd_main_provider_device_added_cb),
1670 priv);
1671 g_signal_connect (provider, "device-removed",
1672 G_CALLBACK (cd_main_provider_device_removed_cb),
1673 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00001674 g_signal_connect (provider, "status-changed",
1675 G_CALLBACK (cd_main_provider_status_changed_cb),
1676 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001677 g_ptr_array_add (priv->providers, provider);
1678}
1679
1680/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001681 * main:
1682 **/
1683int
1684main (int argc, char *argv[])
1685{
1686 FuMainPrivate *priv = NULL;
1687 gboolean immediate_exit = FALSE;
1688 gboolean ret;
1689 gboolean timed_exit = FALSE;
1690 GOptionContext *context;
1691 guint owner_id = 0;
1692 guint retval = 1;
1693 const GOptionEntry options[] = {
1694 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
1695 /* TRANSLATORS: exit after we've started up, used for user profiling */
1696 _("Exit after a small delay"), NULL },
1697 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
1698 /* TRANSLATORS: exit straight away, used for automatic profiling */
1699 _("Exit after the engine has loaded"), NULL },
1700 { NULL}
1701 };
Richard Hughes46832432015-09-11 13:43:15 +01001702 g_autoptr(GError) error = NULL;
1703 g_autofree gchar *config_file = NULL;
1704 g_autoptr(GKeyFile) config = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001705
1706 setlocale (LC_ALL, "");
1707
1708 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1709 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1710 textdomain (GETTEXT_PACKAGE);
1711
1712 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01001713 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001714 context = g_option_context_new (NULL);
1715 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001716 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00001717 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001718 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
1719 ret = g_option_context_parse (context, &argc, &argv, &error);
1720 if (!ret) {
1721 g_warning ("FuMain: failed to parse command line arguments: %s",
1722 error->message);
1723 goto out;
1724 }
1725
1726 /* create new objects */
1727 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00001728 priv->status = FWUPD_STATUS_IDLE;
Richard Hughesf508e762015-02-27 12:49:36 +00001729 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001730 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001731 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01001732 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01001733 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01001734 g_signal_connect (priv->store, "changed",
1735 G_CALLBACK (fu_main_store_changed_cb), priv);
1736 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
1737 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001738
1739 /* load AppStream */
1740 as_store_add_filter (priv->store, AS_ID_KIND_FIRMWARE);
1741 if (!as_store_load (priv->store,
1742 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
1743 NULL, &error)){
1744 g_warning ("FuMain: failed to load AppStream data: %s",
1745 error->message);
1746 return FALSE;
1747 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001748
Richard Hughes804c0752015-08-04 14:53:52 +01001749 /* read config file */
1750 config = g_key_file_new ();
1751 config_file = g_build_filename (SYSCONFDIR, "fwupd.conf", NULL);
1752 g_debug ("Loading fallback values from %s", config_file);
1753 if (!g_key_file_load_from_file (config, config_file,
1754 G_KEY_FILE_NONE, &error)) {
1755 g_print ("failed to load config file %s: %s\n",
1756 config_file, error->message);
1757 retval = EXIT_FAILURE;
1758 goto out;
1759 }
1760
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001761 /* add providers */
1762 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes804c0752015-08-04 14:53:52 +01001763 if (g_key_file_get_boolean (config, "fwupd", "EnableOptionROM", NULL))
1764 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughesc89c1b02015-05-05 15:21:18 +01001765 fu_main_add_provider (priv, fu_provider_usb_new ());
Richard Hughes25cf6ab2015-08-04 21:34:12 +01001766 fu_main_add_provider (priv, fu_provider_rpi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001767#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00001768 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001769#endif
1770#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001771 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001772#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001773
1774 /* load introspection from file */
1775 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
1776 &error);
1777 if (priv->introspection_daemon == NULL) {
1778 g_warning ("FuMain: failed to load daemon introspection: %s",
1779 error->message);
1780 goto out;
1781 }
1782
Richard Hughesf508e762015-02-27 12:49:36 +00001783 /* get authority */
1784 priv->authority = polkit_authority_get_sync (NULL, &error);
1785 if (priv->authority == NULL) {
1786 g_warning ("FuMain: failed to load polkit authority: %s",
1787 error->message);
1788 goto out;
1789 }
1790
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001791 /* own the object */
1792 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
1793 FWUPD_DBUS_SERVICE,
1794 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
1795 G_BUS_NAME_OWNER_FLAGS_REPLACE,
1796 fu_main_on_bus_acquired_cb,
1797 fu_main_on_name_acquired_cb,
1798 fu_main_on_name_lost_cb,
1799 priv, NULL);
1800
1801 /* Only timeout and close the mainloop if we have specified it
1802 * on the command line */
1803 if (immediate_exit)
1804 g_idle_add (fu_main_timed_exit_cb, priv->loop);
1805 else if (timed_exit)
1806 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
1807
1808 /* wait */
1809 g_info ("Daemon ready for requests");
1810 g_main_loop_run (priv->loop);
1811
1812 /* success */
1813 retval = 0;
1814out:
1815 g_option_context_free (context);
1816 if (owner_id > 0)
1817 g_bus_unown_name (owner_id);
1818 if (priv != NULL) {
1819 if (priv->loop != NULL)
1820 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00001821 if (priv->proxy_uid != NULL)
1822 g_object_unref (priv->proxy_uid);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001823 if (priv->connection != NULL)
1824 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00001825 if (priv->authority != NULL)
1826 g_object_unref (priv->authority);
Richard Hughes3f236502015-09-24 15:43:02 +01001827 if (priv->profile != NULL)
1828 g_object_unref (priv->profile);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001829 if (priv->store != NULL)
1830 g_object_unref (priv->store);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001831 if (priv->introspection_daemon != NULL)
1832 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes033ccba2015-09-10 14:51:28 +01001833 if (priv->store_changed_id != 0)
1834 g_source_remove (priv->store_changed_id);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001835 g_object_unref (priv->pending);
Richard Hughes804c0752015-08-04 14:53:52 +01001836 if (priv->providers != NULL)
1837 g_ptr_array_unref (priv->providers);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001838 g_ptr_array_unref (priv->devices);
1839 g_free (priv);
1840 }
1841 return retval;
1842}
1843