]> Git Repo - qemu.git/blob - backends/dbus-vmstate.c
Move QOM typedefs and add missing includes
[qemu.git] / backends / dbus-vmstate.c
1 /*
2  * QEMU dbus-vmstate
3  *
4  * Copyright (C) 2019 Red Hat Inc
5  *
6  * Authors:
7  *  Marc-AndrĂ© Lureau <[email protected]>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12
13 #include "qemu/osdep.h"
14 #include "qemu/units.h"
15 #include "qemu/dbus.h"
16 #include "qemu/error-report.h"
17 #include "qapi/error.h"
18 #include "qom/object_interfaces.h"
19 #include "qapi/qmp/qerror.h"
20 #include "migration/vmstate.h"
21 #include "trace.h"
22 #include "qom/object.h"
23
24 typedef struct DBusVMState DBusVMState;
25 typedef struct DBusVMStateClass DBusVMStateClass;
26
27 #define TYPE_DBUS_VMSTATE "dbus-vmstate"
28 #define DBUS_VMSTATE(obj)                                \
29     OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE)
30 #define DBUS_VMSTATE_GET_CLASS(obj)                              \
31     OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE)
32 #define DBUS_VMSTATE_CLASS(klass)                                    \
33     OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE)
34
35 struct DBusVMStateClass {
36     ObjectClass parent_class;
37 };
38
39 struct DBusVMState {
40     Object parent;
41
42     GDBusConnection *bus;
43     char *dbus_addr;
44     char *id_list;
45
46     uint32_t data_size;
47     uint8_t *data;
48 };
49
50 static const GDBusPropertyInfo vmstate_property_info[] = {
51     { -1, (char *) "Id", (char *) "s",
52       G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
53 };
54
55 static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
56     &vmstate_property_info[0],
57     NULL
58 };
59
60 static const GDBusInterfaceInfo vmstate1_interface_info = {
61     -1,
62     (char *) "org.qemu.VMState1",
63     (GDBusMethodInfo **) NULL,
64     (GDBusSignalInfo **) NULL,
65     (GDBusPropertyInfo **) &vmstate_property_info_pointers,
66     NULL,
67 };
68
69 #define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
70
71 static GHashTable *
72 get_id_list_set(DBusVMState *self)
73 {
74     g_auto(GStrv) ids = NULL;
75     g_autoptr(GHashTable) set = NULL;
76     int i;
77
78     if (!self->id_list) {
79         return NULL;
80     }
81
82     ids = g_strsplit(self->id_list, ",", -1);
83     set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
84     for (i = 0; ids[i]; i++) {
85         g_hash_table_add(set, ids[i]);
86         ids[i] = NULL;
87     }
88
89     return g_steal_pointer(&set);
90 }
91
92 static GHashTable *
93 dbus_get_proxies(DBusVMState *self, GError **err)
94 {
95     g_autoptr(GHashTable) proxies = NULL;
96     g_autoptr(GHashTable) ids = NULL;
97     g_auto(GStrv) names = NULL;
98     Error *error = NULL;
99     size_t i;
100
101     ids = get_id_list_set(self);
102     proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
103                                     g_free, g_object_unref);
104
105     names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
106     if (!names) {
107         g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
108                     error_get_pretty(error));
109         error_free(error);
110         return NULL;
111     }
112
113     for (i = 0; names[i]; i++) {
114         g_autoptr(GDBusProxy) proxy = NULL;
115         g_autoptr(GVariant) result = NULL;
116         g_autofree char *id = NULL;
117         size_t size;
118
119         proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
120                     (GDBusInterfaceInfo *) &vmstate1_interface_info,
121                     names[i],
122                     "/org/qemu/VMState1",
123                     "org.qemu.VMState1",
124                     NULL, err);
125         if (!proxy) {
126             return NULL;
127         }
128
129         result = g_dbus_proxy_get_cached_property(proxy, "Id");
130         if (!result) {
131             g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
132                                 "VMState Id property is missing.");
133             return NULL;
134         }
135
136         id = g_variant_dup_string(result, &size);
137         if (ids && !g_hash_table_remove(ids, id)) {
138             g_clear_pointer(&id, g_free);
139             g_clear_object(&proxy);
140             continue;
141         }
142         if (size == 0 || size >= 256) {
143             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
144                         "VMState Id '%s' is invalid.", id);
145             return NULL;
146         }
147
148         if (!g_hash_table_insert(proxies, id, proxy)) {
149             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
150                         "Duplicated VMState Id '%s'", id);
151             return NULL;
152         }
153         id = NULL;
154         proxy = NULL;
155
156         g_clear_pointer(&result, g_variant_unref);
157     }
158
159     if (ids) {
160         g_autofree char **left = NULL;
161
162         left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
163         if (*left) {
164             g_autofree char *leftids = g_strjoinv(",", left);
165             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
166                         "Required VMState Id are missing: %s", leftids);
167             return NULL;
168         }
169     }
170
171     return g_steal_pointer(&proxies);
172 }
173
174 static int
175 dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
176 {
177     g_autoptr(GError) err = NULL;
178     g_autoptr(GVariant) result = NULL;
179     g_autoptr(GVariant) value = NULL;
180
181     value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
182                                       data, size, sizeof(char));
183     result = g_dbus_proxy_call_sync(proxy, "Load",
184                                     g_variant_new("(@ay)",
185                                                   g_steal_pointer(&value)),
186                                     G_DBUS_CALL_FLAGS_NO_AUTO_START,
187                                     -1, NULL, &err);
188     if (!result) {
189         error_report("%s: Failed to Load: %s", __func__, err->message);
190         return -1;
191     }
192
193     return 0;
194 }
195
196 static int dbus_vmstate_post_load(void *opaque, int version_id)
197 {
198     DBusVMState *self = DBUS_VMSTATE(opaque);
199     g_autoptr(GInputStream) m = NULL;
200     g_autoptr(GDataInputStream) s = NULL;
201     g_autoptr(GError) err = NULL;
202     g_autoptr(GHashTable) proxies = NULL;
203     uint32_t nelem;
204
205     trace_dbus_vmstate_post_load(version_id);
206
207     proxies = dbus_get_proxies(self, &err);
208     if (!proxies) {
209         error_report("%s: Failed to get proxies: %s", __func__, err->message);
210         return -1;
211     }
212
213     m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
214     s = g_data_input_stream_new(m);
215     g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
216
217     nelem = g_data_input_stream_read_uint32(s, NULL, &err);
218     if (err) {
219         goto error;
220     }
221
222     while (nelem > 0) {
223         GDBusProxy *proxy = NULL;
224         uint32_t len;
225         gsize bytes_read, avail;
226         char id[256];
227
228         len = g_data_input_stream_read_uint32(s, NULL, &err);
229         if (err) {
230             goto error;
231         }
232         if (len >= 256) {
233             error_report("%s: Invalid DBus vmstate proxy name %u",
234                          __func__, len);
235             return -1;
236         }
237         if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
238                                      &bytes_read, NULL, &err)) {
239             goto error;
240         }
241         g_return_val_if_fail(bytes_read == len, -1);
242         id[len] = 0;
243
244         trace_dbus_vmstate_loading(id);
245
246         proxy = g_hash_table_lookup(proxies, id);
247         if (!proxy) {
248             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
249             return -1;
250         }
251
252         len = g_data_input_stream_read_uint32(s, NULL, &err);
253         avail = g_buffered_input_stream_get_available(
254             G_BUFFERED_INPUT_STREAM(s));
255
256         if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
257             error_report("%s: Invalid vmstate size: %u", __func__, len);
258             return -1;
259         }
260
261         if (dbus_load_state_proxy(proxy,
262                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
263                                                     NULL),
264                 len) < 0) {
265             error_report("%s: Failed to restore Id '%s'", __func__, id);
266             return -1;
267         }
268
269         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
270             goto error;
271         }
272
273         nelem -= 1;
274     }
275
276     return 0;
277
278 error:
279     error_report("%s: Failed to read from stream: %s", __func__, err->message);
280     return -1;
281 }
282
283 static void
284 dbus_save_state_proxy(gpointer key,
285                       gpointer value,
286                       gpointer user_data)
287 {
288     GDataOutputStream *s = user_data;
289     const char *id = key;
290     GDBusProxy *proxy = value;
291     g_autoptr(GVariant) result = NULL;
292     g_autoptr(GVariant) child = NULL;
293     g_autoptr(GError) err = NULL;
294     const uint8_t *data;
295     gsize size;
296
297     trace_dbus_vmstate_saving(id);
298
299     result = g_dbus_proxy_call_sync(proxy, "Save",
300                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
301                                     -1, NULL, &err);
302     if (!result) {
303         error_report("%s: Failed to Save: %s", __func__, err->message);
304         return;
305     }
306
307     child = g_variant_get_child_value(result, 0);
308     data = g_variant_get_fixed_array(child, &size, sizeof(char));
309     if (!data) {
310         error_report("%s: Failed to Save: not a byte array", __func__);
311         return;
312     }
313     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
314         error_report("%s: Too large vmstate data to save: %zu",
315                      __func__, (size_t)size);
316         return;
317     }
318
319     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
320         !g_data_output_stream_put_string(s, id, NULL, &err) ||
321         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
322         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
323                                    data, size, NULL, NULL, &err)) {
324         error_report("%s: Failed to write to stream: %s",
325                      __func__, err->message);
326     }
327 }
328
329 static int dbus_vmstate_pre_save(void *opaque)
330 {
331     DBusVMState *self = DBUS_VMSTATE(opaque);
332     g_autoptr(GOutputStream) m = NULL;
333     g_autoptr(GDataOutputStream) s = NULL;
334     g_autoptr(GHashTable) proxies = NULL;
335     g_autoptr(GError) err = NULL;
336
337     trace_dbus_vmstate_pre_save();
338
339     proxies = dbus_get_proxies(self, &err);
340     if (!proxies) {
341         error_report("%s: Failed to get proxies: %s", __func__, err->message);
342         return -1;
343     }
344
345     m = g_memory_output_stream_new_resizable();
346     s = g_data_output_stream_new(m);
347     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
348
349     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
350                                          NULL, &err)) {
351         error_report("%s: Failed to write to stream: %s",
352                      __func__, err->message);
353         return -1;
354     }
355
356     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
357
358     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
359         > UINT32_MAX) {
360         error_report("%s: DBus vmstate buffer is too large", __func__);
361         return -1;
362     }
363
364     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
365         error_report("%s: Failed to close stream: %s", __func__, err->message);
366         return -1;
367     }
368
369     g_free(self->data);
370     self->data_size =
371         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
372     self->data =
373         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
374
375     return 0;
376 }
377
378 static const VMStateDescription dbus_vmstate = {
379     .name = TYPE_DBUS_VMSTATE,
380     .version_id = 0,
381     .pre_save = dbus_vmstate_pre_save,
382     .post_load = dbus_vmstate_post_load,
383     .fields = (VMStateField[]) {
384         VMSTATE_UINT32(data_size, DBusVMState),
385         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
386         VMSTATE_END_OF_LIST()
387     }
388 };
389
390 static void
391 dbus_vmstate_complete(UserCreatable *uc, Error **errp)
392 {
393     DBusVMState *self = DBUS_VMSTATE(uc);
394     g_autoptr(GError) err = NULL;
395
396     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
397         error_setg(errp, "There is already an instance of %s",
398                    TYPE_DBUS_VMSTATE);
399         return;
400     }
401
402     if (!self->dbus_addr) {
403         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
404         return;
405     }
406
407     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
408                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
409                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
410                     NULL, NULL, &err);
411     if (err) {
412         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
413         return;
414     }
415
416     if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
417                          &dbus_vmstate, self) < 0) {
418         error_setg(errp, "Failed to register vmstate");
419     }
420 }
421
422 static void
423 dbus_vmstate_finalize(Object *o)
424 {
425     DBusVMState *self = DBUS_VMSTATE(o);
426
427     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
428
429     g_clear_object(&self->bus);
430     g_free(self->dbus_addr);
431     g_free(self->id_list);
432     g_free(self->data);
433 }
434
435 static char *
436 get_dbus_addr(Object *o, Error **errp)
437 {
438     DBusVMState *self = DBUS_VMSTATE(o);
439
440     return g_strdup(self->dbus_addr);
441 }
442
443 static void
444 set_dbus_addr(Object *o, const char *str, Error **errp)
445 {
446     DBusVMState *self = DBUS_VMSTATE(o);
447
448     g_free(self->dbus_addr);
449     self->dbus_addr = g_strdup(str);
450 }
451
452 static char *
453 get_id_list(Object *o, Error **errp)
454 {
455     DBusVMState *self = DBUS_VMSTATE(o);
456
457     return g_strdup(self->id_list);
458 }
459
460 static void
461 set_id_list(Object *o, const char *str, Error **errp)
462 {
463     DBusVMState *self = DBUS_VMSTATE(o);
464
465     g_free(self->id_list);
466     self->id_list = g_strdup(str);
467 }
468
469 static char *
470 dbus_vmstate_get_id(VMStateIf *vmif)
471 {
472     return g_strdup(TYPE_DBUS_VMSTATE);
473 }
474
475 static void
476 dbus_vmstate_class_init(ObjectClass *oc, void *data)
477 {
478     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
479     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
480
481     ucc->complete = dbus_vmstate_complete;
482     vc->get_id = dbus_vmstate_get_id;
483
484     object_class_property_add_str(oc, "addr",
485                                   get_dbus_addr, set_dbus_addr);
486     object_class_property_add_str(oc, "id-list",
487                                   get_id_list, set_id_list);
488 }
489
490 static const TypeInfo dbus_vmstate_info = {
491     .name = TYPE_DBUS_VMSTATE,
492     .parent = TYPE_OBJECT,
493     .instance_size = sizeof(DBusVMState),
494     .instance_finalize = dbus_vmstate_finalize,
495     .class_size = sizeof(DBusVMStateClass),
496     .class_init = dbus_vmstate_class_init,
497     .interfaces = (InterfaceInfo[]) {
498         { TYPE_USER_CREATABLE },
499         { TYPE_VMSTATE_IF },
500         { }
501     }
502 };
503
504 static void
505 register_types(void)
506 {
507     type_register_static(&dbus_vmstate_info);
508 }
509
510 type_init(register_types);
This page took 0.052309 seconds and 4 git commands to generate.