]> git.proxmox.com Git - mirror_qemu.git/blame - backends/dbus-vmstate.c
Move QOM typedefs and add missing includes
[mirror_qemu.git] / backends / dbus-vmstate.c
CommitLineData
5010cec2
MAL
1/*
2 * QEMU dbus-vmstate
3 *
4 * Copyright (C) 2019 Red Hat Inc
5 *
6 * Authors:
7 * Marc-André Lureau <marcandre.lureau@redhat.com>
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"
db1015e9 22#include "qom/object.h"
5010cec2
MAL
23
24typedef struct DBusVMState DBusVMState;
25typedef 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
35struct DBusVMStateClass {
36 ObjectClass parent_class;
37};
38
39struct 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
50static const GDBusPropertyInfo vmstate_property_info[] = {
51 { -1, (char *) "Id", (char *) "s",
52 G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
53};
54
55static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
56 &vmstate_property_info[0],
57 NULL
58};
59
60static 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
71static GHashTable *
72get_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
92static GHashTable *
93dbus_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
174static int
175dbus_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
196static 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
278error:
279 error_report("%s: Failed to read from stream: %s", __func__, err->message);
280 return -1;
281}
282
283static void
284dbus_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
329static 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
378static 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
390static void
391dbus_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
1df2c9a2
PX
416 if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
417 &dbus_vmstate, self) < 0) {
5010cec2
MAL
418 error_setg(errp, "Failed to register vmstate");
419 }
420}
421
422static void
423dbus_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
435static char *
436get_dbus_addr(Object *o, Error **errp)
437{
438 DBusVMState *self = DBUS_VMSTATE(o);
439
440 return g_strdup(self->dbus_addr);
441}
442
443static void
444set_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
452static char *
453get_id_list(Object *o, Error **errp)
454{
455 DBusVMState *self = DBUS_VMSTATE(o);
456
457 return g_strdup(self->id_list);
458}
459
460static void
461set_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
469static char *
470dbus_vmstate_get_id(VMStateIf *vmif)
471{
472 return g_strdup(TYPE_DBUS_VMSTATE);
473}
474
475static void
476dbus_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",
d2623129 485 get_dbus_addr, set_dbus_addr);
5010cec2 486 object_class_property_add_str(oc, "id-list",
d2623129 487 get_id_list, set_id_list);
5010cec2
MAL
488}
489
490static 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
504static void
505register_types(void)
506{
507 type_register_static(&dbus_vmstate_info);
508}
509
510type_init(register_types);