/src/fwupd/libfwupdplugin/fu-device-event.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2024 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuDeviceEvent" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-device-event-private.h" |
12 | | #include "fu-mem.h" |
13 | | |
14 | | /** |
15 | | * FuDeviceEvent: |
16 | | * |
17 | | * A device event, used to enumulate hardware. |
18 | | * |
19 | | * See also: [class@FuDevice] |
20 | | */ |
21 | | |
22 | | typedef struct { |
23 | | GType gtype; |
24 | | gchar *key; |
25 | | GDestroyNotify key_destroy; |
26 | | gpointer data; |
27 | | GDestroyNotify data_destroy; |
28 | | } FuDeviceEventBlob; |
29 | | |
30 | | struct _FuDeviceEvent { |
31 | | GObject parent_instance; |
32 | | gchar *id; |
33 | | gchar *id_uncompressed; |
34 | | GPtrArray *values; /* element-type FuDeviceEventBlob */ |
35 | | }; |
36 | | |
37 | | static void |
38 | | fu_device_event_codec_iface_init(FwupdCodecInterface *iface); |
39 | | |
40 | | G_DEFINE_TYPE_WITH_CODE(FuDeviceEvent, |
41 | | fu_device_event, |
42 | | G_TYPE_OBJECT, |
43 | | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_device_event_codec_iface_init)) |
44 | | |
45 | | /* |
46 | | * NOTE: We use an event *counter* that gets the next event in the emulation, and this ID is only |
47 | | * used as a sanity check in case we have to skip an entry. |
48 | | */ |
49 | 0 | #define FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE 8 |
50 | | |
51 | | static void |
52 | | fu_device_event_blob_free(FuDeviceEventBlob *blob) |
53 | 0 | { |
54 | 0 | if (blob->key_destroy != NULL) |
55 | 0 | g_free(blob->key); |
56 | 0 | if (blob->data_destroy != NULL) |
57 | 0 | blob->data_destroy(blob->data); |
58 | 0 | g_free(blob); |
59 | 0 | } |
60 | | |
61 | | static FuDeviceEventBlob * |
62 | | fu_device_event_blob_new(GType gtype, const gchar *key, gpointer data, GDestroyNotify data_destroy) |
63 | 0 | { |
64 | 0 | FuDeviceEventBlob *blob = g_new0(FuDeviceEventBlob, 1); |
65 | 0 | const gchar *known_keys[] = { |
66 | 0 | "Data", |
67 | 0 | "DataOut", |
68 | 0 | "Error", |
69 | 0 | "ErrorMsg", |
70 | 0 | "Rc", |
71 | 0 | }; |
72 | |
|
73 | 0 | for (guint i = 0; i < G_N_ELEMENTS(known_keys); i++) { |
74 | 0 | if (g_strcmp0(key, known_keys[i]) == 0) { |
75 | 0 | blob->key = (gchar *)known_keys[i]; |
76 | 0 | break; |
77 | 0 | } |
78 | 0 | } |
79 | 0 | if (blob->key == NULL) { |
80 | 0 | blob->key = g_strdup(key); |
81 | 0 | blob->key_destroy = g_free; |
82 | 0 | } |
83 | 0 | blob->gtype = gtype; |
84 | 0 | blob->data = data; |
85 | 0 | blob->data_destroy = data_destroy; |
86 | 0 | return blob; |
87 | 0 | } |
88 | | |
89 | | /** |
90 | | * fu_device_event_build_id: |
91 | | * @id: a string |
92 | | * |
93 | | * Return the hash of the event ID. |
94 | | * |
95 | | * Returns: string hash prefix |
96 | | * |
97 | | * Since: 2.0.3 |
98 | | **/ |
99 | | gchar * |
100 | | fu_device_event_build_id(const gchar *id) |
101 | 0 | { |
102 | 0 | guint8 buf[20] = {0}; |
103 | 0 | gsize bufsz = sizeof(buf); |
104 | 0 | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA1); |
105 | 0 | g_autoptr(GString) id_hash = g_string_sized_new(FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE + 1); |
106 | |
|
107 | 0 | g_return_val_if_fail(id != NULL, NULL); |
108 | | |
109 | | /* IMPORTANT: if you're reading this we're not using the SHA1 prefix for any kind of secure |
110 | | * hash, just because it is a tiny string that takes up less memory than the full ID. */ |
111 | 0 | g_checksum_update(csum, (const guchar *)id, strlen(id)); |
112 | 0 | g_checksum_get_digest(csum, buf, &bufsz); |
113 | 0 | g_string_append_c(id_hash, '#'); |
114 | 0 | for (guint i = 0; i < FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE / 2; i++) |
115 | 0 | g_string_append_printf(id_hash, "%02x", buf[i]); |
116 | 0 | return g_string_free(g_steal_pointer(&id_hash), FALSE); |
117 | 0 | } |
118 | | |
119 | | /** |
120 | | * fu_device_event_get_id: |
121 | | * @self: a #FuDeviceEvent |
122 | | * |
123 | | * Return the truncated SHA1 of the #FuDeviceEvent key, which is normally set when creating the |
124 | | * object. |
125 | | * |
126 | | * Returns: (nullable): string |
127 | | * |
128 | | * Since: 2.0.0 |
129 | | **/ |
130 | | const gchar * |
131 | | fu_device_event_get_id(FuDeviceEvent *self) |
132 | 0 | { |
133 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
134 | 0 | return self->id; |
135 | 0 | } |
136 | | |
137 | | /** |
138 | | * fu_device_event_set_str: |
139 | | * @self: a #FuDeviceEvent |
140 | | * @key: (not nullable): a unique key, e.g. `Name` |
141 | | * @value: (nullable): a string |
142 | | * |
143 | | * Sets a string value on the event. |
144 | | * |
145 | | * Since: 2.0.0 |
146 | | **/ |
147 | | void |
148 | | fu_device_event_set_str(FuDeviceEvent *self, const gchar *key, const gchar *value) |
149 | 0 | { |
150 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
151 | 0 | g_return_if_fail(key != NULL); |
152 | 0 | g_ptr_array_add(self->values, |
153 | 0 | fu_device_event_blob_new(G_TYPE_STRING, key, g_strdup(value), g_free)); |
154 | 0 | } |
155 | | |
156 | | /** |
157 | | * fu_device_event_set_i64: |
158 | | * @self: a #FuDeviceEvent |
159 | | * @key: (not nullable): a unique key, e.g. `Name` |
160 | | * @value: a string |
161 | | * |
162 | | * Sets an integer value on the string. |
163 | | * |
164 | | * Since: 2.0.0 |
165 | | **/ |
166 | | void |
167 | | fu_device_event_set_i64(FuDeviceEvent *self, const gchar *key, gint64 value) |
168 | 0 | { |
169 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
170 | 0 | g_return_if_fail(key != NULL); |
171 | | |
172 | 0 | g_ptr_array_add( |
173 | 0 | self->values, |
174 | 0 | fu_device_event_blob_new(G_TYPE_INT, key, g_memdup2(&value, sizeof(value)), g_free)); |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * fu_device_event_set_bytes: |
179 | | * @self: a #FuDeviceEvent |
180 | | * @key: (not nullable): a unique key, e.g. `Name` |
181 | | * @value: (not nullable): a #GBytes |
182 | | * |
183 | | * Sets a blob on the event. Note: blobs are stored internally as BASE-64 strings. |
184 | | * |
185 | | * Since: 2.0.0 |
186 | | **/ |
187 | | void |
188 | | fu_device_event_set_bytes(FuDeviceEvent *self, const gchar *key, GBytes *value) |
189 | 0 | { |
190 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
191 | 0 | g_return_if_fail(key != NULL); |
192 | 0 | g_return_if_fail(value != NULL); |
193 | 0 | g_ptr_array_add(self->values, |
194 | 0 | fu_device_event_blob_new( |
195 | 0 | G_TYPE_STRING, |
196 | 0 | key, |
197 | 0 | g_base64_encode(g_bytes_get_data(value, NULL), g_bytes_get_size(value)), |
198 | 0 | g_free)); |
199 | 0 | } |
200 | | |
201 | | /** |
202 | | * fu_device_event_set_data: |
203 | | * @self: a #FuDeviceEvent |
204 | | * @key: (not nullable): a unique key, e.g. `Name` |
205 | | * @buf: (nullable): a buffer |
206 | | * @bufsz: size of @buf |
207 | | * |
208 | | * Sets a memory buffer on the event. Note: memory buffers are stored internally as BASE-64 strings. |
209 | | * |
210 | | * Since: 2.0.0 |
211 | | **/ |
212 | | void |
213 | | fu_device_event_set_data(FuDeviceEvent *self, const gchar *key, const guint8 *buf, gsize bufsz) |
214 | 0 | { |
215 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
216 | 0 | g_return_if_fail(key != NULL); |
217 | 0 | g_ptr_array_add( |
218 | 0 | self->values, |
219 | 0 | fu_device_event_blob_new(G_TYPE_STRING, key, g_base64_encode(buf, bufsz), g_free)); |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | * fu_device_event_set_error: |
224 | | * @self: a #FuDeviceEvent |
225 | | * @error: (not nullable): a #GError with domain #FwupdError |
226 | | * |
227 | | * Sets an error on the event. |
228 | | * |
229 | | * Since: 2.0.6 |
230 | | **/ |
231 | | void |
232 | | fu_device_event_set_error(FuDeviceEvent *self, const GError *error) |
233 | 0 | { |
234 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
235 | 0 | g_return_if_fail(error != NULL); |
236 | 0 | g_return_if_fail(error->domain == FWUPD_ERROR); |
237 | 0 | fu_device_event_set_i64(self, "Error", error->code); |
238 | 0 | fu_device_event_set_str(self, "ErrorMsg", error->message); |
239 | 0 | } |
240 | | |
241 | | /** |
242 | | * fu_device_event_check_error: |
243 | | * @self: a #FuDeviceEvent |
244 | | * @error: (nullable): optional return location for an error |
245 | | * |
246 | | * Sets an error from the event if possible. |
247 | | * |
248 | | * Returns: %FALSE if @error was set |
249 | | * |
250 | | * Since: 2.0.6 |
251 | | **/ |
252 | | gboolean |
253 | | fu_device_event_check_error(FuDeviceEvent *self, GError **error) |
254 | 0 | { |
255 | 0 | gint64 code; |
256 | 0 | const gchar *message; |
257 | |
|
258 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE); |
259 | | |
260 | | /* nothing to do */ |
261 | 0 | if (error == NULL) |
262 | 0 | return TRUE; |
263 | | |
264 | | /* anything set */ |
265 | 0 | code = fu_device_event_get_i64(self, "Error", NULL); |
266 | 0 | if (code == G_MAXINT64) |
267 | 0 | return TRUE; |
268 | 0 | message = fu_device_event_get_str(self, "ErrorMsg", NULL); |
269 | 0 | if (message == NULL) |
270 | 0 | message = fwupd_error_to_string(code); |
271 | | |
272 | | /* success, in a way */ |
273 | 0 | g_set_error_literal(error, FWUPD_ERROR, code, message); |
274 | 0 | return FALSE; |
275 | 0 | } |
276 | | |
277 | | static gpointer |
278 | | fu_device_event_lookup(FuDeviceEvent *self, const gchar *key, GType gtype, GError **error) |
279 | 0 | { |
280 | 0 | FuDeviceEventBlob *blob = NULL; |
281 | |
|
282 | 0 | for (guint i = 0; i < self->values->len; i++) { |
283 | 0 | FuDeviceEventBlob *blob_tmp = g_ptr_array_index(self->values, i); |
284 | 0 | if (g_strcmp0(blob_tmp->key, key) == 0) { |
285 | 0 | blob = blob_tmp; |
286 | 0 | break; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | if (blob == NULL) { |
290 | 0 | g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no event for key %s", key); |
291 | 0 | return NULL; |
292 | 0 | } |
293 | 0 | if (blob->gtype != gtype) { |
294 | 0 | g_set_error(error, |
295 | 0 | FWUPD_ERROR, |
296 | 0 | FWUPD_ERROR_INVALID_DATA, |
297 | 0 | "invalid event type for key %s", |
298 | 0 | key); |
299 | 0 | return NULL; |
300 | 0 | } |
301 | 0 | return blob->data; |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * fu_device_event_get_str: |
306 | | * @self: a #FuDeviceEvent |
307 | | * @key: (not nullable): a unique key, e.g. `Name` |
308 | | * @error: (nullable): optional return location for an error |
309 | | * |
310 | | * Gets a string value from the event. |
311 | | * |
312 | | * Returns: (nullable): string, or %NULL on error |
313 | | * |
314 | | * Since: 2.0.0 |
315 | | **/ |
316 | | const gchar * |
317 | | fu_device_event_get_str(FuDeviceEvent *self, const gchar *key, GError **error) |
318 | 0 | { |
319 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
320 | 0 | g_return_val_if_fail(key != NULL, NULL); |
321 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
322 | 0 | return (const gchar *)fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
323 | 0 | } |
324 | | |
325 | | /** |
326 | | * fu_device_event_get_i64: |
327 | | * @self: a #FuDeviceEvent |
328 | | * @key: (not nullable): a unique key, e.g. `Name` |
329 | | * @error: (nullable): optional return location for an error |
330 | | * |
331 | | * Gets an integer value from the event. |
332 | | * |
333 | | * Returns: integer, or %G_MAXINT64 on error |
334 | | * |
335 | | * Since: 2.0.0 |
336 | | **/ |
337 | | gint64 |
338 | | fu_device_event_get_i64(FuDeviceEvent *self, const gchar *key, GError **error) |
339 | 0 | { |
340 | 0 | gint64 *val; |
341 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), G_MAXINT64); |
342 | 0 | g_return_val_if_fail(key != NULL, G_MAXINT64); |
343 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, G_MAXINT64); |
344 | 0 | val = fu_device_event_lookup(self, key, G_TYPE_INT, error); |
345 | 0 | if (val == NULL) |
346 | 0 | return G_MAXINT64; |
347 | 0 | return *val; |
348 | 0 | } |
349 | | |
350 | | /** |
351 | | * fu_device_event_get_bytes: |
352 | | * @self: a #FuDeviceEvent |
353 | | * @key: (not nullable): a unique key, e.g. `Name` |
354 | | * @error: (nullable): optional return location for an error |
355 | | * |
356 | | * Gets a memory blob from the event. |
357 | | * |
358 | | * Returns: (transfer full) (nullable): byes data, or %NULL on error |
359 | | * |
360 | | * Since: 2.0.0 |
361 | | **/ |
362 | | GBytes * |
363 | | fu_device_event_get_bytes(FuDeviceEvent *self, const gchar *key, GError **error) |
364 | 0 | { |
365 | 0 | const gchar *blobstr; |
366 | 0 | gsize bufsz = 0; |
367 | 0 | g_autofree guchar *buf = NULL; |
368 | |
|
369 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
370 | 0 | g_return_val_if_fail(key != NULL, NULL); |
371 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
372 | | |
373 | 0 | blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
374 | 0 | if (blobstr == NULL) |
375 | 0 | return NULL; |
376 | 0 | if (blobstr[0] == '\0') |
377 | 0 | return g_bytes_new(NULL, 0); |
378 | 0 | buf = g_base64_decode(blobstr, &bufsz); |
379 | 0 | return g_bytes_new_take(g_steal_pointer(&buf), bufsz); |
380 | 0 | } |
381 | | |
382 | | /** |
383 | | * fu_device_event_copy_data: |
384 | | * @self: a #FuDeviceEvent |
385 | | * @key: (not nullable): a unique key, e.g. `Name` |
386 | | * @buf: (nullable): a buffer |
387 | | * @bufsz: size of @buf |
388 | | * @actual_length: (out) (optional): the actual number of bytes sent, or %NULL |
389 | | * @error: (nullable): optional return location for an error |
390 | | * |
391 | | * Copies memory from the event. |
392 | | * |
393 | | * Returns: %TRUE if the buffer was copied |
394 | | * |
395 | | * Since: 2.0.0 |
396 | | **/ |
397 | | gboolean |
398 | | fu_device_event_copy_data(FuDeviceEvent *self, |
399 | | const gchar *key, |
400 | | guint8 *buf, |
401 | | gsize bufsz, |
402 | | gsize *actual_length, |
403 | | GError **error) |
404 | 0 | { |
405 | 0 | const gchar *blobstr; |
406 | 0 | gsize bufsz_src = 0; |
407 | 0 | g_autofree guchar *buf_src = NULL; |
408 | |
|
409 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE); |
410 | 0 | g_return_val_if_fail(key != NULL, FALSE); |
411 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
412 | | |
413 | 0 | blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
414 | 0 | if (blobstr == NULL) |
415 | 0 | return FALSE; |
416 | 0 | buf_src = g_base64_decode(blobstr, &bufsz_src); |
417 | 0 | if (actual_length != NULL) |
418 | 0 | *actual_length = bufsz_src; |
419 | 0 | if (buf != NULL) |
420 | 0 | return fu_memcpy_safe(buf, bufsz, 0x0, buf_src, bufsz_src, 0x0, bufsz_src, error); |
421 | 0 | return TRUE; |
422 | 0 | } |
423 | | |
424 | | static void |
425 | | fu_device_event_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags) |
426 | 0 | { |
427 | 0 | FuDeviceEvent *self = FU_DEVICE_EVENT(codec); |
428 | |
|
429 | 0 | if (self->id_uncompressed != NULL && (flags & FWUPD_CODEC_FLAG_COMPRESSED) == 0) { |
430 | 0 | json_builder_set_member_name(builder, "Id"); |
431 | 0 | json_builder_add_string_value(builder, self->id_uncompressed); |
432 | 0 | } else if (self->id != NULL) { |
433 | 0 | json_builder_set_member_name(builder, "Id"); |
434 | 0 | json_builder_add_string_value(builder, self->id); |
435 | 0 | } |
436 | |
|
437 | 0 | for (guint i = 0; i < self->values->len; i++) { |
438 | 0 | FuDeviceEventBlob *blob = g_ptr_array_index(self->values, i); |
439 | 0 | if (blob->gtype == G_TYPE_INT) { |
440 | 0 | json_builder_set_member_name(builder, blob->key); |
441 | 0 | json_builder_add_int_value(builder, *((gint64 *)blob->data)); |
442 | 0 | } else if (blob->gtype == G_TYPE_BYTES || blob->gtype == G_TYPE_STRING) { |
443 | 0 | json_builder_set_member_name(builder, blob->key); |
444 | 0 | json_builder_add_string_value(builder, (const gchar *)blob->data); |
445 | 0 | } else { |
446 | 0 | g_warning("invalid GType %s, ignoring", g_type_name(blob->gtype)); |
447 | 0 | } |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | | static void |
452 | | fu_device_event_set_id(FuDeviceEvent *self, const gchar *id) |
453 | 0 | { |
454 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
455 | 0 | g_return_if_fail(id != NULL); |
456 | | |
457 | 0 | g_clear_pointer(&self->id, g_free); |
458 | 0 | g_clear_pointer(&self->id_uncompressed, g_free); |
459 | | |
460 | | /* already a truncated SHA1 hash? */ |
461 | 0 | if (g_str_has_prefix(id, "#")) { |
462 | 0 | self->id = g_strdup(id); |
463 | 0 | } else { |
464 | 0 | self->id_uncompressed = g_strdup(id); |
465 | 0 | self->id = fu_device_event_build_id(id); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | static gboolean |
470 | | fu_device_event_from_json(FwupdCodec *codec, JsonNode *json_node, GError **error) |
471 | 0 | { |
472 | 0 | FuDeviceEvent *self = FU_DEVICE_EVENT(codec); |
473 | 0 | JsonNode *member_node; |
474 | 0 | JsonObjectIter iter; |
475 | 0 | JsonObject *json_object = json_node_get_object(json_node); |
476 | 0 | const gchar *member_name; |
477 | |
|
478 | 0 | json_object_iter_init(&iter, json_object); |
479 | 0 | while (json_object_iter_next(&iter, &member_name, &member_node)) { |
480 | 0 | GType gtype; |
481 | 0 | if (JSON_NODE_TYPE(member_node) != JSON_NODE_VALUE) |
482 | 0 | continue; |
483 | 0 | gtype = json_node_get_value_type(member_node); |
484 | 0 | if (gtype == G_TYPE_STRING) { |
485 | 0 | const gchar *str = json_node_get_string(member_node); |
486 | 0 | if (g_strcmp0(member_name, "Id") == 0) { |
487 | 0 | fu_device_event_set_id(self, str); |
488 | 0 | } else { |
489 | 0 | fu_device_event_set_str(self, member_name, str); |
490 | 0 | } |
491 | 0 | } else if (gtype == G_TYPE_INT64) { |
492 | 0 | fu_device_event_set_i64(self, member_name, json_node_get_int(member_node)); |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | /* we do not need this again, so avoid keeping all the tree data in memory */ |
497 | 0 | json_node_init_null(json_node); |
498 | | |
499 | | /* success */ |
500 | 0 | return TRUE; |
501 | 0 | } |
502 | | |
503 | | static void |
504 | | fu_device_event_init(FuDeviceEvent *self) |
505 | 0 | { |
506 | 0 | self->values = g_ptr_array_new_with_free_func((GDestroyNotify)fu_device_event_blob_free); |
507 | 0 | } |
508 | | |
509 | | static void |
510 | | fu_device_event_finalize(GObject *object) |
511 | 0 | { |
512 | 0 | FuDeviceEvent *self = FU_DEVICE_EVENT(object); |
513 | 0 | g_free(self->id); |
514 | 0 | g_free(self->id_uncompressed); |
515 | 0 | g_ptr_array_unref(self->values); |
516 | 0 | G_OBJECT_CLASS(fu_device_event_parent_class)->finalize(object); |
517 | 0 | } |
518 | | |
519 | | static void |
520 | | fu_device_event_class_init(FuDeviceEventClass *klass) |
521 | 0 | { |
522 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
523 | 0 | object_class->finalize = fu_device_event_finalize; |
524 | 0 | } |
525 | | |
526 | | static void |
527 | | fu_device_event_codec_iface_init(FwupdCodecInterface *iface) |
528 | 0 | { |
529 | 0 | iface->add_json = fu_device_event_add_json; |
530 | 0 | iface->from_json = fu_device_event_from_json; |
531 | 0 | } |
532 | | |
533 | | /** |
534 | | * fu_device_event_new: |
535 | | * @id: a cache key, which is converted to a truncated SHA1 hash if required |
536 | | * |
537 | | * Return value: (transfer full): a new #FuDeviceEvent object. |
538 | | * |
539 | | * Since: 2.0.0 |
540 | | **/ |
541 | | FuDeviceEvent * |
542 | | fu_device_event_new(const gchar *id) |
543 | 0 | { |
544 | 0 | FuDeviceEvent *self = g_object_new(FU_TYPE_DEVICE_EVENT, NULL); |
545 | 0 | if (id != NULL) |
546 | 0 | fu_device_event_set_id(self, id); |
547 | 0 | return FU_DEVICE_EVENT(self); |
548 | 0 | } |