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 *
get_id_list_set(DBusVMState * self)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 *
dbus_get_proxies(DBusVMState * self,GError ** err)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
dbus_load_state_proxy(GDBusProxy * proxy,const uint8_t * data,size_t size)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
dbus_vmstate_post_load(void * opaque,int version_id)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
dbus_save_state_proxy(gpointer key,gpointer value,gpointer user_data)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
dbus_vmstate_pre_save(void * opaque)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
dbus_vmstate_complete(UserCreatable * uc,Error ** errp)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
dbus_vmstate_finalize(Object * o)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 *
get_dbus_addr(Object * o,Error ** errp)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
set_dbus_addr(Object * o,const char * str,Error ** errp)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 *
get_id_list(Object * o,Error ** errp)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
set_id_list(Object * o,const char * str,Error ** errp)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 *
dbus_vmstate_get_id(VMStateIf * vmif)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
dbus_vmstate_class_init(ObjectClass * oc,void * data)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
register_types(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