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