blob: 14e479553b6e503e35bc08990029037c9127fdb6 [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 Hughes5d057a82015-11-24 18:09:57 +000041#include "fu-provider-dfu.h"
Richard Hughes25cf6ab2015-08-04 21:34:12 +010042#include "fu-provider-rpi.h"
Richard Hughesebb58a32015-05-29 15:35:37 +010043#include "fu-provider-udev.h"
Richard Hughesc89c1b02015-05-05 15:21:18 +010044#include "fu-provider-usb.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000045#include "fu-resources.h"
46
Richard Hughes3c99ba42015-03-05 12:17:48 +000047#ifdef HAVE_COLORHUG
48 #include "fu-provider-chug.h"
49#endif
50#ifdef HAVE_UEFI
51 #include "fu-provider-uefi.h"
52#endif
53
Richard Hughes60f48c22015-10-08 20:25:51 +010054#ifndef PolkitAuthorizationResult_autoptr
55G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
56G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref)
57#endif
58
Richard Hughes5d14def2015-10-07 17:43:10 +010059#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */
60
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000061typedef struct {
62 GDBusConnection *connection;
63 GDBusNodeInfo *introspection_daemon;
Richard Hughes18423292015-03-09 17:10:50 +000064 GDBusProxy *proxy_uid;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000065 GMainLoop *loop;
Richard Hughes5d14def2015-10-07 17:43:10 +010066 GPtrArray *devices; /* of FuDeviceItem */
Richard Hughes8bbfdf42015-02-26 22:28:09 +000067 GPtrArray *providers;
Richard Hughesf508e762015-02-27 12:49:36 +000068 PolkitAuthority *authority;
Richard Hughesf910ac92015-03-19 10:43:42 +000069 FwupdStatus status;
Richard Hughes0e883ee2015-03-18 17:22:33 +000070 FuPending *pending;
Richard Hughes3f236502015-09-24 15:43:02 +010071 AsProfile *profile;
Richard Hughes7708a0f2015-07-21 08:41:22 +010072 AsStore *store;
Richard Hughes033ccba2015-09-10 14:51:28 +010073 guint store_changed_id;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000074} FuMainPrivate;
75
Richard Hughesf508e762015-02-27 12:49:36 +000076typedef struct {
77 FuDevice *device;
78 FuProvider *provider;
79} FuDeviceItem;
80
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000081/**
Richard Hughesd7022b52015-03-11 19:47:06 +000082 * fu_main_emit_changed:
83 **/
84static void
85fu_main_emit_changed (FuMainPrivate *priv)
86{
87 /* not yet connected */
88 if (priv->connection == NULL)
89 return;
90 g_dbus_connection_emit_signal (priv->connection,
91 NULL,
92 FWUPD_DBUS_PATH,
93 FWUPD_DBUS_INTERFACE,
94 "Changed",
95 NULL, NULL);
96}
97
98/**
Richard Hughes773ce982015-03-09 22:40:57 +000099 * fu_main_emit_property_changed:
100 **/
101static void
102fu_main_emit_property_changed (FuMainPrivate *priv,
103 const gchar *property_name,
104 GVariant *property_value)
105{
106 GVariantBuilder builder;
107 GVariantBuilder invalidated_builder;
108
109 /* not yet connected */
110 if (priv->connection == NULL)
111 return;
112
113 /* build the dict */
114 g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
115 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
116 g_variant_builder_add (&builder,
117 "{sv}",
118 property_name,
119 property_value);
120 g_dbus_connection_emit_signal (priv->connection,
121 NULL,
122 FWUPD_DBUS_PATH,
123 "org.freedesktop.DBus.Properties",
124 "PropertiesChanged",
125 g_variant_new ("(sa{sv}as)",
126 FWUPD_DBUS_INTERFACE,
127 &builder,
128 &invalidated_builder),
129 NULL);
130 g_variant_builder_clear (&builder);
131 g_variant_builder_clear (&invalidated_builder);
132}
133
134/**
135 * fu_main_set_status:
136 **/
137static void
Richard Hughesf910ac92015-03-19 10:43:42 +0000138fu_main_set_status (FuMainPrivate *priv, FwupdStatus status)
Richard Hughes773ce982015-03-09 22:40:57 +0000139{
Richard Hughes773ce982015-03-09 22:40:57 +0000140 if (priv->status == status)
141 return;
142 priv->status = status;
143
144 /* emit changed */
Richard Hughes88181512015-03-19 10:57:44 +0000145 g_debug ("Emitting PropertyChanged('Status'='%s')",
146 fwupd_status_to_string (status));
147 fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status));
Richard Hughes773ce982015-03-09 22:40:57 +0000148}
149
150/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000151 * fu_main_device_array_to_variant:
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000152 **/
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000153static GVariant *
Richard Hughes7708a0f2015-07-21 08:41:22 +0100154fu_main_device_array_to_variant (GPtrArray *devices, GError **error)
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000155{
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000156 GVariantBuilder builder;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000157 guint i;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000158
Richard Hughes9a38c032015-03-17 20:58:46 +0000159 /* no devices */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100160 if (devices->len == 0) {
Richard Hughes9a38c032015-03-17 20:58:46 +0000161 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000162 FWUPD_ERROR,
163 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes9d76a872015-09-17 12:49:07 +0100164 "Nothing to do");
Richard Hughes9a38c032015-03-17 20:58:46 +0000165 return NULL;
166 }
167
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000168 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100169 for (i = 0; i < devices->len; i++) {
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000170 GVariant *tmp;
171 FuDeviceItem *item;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100172 item = g_ptr_array_index (devices, i);
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000173 tmp = fu_device_to_variant (item->device);
174 g_variant_builder_add_value (&builder, tmp);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000175 }
Richard Hughes1ffde6c2015-03-02 22:44:48 +0000176 return g_variant_new ("(a{sa{sv}})", &builder);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000177}
178
179/**
Richard Hughesf508e762015-02-27 12:49:36 +0000180 * fu_main_item_free:
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000181 **/
Richard Hughesf508e762015-02-27 12:49:36 +0000182static void
183fu_main_item_free (FuDeviceItem *item)
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000184{
Richard Hughesf508e762015-02-27 12:49:36 +0000185 g_object_unref (item->device);
186 g_object_unref (item->provider);
187 g_free (item);
188}
189
190/**
191 * fu_main_get_item_by_id:
192 **/
193static FuDeviceItem *
194fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id)
195{
196 FuDeviceItem *item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000197 guint i;
198
199 for (i = 0; i < priv->devices->len; i++) {
Richard Hughesf508e762015-02-27 12:49:36 +0000200 item = g_ptr_array_index (priv->devices, i);
201 if (g_strcmp0 (fu_device_get_id (item->device), id) == 0)
202 return item;
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000203 }
204 return NULL;
205}
206
Richard Hughesd079b1a2015-03-06 10:09:55 +0000207/**
Richard Hughes0e883ee2015-03-18 17:22:33 +0000208 * fu_main_get_provider_by_name:
209 **/
210static FuProvider *
211fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name)
212{
213 FuProvider *provider;
214 guint i;
215
216 for (i = 0; i < priv->providers->len; i++) {
217 provider = g_ptr_array_index (priv->providers, i);
218 if (g_strcmp0 (fu_provider_get_name (provider), name) == 0)
219 return provider;
220 }
221 return NULL;
222}
223
Richard Hughes5d14def2015-10-07 17:43:10 +0100224/**
225 * fu_main_get_release_trust_flags:
226 **/
227static gboolean
228fu_main_get_release_trust_flags (AsRelease *release,
229 FwupdTrustFlags *trust_flags,
230 GError **error)
231{
232 AsChecksum *csum_tmp;
233 GBytes *blob_payload;
234 GBytes *blob_signature;
235 const gchar *fn;
236 g_autofree gchar *pki_dir = NULL;
237 g_autofree gchar *fn_signature = NULL;
238 g_autoptr(GError) error_local = NULL;
239 g_autoptr(FuKeyring) kr = NULL;
240
241 /* no filename? */
242 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
243 fn = as_checksum_get_filename (csum_tmp);
244 if (fn == NULL) {
245 g_set_error_literal (error,
246 FWUPD_ERROR,
247 FWUPD_ERROR_INVALID_FILE,
248 "no filename");
249 return FALSE;
250 }
251
252 /* no signature == no trust */
253 fn_signature = g_strdup_printf ("%s.asc", fn);
254 blob_signature = as_release_get_blob (release, fn_signature);
255 if (blob_signature == NULL) {
256 g_debug ("firmware archive contained no GPG signature");
257 return TRUE;
258 }
259
260 /* get payload */
261 blob_payload = as_release_get_blob (release, fn);
262 if (blob_payload == NULL) {
263 g_set_error_literal (error,
264 FWUPD_ERROR,
265 FWUPD_ERROR_INVALID_FILE,
266 "no payload");
267 return FALSE;
268 }
269
270 /* check we were installed correctly */
271 pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL);
272 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
273 g_set_error (error,
274 FWUPD_ERROR,
275 FWUPD_ERROR_NOT_FOUND,
276 "PKI directory %s not found", pki_dir);
277 return FALSE;
278 }
279
280 /* verify against the system trusted keys */
281 kr = fu_keyring_new ();
282 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
283 return FALSE;
284 if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) {
285 g_warning ("untrusted as failed to verify: %s",
286 error_local->message);
287 return TRUE;
288 }
289
290 /* awesome! */
291 g_debug ("marking payload as trusted");
292 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
293 return TRUE;
294}
295
Richard Hughesf508e762015-02-27 12:49:36 +0000296typedef struct {
297 GDBusMethodInvocation *invocation;
Richard Hughes5d14def2015-10-07 17:43:10 +0100298 AsStore *store;
299 FwupdTrustFlags trust_flags;
Richard Hughes67ec8982015-03-03 11:39:27 +0000300 FuDevice *device;
Richard Hughes74cc2172015-02-27 13:19:46 +0000301 FuProviderFlags flags;
Richard Hughes5d14def2015-10-07 17:43:10 +0100302 GBytes *blob_fw;
303 GBytes *blob_cab;
Richard Hughes63bbbf52015-04-14 16:12:16 +0100304 gint vercmp;
Richard Hughes67ec8982015-03-03 11:39:27 +0000305 FuMainPrivate *priv;
Richard Hughesf508e762015-02-27 12:49:36 +0000306} FuMainAuthHelper;
307
308/**
309 * fu_main_helper_free:
310 **/
311static void
312fu_main_helper_free (FuMainAuthHelper *helper)
313{
Richard Hughes67ec8982015-03-03 11:39:27 +0000314 /* free */
Richard Hughesd079b1a2015-03-06 10:09:55 +0000315 if (helper->device != NULL)
316 g_object_unref (helper->device);
Richard Hughes5d14def2015-10-07 17:43:10 +0100317 if (helper->blob_fw > 0)
318 g_bytes_unref (helper->blob_fw);
319 if (helper->blob_cab > 0)
320 g_bytes_unref (helper->blob_cab);
Richard Hughes67ec8982015-03-03 11:39:27 +0000321 g_object_unref (helper->invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +0100322 g_object_unref (helper->store);
Richard Hughesf508e762015-02-27 12:49:36 +0000323 g_free (helper);
324}
325
326/**
Richard Hughes18423292015-03-09 17:10:50 +0000327 * fu_main_provider_update_authenticated:
328 **/
329static gboolean
330fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error)
331{
332 FuDeviceItem *item;
333
334 /* check the device still exists */
335 item = fu_main_get_item_by_id (helper->priv, fu_device_get_id (helper->device));
336 if (item == NULL) {
337 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000338 FWUPD_ERROR,
339 FWUPD_ERROR_INVALID_FILE,
Richard Hughes18423292015-03-09 17:10:50 +0000340 "device %s was removed",
341 fu_device_get_id (helper->device));
342 return FALSE;
343 }
344
345 /* run the correct provider that added this */
Richard Hughesa3a8f502015-11-24 12:31:59 +0000346 if (!fu_provider_update (item->provider,
347 item->device,
348 helper->blob_cab,
349 helper->blob_fw,
350 helper->flags,
351 error))
352 return FALSE;
353
354 /* make the UI update */
355 fu_main_emit_changed (helper->priv);
356 return TRUE;
Richard Hughes18423292015-03-09 17:10:50 +0000357}
358
359/**
Richard Hughesf508e762015-02-27 12:49:36 +0000360 * fu_main_check_authorization_cb:
361 **/
362static void
363fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data)
364{
365 FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data;
Richard Hughes46832432015-09-11 13:43:15 +0100366 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +0100367 g_autoptr(PolkitAuthorizationResult) auth = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000368
369 /* get result */
370 auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
371 res, &error);
372 if (auth == NULL) {
373 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000374 FWUPD_ERROR,
375 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000376 "could not check for auth: %s",
377 error->message);
378 fu_main_helper_free (helper);
379 return;
380 }
381
382 /* did not auth */
383 if (!polkit_authorization_result_get_is_authorized (auth)) {
384 g_dbus_method_invocation_return_error (helper->invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +0000385 FWUPD_ERROR,
386 FWUPD_ERROR_AUTH_FAILED,
Richard Hughesf508e762015-02-27 12:49:36 +0000387 "failed to obtain auth");
388 fu_main_helper_free (helper);
389 return;
390 }
391
Richard Hughes18423292015-03-09 17:10:50 +0000392 /* we're good to go */
393 if (!fu_main_provider_update_authenticated (helper, &error)) {
394 g_dbus_method_invocation_return_gerror (helper->invocation, error);
Richard Hughesf508e762015-02-27 12:49:36 +0000395 fu_main_helper_free (helper);
396 return;
397 }
398
399 /* success */
400 g_dbus_method_invocation_return_value (helper->invocation, NULL);
401 fu_main_helper_free (helper);
402}
403
Richard Hughes8bbfdf42015-02-26 22:28:09 +0000404/**
Richard Hughes5d14def2015-10-07 17:43:10 +0100405 * fu_main_get_guids_from_store:
406 **/
407static gchar *
408fu_main_get_guids_from_store (AsStore *store)
409{
410 AsApp *app;
411 AsProvide *prov;
412 GPtrArray *provides;
413 GPtrArray *apps;
414 GString *str = g_string_new ("");
415 guint i;
416 guint j;
417
418 /* return a string with all the firmware apps in the store */
419 apps = as_store_get_apps (store);
420 for (i = 0; i < apps->len; i++) {
421 app = AS_APP (g_ptr_array_index (apps, i));
422 provides = as_app_get_provides (app);
423 for (j = 0; j < provides->len; j++) {
424 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
425 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
426 continue;
427 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
428 }
429 }
430 if (str->len == 0)
431 return NULL;
432 g_string_truncate (str, str->len - 1);
433 return g_string_free (str, FALSE);
434}
435
436/**
Richard Hughes67ec8982015-03-03 11:39:27 +0000437 * fu_main_update_helper:
438 **/
439static gboolean
440fu_main_update_helper (FuMainAuthHelper *helper, GError **error)
441{
Richard Hughes5d14def2015-10-07 17:43:10 +0100442 AsApp *app;
443 AsChecksum *csum_tmp;
444 AsRelease *rel;
Richard Hughesdef31752015-03-04 19:26:54 +0000445 const gchar *tmp;
Richard Hughescccc7752015-03-06 11:13:19 +0000446 const gchar *version;
Richard Hughes5d14def2015-10-07 17:43:10 +0100447 guint i;
Richard Hughes67ec8982015-03-03 11:39:27 +0000448
Richard Hughes5d14def2015-10-07 17:43:10 +0100449 /* load store file which also decompresses firmware */
450 fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING);
451 if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error))
Richard Hughes67ec8982015-03-03 11:39:27 +0000452 return FALSE;
Richard Hughesd079b1a2015-03-06 10:09:55 +0000453
Richard Hughes5d14def2015-10-07 17:43:10 +0100454 /* if we've not chosen a device, try and find anything in the
455 * cabinet 'store' that matches any installed device */
Richard Hughesd079b1a2015-03-06 10:09:55 +0000456 if (helper->device == NULL) {
Richard Hughes5d14def2015-10-07 17:43:10 +0100457 for (i = 0; i < helper->priv->devices->len; i++) {
458 FuDeviceItem *item;
459 item = g_ptr_array_index (helper->priv->devices, i);
460 app = as_store_get_app_by_provide (helper->store,
461 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
462 fu_device_get_guid (item->device));
463 if (app != NULL) {
464 helper->device = g_object_ref (item->device);
465 break;
466 }
467 }
468
469 /* nothing found */
470 if (helper->device == NULL) {
471 g_autofree gchar *guid = NULL;
472 guid = fu_main_get_guids_from_store (helper->store);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000473 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000474 FWUPD_ERROR,
475 FWUPD_ERROR_INVALID_FILE,
Richard Hughes5d14def2015-10-07 17:43:10 +0100476 "no attached hardware matched %s",
Richard Hughesd079b1a2015-03-06 10:09:55 +0000477 guid);
478 return FALSE;
479 }
Richard Hughes5d14def2015-10-07 17:43:10 +0100480 } else {
481 /* find an application from the cabinet 'store' for the
482 * chosen device */
483 app = as_store_get_app_by_provide (helper->store,
484 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
485 fu_device_get_guid (helper->device));
486 if (app == NULL) {
487 g_autofree gchar *guid = NULL;
488 guid = fu_main_get_guids_from_store (helper->store);
489 g_set_error (error,
490 FWUPD_ERROR,
491 FWUPD_ERROR_INVALID_FILE,
492 "firmware is not for this hw: required %s got %s",
493 fu_device_get_guid (helper->device), guid);
494 return FALSE;
495 }
Richard Hughesdef31752015-03-04 19:26:54 +0000496 }
497
Richard Hughesdef31752015-03-04 19:26:54 +0000498 /* parse the DriverVer */
Richard Hughes5d14def2015-10-07 17:43:10 +0100499 rel = as_app_get_release_default (app);
500 if (rel == NULL) {
501 g_set_error_literal (error,
502 FWUPD_ERROR,
503 FWUPD_ERROR_INVALID_FILE,
504 "no releases in the firmware component");
505 return FALSE;
506 }
507
508 /* get the blob */
509 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
510 tmp = as_checksum_get_filename (csum_tmp);
511 g_assert (tmp != NULL);
512 helper->blob_fw = as_release_get_blob (rel, tmp);
513 if (helper->blob_fw == NULL) {
514 g_set_error_literal (error,
515 FWUPD_ERROR,
516 FWUPD_ERROR_READ,
517 "failed to get firmware blob");
518 return FALSE;
519 }
520
521 version = as_release_get_version (rel);
Richard Hughes2257cee2015-09-08 14:46:17 +0100522 fu_device_set_metadata (helper->device, FU_DEVICE_KEY_UPDATE_VERSION, version);
Richard Hughesdef31752015-03-04 19:26:54 +0000523
Richard Hughes18e75912015-03-05 13:17:17 +0000524 /* compare to the lowest supported version, if it exists */
525 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION_LOWEST);
526 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
527 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000528 FWUPD_ERROR,
529 FWUPD_ERROR_VERSION_NEWER,
Richard Hughes18e75912015-03-05 13:17:17 +0000530 "Specified firmware is older than the minimum "
531 "required version '%s < %s'", tmp, version);
532 return FALSE;
533 }
534
Richard Hughesdef31752015-03-04 19:26:54 +0000535 /* compare the versions of what we have installed */
536 tmp = fu_device_get_metadata (helper->device, FU_DEVICE_KEY_VERSION);
Richard Hughesd1e823c2015-03-04 22:18:30 +0000537 if (tmp == NULL) {
538 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000539 FWUPD_ERROR,
540 FWUPD_ERROR_INTERNAL,
Richard Hughesd1e823c2015-03-04 22:18:30 +0000541 "Device %s does not yet have a current version",
542 fu_device_get_id (helper->device));
543 return FALSE;
544 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100545 helper->vercmp = as_utils_vercmp (tmp, version);
546 if (helper->vercmp == 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000547 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000548 FWUPD_ERROR,
549 FWUPD_ERROR_VERSION_SAME,
Richard Hughesdef31752015-03-04 19:26:54 +0000550 "Specified firmware is already installed '%s'",
551 tmp);
552 return FALSE;
553 }
Richard Hughes63bbbf52015-04-14 16:12:16 +0100554 if (helper->vercmp > 0 && (helper->flags & FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER) == 0) {
Richard Hughesdef31752015-03-04 19:26:54 +0000555 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000556 FWUPD_ERROR,
557 FWUPD_ERROR_VERSION_NEWER,
Richard Hughesdef31752015-03-04 19:26:54 +0000558 "Specified firmware is older than installed '%s < %s'",
559 tmp, version);
Richard Hughes67ec8982015-03-03 11:39:27 +0000560 return FALSE;
561 }
562
Richard Hughes5d14def2015-10-07 17:43:10 +0100563 /* verify */
564 if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error))
Richard Hughes1a886b12015-07-15 17:22:10 +0100565 return FALSE;
Richard Hughes67ec8982015-03-03 11:39:27 +0000566 return TRUE;
567}
568
569/**
Richard Hughes18423292015-03-09 17:10:50 +0000570 * fu_main_dbus_get_uid:
571 *
572 * Return value: the UID, or %G_MAXUINT if it could not be obtained
573 **/
574static guint
575fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender)
576{
577 guint uid;
Richard Hughes46832432015-09-11 13:43:15 +0100578 g_autoptr(GError) error = NULL;
579 g_autoptr(GVariant) value = NULL;
Richard Hughes18423292015-03-09 17:10:50 +0000580
581 if (priv->proxy_uid == NULL)
582 return G_MAXUINT;
583 value = g_dbus_proxy_call_sync (priv->proxy_uid,
584 "GetConnectionUnixUser",
585 g_variant_new ("(s)", sender),
586 G_DBUS_CALL_FLAGS_NONE,
587 -1,
588 NULL,
589 &error);
590 if (value == NULL) {
591 g_warning ("Failed to get uid for %s: %s",
592 sender, error->message);
593 return G_MAXUINT;
594 }
595 g_variant_get (value, "(u)", &uid);
596 return uid;
597}
598
599/**
Richard Hughes0e883ee2015-03-18 17:22:33 +0000600 * fu_main_get_item_by_id_fallback_pending:
601 **/
602static FuDeviceItem *
603fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error)
604{
605 FuDevice *dev;
606 FuProvider *provider;
607 FuDeviceItem *item = NULL;
608 const gchar *tmp;
609 guint i;
Richard Hughes46832432015-09-11 13:43:15 +0100610 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +0000611
612 /* not a wildcard */
613 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
614 item = fu_main_get_item_by_id (priv, id);
615 if (item == NULL) {
616 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000617 FWUPD_ERROR,
618 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000619 "no suitable device found for %s", id);
620 }
621 return item;
622 }
623
624 /* allow '*' for any */
625 devices = fu_pending_get_devices (priv->pending, error);
626 if (devices == NULL)
627 return NULL;
628 for (i = 0; i < devices->len; i++) {
629 dev = g_ptr_array_index (devices, i);
630 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PENDING_STATE);
631 if (tmp == NULL)
632 continue;
633 if (g_strcmp0 (tmp, "scheduled") == 0)
634 continue;
635
636 /* if the device is not still connected, fake a FuDeviceItem */
637 item = fu_main_get_item_by_id (priv, fu_device_get_id (dev));
638 if (item == NULL) {
639 tmp = fu_device_get_metadata (dev, FU_DEVICE_KEY_PROVIDER);
640 provider = fu_main_get_provider_by_name (priv, tmp);
641 if (provider == NULL) {
642 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000643 FWUPD_ERROR,
644 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000645 "no provider %s found", tmp);
646 }
647 item = g_new0 (FuDeviceItem, 1);
648 item->device = g_object_ref (dev);
649 item->provider = g_object_ref (provider);
650 g_ptr_array_add (priv->devices, item);
651
652 /* FIXME: just a boolean on FuDeviceItem? */
653 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
654 }
655 break;
656 }
657
658 /* no device found */
659 if (item == NULL) {
660 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000661 FWUPD_ERROR,
662 FWUPD_ERROR_NOT_FOUND,
Richard Hughes0e883ee2015-03-18 17:22:33 +0000663 "no suitable devices found");
664 }
665 return item;
666}
667
668/**
Richard Hughes63bbbf52015-04-14 16:12:16 +0100669 * fu_main_get_action_id_for_device:
670 **/
671static const gchar *
672fu_main_get_action_id_for_device (FuMainAuthHelper *helper)
673{
Richard Hughes63bbbf52015-04-14 16:12:16 +0100674 gboolean is_trusted;
675 gboolean is_downgrade;
676
677 /* only test the payload */
Richard Hughes5d14def2015-10-07 17:43:10 +0100678 is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
Richard Hughes63bbbf52015-04-14 16:12:16 +0100679 is_downgrade = helper->vercmp > 0;
680
681 /* relax authentication checks for removable devices */
Richard Hughesd7dba982015-05-05 16:02:17 +0100682 if ((fu_device_get_flags (helper->device) & FU_DEVICE_FLAG_INTERNAL) == 0) {
Richard Hughes63bbbf52015-04-14 16:12:16 +0100683 if (is_downgrade)
684 return "org.freedesktop.fwupd.downgrade-hotplug";
685 if (is_trusted)
686 return "org.freedesktop.fwupd.update-hotplug-trusted";
687 return "org.freedesktop.fwupd.update-hotplug";
688 }
689
690 /* internal device */
691 if (is_downgrade)
692 return "org.freedesktop.fwupd.downgrade-internal";
693 if (is_trusted)
694 return "org.freedesktop.fwupd.update-internal-trusted";
695 return "org.freedesktop.fwupd.update-internal";
696}
697
698/**
Richard Hughesae0efdc2015-06-24 16:18:29 +0100699 * fu_main_daemon_update_metadata:
700 *
701 * Supports optionally GZipped AppStream files up to 1MiB in size.
702 **/
703static gboolean
Mario Limonciello3ed54472015-07-23 13:19:39 -0500704fu_main_daemon_update_metadata (FuMainPrivate *priv, gint fd, gint fd_sig, GError **error)
Richard Hughesae0efdc2015-06-24 16:18:29 +0100705{
Richard Hughesf2fca012015-10-30 08:44:44 +0000706 const guint8 *data;
Richard Hughes727664f2015-10-27 09:56:04 +0000707 guint i;
Richard Hughesf2fca012015-10-30 08:44:44 +0000708 gsize size;
Richard Hughes727664f2015-10-27 09:56:04 +0000709 GPtrArray *apps;
Richard Hughesbb840ce2015-10-30 08:47:24 +0000710 g_autofree gchar *xml = NULL;
Richard Hughes727664f2015-10-27 09:56:04 +0000711 g_autoptr(AsStore) store = NULL;
Richard Hughes46832432015-09-11 13:43:15 +0100712 g_autoptr(GBytes) bytes = NULL;
713 g_autoptr(GBytes) bytes_raw = NULL;
714 g_autoptr(GBytes) bytes_sig = NULL;
715 g_autoptr(FuKeyring) kr = NULL;
716 g_autoptr(GConverter) converter = NULL;
717 g_autoptr(GFile) file = NULL;
718 g_autoptr(GInputStream) stream_buf = NULL;
719 g_autoptr(GInputStream) stream_fd = NULL;
720 g_autoptr(GInputStream) stream = NULL;
721 g_autoptr(GInputStream) stream_sig = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +0100722
Richard Hughesae0efdc2015-06-24 16:18:29 +0100723 /* read the entire file into memory */
724 stream_fd = g_unix_input_stream_new (fd, TRUE);
725 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
726 if (bytes_raw == NULL)
727 return FALSE;
728 stream_buf = g_memory_input_stream_new ();
729 g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw);
730
731 /* peek the file type and get data */
Richard Hughesf2fca012015-10-30 08:44:44 +0000732 data = g_bytes_get_data (bytes_raw, &size);
733 if (size < 2) {
734 g_set_error_literal (error,
735 FWUPD_ERROR,
736 FWUPD_ERROR_INVALID_FILE,
737 "file is too small");
Richard Hughesae0efdc2015-06-24 16:18:29 +0100738 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +0000739 }
740 if (data[0] == 0x1f && data[1] == 0x8b) {
Richard Hughesae0efdc2015-06-24 16:18:29 +0100741 g_debug ("using GZip decompressor for data");
Richard Hughesae0efdc2015-06-24 16:18:29 +0100742 converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
743 stream = g_converter_input_stream_new (stream_buf, converter);
744 bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error);
745 if (bytes == NULL)
746 return FALSE;
Richard Hughesf2fca012015-10-30 08:44:44 +0000747 } else if (data[0] == '<' && data[1] == '?') {
Richard Hughesae0efdc2015-06-24 16:18:29 +0100748 g_debug ("using no decompressor for data");
749 bytes = g_bytes_ref (bytes_raw);
750 } else {
751 g_set_error (error,
752 FWUPD_ERROR,
753 FWUPD_ERROR_INVALID_FILE,
754 "file type '0x%02x,0x%02x' not supported",
Richard Hughesf2fca012015-10-30 08:44:44 +0000755 data[0], data[1]);
Richard Hughesae0efdc2015-06-24 16:18:29 +0100756 return FALSE;
757 }
758
759 /* read signature */
760 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
761 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
762 if (bytes_sig == NULL)
763 return FALSE;
764
765 /* verify file */
766 kr = fu_keyring_new ();
767 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
768 return FALSE;
769 if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error))
770 return FALSE;
771
Richard Hughes727664f2015-10-27 09:56:04 +0000772 /* load the store locally until we know it is valid */
773 store = as_store_new ();
Richard Hughesbb840ce2015-10-30 08:47:24 +0000774 data = g_bytes_get_data (bytes, &size);
775 xml = g_strndup ((const gchar *) data, size);
776 if (!as_store_from_xml (store, xml, NULL, error))
Richard Hughesae0efdc2015-06-24 16:18:29 +0100777 return FALSE;
Richard Hughesae0efdc2015-06-24 16:18:29 +0100778
Richard Hughes727664f2015-10-27 09:56:04 +0000779 /* add the new application from the store */
780 as_store_remove_all (priv->store);
781 apps = as_store_get_apps (store);
782 for (i = 0; i < apps->len; i++) {
783 AsApp *app = g_ptr_array_index (apps, i);
784 as_store_add_app (priv->store, app);
785 }
786
Richard Hughesae0efdc2015-06-24 16:18:29 +0100787 /* save the new file */
Richard Hughes033ccba2015-09-10 14:51:28 +0100788 as_store_set_api_version (priv->store, 0.9);
789 file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml");
790 if (!as_store_to_file (priv->store, file,
Richard Hughesae0efdc2015-06-24 16:18:29 +0100791 AS_NODE_TO_XML_FLAG_ADD_HEADER |
792 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE |
793 AS_NODE_TO_XML_FLAG_FORMAT_INDENT,
794 NULL, error)) {
795 return FALSE;
796 }
797
798 return TRUE;
799}
800
801/**
Richard Hughes033ccba2015-09-10 14:51:28 +0100802 * fu_main_store_delay_cb:
803 **/
804static gboolean
805fu_main_store_delay_cb (gpointer user_data)
806{
807 AsApp *app;
808 GPtrArray *apps;
809 guint i;
810 FuMainPrivate *priv = (FuMainPrivate *) user_data;
811
812 g_debug ("devices now in store:");
813 apps = as_store_get_apps (priv->store);
814 for (i = 0; i < apps->len; i++) {
815 app = g_ptr_array_index (apps, i);
816 g_debug ("%i\t%s\t%s", i + 1,
817 as_app_get_id (app),
818 as_app_get_name (app, NULL));
819 }
820 priv->store_changed_id = 0;
821 return G_SOURCE_REMOVE;
822}
823
824/**
825 * fu_main_store_changed_cb:
826 **/
827static void
828fu_main_store_changed_cb (AsStore *store, FuMainPrivate *priv)
829{
830 if (priv->store_changed_id != 0)
831 return;
832 priv->store_changed_id = g_timeout_add (200, fu_main_store_delay_cb, priv);
833}
834
835/**
Richard Hughes7708a0f2015-07-21 08:41:22 +0100836 * fu_main_get_updates:
837 **/
838static GPtrArray *
839fu_main_get_updates (FuMainPrivate *priv, GError **error)
840{
841 AsApp *app;
842 AsRelease *rel;
843 FuDeviceItem *item;
844 GPtrArray *updates;
845 guint i;
846 const gchar *tmp;
847
848 /* find any updates using the AppStream metadata */
849 updates = g_ptr_array_new ();
850 for (i = 0; i < priv->devices->len; i++) {
851 const gchar *version;
Richard Hughes9985a242015-08-03 13:22:31 +0100852 AsChecksum *csum;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100853
854 item = g_ptr_array_index (priv->devices, i);
855
856 /* get device version */
857 version = fu_device_get_metadata (item->device, FU_DEVICE_KEY_VERSION);
858 if (version == NULL)
859 continue;
860
861 /* match the GUID in the XML */
Richard Hughesba145822015-08-11 14:34:08 +0100862 app = as_store_get_app_by_provide (priv->store,
863 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
864 fu_device_get_guid (item->device));
Richard Hughes7708a0f2015-07-21 08:41:22 +0100865 if (app == NULL)
866 continue;
867
868 /* get latest release */
869 rel = as_app_get_release_default (app);
870 if (rel == NULL) {
871 g_debug ("%s has no firmware update metadata",
872 fu_device_get_id (item->device));
873 continue;
874 }
875
876 /* check if actually newer than what we have installed */
877 if (as_utils_vercmp (as_release_get_version (rel), version) <= 0) {
878 g_debug ("%s has no firmware updates",
879 fu_device_get_id (item->device));
880 continue;
881 }
882
Richard Hughes82856d92015-09-08 14:30:38 +0100883 /* add application metadata */
Richard Hughesa2b2b1c2015-11-25 15:00:51 +0000884 fu_device_set_metadata (item->device,
885 FU_DEVICE_KEY_APPSTREAM_ID,
886 as_app_get_id (app));
Richard Hughes82856d92015-09-08 14:30:38 +0100887 tmp = as_app_get_developer_name (app, NULL);
888 if (tmp != NULL) {
889 fu_device_set_metadata (item->device,
890 FU_DEVICE_KEY_VENDOR, tmp);
891 }
892 tmp = as_app_get_name (app, NULL);
893 if (tmp != NULL) {
894 fu_device_set_metadata (item->device,
895 FU_DEVICE_KEY_NAME, tmp);
896 }
897 tmp = as_app_get_comment (app, NULL);
898 if (tmp != NULL) {
899 fu_device_set_metadata (item->device,
900 FU_DEVICE_KEY_SUMMARY, tmp);
901 }
902 tmp = as_app_get_description (app, NULL);
903 if (tmp != NULL) {
904 fu_device_set_metadata (item->device,
905 FU_DEVICE_KEY_DESCRIPTION, tmp);
906 }
907 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
908 if (tmp != NULL) {
909 fu_device_set_metadata (item->device,
910 FU_DEVICE_KEY_URL_HOMEPAGE, tmp);
911 }
912 tmp = as_app_get_project_license (app);
913 if (tmp != NULL) {
914 fu_device_set_metadata (item->device,
915 FU_DEVICE_KEY_LICENSE, tmp);
916 }
917
918 /* add release information */
Richard Hughes7708a0f2015-07-21 08:41:22 +0100919 tmp = as_release_get_version (rel);
920 if (tmp != NULL) {
921 fu_device_set_metadata (item->device,
922 FU_DEVICE_KEY_UPDATE_VERSION, tmp);
923 }
Richard Hughes9985a242015-08-03 13:22:31 +0100924 csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTAINER);
925 if (csum != NULL) {
926 fu_device_set_metadata (item->device,
927 FU_DEVICE_KEY_UPDATE_HASH,
928 as_checksum_get_value (csum));
929 }
Richard Hughes7708a0f2015-07-21 08:41:22 +0100930 tmp = as_release_get_location_default (rel);
931 if (tmp != NULL) {
932 fu_device_set_metadata (item->device,
933 FU_DEVICE_KEY_UPDATE_URI, tmp);
934 }
935 tmp = as_release_get_description (rel, NULL);
936 if (tmp != NULL) {
Richard Hughesfe8b96e2015-07-28 12:18:16 +0100937 fu_device_set_metadata (item->device,
938 FU_DEVICE_KEY_UPDATE_DESCRIPTION,
939 tmp);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100940 }
941 g_ptr_array_add (updates, item);
942 }
943
944 return updates;
945}
946
947/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000948 * fu_main_daemon_method_call:
949 **/
950static void
951fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
952 const gchar *object_path, const gchar *interface_name,
953 const gchar *method_name, GVariant *parameters,
954 GDBusMethodInvocation *invocation, gpointer user_data)
955{
956 FuMainPrivate *priv = (FuMainPrivate *) user_data;
957 GVariant *val;
958
959 /* return 'as' */
960 if (g_strcmp0 (method_name, "GetDevices") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +0100961 g_autoptr(GError) error = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +0000962 g_debug ("Called %s()", method_name);
Richard Hughes7708a0f2015-07-21 08:41:22 +0100963 val = fu_main_device_array_to_variant (priv->devices, &error);
964 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +0100965 if (g_error_matches (error,
966 FWUPD_ERROR,
967 FWUPD_ERROR_NOTHING_TO_DO)) {
968 g_prefix_error (&error, "No detected devices: ");
969 }
Richard Hughes7708a0f2015-07-21 08:41:22 +0100970 g_dbus_method_invocation_return_gerror (invocation, error);
971 return;
972 }
973 g_dbus_method_invocation_return_value (invocation, val);
974 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
975 return;
976 }
977
978 /* return 'as' */
979 if (g_strcmp0 (method_name, "GetUpdates") == 0) {
Richard Hughes46832432015-09-11 13:43:15 +0100980 g_autoptr(GError) error = NULL;
981 g_autoptr(GPtrArray) updates = NULL;
Richard Hughes7708a0f2015-07-21 08:41:22 +0100982 g_debug ("Called %s()", method_name);
983 updates = fu_main_get_updates (priv, &error);
984 if (updates == NULL) {
985 g_dbus_method_invocation_return_gerror (invocation, error);
986 return;
987 }
988 val = fu_main_device_array_to_variant (updates, &error);
Richard Hughes9a38c032015-03-17 20:58:46 +0000989 if (val == NULL) {
Richard Hughes9d76a872015-09-17 12:49:07 +0100990 if (g_error_matches (error,
991 FWUPD_ERROR,
992 FWUPD_ERROR_NOTHING_TO_DO)) {
993 g_prefix_error (&error, "No devices can be updated: ");
994 }
Richard Hughes9a38c032015-03-17 20:58:46 +0000995 g_dbus_method_invocation_return_gerror (invocation, error);
996 return;
997 }
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000998 g_dbus_method_invocation_return_value (invocation, val);
Richard Hughesf910ac92015-03-19 10:43:42 +0000999 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001000 return;
1001 }
1002
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001003 /* return '' */
Richard Hughes0e883ee2015-03-18 17:22:33 +00001004 if (g_strcmp0 (method_name, "ClearResults") == 0) {
1005 FuDeviceItem *item = NULL;
1006 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001007 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001008
1009 g_variant_get (parameters, "(&s)", &id);
1010 g_debug ("Called %s(%s)", method_name, id);
1011
1012 /* find device */
1013 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1014 if (item == NULL) {
1015 g_dbus_method_invocation_return_gerror (invocation, error);
1016 return;
1017 }
1018
1019 /* call into the provider */
1020 if (!fu_provider_clear_results (item->provider, item->device, &error)) {
1021 g_dbus_method_invocation_return_gerror (invocation, error);
1022 return;
1023 }
1024
1025 /* success */
1026 g_dbus_method_invocation_return_value (invocation, NULL);
1027 return;
1028 }
1029
1030 /* return 'a{sv}' */
1031 if (g_strcmp0 (method_name, "GetResults") == 0) {
1032 FuDeviceItem *item = NULL;
1033 const gchar *id = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001034 g_autoptr(GError) error = NULL;
Richard Hughes0e883ee2015-03-18 17:22:33 +00001035
1036 g_variant_get (parameters, "(&s)", &id);
1037 g_debug ("Called %s(%s)", method_name, id);
1038
1039 /* find device */
1040 item = fu_main_get_item_by_id_fallback_pending (priv, id, &error);
1041 if (item == NULL) {
1042 g_dbus_method_invocation_return_gerror (invocation, error);
1043 return;
1044 }
1045
1046 /* call into the provider */
1047 if (!fu_provider_get_results (item->provider, item->device, &error)) {
1048 g_dbus_method_invocation_return_gerror (invocation, error);
1049 return;
1050 }
1051
1052 /* success */
1053 val = fu_device_get_metadata_as_variant (item->device);
1054 g_dbus_method_invocation_return_value (invocation, val);
1055 return;
1056 }
1057
1058 /* return '' */
Richard Hughesae0efdc2015-06-24 16:18:29 +01001059 if (g_strcmp0 (method_name, "UpdateMetadata") == 0) {
1060 GDBusMessage *message;
1061 GUnixFDList *fd_list;
1062 gint fd_data;
1063 gint fd_sig;
Richard Hughes46832432015-09-11 13:43:15 +01001064 g_autoptr(GError) error = NULL;
Richard Hughesae0efdc2015-06-24 16:18:29 +01001065
1066 message = g_dbus_method_invocation_get_message (invocation);
1067 fd_list = g_dbus_message_get_unix_fd_list (message);
1068 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) {
1069 g_dbus_method_invocation_return_error (invocation,
1070 FWUPD_ERROR,
1071 FWUPD_ERROR_INTERNAL,
1072 "invalid handle");
1073 return;
1074 }
1075 fd_data = g_unix_fd_list_get (fd_list, 0, &error);
1076 if (fd_data < 0) {
1077 g_dbus_method_invocation_return_gerror (invocation, error);
1078 return;
1079 }
1080 fd_sig = g_unix_fd_list_get (fd_list, 1, &error);
1081 if (fd_sig < 0) {
1082 g_dbus_method_invocation_return_gerror (invocation, error);
1083 return;
1084 }
Mario Limonciello3ed54472015-07-23 13:19:39 -05001085 if (!fu_main_daemon_update_metadata (priv, fd_data, fd_sig, &error)) {
Richard Hughesae0efdc2015-06-24 16:18:29 +01001086 g_prefix_error (&error, "failed to update metadata: ");
1087 g_dbus_method_invocation_return_gerror (invocation, error);
1088 return;
1089 }
1090 g_dbus_method_invocation_return_value (invocation, NULL);
1091 return;
1092 }
1093
Richard Hughesa043c2e2015-06-29 08:43:18 +01001094 /* return 's' */
1095 if (g_strcmp0 (method_name, "Verify") == 0) {
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001096 AsApp *app;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001097 AsChecksum *csum;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001098 AsRelease *release;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001099 FuDeviceItem *item = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001100 const gchar *hash = NULL;
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001101 const gchar *id = NULL;
1102 const gchar *version = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001103 g_autoptr(GError) error = NULL;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001104
1105 /* check the id exists */
1106 g_variant_get (parameters, "(&s)", &id);
1107 g_debug ("Called %s(%s)", method_name, id);
1108 item = fu_main_get_item_by_id (priv, id);
1109 if (item == NULL) {
1110 g_dbus_method_invocation_return_error (invocation,
1111 FWUPD_ERROR,
1112 FWUPD_ERROR_NOT_FOUND,
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001113 "No such device %s",
Richard Hughesa043c2e2015-06-29 08:43:18 +01001114 id);
1115 return;
1116 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001117
1118 /* set the device firmware hash */
Richard Hughesa043c2e2015-06-29 08:43:18 +01001119 if (!fu_provider_verify (item->provider, item->device,
1120 FU_PROVIDER_VERIFY_FLAG_NONE, &error)) {
1121 g_dbus_method_invocation_return_gerror (invocation, error);
1122 return;
1123 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001124
1125 /* find component in metadata */
Richard Hughes709d0112015-10-09 12:17:08 +01001126 app = as_store_get_app_by_provide (priv->store,
1127 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
1128 fu_device_get_guid (item->device));
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001129 if (app == NULL) {
1130 g_dbus_method_invocation_return_error (invocation,
1131 FWUPD_ERROR,
1132 FWUPD_ERROR_NOT_SUPPORTED,
1133 "No metadata");
1134 return;
1135 }
1136
1137 /* find version in metadata */
1138 version = fu_device_get_metadata (item->device, FU_DEVICE_KEY_VERSION);
1139 release = as_app_get_release (app, version);
1140 if (release == NULL) {
1141 g_dbus_method_invocation_return_error (invocation,
1142 FWUPD_ERROR,
1143 FWUPD_ERROR_NOT_SUPPORTED,
1144 "No version %s",
1145 version);
1146 return;
1147 }
1148
1149 /* find checksum */
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001150 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
1151 if (csum == NULL) {
1152 g_dbus_method_invocation_return_error (invocation,
1153 FWUPD_ERROR,
1154 FWUPD_ERROR_NOT_SUPPORTED,
1155 "No content checksum for %s",
1156 version);
1157 return;
1158 }
Richard Hughesa043c2e2015-06-29 08:43:18 +01001159 hash = fu_device_get_metadata (item->device, FU_DEVICE_KEY_FIRMWARE_HASH);
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001160 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
1161 g_dbus_method_invocation_return_error (invocation,
1162 FWUPD_ERROR,
1163 FWUPD_ERROR_NOT_SUPPORTED,
1164 "For v%s expected %s, got %s",
1165 version,
1166 as_checksum_get_value (csum),
1167 hash);
1168 return;
1169 }
Richard Hughesc6ff8fa2015-08-03 18:00:51 +01001170 g_dbus_method_invocation_return_value (invocation, NULL);
Richard Hughesa043c2e2015-06-29 08:43:18 +01001171 return;
1172 }
1173
Richard Hughesae0efdc2015-06-24 16:18:29 +01001174 /* return '' */
Richard Hughes63a407a2015-07-22 08:54:14 +01001175 if (g_strcmp0 (method_name, "Install") == 0) {
Richard Hughesd079b1a2015-03-06 10:09:55 +00001176 FuDeviceItem *item = NULL;
Richard Hughesf508e762015-02-27 12:49:36 +00001177 FuMainAuthHelper *helper;
Richard Hughes74cc2172015-02-27 13:19:46 +00001178 FuProviderFlags flags = FU_PROVIDER_UPDATE_FLAG_NONE;
1179 GDBusMessage *message;
1180 GUnixFDList *fd_list;
1181 GVariant *prop_value;
Richard Hughesa8e83942015-03-09 17:19:35 +00001182 const gchar *action_id;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001183 const gchar *id = NULL;
Richard Hughes74cc2172015-02-27 13:19:46 +00001184 gchar *prop_key;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001185 gint32 fd_handle = 0;
1186 gint fd;
Richard Hughes46832432015-09-11 13:43:15 +01001187 g_autoptr(GError) error = NULL;
Richard Hughes60f48c22015-10-08 20:25:51 +01001188 g_autoptr(PolkitSubject) subject = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001189 g_autoptr(GVariantIter) iter = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001190 g_autoptr(GBytes) blob_cab = NULL;
1191 g_autoptr(GInputStream) stream = NULL;
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001192
1193 /* check the id exists */
Richard Hughes74cc2172015-02-27 13:19:46 +00001194 g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter);
Richard Hughesf508e762015-02-27 12:49:36 +00001195 g_debug ("Called %s(%s,%i)", method_name, id, fd_handle);
Richard Hughesd079b1a2015-03-06 10:09:55 +00001196 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1197 item = fu_main_get_item_by_id (priv, id);
1198 if (item == NULL) {
1199 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001200 FWUPD_ERROR,
1201 FWUPD_ERROR_NOT_FOUND,
Richard Hughesd079b1a2015-03-06 10:09:55 +00001202 "no such device %s",
1203 id);
1204 return;
1205 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001206 }
1207
Richard Hughes74cc2172015-02-27 13:19:46 +00001208 /* get options */
1209 while (g_variant_iter_next (iter, "{&sv}",
1210 &prop_key, &prop_value)) {
1211 g_debug ("got option %s", prop_key);
1212 if (g_strcmp0 (prop_key, "offline") == 0 &&
1213 g_variant_get_boolean (prop_value) == TRUE)
1214 flags |= FU_PROVIDER_UPDATE_FLAG_OFFLINE;
Richard Hughese7c12642015-03-04 20:28:59 +00001215 if (g_strcmp0 (prop_key, "allow-older") == 0 &&
1216 g_variant_get_boolean (prop_value) == TRUE)
1217 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_OLDER;
1218 if (g_strcmp0 (prop_key, "allow-reinstall") == 0 &&
1219 g_variant_get_boolean (prop_value) == TRUE)
1220 flags |= FU_PROVIDER_UPDATE_FLAG_ALLOW_REINSTALL;
Richard Hughes1ffde6c2015-03-02 22:44:48 +00001221 g_variant_unref (prop_value);
Richard Hughes74cc2172015-02-27 13:19:46 +00001222 }
1223
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001224 /* get the fd */
1225 message = g_dbus_method_invocation_get_message (invocation);
1226 fd_list = g_dbus_message_get_unix_fd_list (message);
1227 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1228 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001229 FWUPD_ERROR,
1230 FWUPD_ERROR_INTERNAL,
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001231 "invalid handle");
1232 return;
1233 }
1234 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1235 if (fd < 0) {
1236 g_dbus_method_invocation_return_gerror (invocation,
1237 error);
1238 return;
1239 }
1240
Richard Hughes5d14def2015-10-07 17:43:10 +01001241 /* read the entire fd to a data blob */
1242 stream = g_unix_input_stream_new (fd, TRUE);
1243 blob_cab = g_input_stream_read_bytes (stream,
1244 FU_MAIN_FIRMWARE_SIZE_MAX,
1245 NULL, &error);
1246 if (blob_cab == NULL){
1247 g_dbus_method_invocation_return_gerror (invocation,
1248 error);
1249 return;
1250 }
1251
Richard Hughes67ec8982015-03-03 11:39:27 +00001252 /* process the firmware */
Richard Hughesf508e762015-02-27 12:49:36 +00001253 helper = g_new0 (FuMainAuthHelper, 1);
1254 helper->invocation = g_object_ref (invocation);
Richard Hughes5d14def2015-10-07 17:43:10 +01001255 helper->trust_flags = FWUPD_TRUST_FLAG_NONE;
1256 helper->blob_cab = g_bytes_ref (blob_cab);
Richard Hughes74cc2172015-02-27 13:19:46 +00001257 helper->flags = flags;
Richard Hughesf508e762015-02-27 12:49:36 +00001258 helper->priv = priv;
Richard Hughes5d14def2015-10-07 17:43:10 +01001259 helper->store = as_store_new ();
Richard Hughesd079b1a2015-03-06 10:09:55 +00001260 if (item != NULL)
1261 helper->device = g_object_ref (item->device);
Richard Hughes67ec8982015-03-03 11:39:27 +00001262 if (!fu_main_update_helper (helper, &error)) {
1263 g_dbus_method_invocation_return_gerror (helper->invocation,
1264 error);
Richard Hughesf910ac92015-03-19 10:43:42 +00001265 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes67ec8982015-03-03 11:39:27 +00001266 fu_main_helper_free (helper);
1267 return;
1268 }
1269
Richard Hughes18423292015-03-09 17:10:50 +00001270 /* is root */
1271 if (fu_main_dbus_get_uid (priv, sender) == 0) {
1272 if (!fu_main_provider_update_authenticated (helper, &error)) {
1273 g_dbus_method_invocation_return_gerror (invocation, error);
1274 } else {
1275 g_dbus_method_invocation_return_value (invocation, NULL);
1276 }
Richard Hughesf910ac92015-03-19 10:43:42 +00001277 fu_main_set_status (priv, FWUPD_STATUS_IDLE);
Richard Hughes18423292015-03-09 17:10:50 +00001278 fu_main_helper_free (helper);
1279 return;
1280 }
1281
Richard Hughes67ec8982015-03-03 11:39:27 +00001282 /* authenticate */
Richard Hughes63bbbf52015-04-14 16:12:16 +01001283 action_id = fu_main_get_action_id_for_device (helper);
Richard Hughesf508e762015-02-27 12:49:36 +00001284 subject = polkit_system_bus_name_new (sender);
Richard Hughes67ec8982015-03-03 11:39:27 +00001285 polkit_authority_check_authorization (helper->priv->authority, subject,
Richard Hughesa8e83942015-03-09 17:19:35 +00001286 action_id,
Richard Hughesf508e762015-02-27 12:49:36 +00001287 NULL,
1288 POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
1289 NULL,
1290 fu_main_check_authorization_cb,
1291 helper);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001292 return;
1293 }
1294
Richard Hughescccc7752015-03-06 11:13:19 +00001295 /* return 'a{sv}' */
1296 if (g_strcmp0 (method_name, "GetDetails") == 0) {
Richard Hughes5d14def2015-10-07 17:43:10 +01001297 AsApp *app = NULL;
1298 AsRelease *rel;
Richard Hughescccc7752015-03-06 11:13:19 +00001299 GDBusMessage *message;
Richard Hughes5d14def2015-10-07 17:43:10 +01001300 GPtrArray *apps;
1301 GPtrArray *provides;
Richard Hughescccc7752015-03-06 11:13:19 +00001302 GUnixFDList *fd_list;
1303 GVariantBuilder builder;
Richard Hughes5d14def2015-10-07 17:43:10 +01001304 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
Richard Hughes3bf94802015-03-10 15:57:30 +00001305 const gchar *tmp;
Richard Hughes5d14def2015-10-07 17:43:10 +01001306 const gchar *guid = NULL;
Richard Hughescccc7752015-03-06 11:13:19 +00001307 gint32 fd_handle = 0;
Richard Hughes5d14def2015-10-07 17:43:10 +01001308 guint i;
Richard Hughescccc7752015-03-06 11:13:19 +00001309 gint fd;
Richard Hughes5d14def2015-10-07 17:43:10 +01001310 g_autoptr(AsStore) store = NULL;
1311 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes46832432015-09-11 13:43:15 +01001312 g_autoptr(GError) error = NULL;
Richard Hughes5d14def2015-10-07 17:43:10 +01001313 g_autoptr(GInputStream) stream = NULL;
Richard Hughescccc7752015-03-06 11:13:19 +00001314
1315 /* check the id exists */
1316 g_variant_get (parameters, "(h)", &fd_handle);
1317 g_debug ("Called %s(%i)", method_name, fd_handle);
1318
1319 /* get the fd */
1320 message = g_dbus_method_invocation_get_message (invocation);
1321 fd_list = g_dbus_message_get_unix_fd_list (message);
1322 if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 1) {
1323 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001324 FWUPD_ERROR,
1325 FWUPD_ERROR_INTERNAL,
Richard Hughescccc7752015-03-06 11:13:19 +00001326 "invalid handle");
1327 return;
1328 }
1329 fd = g_unix_fd_list_get (fd_list, fd_handle, &error);
1330 if (fd < 0) {
1331 g_dbus_method_invocation_return_gerror (invocation,
1332 error);
1333 return;
1334 }
1335
Richard Hughes5d14def2015-10-07 17:43:10 +01001336 /* read the entire fd to a data blob */
1337 stream = g_unix_input_stream_new (fd, TRUE);
1338 blob_cab = g_input_stream_read_bytes (stream,
1339 FU_MAIN_FIRMWARE_SIZE_MAX,
1340 NULL, &error);
1341 if (blob_cab == NULL){
1342 g_dbus_method_invocation_return_gerror (invocation,
1343 error);
Richard Hughescccc7752015-03-06 11:13:19 +00001344 return;
1345 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001346
1347 /* load file */
1348 store = as_store_new ();
1349 if (!as_store_from_bytes (store, blob_cab, NULL, &error)) {
Richard Hughescccc7752015-03-06 11:13:19 +00001350 g_dbus_method_invocation_return_gerror (invocation, error);
1351 return;
1352 }
1353
Richard Hughes5d14def2015-10-07 17:43:10 +01001354 /* get default app */
1355 apps = as_store_get_apps (store);
1356 if (apps->len == 0) {
1357 g_dbus_method_invocation_return_error (invocation,
1358 FWUPD_ERROR,
1359 FWUPD_ERROR_INVALID_FILE,
1360 "no components");
1361 return;
1362 }
1363 if (apps->len > 1) {
1364 /* we've got a .cab file with multiple components,
1365 * so try to find the first thing that's installed */
1366 for (i = 0; i < priv->devices->len; i++) {
1367 FuDeviceItem *item;
1368 item = g_ptr_array_index (priv->devices, i);
1369 app = as_store_get_app_by_provide (store,
1370 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
1371 fu_device_get_guid (item->device));
1372 if (app != NULL)
1373 break;
1374 }
1375 }
1376
1377 /* well, we've tried our best, just show the first entry */
1378 if (app == NULL)
1379 app = AS_APP (g_ptr_array_index (apps, 0));
1380
1381 /* get guid */
1382 provides = as_app_get_provides (app);
1383 for (i = 0; i < provides->len; i++) {
1384 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1385 if (as_provide_get_kind (prov) == AS_PROVIDE_KIND_FIRMWARE_FLASHED) {
1386 guid = as_provide_get_value (prov);
1387 break;
1388 }
1389 }
1390 if (guid == NULL) {
1391 g_dbus_method_invocation_return_error (invocation,
1392 FWUPD_ERROR,
1393 FWUPD_ERROR_INTERNAL,
1394 "component has no GUID");
1395 return;
1396 }
1397
1398 /* verify trust */
1399 rel = as_app_get_release_default (app);
1400 if (!fu_main_get_release_trust_flags (rel, &trust_flags, &error)) {
Richard Hughesd8fa0492015-04-14 15:56:56 +01001401 g_dbus_method_invocation_return_gerror (invocation, error);
1402 return;
1403 }
1404
Richard Hughescccc7752015-03-06 11:13:19 +00001405 /* create an array with all the metadata in */
1406 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
1407 g_variant_builder_add (&builder, "{sv}",
1408 FU_DEVICE_KEY_VERSION,
Richard Hughes5d14def2015-10-07 17:43:10 +01001409 g_variant_new_string (as_release_get_version (rel)));
Richard Hughescccc7752015-03-06 11:13:19 +00001410 g_variant_builder_add (&builder, "{sv}",
1411 FU_DEVICE_KEY_GUID,
Richard Hughes5d14def2015-10-07 17:43:10 +01001412 g_variant_new_string (guid));
Richard Hughesbbac6d72015-03-11 22:09:44 +00001413 g_variant_builder_add (&builder, "{sv}",
1414 FU_DEVICE_KEY_SIZE,
Richard Hughes5d14def2015-10-07 17:43:10 +01001415 g_variant_new_uint64 (as_release_get_size (rel, AS_SIZE_KIND_INSTALLED)));
Richard Hughes3bf94802015-03-10 15:57:30 +00001416
1417 /* optional properties */
Richard Hughes5d14def2015-10-07 17:43:10 +01001418 tmp = as_app_get_developer_name (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001419 if (tmp != NULL) {
1420 g_variant_builder_add (&builder, "{sv}",
1421 FU_DEVICE_KEY_VENDOR,
1422 g_variant_new_string (tmp));
1423 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001424 tmp = as_app_get_name (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001425 if (tmp != NULL) {
1426 g_variant_builder_add (&builder, "{sv}",
1427 FU_DEVICE_KEY_NAME,
1428 g_variant_new_string (tmp));
1429 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001430 tmp = as_app_get_comment (app, NULL);
Richard Hughes3bf94802015-03-10 15:57:30 +00001431 if (tmp != NULL) {
1432 g_variant_builder_add (&builder, "{sv}",
1433 FU_DEVICE_KEY_SUMMARY,
1434 g_variant_new_string (tmp));
1435 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001436 tmp = as_app_get_description (app, NULL);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001437 if (tmp != NULL) {
1438 g_variant_builder_add (&builder, "{sv}",
1439 FU_DEVICE_KEY_DESCRIPTION,
1440 g_variant_new_string (tmp));
1441 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001442 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001443 if (tmp != NULL) {
1444 g_variant_builder_add (&builder, "{sv}",
1445 FU_DEVICE_KEY_URL_HOMEPAGE,
1446 g_variant_new_string (tmp));
1447 }
Richard Hughes5d14def2015-10-07 17:43:10 +01001448 tmp = as_app_get_project_license (app);
Richard Hughesbbac6d72015-03-11 22:09:44 +00001449 if (tmp != NULL) {
1450 g_variant_builder_add (&builder, "{sv}",
1451 FU_DEVICE_KEY_LICENSE,
1452 g_variant_new_string (tmp));
1453 }
Richard Hughesd4494472015-10-08 20:10:44 +01001454 tmp = as_release_get_description (rel, NULL);
1455 if (tmp != NULL) {
1456 g_variant_builder_add (&builder, "{sv}",
1457 FU_DEVICE_KEY_UPDATE_DESCRIPTION,
1458 g_variant_new_string (tmp));
1459 }
Richard Hughesd8fa0492015-04-14 15:56:56 +01001460 g_variant_builder_add (&builder, "{sv}",
1461 FU_DEVICE_KEY_TRUSTED,
1462 g_variant_new_uint64 (trust_flags));
Richard Hughescccc7752015-03-06 11:13:19 +00001463
1464 /* return whole array */
1465 val = g_variant_new ("(a{sv})", &builder);
1466 g_dbus_method_invocation_return_value (invocation, val);
1467 return;
1468 }
1469
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001470 /* we suck */
1471 g_dbus_method_invocation_return_error (invocation,
Richard Hughes8645ec92015-03-19 10:14:32 +00001472 G_DBUS_ERROR,
1473 G_DBUS_ERROR_UNKNOWN_METHOD,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001474 "no such method %s",
1475 method_name);
1476}
1477
1478/**
1479 * fu_main_daemon_get_property:
1480 **/
1481static GVariant *
1482fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
1483 const gchar *object_path, const gchar *interface_name,
1484 const gchar *property_name, GError **error,
1485 gpointer user_data)
1486{
Richard Hughes773ce982015-03-09 22:40:57 +00001487 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1488
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001489 if (g_strcmp0 (property_name, "DaemonVersion") == 0)
1490 return g_variant_new_string (VERSION);
1491
Richard Hughes773ce982015-03-09 22:40:57 +00001492 if (g_strcmp0 (property_name, "Status") == 0)
Richard Hughesf910ac92015-03-19 10:43:42 +00001493 return g_variant_new_string (fwupd_status_to_string (priv->status));
Richard Hughes773ce982015-03-09 22:40:57 +00001494
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001495 /* return an error */
1496 g_set_error (error,
Richard Hughes8645ec92015-03-19 10:14:32 +00001497 G_DBUS_ERROR,
1498 G_DBUS_ERROR_UNKNOWN_PROPERTY,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001499 "failed to get daemon property %s",
1500 property_name);
1501 return NULL;
1502}
1503
1504/**
Richard Hughesfd468842015-04-22 16:44:08 +01001505 * fu_main_providers_coldplug:
1506 **/
1507static void
1508fu_main_providers_coldplug (FuMainPrivate *priv)
1509{
1510 FuProvider *provider;
1511 guint i;
Richard Hughes3f236502015-09-24 15:43:02 +01001512 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01001513
Richard Hughes3f236502015-09-24 15:43:02 +01001514 ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug");
Richard Hughesfd468842015-04-22 16:44:08 +01001515 for (i = 0; i < priv->providers->len; i++) {
Richard Hughes46832432015-09-11 13:43:15 +01001516 g_autoptr(GError) error = NULL;
Richard Hughes3f236502015-09-24 15:43:02 +01001517 g_autoptr(AsProfileTask) ptask2 = NULL;
Richard Hughesfd468842015-04-22 16:44:08 +01001518 provider = g_ptr_array_index (priv->providers, i);
Richard Hughes3f236502015-09-24 15:43:02 +01001519 ptask2 = as_profile_start (priv->profile,
1520 "FuMain:coldplug{%s}",
1521 fu_provider_get_name (provider));
Richard Hughesfd468842015-04-22 16:44:08 +01001522 if (!fu_provider_coldplug (FU_PROVIDER (provider), &error))
1523 g_warning ("Failed to coldplug: %s", error->message);
1524 }
1525}
1526
1527/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001528 * fu_main_on_bus_acquired_cb:
1529 **/
1530static void
1531fu_main_on_bus_acquired_cb (GDBusConnection *connection,
1532 const gchar *name,
1533 gpointer user_data)
1534{
1535 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1536 guint registration_id;
Richard Hughes46832432015-09-11 13:43:15 +01001537 g_autoptr(GError) error = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001538 static const GDBusInterfaceVTable interface_vtable = {
1539 fu_main_daemon_method_call,
1540 fu_main_daemon_get_property,
1541 NULL
1542 };
1543
1544 priv->connection = g_object_ref (connection);
1545 registration_id = g_dbus_connection_register_object (connection,
1546 FWUPD_DBUS_PATH,
1547 priv->introspection_daemon->interfaces[0],
1548 &interface_vtable,
1549 priv, /* user_data */
1550 NULL, /* user_data_free_func */
1551 NULL); /* GError** */
1552 g_assert (registration_id > 0);
Richard Hughes18423292015-03-09 17:10:50 +00001553
Richard Hughesfd468842015-04-22 16:44:08 +01001554 /* add devices */
1555 fu_main_providers_coldplug (priv);
1556
Richard Hughes18423292015-03-09 17:10:50 +00001557 /* connect to D-Bus directly */
1558 priv->proxy_uid =
1559 g_dbus_proxy_new_sync (priv->connection,
1560 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1561 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1562 NULL,
1563 "org.freedesktop.DBus",
1564 "/org/freedesktop/DBus",
1565 "org.freedesktop.DBus",
1566 NULL,
1567 &error);
1568 if (priv->proxy_uid == NULL) {
1569 g_warning ("cannot connect to DBus: %s", error->message);
1570 return;
1571 }
Richard Hughes3f236502015-09-24 15:43:02 +01001572
1573 /* dump startup profile data */
Richard Hughes2a1e75d2015-12-18 17:42:53 +00001574 if (fu_debug_is_verbose ())
1575 as_profile_dump (priv->profile);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001576}
1577
1578/**
1579 * fu_main_on_name_acquired_cb:
1580 **/
1581static void
1582fu_main_on_name_acquired_cb (GDBusConnection *connection,
1583 const gchar *name,
1584 gpointer user_data)
1585{
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001586 g_debug ("FuMain: acquired name: %s", name);
1587}
1588
1589/**
1590 * fu_main_on_name_lost_cb:
1591 **/
1592static void
1593fu_main_on_name_lost_cb (GDBusConnection *connection,
1594 const gchar *name,
1595 gpointer user_data)
1596{
1597 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1598 g_debug ("FuMain: lost name: %s", name);
1599 g_main_loop_quit (priv->loop);
1600}
1601
1602/**
1603 * fu_main_timed_exit_cb:
1604 **/
1605static gboolean
1606fu_main_timed_exit_cb (gpointer user_data)
1607{
1608 GMainLoop *loop = (GMainLoop *) user_data;
1609 g_main_loop_quit (loop);
1610 return G_SOURCE_REMOVE;
1611}
1612
1613/**
1614 * fu_main_load_introspection:
1615 **/
1616static GDBusNodeInfo *
1617fu_main_load_introspection (const gchar *filename, GError **error)
1618{
Richard Hughes46832432015-09-11 13:43:15 +01001619 g_autoptr(GBytes) data = NULL;
1620 g_autofree gchar *path = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001621
1622 /* lookup data */
1623 path = g_build_filename ("/org/freedesktop/fwupd", filename, NULL);
1624 data = g_resource_lookup_data (fu_get_resource (),
1625 path,
1626 G_RESOURCE_LOOKUP_FLAGS_NONE,
1627 error);
1628 if (data == NULL)
1629 return NULL;
1630
1631 /* build introspection from XML */
1632 return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error);
1633}
1634
1635/**
1636 * cd_main_provider_device_added_cb:
1637 **/
1638static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001639cd_main_provider_device_added_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001640 FuDevice *device,
1641 gpointer user_data)
1642{
1643 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001644 FuDeviceItem *item;
1645
Richard Hughes0e883ee2015-03-18 17:22:33 +00001646 /* remove any fake device */
1647 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
Richard Hughes5d057a82015-11-24 18:09:57 +00001648 if (item != NULL) {
1649 g_debug ("already added %s by %s, ignoring same device from %s",
1650 fu_device_get_id (item->device),
1651 fu_device_get_metadata (item->device, FU_DEVICE_KEY_PROVIDER),
1652 fu_provider_get_name (provider));
1653 return;
1654 }
Richard Hughes0e883ee2015-03-18 17:22:33 +00001655
1656 /* create new device */
Richard Hughesf508e762015-02-27 12:49:36 +00001657 item = g_new0 (FuDeviceItem, 1);
1658 item->device = g_object_ref (device);
1659 item->provider = g_object_ref (provider);
1660 g_ptr_array_add (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001661 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001662}
1663
1664/**
1665 * cd_main_provider_device_removed_cb:
1666 **/
1667static void
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001668cd_main_provider_device_removed_cb (FuProvider *provider,
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001669 FuDevice *device,
1670 gpointer user_data)
1671{
1672 FuMainPrivate *priv = (FuMainPrivate *) user_data;
Richard Hughesf508e762015-02-27 12:49:36 +00001673 FuDeviceItem *item;
1674
1675 item = fu_main_get_item_by_id (priv, fu_device_get_id (device));
1676 if (item == NULL) {
Richard Hughes5d057a82015-11-24 18:09:57 +00001677 g_debug ("no device to remove %s", fu_device_get_id (device));
Richard Hughesf508e762015-02-27 12:49:36 +00001678 return;
1679 }
Richard Hughes5d057a82015-11-24 18:09:57 +00001680
1681 /* check this came from the same provider */
1682 if (g_strcmp0 (fu_provider_get_name (provider),
1683 fu_provider_get_name (item->provider)) != 0) {
1684 g_debug ("ignoring duplicate removal from %s",
1685 fu_provider_get_name (provider));
1686 return;
1687 }
1688
Richard Hughesf508e762015-02-27 12:49:36 +00001689 g_ptr_array_remove (priv->devices, item);
Richard Hughesd7022b52015-03-11 19:47:06 +00001690 fu_main_emit_changed (priv);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001691}
1692
1693/**
Richard Hughes773ce982015-03-09 22:40:57 +00001694 * cd_main_provider_status_changed_cb:
1695 **/
1696static void
1697cd_main_provider_status_changed_cb (FuProvider *provider,
Richard Hughesf910ac92015-03-19 10:43:42 +00001698 FwupdStatus status,
Richard Hughes773ce982015-03-09 22:40:57 +00001699 gpointer user_data)
1700{
1701 FuMainPrivate *priv = (FuMainPrivate *) user_data;
1702 fu_main_set_status (priv, status);
1703}
1704
1705/**
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001706 * fu_main_add_provider:
1707 **/
1708static void
1709fu_main_add_provider (FuMainPrivate *priv, FuProvider *provider)
1710{
1711 g_signal_connect (provider, "device-added",
1712 G_CALLBACK (cd_main_provider_device_added_cb),
1713 priv);
1714 g_signal_connect (provider, "device-removed",
1715 G_CALLBACK (cd_main_provider_device_removed_cb),
1716 priv);
Richard Hughes773ce982015-03-09 22:40:57 +00001717 g_signal_connect (provider, "status-changed",
1718 G_CALLBACK (cd_main_provider_status_changed_cb),
1719 priv);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001720 g_ptr_array_add (priv->providers, provider);
1721}
1722
1723/**
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001724 * main:
1725 **/
1726int
1727main (int argc, char *argv[])
1728{
1729 FuMainPrivate *priv = NULL;
1730 gboolean immediate_exit = FALSE;
1731 gboolean ret;
1732 gboolean timed_exit = FALSE;
1733 GOptionContext *context;
1734 guint owner_id = 0;
1735 guint retval = 1;
1736 const GOptionEntry options[] = {
1737 { "timed-exit", '\0', 0, G_OPTION_ARG_NONE, &timed_exit,
1738 /* TRANSLATORS: exit after we've started up, used for user profiling */
1739 _("Exit after a small delay"), NULL },
1740 { "immediate-exit", '\0', 0, G_OPTION_ARG_NONE, &immediate_exit,
1741 /* TRANSLATORS: exit straight away, used for automatic profiling */
1742 _("Exit after the engine has loaded"), NULL },
1743 { NULL}
1744 };
Richard Hughes46832432015-09-11 13:43:15 +01001745 g_autoptr(GError) error = NULL;
1746 g_autofree gchar *config_file = NULL;
1747 g_autoptr(GKeyFile) config = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001748
1749 setlocale (LC_ALL, "");
1750
1751 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1752 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1753 textdomain (GETTEXT_PACKAGE);
1754
1755 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01001756 g_set_application_name (_("Firmware Update Daemon"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001757 context = g_option_context_new (NULL);
1758 g_option_context_add_main_entries (context, options, NULL);
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001759 g_option_context_add_group (context, fu_debug_get_option_group ());
Richard Hughes8ded6ca2015-03-16 12:51:36 +00001760 /* TRANSLATORS: program summary */
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001761 g_option_context_set_summary (context, _("Firmware Update D-Bus Service"));
1762 ret = g_option_context_parse (context, &argc, &argv, &error);
1763 if (!ret) {
1764 g_warning ("FuMain: failed to parse command line arguments: %s",
1765 error->message);
1766 goto out;
1767 }
1768
1769 /* create new objects */
1770 priv = g_new0 (FuMainPrivate, 1);
Richard Hughesf910ac92015-03-19 10:43:42 +00001771 priv->status = FWUPD_STATUS_IDLE;
Richard Hughesf508e762015-02-27 12:49:36 +00001772 priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001773 priv->loop = g_main_loop_new (NULL, FALSE);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001774 priv->pending = fu_pending_new ();
Richard Hughes7708a0f2015-07-21 08:41:22 +01001775 priv->store = as_store_new ();
Richard Hughes3f236502015-09-24 15:43:02 +01001776 priv->profile = as_profile_new ();
Richard Hughes033ccba2015-09-10 14:51:28 +01001777 g_signal_connect (priv->store, "changed",
1778 G_CALLBACK (fu_main_store_changed_cb), priv);
1779 as_store_set_watch_flags (priv->store, AS_STORE_WATCH_FLAG_ADDED |
1780 AS_STORE_WATCH_FLAG_REMOVED);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001781
1782 /* load AppStream */
1783 as_store_add_filter (priv->store, AS_ID_KIND_FIRMWARE);
1784 if (!as_store_load (priv->store,
1785 AS_STORE_LOAD_FLAG_APP_INFO_SYSTEM,
1786 NULL, &error)){
1787 g_warning ("FuMain: failed to load AppStream data: %s",
1788 error->message);
1789 return FALSE;
1790 }
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001791
Richard Hughes804c0752015-08-04 14:53:52 +01001792 /* read config file */
1793 config = g_key_file_new ();
1794 config_file = g_build_filename (SYSCONFDIR, "fwupd.conf", NULL);
1795 g_debug ("Loading fallback values from %s", config_file);
1796 if (!g_key_file_load_from_file (config, config_file,
1797 G_KEY_FILE_NONE, &error)) {
1798 g_print ("failed to load config file %s: %s\n",
1799 config_file, error->message);
1800 retval = EXIT_FAILURE;
1801 goto out;
1802 }
1803
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001804 /* add providers */
1805 priv->providers = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes804c0752015-08-04 14:53:52 +01001806 if (g_key_file_get_boolean (config, "fwupd", "EnableOptionROM", NULL))
1807 fu_main_add_provider (priv, fu_provider_udev_new ());
Richard Hughes5d057a82015-11-24 18:09:57 +00001808 fu_main_add_provider (priv, fu_provider_dfu_new ());
Richard Hughes25cf6ab2015-08-04 21:34:12 +01001809 fu_main_add_provider (priv, fu_provider_rpi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001810#ifdef HAVE_COLORHUG
Richard Hughes72dff812015-03-03 15:13:25 +00001811 fu_main_add_provider (priv, fu_provider_chug_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001812#endif
1813#ifdef HAVE_UEFI
Richard Hughes8bbfdf42015-02-26 22:28:09 +00001814 fu_main_add_provider (priv, fu_provider_uefi_new ());
Richard Hughes3c99ba42015-03-05 12:17:48 +00001815#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001816
Richard Hughes5d057a82015-11-24 18:09:57 +00001817 /* last as least priority */
1818 fu_main_add_provider (priv, fu_provider_usb_new ());
1819
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001820 /* load introspection from file */
1821 priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml",
1822 &error);
1823 if (priv->introspection_daemon == NULL) {
1824 g_warning ("FuMain: failed to load daemon introspection: %s",
1825 error->message);
1826 goto out;
1827 }
1828
Richard Hughesf508e762015-02-27 12:49:36 +00001829 /* get authority */
1830 priv->authority = polkit_authority_get_sync (NULL, &error);
1831 if (priv->authority == NULL) {
1832 g_warning ("FuMain: failed to load polkit authority: %s",
1833 error->message);
1834 goto out;
1835 }
1836
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001837 /* own the object */
1838 owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
1839 FWUPD_DBUS_SERVICE,
1840 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
1841 G_BUS_NAME_OWNER_FLAGS_REPLACE,
1842 fu_main_on_bus_acquired_cb,
1843 fu_main_on_name_acquired_cb,
1844 fu_main_on_name_lost_cb,
1845 priv, NULL);
1846
1847 /* Only timeout and close the mainloop if we have specified it
1848 * on the command line */
1849 if (immediate_exit)
1850 g_idle_add (fu_main_timed_exit_cb, priv->loop);
1851 else if (timed_exit)
1852 g_timeout_add_seconds (5, fu_main_timed_exit_cb, priv->loop);
1853
1854 /* wait */
1855 g_info ("Daemon ready for requests");
1856 g_main_loop_run (priv->loop);
1857
1858 /* success */
1859 retval = 0;
1860out:
1861 g_option_context_free (context);
1862 if (owner_id > 0)
1863 g_bus_unown_name (owner_id);
1864 if (priv != NULL) {
1865 if (priv->loop != NULL)
1866 g_main_loop_unref (priv->loop);
Richard Hughes18423292015-03-09 17:10:50 +00001867 if (priv->proxy_uid != NULL)
1868 g_object_unref (priv->proxy_uid);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001869 if (priv->connection != NULL)
1870 g_object_unref (priv->connection);
Richard Hughesf508e762015-02-27 12:49:36 +00001871 if (priv->authority != NULL)
1872 g_object_unref (priv->authority);
Richard Hughes3f236502015-09-24 15:43:02 +01001873 if (priv->profile != NULL)
1874 g_object_unref (priv->profile);
Richard Hughes7708a0f2015-07-21 08:41:22 +01001875 if (priv->store != NULL)
1876 g_object_unref (priv->store);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001877 if (priv->introspection_daemon != NULL)
1878 g_dbus_node_info_unref (priv->introspection_daemon);
Richard Hughes033ccba2015-09-10 14:51:28 +01001879 if (priv->store_changed_id != 0)
1880 g_source_remove (priv->store_changed_id);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001881 g_object_unref (priv->pending);
Richard Hughes804c0752015-08-04 14:53:52 +01001882 if (priv->providers != NULL)
1883 g_ptr_array_unref (priv->providers);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00001884 g_ptr_array_unref (priv->devices);
1885 g_free (priv);
1886 }
1887 return retval;
1888}
1889