blob: 7faeb036f459292d15e3c39b81cc2bbe9443c446 [file] [log] [blame]
Richard Hughesd0905142016-03-13 09:46:49 +00001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2016 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 Hughescff38bc2016-12-12 12:03:37 +000024#include <fwupd.h>
25#include <gmodule.h>
26#include <appstream-glib.h>
27#include <errno.h>
28#include <string.h>
29#include <gio/gunixinputstream.h>
Richard Hughesd0905142016-03-13 09:46:49 +000030
Richard Hughescff38bc2016-12-12 12:03:37 +000031#include "fu-plugin-private.h"
32#include "fu-pending.h"
Richard Hughesd0905142016-03-13 09:46:49 +000033
Richard Hughescff38bc2016-12-12 12:03:37 +000034static void fu_plugin_finalize (GObject *object);
35
36typedef struct {
37 GModule *module;
38 GUsbContext *usb_ctx;
39 gboolean enabled;
40 gchar *name;
41 GHashTable *devices; /* platform_id:GObject */
Richard Hughesae3d65f2016-12-16 09:38:01 +000042 GHashTable *devices_delay; /* FuDevice:FuPluginHelper */
Richard Hughescff38bc2016-12-12 12:03:37 +000043 FuPluginData *data;
44} FuPluginPrivate;
45
46enum {
47 SIGNAL_DEVICE_ADDED,
48 SIGNAL_DEVICE_REMOVED,
49 SIGNAL_STATUS_CHANGED,
50 SIGNAL_PERCENTAGE_CHANGED,
51 SIGNAL_LAST
52};
53
54static guint signals[SIGNAL_LAST] = { 0 };
55
56G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT)
57#define GET_PRIVATE(o) (fu_plugin_get_instance_private (o))
58
59typedef const gchar *(*FuPluginGetNameFunc) (void);
60typedef void (*FuPluginInitFunc) (FuPlugin *plugin);
61typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin,
62 GError **error);
63typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin,
64 FuDevice *device,
65 GError **error);
66typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin,
67 FuDevice *device,
68 FuPluginVerifyFlags flags,
69 GError **error);
70typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin,
71 FuDevice *device,
72 GBytes *blob_fw,
73 FwupdInstallFlags flags,
74 GError **error);
75
76const gchar *
77fu_plugin_get_name (FuPlugin *plugin)
Richard Hughesd0905142016-03-13 09:46:49 +000078{
Richard Hughescff38bc2016-12-12 12:03:37 +000079 FuPluginPrivate *priv = GET_PRIVATE (plugin);
80 return priv->name;
81}
Richard Hughesd0905142016-03-13 09:46:49 +000082
Richard Hughescff38bc2016-12-12 12:03:37 +000083void
84fu_plugin_set_name (FuPlugin *plugin, const gchar *name)
85{
86 FuPluginPrivate *priv = GET_PRIVATE (plugin);
87 g_free (priv->name);
88 priv->name = g_strdup (name);
89}
Richard Hughesd0905142016-03-13 09:46:49 +000090
Richard Hughescff38bc2016-12-12 12:03:37 +000091gpointer
92fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id)
93{
94 FuPluginPrivate *priv = GET_PRIVATE (plugin);
95 return g_hash_table_lookup (priv->devices, id);
96}
Richard Hughesd0905142016-03-13 09:46:49 +000097
Richard Hughescff38bc2016-12-12 12:03:37 +000098void
99fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev)
100{
101 FuPluginPrivate *priv = GET_PRIVATE (plugin);
102 g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev));
103}
104
105void
106fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id)
107{
108 FuPluginPrivate *priv = GET_PRIVATE (plugin);
109 g_hash_table_remove (priv->devices, id);
110}
111
112FuPluginData *
113fu_plugin_get_data (FuPlugin *plugin)
114{
115 FuPluginPrivate *priv = GET_PRIVATE (plugin);
116 return priv->data;
117}
118
119FuPluginData *
120fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz)
121{
122 FuPluginPrivate *priv = GET_PRIVATE (plugin);
123 priv->data = g_malloc0 (data_sz);
124 return priv->data;
Richard Hughesd0905142016-03-13 09:46:49 +0000125}
126
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000127GUsbContext *
128fu_plugin_get_usb_context (FuPlugin *plugin)
129{
Richard Hughescff38bc2016-12-12 12:03:37 +0000130 FuPluginPrivate *priv = GET_PRIVATE (plugin);
131 return priv->usb_ctx;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000132}
133
134void
135fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx)
136{
Richard Hughescff38bc2016-12-12 12:03:37 +0000137 FuPluginPrivate *priv = GET_PRIVATE (plugin);
138 g_set_object (&priv->usb_ctx, usb_ctx);
139}
140
141gboolean
142fu_plugin_get_enabled (FuPlugin *plugin)
143{
144 FuPluginPrivate *priv = GET_PRIVATE (plugin);
145 return priv->enabled;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000146}
147
Richard Hughesd0905142016-03-13 09:46:49 +0000148void
Richard Hughescff38bc2016-12-12 12:03:37 +0000149fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000150{
Richard Hughescff38bc2016-12-12 12:03:37 +0000151 FuPluginPrivate *priv = GET_PRIVATE (plugin);
152 priv->enabled = enabled;
153}
154
155gboolean
156fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error)
157{
158 FuPluginPrivate *priv = GET_PRIVATE (plugin);
159 FuPluginInitFunc func = NULL;
160 gchar *str;
161
162 priv->module = g_module_open (filename, 0);
163 if (priv->module == NULL) {
164 g_set_error (error,
165 G_IO_ERROR,
166 G_IO_ERROR_FAILED,
167 "failed to open plugin: %s",
168 g_module_error ());
169 return FALSE;
170 }
171
172 /* set automatically */
173 str = g_strstr_len (filename, -1, "libfu_plugin_");
174 if (str != NULL) {
175 priv->name = g_strdup (str + 13);
176 g_strdelimit (priv->name, ".", '\0');
177 }
Richard Hughesd0905142016-03-13 09:46:49 +0000178
179 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000180 g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func);
181 if (func != NULL) {
182 g_debug ("performing init() on %s", filename);
Richard Hughesd0905142016-03-13 09:46:49 +0000183 func (plugin);
184 }
185
Richard Hughescff38bc2016-12-12 12:03:37 +0000186 return TRUE;
187}
188
189void
190fu_plugin_device_add (FuPlugin *plugin, FuDevice *device)
191{
192 g_debug ("emit added from %s: %s",
193 fu_plugin_get_name (plugin),
194 fu_device_get_id (device));
195 fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
196 fu_device_set_plugin (device, fu_plugin_get_name (plugin));
197 g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device);
198}
199
Richard Hughesae3d65f2016-12-16 09:38:01 +0000200typedef struct {
201 FuPlugin *plugin;
202 FuDevice *device;
203 guint timeout_id;
204} FuPluginHelper;
205
206static void
207fu_plugin_helper_free (FuPluginHelper *helper)
208{
209 g_object_unref (helper->plugin);
210 g_object_unref (helper->device);
211 g_free (helper);
212}
213
214static gboolean
215fu_plugin_device_add_delay_cb (gpointer user_data)
216{
217 FuPluginHelper *helper = (FuPluginHelper *) user_data;
218 fu_plugin_device_add (helper->plugin, helper->device);
219 fu_plugin_helper_free (helper);
220 return FALSE;
221}
222
223void
224fu_plugin_device_add_delay (FuPlugin *plugin, FuDevice *device)
225{
226 FuPluginPrivate *priv = GET_PRIVATE (plugin);
227 FuPluginHelper *helper;
228 g_debug ("waiting a small time for other plugins");
229 helper = g_new0 (FuPluginHelper, 1);
230 helper->plugin = g_object_ref (plugin);
231 helper->device = g_object_ref (device);
232 helper->timeout_id = g_timeout_add (500, fu_plugin_device_add_delay_cb, helper);
233 g_hash_table_insert (priv->devices_delay, device, helper);
234}
235
Richard Hughescff38bc2016-12-12 12:03:37 +0000236void
237fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device)
238{
Richard Hughesae3d65f2016-12-16 09:38:01 +0000239 FuPluginPrivate *priv = GET_PRIVATE (plugin);
240 FuPluginHelper *helper;
241
242 /* waiting for add */
243 helper = g_hash_table_lookup (priv->devices_delay, device);
244 if (helper != NULL) {
245 g_debug ("ignoring remove from delayed addition");
246 g_source_remove (helper->timeout_id);
247 fu_plugin_helper_free (helper);
248 return;
249 }
250
Richard Hughescff38bc2016-12-12 12:03:37 +0000251 g_debug ("emit removed from %s: %s",
252 fu_plugin_get_name (plugin),
253 fu_device_get_id (device));
254 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device);
255}
256
257void
258fu_plugin_set_status (FuPlugin *plugin, FwupdStatus status)
259{
260 g_signal_emit (plugin, signals[SIGNAL_STATUS_CHANGED], 0, status);
261}
262
263void
264fu_plugin_set_percentage (FuPlugin *plugin, guint percentage)
265{
266 g_signal_emit (plugin, signals[SIGNAL_PERCENTAGE_CHANGED], 0,
267 percentage);
Richard Hughesd0905142016-03-13 09:46:49 +0000268}
269
Richard Hughesd0905142016-03-13 09:46:49 +0000270gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000271fu_plugin_runner_startup (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000272{
Richard Hughescff38bc2016-12-12 12:03:37 +0000273 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000274 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000275
276 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000277 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000278 return TRUE;
279
280 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000281 g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func);
282 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000283 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000284 g_debug ("performing startup() on %s", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000285 if (!func (plugin, error)) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000286 g_prefix_error (error, "failed to startup %s: ", priv->name);
287 return FALSE;
288 }
289 return TRUE;
290}
291
292static gboolean
293fu_plugin_runner_offline_invalidate (GError **error)
294{
295 g_autoptr(GError) error_local = NULL;
296 g_autoptr(GFile) file1 = NULL;
297
298 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
299
300 file1 = g_file_new_for_path (FU_OFFLINE_TRIGGER_FILENAME);
301 if (!g_file_query_exists (file1, NULL))
302 return TRUE;
303 if (!g_file_delete (file1, NULL, &error_local)) {
304 g_set_error (error,
305 FWUPD_ERROR,
306 FWUPD_ERROR_INTERNAL,
307 "Cannot delete %s: %s",
308 FU_OFFLINE_TRIGGER_FILENAME,
309 error_local->message);
310 return FALSE;
311 }
312 return TRUE;
313}
314
315static gboolean
316fu_plugin_runner_offline_setup (GError **error)
317{
318 gint rc;
319
320 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
321
322 /* create symlink for the systemd-system-update-generator */
323 rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME);
324 if (rc < 0) {
325 g_set_error (error,
326 FWUPD_ERROR,
327 FWUPD_ERROR_INTERNAL,
328 "Failed to create symlink %s to %s: %s",
329 FU_OFFLINE_TRIGGER_FILENAME,
330 "/var/lib", strerror (errno));
Richard Hughesd0905142016-03-13 09:46:49 +0000331 return FALSE;
332 }
333 return TRUE;
334}
335
Richard Hughesd0905142016-03-13 09:46:49 +0000336gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000337fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000338{
Richard Hughescff38bc2016-12-12 12:03:37 +0000339 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000340 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000341
342 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000343 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000344 return TRUE;
345
346 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000347 g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func);
348 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000349 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000350 g_debug ("performing coldplug() on %s", priv->name);
351 if (!func (plugin, error)) {
352 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
353 return FALSE;
354 }
355 return TRUE;
356}
357
Richard Hughes7b8b2022016-12-12 16:15:03 +0000358gboolean
359fu_plugin_runner_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
360{
361 FuPluginPrivate *priv = GET_PRIVATE (plugin);
362 FuPluginDeviceFunc func = NULL;
363
364 /* not enabled */
365 if (!priv->enabled)
366 return TRUE;
367
368 /* optional */
369 g_module_symbol (priv->module, "fu_plugin_update_prepare", (gpointer *) &func);
370 if (func == NULL)
371 return TRUE;
372 g_debug ("performing update_prepare() on %s", priv->name);
373 if (!func (plugin, device, error)) {
374 g_prefix_error (error, "failed to prepare for update %s: ", priv->name);
375 return FALSE;
376 }
377 return TRUE;
378}
379
380gboolean
381fu_plugin_runner_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
382{
383 FuPluginPrivate *priv = GET_PRIVATE (plugin);
384 FuPluginDeviceFunc func = NULL;
385
386 /* not enabled */
387 if (!priv->enabled)
388 return TRUE;
389
390 /* optional */
391 g_module_symbol (priv->module, "fu_plugin_update_cleanup", (gpointer *) &func);
392 if (func == NULL)
393 return TRUE;
394 g_debug ("performing update_cleanup() on %s", priv->name);
395 if (!func (plugin, device, error)) {
396 g_prefix_error (error, "failed to cleanup update %s: ", priv->name);
397 return FALSE;
398 }
399 return TRUE;
400}
401
Richard Hughescff38bc2016-12-12 12:03:37 +0000402static gboolean
403fu_plugin_runner_schedule_update (FuPlugin *plugin,
404 FuDevice *device,
405 GBytes *blob_cab,
406 GError **error)
407{
408 gchar tmpname[] = {"XXXXXX.cap"};
409 g_autofree gchar *dirname = NULL;
410 g_autofree gchar *filename = NULL;
411 g_autoptr(FwupdResult) res_tmp = NULL;
412 g_autoptr(FuPending) pending = NULL;
413 g_autoptr(GFile) file = NULL;
414
415 /* id already exists */
416 pending = fu_pending_new ();
417 res_tmp = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
418 if (res_tmp != NULL) {
419 g_set_error (error,
420 FWUPD_ERROR,
421 FWUPD_ERROR_ALREADY_PENDING,
422 "%s is already scheduled to be updated",
423 fu_device_get_id (device));
424 return FALSE;
425 }
426
427 /* create directory */
428 dirname = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
429 file = g_file_new_for_path (dirname);
430 if (!g_file_query_exists (file, NULL)) {
431 if (!g_file_make_directory_with_parents (file, NULL, error))
432 return FALSE;
433 }
434
435 /* get a random filename */
436 for (guint i = 0; i < 6; i++)
437 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
438 filename = g_build_filename (dirname, tmpname, NULL);
439
440 /* just copy to the temp file */
441 fu_plugin_set_status (plugin, FWUPD_STATUS_SCHEDULING);
442 if (!g_file_set_contents (filename,
443 g_bytes_get_data (blob_cab, NULL),
444 (gssize) g_bytes_get_size (blob_cab),
445 error))
446 return FALSE;
447
448 /* schedule for next boot */
449 g_debug ("schedule %s to be installed to %s on next boot",
450 filename, fu_device_get_id (device));
451 fu_device_set_update_filename (device, filename);
452
453 /* add to database */
454 if (!fu_pending_add_device (pending, FWUPD_RESULT (device), error))
455 return FALSE;
456
457 /* next boot we run offline */
458 return fu_plugin_runner_offline_setup (error);
459}
460
461gboolean
462fu_plugin_runner_verify (FuPlugin *plugin,
463 FuDevice *device,
464 FuPluginVerifyFlags flags,
465 GError **error)
466{
467 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000468 FuPluginVerifyFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000469
470 /* not enabled */
471 if (!priv->enabled)
472 return TRUE;
473
474 /* optional */
475 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
476 if (func == NULL)
477 return TRUE;
478 g_debug ("performing verify() on %s", priv->name);
479 if (!func (plugin, device, flags, error)) {
480 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000481 return FALSE;
482 }
483 return TRUE;
484}
485
Richard Hughesd0905142016-03-13 09:46:49 +0000486gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000487fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000488{
Richard Hughescff38bc2016-12-12 12:03:37 +0000489 guint64 flags;
490 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000491 FuPluginDeviceFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000492
493 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000494 if (!priv->enabled)
495 return TRUE;
496
497 /* final check */
498 flags = fu_device_get_flags (device);
499 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
500 g_set_error (error,
501 FWUPD_ERROR,
502 FWUPD_ERROR_NOT_SUPPORTED,
503 "Device %s is not locked",
504 fu_device_get_id (device));
505 return FALSE;
506 }
507
508 /* optional */
509 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
510 if (func != NULL) {
511 g_debug ("performing unlock() on %s", priv->name);
512 if (!func (plugin, device, error)) {
Richard Hughes7b8b2022016-12-12 16:15:03 +0000513 g_prefix_error (error, "failed to unlock %s: ", priv->name);
Richard Hughescff38bc2016-12-12 12:03:37 +0000514 return FALSE;
515 }
516 }
517
518 /* update with correct flags */
519 flags = fu_device_get_flags (device);
520 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
521 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
522 return TRUE;
523}
524
525gboolean
526fu_plugin_runner_update (FuPlugin *plugin,
527 FuDevice *device,
528 GBytes *blob_cab,
529 GBytes *blob_fw,
530 FwupdInstallFlags flags,
531 GError **error)
532{
533 FuPluginPrivate *priv = GET_PRIVATE (plugin);
534 FuPluginUpdateFunc func_online;
535 FuPluginUpdateFunc func_offline;
536 g_autoptr(FuPending) pending = NULL;
537 g_autoptr(FwupdResult) res_pending = NULL;
538 GError *error_update = NULL;
539
540 /* not enabled */
541 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000542 return TRUE;
543
544 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000545 g_module_symbol (priv->module, "fu_plugin_update_online", (gpointer *) &func_online);
546 g_module_symbol (priv->module, "fu_plugin_update_offline", (gpointer *) &func_offline);
Richard Hughesd0905142016-03-13 09:46:49 +0000547
Richard Hughescff38bc2016-12-12 12:03:37 +0000548 /* schedule for next reboot, or handle in the plugin */
549 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
550 if (func_offline == NULL) {
551 return fu_plugin_runner_schedule_update (plugin,
552 device,
553 blob_cab,
554 error);
555 }
556 return func_offline (plugin, device, blob_fw, flags, error);
557 }
558
559 /* cancel the pending action */
560 if (!fu_plugin_runner_offline_invalidate (error))
561 return FALSE;
562
563 /* online */
564 if (func_online == NULL) {
565 g_set_error_literal (error,
566 FWUPD_ERROR,
567 FWUPD_ERROR_NOT_SUPPORTED,
568 "No online update possible");
Richard Hughesd0905142016-03-13 09:46:49 +0000569 return FALSE;
570 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000571 pending = fu_pending_new ();
572 res_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
573 if (!func_online (plugin, device, blob_fw, flags, &error_update)) {
574 /* save the error to the database */
575 if (res_pending != NULL) {
576 fu_pending_set_error_msg (pending, FWUPD_RESULT (device),
577 error_update->message, NULL);
578 }
579 g_propagate_error (error, error_update);
580 return FALSE;
581 }
582
583 /* cleanup */
584 if (res_pending != NULL) {
585 const gchar *tmp;
586
587 /* update pending database */
588 fu_pending_set_state (pending, FWUPD_RESULT (device),
589 FWUPD_UPDATE_STATE_SUCCESS, NULL);
590
591 /* delete cab file */
592 tmp = fwupd_result_get_update_filename (res_pending);
593 if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) {
594 g_autoptr(GError) error_local = NULL;
595 g_autoptr(GFile) file = NULL;
596 file = g_file_new_for_path (tmp);
597 if (!g_file_delete (file, NULL, &error_local)) {
598 g_set_error (error,
599 FWUPD_ERROR,
600 FWUPD_ERROR_INVALID_FILE,
601 "Failed to delete %s: %s",
602 tmp, error_local->message);
603 return FALSE;
604 }
605 }
606 }
Richard Hughesd0905142016-03-13 09:46:49 +0000607 return TRUE;
608}
Richard Hughescff38bc2016-12-12 12:03:37 +0000609
610gboolean
611fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error)
612{
613 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000614 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000615 g_autoptr(GError) error_local = NULL;
616 g_autoptr(FwupdResult) res_pending = NULL;
617 g_autoptr(FuPending) pending = NULL;
618
619 /* not enabled */
620 if (!priv->enabled)
621 return TRUE;
622
623 /* use the plugin if the vfunc is provided */
624 g_module_symbol (priv->module, "fu_plugin_clear_result", (gpointer *) &func);
625 if (func != NULL) {
626 g_debug ("performing clear_result() on %s", priv->name);
627 if (!func (plugin, device, error)) {
628 g_prefix_error (error, "failed to clear_result %s: ", priv->name);
629 return FALSE;
630 }
631 return TRUE;
632 }
633
634 /* handled using the database */
635 pending = fu_pending_new ();
636 res_pending = fu_pending_get_device (pending,
637 fu_device_get_id (device),
638 &error_local);
639 if (res_pending == NULL) {
640 g_set_error (error,
641 FWUPD_ERROR,
642 FWUPD_ERROR_INVALID_FILE,
643 "Failed to find %s in pending database: %s",
644 fu_device_get_id (device),
645 error_local->message);
646 return FALSE;
647 }
648
649 /* remove from pending database */
650 return fu_pending_remove_device (pending, FWUPD_RESULT (device), error);
651}
652
653gboolean
654fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error)
655{
656 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000657 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000658 FwupdUpdateState update_state;
659 const gchar *tmp;
660 g_autoptr(GError) error_local = NULL;
661 g_autoptr(FwupdResult) res_pending = NULL;
662 g_autoptr(FuPending) pending = NULL;
663
664 /* not enabled */
665 if (!priv->enabled)
666 return TRUE;
667
668 /* use the plugin if the vfunc is provided */
669 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
670 if (func != NULL) {
671 g_debug ("performing get_results() on %s", priv->name);
672 if (!func (plugin, device, error)) {
673 g_prefix_error (error, "failed to get_results %s: ", priv->name);
674 return FALSE;
675 }
676 return TRUE;
677 }
678
679 /* handled using the database */
680 pending = fu_pending_new ();
681 res_pending = fu_pending_get_device (pending,
682 fu_device_get_id (device),
683 &error_local);
684 if (res_pending == NULL) {
685 g_set_error (error,
686 FWUPD_ERROR,
687 FWUPD_ERROR_NOTHING_TO_DO,
688 "Failed to find %s in pending database: %s",
689 fu_device_get_id (device),
690 error_local->message);
691 return FALSE;
692 }
693
694 /* copy the important parts from the pending device to the real one */
695 update_state = fwupd_result_get_update_state (res_pending);
696 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
697 update_state == FWUPD_UPDATE_STATE_PENDING) {
698 g_set_error (error,
699 FWUPD_ERROR,
700 FWUPD_ERROR_NOTHING_TO_DO,
701 "Device %s has not been updated offline yet",
702 fu_device_get_id (device));
703 return FALSE;
704 }
705
706 /* copy */
707 fu_device_set_update_state (device, update_state);
708 tmp = fwupd_result_get_update_error (res_pending);
709 if (tmp != NULL)
710 fu_device_set_update_error (device, tmp);
711 tmp = fwupd_result_get_device_version (res_pending);
712 if (tmp != NULL)
713 fu_device_set_version (device, tmp);
714 tmp = fwupd_result_get_update_version (res_pending);
715 if (tmp != NULL)
716 fu_device_set_update_version (device, tmp);
717 return TRUE;
718}
719
720static void
721fu_plugin_class_init (FuPluginClass *klass)
722{
723 GObjectClass *object_class = G_OBJECT_CLASS (klass);
724 object_class->finalize = fu_plugin_finalize;
725 signals[SIGNAL_DEVICE_ADDED] =
726 g_signal_new ("device-added",
727 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
728 G_STRUCT_OFFSET (FuPluginClass, device_added),
729 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
730 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
731 signals[SIGNAL_DEVICE_REMOVED] =
732 g_signal_new ("device-removed",
733 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
734 G_STRUCT_OFFSET (FuPluginClass, device_removed),
735 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
736 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
737 signals[SIGNAL_STATUS_CHANGED] =
738 g_signal_new ("status-changed",
739 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
740 G_STRUCT_OFFSET (FuPluginClass, status_changed),
741 NULL, NULL, g_cclosure_marshal_VOID__UINT,
742 G_TYPE_NONE, 1, G_TYPE_UINT);
743 signals[SIGNAL_PERCENTAGE_CHANGED] =
744 g_signal_new ("percentage-changed",
745 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
746 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
747 NULL, NULL, g_cclosure_marshal_VOID__UINT,
748 G_TYPE_NONE, 1, G_TYPE_UINT);
749}
750
751static void
752fu_plugin_init (FuPlugin *plugin)
753{
754 FuPluginPrivate *priv = GET_PRIVATE (plugin);
755 priv->enabled = TRUE;
756 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
757 g_free, (GDestroyNotify) g_object_unref);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000758 priv->devices_delay = g_hash_table_new (g_str_hash, g_str_equal);
Richard Hughescff38bc2016-12-12 12:03:37 +0000759}
760
761static void
762fu_plugin_finalize (GObject *object)
763{
764 FuPlugin *plugin = FU_PLUGIN (object);
765 FuPluginPrivate *priv = GET_PRIVATE (plugin);
766 FuPluginInitFunc func = NULL;
767
768 /* optional */
769 if (priv->module != NULL) {
770 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
771 if (func != NULL) {
772 g_debug ("performing destroy() on %s", priv->name);
773 func (plugin);
774 }
775 }
776
777 if (priv->usb_ctx != NULL)
778 g_object_unref (priv->usb_ctx);
779 if (priv->module != NULL)
780 g_module_close (priv->module);
781 g_hash_table_unref (priv->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000782 g_hash_table_unref (priv->devices_delay);
Richard Hughescff38bc2016-12-12 12:03:37 +0000783 g_free (priv->name);
784 g_free (priv->data);
785
786 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
787}
788
789FuPlugin *
790fu_plugin_new (void)
791{
792 FuPlugin *plugin;
793 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
794 return plugin;
795}
796
797GChecksumType
798fu_plugin_get_checksum_type (FuPluginVerifyFlags flags)
799{
800 if (flags & FU_PLUGIN_VERIFY_FLAG_USE_SHA256)
801 return G_CHECKSUM_SHA256;
802 return G_CHECKSUM_SHA1;
803}