/src/fwupd/libfwupdplugin/fu-device-locker.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2017 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuDeviceLocker" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <gio/gio.h> |
12 | | |
13 | | #include "fu-device-locker.h" |
14 | | #include "fu-device.h" |
15 | | |
16 | | /** |
17 | | * FuDeviceLocker: |
18 | | * |
19 | | * Easily close a shared resource (such as a device) when an object goes out of |
20 | | * scope. |
21 | | * |
22 | | * See also: [class@FuDevice] |
23 | | */ |
24 | | |
25 | | struct _FuDeviceLocker { |
26 | | GObject parent_instance; |
27 | | FuDevice *device; |
28 | | gboolean device_open; |
29 | | FuDeviceLockerFunc open_func; |
30 | | FuDeviceLockerFunc close_func; |
31 | | }; |
32 | | |
33 | 0 | G_DEFINE_TYPE(FuDeviceLocker, fu_device_locker, G_TYPE_OBJECT) |
34 | 0 |
|
35 | 0 | static void |
36 | 0 | fu_device_locker_finalize(GObject *obj) |
37 | 0 | { |
38 | 0 | FuDeviceLocker *self = FU_DEVICE_LOCKER(obj); |
39 | | |
40 | | /* close device */ |
41 | 0 | if (self->device_open) { |
42 | 0 | g_autoptr(GError) error = NULL; |
43 | 0 | if (!self->close_func(self->device, &error)) |
44 | 0 | g_warning("failed to close device: %s", error->message); |
45 | 0 | } |
46 | 0 | if (self->device != NULL) |
47 | 0 | g_object_unref(self->device); |
48 | 0 | G_OBJECT_CLASS(fu_device_locker_parent_class)->finalize(obj); |
49 | 0 | } |
50 | | |
51 | | static void |
52 | | fu_device_locker_class_init(FuDeviceLockerClass *klass) |
53 | 0 | { |
54 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
55 | 0 | object_class->finalize = fu_device_locker_finalize; |
56 | 0 | } |
57 | | |
58 | | static void |
59 | | fu_device_locker_init(FuDeviceLocker *self) |
60 | 0 | { |
61 | 0 | } |
62 | | |
63 | | /** |
64 | | * fu_device_locker_close: |
65 | | * @self: a #FuDeviceLocker |
66 | | * @error: (nullable): optional return location for an error |
67 | | * |
68 | | * Closes the locker before it gets cleaned up. |
69 | | * |
70 | | * This function can be used to manually close a device managed by a locker, |
71 | | * and allows the caller to properly handle the error. |
72 | | * |
73 | | * Returns: %TRUE for success |
74 | | * |
75 | | * Since: 1.4.0 |
76 | | **/ |
77 | | gboolean |
78 | | fu_device_locker_close(FuDeviceLocker *self, GError **error) |
79 | 0 | { |
80 | 0 | g_autoptr(GError) error_local = NULL; |
81 | 0 | g_return_val_if_fail(FU_IS_DEVICE_LOCKER(self), FALSE); |
82 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
83 | 0 | if (!self->device_open) |
84 | 0 | return TRUE; |
85 | 0 | if (!self->close_func(self->device, &error_local)) { |
86 | 0 | if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { |
87 | 0 | g_debug("ignoring: %s", error_local->message); |
88 | 0 | return TRUE; |
89 | 0 | } |
90 | 0 | g_propagate_error(error, g_steal_pointer(&error_local)); |
91 | 0 | return FALSE; |
92 | 0 | } |
93 | 0 | self->device_open = FALSE; |
94 | 0 | return TRUE; |
95 | 0 | } |
96 | | |
97 | | /** |
98 | | * fu_device_locker_new: |
99 | | * @device: a #FuDevice |
100 | | * @error: (nullable): optional return location for an error |
101 | | * |
102 | | * Opens the device for use. When the #FuDeviceLocker is deallocated the device |
103 | | * will be closed and any error will just be directed to the console. |
104 | | * This object is typically called using g_autoptr() but the device can also be |
105 | | * manually closed using g_clear_object(). |
106 | | * |
107 | | * The functions used for opening and closing the device are set automatically. |
108 | | * |
109 | | * NOTE: If the @open_func failed then the @close_func will not be called. |
110 | | * |
111 | | * Think of this object as the device ownership. |
112 | | * |
113 | | * Returns: a device locker, or %NULL if the @open_func failed. |
114 | | * |
115 | | * Since: 1.0.0 |
116 | | **/ |
117 | | FuDeviceLocker * |
118 | | fu_device_locker_new(FuDevice *device, GError **error) |
119 | 0 | { |
120 | 0 | g_return_val_if_fail(FU_IS_DEVICE(device), NULL); |
121 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
122 | | |
123 | | /* FuDevice */ |
124 | 0 | return fu_device_locker_new_full(device, fu_device_open, fu_device_close, error); |
125 | 0 | } |
126 | | |
127 | | /** |
128 | | * fu_device_locker_new_full: |
129 | | * @device: a #FuDevice |
130 | | * @open_func: (scope async): a function to open the device |
131 | | * @close_func: (scope async): a function to close the device |
132 | | * @error: (nullable): optional return location for an error |
133 | | * |
134 | | * Opens the device for use. When the #FuDeviceLocker is deallocated the device |
135 | | * will be closed and any error will just be directed to the console. |
136 | | * This object is typically called using g_autoptr() but the device can also be |
137 | | * manually closed using g_clear_object(). |
138 | | * |
139 | | * NOTE: If the @open_func failed then the @close_func will not be called. |
140 | | * |
141 | | * Think of this object as the device ownership. |
142 | | * |
143 | | * Returns: a device locker, or %NULL if the @open_func failed. |
144 | | * |
145 | | * Since: 1.0.0 |
146 | | **/ |
147 | | FuDeviceLocker * |
148 | | fu_device_locker_new_full(FuDevice *device, |
149 | | FuDeviceLockerFunc open_func, |
150 | | FuDeviceLockerFunc close_func, |
151 | | GError **error) |
152 | 0 | { |
153 | 0 | g_autoptr(FuDeviceLocker) self = NULL; |
154 | |
|
155 | 0 | g_return_val_if_fail(FU_IS_DEVICE(device), NULL); |
156 | 0 | g_return_val_if_fail(open_func != NULL, NULL); |
157 | 0 | g_return_val_if_fail(close_func != NULL, NULL); |
158 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
159 | | |
160 | | /* create object */ |
161 | 0 | self = g_object_new(FU_TYPE_DEVICE_LOCKER, NULL); |
162 | 0 | self->device = g_object_ref(device); |
163 | 0 | self->open_func = open_func; |
164 | 0 | self->close_func = close_func; |
165 | | |
166 | | /* open device */ |
167 | 0 | if (!self->open_func(device, error)) { |
168 | 0 | g_autoptr(GError) error_local = NULL; |
169 | 0 | if (!self->close_func(device, &error_local)) { |
170 | 0 | g_debug("ignoring close error on aborted open: %s", error_local->message); |
171 | 0 | } |
172 | 0 | return NULL; |
173 | 0 | } |
174 | | |
175 | | /* success */ |
176 | 0 | self->device_open = TRUE; |
177 | 0 | return g_steal_pointer(&self); |
178 | 0 | } |