blob: 2d68c4b73c0575ad9889371b2c498d1f5450926c [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 Hughescff38bc2016-12-12 12:03:37 +000034#include "fu-plugin-private.h"
35#include "fu-pending.h"
Richard Hughesd0905142016-03-13 09:46:49 +000036
Richard Hughesb0829032017-01-10 09:27:08 +000037#define FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM 3000u /* ms */
38
Richard Hughescff38bc2016-12-12 12:03:37 +000039static void fu_plugin_finalize (GObject *object);
40
41typedef struct {
42 GModule *module;
43 GUsbContext *usb_ctx;
44 gboolean enabled;
45 gchar *name;
Richard Hughesd7704d42017-08-08 20:29:09 +010046 FuHwids *hwids;
Richard Hughes49e5e052017-09-03 12:15:41 +010047 FuSmbios *smbios;
Richard Hughescff38bc2016-12-12 12:03:37 +000048 GHashTable *devices; /* platform_id:GObject */
Richard Hughesae3d65f2016-12-16 09:38:01 +000049 GHashTable *devices_delay; /* FuDevice:FuPluginHelper */
Richard Hughescff38bc2016-12-12 12:03:37 +000050 FuPluginData *data;
51} FuPluginPrivate;
52
53enum {
54 SIGNAL_DEVICE_ADDED,
55 SIGNAL_DEVICE_REMOVED,
Richard Hughese1fd34d2017-08-24 14:19:51 +010056 SIGNAL_DEVICE_REGISTER,
Richard Hughescff38bc2016-12-12 12:03:37 +000057 SIGNAL_STATUS_CHANGED,
58 SIGNAL_PERCENTAGE_CHANGED,
Richard Hughes362d6d72017-01-07 21:42:14 +000059 SIGNAL_RECOLDPLUG,
Richard Hughesb0829032017-01-10 09:27:08 +000060 SIGNAL_SET_COLDPLUG_DELAY,
Richard Hughescff38bc2016-12-12 12:03:37 +000061 SIGNAL_LAST
62};
63
64static guint signals[SIGNAL_LAST] = { 0 };
65
66G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT)
67#define GET_PRIVATE(o) (fu_plugin_get_instance_private (o))
68
69typedef const gchar *(*FuPluginGetNameFunc) (void);
70typedef void (*FuPluginInitFunc) (FuPlugin *plugin);
71typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin,
72 GError **error);
Richard Hughese1fd34d2017-08-24 14:19:51 +010073typedef void (*FuPluginDeviceRegisterFunc) (FuPlugin *plugin,
74 FuDevice *device);
Richard Hughescff38bc2016-12-12 12:03:37 +000075typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin,
76 FuDevice *device,
77 GError **error);
78typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin,
79 FuDevice *device,
80 FuPluginVerifyFlags flags,
81 GError **error);
82typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin,
83 FuDevice *device,
84 GBytes *blob_fw,
85 FwupdInstallFlags flags,
86 GError **error);
87
Richard Hughes57d18222017-01-10 16:02:59 +000088/**
89 * fu_plugin_get_name:
90 * @plugin: A #FuPlugin
91 *
92 * Gets the plugin name.
93 *
94 * Returns: a plugin name, or %NULL for unknown.
95 *
96 * Since: 0.8.0
97 **/
Richard Hughescff38bc2016-12-12 12:03:37 +000098const gchar *
99fu_plugin_get_name (FuPlugin *plugin)
Richard Hughesd0905142016-03-13 09:46:49 +0000100{
Richard Hughescff38bc2016-12-12 12:03:37 +0000101 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000102 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000103 return priv->name;
104}
Richard Hughesd0905142016-03-13 09:46:49 +0000105
Richard Hughes57d18222017-01-10 16:02:59 +0000106/**
107 * fu_plugin_cache_lookup:
108 * @plugin: A #FuPlugin
109 * @id: the key
110 *
111 * Finds an object in the per-plugin cache.
112 *
113 * Returns: (transfer none): a #GObject, or %NULL for unfound.
114 *
115 * Since: 0.8.0
116 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000117gpointer
118fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id)
119{
120 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000121 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
122 g_return_val_if_fail (id != NULL, NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000123 return g_hash_table_lookup (priv->devices, id);
124}
Richard Hughesd0905142016-03-13 09:46:49 +0000125
Richard Hughes57d18222017-01-10 16:02:59 +0000126/**
127 * fu_plugin_cache_add:
128 * @plugin: A #FuPlugin
129 * @id: the key
130 * @dev: a #GObject, typically a #FuDevice
131 *
132 * Adds an object to the per-plugin cache.
133 *
134 * Since: 0.8.0
135 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000136void
137fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev)
138{
139 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000140 g_return_if_fail (FU_IS_PLUGIN (plugin));
141 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000142 g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev));
143}
144
Richard Hughes57d18222017-01-10 16:02:59 +0000145/**
146 * fu_plugin_cache_remove:
147 * @plugin: A #FuPlugin
148 * @id: the key
149 *
150 * Removes an object from the per-plugin cache.
151 *
152 * Since: 0.8.0
153 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000154void
155fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id)
156{
157 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000158 g_return_if_fail (FU_IS_PLUGIN (plugin));
159 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000160 g_hash_table_remove (priv->devices, id);
161}
162
Richard Hughes57d18222017-01-10 16:02:59 +0000163/**
164 * fu_plugin_get_data:
165 * @plugin: A #FuPlugin
166 *
167 * Gets the per-plugin allocated private data.
168 *
169 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
170 *
171 * Since: 0.8.0
172 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000173FuPluginData *
174fu_plugin_get_data (FuPlugin *plugin)
175{
176 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000177 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000178 return priv->data;
179}
180
Richard Hughes57d18222017-01-10 16:02:59 +0000181/**
182 * fu_plugin_alloc_data:
183 * @plugin: A #FuPlugin
184 * @data_sz: the size to allocate
185 *
186 * Allocates the per-plugin allocated private data.
187 *
188 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
189 *
190 * Since: 0.8.0
191 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000192FuPluginData *
193fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz)
194{
195 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000196 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughes44dee882017-01-11 08:31:10 +0000197 if (priv->data != NULL) {
198 g_critical ("fu_plugin_alloc_data() already used by plugin");
199 return priv->data;
200 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000201 priv->data = g_malloc0 (data_sz);
202 return priv->data;
Richard Hughesd0905142016-03-13 09:46:49 +0000203}
204
Richard Hughes57d18222017-01-10 16:02:59 +0000205/**
206 * fu_plugin_get_usb_context:
207 * @plugin: A #FuPlugin
208 *
209 * Gets the shared USB context that all plugins can use.
210 *
211 * Returns: (transfer none): a #GUsbContext.
212 *
213 * Since: 0.8.0
214 **/
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000215GUsbContext *
216fu_plugin_get_usb_context (FuPlugin *plugin)
217{
Richard Hughescff38bc2016-12-12 12:03:37 +0000218 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000219 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000220 return priv->usb_ctx;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000221}
222
223void
224fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx)
225{
Richard Hughescff38bc2016-12-12 12:03:37 +0000226 FuPluginPrivate *priv = GET_PRIVATE (plugin);
227 g_set_object (&priv->usb_ctx, usb_ctx);
228}
229
Richard Hughes57d18222017-01-10 16:02:59 +0000230/**
231 * fu_plugin_get_enabled:
232 * @plugin: A #FuPlugin
233 *
234 * Returns if the plugin is enabled.
235 *
236 * Returns: %TRUE if the plugin is currently enabled.
237 *
238 * Since: 0.8.0
239 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000240gboolean
241fu_plugin_get_enabled (FuPlugin *plugin)
242{
243 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000244 g_return_val_if_fail (FU_IS_PLUGIN (plugin), FALSE);
Richard Hughescff38bc2016-12-12 12:03:37 +0000245 return priv->enabled;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000246}
247
Richard Hughes57d18222017-01-10 16:02:59 +0000248/**
249 * fu_plugin_set_enabled:
250 * @plugin: A #FuPlugin
251 * @enabled: the enabled value
252 *
253 * Enables or disables a plugin. Plugins can self-disable at any point.
254 *
255 * Since: 0.8.0
256 **/
Richard Hughesd0905142016-03-13 09:46:49 +0000257void
Richard Hughescff38bc2016-12-12 12:03:37 +0000258fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000259{
Richard Hughescff38bc2016-12-12 12:03:37 +0000260 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000261 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000262 priv->enabled = enabled;
263}
264
265gboolean
266fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error)
267{
268 FuPluginPrivate *priv = GET_PRIVATE (plugin);
269 FuPluginInitFunc func = NULL;
270 gchar *str;
271
272 priv->module = g_module_open (filename, 0);
273 if (priv->module == NULL) {
274 g_set_error (error,
275 G_IO_ERROR,
276 G_IO_ERROR_FAILED,
277 "failed to open plugin: %s",
278 g_module_error ());
279 return FALSE;
280 }
281
282 /* set automatically */
283 str = g_strstr_len (filename, -1, "libfu_plugin_");
284 if (str != NULL) {
285 priv->name = g_strdup (str + 13);
286 g_strdelimit (priv->name, ".", '\0');
287 }
Richard Hughesd0905142016-03-13 09:46:49 +0000288
289 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000290 g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func);
291 if (func != NULL) {
292 g_debug ("performing init() on %s", filename);
Richard Hughesd0905142016-03-13 09:46:49 +0000293 func (plugin);
294 }
295
Richard Hughescff38bc2016-12-12 12:03:37 +0000296 return TRUE;
297}
298
Richard Hughes57d18222017-01-10 16:02:59 +0000299/**
300 * fu_plugin_device_add:
301 * @plugin: A #FuPlugin
302 * @device: A #FuDevice
303 *
304 * Asks the daemon to add a device to the exported list. If this device ID
305 * has already been added by a different plugin then this request will be
306 * ignored.
307 *
308 * Plugins should use fu_plugin_device_add_delay() if they are not capable of
309 * actually flashing an image to the hardware so that higher-priority plugins
310 * can add the device themselves.
311 *
312 * Since: 0.8.0
313 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000314void
315fu_plugin_device_add (FuPlugin *plugin, FuDevice *device)
316{
Richard Hughesccd78a92017-01-11 16:57:41 +0000317 g_return_if_fail (FU_IS_PLUGIN (plugin));
318 g_return_if_fail (FU_IS_DEVICE (device));
319
Richard Hughescff38bc2016-12-12 12:03:37 +0000320 g_debug ("emit added from %s: %s",
321 fu_plugin_get_name (plugin),
322 fu_device_get_id (device));
323 fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
324 fu_device_set_plugin (device, fu_plugin_get_name (plugin));
325 g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device);
326}
327
Richard Hughese1fd34d2017-08-24 14:19:51 +0100328/**
329 * fu_plugin_device_register:
330 * @plugin: A #FuPlugin
331 * @device: A #FuDevice
332 *
333 * Registers the device with other plugins so they can set metadata.
334 *
335 * Plugins do not have to call this manually as this is done automatically
336 * when using fu_plugin_device_add(). They may wish to use this manually
337 * if for intance the coldplug should be ignored based on the metadata
338 * set from other plugins.
339 *
340 * Since: 0.9.7
341 **/
342void
343fu_plugin_device_register (FuPlugin *plugin, FuDevice *device)
344{
345 g_return_if_fail (FU_IS_PLUGIN (plugin));
346 g_return_if_fail (FU_IS_DEVICE (device));
347
348 g_debug ("emit device-register from %s: %s",
349 fu_plugin_get_name (plugin),
350 fu_device_get_id (device));
351 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REGISTER], 0, device);
352}
353
Richard Hughesae3d65f2016-12-16 09:38:01 +0000354typedef struct {
355 FuPlugin *plugin;
356 FuDevice *device;
357 guint timeout_id;
Richard Hughesd4184cf2016-12-21 16:05:17 +0000358 GHashTable *devices;
Richard Hughesae3d65f2016-12-16 09:38:01 +0000359} FuPluginHelper;
360
361static void
362fu_plugin_helper_free (FuPluginHelper *helper)
363{
364 g_object_unref (helper->plugin);
365 g_object_unref (helper->device);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000366 g_hash_table_unref (helper->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000367 g_free (helper);
368}
369
370static gboolean
371fu_plugin_device_add_delay_cb (gpointer user_data)
372{
373 FuPluginHelper *helper = (FuPluginHelper *) user_data;
Richard Hughesf0a799e2017-01-17 20:13:30 +0000374 g_hash_table_remove (helper->devices, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000375 fu_plugin_device_add (helper->plugin, helper->device);
376 fu_plugin_helper_free (helper);
377 return FALSE;
378}
379
Richard Hughes57d18222017-01-10 16:02:59 +0000380/**
Richard Hughesf0a799e2017-01-17 20:13:30 +0000381 * fu_plugin_has_device_delay:
382 * @plugin: A #FuPlugin
383 *
384 * Returns if the device has a pending device that is waiting to be added.
385 *
386 * Returns: %TRUE if a device is waiting to be added
387 *
388 * Since: 0.8.0
389 **/
390gboolean
391fu_plugin_has_device_delay (FuPlugin *plugin)
392{
393 FuPluginPrivate *priv = GET_PRIVATE (plugin);
394 return g_hash_table_size (priv->devices_delay) > 0;
395}
396
397/**
Richard Hughes57d18222017-01-10 16:02:59 +0000398 * fu_plugin_device_add_delay:
399 * @plugin: A #FuPlugin
400 * @device: A #FuDevice
401 *
402 * Asks the daemon to add a device to the exported list after a small delay.
403 *
404 * Since: 0.8.0
405 **/
Richard Hughesae3d65f2016-12-16 09:38:01 +0000406void
407fu_plugin_device_add_delay (FuPlugin *plugin, FuDevice *device)
408{
409 FuPluginPrivate *priv = GET_PRIVATE (plugin);
410 FuPluginHelper *helper;
Richard Hughesccd78a92017-01-11 16:57:41 +0000411
412 g_return_if_fail (FU_IS_PLUGIN (plugin));
413 g_return_if_fail (FU_IS_DEVICE (device));
414
Richard Hughes6ad951d2017-02-03 10:12:12 +0000415 /* already waiting for add */
416 helper = g_hash_table_lookup (priv->devices_delay, device);
417 if (helper != NULL) {
Richard Hughes3cca1c62017-07-21 13:38:20 +0100418 g_debug ("ignoring add-delay as device %s already pending",
419 fu_device_get_id (device));
Richard Hughes6ad951d2017-02-03 10:12:12 +0000420 return;
421 }
422
423 /* add after a small delay */
Richard Hughesae3d65f2016-12-16 09:38:01 +0000424 g_debug ("waiting a small time for other plugins");
425 helper = g_new0 (FuPluginHelper, 1);
426 helper->plugin = g_object_ref (plugin);
427 helper->device = g_object_ref (device);
428 helper->timeout_id = g_timeout_add (500, fu_plugin_device_add_delay_cb, helper);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000429 helper->devices = g_hash_table_ref (priv->devices_delay);
430 g_hash_table_insert (helper->devices, device, helper);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000431}
432
Richard Hughes57d18222017-01-10 16:02:59 +0000433/**
434 * fu_plugin_device_add:
435 * @plugin: A #FuPlugin
436 * @device: A #FuDevice
437 *
438 * Asks the daemon to remove a device from the exported list.
439 *
440 * Since: 0.8.0
441 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000442void
443fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device)
444{
Richard Hughesae3d65f2016-12-16 09:38:01 +0000445 FuPluginPrivate *priv = GET_PRIVATE (plugin);
446 FuPluginHelper *helper;
447
Richard Hughesccd78a92017-01-11 16:57:41 +0000448 g_return_if_fail (FU_IS_PLUGIN (plugin));
449 g_return_if_fail (FU_IS_DEVICE (device));
450
Richard Hughesae3d65f2016-12-16 09:38:01 +0000451 /* waiting for add */
452 helper = g_hash_table_lookup (priv->devices_delay, device);
453 if (helper != NULL) {
454 g_debug ("ignoring remove from delayed addition");
455 g_source_remove (helper->timeout_id);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000456 g_hash_table_remove (priv->devices_delay, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000457 fu_plugin_helper_free (helper);
458 return;
459 }
460
Richard Hughescff38bc2016-12-12 12:03:37 +0000461 g_debug ("emit removed from %s: %s",
462 fu_plugin_get_name (plugin),
463 fu_device_get_id (device));
464 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device);
465}
466
Richard Hughes57d18222017-01-10 16:02:59 +0000467/**
468 * fu_plugin_set_status:
469 * @plugin: A #FuPlugin
470 * @status: A #FwupdStatus, e.g. #FWUPD_STATUS_DECOMPRESSING
471 *
472 * Sets the global state of the daemon according to the current plugin action.
473 *
474 * Since: 0.8.0
475 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000476void
477fu_plugin_set_status (FuPlugin *plugin, FwupdStatus status)
478{
Richard Hughesccd78a92017-01-11 16:57:41 +0000479 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000480 g_signal_emit (plugin, signals[SIGNAL_STATUS_CHANGED], 0, status);
481}
482
Richard Hughes57d18222017-01-10 16:02:59 +0000483/**
484 * fu_plugin_set_percentage:
485 * @plugin: A #FuPlugin
486 * @percentage: the percentage complete
487 *
488 * Sets the global completion of the daemon according to the current plugin
489 * action.
490 *
491 * Since: 0.8.0
492 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000493void
494fu_plugin_set_percentage (FuPlugin *plugin, guint percentage)
495{
Richard Hughesccd78a92017-01-11 16:57:41 +0000496 g_return_if_fail (FU_IS_PLUGIN (plugin));
497 g_return_if_fail (percentage <= 100);
Richard Hughescff38bc2016-12-12 12:03:37 +0000498 g_signal_emit (plugin, signals[SIGNAL_PERCENTAGE_CHANGED], 0,
499 percentage);
Richard Hughesd0905142016-03-13 09:46:49 +0000500}
501
Richard Hughes362d6d72017-01-07 21:42:14 +0000502/**
503 * fu_plugin_recoldplug:
504 * @plugin: A #FuPlugin
505 *
506 * Ask all the plugins to coldplug all devices, which will include the prepare()
507 * and cleanup() phases. Duplicate devices added will be ignored.
508 *
509 * Since: 0.8.0
510 **/
511void
512fu_plugin_recoldplug (FuPlugin *plugin)
513{
Richard Hughesccd78a92017-01-11 16:57:41 +0000514 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughes362d6d72017-01-07 21:42:14 +0000515 g_signal_emit (plugin, signals[SIGNAL_RECOLDPLUG], 0);
516}
517
Richard Hughesb0829032017-01-10 09:27:08 +0000518/**
Richard Hughesb8f8db22017-04-25 15:56:00 +0100519 * fu_plugin_check_hwid:
520 * @plugin: A #FuPlugin
521 * @hwid: A Hardware ID GUID, e.g. "6de5d951-d755-576b-bd09-c5cf66b27234"
522 *
Richard Hughesd7704d42017-08-08 20:29:09 +0100523 * Checks to see if a specific GUID exists. All hardware IDs on a
Richard Hughesb8f8db22017-04-25 15:56:00 +0100524 * specific system can be shown using the `fwupdmgr hwids` command.
525 *
526 * Since: 0.9.1
527 **/
528gboolean
529fu_plugin_check_hwid (FuPlugin *plugin, const gchar *hwid)
530{
531 FuPluginPrivate *priv = GET_PRIVATE (plugin);
532 if (priv->hwids == NULL)
533 return FALSE;
Richard Hughesd7704d42017-08-08 20:29:09 +0100534 return fu_hwids_has_guid (priv->hwids, hwid);
535}
536
537/**
538 * fu_plugin_get_dmi_value:
539 * @plugin: A #FuPlugin
540 * @dmi_id: A DMI ID, e.g. "BiosVersion"
541 *
542 * Gets a hardware DMI value.
543 *
544 * Since: 0.9.7
545 **/
546const gchar *
547fu_plugin_get_dmi_value (FuPlugin *plugin, const gchar *dmi_id)
548{
549 FuPluginPrivate *priv = GET_PRIVATE (plugin);
550 if (priv->hwids == NULL)
Richard Hughes7ef96b82017-08-23 18:28:24 +0100551 return NULL;
Richard Hughesd7704d42017-08-08 20:29:09 +0100552 return fu_hwids_get_value (priv->hwids, dmi_id);
Richard Hughesb8f8db22017-04-25 15:56:00 +0100553}
554
Richard Hughes49e5e052017-09-03 12:15:41 +0100555/**
556 * fu_plugin_get_smbios_string:
557 * @plugin: A #FuPlugin
558 * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
559 * @offset: A SMBIOS offset
560 *
561 * Gets a hardware SMBIOS string.
562 *
563 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
564 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
565 *
566 * Since: 0.9.8
567 **/
568const gchar *
569fu_plugin_get_smbios_string (FuPlugin *plugin, guint8 structure_type, guint8 offset)
570{
571 FuPluginPrivate *priv = GET_PRIVATE (plugin);
572 if (priv->smbios == NULL)
573 return NULL;
574 return fu_smbios_get_string (priv->smbios, structure_type, offset, NULL);
575}
576
577/**
578 * fu_plugin_get_smbios_data:
579 * @plugin: A #FuPlugin
580 * @structure_type: A SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
581 *
582 * Gets a hardware SMBIOS data.
583 *
584 * Since: 0.9.8
585 **/
586GBytes *
587fu_plugin_get_smbios_data (FuPlugin *plugin, guint8 structure_type)
588{
589 FuPluginPrivate *priv = GET_PRIVATE (plugin);
590 if (priv->smbios == NULL)
591 return NULL;
592 return fu_smbios_get_data (priv->smbios, structure_type, NULL);
593}
594
Richard Hughesb8f8db22017-04-25 15:56:00 +0100595void
Richard Hughesd7704d42017-08-08 20:29:09 +0100596fu_plugin_set_hwids (FuPlugin *plugin, FuHwids *hwids)
Richard Hughesb8f8db22017-04-25 15:56:00 +0100597{
598 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesd7704d42017-08-08 20:29:09 +0100599 g_set_object (&priv->hwids, hwids);
Richard Hughesb8f8db22017-04-25 15:56:00 +0100600}
601
Richard Hughes49e5e052017-09-03 12:15:41 +0100602void
603fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios)
604{
605 FuPluginPrivate *priv = GET_PRIVATE (plugin);
606 g_set_object (&priv->smbios, smbios);
607}
608
Richard Hughesb8f8db22017-04-25 15:56:00 +0100609/**
Richard Hughesb0829032017-01-10 09:27:08 +0000610 * fu_plugin_set_coldplug_delay:
611 * @plugin: A #FuPlugin
612 * @duration: A delay in milliseconds
613 *
614 * Set the minimum time that should be waited inbetween the call to
615 * fu_plugin_coldplug_prepare() and fu_plugin_coldplug(). This is usually going
616 * to be the minimum hardware initialisation time from a datasheet.
617 *
618 * It is better to use this function rather than using a sleep() in the plugin
619 * itself as then only one delay is done in the daemon rather than waiting for
620 * each coldplug prepare in a serial way.
621 *
622 * Additionally, very long delays should be avoided as the daemon will be
623 * blocked from processing requests whilst the coldplug delay is being
624 * performed.
625 *
626 * Since: 0.8.0
627 **/
628void
629fu_plugin_set_coldplug_delay (FuPlugin *plugin, guint duration)
630{
631 g_return_if_fail (FU_IS_PLUGIN (plugin));
632 g_return_if_fail (duration > 0);
633
634 /* check sanity */
635 if (duration > FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM) {
636 g_warning ("duration of %ums is crazy, truncating to %ums",
637 duration,
638 FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM);
639 duration = FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM;
640 }
641
642 /* emit */
643 g_signal_emit (plugin, signals[SIGNAL_SET_COLDPLUG_DELAY], 0, duration);
644}
645
Richard Hughesd0905142016-03-13 09:46:49 +0000646gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000647fu_plugin_runner_startup (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000648{
Richard Hughescff38bc2016-12-12 12:03:37 +0000649 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000650 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000651
652 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000653 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000654 return TRUE;
655
656 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000657 g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func);
658 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000659 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000660 g_debug ("performing startup() on %s", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000661 if (!func (plugin, error)) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000662 g_prefix_error (error, "failed to startup %s: ", priv->name);
663 return FALSE;
664 }
665 return TRUE;
666}
667
668static gboolean
669fu_plugin_runner_offline_invalidate (GError **error)
670{
671 g_autoptr(GError) error_local = NULL;
672 g_autoptr(GFile) file1 = NULL;
673
674 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
675
676 file1 = g_file_new_for_path (FU_OFFLINE_TRIGGER_FILENAME);
677 if (!g_file_query_exists (file1, NULL))
678 return TRUE;
679 if (!g_file_delete (file1, NULL, &error_local)) {
680 g_set_error (error,
681 FWUPD_ERROR,
682 FWUPD_ERROR_INTERNAL,
683 "Cannot delete %s: %s",
684 FU_OFFLINE_TRIGGER_FILENAME,
685 error_local->message);
686 return FALSE;
687 }
688 return TRUE;
689}
690
691static gboolean
692fu_plugin_runner_offline_setup (GError **error)
693{
694 gint rc;
695
696 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
697
698 /* create symlink for the systemd-system-update-generator */
699 rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME);
700 if (rc < 0) {
701 g_set_error (error,
702 FWUPD_ERROR,
703 FWUPD_ERROR_INTERNAL,
704 "Failed to create symlink %s to %s: %s",
705 FU_OFFLINE_TRIGGER_FILENAME,
706 "/var/lib", strerror (errno));
Richard Hughesd0905142016-03-13 09:46:49 +0000707 return FALSE;
708 }
709 return TRUE;
710}
711
Richard Hughesd0905142016-03-13 09:46:49 +0000712gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000713fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000714{
Richard Hughescff38bc2016-12-12 12:03:37 +0000715 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000716 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000717
718 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000719 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000720 return TRUE;
721
722 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000723 g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func);
724 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000725 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000726 g_debug ("performing coldplug() on %s", priv->name);
727 if (!func (plugin, error)) {
728 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
729 return FALSE;
730 }
731 return TRUE;
732}
733
Richard Hughes7b8b2022016-12-12 16:15:03 +0000734gboolean
Richard Hughes46487c92017-01-07 21:26:34 +0000735fu_plugin_runner_coldplug_prepare (FuPlugin *plugin, GError **error)
736{
737 FuPluginPrivate *priv = GET_PRIVATE (plugin);
738 FuPluginStartupFunc func = NULL;
739
740 /* not enabled */
741 if (!priv->enabled)
742 return TRUE;
743
744 /* optional */
745 g_module_symbol (priv->module, "fu_plugin_coldplug_prepare", (gpointer *) &func);
746 if (func == NULL)
747 return TRUE;
748 g_debug ("performing coldplug_prepare() on %s", priv->name);
749 if (!func (plugin, error)) {
750 g_prefix_error (error, "failed to prepare for coldplug %s: ", priv->name);
751 return FALSE;
752 }
753 return TRUE;
754}
755
756gboolean
757fu_plugin_runner_coldplug_cleanup (FuPlugin *plugin, GError **error)
758{
759 FuPluginPrivate *priv = GET_PRIVATE (plugin);
760 FuPluginStartupFunc func = NULL;
761
762 /* not enabled */
763 if (!priv->enabled)
764 return TRUE;
765
766 /* optional */
767 g_module_symbol (priv->module, "fu_plugin_coldplug_cleanup", (gpointer *) &func);
768 if (func == NULL)
769 return TRUE;
770 g_debug ("performing coldplug_cleanup() on %s", priv->name);
771 if (!func (plugin, error)) {
772 g_prefix_error (error, "failed to cleanup coldplug %s: ", priv->name);
773 return FALSE;
774 }
775 return TRUE;
776}
777
778gboolean
Richard Hughes7b8b2022016-12-12 16:15:03 +0000779fu_plugin_runner_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
780{
781 FuPluginPrivate *priv = GET_PRIVATE (plugin);
782 FuPluginDeviceFunc func = NULL;
783
784 /* not enabled */
785 if (!priv->enabled)
786 return TRUE;
787
788 /* optional */
789 g_module_symbol (priv->module, "fu_plugin_update_prepare", (gpointer *) &func);
790 if (func == NULL)
791 return TRUE;
792 g_debug ("performing update_prepare() on %s", priv->name);
793 if (!func (plugin, device, error)) {
794 g_prefix_error (error, "failed to prepare for update %s: ", priv->name);
795 return FALSE;
796 }
797 return TRUE;
798}
799
800gboolean
801fu_plugin_runner_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
802{
803 FuPluginPrivate *priv = GET_PRIVATE (plugin);
804 FuPluginDeviceFunc func = NULL;
805
806 /* not enabled */
807 if (!priv->enabled)
808 return TRUE;
809
810 /* optional */
811 g_module_symbol (priv->module, "fu_plugin_update_cleanup", (gpointer *) &func);
812 if (func == NULL)
813 return TRUE;
814 g_debug ("performing update_cleanup() on %s", priv->name);
815 if (!func (plugin, device, error)) {
816 g_prefix_error (error, "failed to cleanup update %s: ", priv->name);
817 return FALSE;
818 }
819 return TRUE;
820}
821
Richard Hughese1fd34d2017-08-24 14:19:51 +0100822void
823fu_plugin_runner_device_register (FuPlugin *plugin, FuDevice *device)
824{
825 FuPluginPrivate *priv = GET_PRIVATE (plugin);
826 FuPluginDeviceRegisterFunc func = NULL;
827
828 /* not enabled */
829 if (!priv->enabled)
830 return;
831
832 /* optional */
833 g_module_symbol (priv->module, "fu_plugin_device_registered", (gpointer *) &func);
834 if (func != NULL) {
835 g_debug ("performing device_added() on %s", priv->name);
836 func (plugin, device);
837 }
838}
839
Richard Hughescff38bc2016-12-12 12:03:37 +0000840static gboolean
841fu_plugin_runner_schedule_update (FuPlugin *plugin,
842 FuDevice *device,
843 GBytes *blob_cab,
844 GError **error)
845{
846 gchar tmpname[] = {"XXXXXX.cap"};
847 g_autofree gchar *dirname = NULL;
848 g_autofree gchar *filename = NULL;
849 g_autoptr(FwupdResult) res_tmp = NULL;
850 g_autoptr(FuPending) pending = NULL;
851 g_autoptr(GFile) file = NULL;
852
853 /* id already exists */
854 pending = fu_pending_new ();
855 res_tmp = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
856 if (res_tmp != NULL) {
857 g_set_error (error,
858 FWUPD_ERROR,
859 FWUPD_ERROR_ALREADY_PENDING,
860 "%s is already scheduled to be updated",
861 fu_device_get_id (device));
862 return FALSE;
863 }
864
865 /* create directory */
866 dirname = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
867 file = g_file_new_for_path (dirname);
868 if (!g_file_query_exists (file, NULL)) {
869 if (!g_file_make_directory_with_parents (file, NULL, error))
870 return FALSE;
871 }
872
873 /* get a random filename */
874 for (guint i = 0; i < 6; i++)
875 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
876 filename = g_build_filename (dirname, tmpname, NULL);
877
878 /* just copy to the temp file */
879 fu_plugin_set_status (plugin, FWUPD_STATUS_SCHEDULING);
880 if (!g_file_set_contents (filename,
881 g_bytes_get_data (blob_cab, NULL),
882 (gssize) g_bytes_get_size (blob_cab),
883 error))
884 return FALSE;
885
886 /* schedule for next boot */
887 g_debug ("schedule %s to be installed to %s on next boot",
888 filename, fu_device_get_id (device));
889 fu_device_set_update_filename (device, filename);
890
891 /* add to database */
892 if (!fu_pending_add_device (pending, FWUPD_RESULT (device), error))
893 return FALSE;
894
895 /* next boot we run offline */
896 return fu_plugin_runner_offline_setup (error);
897}
898
899gboolean
900fu_plugin_runner_verify (FuPlugin *plugin,
901 FuDevice *device,
902 FuPluginVerifyFlags flags,
903 GError **error)
904{
905 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000906 FuPluginVerifyFunc func = NULL;
Richard Hughesababbb72017-06-15 20:18:36 +0100907 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000908
909 /* not enabled */
910 if (!priv->enabled)
911 return TRUE;
912
Richard Hughesababbb72017-06-15 20:18:36 +0100913 /* clear any existing verification checksums */
914 checksums = fu_device_get_checksums (device);
915 g_ptr_array_set_size (checksums, 0);
916
Richard Hughescff38bc2016-12-12 12:03:37 +0000917 /* optional */
918 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
919 if (func == NULL)
920 return TRUE;
921 g_debug ("performing verify() on %s", priv->name);
922 if (!func (plugin, device, flags, error)) {
923 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000924 return FALSE;
925 }
926 return TRUE;
927}
928
Richard Hughesd0905142016-03-13 09:46:49 +0000929gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000930fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000931{
Richard Hughescff38bc2016-12-12 12:03:37 +0000932 guint64 flags;
933 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000934 FuPluginDeviceFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000935
936 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000937 if (!priv->enabled)
938 return TRUE;
939
940 /* final check */
941 flags = fu_device_get_flags (device);
942 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
943 g_set_error (error,
944 FWUPD_ERROR,
945 FWUPD_ERROR_NOT_SUPPORTED,
946 "Device %s is not locked",
947 fu_device_get_id (device));
948 return FALSE;
949 }
950
951 /* optional */
952 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
953 if (func != NULL) {
954 g_debug ("performing unlock() on %s", priv->name);
955 if (!func (plugin, device, error)) {
Richard Hughes7b8b2022016-12-12 16:15:03 +0000956 g_prefix_error (error, "failed to unlock %s: ", priv->name);
Richard Hughescff38bc2016-12-12 12:03:37 +0000957 return FALSE;
958 }
959 }
960
961 /* update with correct flags */
962 flags = fu_device_get_flags (device);
963 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
964 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
965 return TRUE;
966}
967
968gboolean
969fu_plugin_runner_update (FuPlugin *plugin,
Richard Hughesa785a1c2017-08-25 16:00:58 +0100970 FuDevice *device,
971 GBytes *blob_cab,
972 GBytes *blob_fw,
973 FwupdInstallFlags flags,
974 GError **error)
Richard Hughescff38bc2016-12-12 12:03:37 +0000975{
976 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesa785a1c2017-08-25 16:00:58 +0100977 FuPluginUpdateFunc update_func;
Richard Hughescff38bc2016-12-12 12:03:37 +0000978 g_autoptr(FuPending) pending = NULL;
979 g_autoptr(FwupdResult) res_pending = NULL;
980 GError *error_update = NULL;
Richard Hughesf556d372017-06-15 19:49:18 +0100981 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000982
983 /* not enabled */
984 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000985 return TRUE;
986
987 /* optional */
Richard Hughesa785a1c2017-08-25 16:00:58 +0100988 g_module_symbol (priv->module, "fu_plugin_update", (gpointer *) &update_func);
989 if (update_func == NULL) {
990 g_set_error_literal (error,
991 FWUPD_ERROR,
992 FWUPD_ERROR_NOT_SUPPORTED,
993 "No update possible");
994 return FALSE;
995 }
Richard Hughesd0905142016-03-13 09:46:49 +0000996
Richard Hughesa785a1c2017-08-25 16:00:58 +0100997 /* just schedule this for the next reboot */
Richard Hughescff38bc2016-12-12 12:03:37 +0000998 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
Richard Hughesa785a1c2017-08-25 16:00:58 +0100999 return fu_plugin_runner_schedule_update (plugin,
1000 device,
1001 blob_cab,
1002 error);
Richard Hughescff38bc2016-12-12 12:03:37 +00001003 }
1004
1005 /* cancel the pending action */
1006 if (!fu_plugin_runner_offline_invalidate (error))
1007 return FALSE;
1008
1009 /* online */
Richard Hughescff38bc2016-12-12 12:03:37 +00001010 pending = fu_pending_new ();
1011 res_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
Richard Hughesa785a1c2017-08-25 16:00:58 +01001012 if (!update_func (plugin, device, blob_fw, flags, &error_update)) {
Richard Hughescff38bc2016-12-12 12:03:37 +00001013 /* save the error to the database */
1014 if (res_pending != NULL) {
1015 fu_pending_set_error_msg (pending, FWUPD_RESULT (device),
1016 error_update->message, NULL);
1017 }
1018 g_propagate_error (error, error_update);
1019 return FALSE;
1020 }
1021
Richard Hughesf556d372017-06-15 19:49:18 +01001022 /* no longer valid */
1023 checksums = fu_device_get_checksums (device);
1024 g_ptr_array_set_size (checksums, 0);
1025
Richard Hughescff38bc2016-12-12 12:03:37 +00001026 /* cleanup */
1027 if (res_pending != NULL) {
1028 const gchar *tmp;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001029 FwupdRelease *rel = fwupd_result_get_release (res_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +00001030
1031 /* update pending database */
1032 fu_pending_set_state (pending, FWUPD_RESULT (device),
1033 FWUPD_UPDATE_STATE_SUCCESS, NULL);
1034
1035 /* delete cab file */
Richard Hughes1642b3b2017-06-05 17:40:08 +01001036 tmp = fwupd_release_get_filename (rel);
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;
1060 g_autoptr(FwupdResult) res_pending = NULL;
1061 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 ();
1080 res_pending = fu_pending_get_device (pending,
1081 fu_device_get_id (device),
1082 &error_local);
1083 if (res_pending == NULL) {
1084 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 */
1094 return fu_pending_remove_device (pending, FWUPD_RESULT (device), error);
1095}
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;
Richard Hughes1642b3b2017-06-05 17:40:08 +01001103 FwupdRelease *rel;
1104 FwupdDevice *dev;
Richard Hughescff38bc2016-12-12 12:03:37 +00001105 const gchar *tmp;
1106 g_autoptr(GError) error_local = NULL;
1107 g_autoptr(FwupdResult) res_pending = NULL;
1108 g_autoptr(FuPending) pending = NULL;
1109
1110 /* not enabled */
1111 if (!priv->enabled)
1112 return TRUE;
1113
1114 /* use the plugin if the vfunc is provided */
1115 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
1116 if (func != NULL) {
1117 g_debug ("performing get_results() on %s", priv->name);
1118 if (!func (plugin, device, error)) {
1119 g_prefix_error (error, "failed to get_results %s: ", priv->name);
1120 return FALSE;
1121 }
1122 return TRUE;
1123 }
1124
1125 /* handled using the database */
1126 pending = fu_pending_new ();
1127 res_pending = fu_pending_get_device (pending,
1128 fu_device_get_id (device),
1129 &error_local);
1130 if (res_pending == NULL) {
1131 g_set_error (error,
1132 FWUPD_ERROR,
1133 FWUPD_ERROR_NOTHING_TO_DO,
1134 "Failed to find %s in pending database: %s",
1135 fu_device_get_id (device),
1136 error_local->message);
1137 return FALSE;
1138 }
1139
1140 /* copy the important parts from the pending device to the real one */
Richard Hughesa1aab512017-09-13 11:53:30 +01001141 dev = fwupd_result_get_device (res_pending);
1142 update_state = fwupd_device_get_update_state (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001143 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
1144 update_state == FWUPD_UPDATE_STATE_PENDING) {
1145 g_set_error (error,
1146 FWUPD_ERROR,
1147 FWUPD_ERROR_NOTHING_TO_DO,
1148 "Device %s has not been updated offline yet",
1149 fu_device_get_id (device));
1150 return FALSE;
1151 }
1152
1153 /* copy */
1154 fu_device_set_update_state (device, update_state);
Richard Hughesa1aab512017-09-13 11:53:30 +01001155 tmp = fwupd_device_get_update_error (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001156 if (tmp != NULL)
1157 fu_device_set_update_error (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001158 tmp = fwupd_device_get_version (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001159 if (tmp != NULL)
1160 fu_device_set_version (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001161 rel = fwupd_result_get_release (res_pending);
1162 tmp = fwupd_release_get_version (rel);
Richard Hughescff38bc2016-12-12 12:03:37 +00001163 if (tmp != NULL)
1164 fu_device_set_update_version (device, tmp);
1165 return TRUE;
1166}
1167
1168static void
1169fu_plugin_class_init (FuPluginClass *klass)
1170{
1171 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1172 object_class->finalize = fu_plugin_finalize;
1173 signals[SIGNAL_DEVICE_ADDED] =
1174 g_signal_new ("device-added",
1175 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1176 G_STRUCT_OFFSET (FuPluginClass, device_added),
1177 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1178 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1179 signals[SIGNAL_DEVICE_REMOVED] =
1180 g_signal_new ("device-removed",
1181 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1182 G_STRUCT_OFFSET (FuPluginClass, device_removed),
1183 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1184 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughese1fd34d2017-08-24 14:19:51 +01001185 signals[SIGNAL_DEVICE_REGISTER] =
1186 g_signal_new ("device-register",
1187 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1188 G_STRUCT_OFFSET (FuPluginClass, device_register),
1189 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1190 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughescff38bc2016-12-12 12:03:37 +00001191 signals[SIGNAL_STATUS_CHANGED] =
1192 g_signal_new ("status-changed",
1193 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1194 G_STRUCT_OFFSET (FuPluginClass, status_changed),
1195 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1196 G_TYPE_NONE, 1, G_TYPE_UINT);
1197 signals[SIGNAL_PERCENTAGE_CHANGED] =
1198 g_signal_new ("percentage-changed",
1199 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1200 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
1201 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1202 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughes362d6d72017-01-07 21:42:14 +00001203 signals[SIGNAL_RECOLDPLUG] =
1204 g_signal_new ("recoldplug",
1205 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1206 G_STRUCT_OFFSET (FuPluginClass, recoldplug),
1207 NULL, NULL, g_cclosure_marshal_VOID__VOID,
1208 G_TYPE_NONE, 0);
Richard Hughesb0829032017-01-10 09:27:08 +00001209 signals[SIGNAL_SET_COLDPLUG_DELAY] =
1210 g_signal_new ("set-coldplug-delay",
1211 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1212 G_STRUCT_OFFSET (FuPluginClass, set_coldplug_delay),
1213 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1214 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughescff38bc2016-12-12 12:03:37 +00001215}
1216
1217static void
1218fu_plugin_init (FuPlugin *plugin)
1219{
1220 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1221 priv->enabled = TRUE;
1222 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
1223 g_free, (GDestroyNotify) g_object_unref);
Richard Hughese8b5db62017-07-17 14:18:22 +01001224 priv->devices_delay = g_hash_table_new (g_direct_hash, g_direct_equal);
Richard Hughescff38bc2016-12-12 12:03:37 +00001225}
1226
1227static void
1228fu_plugin_finalize (GObject *object)
1229{
1230 FuPlugin *plugin = FU_PLUGIN (object);
1231 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1232 FuPluginInitFunc func = NULL;
1233
1234 /* optional */
1235 if (priv->module != NULL) {
1236 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
1237 if (func != NULL) {
1238 g_debug ("performing destroy() on %s", priv->name);
1239 func (plugin);
1240 }
1241 }
1242
1243 if (priv->usb_ctx != NULL)
1244 g_object_unref (priv->usb_ctx);
Richard Hughesb8f8db22017-04-25 15:56:00 +01001245 if (priv->hwids != NULL)
Richard Hughesd7704d42017-08-08 20:29:09 +01001246 g_object_unref (priv->hwids);
Richard Hughes49e5e052017-09-03 12:15:41 +01001247 if (priv->smbios != NULL)
1248 g_object_unref (priv->smbios);
Richard Hughes576c0122017-02-24 09:47:00 +00001249#ifndef RUNNING_ON_VALGRIND
Richard Hughescff38bc2016-12-12 12:03:37 +00001250 if (priv->module != NULL)
1251 g_module_close (priv->module);
Richard Hughes576c0122017-02-24 09:47:00 +00001252#endif
Richard Hughescff38bc2016-12-12 12:03:37 +00001253 g_hash_table_unref (priv->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001254 g_hash_table_unref (priv->devices_delay);
Richard Hughescff38bc2016-12-12 12:03:37 +00001255 g_free (priv->name);
1256 g_free (priv->data);
1257
1258 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
1259}
1260
1261FuPlugin *
1262fu_plugin_new (void)
1263{
1264 FuPlugin *plugin;
1265 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
1266 return plugin;
1267}