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