blob: ffa1c08478a4f798fa0529f5ea321a1023083705 [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 *
Richard Hughesa785a1c2017-08-25 16:00:58 +01003 * Copyright (C) 2016-2017 Richard Hughes <richard@hughsie.com>
Richard Hughesd0905142016-03-13 09:46:49 +00004 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
Richard 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>
Mario Limonciello6d0aa3d2017-02-28 08:22:27 -060030#ifdef HAVE_VALGRIND
Richard Hughes576c0122017-02-24 09:47:00 +000031#include <valgrind.h>
Mario Limonciello6d0aa3d2017-02-28 08:22:27 -060032#endif /* HAVE_VALGRIND */
Richard Hughesd0905142016-03-13 09:46:49 +000033
Richard Hughes9dde04f2017-09-13 12:07:15 +010034#include "fu-device-private.h"
Richard Hughescff38bc2016-12-12 12:03:37 +000035#include "fu-plugin-private.h"
36#include "fu-pending.h"
Richard Hughesd0905142016-03-13 09:46:49 +000037
Richard Hughesb0829032017-01-10 09:27:08 +000038#define FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM 3000u /* ms */
39
Richard Hughescff38bc2016-12-12 12:03:37 +000040static void fu_plugin_finalize (GObject *object);
41
42typedef struct {
43 GModule *module;
44 GUsbContext *usb_ctx;
45 gboolean enabled;
46 gchar *name;
Richard Hughesd7704d42017-08-08 20:29:09 +010047 FuHwids *hwids;
Richard Hughes49e5e052017-09-03 12:15:41 +010048 FuSmbios *smbios;
Richard Hughescff38bc2016-12-12 12:03:37 +000049 GHashTable *devices; /* platform_id:GObject */
Richard Hughesae3d65f2016-12-16 09:38:01 +000050 GHashTable *devices_delay; /* FuDevice:FuPluginHelper */
Richard Hughescff38bc2016-12-12 12:03:37 +000051 FuPluginData *data;
52} FuPluginPrivate;
53
54enum {
55 SIGNAL_DEVICE_ADDED,
56 SIGNAL_DEVICE_REMOVED,
Richard Hughese1fd34d2017-08-24 14:19:51 +010057 SIGNAL_DEVICE_REGISTER,
Richard Hughescff38bc2016-12-12 12:03:37 +000058 SIGNAL_STATUS_CHANGED,
59 SIGNAL_PERCENTAGE_CHANGED,
Richard Hughes362d6d72017-01-07 21:42:14 +000060 SIGNAL_RECOLDPLUG,
Richard Hughesb0829032017-01-10 09:27:08 +000061 SIGNAL_SET_COLDPLUG_DELAY,
Richard Hughescff38bc2016-12-12 12:03:37 +000062 SIGNAL_LAST
63};
64
65static guint signals[SIGNAL_LAST] = { 0 };
66
67G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT)
68#define GET_PRIVATE(o) (fu_plugin_get_instance_private (o))
69
70typedef const gchar *(*FuPluginGetNameFunc) (void);
71typedef void (*FuPluginInitFunc) (FuPlugin *plugin);
72typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin,
73 GError **error);
Richard Hughese1fd34d2017-08-24 14:19:51 +010074typedef void (*FuPluginDeviceRegisterFunc) (FuPlugin *plugin,
75 FuDevice *device);
Richard Hughescff38bc2016-12-12 12:03:37 +000076typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin,
77 FuDevice *device,
78 GError **error);
79typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin,
80 FuDevice *device,
81 FuPluginVerifyFlags flags,
82 GError **error);
83typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin,
84 FuDevice *device,
85 GBytes *blob_fw,
86 FwupdInstallFlags flags,
87 GError **error);
88
Richard Hughes57d18222017-01-10 16:02:59 +000089/**
90 * fu_plugin_get_name:
91 * @plugin: A #FuPlugin
92 *
93 * Gets the plugin name.
94 *
95 * Returns: a plugin name, or %NULL for unknown.
96 *
97 * Since: 0.8.0
98 **/
Richard Hughescff38bc2016-12-12 12:03:37 +000099const gchar *
100fu_plugin_get_name (FuPlugin *plugin)
Richard Hughesd0905142016-03-13 09:46:49 +0000101{
Richard Hughescff38bc2016-12-12 12:03:37 +0000102 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000103 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000104 return priv->name;
105}
Richard Hughesd0905142016-03-13 09:46:49 +0000106
Richard Hughes57d18222017-01-10 16:02:59 +0000107/**
108 * fu_plugin_cache_lookup:
109 * @plugin: A #FuPlugin
110 * @id: the key
111 *
112 * Finds an object in the per-plugin cache.
113 *
114 * Returns: (transfer none): a #GObject, or %NULL for unfound.
115 *
116 * Since: 0.8.0
117 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000118gpointer
119fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id)
120{
121 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000122 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
123 g_return_val_if_fail (id != NULL, NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000124 return g_hash_table_lookup (priv->devices, id);
125}
Richard Hughesd0905142016-03-13 09:46:49 +0000126
Richard Hughes57d18222017-01-10 16:02:59 +0000127/**
128 * fu_plugin_cache_add:
129 * @plugin: A #FuPlugin
130 * @id: the key
131 * @dev: a #GObject, typically a #FuDevice
132 *
133 * Adds an object to the per-plugin cache.
134 *
135 * Since: 0.8.0
136 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000137void
138fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev)
139{
140 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000141 g_return_if_fail (FU_IS_PLUGIN (plugin));
142 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000143 g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev));
144}
145
Richard Hughes57d18222017-01-10 16:02:59 +0000146/**
147 * fu_plugin_cache_remove:
148 * @plugin: A #FuPlugin
149 * @id: the key
150 *
151 * Removes an object from the per-plugin cache.
152 *
153 * Since: 0.8.0
154 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000155void
156fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id)
157{
158 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000159 g_return_if_fail (FU_IS_PLUGIN (plugin));
160 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000161 g_hash_table_remove (priv->devices, id);
162}
163
Richard Hughes57d18222017-01-10 16:02:59 +0000164/**
165 * fu_plugin_get_data:
166 * @plugin: A #FuPlugin
167 *
168 * Gets the per-plugin allocated private data.
169 *
170 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
171 *
172 * Since: 0.8.0
173 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000174FuPluginData *
175fu_plugin_get_data (FuPlugin *plugin)
176{
177 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000178 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000179 return priv->data;
180}
181
Richard Hughes57d18222017-01-10 16:02:59 +0000182/**
183 * fu_plugin_alloc_data:
184 * @plugin: A #FuPlugin
185 * @data_sz: the size to allocate
186 *
187 * Allocates the per-plugin allocated private data.
188 *
189 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
190 *
191 * Since: 0.8.0
192 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000193FuPluginData *
194fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz)
195{
196 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000197 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughes44dee882017-01-11 08:31:10 +0000198 if (priv->data != NULL) {
199 g_critical ("fu_plugin_alloc_data() already used by plugin");
200 return priv->data;
201 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000202 priv->data = g_malloc0 (data_sz);
203 return priv->data;
Richard Hughesd0905142016-03-13 09:46:49 +0000204}
205
Richard Hughes57d18222017-01-10 16:02:59 +0000206/**
207 * fu_plugin_get_usb_context:
208 * @plugin: A #FuPlugin
209 *
210 * Gets the shared USB context that all plugins can use.
211 *
212 * Returns: (transfer none): a #GUsbContext.
213 *
214 * Since: 0.8.0
215 **/
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000216GUsbContext *
217fu_plugin_get_usb_context (FuPlugin *plugin)
218{
Richard Hughescff38bc2016-12-12 12:03:37 +0000219 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000220 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000221 return priv->usb_ctx;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000222}
223
224void
225fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx)
226{
Richard Hughescff38bc2016-12-12 12:03:37 +0000227 FuPluginPrivate *priv = GET_PRIVATE (plugin);
228 g_set_object (&priv->usb_ctx, usb_ctx);
229}
230
Richard Hughes57d18222017-01-10 16:02:59 +0000231/**
232 * fu_plugin_get_enabled:
233 * @plugin: A #FuPlugin
234 *
235 * Returns if the plugin is enabled.
236 *
237 * Returns: %TRUE if the plugin is currently enabled.
238 *
239 * Since: 0.8.0
240 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000241gboolean
242fu_plugin_get_enabled (FuPlugin *plugin)
243{
244 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000245 g_return_val_if_fail (FU_IS_PLUGIN (plugin), FALSE);
Richard Hughescff38bc2016-12-12 12:03:37 +0000246 return priv->enabled;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000247}
248
Richard Hughes57d18222017-01-10 16:02:59 +0000249/**
250 * fu_plugin_set_enabled:
251 * @plugin: A #FuPlugin
252 * @enabled: the enabled value
253 *
254 * Enables or disables a plugin. Plugins can self-disable at any point.
255 *
256 * Since: 0.8.0
257 **/
Richard Hughesd0905142016-03-13 09:46:49 +0000258void
Richard Hughescff38bc2016-12-12 12:03:37 +0000259fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000260{
Richard Hughescff38bc2016-12-12 12:03:37 +0000261 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000262 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000263 priv->enabled = enabled;
264}
265
266gboolean
267fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error)
268{
269 FuPluginPrivate *priv = GET_PRIVATE (plugin);
270 FuPluginInitFunc func = NULL;
271 gchar *str;
272
273 priv->module = g_module_open (filename, 0);
274 if (priv->module == NULL) {
275 g_set_error (error,
276 G_IO_ERROR,
277 G_IO_ERROR_FAILED,
278 "failed to open plugin: %s",
279 g_module_error ());
280 return FALSE;
281 }
282
283 /* set automatically */
284 str = g_strstr_len (filename, -1, "libfu_plugin_");
285 if (str != NULL) {
286 priv->name = g_strdup (str + 13);
287 g_strdelimit (priv->name, ".", '\0');
288 }
Richard Hughesd0905142016-03-13 09:46:49 +0000289
290 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000291 g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func);
292 if (func != NULL) {
293 g_debug ("performing init() on %s", filename);
Richard Hughesd0905142016-03-13 09:46:49 +0000294 func (plugin);
295 }
296
Richard Hughescff38bc2016-12-12 12:03:37 +0000297 return TRUE;
298}
299
Richard Hughes57d18222017-01-10 16:02:59 +0000300/**
301 * fu_plugin_device_add:
302 * @plugin: A #FuPlugin
303 * @device: A #FuDevice
304 *
305 * Asks the daemon to add a device to the exported list. If this device ID
306 * has already been added by a different plugin then this request will be
307 * ignored.
308 *
309 * Plugins should use fu_plugin_device_add_delay() if they are not capable of
310 * actually flashing an image to the hardware so that higher-priority plugins
311 * can add the device themselves.
312 *
313 * Since: 0.8.0
314 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000315void
316fu_plugin_device_add (FuPlugin *plugin, FuDevice *device)
317{
Richard Hughesccd78a92017-01-11 16:57:41 +0000318 g_return_if_fail (FU_IS_PLUGIN (plugin));
319 g_return_if_fail (FU_IS_DEVICE (device));
320
Richard Hughescff38bc2016-12-12 12:03:37 +0000321 g_debug ("emit added from %s: %s",
322 fu_plugin_get_name (plugin),
323 fu_device_get_id (device));
324 fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
325 fu_device_set_plugin (device, fu_plugin_get_name (plugin));
326 g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device);
327}
328
Richard Hughese1fd34d2017-08-24 14:19:51 +0100329/**
330 * fu_plugin_device_register:
331 * @plugin: A #FuPlugin
332 * @device: A #FuDevice
333 *
334 * Registers the device with other plugins so they can set metadata.
335 *
336 * Plugins do not have to call this manually as this is done automatically
337 * when using fu_plugin_device_add(). They may wish to use this manually
338 * if for intance the coldplug should be ignored based on the metadata
339 * set from other plugins.
340 *
341 * Since: 0.9.7
342 **/
343void
344fu_plugin_device_register (FuPlugin *plugin, FuDevice *device)
345{
346 g_return_if_fail (FU_IS_PLUGIN (plugin));
347 g_return_if_fail (FU_IS_DEVICE (device));
348
349 g_debug ("emit device-register from %s: %s",
350 fu_plugin_get_name (plugin),
351 fu_device_get_id (device));
352 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REGISTER], 0, device);
353}
354
Richard Hughesae3d65f2016-12-16 09:38:01 +0000355typedef struct {
356 FuPlugin *plugin;
357 FuDevice *device;
358 guint timeout_id;
Richard Hughesd4184cf2016-12-21 16:05:17 +0000359 GHashTable *devices;
Richard Hughesae3d65f2016-12-16 09:38:01 +0000360} FuPluginHelper;
361
362static void
363fu_plugin_helper_free (FuPluginHelper *helper)
364{
365 g_object_unref (helper->plugin);
366 g_object_unref (helper->device);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000367 g_hash_table_unref (helper->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000368 g_free (helper);
369}
370
371static gboolean
372fu_plugin_device_add_delay_cb (gpointer user_data)
373{
374 FuPluginHelper *helper = (FuPluginHelper *) user_data;
Richard Hughesf0a799e2017-01-17 20:13:30 +0000375 g_hash_table_remove (helper->devices, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000376 fu_plugin_device_add (helper->plugin, helper->device);
377 fu_plugin_helper_free (helper);
378 return FALSE;
379}
380
Richard Hughes57d18222017-01-10 16:02:59 +0000381/**
Richard Hughesf0a799e2017-01-17 20:13:30 +0000382 * fu_plugin_has_device_delay:
383 * @plugin: A #FuPlugin
384 *
385 * Returns if the device has a pending device that is waiting to be added.
386 *
387 * Returns: %TRUE if a device is waiting to be added
388 *
389 * Since: 0.8.0
390 **/
391gboolean
392fu_plugin_has_device_delay (FuPlugin *plugin)
393{
394 FuPluginPrivate *priv = GET_PRIVATE (plugin);
395 return g_hash_table_size (priv->devices_delay) > 0;
396}
397
398/**
Richard Hughes57d18222017-01-10 16:02:59 +0000399 * fu_plugin_device_add_delay:
400 * @plugin: A #FuPlugin
401 * @device: A #FuDevice
402 *
403 * Asks the daemon to add a device to the exported list after a small delay.
404 *
405 * Since: 0.8.0
406 **/
Richard Hughesae3d65f2016-12-16 09:38:01 +0000407void
408fu_plugin_device_add_delay (FuPlugin *plugin, FuDevice *device)
409{
410 FuPluginPrivate *priv = GET_PRIVATE (plugin);
411 FuPluginHelper *helper;
Richard Hughesccd78a92017-01-11 16:57:41 +0000412
413 g_return_if_fail (FU_IS_PLUGIN (plugin));
414 g_return_if_fail (FU_IS_DEVICE (device));
415
Richard Hughes6ad951d2017-02-03 10:12:12 +0000416 /* already waiting for add */
417 helper = g_hash_table_lookup (priv->devices_delay, device);
418 if (helper != NULL) {
Richard Hughes3cca1c62017-07-21 13:38:20 +0100419 g_debug ("ignoring add-delay as device %s already pending",
420 fu_device_get_id (device));
Richard Hughes6ad951d2017-02-03 10:12:12 +0000421 return;
422 }
423
424 /* add after a small delay */
Richard Hughesae3d65f2016-12-16 09:38:01 +0000425 g_debug ("waiting a small time for other plugins");
426 helper = g_new0 (FuPluginHelper, 1);
427 helper->plugin = g_object_ref (plugin);
428 helper->device = g_object_ref (device);
429 helper->timeout_id = g_timeout_add (500, fu_plugin_device_add_delay_cb, helper);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000430 helper->devices = g_hash_table_ref (priv->devices_delay);
431 g_hash_table_insert (helper->devices, device, helper);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000432}
433
Richard Hughes57d18222017-01-10 16:02:59 +0000434/**
435 * fu_plugin_device_add:
436 * @plugin: A #FuPlugin
437 * @device: A #FuDevice
438 *
439 * Asks the daemon to remove a device from the exported list.
440 *
441 * Since: 0.8.0
442 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000443void
444fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device)
445{
Richard Hughesae3d65f2016-12-16 09:38:01 +0000446 FuPluginPrivate *priv = GET_PRIVATE (plugin);
447 FuPluginHelper *helper;
448
Richard Hughesccd78a92017-01-11 16:57:41 +0000449 g_return_if_fail (FU_IS_PLUGIN (plugin));
450 g_return_if_fail (FU_IS_DEVICE (device));
451
Richard Hughesae3d65f2016-12-16 09:38:01 +0000452 /* waiting for add */
453 helper = g_hash_table_lookup (priv->devices_delay, device);
454 if (helper != NULL) {
455 g_debug ("ignoring remove from delayed addition");
456 g_source_remove (helper->timeout_id);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000457 g_hash_table_remove (priv->devices_delay, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000458 fu_plugin_helper_free (helper);
459 return;
460 }
461
Richard Hughescff38bc2016-12-12 12:03:37 +0000462 g_debug ("emit removed from %s: %s",
463 fu_plugin_get_name (plugin),
464 fu_device_get_id (device));
465 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device);
466}
467
Richard Hughes57d18222017-01-10 16:02:59 +0000468/**
469 * fu_plugin_set_status:
470 * @plugin: A #FuPlugin
471 * @status: A #FwupdStatus, e.g. #FWUPD_STATUS_DECOMPRESSING
472 *
473 * Sets the global state of the daemon according to the current plugin action.
474 *
475 * Since: 0.8.0
476 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000477void
478fu_plugin_set_status (FuPlugin *plugin, FwupdStatus status)
479{
Richard Hughesccd78a92017-01-11 16:57:41 +0000480 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000481 g_signal_emit (plugin, signals[SIGNAL_STATUS_CHANGED], 0, status);
482}
483
Richard Hughes57d18222017-01-10 16:02:59 +0000484/**
485 * fu_plugin_set_percentage:
486 * @plugin: A #FuPlugin
487 * @percentage: the percentage complete
488 *
489 * Sets the global completion of the daemon according to the current plugin
490 * action.
491 *
492 * Since: 0.8.0
493 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000494void
495fu_plugin_set_percentage (FuPlugin *plugin, guint percentage)
496{
Richard Hughesccd78a92017-01-11 16:57:41 +0000497 g_return_if_fail (FU_IS_PLUGIN (plugin));
498 g_return_if_fail (percentage <= 100);
Richard Hughescff38bc2016-12-12 12:03:37 +0000499 g_signal_emit (plugin, signals[SIGNAL_PERCENTAGE_CHANGED], 0,
500 percentage);
Richard Hughesd0905142016-03-13 09:46:49 +0000501}
502
Richard Hughes362d6d72017-01-07 21:42:14 +0000503/**
504 * fu_plugin_recoldplug:
505 * @plugin: A #FuPlugin
506 *
507 * Ask all the plugins to coldplug all devices, which will include the prepare()
508 * and cleanup() phases. Duplicate devices added will be ignored.
509 *
510 * Since: 0.8.0
511 **/
512void
513fu_plugin_recoldplug (FuPlugin *plugin)
514{
Richard Hughesccd78a92017-01-11 16:57:41 +0000515 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughes362d6d72017-01-07 21:42:14 +0000516 g_signal_emit (plugin, signals[SIGNAL_RECOLDPLUG], 0);
517}
518
Richard Hughesb0829032017-01-10 09:27:08 +0000519/**
Richard Hughesb8f8db22017-04-25 15:56:00 +0100520 * fu_plugin_check_hwid:
521 * @plugin: A #FuPlugin
522 * @hwid: A Hardware ID GUID, e.g. "6de5d951-d755-576b-bd09-c5cf66b27234"
523 *
Richard Hughesd7704d42017-08-08 20:29:09 +0100524 * Checks to see if a specific GUID exists. All hardware IDs on a
Richard Hughesb8f8db22017-04-25 15:56:00 +0100525 * specific system can be shown using the `fwupdmgr hwids` command.
526 *
527 * Since: 0.9.1
528 **/
529gboolean
530fu_plugin_check_hwid (FuPlugin *plugin, const gchar *hwid)
531{
532 FuPluginPrivate *priv = GET_PRIVATE (plugin);
533 if (priv->hwids == NULL)
534 return FALSE;
Richard Hughesd7704d42017-08-08 20:29:09 +0100535 return fu_hwids_has_guid (priv->hwids, hwid);
536}
537
538/**
539 * fu_plugin_get_dmi_value:
540 * @plugin: A #FuPlugin
541 * @dmi_id: A DMI ID, e.g. "BiosVersion"
542 *
543 * Gets a hardware DMI value.
544 *
545 * Since: 0.9.7
546 **/
547const gchar *
548fu_plugin_get_dmi_value (FuPlugin *plugin, const gchar *dmi_id)
549{
550 FuPluginPrivate *priv = GET_PRIVATE (plugin);
551 if (priv->hwids == NULL)
Richard Hughes7ef96b82017-08-23 18:28:24 +0100552 return NULL;
Richard Hughesd7704d42017-08-08 20:29:09 +0100553 return fu_hwids_get_value (priv->hwids, dmi_id);
Richard Hughesb8f8db22017-04-25 15:56:00 +0100554}
555
Richard Hughes49e5e052017-09-03 12:15:41 +0100556/**
557 * fu_plugin_get_smbios_string:
558 * @plugin: A #FuPlugin
559 * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
560 * @offset: A SMBIOS offset
561 *
562 * Gets a hardware SMBIOS string.
563 *
564 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
565 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
566 *
567 * Since: 0.9.8
568 **/
569const gchar *
570fu_plugin_get_smbios_string (FuPlugin *plugin, guint8 structure_type, guint8 offset)
571{
572 FuPluginPrivate *priv = GET_PRIVATE (plugin);
573 if (priv->smbios == NULL)
574 return NULL;
575 return fu_smbios_get_string (priv->smbios, structure_type, offset, NULL);
576}
577
578/**
579 * fu_plugin_get_smbios_data:
580 * @plugin: A #FuPlugin
581 * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
582 *
583 * Gets a hardware SMBIOS data.
584 *
585 * Since: 0.9.8
586 **/
587GBytes *
588fu_plugin_get_smbios_data (FuPlugin *plugin, guint8 structure_type)
589{
590 FuPluginPrivate *priv = GET_PRIVATE (plugin);
591 if (priv->smbios == NULL)
592 return NULL;
593 return fu_smbios_get_data (priv->smbios, structure_type, NULL);
594}
595
Richard Hughesb8f8db22017-04-25 15:56:00 +0100596void
Richard Hughesd7704d42017-08-08 20:29:09 +0100597fu_plugin_set_hwids (FuPlugin *plugin, FuHwids *hwids)
Richard Hughesb8f8db22017-04-25 15:56:00 +0100598{
599 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesd7704d42017-08-08 20:29:09 +0100600 g_set_object (&priv->hwids, hwids);
Richard Hughesb8f8db22017-04-25 15:56:00 +0100601}
602
Richard Hughes49e5e052017-09-03 12:15:41 +0100603void
604fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios)
605{
606 FuPluginPrivate *priv = GET_PRIVATE (plugin);
607 g_set_object (&priv->smbios, smbios);
608}
609
Richard Hughesb8f8db22017-04-25 15:56:00 +0100610/**
Richard Hughesb0829032017-01-10 09:27:08 +0000611 * fu_plugin_set_coldplug_delay:
612 * @plugin: A #FuPlugin
613 * @duration: A delay in milliseconds
614 *
615 * Set the minimum time that should be waited inbetween the call to
616 * fu_plugin_coldplug_prepare() and fu_plugin_coldplug(). This is usually going
617 * to be the minimum hardware initialisation time from a datasheet.
618 *
619 * It is better to use this function rather than using a sleep() in the plugin
620 * itself as then only one delay is done in the daemon rather than waiting for
621 * each coldplug prepare in a serial way.
622 *
623 * Additionally, very long delays should be avoided as the daemon will be
624 * blocked from processing requests whilst the coldplug delay is being
625 * performed.
626 *
627 * Since: 0.8.0
628 **/
629void
630fu_plugin_set_coldplug_delay (FuPlugin *plugin, guint duration)
631{
632 g_return_if_fail (FU_IS_PLUGIN (plugin));
633 g_return_if_fail (duration > 0);
634
635 /* check sanity */
636 if (duration > FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM) {
637 g_warning ("duration of %ums is crazy, truncating to %ums",
638 duration,
639 FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM);
640 duration = FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM;
641 }
642
643 /* emit */
644 g_signal_emit (plugin, signals[SIGNAL_SET_COLDPLUG_DELAY], 0, duration);
645}
646
Richard Hughesd0905142016-03-13 09:46:49 +0000647gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000648fu_plugin_runner_startup (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000649{
Richard Hughescff38bc2016-12-12 12:03:37 +0000650 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000651 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000652
653 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000654 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000655 return TRUE;
656
657 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000658 g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func);
659 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000660 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000661 g_debug ("performing startup() on %s", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000662 if (!func (plugin, error)) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000663 g_prefix_error (error, "failed to startup %s: ", priv->name);
664 return FALSE;
665 }
666 return TRUE;
667}
668
669static gboolean
670fu_plugin_runner_offline_invalidate (GError **error)
671{
672 g_autoptr(GError) error_local = NULL;
673 g_autoptr(GFile) file1 = NULL;
674
675 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
676
677 file1 = g_file_new_for_path (FU_OFFLINE_TRIGGER_FILENAME);
678 if (!g_file_query_exists (file1, NULL))
679 return TRUE;
680 if (!g_file_delete (file1, NULL, &error_local)) {
681 g_set_error (error,
682 FWUPD_ERROR,
683 FWUPD_ERROR_INTERNAL,
684 "Cannot delete %s: %s",
685 FU_OFFLINE_TRIGGER_FILENAME,
686 error_local->message);
687 return FALSE;
688 }
689 return TRUE;
690}
691
692static gboolean
693fu_plugin_runner_offline_setup (GError **error)
694{
695 gint rc;
696
697 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
698
699 /* create symlink for the systemd-system-update-generator */
700 rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME);
701 if (rc < 0) {
702 g_set_error (error,
703 FWUPD_ERROR,
704 FWUPD_ERROR_INTERNAL,
705 "Failed to create symlink %s to %s: %s",
706 FU_OFFLINE_TRIGGER_FILENAME,
707 "/var/lib", strerror (errno));
Richard Hughesd0905142016-03-13 09:46:49 +0000708 return FALSE;
709 }
710 return TRUE;
711}
712
Richard Hughesd0905142016-03-13 09:46:49 +0000713gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000714fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000715{
Richard Hughescff38bc2016-12-12 12:03:37 +0000716 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000717 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000718
719 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000720 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000721 return TRUE;
722
723 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000724 g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func);
725 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000726 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000727 g_debug ("performing coldplug() on %s", priv->name);
728 if (!func (plugin, error)) {
729 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
730 return FALSE;
731 }
732 return TRUE;
733}
734
Richard Hughes7b8b2022016-12-12 16:15:03 +0000735gboolean
Richard Hughes46487c92017-01-07 21:26:34 +0000736fu_plugin_runner_coldplug_prepare (FuPlugin *plugin, GError **error)
737{
738 FuPluginPrivate *priv = GET_PRIVATE (plugin);
739 FuPluginStartupFunc func = NULL;
740
741 /* not enabled */
742 if (!priv->enabled)
743 return TRUE;
744
745 /* optional */
746 g_module_symbol (priv->module, "fu_plugin_coldplug_prepare", (gpointer *) &func);
747 if (func == NULL)
748 return TRUE;
749 g_debug ("performing coldplug_prepare() on %s", priv->name);
750 if (!func (plugin, error)) {
751 g_prefix_error (error, "failed to prepare for coldplug %s: ", priv->name);
752 return FALSE;
753 }
754 return TRUE;
755}
756
757gboolean
758fu_plugin_runner_coldplug_cleanup (FuPlugin *plugin, GError **error)
759{
760 FuPluginPrivate *priv = GET_PRIVATE (plugin);
761 FuPluginStartupFunc func = NULL;
762
763 /* not enabled */
764 if (!priv->enabled)
765 return TRUE;
766
767 /* optional */
768 g_module_symbol (priv->module, "fu_plugin_coldplug_cleanup", (gpointer *) &func);
769 if (func == NULL)
770 return TRUE;
771 g_debug ("performing coldplug_cleanup() on %s", priv->name);
772 if (!func (plugin, error)) {
773 g_prefix_error (error, "failed to cleanup coldplug %s: ", priv->name);
774 return FALSE;
775 }
776 return TRUE;
777}
778
779gboolean
Richard Hughes7b8b2022016-12-12 16:15:03 +0000780fu_plugin_runner_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
781{
782 FuPluginPrivate *priv = GET_PRIVATE (plugin);
783 FuPluginDeviceFunc func = NULL;
784
785 /* not enabled */
786 if (!priv->enabled)
787 return TRUE;
788
789 /* optional */
790 g_module_symbol (priv->module, "fu_plugin_update_prepare", (gpointer *) &func);
791 if (func == NULL)
792 return TRUE;
793 g_debug ("performing update_prepare() on %s", priv->name);
794 if (!func (plugin, device, error)) {
795 g_prefix_error (error, "failed to prepare for update %s: ", priv->name);
796 return FALSE;
797 }
798 return TRUE;
799}
800
801gboolean
802fu_plugin_runner_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
803{
804 FuPluginPrivate *priv = GET_PRIVATE (plugin);
805 FuPluginDeviceFunc func = NULL;
806
807 /* not enabled */
808 if (!priv->enabled)
809 return TRUE;
810
811 /* optional */
812 g_module_symbol (priv->module, "fu_plugin_update_cleanup", (gpointer *) &func);
813 if (func == NULL)
814 return TRUE;
815 g_debug ("performing update_cleanup() on %s", priv->name);
816 if (!func (plugin, device, error)) {
817 g_prefix_error (error, "failed to cleanup update %s: ", priv->name);
818 return FALSE;
819 }
820 return TRUE;
821}
822
Richard Hughese1fd34d2017-08-24 14:19:51 +0100823void
824fu_plugin_runner_device_register (FuPlugin *plugin, FuDevice *device)
825{
826 FuPluginPrivate *priv = GET_PRIVATE (plugin);
827 FuPluginDeviceRegisterFunc func = NULL;
828
829 /* not enabled */
830 if (!priv->enabled)
831 return;
832
833 /* optional */
834 g_module_symbol (priv->module, "fu_plugin_device_registered", (gpointer *) &func);
835 if (func != NULL) {
836 g_debug ("performing device_added() on %s", priv->name);
837 func (plugin, device);
838 }
839}
840
Richard Hughescff38bc2016-12-12 12:03:37 +0000841static gboolean
842fu_plugin_runner_schedule_update (FuPlugin *plugin,
843 FuDevice *device,
844 GBytes *blob_cab,
845 GError **error)
846{
847 gchar tmpname[] = {"XXXXXX.cap"};
848 g_autofree gchar *dirname = NULL;
849 g_autofree gchar *filename = NULL;
Richard Hughes68982c62017-09-13 15:40:14 +0100850 g_autoptr(FuDevice) res_tmp = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000851 g_autoptr(FuPending) pending = NULL;
852 g_autoptr(GFile) file = NULL;
853
854 /* id already exists */
855 pending = fu_pending_new ();
856 res_tmp = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
857 if (res_tmp != NULL) {
858 g_set_error (error,
859 FWUPD_ERROR,
860 FWUPD_ERROR_ALREADY_PENDING,
861 "%s is already scheduled to be updated",
862 fu_device_get_id (device));
863 return FALSE;
864 }
865
866 /* create directory */
867 dirname = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
868 file = g_file_new_for_path (dirname);
869 if (!g_file_query_exists (file, NULL)) {
870 if (!g_file_make_directory_with_parents (file, NULL, error))
871 return FALSE;
872 }
873
874 /* get a random filename */
875 for (guint i = 0; i < 6; i++)
876 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
877 filename = g_build_filename (dirname, tmpname, NULL);
878
879 /* just copy to the temp file */
880 fu_plugin_set_status (plugin, FWUPD_STATUS_SCHEDULING);
881 if (!g_file_set_contents (filename,
882 g_bytes_get_data (blob_cab, NULL),
883 (gssize) g_bytes_get_size (blob_cab),
884 error))
885 return FALSE;
886
887 /* schedule for next boot */
888 g_debug ("schedule %s to be installed to %s on next boot",
889 filename, fu_device_get_id (device));
Richard Hughescc3de2e2017-09-13 19:28:17 +0100890 fu_device_set_filename_pending (device, filename);
Richard Hughescff38bc2016-12-12 12:03:37 +0000891
892 /* add to database */
Richard Hughes68982c62017-09-13 15:40:14 +0100893 if (!fu_pending_add_device (pending, device, error))
Richard Hughescff38bc2016-12-12 12:03:37 +0000894 return FALSE;
895
896 /* next boot we run offline */
897 return fu_plugin_runner_offline_setup (error);
898}
899
900gboolean
901fu_plugin_runner_verify (FuPlugin *plugin,
902 FuDevice *device,
903 FuPluginVerifyFlags flags,
904 GError **error)
905{
906 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000907 FuPluginVerifyFunc func = NULL;
Richard Hughesababbb72017-06-15 20:18:36 +0100908 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000909
910 /* not enabled */
911 if (!priv->enabled)
912 return TRUE;
913
Richard Hughesababbb72017-06-15 20:18:36 +0100914 /* clear any existing verification checksums */
915 checksums = fu_device_get_checksums (device);
916 g_ptr_array_set_size (checksums, 0);
917
Richard Hughescff38bc2016-12-12 12:03:37 +0000918 /* optional */
919 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
920 if (func == NULL)
921 return TRUE;
922 g_debug ("performing verify() on %s", priv->name);
923 if (!func (plugin, device, flags, error)) {
924 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000925 return FALSE;
926 }
927 return TRUE;
928}
929
Richard Hughesd0905142016-03-13 09:46:49 +0000930gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000931fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000932{
Richard Hughescff38bc2016-12-12 12:03:37 +0000933 guint64 flags;
934 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000935 FuPluginDeviceFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000936
937 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000938 if (!priv->enabled)
939 return TRUE;
940
941 /* final check */
942 flags = fu_device_get_flags (device);
943 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
944 g_set_error (error,
945 FWUPD_ERROR,
946 FWUPD_ERROR_NOT_SUPPORTED,
947 "Device %s is not locked",
948 fu_device_get_id (device));
949 return FALSE;
950 }
951
952 /* optional */
953 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
954 if (func != NULL) {
955 g_debug ("performing unlock() on %s", priv->name);
956 if (!func (plugin, device, error)) {
Richard Hughes7b8b2022016-12-12 16:15:03 +0000957 g_prefix_error (error, "failed to unlock %s: ", priv->name);
Richard Hughescff38bc2016-12-12 12:03:37 +0000958 return FALSE;
959 }
960 }
961
962 /* update with correct flags */
963 flags = fu_device_get_flags (device);
964 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
965 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
966 return TRUE;
967}
968
969gboolean
970fu_plugin_runner_update (FuPlugin *plugin,
Richard Hughesa785a1c2017-08-25 16:00:58 +0100971 FuDevice *device,
972 GBytes *blob_cab,
973 GBytes *blob_fw,
974 FwupdInstallFlags flags,
975 GError **error)
Richard Hughescff38bc2016-12-12 12:03:37 +0000976{
977 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesa785a1c2017-08-25 16:00:58 +0100978 FuPluginUpdateFunc update_func;
Richard Hughescff38bc2016-12-12 12:03:37 +0000979 g_autoptr(FuPending) pending = NULL;
Richard Hughes68982c62017-09-13 15:40:14 +0100980 g_autoptr(FuDevice) device_pending = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000981 GError *error_update = NULL;
Richard Hughesf556d372017-06-15 19:49:18 +0100982 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000983
984 /* not enabled */
985 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000986 return TRUE;
987
988 /* optional */
Richard Hughesa785a1c2017-08-25 16:00:58 +0100989 g_module_symbol (priv->module, "fu_plugin_update", (gpointer *) &update_func);
990 if (update_func == NULL) {
991 g_set_error_literal (error,
992 FWUPD_ERROR,
993 FWUPD_ERROR_NOT_SUPPORTED,
994 "No update possible");
995 return FALSE;
996 }
Richard Hughesd0905142016-03-13 09:46:49 +0000997
Richard Hughesa785a1c2017-08-25 16:00:58 +0100998 /* just schedule this for the next reboot */
Richard Hughescff38bc2016-12-12 12:03:37 +0000999 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
Richard Hughesa785a1c2017-08-25 16:00:58 +01001000 return fu_plugin_runner_schedule_update (plugin,
1001 device,
1002 blob_cab,
1003 error);
Richard Hughescff38bc2016-12-12 12:03:37 +00001004 }
1005
1006 /* cancel the pending action */
1007 if (!fu_plugin_runner_offline_invalidate (error))
1008 return FALSE;
1009
1010 /* online */
Richard Hughescff38bc2016-12-12 12:03:37 +00001011 pending = fu_pending_new ();
Richard Hughes68982c62017-09-13 15:40:14 +01001012 device_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
Richard Hughesa785a1c2017-08-25 16:00:58 +01001013 if (!update_func (plugin, device, blob_fw, flags, &error_update)) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001014 /* save the error to the database */
Richard Hughes68982c62017-09-13 15:40:14 +01001015 if (device_pending != NULL) {
1016 fu_pending_set_error_msg (pending, device,
Richard Hughescff38bc2016-12-12 12:03:37 +00001017 error_update->message, NULL);
1018 }
1019 g_propagate_error (error, error_update);
1020 return FALSE;
1021 }
1022
Richard Hughesf556d372017-06-15 19:49:18 +01001023 /* no longer valid */
1024 checksums = fu_device_get_checksums (device);
1025 g_ptr_array_set_size (checksums, 0);
1026
Richard Hughescff38bc2016-12-12 12:03:37 +00001027 /* cleanup */
Richard Hughes68982c62017-09-13 15:40:14 +01001028 if (device_pending != NULL) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001029 const gchar *tmp;
1030
1031 /* update pending database */
Richard Hughes68982c62017-09-13 15:40:14 +01001032 fu_pending_set_state (pending, device,
Richard Hughescff38bc2016-12-12 12:03:37 +00001033 FWUPD_UPDATE_STATE_SUCCESS, NULL);
1034
1035 /* delete cab file */
Richard Hughescc3de2e2017-09-13 19:28:17 +01001036 tmp = fu_device_get_filename_pending (device_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001037 if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) {
1038 g_autoptr(GError) error_local = NULL;
1039 g_autoptr(GFile) file = NULL;
1040 file = g_file_new_for_path (tmp);
1041 if (!g_file_delete (file, NULL, &error_local)) {
1042 g_set_error (error,
1043 FWUPD_ERROR,
1044 FWUPD_ERROR_INVALID_FILE,
1045 "Failed to delete %s: %s",
1046 tmp, error_local->message);
1047 return FALSE;
1048 }
1049 }
1050 }
Richard Hughesd0905142016-03-13 09:46:49 +00001051 return TRUE;
1052}
Richard Hughescff38bc2016-12-12 12:03:37 +00001053
1054gboolean
1055fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error)
1056{
1057 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +00001058 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00001059 g_autoptr(GError) error_local = NULL;
Richard Hughes68982c62017-09-13 15:40:14 +01001060 g_autoptr(FuDevice) device_pending = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00001061 g_autoptr(FuPending) pending = NULL;
1062
1063 /* not enabled */
1064 if (!priv->enabled)
1065 return TRUE;
1066
1067 /* use the plugin if the vfunc is provided */
1068 g_module_symbol (priv->module, "fu_plugin_clear_result", (gpointer *) &func);
1069 if (func != NULL) {
1070 g_debug ("performing clear_result() on %s", priv->name);
1071 if (!func (plugin, device, error)) {
1072 g_prefix_error (error, "failed to clear_result %s: ", priv->name);
1073 return FALSE;
1074 }
1075 return TRUE;
1076 }
1077
1078 /* handled using the database */
1079 pending = fu_pending_new ();
Richard Hughes68982c62017-09-13 15:40:14 +01001080 device_pending = fu_pending_get_device (pending,
Richard Hughescff38bc2016-12-12 12:03:37 +00001081 fu_device_get_id (device),
1082 &error_local);
Richard Hughes68982c62017-09-13 15:40:14 +01001083 if (device_pending == NULL) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001084 g_set_error (error,
1085 FWUPD_ERROR,
1086 FWUPD_ERROR_INVALID_FILE,
1087 "Failed to find %s in pending database: %s",
1088 fu_device_get_id (device),
1089 error_local->message);
1090 return FALSE;
1091 }
1092
1093 /* remove from pending database */
Richard Hughes68982c62017-09-13 15:40:14 +01001094 return fu_pending_remove_device (pending, device, error);
Richard Hughescff38bc2016-12-12 12:03:37 +00001095}
1096
1097gboolean
1098fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error)
1099{
1100 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +00001101 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00001102 FwupdUpdateState update_state;
1103 const gchar *tmp;
1104 g_autoptr(GError) error_local = NULL;
Richard Hughes68982c62017-09-13 15:40:14 +01001105 g_autoptr(FuDevice) device_pending = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +00001106 g_autoptr(FuPending) pending = NULL;
1107
1108 /* not enabled */
1109 if (!priv->enabled)
1110 return TRUE;
1111
1112 /* use the plugin if the vfunc is provided */
1113 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
1114 if (func != NULL) {
1115 g_debug ("performing get_results() on %s", priv->name);
1116 if (!func (plugin, device, error)) {
1117 g_prefix_error (error, "failed to get_results %s: ", priv->name);
1118 return FALSE;
1119 }
1120 return TRUE;
1121 }
1122
1123 /* handled using the database */
1124 pending = fu_pending_new ();
Richard Hughes68982c62017-09-13 15:40:14 +01001125 device_pending = fu_pending_get_device (pending,
Richard Hughescff38bc2016-12-12 12:03:37 +00001126 fu_device_get_id (device),
1127 &error_local);
Richard Hughes68982c62017-09-13 15:40:14 +01001128 if (device_pending == NULL) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001129 g_set_error (error,
1130 FWUPD_ERROR,
1131 FWUPD_ERROR_NOTHING_TO_DO,
1132 "Failed to find %s in pending database: %s",
1133 fu_device_get_id (device),
1134 error_local->message);
1135 return FALSE;
1136 }
1137
1138 /* copy the important parts from the pending device to the real one */
Richard Hughescc3de2e2017-09-13 19:28:17 +01001139 update_state = fu_device_get_update_state (device_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001140 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
1141 update_state == FWUPD_UPDATE_STATE_PENDING) {
1142 g_set_error (error,
1143 FWUPD_ERROR,
1144 FWUPD_ERROR_NOTHING_TO_DO,
1145 "Device %s has not been updated offline yet",
1146 fu_device_get_id (device));
1147 return FALSE;
1148 }
1149
1150 /* copy */
1151 fu_device_set_update_state (device, update_state);
Richard Hughescc3de2e2017-09-13 19:28:17 +01001152 tmp = fu_device_get_update_error (device_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001153 if (tmp != NULL)
1154 fu_device_set_update_error (device, tmp);
Richard Hughescc3de2e2017-09-13 19:28:17 +01001155 tmp = fu_device_get_version (device_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001156 if (tmp != NULL)
1157 fu_device_set_version (device, tmp);
Richard Hughescc3de2e2017-09-13 19:28:17 +01001158 tmp = fu_device_get_version_new (device_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001159 if (tmp != NULL)
Richard Hughescc3de2e2017-09-13 19:28:17 +01001160 fu_device_set_version_new (device, tmp);
Richard Hughescff38bc2016-12-12 12:03:37 +00001161 return TRUE;
1162}
1163
1164static void
1165fu_plugin_class_init (FuPluginClass *klass)
1166{
1167 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1168 object_class->finalize = fu_plugin_finalize;
1169 signals[SIGNAL_DEVICE_ADDED] =
1170 g_signal_new ("device-added",
1171 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1172 G_STRUCT_OFFSET (FuPluginClass, device_added),
1173 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1174 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1175 signals[SIGNAL_DEVICE_REMOVED] =
1176 g_signal_new ("device-removed",
1177 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1178 G_STRUCT_OFFSET (FuPluginClass, device_removed),
1179 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1180 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughese1fd34d2017-08-24 14:19:51 +01001181 signals[SIGNAL_DEVICE_REGISTER] =
1182 g_signal_new ("device-register",
1183 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1184 G_STRUCT_OFFSET (FuPluginClass, device_register),
1185 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1186 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughescff38bc2016-12-12 12:03:37 +00001187 signals[SIGNAL_STATUS_CHANGED] =
1188 g_signal_new ("status-changed",
1189 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1190 G_STRUCT_OFFSET (FuPluginClass, status_changed),
1191 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1192 G_TYPE_NONE, 1, G_TYPE_UINT);
1193 signals[SIGNAL_PERCENTAGE_CHANGED] =
1194 g_signal_new ("percentage-changed",
1195 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1196 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
1197 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1198 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughes362d6d72017-01-07 21:42:14 +00001199 signals[SIGNAL_RECOLDPLUG] =
1200 g_signal_new ("recoldplug",
1201 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1202 G_STRUCT_OFFSET (FuPluginClass, recoldplug),
1203 NULL, NULL, g_cclosure_marshal_VOID__VOID,
1204 G_TYPE_NONE, 0);
Richard Hughesb0829032017-01-10 09:27:08 +00001205 signals[SIGNAL_SET_COLDPLUG_DELAY] =
1206 g_signal_new ("set-coldplug-delay",
1207 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1208 G_STRUCT_OFFSET (FuPluginClass, set_coldplug_delay),
1209 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1210 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughescff38bc2016-12-12 12:03:37 +00001211}
1212
1213static void
1214fu_plugin_init (FuPlugin *plugin)
1215{
1216 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1217 priv->enabled = TRUE;
1218 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
1219 g_free, (GDestroyNotify) g_object_unref);
Richard Hughese8b5db62017-07-17 14:18:22 +01001220 priv->devices_delay = g_hash_table_new (g_direct_hash, g_direct_equal);
Richard Hughescff38bc2016-12-12 12:03:37 +00001221}
1222
1223static void
1224fu_plugin_finalize (GObject *object)
1225{
1226 FuPlugin *plugin = FU_PLUGIN (object);
1227 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1228 FuPluginInitFunc func = NULL;
1229
1230 /* optional */
1231 if (priv->module != NULL) {
1232 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
1233 if (func != NULL) {
1234 g_debug ("performing destroy() on %s", priv->name);
1235 func (plugin);
1236 }
1237 }
1238
1239 if (priv->usb_ctx != NULL)
1240 g_object_unref (priv->usb_ctx);
Richard Hughesb8f8db22017-04-25 15:56:00 +01001241 if (priv->hwids != NULL)
Richard Hughesd7704d42017-08-08 20:29:09 +01001242 g_object_unref (priv->hwids);
Richard Hughes49e5e052017-09-03 12:15:41 +01001243 if (priv->smbios != NULL)
1244 g_object_unref (priv->smbios);
Richard Hughes576c0122017-02-24 09:47:00 +00001245#ifndef RUNNING_ON_VALGRIND
Richard Hughescff38bc2016-12-12 12:03:37 +00001246 if (priv->module != NULL)
1247 g_module_close (priv->module);
Richard Hughes576c0122017-02-24 09:47:00 +00001248#endif
Richard Hughescff38bc2016-12-12 12:03:37 +00001249 g_hash_table_unref (priv->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001250 g_hash_table_unref (priv->devices_delay);
Richard Hughescff38bc2016-12-12 12:03:37 +00001251 g_free (priv->name);
1252 g_free (priv->data);
1253
1254 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
1255}
1256
1257FuPlugin *
1258fu_plugin_new (void)
1259{
1260 FuPlugin *plugin;
1261 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
1262 return plugin;
1263}