xref: /openbmc/qemu/backends/dbus-vmstate.c (revision 30b5707c269cac1ad80b72f777e52c8e08b2ff19)
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"
26*30b5707cSEduardo 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         }
2325010cec2SMarc-André Lureau         g_return_val_if_fail(bytes_read == len, -1);
2335010cec2SMarc-André Lureau         id[len] = 0;
2345010cec2SMarc-André Lureau 
2355010cec2SMarc-André Lureau         trace_dbus_vmstate_loading(id);
2365010cec2SMarc-André Lureau 
2375010cec2SMarc-André Lureau         proxy = g_hash_table_lookup(proxies, id);
2385010cec2SMarc-André Lureau         if (!proxy) {
2395010cec2SMarc-André Lureau             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
2405010cec2SMarc-André Lureau             return -1;
2415010cec2SMarc-André Lureau         }
2425010cec2SMarc-André Lureau 
2435010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
2445010cec2SMarc-André Lureau         avail = g_buffered_input_stream_get_available(
2455010cec2SMarc-André Lureau             G_BUFFERED_INPUT_STREAM(s));
2465010cec2SMarc-André Lureau 
2475010cec2SMarc-André Lureau         if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
2485010cec2SMarc-André Lureau             error_report("%s: Invalid vmstate size: %u", __func__, len);
2495010cec2SMarc-André Lureau             return -1;
2505010cec2SMarc-André Lureau         }
2515010cec2SMarc-André Lureau 
2525010cec2SMarc-André Lureau         if (dbus_load_state_proxy(proxy,
2535010cec2SMarc-André Lureau                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
2545010cec2SMarc-André Lureau                                                     NULL),
2555010cec2SMarc-André Lureau                 len) < 0) {
2565010cec2SMarc-André Lureau             error_report("%s: Failed to restore Id '%s'", __func__, id);
2575010cec2SMarc-André Lureau             return -1;
2585010cec2SMarc-André Lureau         }
2595010cec2SMarc-André Lureau 
2605010cec2SMarc-André Lureau         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
2615010cec2SMarc-André Lureau             goto error;
2625010cec2SMarc-André Lureau         }
2635010cec2SMarc-André Lureau 
2645010cec2SMarc-André Lureau         nelem -= 1;
2655010cec2SMarc-André Lureau     }
2665010cec2SMarc-André Lureau 
2675010cec2SMarc-André Lureau     return 0;
2685010cec2SMarc-André Lureau 
2695010cec2SMarc-André Lureau error:
2705010cec2SMarc-André Lureau     error_report("%s: Failed to read from stream: %s", __func__, err->message);
2715010cec2SMarc-André Lureau     return -1;
2725010cec2SMarc-André Lureau }
2735010cec2SMarc-André Lureau 
2745010cec2SMarc-André Lureau static void
2755010cec2SMarc-André Lureau dbus_save_state_proxy(gpointer key,
2765010cec2SMarc-André Lureau                       gpointer value,
2775010cec2SMarc-André Lureau                       gpointer user_data)
2785010cec2SMarc-André Lureau {
2795010cec2SMarc-André Lureau     GDataOutputStream *s = user_data;
2805010cec2SMarc-André Lureau     const char *id = key;
2815010cec2SMarc-André Lureau     GDBusProxy *proxy = value;
2825010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
2835010cec2SMarc-André Lureau     g_autoptr(GVariant) child = NULL;
2845010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
2855010cec2SMarc-André Lureau     const uint8_t *data;
2865010cec2SMarc-André Lureau     gsize size;
2875010cec2SMarc-André Lureau 
2885010cec2SMarc-André Lureau     trace_dbus_vmstate_saving(id);
2895010cec2SMarc-André Lureau 
2905010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Save",
2915010cec2SMarc-André Lureau                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
2925010cec2SMarc-André Lureau                                     -1, NULL, &err);
2935010cec2SMarc-André Lureau     if (!result) {
2945010cec2SMarc-André Lureau         error_report("%s: Failed to Save: %s", __func__, err->message);
2955010cec2SMarc-André Lureau         return;
2965010cec2SMarc-André Lureau     }
2975010cec2SMarc-André Lureau 
2985010cec2SMarc-André Lureau     child = g_variant_get_child_value(result, 0);
2995010cec2SMarc-André Lureau     data = g_variant_get_fixed_array(child, &size, sizeof(char));
3005010cec2SMarc-André Lureau     if (!data) {
3015010cec2SMarc-André Lureau         error_report("%s: Failed to Save: not a byte array", __func__);
3025010cec2SMarc-André Lureau         return;
3035010cec2SMarc-André Lureau     }
3045010cec2SMarc-André Lureau     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
3055010cec2SMarc-André Lureau         error_report("%s: Too large vmstate data to save: %zu",
3065010cec2SMarc-André Lureau                      __func__, (size_t)size);
3075010cec2SMarc-André Lureau         return;
3085010cec2SMarc-André Lureau     }
3095010cec2SMarc-André Lureau 
3105010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
3115010cec2SMarc-André Lureau         !g_data_output_stream_put_string(s, id, NULL, &err) ||
3125010cec2SMarc-André Lureau         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
3135010cec2SMarc-André Lureau         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
3145010cec2SMarc-André Lureau                                    data, size, NULL, NULL, &err)) {
3155010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3165010cec2SMarc-André Lureau                      __func__, err->message);
3175010cec2SMarc-André Lureau     }
3185010cec2SMarc-André Lureau }
3195010cec2SMarc-André Lureau 
3205010cec2SMarc-André Lureau static int dbus_vmstate_pre_save(void *opaque)
3215010cec2SMarc-André Lureau {
3225010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
3235010cec2SMarc-André Lureau     g_autoptr(GOutputStream) m = NULL;
3245010cec2SMarc-André Lureau     g_autoptr(GDataOutputStream) s = NULL;
3255010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
3265010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3275010cec2SMarc-André Lureau 
3285010cec2SMarc-André Lureau     trace_dbus_vmstate_pre_save();
3295010cec2SMarc-André Lureau 
3305010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
3315010cec2SMarc-André Lureau     if (!proxies) {
3325010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
3335010cec2SMarc-André Lureau         return -1;
3345010cec2SMarc-André Lureau     }
3355010cec2SMarc-André Lureau 
3365010cec2SMarc-André Lureau     m = g_memory_output_stream_new_resizable();
3375010cec2SMarc-André Lureau     s = g_data_output_stream_new(m);
3385010cec2SMarc-André Lureau     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
3395010cec2SMarc-André Lureau 
3405010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
3415010cec2SMarc-André Lureau                                          NULL, &err)) {
3425010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
3435010cec2SMarc-André Lureau                      __func__, err->message);
3445010cec2SMarc-André Lureau         return -1;
3455010cec2SMarc-André Lureau     }
3465010cec2SMarc-André Lureau 
3475010cec2SMarc-André Lureau     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
3485010cec2SMarc-André Lureau 
3495010cec2SMarc-André Lureau     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
3505010cec2SMarc-André Lureau         > UINT32_MAX) {
3515010cec2SMarc-André Lureau         error_report("%s: DBus vmstate buffer is too large", __func__);
3525010cec2SMarc-André Lureau         return -1;
3535010cec2SMarc-André Lureau     }
3545010cec2SMarc-André Lureau 
3555010cec2SMarc-André Lureau     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
3565010cec2SMarc-André Lureau         error_report("%s: Failed to close stream: %s", __func__, err->message);
3575010cec2SMarc-André Lureau         return -1;
3585010cec2SMarc-André Lureau     }
3595010cec2SMarc-André Lureau 
3605010cec2SMarc-André Lureau     g_free(self->data);
3615010cec2SMarc-André Lureau     self->data_size =
3625010cec2SMarc-André Lureau         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
3635010cec2SMarc-André Lureau     self->data =
3645010cec2SMarc-André Lureau         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
3655010cec2SMarc-André Lureau 
3665010cec2SMarc-André Lureau     return 0;
3675010cec2SMarc-André Lureau }
3685010cec2SMarc-André Lureau 
3695010cec2SMarc-André Lureau static const VMStateDescription dbus_vmstate = {
3705010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
3715010cec2SMarc-André Lureau     .version_id = 0,
3725010cec2SMarc-André Lureau     .pre_save = dbus_vmstate_pre_save,
3735010cec2SMarc-André Lureau     .post_load = dbus_vmstate_post_load,
3745010cec2SMarc-André Lureau     .fields = (VMStateField[]) {
3755010cec2SMarc-André Lureau         VMSTATE_UINT32(data_size, DBusVMState),
3765010cec2SMarc-André Lureau         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
3775010cec2SMarc-André Lureau         VMSTATE_END_OF_LIST()
3785010cec2SMarc-André Lureau     }
3795010cec2SMarc-André Lureau };
3805010cec2SMarc-André Lureau 
3815010cec2SMarc-André Lureau static void
3825010cec2SMarc-André Lureau dbus_vmstate_complete(UserCreatable *uc, Error **errp)
3835010cec2SMarc-André Lureau {
3845010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(uc);
3855010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
3865010cec2SMarc-André Lureau 
3875010cec2SMarc-André Lureau     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
3885010cec2SMarc-André Lureau         error_setg(errp, "There is already an instance of %s",
3895010cec2SMarc-André Lureau                    TYPE_DBUS_VMSTATE);
3905010cec2SMarc-André Lureau         return;
3915010cec2SMarc-André Lureau     }
3925010cec2SMarc-André Lureau 
3935010cec2SMarc-André Lureau     if (!self->dbus_addr) {
3945010cec2SMarc-André Lureau         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
3955010cec2SMarc-André Lureau         return;
3965010cec2SMarc-André Lureau     }
3975010cec2SMarc-André Lureau 
3985010cec2SMarc-André Lureau     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
3995010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
4005010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
4015010cec2SMarc-André Lureau                     NULL, NULL, &err);
4025010cec2SMarc-André Lureau     if (err) {
4035010cec2SMarc-André Lureau         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
4045010cec2SMarc-André Lureau         return;
4055010cec2SMarc-André Lureau     }
4065010cec2SMarc-André Lureau 
4071df2c9a2SPeter Xu     if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
4081df2c9a2SPeter Xu                          &dbus_vmstate, self) < 0) {
4095010cec2SMarc-André Lureau         error_setg(errp, "Failed to register vmstate");
4105010cec2SMarc-André Lureau     }
4115010cec2SMarc-André Lureau }
4125010cec2SMarc-André Lureau 
4135010cec2SMarc-André Lureau static void
4145010cec2SMarc-André Lureau dbus_vmstate_finalize(Object *o)
4155010cec2SMarc-André Lureau {
4165010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4175010cec2SMarc-André Lureau 
4185010cec2SMarc-André Lureau     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
4195010cec2SMarc-André Lureau 
4205010cec2SMarc-André Lureau     g_clear_object(&self->bus);
4215010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4225010cec2SMarc-André Lureau     g_free(self->id_list);
4235010cec2SMarc-André Lureau     g_free(self->data);
4245010cec2SMarc-André Lureau }
4255010cec2SMarc-André Lureau 
4265010cec2SMarc-André Lureau static char *
4275010cec2SMarc-André Lureau get_dbus_addr(Object *o, Error **errp)
4285010cec2SMarc-André Lureau {
4295010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4305010cec2SMarc-André Lureau 
4315010cec2SMarc-André Lureau     return g_strdup(self->dbus_addr);
4325010cec2SMarc-André Lureau }
4335010cec2SMarc-André Lureau 
4345010cec2SMarc-André Lureau static void
4355010cec2SMarc-André Lureau set_dbus_addr(Object *o, const char *str, Error **errp)
4365010cec2SMarc-André Lureau {
4375010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4385010cec2SMarc-André Lureau 
4395010cec2SMarc-André Lureau     g_free(self->dbus_addr);
4405010cec2SMarc-André Lureau     self->dbus_addr = g_strdup(str);
4415010cec2SMarc-André Lureau }
4425010cec2SMarc-André Lureau 
4435010cec2SMarc-André Lureau static char *
4445010cec2SMarc-André Lureau get_id_list(Object *o, Error **errp)
4455010cec2SMarc-André Lureau {
4465010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4475010cec2SMarc-André Lureau 
4485010cec2SMarc-André Lureau     return g_strdup(self->id_list);
4495010cec2SMarc-André Lureau }
4505010cec2SMarc-André Lureau 
4515010cec2SMarc-André Lureau static void
4525010cec2SMarc-André Lureau set_id_list(Object *o, const char *str, Error **errp)
4535010cec2SMarc-André Lureau {
4545010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
4555010cec2SMarc-André Lureau 
4565010cec2SMarc-André Lureau     g_free(self->id_list);
4575010cec2SMarc-André Lureau     self->id_list = g_strdup(str);
4585010cec2SMarc-André Lureau }
4595010cec2SMarc-André Lureau 
4605010cec2SMarc-André Lureau static char *
4615010cec2SMarc-André Lureau dbus_vmstate_get_id(VMStateIf *vmif)
4625010cec2SMarc-André Lureau {
4635010cec2SMarc-André Lureau     return g_strdup(TYPE_DBUS_VMSTATE);
4645010cec2SMarc-André Lureau }
4655010cec2SMarc-André Lureau 
4665010cec2SMarc-André Lureau static void
4675010cec2SMarc-André Lureau dbus_vmstate_class_init(ObjectClass *oc, void *data)
4685010cec2SMarc-André Lureau {
4695010cec2SMarc-André Lureau     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
4705010cec2SMarc-André Lureau     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
4715010cec2SMarc-André Lureau 
4725010cec2SMarc-André Lureau     ucc->complete = dbus_vmstate_complete;
4735010cec2SMarc-André Lureau     vc->get_id = dbus_vmstate_get_id;
4745010cec2SMarc-André Lureau 
4755010cec2SMarc-André Lureau     object_class_property_add_str(oc, "addr",
476d2623129SMarkus Armbruster                                   get_dbus_addr, set_dbus_addr);
4775010cec2SMarc-André Lureau     object_class_property_add_str(oc, "id-list",
478d2623129SMarkus Armbruster                                   get_id_list, set_id_list);
4795010cec2SMarc-André Lureau }
4805010cec2SMarc-André Lureau 
4815010cec2SMarc-André Lureau static const TypeInfo dbus_vmstate_info = {
4825010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
4835010cec2SMarc-André Lureau     .parent = TYPE_OBJECT,
4845010cec2SMarc-André Lureau     .instance_size = sizeof(DBusVMState),
4855010cec2SMarc-André Lureau     .instance_finalize = dbus_vmstate_finalize,
4865010cec2SMarc-André Lureau     .class_init = dbus_vmstate_class_init,
4875010cec2SMarc-André Lureau     .interfaces = (InterfaceInfo[]) {
4885010cec2SMarc-André Lureau         { TYPE_USER_CREATABLE },
4895010cec2SMarc-André Lureau         { TYPE_VMSTATE_IF },
4905010cec2SMarc-André Lureau         { }
4915010cec2SMarc-André Lureau     }
4925010cec2SMarc-André Lureau };
4935010cec2SMarc-André Lureau 
4945010cec2SMarc-André Lureau static void
4955010cec2SMarc-André Lureau register_types(void)
4965010cec2SMarc-André Lureau {
4975010cec2SMarc-André Lureau     type_register_static(&dbus_vmstate_info);
4985010cec2SMarc-André Lureau }
4995010cec2SMarc-André Lureau 
5005010cec2SMarc-André Lureau type_init(register_types);
501