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