File: fu-device-locker.c

package info (click to toggle)
fwupd 2.0.19-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 32,340 kB
  • sloc: ansic: 274,440; python: 11,468; xml: 9,432; sh: 1,625; makefile: 167; cpp: 19; asm: 11; javascript: 9
file content (178 lines) | stat: -rw-r--r-- 4,865 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#define G_LOG_DOMAIN "FuDeviceLocker"

#include "config.h"

#include <gio/gio.h>

#include "fu-device-locker.h"
#include "fu-device.h"

/**
 * FuDeviceLocker:
 *
 * Easily close a shared resource (such as a device) when an object goes out of
 * scope.
 *
 * See also: [class@FuDevice]
 */

struct _FuDeviceLocker {
	GObject parent_instance;
	FuDevice *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);
	}
	if (self->device != NULL)
		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_close:
 * @self: a #FuDeviceLocker
 * @error: (nullable): optional return location for an error
 *
 * Closes the locker before it gets cleaned up.
 *
 * This function can be used to manually close a device managed by a locker,
 * and allows the caller to properly handle the error.
 *
 * Returns: %TRUE for success
 *
 * Since: 1.4.0
 **/
gboolean
fu_device_locker_close(FuDeviceLocker *self, GError **error)
{
	g_autoptr(GError) error_local = NULL;
	g_return_val_if_fail(FU_IS_DEVICE_LOCKER(self), FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
	if (!self->device_open)
		return TRUE;
	if (!self->close_func(self->device, &error_local)) {
		if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
			g_debug("ignoring: %s", error_local->message);
			return TRUE;
		}
		g_propagate_error(error, g_steal_pointer(&error_local));
		return FALSE;
	}
	self->device_open = FALSE;
	return TRUE;
}

/**
 * fu_device_locker_new:
 * @device: a #FuDevice
 * @error: (nullable): optional return location for an error
 *
 * 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.
 *
 * NOTE: If the @open_func failed then the @close_func will not be called.
 *
 * Think of this object as the device ownership.
 *
 * Returns: a device locker, or %NULL if the @open_func failed.
 *
 * Since: 1.0.0
 **/
FuDeviceLocker *
fu_device_locker_new(FuDevice *device, GError **error)
{
	g_return_val_if_fail(FU_IS_DEVICE(device), NULL);
	g_return_val_if_fail(error == NULL || *error == NULL, NULL);

	/* FuDevice */
	return fu_device_locker_new_full(device, fu_device_open, fu_device_close, error);
}

/**
 * fu_device_locker_new_full:
 * @device: a #FuDevice
 * @open_func: (scope async): a function to open the device
 * @close_func: (scope async): a function to close the device
 * @error: (nullable): optional return location for an error
 *
 * 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 device locker, or %NULL if the @open_func failed.
 *
 * Since: 1.0.0
 **/
FuDeviceLocker *
fu_device_locker_new_full(FuDevice *device,
			  FuDeviceLockerFunc open_func,
			  FuDeviceLockerFunc close_func,
			  GError **error)
{
	g_autoptr(FuDeviceLocker) self = NULL;

	g_return_val_if_fail(FU_IS_DEVICE(device), 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 || *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)) {
		g_autoptr(GError) error_local = NULL;
		if (!self->close_func(device, &error_local)) {
			g_debug("ignoring close error on aborted open: %s", error_local->message);
		}
		return NULL;
	}

	/* success */
	self->device_open = TRUE;
	return g_steal_pointer(&self);
}