Coverage Report

Created: 2026-10-17 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}