xref: /openbmc/qemu/backends/dbus-vmstate.c (revision 5010cec2bc87dafab39b3913c8ca91f88df9c540)
1*5010cec2SMarc-André Lureau /*
2*5010cec2SMarc-André Lureau  * QEMU dbus-vmstate
3*5010cec2SMarc-André Lureau  *
4*5010cec2SMarc-André Lureau  * Copyright (C) 2019 Red Hat Inc
5*5010cec2SMarc-André Lureau  *
6*5010cec2SMarc-André Lureau  * Authors:
7*5010cec2SMarc-André Lureau  *  Marc-André Lureau <marcandre.lureau@redhat.com>
8*5010cec2SMarc-André Lureau  *
9*5010cec2SMarc-André Lureau  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10*5010cec2SMarc-André Lureau  * See the COPYING file in the top-level directory.
11*5010cec2SMarc-André Lureau  */
12*5010cec2SMarc-André Lureau 
13*5010cec2SMarc-André Lureau #include "qemu/osdep.h"
14*5010cec2SMarc-André Lureau #include "qemu/units.h"
15*5010cec2SMarc-André Lureau #include "qemu/dbus.h"
16*5010cec2SMarc-André Lureau #include "qemu/error-report.h"
17*5010cec2SMarc-André Lureau #include "qapi/error.h"
18*5010cec2SMarc-André Lureau #include "qom/object_interfaces.h"
19*5010cec2SMarc-André Lureau #include "qapi/qmp/qerror.h"
20*5010cec2SMarc-André Lureau #include "migration/vmstate.h"
21*5010cec2SMarc-André Lureau #include "trace.h"
22*5010cec2SMarc-André Lureau 
23*5010cec2SMarc-André Lureau typedef struct DBusVMState DBusVMState;
24*5010cec2SMarc-André Lureau typedef struct DBusVMStateClass DBusVMStateClass;
25*5010cec2SMarc-André Lureau 
26*5010cec2SMarc-André Lureau #define TYPE_DBUS_VMSTATE "dbus-vmstate"
27*5010cec2SMarc-André Lureau #define DBUS_VMSTATE(obj)                                \
28*5010cec2SMarc-André Lureau     OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE)
29*5010cec2SMarc-André Lureau #define DBUS_VMSTATE_GET_CLASS(obj)                              \
30*5010cec2SMarc-André Lureau     OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE)
31*5010cec2SMarc-André Lureau #define DBUS_VMSTATE_CLASS(klass)                                    \
32*5010cec2SMarc-André Lureau     OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE)
33*5010cec2SMarc-André Lureau 
34*5010cec2SMarc-André Lureau struct DBusVMStateClass {
35*5010cec2SMarc-André Lureau     ObjectClass parent_class;
36*5010cec2SMarc-André Lureau };
37*5010cec2SMarc-André Lureau 
38*5010cec2SMarc-André Lureau struct DBusVMState {
39*5010cec2SMarc-André Lureau     Object parent;
40*5010cec2SMarc-André Lureau 
41*5010cec2SMarc-André Lureau     GDBusConnection *bus;
42*5010cec2SMarc-André Lureau     char *dbus_addr;
43*5010cec2SMarc-André Lureau     char *id_list;
44*5010cec2SMarc-André Lureau 
45*5010cec2SMarc-André Lureau     uint32_t data_size;
46*5010cec2SMarc-André Lureau     uint8_t *data;
47*5010cec2SMarc-André Lureau };
48*5010cec2SMarc-André Lureau 
49*5010cec2SMarc-André Lureau static const GDBusPropertyInfo vmstate_property_info[] = {
50*5010cec2SMarc-André Lureau     { -1, (char *) "Id", (char *) "s",
51*5010cec2SMarc-André Lureau       G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
52*5010cec2SMarc-André Lureau };
53*5010cec2SMarc-André Lureau 
54*5010cec2SMarc-André Lureau static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
55*5010cec2SMarc-André Lureau     &vmstate_property_info[0],
56*5010cec2SMarc-André Lureau     NULL
57*5010cec2SMarc-André Lureau };
58*5010cec2SMarc-André Lureau 
59*5010cec2SMarc-André Lureau static const GDBusInterfaceInfo vmstate1_interface_info = {
60*5010cec2SMarc-André Lureau     -1,
61*5010cec2SMarc-André Lureau     (char *) "org.qemu.VMState1",
62*5010cec2SMarc-André Lureau     (GDBusMethodInfo **) NULL,
63*5010cec2SMarc-André Lureau     (GDBusSignalInfo **) NULL,
64*5010cec2SMarc-André Lureau     (GDBusPropertyInfo **) &vmstate_property_info_pointers,
65*5010cec2SMarc-André Lureau     NULL,
66*5010cec2SMarc-André Lureau };
67*5010cec2SMarc-André Lureau 
68*5010cec2SMarc-André Lureau #define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
69*5010cec2SMarc-André Lureau 
70*5010cec2SMarc-André Lureau static GHashTable *
71*5010cec2SMarc-André Lureau get_id_list_set(DBusVMState *self)
72*5010cec2SMarc-André Lureau {
73*5010cec2SMarc-André Lureau     g_auto(GStrv) ids = NULL;
74*5010cec2SMarc-André Lureau     g_autoptr(GHashTable) set = NULL;
75*5010cec2SMarc-André Lureau     int i;
76*5010cec2SMarc-André Lureau 
77*5010cec2SMarc-André Lureau     if (!self->id_list) {
78*5010cec2SMarc-André Lureau         return NULL;
79*5010cec2SMarc-André Lureau     }
80*5010cec2SMarc-André Lureau 
81*5010cec2SMarc-André Lureau     ids = g_strsplit(self->id_list, ",", -1);
82*5010cec2SMarc-André Lureau     set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
83*5010cec2SMarc-André Lureau     for (i = 0; ids[i]; i++) {
84*5010cec2SMarc-André Lureau         g_hash_table_add(set, ids[i]);
85*5010cec2SMarc-André Lureau         ids[i] = NULL;
86*5010cec2SMarc-André Lureau     }
87*5010cec2SMarc-André Lureau 
88*5010cec2SMarc-André Lureau     return g_steal_pointer(&set);
89*5010cec2SMarc-André Lureau }
90*5010cec2SMarc-André Lureau 
91*5010cec2SMarc-André Lureau static GHashTable *
92*5010cec2SMarc-André Lureau dbus_get_proxies(DBusVMState *self, GError **err)
93*5010cec2SMarc-André Lureau {
94*5010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
95*5010cec2SMarc-André Lureau     g_autoptr(GHashTable) ids = NULL;
96*5010cec2SMarc-André Lureau     g_auto(GStrv) names = NULL;
97*5010cec2SMarc-André Lureau     Error *error = NULL;
98*5010cec2SMarc-André Lureau     size_t i;
99*5010cec2SMarc-André Lureau 
100*5010cec2SMarc-André Lureau     ids = get_id_list_set(self);
101*5010cec2SMarc-André Lureau     proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
102*5010cec2SMarc-André Lureau                                     g_free, g_object_unref);
103*5010cec2SMarc-André Lureau 
104*5010cec2SMarc-André Lureau     names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
105*5010cec2SMarc-André Lureau     if (!names) {
106*5010cec2SMarc-André Lureau         g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
107*5010cec2SMarc-André Lureau                     error_get_pretty(error));
108*5010cec2SMarc-André Lureau         error_free(error);
109*5010cec2SMarc-André Lureau         return NULL;
110*5010cec2SMarc-André Lureau     }
111*5010cec2SMarc-André Lureau 
112*5010cec2SMarc-André Lureau     for (i = 0; names[i]; i++) {
113*5010cec2SMarc-André Lureau         g_autoptr(GDBusProxy) proxy = NULL;
114*5010cec2SMarc-André Lureau         g_autoptr(GVariant) result = NULL;
115*5010cec2SMarc-André Lureau         g_autofree char *id = NULL;
116*5010cec2SMarc-André Lureau         size_t size;
117*5010cec2SMarc-André Lureau 
118*5010cec2SMarc-André Lureau         proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
119*5010cec2SMarc-André Lureau                     (GDBusInterfaceInfo *) &vmstate1_interface_info,
120*5010cec2SMarc-André Lureau                     names[i],
121*5010cec2SMarc-André Lureau                     "/org/qemu/VMState1",
122*5010cec2SMarc-André Lureau                     "org.qemu.VMState1",
123*5010cec2SMarc-André Lureau                     NULL, err);
124*5010cec2SMarc-André Lureau         if (!proxy) {
125*5010cec2SMarc-André Lureau             return NULL;
126*5010cec2SMarc-André Lureau         }
127*5010cec2SMarc-André Lureau 
128*5010cec2SMarc-André Lureau         result = g_dbus_proxy_get_cached_property(proxy, "Id");
129*5010cec2SMarc-André Lureau         if (!result) {
130*5010cec2SMarc-André Lureau             g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
131*5010cec2SMarc-André Lureau                                 "VMState Id property is missing.");
132*5010cec2SMarc-André Lureau             return NULL;
133*5010cec2SMarc-André Lureau         }
134*5010cec2SMarc-André Lureau 
135*5010cec2SMarc-André Lureau         id = g_variant_dup_string(result, &size);
136*5010cec2SMarc-André Lureau         if (ids && !g_hash_table_remove(ids, id)) {
137*5010cec2SMarc-André Lureau             g_clear_pointer(&id, g_free);
138*5010cec2SMarc-André Lureau             g_clear_object(&proxy);
139*5010cec2SMarc-André Lureau             continue;
140*5010cec2SMarc-André Lureau         }
141*5010cec2SMarc-André Lureau         if (size == 0 || size >= 256) {
142*5010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
143*5010cec2SMarc-André Lureau                         "VMState Id '%s' is invalid.", id);
144*5010cec2SMarc-André Lureau             return NULL;
145*5010cec2SMarc-André Lureau         }
146*5010cec2SMarc-André Lureau 
147*5010cec2SMarc-André Lureau         if (!g_hash_table_insert(proxies, id, proxy)) {
148*5010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
149*5010cec2SMarc-André Lureau                         "Duplicated VMState Id '%s'", id);
150*5010cec2SMarc-André Lureau             return NULL;
151*5010cec2SMarc-André Lureau         }
152*5010cec2SMarc-André Lureau         id = NULL;
153*5010cec2SMarc-André Lureau         proxy = NULL;
154*5010cec2SMarc-André Lureau 
155*5010cec2SMarc-André Lureau         g_clear_pointer(&result, g_variant_unref);
156*5010cec2SMarc-André Lureau     }
157*5010cec2SMarc-André Lureau 
158*5010cec2SMarc-André Lureau     if (ids) {
159*5010cec2SMarc-André Lureau         g_autofree char **left = NULL;
160*5010cec2SMarc-André Lureau 
161*5010cec2SMarc-André Lureau         left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
162*5010cec2SMarc-André Lureau         if (*left) {
163*5010cec2SMarc-André Lureau             g_autofree char *leftids = g_strjoinv(",", left);
164*5010cec2SMarc-André Lureau             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
165*5010cec2SMarc-André Lureau                         "Required VMState Id are missing: %s", leftids);
166*5010cec2SMarc-André Lureau             return NULL;
167*5010cec2SMarc-André Lureau         }
168*5010cec2SMarc-André Lureau     }
169*5010cec2SMarc-André Lureau 
170*5010cec2SMarc-André Lureau     return g_steal_pointer(&proxies);
171*5010cec2SMarc-André Lureau }
172*5010cec2SMarc-André Lureau 
173*5010cec2SMarc-André Lureau static int
174*5010cec2SMarc-André Lureau dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
175*5010cec2SMarc-André Lureau {
176*5010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
177*5010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
178*5010cec2SMarc-André Lureau     g_autoptr(GVariant) value = NULL;
179*5010cec2SMarc-André Lureau 
180*5010cec2SMarc-André Lureau     value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
181*5010cec2SMarc-André Lureau                                       data, size, sizeof(char));
182*5010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Load",
183*5010cec2SMarc-André Lureau                                     g_variant_new("(@ay)",
184*5010cec2SMarc-André Lureau                                                   g_steal_pointer(&value)),
185*5010cec2SMarc-André Lureau                                     G_DBUS_CALL_FLAGS_NO_AUTO_START,
186*5010cec2SMarc-André Lureau                                     -1, NULL, &err);
187*5010cec2SMarc-André Lureau     if (!result) {
188*5010cec2SMarc-André Lureau         error_report("%s: Failed to Load: %s", __func__, err->message);
189*5010cec2SMarc-André Lureau         return -1;
190*5010cec2SMarc-André Lureau     }
191*5010cec2SMarc-André Lureau 
192*5010cec2SMarc-André Lureau     return 0;
193*5010cec2SMarc-André Lureau }
194*5010cec2SMarc-André Lureau 
195*5010cec2SMarc-André Lureau static int dbus_vmstate_post_load(void *opaque, int version_id)
196*5010cec2SMarc-André Lureau {
197*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
198*5010cec2SMarc-André Lureau     g_autoptr(GInputStream) m = NULL;
199*5010cec2SMarc-André Lureau     g_autoptr(GDataInputStream) s = NULL;
200*5010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
201*5010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
202*5010cec2SMarc-André Lureau     uint32_t nelem;
203*5010cec2SMarc-André Lureau 
204*5010cec2SMarc-André Lureau     trace_dbus_vmstate_post_load(version_id);
205*5010cec2SMarc-André Lureau 
206*5010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
207*5010cec2SMarc-André Lureau     if (!proxies) {
208*5010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
209*5010cec2SMarc-André Lureau         return -1;
210*5010cec2SMarc-André Lureau     }
211*5010cec2SMarc-André Lureau 
212*5010cec2SMarc-André Lureau     m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
213*5010cec2SMarc-André Lureau     s = g_data_input_stream_new(m);
214*5010cec2SMarc-André Lureau     g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
215*5010cec2SMarc-André Lureau 
216*5010cec2SMarc-André Lureau     nelem = g_data_input_stream_read_uint32(s, NULL, &err);
217*5010cec2SMarc-André Lureau     if (err) {
218*5010cec2SMarc-André Lureau         goto error;
219*5010cec2SMarc-André Lureau     }
220*5010cec2SMarc-André Lureau 
221*5010cec2SMarc-André Lureau     while (nelem > 0) {
222*5010cec2SMarc-André Lureau         GDBusProxy *proxy = NULL;
223*5010cec2SMarc-André Lureau         uint32_t len;
224*5010cec2SMarc-André Lureau         gsize bytes_read, avail;
225*5010cec2SMarc-André Lureau         char id[256];
226*5010cec2SMarc-André Lureau 
227*5010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
228*5010cec2SMarc-André Lureau         if (err) {
229*5010cec2SMarc-André Lureau             goto error;
230*5010cec2SMarc-André Lureau         }
231*5010cec2SMarc-André Lureau         if (len >= 256) {
232*5010cec2SMarc-André Lureau             error_report("%s: Invalid DBus vmstate proxy name %u",
233*5010cec2SMarc-André Lureau                          __func__, len);
234*5010cec2SMarc-André Lureau             return -1;
235*5010cec2SMarc-André Lureau         }
236*5010cec2SMarc-André Lureau         if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
237*5010cec2SMarc-André Lureau                                      &bytes_read, NULL, &err)) {
238*5010cec2SMarc-André Lureau             goto error;
239*5010cec2SMarc-André Lureau         }
240*5010cec2SMarc-André Lureau         g_return_val_if_fail(bytes_read == len, -1);
241*5010cec2SMarc-André Lureau         id[len] = 0;
242*5010cec2SMarc-André Lureau 
243*5010cec2SMarc-André Lureau         trace_dbus_vmstate_loading(id);
244*5010cec2SMarc-André Lureau 
245*5010cec2SMarc-André Lureau         proxy = g_hash_table_lookup(proxies, id);
246*5010cec2SMarc-André Lureau         if (!proxy) {
247*5010cec2SMarc-André Lureau             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
248*5010cec2SMarc-André Lureau             return -1;
249*5010cec2SMarc-André Lureau         }
250*5010cec2SMarc-André Lureau 
251*5010cec2SMarc-André Lureau         len = g_data_input_stream_read_uint32(s, NULL, &err);
252*5010cec2SMarc-André Lureau         avail = g_buffered_input_stream_get_available(
253*5010cec2SMarc-André Lureau             G_BUFFERED_INPUT_STREAM(s));
254*5010cec2SMarc-André Lureau 
255*5010cec2SMarc-André Lureau         if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
256*5010cec2SMarc-André Lureau             error_report("%s: Invalid vmstate size: %u", __func__, len);
257*5010cec2SMarc-André Lureau             return -1;
258*5010cec2SMarc-André Lureau         }
259*5010cec2SMarc-André Lureau 
260*5010cec2SMarc-André Lureau         if (dbus_load_state_proxy(proxy,
261*5010cec2SMarc-André Lureau                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
262*5010cec2SMarc-André Lureau                                                     NULL),
263*5010cec2SMarc-André Lureau                 len) < 0) {
264*5010cec2SMarc-André Lureau             error_report("%s: Failed to restore Id '%s'", __func__, id);
265*5010cec2SMarc-André Lureau             return -1;
266*5010cec2SMarc-André Lureau         }
267*5010cec2SMarc-André Lureau 
268*5010cec2SMarc-André Lureau         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
269*5010cec2SMarc-André Lureau             goto error;
270*5010cec2SMarc-André Lureau         }
271*5010cec2SMarc-André Lureau 
272*5010cec2SMarc-André Lureau         nelem -= 1;
273*5010cec2SMarc-André Lureau     }
274*5010cec2SMarc-André Lureau 
275*5010cec2SMarc-André Lureau     return 0;
276*5010cec2SMarc-André Lureau 
277*5010cec2SMarc-André Lureau error:
278*5010cec2SMarc-André Lureau     error_report("%s: Failed to read from stream: %s", __func__, err->message);
279*5010cec2SMarc-André Lureau     return -1;
280*5010cec2SMarc-André Lureau }
281*5010cec2SMarc-André Lureau 
282*5010cec2SMarc-André Lureau static void
283*5010cec2SMarc-André Lureau dbus_save_state_proxy(gpointer key,
284*5010cec2SMarc-André Lureau                       gpointer value,
285*5010cec2SMarc-André Lureau                       gpointer user_data)
286*5010cec2SMarc-André Lureau {
287*5010cec2SMarc-André Lureau     GDataOutputStream *s = user_data;
288*5010cec2SMarc-André Lureau     const char *id = key;
289*5010cec2SMarc-André Lureau     GDBusProxy *proxy = value;
290*5010cec2SMarc-André Lureau     g_autoptr(GVariant) result = NULL;
291*5010cec2SMarc-André Lureau     g_autoptr(GVariant) child = NULL;
292*5010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
293*5010cec2SMarc-André Lureau     const uint8_t *data;
294*5010cec2SMarc-André Lureau     gsize size;
295*5010cec2SMarc-André Lureau 
296*5010cec2SMarc-André Lureau     trace_dbus_vmstate_saving(id);
297*5010cec2SMarc-André Lureau 
298*5010cec2SMarc-André Lureau     result = g_dbus_proxy_call_sync(proxy, "Save",
299*5010cec2SMarc-André Lureau                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
300*5010cec2SMarc-André Lureau                                     -1, NULL, &err);
301*5010cec2SMarc-André Lureau     if (!result) {
302*5010cec2SMarc-André Lureau         error_report("%s: Failed to Save: %s", __func__, err->message);
303*5010cec2SMarc-André Lureau         return;
304*5010cec2SMarc-André Lureau     }
305*5010cec2SMarc-André Lureau 
306*5010cec2SMarc-André Lureau     child = g_variant_get_child_value(result, 0);
307*5010cec2SMarc-André Lureau     data = g_variant_get_fixed_array(child, &size, sizeof(char));
308*5010cec2SMarc-André Lureau     if (!data) {
309*5010cec2SMarc-André Lureau         error_report("%s: Failed to Save: not a byte array", __func__);
310*5010cec2SMarc-André Lureau         return;
311*5010cec2SMarc-André Lureau     }
312*5010cec2SMarc-André Lureau     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
313*5010cec2SMarc-André Lureau         error_report("%s: Too large vmstate data to save: %zu",
314*5010cec2SMarc-André Lureau                      __func__, (size_t)size);
315*5010cec2SMarc-André Lureau         return;
316*5010cec2SMarc-André Lureau     }
317*5010cec2SMarc-André Lureau 
318*5010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
319*5010cec2SMarc-André Lureau         !g_data_output_stream_put_string(s, id, NULL, &err) ||
320*5010cec2SMarc-André Lureau         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
321*5010cec2SMarc-André Lureau         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
322*5010cec2SMarc-André Lureau                                    data, size, NULL, NULL, &err)) {
323*5010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
324*5010cec2SMarc-André Lureau                      __func__, err->message);
325*5010cec2SMarc-André Lureau     }
326*5010cec2SMarc-André Lureau }
327*5010cec2SMarc-André Lureau 
328*5010cec2SMarc-André Lureau static int dbus_vmstate_pre_save(void *opaque)
329*5010cec2SMarc-André Lureau {
330*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(opaque);
331*5010cec2SMarc-André Lureau     g_autoptr(GOutputStream) m = NULL;
332*5010cec2SMarc-André Lureau     g_autoptr(GDataOutputStream) s = NULL;
333*5010cec2SMarc-André Lureau     g_autoptr(GHashTable) proxies = NULL;
334*5010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
335*5010cec2SMarc-André Lureau 
336*5010cec2SMarc-André Lureau     trace_dbus_vmstate_pre_save();
337*5010cec2SMarc-André Lureau 
338*5010cec2SMarc-André Lureau     proxies = dbus_get_proxies(self, &err);
339*5010cec2SMarc-André Lureau     if (!proxies) {
340*5010cec2SMarc-André Lureau         error_report("%s: Failed to get proxies: %s", __func__, err->message);
341*5010cec2SMarc-André Lureau         return -1;
342*5010cec2SMarc-André Lureau     }
343*5010cec2SMarc-André Lureau 
344*5010cec2SMarc-André Lureau     m = g_memory_output_stream_new_resizable();
345*5010cec2SMarc-André Lureau     s = g_data_output_stream_new(m);
346*5010cec2SMarc-André Lureau     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
347*5010cec2SMarc-André Lureau 
348*5010cec2SMarc-André Lureau     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
349*5010cec2SMarc-André Lureau                                          NULL, &err)) {
350*5010cec2SMarc-André Lureau         error_report("%s: Failed to write to stream: %s",
351*5010cec2SMarc-André Lureau                      __func__, err->message);
352*5010cec2SMarc-André Lureau         return -1;
353*5010cec2SMarc-André Lureau     }
354*5010cec2SMarc-André Lureau 
355*5010cec2SMarc-André Lureau     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
356*5010cec2SMarc-André Lureau 
357*5010cec2SMarc-André Lureau     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
358*5010cec2SMarc-André Lureau         > UINT32_MAX) {
359*5010cec2SMarc-André Lureau         error_report("%s: DBus vmstate buffer is too large", __func__);
360*5010cec2SMarc-André Lureau         return -1;
361*5010cec2SMarc-André Lureau     }
362*5010cec2SMarc-André Lureau 
363*5010cec2SMarc-André Lureau     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
364*5010cec2SMarc-André Lureau         error_report("%s: Failed to close stream: %s", __func__, err->message);
365*5010cec2SMarc-André Lureau         return -1;
366*5010cec2SMarc-André Lureau     }
367*5010cec2SMarc-André Lureau 
368*5010cec2SMarc-André Lureau     g_free(self->data);
369*5010cec2SMarc-André Lureau     self->data_size =
370*5010cec2SMarc-André Lureau         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
371*5010cec2SMarc-André Lureau     self->data =
372*5010cec2SMarc-André Lureau         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
373*5010cec2SMarc-André Lureau 
374*5010cec2SMarc-André Lureau     return 0;
375*5010cec2SMarc-André Lureau }
376*5010cec2SMarc-André Lureau 
377*5010cec2SMarc-André Lureau static const VMStateDescription dbus_vmstate = {
378*5010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
379*5010cec2SMarc-André Lureau     .version_id = 0,
380*5010cec2SMarc-André Lureau     .pre_save = dbus_vmstate_pre_save,
381*5010cec2SMarc-André Lureau     .post_load = dbus_vmstate_post_load,
382*5010cec2SMarc-André Lureau     .fields = (VMStateField[]) {
383*5010cec2SMarc-André Lureau         VMSTATE_UINT32(data_size, DBusVMState),
384*5010cec2SMarc-André Lureau         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
385*5010cec2SMarc-André Lureau         VMSTATE_END_OF_LIST()
386*5010cec2SMarc-André Lureau     }
387*5010cec2SMarc-André Lureau };
388*5010cec2SMarc-André Lureau 
389*5010cec2SMarc-André Lureau static void
390*5010cec2SMarc-André Lureau dbus_vmstate_complete(UserCreatable *uc, Error **errp)
391*5010cec2SMarc-André Lureau {
392*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(uc);
393*5010cec2SMarc-André Lureau     g_autoptr(GError) err = NULL;
394*5010cec2SMarc-André Lureau 
395*5010cec2SMarc-André Lureau     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
396*5010cec2SMarc-André Lureau         error_setg(errp, "There is already an instance of %s",
397*5010cec2SMarc-André Lureau                    TYPE_DBUS_VMSTATE);
398*5010cec2SMarc-André Lureau         return;
399*5010cec2SMarc-André Lureau     }
400*5010cec2SMarc-André Lureau 
401*5010cec2SMarc-André Lureau     if (!self->dbus_addr) {
402*5010cec2SMarc-André Lureau         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
403*5010cec2SMarc-André Lureau         return;
404*5010cec2SMarc-André Lureau     }
405*5010cec2SMarc-André Lureau 
406*5010cec2SMarc-André Lureau     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
407*5010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
408*5010cec2SMarc-André Lureau                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
409*5010cec2SMarc-André Lureau                     NULL, NULL, &err);
410*5010cec2SMarc-André Lureau     if (err) {
411*5010cec2SMarc-André Lureau         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
412*5010cec2SMarc-André Lureau         return;
413*5010cec2SMarc-André Lureau     }
414*5010cec2SMarc-André Lureau 
415*5010cec2SMarc-André Lureau     if (vmstate_register(VMSTATE_IF(self), -1, &dbus_vmstate, self) < 0) {
416*5010cec2SMarc-André Lureau         error_setg(errp, "Failed to register vmstate");
417*5010cec2SMarc-André Lureau     }
418*5010cec2SMarc-André Lureau }
419*5010cec2SMarc-André Lureau 
420*5010cec2SMarc-André Lureau static void
421*5010cec2SMarc-André Lureau dbus_vmstate_finalize(Object *o)
422*5010cec2SMarc-André Lureau {
423*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
424*5010cec2SMarc-André Lureau 
425*5010cec2SMarc-André Lureau     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
426*5010cec2SMarc-André Lureau 
427*5010cec2SMarc-André Lureau     g_clear_object(&self->bus);
428*5010cec2SMarc-André Lureau     g_free(self->dbus_addr);
429*5010cec2SMarc-André Lureau     g_free(self->id_list);
430*5010cec2SMarc-André Lureau     g_free(self->data);
431*5010cec2SMarc-André Lureau }
432*5010cec2SMarc-André Lureau 
433*5010cec2SMarc-André Lureau static char *
434*5010cec2SMarc-André Lureau get_dbus_addr(Object *o, Error **errp)
435*5010cec2SMarc-André Lureau {
436*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
437*5010cec2SMarc-André Lureau 
438*5010cec2SMarc-André Lureau     return g_strdup(self->dbus_addr);
439*5010cec2SMarc-André Lureau }
440*5010cec2SMarc-André Lureau 
441*5010cec2SMarc-André Lureau static void
442*5010cec2SMarc-André Lureau set_dbus_addr(Object *o, const char *str, Error **errp)
443*5010cec2SMarc-André Lureau {
444*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
445*5010cec2SMarc-André Lureau 
446*5010cec2SMarc-André Lureau     g_free(self->dbus_addr);
447*5010cec2SMarc-André Lureau     self->dbus_addr = g_strdup(str);
448*5010cec2SMarc-André Lureau }
449*5010cec2SMarc-André Lureau 
450*5010cec2SMarc-André Lureau static char *
451*5010cec2SMarc-André Lureau get_id_list(Object *o, Error **errp)
452*5010cec2SMarc-André Lureau {
453*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
454*5010cec2SMarc-André Lureau 
455*5010cec2SMarc-André Lureau     return g_strdup(self->id_list);
456*5010cec2SMarc-André Lureau }
457*5010cec2SMarc-André Lureau 
458*5010cec2SMarc-André Lureau static void
459*5010cec2SMarc-André Lureau set_id_list(Object *o, const char *str, Error **errp)
460*5010cec2SMarc-André Lureau {
461*5010cec2SMarc-André Lureau     DBusVMState *self = DBUS_VMSTATE(o);
462*5010cec2SMarc-André Lureau 
463*5010cec2SMarc-André Lureau     g_free(self->id_list);
464*5010cec2SMarc-André Lureau     self->id_list = g_strdup(str);
465*5010cec2SMarc-André Lureau }
466*5010cec2SMarc-André Lureau 
467*5010cec2SMarc-André Lureau static char *
468*5010cec2SMarc-André Lureau dbus_vmstate_get_id(VMStateIf *vmif)
469*5010cec2SMarc-André Lureau {
470*5010cec2SMarc-André Lureau     return g_strdup(TYPE_DBUS_VMSTATE);
471*5010cec2SMarc-André Lureau }
472*5010cec2SMarc-André Lureau 
473*5010cec2SMarc-André Lureau static void
474*5010cec2SMarc-André Lureau dbus_vmstate_class_init(ObjectClass *oc, void *data)
475*5010cec2SMarc-André Lureau {
476*5010cec2SMarc-André Lureau     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
477*5010cec2SMarc-André Lureau     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
478*5010cec2SMarc-André Lureau 
479*5010cec2SMarc-André Lureau     ucc->complete = dbus_vmstate_complete;
480*5010cec2SMarc-André Lureau     vc->get_id = dbus_vmstate_get_id;
481*5010cec2SMarc-André Lureau 
482*5010cec2SMarc-André Lureau     object_class_property_add_str(oc, "addr",
483*5010cec2SMarc-André Lureau                                   get_dbus_addr, set_dbus_addr,
484*5010cec2SMarc-André Lureau                                   &error_abort);
485*5010cec2SMarc-André Lureau     object_class_property_add_str(oc, "id-list",
486*5010cec2SMarc-André Lureau                                   get_id_list, set_id_list,
487*5010cec2SMarc-André Lureau                                   &error_abort);
488*5010cec2SMarc-André Lureau }
489*5010cec2SMarc-André Lureau 
490*5010cec2SMarc-André Lureau static const TypeInfo dbus_vmstate_info = {
491*5010cec2SMarc-André Lureau     .name = TYPE_DBUS_VMSTATE,
492*5010cec2SMarc-André Lureau     .parent = TYPE_OBJECT,
493*5010cec2SMarc-André Lureau     .instance_size = sizeof(DBusVMState),
494*5010cec2SMarc-André Lureau     .instance_finalize = dbus_vmstate_finalize,
495*5010cec2SMarc-André Lureau     .class_size = sizeof(DBusVMStateClass),
496*5010cec2SMarc-André Lureau     .class_init = dbus_vmstate_class_init,
497*5010cec2SMarc-André Lureau     .interfaces = (InterfaceInfo[]) {
498*5010cec2SMarc-André Lureau         { TYPE_USER_CREATABLE },
499*5010cec2SMarc-André Lureau         { TYPE_VMSTATE_IF },
500*5010cec2SMarc-André Lureau         { }
501*5010cec2SMarc-André Lureau     }
502*5010cec2SMarc-André Lureau };
503*5010cec2SMarc-André Lureau 
504*5010cec2SMarc-André Lureau static void
505*5010cec2SMarc-André Lureau register_types(void)
506*5010cec2SMarc-André Lureau {
507*5010cec2SMarc-André Lureau     type_register_static(&dbus_vmstate_info);
508*5010cec2SMarc-André Lureau }
509*5010cec2SMarc-André Lureau 
510*5010cec2SMarc-André Lureau type_init(register_types);
511