diff --git a/src/fu-device-locker.c b/src/fu-device-locker.c new file mode 100644 index 000000000..dd800ac69 --- /dev/null +++ b/src/fu-device-locker.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include + +#include "fu-device-locker.h" + +struct _FuDeviceLocker { + GObject parent_instance; + GObject *device; + gboolean device_open; + FuDeviceLockerFunc open_func; + FuDeviceLockerFunc close_func; +}; + +G_DEFINE_TYPE (FuDeviceLocker, fu_device_locker, G_TYPE_OBJECT) + +static void +fu_device_locker_finalize (GObject *obj) +{ + FuDeviceLocker *self = FU_DEVICE_LOCKER (obj); + + /* close device */ + if (self->device_open) { + g_autoptr(GError) error = NULL; + if (!self->close_func (self->device, &error)) + g_warning ("failed to close device: %s", error->message); + } + + g_object_unref (self->device); + G_OBJECT_CLASS (fu_device_locker_parent_class)->finalize (obj); +} + +static void +fu_device_locker_class_init (FuDeviceLockerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_device_locker_finalize; +} + +static void +fu_device_locker_init (FuDeviceLocker *self) +{ +} + +/** + * fu_device_locker_new: + * @device: A #GObject + * @error: A #GError, or %NULL + * + * Opens the device for use. When the #FuDeviceLocker is deallocated the device + * will be closed and any error will just be directed to the console. + * This object is typically called using g_autoptr() but the device can also be + * manually closed using g_clear_object(). + * + * The functions used for opening and closing the device are set automatically. + * If the @device is not a type or supertype of @GUsbDevice then this function + * will not work. For custom objects please use fu_device_locker_new_full(). + * + * NOTE: If the @open_func failed then the @close_func will not be called. + * + * Think of this object as the device ownership. + * + * Returns: a #FuDeviceLocker, or %NULL if the @open_func failed. + **/ +FuDeviceLocker * +fu_device_locker_new (gpointer device, GError **error) +{ + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + + /* GUsbDevice */ + if (G_USB_IS_DEVICE (device)) { + return fu_device_locker_new_full (device, + (FuDeviceLockerFunc) g_usb_device_open, + (FuDeviceLockerFunc) g_usb_device_close, + error); + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device object type not supported"); + return NULL; +} + +/** + * fu_device_locker_new_full: + * @device: A #GObject + * @open_func: A function to open the device + * @close_func: A function to close the device + * @error: A #GError, or %NULL + * + * Opens the device for use. When the #FuDeviceLocker is deallocated the device + * will be closed and any error will just be directed to the console. + * This object is typically called using g_autoptr() but the device can also be + * manually closed using g_clear_object(). + * + * NOTE: If the @open_func failed then the @close_func will not be called. + * + * Think of this object as the device ownership. + * + * Returns: a #FuDeviceLocker, or %NULL if the @open_func failed. + **/ +FuDeviceLocker * +fu_device_locker_new_full (gpointer device, + FuDeviceLockerFunc open_func, + FuDeviceLockerFunc close_func, + GError **error) +{ + g_autoptr(FuDeviceLocker) self = NULL; + + g_return_val_if_fail (device != NULL, NULL); + g_return_val_if_fail (open_func != NULL, NULL); + g_return_val_if_fail (close_func != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + + /* create object */ + self = g_object_new (FU_TYPE_DEVICE_LOCKER, NULL); + self->device = g_object_ref (device); + self->open_func = open_func; + self->close_func = close_func; + + /* open device */ + if (!self->open_func (device, error)) + return NULL; + + /* success */ + self->device_open = TRUE; + return g_steal_pointer (&self); +} diff --git a/src/fu-device-locker.h b/src/fu-device-locker.h new file mode 100644 index 000000000..d8d11b06f --- /dev/null +++ b/src/fu-device-locker.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __FU_DEVICE_LOCKER_H +#define __FU_DEVICE_LOCKER_H + +#include + +G_BEGIN_DECLS + +#define FU_TYPE_DEVICE_LOCKER (fu_device_locker_get_type ()) + +G_DECLARE_FINAL_TYPE (FuDeviceLocker, fu_device_locker, FU, DEVICE_LOCKER, GObject) + +typedef gboolean (*FuDeviceLockerFunc) (GObject *device, + GError **error); + +FuDeviceLocker *fu_device_locker_new (gpointer device, + GError **error); +FuDeviceLocker *fu_device_locker_new_full (gpointer device, + FuDeviceLockerFunc open_func, + FuDeviceLockerFunc close_func, + GError **error); + +G_END_DECLS + +#endif /* __FU_DEVICE_LOCKER_H */ diff --git a/src/fu-self-test.c b/src/fu-self-test.c index c8c40fd02..1fe9f21f2 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -650,6 +650,62 @@ fu_test_stdout_cb (const gchar *line, gpointer user_data) (*lines)++; } +static gboolean +_open_cb (GObject *device, GError **error) +{ + g_assert_cmpstr (g_object_get_data (device, "state"), ==, "closed"); + g_object_set_data (device, "state", "opened"); + return TRUE; +} + +static gboolean +_close_cb (GObject *device, GError **error) +{ + g_assert_cmpstr (g_object_get_data (device, "state"), ==, "opened"); + g_object_set_data (device, "state", "closed-on-unref"); + return TRUE; +} + +static void +fu_device_locker_func (void) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GObject) device = g_object_new (G_TYPE_OBJECT, NULL); + + g_object_set_data (device, "state", "closed"); + locker = fu_device_locker_new_full (device, _open_cb, _close_cb, &error); + g_assert_no_error (error); + g_assert_nonnull (locker); + g_clear_object (&locker); + g_assert_cmpstr (g_object_get_data (device, "state"), ==, "closed-on-unref"); +} + +static gboolean +_fail_open_cb (GObject *device, GError **error) +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "fail"); + return FALSE; +} + +static gboolean +_fail_close_cb (GObject *device, GError **error) +{ + g_assert_not_reached (); + return TRUE; +} + +static void +fu_device_locker_fail_func (void) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GObject) device = g_object_new (G_TYPE_OBJECT, NULL); + locker = fu_device_locker_new_full (device, _fail_open_cb, _fail_close_cb, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_null (locker); +} + static void fu_common_spawn_func (void) { @@ -681,6 +737,8 @@ main (int argc, char **argv) g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); /* tests go here */ + g_test_add_func ("/fwupd/device-locker{success}", fu_device_locker_func); + g_test_add_func ("/fwupd/device-locker{fail}", fu_device_locker_fail_func); g_test_add_func ("/fwupd/device{metadata}", fu_device_metadata_func); g_test_add_func ("/fwupd/hwids", fu_hwids_func); g_test_add_func ("/fwupd/smbios", fu_smbios_func); diff --git a/src/meson.build b/src/meson.build index 57a75f963..8dc7a8c2a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,6 +23,7 @@ libfwupdprivate = static_library( sources : [ 'fu-common.c', 'fu-device.c', + 'fu-device-locker.c', 'fu-hwids.c', 'fu-pending.c', 'fu-plugin.c', @@ -105,6 +106,7 @@ executable( 'fu-hwids.c', 'fu-debug.c', 'fu-device.c', + 'fu-device-locker.c', 'fu-keyring.c', 'fu-pending.c', 'fu-plugin.c', @@ -153,6 +155,7 @@ if get_option('enable-tests') 'fu-keyring.c', 'fu-hwids.c', 'fu-device.c', + 'fu-device-locker.c', 'fu-pending.c', 'fu-keyring.c', 'fu-keyring-result.c',