xref: /openbmc/qemu/backends/dbus-vmstate.c (revision 166a1cf404cdea4c5839e3bd3028a6d28cb25b43)
15010cec2SMarc-André Lureau /*
25010cec2SMarc-André Lureau  * QEMU dbus-vmstate
35010cec2SMarc-André Lureau  *
45010cec2SMarc-André Lureau  * Copyright (C) 2019 Red Hat Inc
55010cec2SMarc-André Lureau  *
65010cec2SMarc-André Lureau  * Authors:
75010cec2SMarc-André Lureau  *  Marc-André Lureau <marcandre.lureau@redhat.com>
85010cec2SMarc-André Lureau  *
95010cec2SMarc-André Lureau  * This work is licensed under the terms of the GNU GPL, version 2 or later.
105010cec2SMarc-André Lureau  * See the COPYING file in the top-level directory.
115010cec2SMarc-André Lureau  */
125010cec2SMarc-André Lureau 
135010cec2SMarc-André Lureau #include "qemu/osdep.h"
145010cec2SMarc-André Lureau #include "qemu/units.h"
155010cec2SMarc-André Lureau #include "qemu/dbus.h"
165010cec2SMarc-André Lureau #include "qemu/error-report.h"
175010cec2SMarc-André Lureau #include "qapi/error.h"
185010cec2SMarc-André Lureau #include "qom/object_interfaces.h"
195010cec2SMarc-André Lureau #include "qapi/qmp/qerror.h"
205010cec2SMarc-André Lureau #include "migration/vmstate.h"
215010cec2SMarc-André Lureau #include "trace.h"
22db1015e9SEduardo Habkost #include "qom/object.h"
235010cec2SMarc-André Lureau 
245010cec2SMarc-André Lureau 
255010cec2SMarc-André Lureau #define TYPE_DBUS_VMSTATE "dbus-vmstate"
2630b5707cSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(DBusVMState,
27c734cd40SEduardo Habkost                            DBUS_VMSTATE)
285010cec2SMarc-André Lureau 
295010cec2SMarc-André Lureau 
305010cec2SMarc-André Lureau struct DBusVMState {
315010cec2SMarc-André Lureau     Object parent;
325010cec2SMarc-André Lureau 
335010cec2SMarc-André Lureau     GDBusConnection *bus;
345010cec2SMarc-André Lureau     char *dbus_addr;
355010cec2SMarc-André Lureau     char *id_list;
365010cec2SMarc-André Lureau 
375010cec2SMarc-André Lureau     uint32_t data_size;
385010cec2SMarc-André Lureau     uint8_t *data;
395010cec2SMarc-André Lureau };
405010cec2SMarc-André Lureau 
415010cec2SMarc-André Lureau static const GDBusPropertyInfo vmstate_property_info[] = {
425010cec2SMarc-André Lureau     { -1, (char *) "Id", (char *) "s",
435010cec2SMarc-André Lureau       G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
445010cec2SMarc-André Lureau };
455010cec2SMarc-André Lureau 
465010cec2SMarc-André Lureau static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
475010cec2SMarc-André Lureau     &vmstate_property_info[0],
485010cec2SMarc-André Lureau     NULL
495010cec2SMarc-André Lureau };
505010cec2SMarc-André Lureau 
515010cec2SMarc-André Lureau static const GDBusInterfaceInfo vmstate1_interface_info = {
525010cec2SMarc-André Lureau     -1,
535010cec2SMarc-André Lureau     (char *) "org.qemu.VMState1",
545010cec2SMarc-André Lureau     (GDBusMethodInfo **) NULL,
555010cec2SMarc-André Lureau     (GDBusSignalInfo **) NULL,
565010cec2SMarc-André Lureau     (GDBusPropertyInfo **) &vmstate_property_info_pointers,
575010cec2SMarc-André Lureau     NULL,
585010cec2SMarc-André Lureau };
595010cec2SMarc-André Lureau 
605010cec2SMarc-André Lureau #define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
615010cec2SMarc-André Lureau 
625010cec2SMarc-André Lureau static GHashTable *
635010cec2SMarc-André Lureau get_id_list_set(DBusVMState *self)
645010cec2SMarc-André Lureau {
655010cec2SMarc-André Lureau     g_auto(GStrv) ids = NULL;
665010cec2SMarc-André Lureau     g_autoptr(GHashTable) set = NULL;
675010cec2SMarc-André Lureau     int i;
685010cec2SMarc-André Lureau 
695010cec2SMarc-André Lureau     if (!self->id_list) {
705010cec2SMarc-André Lureau         return NULL;
715010cec2SMarc-André Lureau     }
725010cec2SMarc-André Lureau 
735010cec2SMarc-André Lureau     ids = g_strsplit(self->id_list, ",", -1);
745010cec2SMarc-André Lureau     set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
755010cec2SMarc-André Lureau     for (i = 0; ids[i]; i++) {
765010cec2SMarc-André Lureau         g_hash_table_add(set, ids[i]);
775010cec2SMarc-André Lureau         ids[i] = NULL;
785010cec2SMarc-André Lureau     }
795010cec2SMarc-André Lureau 
805010cec2SMarc-André Lureau     return g_steal_pointer(&set);
815010cec2SMarc-André Lureau }
825010cec2SMarc-André Lureau 
835010cec2SMarc-André Lureau static GHashTable *
845010cec2SMarc-André Lureau dbus_get_proxies(DBusVMState *self, GError **err)
855010cec2SMarc-André Lureau {
865010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
875010cec2SMarc-André Lureau     g_autoptr(GHashTable) ids = NULL;
885010cec2SMarc-André Lureau     g_auto(GStrv) names = NULL;
895010cec2SMarc-André Lureau     Error *error = NULL;
905010cec2SMarc-André Lureau     size_t i;
915010cec2SMarc-André Lureau 
925010cec2SMarc-André Lureau     ids = get_id_list_set(self);
935010cec2SMarc-André Lureau     proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
945010cec2SMarc-André Lureau                                     g_free, g_object_unref);
955010cec2SMarc-André Lureau 
965010cec2SMarc-André Lureau     names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
975010cec2SMarc-André Lureau     if (!names) {
985010cec2SMarc-André Lureau         g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
995010cec2SMarc-André Lureau                     error_get_pretty(error));
1005010cec2SMarc-André Lureau         error_free(error);
1015010cec2SMarc-André Lureau         return NULL;
1025010cec2SMarc-André Lureau     }
1035010cec2SMarc-André Lureau 
1045010cec2SMarc-André Lureau     for (i = 0; names[i]; i++) {
1055010cec2SMarc-André Lureau         g_autoptr(GDBusProxy) proxy = NULL;
1065010cec2SMarc-André Lureau         g_autoptr(GVariant) result = NULL;
1075010cec2SMarc-André Lureau         g_autofree char *id = NULL;
1085010cec2SMarc-André Lureau         size_t size;
1095010cec2SMarc-André Lureau 
1105010cec2SMarc-André Lureau         proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
1115010cec2SMarc-André Lureau                     (GDBusInterfaceInfo *) &vmstate1_interface_info,
1125010cec2SMarc-André Lureau                     names[i],
1135010cec2SMarc-André Lureau                     "/org/qemu/VMState1",
1145010cec2SMarc-André Lureau                     "org.qemu.VMState1",
1155010cec2SMarc-André Lureau                     NULL, err);
1165010cec2SMarc-André Lureau         if (!proxy) {
1175010cec2SMarc-André Lureau             return NULL;
1185010cec2SMarc-André Lureau         }
1195010cec2SMarc-André Lureau 
1205010cec2SMarc-André Lureau         result = g_dbus_proxy_get_cached_property(proxy, "Id");
1215010cec2SMarc-André Lureau         if (!result) {
1225010cec2SMarc-André Lureau             g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1235010cec2SMarc-André Lureau                                 "VMState Id property is missing.");
1245010cec2SMarc-André Lureau             return NULL;
1255010cec2SMarc-André Lureau         }
1265010cec2SMarc-André Lureau 
1275010cec2SMarc-André Lureau         id = g_variant_dup_string(result, &size);
1285010cec2SMarc-André Lureau         if (ids && !g_hash_table_remove(ids, id)) {
1295010cec2SMarc-André Lureau             g_clear_pointer(&id, g_free);
1305010cec2SMarc-André Lureau             g_clear_object(&proxy);
1315010cec2SMarc-André Lureau             continue;
1325010cec2SMarc-André Lureau         }
1335010cec2SMarc-André Lureau         if (size == 0 || size >= 256) {
1345010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1355010cec2SMarc-André Lureau                         "VMState Id '%s' is invalid.", id);
1365010cec2SMarc-André Lureau             return NULL;
1375010cec2SMarc-André Lureau         }
1385010cec2SMarc-André Lureau 
1395010cec2SMarc-André Lureau         if (!g_hash_table_insert(proxies, id, proxy)) {
1405010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1415010cec2SMarc-André Lureau                         "Duplicated VMState Id '%s'", id);
1425010cec2SMarc-André Lureau             return NULL;
1435010cec2SMarc-André Lureau         }
1445010cec2SMarc-André Lureau         id = NULL;
1455010cec2SMarc-André Lureau         proxy = NULL;
1465010cec2SMarc-André Lureau 
1475010cec2SMarc-André Lureau         g_clear_pointer(&result, g_variant_unref);
1485010cec2SMarc-André Lureau     }
1495010cec2SMarc-André Lureau 
1505010cec2SMarc-André Lureau     if (ids) {
1515010cec2SMarc-André Lureau         g_autofree char **left = NULL;
1525010cec2SMarc-André Lureau 
1535010cec2SMarc-André Lureau         left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
1545010cec2SMarc-André Lureau         if (*left) {
1555010cec2SMarc-André Lureau             g_autofree char *leftids = g_strjoinv(",", left);
1565010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1575010cec2SMarc-André Lureau                         "Required VMState Id are missing: %s", leftids);
1585010cec2SMarc-André Lureau             return NULL;
1595010cec2SMarc-André Lureau         }
1605010cec2SMarc-André Lureau     }
1615010cec2SMarc-André Lureau 
1625010cec2SMarc-André Lureau     return g_steal_pointer(&proxies);
1635010cec2SMarc-André Lureau }
1645010cec2SMarc-André Lureau 
1655010cec2SMarc-André Lureau static int
1665010cec2SMarc-André Lureau dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
1675010cec2SMarc-André Lureau {
1685010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
1695010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
1705010cec2SMarc-André Lureau     g_autoptr(GVariant) value = NULL;
1715010cec2SMarc-André Lureau 
1725010cec2SMarc-André Lureau     value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
1735010cec2SMarc-André Lureau                                       data, size, sizeof(char));
1745010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Load",
1755010cec2SMarc-André Lureau                                     g_variant_new("(@ay)",
1765010cec2SMarc-André Lureau                                                   g_steal_pointer(&value)),
1775010cec2SMarc-André Lureau                                     G_DBUS_CALL_FLAGS_NO_AUTO_START,
1785010cec2SMarc-André Lureau                                     -1, NULL, &err);
1795010cec2SMarc-André Lureau     if (!result) {
1805010cec2SMarc-André Lureau         error_report("%s: Failed to Load: %s", __func__, err->message);
1815010cec2SMarc-André Lureau         return -1;
1825010cec2SMarc-André Lureau     }
1835010cec2SMarc-André Lureau 
1845010cec2SMarc-André Lureau     return 0;
1855010cec2SMarc-André Lureau }
1865010cec2SMarc-André Lureau 
1875010cec2SMarc-André Lureau static int dbus_vmstate_post_load(void *opaque, int version_id)
1885010cec2SMarc-André Lureau {
1895010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
1905010cec2SMarc-André Lureau     g_autoptr(GInputStream) m = NULL;
1915010cec2SMarc-André Lureau     g_autoptr(GDataInputStream) s = NULL;
1925010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
1935010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
1945010cec2SMarc-André Lureau     uint32_t nelem;
1955010cec2SMarc-André Lureau 
1965010cec2SMarc-André Lureau     trace_dbus_vmstate_post_load(version_id);
1975010cec2SMarc-André Lureau 
1985010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
1995010cec2SMarc-André Lureau     if (!proxies) {
2005010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
2015010cec2SMarc-André Lureau         return -1;
2025010cec2SMarc-André Lureau     }
2035010cec2SMarc-André Lureau 
2045010cec2SMarc-André Lureau     m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
2055010cec2SMarc-André Lureau     s = g_data_input_stream_new(m);
2065010cec2SMarc-André Lureau     g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
2075010cec2SMarc-André Lureau 
2085010cec2SMarc-André Lureau     nelem = g_data_input_stream_read_uint32(s, NULL, &err);
2095010cec2SMarc-André Lureau     if (err) {
2105010cec2SMarc-André Lureau         goto error;
2115010cec2SMarc-André Lureau     }
2125010cec2SMarc-André Lureau 
2135010cec2SMarc-André Lureau     while (nelem > 0) {
2145010cec2SMarc-André Lureau         GDBusProxy *proxy = NULL;
2155010cec2SMarc-André Lureau         uint32_t len;
2165010cec2SMarc-André Lureau         gsize bytes_read, avail;
2175010cec2SMarc-André Lureau         char id[256];
2185010cec2SMarc-André Lureau 
2195010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
2205010cec2SMarc-André Lureau         if (err) {
2215010cec2SMarc-André Lureau             goto error;
2225010cec2SMarc-André Lureau         }
2235010cec2SMarc-André Lureau         if (len >= 256) {
2245010cec2SMarc-André Lureau             error_report("%s: Invalid DBus vmstate proxy name %u",
2255010cec2SMarc-André Lureau                          __func__, len);
2265010cec2SMarc-André Lureau             return -1;
2275010cec2SMarc-André Lureau         }
2285010cec2SMarc-André Lureau         if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
2295010cec2SMarc-André Lureau                                      &bytes_read, NULL, &err)) {
2305010cec2SMarc-André Lureau             goto error;
2315010cec2SMarc-André Lureau         }
232*166a1cf4SMarkus Armbruster         if (bytes_read != len) {
233*166a1cf4SMarkus Armbruster             error_report("%s: Short read", __func__);
234*166a1cf4SMarkus Armbruster             return -1;
235*166a1cf4SMarkus Armbruster         }
2365010cec2SMarc-André Lureau         id[len] = 0;
2375010cec2SMarc-André Lureau 
2385010cec2SMarc-André Lureau         trace_dbus_vmstate_loading(id);
2395010cec2SMarc-André Lureau 
2405010cec2SMarc-André Lureau         proxy = g_hash_table_lookup(proxies, id);
2415010cec2SMarc-André Lureau         if (!proxy) {
2425010cec2SMarc-André Lureau             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
2435010cec2SMarc-André Lureau             return -1;
2445010cec2SMarc-André Lureau         }
2455010cec2SMarc-André Lureau 
2465010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
2475010cec2SMarc-André Lureau         avail = g_buffered_input_stream_get_available(
2485010cec2SMarc-André Lureau             G_BUFFERED_INPUT_STREAM(s));
2495010cec2SMarc-André Lureau 
2505010cec2SMarc-André Lureau         if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
2515010cec2SMarc-André Lureau             error_report("%s: Invalid vmstate size: %u", __func__, len);
2525010cec2SMarc-André Lureau             return -1;
2535010cec2SMarc-André Lureau         }
2545010cec2SMarc-André Lureau 
2555010cec2SMarc-André Lureau         if (dbus_load_state_proxy(proxy,
2565010cec2SMarc-André Lureau                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
2575010cec2SMarc-André Lureau                                                     NULL),
2585010cec2SMarc-André Lureau                 len) < 0) {
2595010cec2SMarc-André Lureau             error_report("%s: Failed to restore Id '%s'", __func__, id);
2605010cec2SMarc-André Lureau             return -1;
2615010cec2SMarc-André Lureau         }
2625010cec2SMarc-André Lureau 
2635010cec2SMarc-André Lureau         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
2645010cec2SMarc-André Lureau             goto error;
2655010cec2SMarc-André Lureau         }
2665010cec2SMarc-André Lureau 
2675010cec2SMarc-André Lureau         nelem -= 1;
2685010cec2SMarc-André Lureau     }
2695010cec2SMarc-André Lureau 
2705010cec2SMarc-André Lureau     return 0;
2715010cec2SMarc-André Lureau 
2725010cec2SMarc-André Lureau error:
2735010cec2SMarc-André Lureau     error_report("%s: Failed to read from stream: %s", __func__, err->message);
2745010cec2SMarc-André Lureau     return -1;
2755010cec2SMarc-André Lureau }
2765010cec2SMarc-André Lureau 
2775010cec2SMarc-André Lureau static void
2785010cec2SMarc-André Lureau dbus_save_state_proxy(gpointer key,
2795010cec2SMarc-André Lureau                       gpointer value,
2805010cec2SMarc-André Lureau                       gpointer user_data)
2815010cec2SMarc-André Lureau {
2825010cec2SMarc-André Lureau     GDataOutputStream *s = user_data;
2835010cec2SMarc-André Lureau     const char *id = key;
2845010cec2SMarc-André Lureau     GDBusProxy *proxy = value;
2855010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
2865010cec2SMarc-André Lureau     g_autoptr(GVariant) child = NULL;
2875010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
2885010cec2SMarc-André Lureau     const uint8_t *data;
2895010cec2SMarc-André Lureau     gsize size;
2905010cec2SMarc-André Lureau 
2915010cec2SMarc-André Lureau     trace_dbus_vmstate_saving(id);
2925010cec2SMarc-André Lureau 
2935010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Save",
2945010cec2SMarc-André Lureau                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
2955010cec2SMarc-André Lureau                                     -1, NULL, &err);
2965010cec2SMarc-André Lureau     if (!result) {
2975010cec2SMarc-André Lureau         error_report("%s: Failed to Save: %s", __func__, err->message);
2985010cec2SMarc-André Lureau         return;
2995010cec2SMarc-André Lureau     }
3005010cec2SMarc-André Lureau 
3015010cec2SMarc-André Lureau     child = g_variant_get_child_value(result, 0);
3025010cec2SMarc-André Lureau     data = g_variant_get_fixed_array(child, &size, sizeof(char));
3035010cec2SMarc-André Lureau     if (!data) {
3045010cec2SMarc-André Lureau         error_report("%s: Failed to Save: not a byte array", __func__);
3055010cec2SMarc-André Lureau         return;
3065010cec2SMarc-André Lureau     }
3075010cec2SMarc-André Lureau     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
3085010cec2SMarc-André Lureau         error_report("%s: Too large vmstate data to save: %zu",
3095010cec2SMarc-André Lureau                      __func__, (size_t)size);
3105010cec2SMarc-André Lureau         return;
3115010cec2SMarc-André Lureau     }
3125010cec2SMarc-André Lureau 
3135010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
3145010cec2SMarc-André Lureau         !g_data_output_stream_put_string(s, id, NULL, &err) ||
3155010cec2SMarc-André Lureau         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
3165010cec2SMarc-André Lureau         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
3175010cec2SMarc-André Lureau                                    data, size, NULL, NULL, &err)) {
3185010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3195010cec2SMarc-André Lureau                      __func__, err->message);
3205010cec2SMarc-André Lureau     }
3215010cec2SMarc-André Lureau }
3225010cec2SMarc-André Lureau 
3235010cec2SMarc-André Lureau static int dbus_vmstate_pre_save(void *opaque)
3245010cec2SMarc-André Lureau {
3255010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
3265010cec2SMarc-André Lureau     g_autoptr(GOutputStream) m = NULL;
3275010cec2SMarc-André Lureau     g_autoptr(GDataOutputStream) s = NULL;
3285010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
3295010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3305010cec2SMarc-André Lureau 
3315010cec2SMarc-André Lureau     trace_dbus_vmstate_pre_save();
3325010cec2SMarc-André Lureau 
3335010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
3345010cec2SMarc-André Lureau     if (!proxies) {
3355010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
3365010cec2SMarc-André Lureau         return -1;
3375010cec2SMarc-André Lureau     }
3385010cec2SMarc-André Lureau 
3395010cec2SMarc-André Lureau     m = g_memory_output_stream_new_resizable();
3405010cec2SMarc-André Lureau     s = g_data_output_stream_new(m);
3415010cec2SMarc-André Lureau     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
3425010cec2SMarc-André Lureau 
3435010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
3445010cec2SMarc-André Lureau                                          NULL, &err)) {
3455010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3465010cec2SMarc-André Lureau                      __func__, err->message);
3475010cec2SMarc-André Lureau         return -1;
3485010cec2SMarc-André Lureau     }
3495010cec2SMarc-André Lureau 
3505010cec2SMarc-André Lureau     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
3515010cec2SMarc-André Lureau 
3525010cec2SMarc-André Lureau     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
3535010cec2SMarc-André Lureau         > UINT32_MAX) {
3545010cec2SMarc-André Lureau         error_report("%s: DBus vmstate buffer is too large", __func__);
3555010cec2SMarc-André Lureau         return -1;
3565010cec2SMarc-André Lureau     }
3575010cec2SMarc-André Lureau 
3585010cec2SMarc-André Lureau     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
3595010cec2SMarc-André Lureau         error_report("%s: Failed to close stream: %s", __func__, err->message);
3605010cec2SMarc-André Lureau         return -1;
3615010cec2SMarc-André Lureau     }
3625010cec2SMarc-André Lureau 
3635010cec2SMarc-André Lureau     g_free(self->data);
3645010cec2SMarc-André Lureau     self->data_size =
3655010cec2SMarc-André Lureau         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
3665010cec2SMarc-André Lureau     self->data =
3675010cec2SMarc-André Lureau         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
3685010cec2SMarc-André Lureau 
3695010cec2SMarc-André Lureau     return 0;
3705010cec2SMarc-André Lureau }
3715010cec2SMarc-André Lureau 
3725010cec2SMarc-André Lureau static const VMStateDescription dbus_vmstate = {
3735010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
3745010cec2SMarc-André Lureau     .version_id = 0,
3755010cec2SMarc-André Lureau     .pre_save = dbus_vmstate_pre_save,
3765010cec2SMarc-André Lureau     .post_load = dbus_vmstate_post_load,
3775010cec2SMarc-André Lureau     .fields = (VMStateField[]) {
3785010cec2SMarc-André Lureau         VMSTATE_UINT32(data_size, DBusVMState),
3795010cec2SMarc-André Lureau         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
3805010cec2SMarc-André Lureau         VMSTATE_END_OF_LIST()
3815010cec2SMarc-André Lureau     }
3825010cec2SMarc-André Lureau };
3835010cec2SMarc-André Lureau 
3845010cec2SMarc-André Lureau static void
3855010cec2SMarc-André Lureau dbus_vmstate_complete(UserCreatable *uc, Error **errp)
3865010cec2SMarc-André Lureau {
3875010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(uc);
3885010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3895010cec2SMarc-André Lureau 
3905010cec2SMarc-André Lureau     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
3915010cec2SMarc-André Lureau         error_setg(errp, "There is already an instance of %s",
3925010cec2SMarc-André Lureau                    TYPE_DBUS_VMSTATE);
3935010cec2SMarc-André Lureau         return;
3945010cec2SMarc-André Lureau     }
3955010cec2SMarc-André Lureau 
3965010cec2SMarc-André Lureau     if (!self->dbus_addr) {
3975010cec2SMarc-André Lureau         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
3985010cec2SMarc-André Lureau         return;
3995010cec2SMarc-André Lureau     }
4005010cec2SMarc-André Lureau 
4015010cec2SMarc-André Lureau     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
4025010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
4035010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
4045010cec2SMarc-André Lureau                     NULL, NULL, &err);
4055010cec2SMarc-André Lureau     if (err) {
4065010cec2SMarc-André Lureau         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
4075010cec2SMarc-André Lureau         return;
4085010cec2SMarc-André Lureau     }
4095010cec2SMarc-André Lureau 
4101df2c9a2SPeter Xu     if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
4111df2c9a2SPeter Xu                          &dbus_vmstate, self) < 0) {
4125010cec2SMarc-André Lureau         error_setg(errp, "Failed to register vmstate");
4135010cec2SMarc-André Lureau     }
4145010cec2SMarc-André Lureau }
4155010cec2SMarc-André Lureau 
4165010cec2SMarc-André Lureau static void
4175010cec2SMarc-André Lureau dbus_vmstate_finalize(Object *o)
4185010cec2SMarc-André Lureau {
4195010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4205010cec2SMarc-André Lureau 
4215010cec2SMarc-André Lureau     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
4225010cec2SMarc-André Lureau 
4235010cec2SMarc-André Lureau     g_clear_object(&self->bus);
4245010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4255010cec2SMarc-André Lureau     g_free(self->id_list);
4265010cec2SMarc-André Lureau     g_free(self->data);
4275010cec2SMarc-André Lureau }
4285010cec2SMarc-André Lureau 
4295010cec2SMarc-André Lureau static char *
4305010cec2SMarc-André Lureau get_dbus_addr(Object *o, Error **errp)
4315010cec2SMarc-André Lureau {
4325010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4335010cec2SMarc-André Lureau 
4345010cec2SMarc-André Lureau     return g_strdup(self->dbus_addr);
4355010cec2SMarc-André Lureau }
4365010cec2SMarc-André Lureau 
4375010cec2SMarc-André Lureau static void
4385010cec2SMarc-André Lureau set_dbus_addr(Object *o, const char *str, Error **errp)
4395010cec2SMarc-André Lureau {
4405010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4415010cec2SMarc-André Lureau 
4425010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4435010cec2SMarc-André Lureau     self->dbus_addr = g_strdup(str);
4445010cec2SMarc-André Lureau }
4455010cec2SMarc-André Lureau 
4465010cec2SMarc-André Lureau static char *
4475010cec2SMarc-André Lureau get_id_list(Object *o, Error **errp)
4485010cec2SMarc-André Lureau {
4495010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4505010cec2SMarc-André Lureau 
4515010cec2SMarc-André Lureau     return g_strdup(self->id_list);
4525010cec2SMarc-André Lureau }
4535010cec2SMarc-André Lureau 
4545010cec2SMarc-André Lureau static void
4555010cec2SMarc-André Lureau set_id_list(Object *o, const char *str, Error **errp)
4565010cec2SMarc-André Lureau {
4575010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4585010cec2SMarc-André Lureau 
4595010cec2SMarc-André Lureau     g_free(self->id_list);
4605010cec2SMarc-André Lureau     self->id_list = g_strdup(str);
4615010cec2SMarc-André Lureau }
4625010cec2SMarc-André Lureau 
4635010cec2SMarc-André Lureau static char *
4645010cec2SMarc-André Lureau dbus_vmstate_get_id(VMStateIf *vmif)
4655010cec2SMarc-André Lureau {
4665010cec2SMarc-André Lureau     return g_strdup(TYPE_DBUS_VMSTATE);
4675010cec2SMarc-André Lureau }
4685010cec2SMarc-André Lureau 
4695010cec2SMarc-André Lureau static void
4705010cec2SMarc-André Lureau dbus_vmstate_class_init(ObjectClass *oc, void *data)
4715010cec2SMarc-André Lureau {
4725010cec2SMarc-André Lureau     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
4735010cec2SMarc-André Lureau     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
4745010cec2SMarc-André Lureau 
4755010cec2SMarc-André Lureau     ucc->complete = dbus_vmstate_complete;
4765010cec2SMarc-André Lureau     vc->get_id = dbus_vmstate_get_id;
4775010cec2SMarc-André Lureau 
4785010cec2SMarc-André Lureau     object_class_property_add_str(oc, "addr",
479d2623129SMarkus Armbruster                                   get_dbus_addr, set_dbus_addr);
4805010cec2SMarc-André Lureau     object_class_property_add_str(oc, "id-list",
481d2623129SMarkus Armbruster                                   get_id_list, set_id_list);
4825010cec2SMarc-André Lureau }
4835010cec2SMarc-André Lureau 
4845010cec2SMarc-André Lureau static const TypeInfo dbus_vmstate_info = {
4855010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
4865010cec2SMarc-André Lureau     .parent = TYPE_OBJECT,
4875010cec2SMarc-André Lureau     .instance_size = sizeof(DBusVMState),
4885010cec2SMarc-André Lureau     .instance_finalize = dbus_vmstate_finalize,
4895010cec2SMarc-André Lureau     .class_init = dbus_vmstate_class_init,
4905010cec2SMarc-André Lureau     .interfaces = (InterfaceInfo[]) {
4915010cec2SMarc-André Lureau         { TYPE_USER_CREATABLE },
4925010cec2SMarc-André Lureau         { TYPE_VMSTATE_IF },
4935010cec2SMarc-André Lureau         { }
4945010cec2SMarc-André Lureau     }
4955010cec2SMarc-André Lureau };
4965010cec2SMarc-André Lureau 
4975010cec2SMarc-André Lureau static void
4985010cec2SMarc-André Lureau register_types(void)
4995010cec2SMarc-André Lureau {
5005010cec2SMarc-André Lureau     type_register_static(&dbus_vmstate_info);
5015010cec2SMarc-André Lureau }
5025010cec2SMarc-André Lureau 
5035010cec2SMarc-André Lureau type_init(register_types);
504