xref: /openbmc/qemu/backends/dbus-vmstate.c (revision d349d5ab398fcf6f4bb3368abc14ef7ce6ba9afc)
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) {
11727485832SPriyankar Jain             if (err != NULL && *err != NULL) {
11827485832SPriyankar Jain                 warn_report("%s: Failed to create proxy: %s",
11927485832SPriyankar Jain                             __func__, (*err)->message);
12027485832SPriyankar Jain                 g_clear_error(err);
12127485832SPriyankar Jain             }
12227485832SPriyankar Jain             continue;
1235010cec2SMarc-André Lureau         }
1245010cec2SMarc-André Lureau 
1255010cec2SMarc-André Lureau         result = g_dbus_proxy_get_cached_property(proxy, "Id");
1265010cec2SMarc-André Lureau         if (!result) {
12727485832SPriyankar Jain             warn_report("%s: VMState Id property is missing.", __func__);
12827485832SPriyankar Jain             g_clear_object(&proxy);
12927485832SPriyankar Jain             continue;
1305010cec2SMarc-André Lureau         }
1315010cec2SMarc-André Lureau 
1325010cec2SMarc-André Lureau         id = g_variant_dup_string(result, &size);
1335010cec2SMarc-André Lureau         if (ids && !g_hash_table_remove(ids, id)) {
1345010cec2SMarc-André Lureau             g_clear_pointer(&id, g_free);
1355010cec2SMarc-André Lureau             g_clear_object(&proxy);
1365010cec2SMarc-André Lureau             continue;
1375010cec2SMarc-André Lureau         }
1385010cec2SMarc-André Lureau         if (size == 0 || size >= 256) {
1395010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1405010cec2SMarc-André Lureau                         "VMState Id '%s' is invalid.", id);
1415010cec2SMarc-André Lureau             return NULL;
1425010cec2SMarc-André Lureau         }
1435010cec2SMarc-André Lureau 
1445010cec2SMarc-André Lureau         if (!g_hash_table_insert(proxies, id, proxy)) {
1455010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1465010cec2SMarc-André Lureau                         "Duplicated VMState Id '%s'", id);
1475010cec2SMarc-André Lureau             return NULL;
1485010cec2SMarc-André Lureau         }
1495010cec2SMarc-André Lureau         id = NULL;
1505010cec2SMarc-André Lureau         proxy = NULL;
1515010cec2SMarc-André Lureau 
1525010cec2SMarc-André Lureau         g_clear_pointer(&result, g_variant_unref);
1535010cec2SMarc-André Lureau     }
1545010cec2SMarc-André Lureau 
1555010cec2SMarc-André Lureau     if (ids) {
1565010cec2SMarc-André Lureau         g_autofree char **left = NULL;
1575010cec2SMarc-André Lureau 
1585010cec2SMarc-André Lureau         left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
1595010cec2SMarc-André Lureau         if (*left) {
1605010cec2SMarc-André Lureau             g_autofree char *leftids = g_strjoinv(",", left);
1615010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
1625010cec2SMarc-André Lureau                         "Required VMState Id are missing: %s", leftids);
1635010cec2SMarc-André Lureau             return NULL;
1645010cec2SMarc-André Lureau         }
1655010cec2SMarc-André Lureau     }
1665010cec2SMarc-André Lureau 
1675010cec2SMarc-André Lureau     return g_steal_pointer(&proxies);
1685010cec2SMarc-André Lureau }
1695010cec2SMarc-André Lureau 
1705010cec2SMarc-André Lureau static int
1715010cec2SMarc-André Lureau dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
1725010cec2SMarc-André Lureau {
1735010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
1745010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
1755010cec2SMarc-André Lureau     g_autoptr(GVariant) value = NULL;
1765010cec2SMarc-André Lureau 
1775010cec2SMarc-André Lureau     value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
1785010cec2SMarc-André Lureau                                       data, size, sizeof(char));
1795010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Load",
1805010cec2SMarc-André Lureau                                     g_variant_new("(@ay)",
1815010cec2SMarc-André Lureau                                                   g_steal_pointer(&value)),
1825010cec2SMarc-André Lureau                                     G_DBUS_CALL_FLAGS_NO_AUTO_START,
1835010cec2SMarc-André Lureau                                     -1, NULL, &err);
1845010cec2SMarc-André Lureau     if (!result) {
1855010cec2SMarc-André Lureau         error_report("%s: Failed to Load: %s", __func__, err->message);
1865010cec2SMarc-André Lureau         return -1;
1875010cec2SMarc-André Lureau     }
1885010cec2SMarc-André Lureau 
1895010cec2SMarc-André Lureau     return 0;
1905010cec2SMarc-André Lureau }
1915010cec2SMarc-André Lureau 
1925010cec2SMarc-André Lureau static int dbus_vmstate_post_load(void *opaque, int version_id)
1935010cec2SMarc-André Lureau {
1945010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
1955010cec2SMarc-André Lureau     g_autoptr(GInputStream) m = NULL;
1965010cec2SMarc-André Lureau     g_autoptr(GDataInputStream) s = NULL;
1975010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
1985010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
1995010cec2SMarc-André Lureau     uint32_t nelem;
2005010cec2SMarc-André Lureau 
2015010cec2SMarc-André Lureau     trace_dbus_vmstate_post_load(version_id);
2025010cec2SMarc-André Lureau 
2035010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
2045010cec2SMarc-André Lureau     if (!proxies) {
2055010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
2065010cec2SMarc-André Lureau         return -1;
2075010cec2SMarc-André Lureau     }
2085010cec2SMarc-André Lureau 
2095010cec2SMarc-André Lureau     m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
2105010cec2SMarc-André Lureau     s = g_data_input_stream_new(m);
2115010cec2SMarc-André Lureau     g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
2121d9fa7a8SPriyankar Jain     g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s),
2131d9fa7a8SPriyankar Jain                                             DBUS_VMSTATE_SIZE_LIMIT);
2145010cec2SMarc-André Lureau 
2155010cec2SMarc-André Lureau     nelem = g_data_input_stream_read_uint32(s, NULL, &err);
2165010cec2SMarc-André Lureau     if (err) {
2175010cec2SMarc-André Lureau         goto error;
2185010cec2SMarc-André Lureau     }
2195010cec2SMarc-André Lureau 
2205010cec2SMarc-André Lureau     while (nelem > 0) {
2215010cec2SMarc-André Lureau         GDBusProxy *proxy = NULL;
2225010cec2SMarc-André Lureau         uint32_t len;
2235010cec2SMarc-André Lureau         gsize bytes_read, avail;
2245010cec2SMarc-André Lureau         char id[256];
2255010cec2SMarc-André Lureau 
2265010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
2275010cec2SMarc-André Lureau         if (err) {
2285010cec2SMarc-André Lureau             goto error;
2295010cec2SMarc-André Lureau         }
2305010cec2SMarc-André Lureau         if (len >= 256) {
2315010cec2SMarc-André Lureau             error_report("%s: Invalid DBus vmstate proxy name %u",
2325010cec2SMarc-André Lureau                          __func__, len);
2335010cec2SMarc-André Lureau             return -1;
2345010cec2SMarc-André Lureau         }
2355010cec2SMarc-André Lureau         if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
2365010cec2SMarc-André Lureau                                      &bytes_read, NULL, &err)) {
2375010cec2SMarc-André Lureau             goto error;
2385010cec2SMarc-André Lureau         }
239166a1cf4SMarkus Armbruster         if (bytes_read != len) {
240166a1cf4SMarkus Armbruster             error_report("%s: Short read", __func__);
241166a1cf4SMarkus Armbruster             return -1;
242166a1cf4SMarkus Armbruster         }
2435010cec2SMarc-André Lureau         id[len] = 0;
2445010cec2SMarc-André Lureau 
2455010cec2SMarc-André Lureau         trace_dbus_vmstate_loading(id);
2465010cec2SMarc-André Lureau 
2475010cec2SMarc-André Lureau         proxy = g_hash_table_lookup(proxies, id);
2485010cec2SMarc-André Lureau         if (!proxy) {
2495010cec2SMarc-André Lureau             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
2505010cec2SMarc-André Lureau             return -1;
2515010cec2SMarc-André Lureau         }
2525010cec2SMarc-André Lureau 
2535010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
2541d9fa7a8SPriyankar Jain         if (len > DBUS_VMSTATE_SIZE_LIMIT) {
2551d9fa7a8SPriyankar Jain             error_report("%s: Invalid vmstate size: %u", __func__, len);
2561d9fa7a8SPriyankar Jain             return -1;
2571d9fa7a8SPriyankar Jain         }
2581d9fa7a8SPriyankar Jain 
2591d9fa7a8SPriyankar Jain         g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL,
2601d9fa7a8SPriyankar Jain                                      &err);
2611d9fa7a8SPriyankar Jain         if (err) {
2621d9fa7a8SPriyankar Jain             goto error;
2631d9fa7a8SPriyankar Jain         }
2641d9fa7a8SPriyankar Jain 
2655010cec2SMarc-André Lureau         avail = g_buffered_input_stream_get_available(
2665010cec2SMarc-André Lureau             G_BUFFERED_INPUT_STREAM(s));
2671d9fa7a8SPriyankar Jain         if (len > avail) {
2681d9fa7a8SPriyankar Jain             error_report("%s: Not enough data available to load for Id: '%s'. "
2691d9fa7a8SPriyankar Jain                 "Available data size: %zu, Actual vmstate size: %u",
2701d9fa7a8SPriyankar Jain                 __func__, id, avail, len);
2715010cec2SMarc-André Lureau             return -1;
2725010cec2SMarc-André Lureau         }
2735010cec2SMarc-André Lureau 
2745010cec2SMarc-André Lureau         if (dbus_load_state_proxy(proxy,
2755010cec2SMarc-André Lureau                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
2765010cec2SMarc-André Lureau                                                     NULL),
2775010cec2SMarc-André Lureau                 len) < 0) {
2785010cec2SMarc-André Lureau             error_report("%s: Failed to restore Id '%s'", __func__, id);
2795010cec2SMarc-André Lureau             return -1;
2805010cec2SMarc-André Lureau         }
2815010cec2SMarc-André Lureau 
2825010cec2SMarc-André Lureau         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
2835010cec2SMarc-André Lureau             goto error;
2845010cec2SMarc-André Lureau         }
2855010cec2SMarc-André Lureau 
2865010cec2SMarc-André Lureau         nelem -= 1;
2875010cec2SMarc-André Lureau     }
2885010cec2SMarc-André Lureau 
2895010cec2SMarc-André Lureau     return 0;
2905010cec2SMarc-André Lureau 
2915010cec2SMarc-André Lureau error:
2925010cec2SMarc-André Lureau     error_report("%s: Failed to read from stream: %s", __func__, err->message);
2935010cec2SMarc-André Lureau     return -1;
2945010cec2SMarc-André Lureau }
2955010cec2SMarc-André Lureau 
2965010cec2SMarc-André Lureau static void
2975010cec2SMarc-André Lureau dbus_save_state_proxy(gpointer key,
2985010cec2SMarc-André Lureau                       gpointer value,
2995010cec2SMarc-André Lureau                       gpointer user_data)
3005010cec2SMarc-André Lureau {
3015010cec2SMarc-André Lureau     GDataOutputStream *s = user_data;
3025010cec2SMarc-André Lureau     const char *id = key;
3035010cec2SMarc-André Lureau     GDBusProxy *proxy = value;
3045010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
3055010cec2SMarc-André Lureau     g_autoptr(GVariant) child = NULL;
3065010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3075010cec2SMarc-André Lureau     const uint8_t *data;
3085010cec2SMarc-André Lureau     gsize size;
3095010cec2SMarc-André Lureau 
3105010cec2SMarc-André Lureau     trace_dbus_vmstate_saving(id);
3115010cec2SMarc-André Lureau 
3125010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Save",
3135010cec2SMarc-André Lureau                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
3145010cec2SMarc-André Lureau                                     -1, NULL, &err);
3155010cec2SMarc-André Lureau     if (!result) {
3165010cec2SMarc-André Lureau         error_report("%s: Failed to Save: %s", __func__, err->message);
3175010cec2SMarc-André Lureau         return;
3185010cec2SMarc-André Lureau     }
3195010cec2SMarc-André Lureau 
3205010cec2SMarc-André Lureau     child = g_variant_get_child_value(result, 0);
3215010cec2SMarc-André Lureau     data = g_variant_get_fixed_array(child, &size, sizeof(char));
3225010cec2SMarc-André Lureau     if (!data) {
3235010cec2SMarc-André Lureau         error_report("%s: Failed to Save: not a byte array", __func__);
3245010cec2SMarc-André Lureau         return;
3255010cec2SMarc-André Lureau     }
3265010cec2SMarc-André Lureau     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
3275010cec2SMarc-André Lureau         error_report("%s: Too large vmstate data to save: %zu",
3285010cec2SMarc-André Lureau                      __func__, (size_t)size);
3295010cec2SMarc-André Lureau         return;
3305010cec2SMarc-André Lureau     }
3315010cec2SMarc-André Lureau 
3325010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
3335010cec2SMarc-André Lureau         !g_data_output_stream_put_string(s, id, NULL, &err) ||
3345010cec2SMarc-André Lureau         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
3355010cec2SMarc-André Lureau         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
3365010cec2SMarc-André Lureau                                    data, size, NULL, NULL, &err)) {
3375010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3385010cec2SMarc-André Lureau                      __func__, err->message);
3395010cec2SMarc-André Lureau     }
3405010cec2SMarc-André Lureau }
3415010cec2SMarc-André Lureau 
3425010cec2SMarc-André Lureau static int dbus_vmstate_pre_save(void *opaque)
3435010cec2SMarc-André Lureau {
3445010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
3455010cec2SMarc-André Lureau     g_autoptr(GOutputStream) m = NULL;
3465010cec2SMarc-André Lureau     g_autoptr(GDataOutputStream) s = NULL;
3475010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
3485010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3495010cec2SMarc-André Lureau 
3505010cec2SMarc-André Lureau     trace_dbus_vmstate_pre_save();
3515010cec2SMarc-André Lureau 
3525010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
3535010cec2SMarc-André Lureau     if (!proxies) {
3545010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
3555010cec2SMarc-André Lureau         return -1;
3565010cec2SMarc-André Lureau     }
3575010cec2SMarc-André Lureau 
3585010cec2SMarc-André Lureau     m = g_memory_output_stream_new_resizable();
3595010cec2SMarc-André Lureau     s = g_data_output_stream_new(m);
3605010cec2SMarc-André Lureau     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
3615010cec2SMarc-André Lureau 
3625010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
3635010cec2SMarc-André Lureau                                          NULL, &err)) {
3645010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3655010cec2SMarc-André Lureau                      __func__, err->message);
3665010cec2SMarc-André Lureau         return -1;
3675010cec2SMarc-André Lureau     }
3685010cec2SMarc-André Lureau 
3695010cec2SMarc-André Lureau     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
3705010cec2SMarc-André Lureau 
3715010cec2SMarc-André Lureau     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
3725010cec2SMarc-André Lureau         > UINT32_MAX) {
3735010cec2SMarc-André Lureau         error_report("%s: DBus vmstate buffer is too large", __func__);
3745010cec2SMarc-André Lureau         return -1;
3755010cec2SMarc-André Lureau     }
3765010cec2SMarc-André Lureau 
3775010cec2SMarc-André Lureau     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
3785010cec2SMarc-André Lureau         error_report("%s: Failed to close stream: %s", __func__, err->message);
3795010cec2SMarc-André Lureau         return -1;
3805010cec2SMarc-André Lureau     }
3815010cec2SMarc-André Lureau 
3825010cec2SMarc-André Lureau     g_free(self->data);
3835010cec2SMarc-André Lureau     self->data_size =
3845010cec2SMarc-André Lureau         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
3855010cec2SMarc-André Lureau     self->data =
3865010cec2SMarc-André Lureau         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
3875010cec2SMarc-André Lureau 
3885010cec2SMarc-André Lureau     return 0;
3895010cec2SMarc-André Lureau }
3905010cec2SMarc-André Lureau 
3915010cec2SMarc-André Lureau static const VMStateDescription dbus_vmstate = {
3925010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
3935010cec2SMarc-André Lureau     .version_id = 0,
3945010cec2SMarc-André Lureau     .pre_save = dbus_vmstate_pre_save,
3955010cec2SMarc-André Lureau     .post_load = dbus_vmstate_post_load,
396*d349d5abSRichard Henderson     .fields = (const VMStateField[]) {
3975010cec2SMarc-André Lureau         VMSTATE_UINT32(data_size, DBusVMState),
3985010cec2SMarc-André Lureau         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
3995010cec2SMarc-André Lureau         VMSTATE_END_OF_LIST()
4005010cec2SMarc-André Lureau     }
4015010cec2SMarc-André Lureau };
4025010cec2SMarc-André Lureau 
4035010cec2SMarc-André Lureau static void
4045010cec2SMarc-André Lureau dbus_vmstate_complete(UserCreatable *uc, Error **errp)
4055010cec2SMarc-André Lureau {
4065010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(uc);
4075010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
4085010cec2SMarc-André Lureau 
4095010cec2SMarc-André Lureau     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
4105010cec2SMarc-André Lureau         error_setg(errp, "There is already an instance of %s",
4115010cec2SMarc-André Lureau                    TYPE_DBUS_VMSTATE);
4125010cec2SMarc-André Lureau         return;
4135010cec2SMarc-André Lureau     }
4145010cec2SMarc-André Lureau 
4155010cec2SMarc-André Lureau     if (!self->dbus_addr) {
4165010cec2SMarc-André Lureau         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
4175010cec2SMarc-André Lureau         return;
4185010cec2SMarc-André Lureau     }
4195010cec2SMarc-André Lureau 
4205010cec2SMarc-André Lureau     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
4215010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
4225010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
4235010cec2SMarc-André Lureau                     NULL, NULL, &err);
4245010cec2SMarc-André Lureau     if (err) {
4255010cec2SMarc-André Lureau         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
4265010cec2SMarc-André Lureau         return;
4275010cec2SMarc-André Lureau     }
4285010cec2SMarc-André Lureau 
42999b16e8eSJuan Quintela     if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) {
4305010cec2SMarc-André Lureau         error_setg(errp, "Failed to register vmstate");
4315010cec2SMarc-André Lureau     }
4325010cec2SMarc-André Lureau }
4335010cec2SMarc-André Lureau 
4345010cec2SMarc-André Lureau static void
4355010cec2SMarc-André Lureau dbus_vmstate_finalize(Object *o)
4365010cec2SMarc-André Lureau {
4375010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4385010cec2SMarc-André Lureau 
4395010cec2SMarc-André Lureau     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
4405010cec2SMarc-André Lureau 
4415010cec2SMarc-André Lureau     g_clear_object(&self->bus);
4425010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4435010cec2SMarc-André Lureau     g_free(self->id_list);
4445010cec2SMarc-André Lureau     g_free(self->data);
4455010cec2SMarc-André Lureau }
4465010cec2SMarc-André Lureau 
4475010cec2SMarc-André Lureau static char *
4485010cec2SMarc-André Lureau get_dbus_addr(Object *o, Error **errp)
4495010cec2SMarc-André Lureau {
4505010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4515010cec2SMarc-André Lureau 
4525010cec2SMarc-André Lureau     return g_strdup(self->dbus_addr);
4535010cec2SMarc-André Lureau }
4545010cec2SMarc-André Lureau 
4555010cec2SMarc-André Lureau static void
4565010cec2SMarc-André Lureau set_dbus_addr(Object *o, const char *str, Error **errp)
4575010cec2SMarc-André Lureau {
4585010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4595010cec2SMarc-André Lureau 
4605010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4615010cec2SMarc-André Lureau     self->dbus_addr = g_strdup(str);
4625010cec2SMarc-André Lureau }
4635010cec2SMarc-André Lureau 
4645010cec2SMarc-André Lureau static char *
4655010cec2SMarc-André Lureau get_id_list(Object *o, Error **errp)
4665010cec2SMarc-André Lureau {
4675010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4685010cec2SMarc-André Lureau 
4695010cec2SMarc-André Lureau     return g_strdup(self->id_list);
4705010cec2SMarc-André Lureau }
4715010cec2SMarc-André Lureau 
4725010cec2SMarc-André Lureau static void
4735010cec2SMarc-André Lureau set_id_list(Object *o, const char *str, Error **errp)
4745010cec2SMarc-André Lureau {
4755010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4765010cec2SMarc-André Lureau 
4775010cec2SMarc-André Lureau     g_free(self->id_list);
4785010cec2SMarc-André Lureau     self->id_list = g_strdup(str);
4795010cec2SMarc-André Lureau }
4805010cec2SMarc-André Lureau 
4815010cec2SMarc-André Lureau static char *
4825010cec2SMarc-André Lureau dbus_vmstate_get_id(VMStateIf *vmif)
4835010cec2SMarc-André Lureau {
4845010cec2SMarc-André Lureau     return g_strdup(TYPE_DBUS_VMSTATE);
4855010cec2SMarc-André Lureau }
4865010cec2SMarc-André Lureau 
4875010cec2SMarc-André Lureau static void
4885010cec2SMarc-André Lureau dbus_vmstate_class_init(ObjectClass *oc, void *data)
4895010cec2SMarc-André Lureau {
4905010cec2SMarc-André Lureau     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
4915010cec2SMarc-André Lureau     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
4925010cec2SMarc-André Lureau 
4935010cec2SMarc-André Lureau     ucc->complete = dbus_vmstate_complete;
4945010cec2SMarc-André Lureau     vc->get_id = dbus_vmstate_get_id;
4955010cec2SMarc-André Lureau 
4965010cec2SMarc-André Lureau     object_class_property_add_str(oc, "addr",
497d2623129SMarkus Armbruster                                   get_dbus_addr, set_dbus_addr);
4985010cec2SMarc-André Lureau     object_class_property_add_str(oc, "id-list",
499d2623129SMarkus Armbruster                                   get_id_list, set_id_list);
5005010cec2SMarc-André Lureau }
5015010cec2SMarc-André Lureau 
5025010cec2SMarc-André Lureau static const TypeInfo dbus_vmstate_info = {
5035010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
5045010cec2SMarc-André Lureau     .parent = TYPE_OBJECT,
5055010cec2SMarc-André Lureau     .instance_size = sizeof(DBusVMState),
5065010cec2SMarc-André Lureau     .instance_finalize = dbus_vmstate_finalize,
5075010cec2SMarc-André Lureau     .class_init = dbus_vmstate_class_init,
5085010cec2SMarc-André Lureau     .interfaces = (InterfaceInfo[]) {
5095010cec2SMarc-André Lureau         { TYPE_USER_CREATABLE },
5105010cec2SMarc-André Lureau         { TYPE_VMSTATE_IF },
5115010cec2SMarc-André Lureau         { }
5125010cec2SMarc-André Lureau     }
5135010cec2SMarc-André Lureau };
5145010cec2SMarc-André Lureau 
5155010cec2SMarc-André Lureau static void
5165010cec2SMarc-André Lureau register_types(void)
5175010cec2SMarc-André Lureau {
5185010cec2SMarc-André Lureau     type_register_static(&dbus_vmstate_info);
5195010cec2SMarc-André Lureau }
5205010cec2SMarc-André Lureau 
5215010cec2SMarc-André Lureau type_init(register_types);
522