blob: c18d738b64e7ddb95cf318add68d237bb2d3a490 [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 */
42 FuPluginData *data;
43} FuPluginPrivate;
44
45enum {
46 SIGNAL_DEVICE_ADDED,
47 SIGNAL_DEVICE_REMOVED,
48 SIGNAL_STATUS_CHANGED,
49 SIGNAL_PERCENTAGE_CHANGED,
50 SIGNAL_LAST
51};
52
53static guint signals[SIGNAL_LAST] = { 0 };
54
55G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT)
56#define GET_PRIVATE(o) (fu_plugin_get_instance_private (o))
57
58typedef const gchar *(*FuPluginGetNameFunc) (void);
59typedef void (*FuPluginInitFunc) (FuPlugin *plugin);
60typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin,
61 GError **error);
62typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin,
63 FuDevice *device,
64 GError **error);
65typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin,
66 FuDevice *device,
67 FuPluginVerifyFlags flags,
68 GError **error);
69typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin,
70 FuDevice *device,
71 GBytes *blob_fw,
72 FwupdInstallFlags flags,
73 GError **error);
74
75const gchar *
76fu_plugin_get_name (FuPlugin *plugin)
Richard Hughesd0905142016-03-13 09:46:49 +000077{
Richard Hughescff38bc2016-12-12 12:03:37 +000078 FuPluginPrivate *priv = GET_PRIVATE (plugin);
79 return priv->name;
80}
Richard Hughesd0905142016-03-13 09:46:49 +000081
Richard Hughescff38bc2016-12-12 12:03:37 +000082void
83fu_plugin_set_name (FuPlugin *plugin, const gchar *name)
84{
85 FuPluginPrivate *priv = GET_PRIVATE (plugin);
86 g_free (priv->name);
87 priv->name = g_strdup (name);
88}
Richard Hughesd0905142016-03-13 09:46:49 +000089
Richard Hughescff38bc2016-12-12 12:03:37 +000090gpointer
91fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id)
92{
93 FuPluginPrivate *priv = GET_PRIVATE (plugin);
94 return g_hash_table_lookup (priv->devices, id);
95}
Richard Hughesd0905142016-03-13 09:46:49 +000096
Richard Hughescff38bc2016-12-12 12:03:37 +000097void
98fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev)
99{
100 FuPluginPrivate *priv = GET_PRIVATE (plugin);
101 g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev));
102}
103
104void
105fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id)
106{
107 FuPluginPrivate *priv = GET_PRIVATE (plugin);
108 g_hash_table_remove (priv->devices, id);
109}
110
111FuPluginData *
112fu_plugin_get_data (FuPlugin *plugin)
113{
114 FuPluginPrivate *priv = GET_PRIVATE (plugin);
115 return priv->data;
116}
117
118FuPluginData *
119fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz)
120{
121 FuPluginPrivate *priv = GET_PRIVATE (plugin);
122 priv->data = g_malloc0 (data_sz);
123 return priv->data;
Richard Hughesd0905142016-03-13 09:46:49 +0000124}
125
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000126GUsbContext *
127fu_plugin_get_usb_context (FuPlugin *plugin)
128{
Richard Hughescff38bc2016-12-12 12:03:37 +0000129 FuPluginPrivate *priv = GET_PRIVATE (plugin);
130 return priv->usb_ctx;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000131}
132
133void
134fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx)
135{
Richard Hughescff38bc2016-12-12 12:03:37 +0000136 FuPluginPrivate *priv = GET_PRIVATE (plugin);
137 g_set_object (&priv->usb_ctx, usb_ctx);
138}
139
140gboolean
141fu_plugin_get_enabled (FuPlugin *plugin)
142{
143 FuPluginPrivate *priv = GET_PRIVATE (plugin);
144 return priv->enabled;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000145}
146
Richard Hughesd0905142016-03-13 09:46:49 +0000147void
Richard Hughescff38bc2016-12-12 12:03:37 +0000148fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000149{
Richard Hughescff38bc2016-12-12 12:03:37 +0000150 FuPluginPrivate *priv = GET_PRIVATE (plugin);
151 priv->enabled = enabled;
152}
153
154gboolean
155fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error)
156{
157 FuPluginPrivate *priv = GET_PRIVATE (plugin);
158 FuPluginInitFunc func = NULL;
159 gchar *str;
160
161 priv->module = g_module_open (filename, 0);
162 if (priv->module == NULL) {
163 g_set_error (error,
164 G_IO_ERROR,
165 G_IO_ERROR_FAILED,
166 "failed to open plugin: %s",
167 g_module_error ());
168 return FALSE;
169 }
170
171 /* set automatically */
172 str = g_strstr_len (filename, -1, "libfu_plugin_");
173 if (str != NULL) {
174 priv->name = g_strdup (str + 13);
175 g_strdelimit (priv->name, ".", '\0');
176 }
Richard Hughesd0905142016-03-13 09:46:49 +0000177
178 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000179 g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func);
180 if (func != NULL) {
181 g_debug ("performing init() on %s", filename);
Richard Hughesd0905142016-03-13 09:46:49 +0000182 func (plugin);
183 }
184
Richard Hughescff38bc2016-12-12 12:03:37 +0000185 return TRUE;
186}
187
188void
189fu_plugin_device_add (FuPlugin *plugin, FuDevice *device)
190{
191 g_debug ("emit added from %s: %s",
192 fu_plugin_get_name (plugin),
193 fu_device_get_id (device));
194 fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
195 fu_device_set_plugin (device, fu_plugin_get_name (plugin));
196 g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device);
197}
198
199void
200fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device)
201{
202 g_debug ("emit removed from %s: %s",
203 fu_plugin_get_name (plugin),
204 fu_device_get_id (device));
205 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device);
206}
207
208void
209fu_plugin_set_status (FuPlugin *plugin, FwupdStatus status)
210{
211 g_signal_emit (plugin, signals[SIGNAL_STATUS_CHANGED], 0, status);
212}
213
214void
215fu_plugin_set_percentage (FuPlugin *plugin, guint percentage)
216{
217 g_signal_emit (plugin, signals[SIGNAL_PERCENTAGE_CHANGED], 0,
218 percentage);
Richard Hughesd0905142016-03-13 09:46:49 +0000219}
220
Richard Hughesd0905142016-03-13 09:46:49 +0000221gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000222fu_plugin_runner_startup (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000223{
Richard Hughescff38bc2016-12-12 12:03:37 +0000224 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesd0905142016-03-13 09:46:49 +0000225 FuPluginStartupFunc func;
226
227 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000228 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000229 return TRUE;
230
231 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000232 g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func);
233 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000234 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000235 g_debug ("performing startup() on %s", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000236 if (!func (plugin, error)) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000237 g_prefix_error (error, "failed to startup %s: ", priv->name);
238 return FALSE;
239 }
240 return TRUE;
241}
242
243static gboolean
244fu_plugin_runner_offline_invalidate (GError **error)
245{
246 g_autoptr(GError) error_local = NULL;
247 g_autoptr(GFile) file1 = NULL;
248
249 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
250
251 file1 = g_file_new_for_path (FU_OFFLINE_TRIGGER_FILENAME);
252 if (!g_file_query_exists (file1, NULL))
253 return TRUE;
254 if (!g_file_delete (file1, NULL, &error_local)) {
255 g_set_error (error,
256 FWUPD_ERROR,
257 FWUPD_ERROR_INTERNAL,
258 "Cannot delete %s: %s",
259 FU_OFFLINE_TRIGGER_FILENAME,
260 error_local->message);
261 return FALSE;
262 }
263 return TRUE;
264}
265
266static gboolean
267fu_plugin_runner_offline_setup (GError **error)
268{
269 gint rc;
270
271 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
272
273 /* create symlink for the systemd-system-update-generator */
274 rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME);
275 if (rc < 0) {
276 g_set_error (error,
277 FWUPD_ERROR,
278 FWUPD_ERROR_INTERNAL,
279 "Failed to create symlink %s to %s: %s",
280 FU_OFFLINE_TRIGGER_FILENAME,
281 "/var/lib", strerror (errno));
Richard Hughesd0905142016-03-13 09:46:49 +0000282 return FALSE;
283 }
284 return TRUE;
285}
286
Richard Hughesd0905142016-03-13 09:46:49 +0000287gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000288fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000289{
Richard Hughescff38bc2016-12-12 12:03:37 +0000290 FuPluginPrivate *priv = GET_PRIVATE (plugin);
291 FuPluginStartupFunc func;
Richard Hughesd0905142016-03-13 09:46:49 +0000292
293 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000294 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000295 return TRUE;
296
297 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000298 g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func);
299 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000300 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000301 g_debug ("performing coldplug() on %s", priv->name);
302 if (!func (plugin, error)) {
303 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
304 return FALSE;
305 }
306 return TRUE;
307}
308
309static gboolean
310fu_plugin_runner_schedule_update (FuPlugin *plugin,
311 FuDevice *device,
312 GBytes *blob_cab,
313 GError **error)
314{
315 gchar tmpname[] = {"XXXXXX.cap"};
316 g_autofree gchar *dirname = NULL;
317 g_autofree gchar *filename = NULL;
318 g_autoptr(FwupdResult) res_tmp = NULL;
319 g_autoptr(FuPending) pending = NULL;
320 g_autoptr(GFile) file = NULL;
321
322 /* id already exists */
323 pending = fu_pending_new ();
324 res_tmp = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
325 if (res_tmp != NULL) {
326 g_set_error (error,
327 FWUPD_ERROR,
328 FWUPD_ERROR_ALREADY_PENDING,
329 "%s is already scheduled to be updated",
330 fu_device_get_id (device));
331 return FALSE;
332 }
333
334 /* create directory */
335 dirname = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
336 file = g_file_new_for_path (dirname);
337 if (!g_file_query_exists (file, NULL)) {
338 if (!g_file_make_directory_with_parents (file, NULL, error))
339 return FALSE;
340 }
341
342 /* get a random filename */
343 for (guint i = 0; i < 6; i++)
344 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
345 filename = g_build_filename (dirname, tmpname, NULL);
346
347 /* just copy to the temp file */
348 fu_plugin_set_status (plugin, FWUPD_STATUS_SCHEDULING);
349 if (!g_file_set_contents (filename,
350 g_bytes_get_data (blob_cab, NULL),
351 (gssize) g_bytes_get_size (blob_cab),
352 error))
353 return FALSE;
354
355 /* schedule for next boot */
356 g_debug ("schedule %s to be installed to %s on next boot",
357 filename, fu_device_get_id (device));
358 fu_device_set_update_filename (device, filename);
359
360 /* add to database */
361 if (!fu_pending_add_device (pending, FWUPD_RESULT (device), error))
362 return FALSE;
363
364 /* next boot we run offline */
365 return fu_plugin_runner_offline_setup (error);
366}
367
368gboolean
369fu_plugin_runner_verify (FuPlugin *plugin,
370 FuDevice *device,
371 FuPluginVerifyFlags flags,
372 GError **error)
373{
374 FuPluginPrivate *priv = GET_PRIVATE (plugin);
375 FuPluginVerifyFunc func;
376
377 /* not enabled */
378 if (!priv->enabled)
379 return TRUE;
380
381 /* optional */
382 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
383 if (func == NULL)
384 return TRUE;
385 g_debug ("performing verify() on %s", priv->name);
386 if (!func (plugin, device, flags, error)) {
387 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000388 return FALSE;
389 }
390 return TRUE;
391}
392
Richard Hughesd0905142016-03-13 09:46:49 +0000393gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000394fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000395{
Richard Hughescff38bc2016-12-12 12:03:37 +0000396 guint64 flags;
397 FuPluginPrivate *priv = GET_PRIVATE (plugin);
398 FuPluginDeviceFunc func;
Richard Hughesd0905142016-03-13 09:46:49 +0000399
400 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000401 if (!priv->enabled)
402 return TRUE;
403
404 /* final check */
405 flags = fu_device_get_flags (device);
406 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
407 g_set_error (error,
408 FWUPD_ERROR,
409 FWUPD_ERROR_NOT_SUPPORTED,
410 "Device %s is not locked",
411 fu_device_get_id (device));
412 return FALSE;
413 }
414
415 /* optional */
416 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
417 if (func != NULL) {
418 g_debug ("performing unlock() on %s", priv->name);
419 if (!func (plugin, device, error)) {
420 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
421 return FALSE;
422 }
423 }
424
425 /* update with correct flags */
426 flags = fu_device_get_flags (device);
427 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
428 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
429 return TRUE;
430}
431
432gboolean
433fu_plugin_runner_update (FuPlugin *plugin,
434 FuDevice *device,
435 GBytes *blob_cab,
436 GBytes *blob_fw,
437 FwupdInstallFlags flags,
438 GError **error)
439{
440 FuPluginPrivate *priv = GET_PRIVATE (plugin);
441 FuPluginUpdateFunc func_online;
442 FuPluginUpdateFunc func_offline;
443 g_autoptr(FuPending) pending = NULL;
444 g_autoptr(FwupdResult) res_pending = NULL;
445 GError *error_update = NULL;
446
447 /* not enabled */
448 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000449 return TRUE;
450
451 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000452 g_module_symbol (priv->module, "fu_plugin_update_online", (gpointer *) &func_online);
453 g_module_symbol (priv->module, "fu_plugin_update_offline", (gpointer *) &func_offline);
Richard Hughesd0905142016-03-13 09:46:49 +0000454
Richard Hughescff38bc2016-12-12 12:03:37 +0000455 /* schedule for next reboot, or handle in the plugin */
456 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
457 if (func_offline == NULL) {
458 return fu_plugin_runner_schedule_update (plugin,
459 device,
460 blob_cab,
461 error);
462 }
463 return func_offline (plugin, device, blob_fw, flags, error);
464 }
465
466 /* cancel the pending action */
467 if (!fu_plugin_runner_offline_invalidate (error))
468 return FALSE;
469
470 /* online */
471 if (func_online == NULL) {
472 g_set_error_literal (error,
473 FWUPD_ERROR,
474 FWUPD_ERROR_NOT_SUPPORTED,
475 "No online update possible");
Richard Hughesd0905142016-03-13 09:46:49 +0000476 return FALSE;
477 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000478 pending = fu_pending_new ();
479 res_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
480 if (!func_online (plugin, device, blob_fw, flags, &error_update)) {
481 /* save the error to the database */
482 if (res_pending != NULL) {
483 fu_pending_set_error_msg (pending, FWUPD_RESULT (device),
484 error_update->message, NULL);
485 }
486 g_propagate_error (error, error_update);
487 return FALSE;
488 }
489
490 /* cleanup */
491 if (res_pending != NULL) {
492 const gchar *tmp;
493
494 /* update pending database */
495 fu_pending_set_state (pending, FWUPD_RESULT (device),
496 FWUPD_UPDATE_STATE_SUCCESS, NULL);
497
498 /* delete cab file */
499 tmp = fwupd_result_get_update_filename (res_pending);
500 if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) {
501 g_autoptr(GError) error_local = NULL;
502 g_autoptr(GFile) file = NULL;
503 file = g_file_new_for_path (tmp);
504 if (!g_file_delete (file, NULL, &error_local)) {
505 g_set_error (error,
506 FWUPD_ERROR,
507 FWUPD_ERROR_INVALID_FILE,
508 "Failed to delete %s: %s",
509 tmp, error_local->message);
510 return FALSE;
511 }
512 }
513 }
Richard Hughesd0905142016-03-13 09:46:49 +0000514 return TRUE;
515}
Richard Hughescff38bc2016-12-12 12:03:37 +0000516
517gboolean
518fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error)
519{
520 FuPluginPrivate *priv = GET_PRIVATE (plugin);
521 FuPluginDeviceFunc func;
522 g_autoptr(GError) error_local = NULL;
523 g_autoptr(FwupdResult) res_pending = NULL;
524 g_autoptr(FuPending) pending = NULL;
525
526 /* not enabled */
527 if (!priv->enabled)
528 return TRUE;
529
530 /* use the plugin if the vfunc is provided */
531 g_module_symbol (priv->module, "fu_plugin_clear_result", (gpointer *) &func);
532 if (func != NULL) {
533 g_debug ("performing clear_result() on %s", priv->name);
534 if (!func (plugin, device, error)) {
535 g_prefix_error (error, "failed to clear_result %s: ", priv->name);
536 return FALSE;
537 }
538 return TRUE;
539 }
540
541 /* handled using the database */
542 pending = fu_pending_new ();
543 res_pending = fu_pending_get_device (pending,
544 fu_device_get_id (device),
545 &error_local);
546 if (res_pending == NULL) {
547 g_set_error (error,
548 FWUPD_ERROR,
549 FWUPD_ERROR_INVALID_FILE,
550 "Failed to find %s in pending database: %s",
551 fu_device_get_id (device),
552 error_local->message);
553 return FALSE;
554 }
555
556 /* remove from pending database */
557 return fu_pending_remove_device (pending, FWUPD_RESULT (device), error);
558}
559
560gboolean
561fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error)
562{
563 FuPluginPrivate *priv = GET_PRIVATE (plugin);
564 FuPluginDeviceFunc func;
565 FwupdUpdateState update_state;
566 const gchar *tmp;
567 g_autoptr(GError) error_local = NULL;
568 g_autoptr(FwupdResult) res_pending = NULL;
569 g_autoptr(FuPending) pending = NULL;
570
571 /* not enabled */
572 if (!priv->enabled)
573 return TRUE;
574
575 /* use the plugin if the vfunc is provided */
576 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
577 if (func != NULL) {
578 g_debug ("performing get_results() on %s", priv->name);
579 if (!func (plugin, device, error)) {
580 g_prefix_error (error, "failed to get_results %s: ", priv->name);
581 return FALSE;
582 }
583 return TRUE;
584 }
585
586 /* handled using the database */
587 pending = fu_pending_new ();
588 res_pending = fu_pending_get_device (pending,
589 fu_device_get_id (device),
590 &error_local);
591 if (res_pending == NULL) {
592 g_set_error (error,
593 FWUPD_ERROR,
594 FWUPD_ERROR_NOTHING_TO_DO,
595 "Failed to find %s in pending database: %s",
596 fu_device_get_id (device),
597 error_local->message);
598 return FALSE;
599 }
600
601 /* copy the important parts from the pending device to the real one */
602 update_state = fwupd_result_get_update_state (res_pending);
603 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
604 update_state == FWUPD_UPDATE_STATE_PENDING) {
605 g_set_error (error,
606 FWUPD_ERROR,
607 FWUPD_ERROR_NOTHING_TO_DO,
608 "Device %s has not been updated offline yet",
609 fu_device_get_id (device));
610 return FALSE;
611 }
612
613 /* copy */
614 fu_device_set_update_state (device, update_state);
615 tmp = fwupd_result_get_update_error (res_pending);
616 if (tmp != NULL)
617 fu_device_set_update_error (device, tmp);
618 tmp = fwupd_result_get_device_version (res_pending);
619 if (tmp != NULL)
620 fu_device_set_version (device, tmp);
621 tmp = fwupd_result_get_update_version (res_pending);
622 if (tmp != NULL)
623 fu_device_set_update_version (device, tmp);
624 return TRUE;
625}
626
627static void
628fu_plugin_class_init (FuPluginClass *klass)
629{
630 GObjectClass *object_class = G_OBJECT_CLASS (klass);
631 object_class->finalize = fu_plugin_finalize;
632 signals[SIGNAL_DEVICE_ADDED] =
633 g_signal_new ("device-added",
634 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
635 G_STRUCT_OFFSET (FuPluginClass, device_added),
636 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
637 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
638 signals[SIGNAL_DEVICE_REMOVED] =
639 g_signal_new ("device-removed",
640 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
641 G_STRUCT_OFFSET (FuPluginClass, device_removed),
642 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
643 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
644 signals[SIGNAL_STATUS_CHANGED] =
645 g_signal_new ("status-changed",
646 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
647 G_STRUCT_OFFSET (FuPluginClass, status_changed),
648 NULL, NULL, g_cclosure_marshal_VOID__UINT,
649 G_TYPE_NONE, 1, G_TYPE_UINT);
650 signals[SIGNAL_PERCENTAGE_CHANGED] =
651 g_signal_new ("percentage-changed",
652 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
653 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
654 NULL, NULL, g_cclosure_marshal_VOID__UINT,
655 G_TYPE_NONE, 1, G_TYPE_UINT);
656}
657
658static void
659fu_plugin_init (FuPlugin *plugin)
660{
661 FuPluginPrivate *priv = GET_PRIVATE (plugin);
662 priv->enabled = TRUE;
663 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
664 g_free, (GDestroyNotify) g_object_unref);
665}
666
667static void
668fu_plugin_finalize (GObject *object)
669{
670 FuPlugin *plugin = FU_PLUGIN (object);
671 FuPluginPrivate *priv = GET_PRIVATE (plugin);
672 FuPluginInitFunc func = NULL;
673
674 /* optional */
675 if (priv->module != NULL) {
676 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
677 if (func != NULL) {
678 g_debug ("performing destroy() on %s", priv->name);
679 func (plugin);
680 }
681 }
682
683 if (priv->usb_ctx != NULL)
684 g_object_unref (priv->usb_ctx);
685 if (priv->module != NULL)
686 g_module_close (priv->module);
687 g_hash_table_unref (priv->devices);
688 g_free (priv->name);
689 g_free (priv->data);
690
691 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
692}
693
694FuPlugin *
695fu_plugin_new (void)
696{
697 FuPlugin *plugin;
698 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
699 return plugin;
700}
701
702GChecksumType
703fu_plugin_get_checksum_type (FuPluginVerifyFlags flags)
704{
705 if (flags & FU_PLUGIN_VERIFY_FLAG_USE_SHA256)
706 return G_CHECKSUM_SHA256;
707 return G_CHECKSUM_SHA1;
708}