123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- /*
- * QEMU dbus-vmstate
- *
- * Copyright (C) 2019 Red Hat Inc
- *
- * Authors:
- * Marc-André Lureau <marcandre.lureau@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
- #include "qemu/osdep.h"
- #include "qemu/units.h"
- #include "qemu/dbus.h"
- #include "qemu/error-report.h"
- #include "qapi/error.h"
- #include "qom/object_interfaces.h"
- #include "qapi/qmp/qerror.h"
- #include "migration/vmstate.h"
- #include "trace.h"
- #include "qom/object.h"
- #define TYPE_DBUS_VMSTATE "dbus-vmstate"
- OBJECT_DECLARE_SIMPLE_TYPE(DBusVMState,
- DBUS_VMSTATE)
- struct DBusVMState {
- Object parent;
- GDBusConnection *bus;
- char *dbus_addr;
- char *id_list;
- uint32_t data_size;
- uint8_t *data;
- };
- static const GDBusPropertyInfo vmstate_property_info[] = {
- { -1, (char *) "Id", (char *) "s",
- G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
- };
- static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
- &vmstate_property_info[0],
- NULL
- };
- static const GDBusInterfaceInfo vmstate1_interface_info = {
- -1,
- (char *) "org.qemu.VMState1",
- (GDBusMethodInfo **) NULL,
- (GDBusSignalInfo **) NULL,
- (GDBusPropertyInfo **) &vmstate_property_info_pointers,
- NULL,
- };
- #define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
- static GHashTable *
- get_id_list_set(DBusVMState *self)
- {
- g_auto(GStrv) ids = NULL;
- g_autoptr(GHashTable) set = NULL;
- int i;
- if (!self->id_list) {
- return NULL;
- }
- ids = g_strsplit(self->id_list, ",", -1);
- set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- for (i = 0; ids[i]; i++) {
- g_hash_table_add(set, ids[i]);
- ids[i] = NULL;
- }
- return g_steal_pointer(&set);
- }
- static GHashTable *
- dbus_get_proxies(DBusVMState *self, GError **err)
- {
- g_autoptr(GHashTable) proxies = NULL;
- g_autoptr(GHashTable) ids = NULL;
- g_auto(GStrv) names = NULL;
- Error *error = NULL;
- size_t i;
- ids = get_id_list_set(self);
- proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_object_unref);
- names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
- if (!names) {
- g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
- error_get_pretty(error));
- error_free(error);
- return NULL;
- }
- for (i = 0; names[i]; i++) {
- g_autoptr(GDBusProxy) proxy = NULL;
- g_autoptr(GVariant) result = NULL;
- g_autofree char *id = NULL;
- size_t size;
- proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
- (GDBusInterfaceInfo *) &vmstate1_interface_info,
- names[i],
- "/org/qemu/VMState1",
- "org.qemu.VMState1",
- NULL, err);
- if (!proxy) {
- if (err != NULL && *err != NULL) {
- warn_report("%s: Failed to create proxy: %s",
- __func__, (*err)->message);
- g_clear_error(err);
- }
- continue;
- }
- result = g_dbus_proxy_get_cached_property(proxy, "Id");
- if (!result) {
- warn_report("%s: VMState Id property is missing.", __func__);
- g_clear_object(&proxy);
- continue;
- }
- id = g_variant_dup_string(result, &size);
- if (ids && !g_hash_table_remove(ids, id)) {
- g_clear_pointer(&id, g_free);
- g_clear_object(&proxy);
- continue;
- }
- if (size == 0 || size >= 256) {
- g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
- "VMState Id '%s' is invalid.", id);
- return NULL;
- }
- if (!g_hash_table_insert(proxies, id, proxy)) {
- g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Duplicated VMState Id '%s'", id);
- return NULL;
- }
- id = NULL;
- proxy = NULL;
- g_clear_pointer(&result, g_variant_unref);
- }
- if (ids) {
- g_autofree char **left = NULL;
- left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
- if (*left) {
- g_autofree char *leftids = g_strjoinv(",", left);
- g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Required VMState Id are missing: %s", leftids);
- return NULL;
- }
- }
- return g_steal_pointer(&proxies);
- }
- static int
- dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
- {
- g_autoptr(GError) err = NULL;
- g_autoptr(GVariant) result = NULL;
- g_autoptr(GVariant) value = NULL;
- value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
- data, size, sizeof(char));
- result = g_dbus_proxy_call_sync(proxy, "Load",
- g_variant_new("(@ay)",
- g_steal_pointer(&value)),
- G_DBUS_CALL_FLAGS_NO_AUTO_START,
- -1, NULL, &err);
- if (!result) {
- error_report("%s: Failed to Load: %s", __func__, err->message);
- return -1;
- }
- return 0;
- }
- static int dbus_vmstate_post_load(void *opaque, int version_id)
- {
- DBusVMState *self = DBUS_VMSTATE(opaque);
- g_autoptr(GInputStream) m = NULL;
- g_autoptr(GDataInputStream) s = NULL;
- g_autoptr(GError) err = NULL;
- g_autoptr(GHashTable) proxies = NULL;
- uint32_t nelem;
- trace_dbus_vmstate_post_load(version_id);
- proxies = dbus_get_proxies(self, &err);
- if (!proxies) {
- error_report("%s: Failed to get proxies: %s", __func__, err->message);
- return -1;
- }
- m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
- s = g_data_input_stream_new(m);
- g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
- g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s),
- DBUS_VMSTATE_SIZE_LIMIT);
- nelem = g_data_input_stream_read_uint32(s, NULL, &err);
- if (err) {
- goto error;
- }
- while (nelem > 0) {
- GDBusProxy *proxy = NULL;
- uint32_t len;
- gsize bytes_read, avail;
- char id[256];
- len = g_data_input_stream_read_uint32(s, NULL, &err);
- if (err) {
- goto error;
- }
- if (len >= 256) {
- error_report("%s: Invalid DBus vmstate proxy name %u",
- __func__, len);
- return -1;
- }
- if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
- &bytes_read, NULL, &err)) {
- goto error;
- }
- if (bytes_read != len) {
- error_report("%s: Short read", __func__);
- return -1;
- }
- id[len] = 0;
- trace_dbus_vmstate_loading(id);
- proxy = g_hash_table_lookup(proxies, id);
- if (!proxy) {
- error_report("%s: Failed to find proxy Id '%s'", __func__, id);
- return -1;
- }
- len = g_data_input_stream_read_uint32(s, NULL, &err);
- if (len > DBUS_VMSTATE_SIZE_LIMIT) {
- error_report("%s: Invalid vmstate size: %u", __func__, len);
- return -1;
- }
- g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL,
- &err);
- if (err) {
- goto error;
- }
- avail = g_buffered_input_stream_get_available(
- G_BUFFERED_INPUT_STREAM(s));
- if (len > avail) {
- error_report("%s: Not enough data available to load for Id: '%s'. "
- "Available data size: %zu, Actual vmstate size: %u",
- __func__, id, avail, len);
- return -1;
- }
- if (dbus_load_state_proxy(proxy,
- g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
- NULL),
- len) < 0) {
- error_report("%s: Failed to restore Id '%s'", __func__, id);
- return -1;
- }
- if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
- goto error;
- }
- nelem -= 1;
- }
- return 0;
- error:
- error_report("%s: Failed to read from stream: %s", __func__, err->message);
- return -1;
- }
- static void
- dbus_save_state_proxy(gpointer key,
- gpointer value,
- gpointer user_data)
- {
- GDataOutputStream *s = user_data;
- const char *id = key;
- GDBusProxy *proxy = value;
- g_autoptr(GVariant) result = NULL;
- g_autoptr(GVariant) child = NULL;
- g_autoptr(GError) err = NULL;
- const uint8_t *data;
- gsize size;
- trace_dbus_vmstate_saving(id);
- result = g_dbus_proxy_call_sync(proxy, "Save",
- NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
- -1, NULL, &err);
- if (!result) {
- error_report("%s: Failed to Save: %s", __func__, err->message);
- return;
- }
- child = g_variant_get_child_value(result, 0);
- data = g_variant_get_fixed_array(child, &size, sizeof(char));
- if (!data) {
- error_report("%s: Failed to Save: not a byte array", __func__);
- return;
- }
- if (size > DBUS_VMSTATE_SIZE_LIMIT) {
- error_report("%s: Too large vmstate data to save: %zu",
- __func__, (size_t)size);
- return;
- }
- if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
- !g_data_output_stream_put_string(s, id, NULL, &err) ||
- !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
- !g_output_stream_write_all(G_OUTPUT_STREAM(s),
- data, size, NULL, NULL, &err)) {
- error_report("%s: Failed to write to stream: %s",
- __func__, err->message);
- }
- }
- static int dbus_vmstate_pre_save(void *opaque)
- {
- DBusVMState *self = DBUS_VMSTATE(opaque);
- g_autoptr(GOutputStream) m = NULL;
- g_autoptr(GDataOutputStream) s = NULL;
- g_autoptr(GHashTable) proxies = NULL;
- g_autoptr(GError) err = NULL;
- trace_dbus_vmstate_pre_save();
- proxies = dbus_get_proxies(self, &err);
- if (!proxies) {
- error_report("%s: Failed to get proxies: %s", __func__, err->message);
- return -1;
- }
- m = g_memory_output_stream_new_resizable();
- s = g_data_output_stream_new(m);
- g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
- if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
- NULL, &err)) {
- error_report("%s: Failed to write to stream: %s",
- __func__, err->message);
- return -1;
- }
- g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
- if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
- > UINT32_MAX) {
- error_report("%s: DBus vmstate buffer is too large", __func__);
- return -1;
- }
- if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
- error_report("%s: Failed to close stream: %s", __func__, err->message);
- return -1;
- }
- g_free(self->data);
- self->data_size =
- g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
- self->data =
- g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
- return 0;
- }
- static const VMStateDescription dbus_vmstate = {
- .name = TYPE_DBUS_VMSTATE,
- .version_id = 0,
- .pre_save = dbus_vmstate_pre_save,
- .post_load = dbus_vmstate_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(data_size, DBusVMState),
- VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
- VMSTATE_END_OF_LIST()
- }
- };
- static void
- dbus_vmstate_complete(UserCreatable *uc, Error **errp)
- {
- DBusVMState *self = DBUS_VMSTATE(uc);
- g_autoptr(GError) err = NULL;
- if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
- error_setg(errp, "There is already an instance of %s",
- TYPE_DBUS_VMSTATE);
- return;
- }
- if (!self->dbus_addr) {
- error_setg(errp, QERR_MISSING_PARAMETER, "addr");
- return;
- }
- self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
- G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
- G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
- NULL, NULL, &err);
- if (err) {
- error_setg(errp, "failed to connect to DBus: '%s'", err->message);
- return;
- }
- if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
- &dbus_vmstate, self) < 0) {
- error_setg(errp, "Failed to register vmstate");
- }
- }
- static void
- dbus_vmstate_finalize(Object *o)
- {
- DBusVMState *self = DBUS_VMSTATE(o);
- vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
- g_clear_object(&self->bus);
- g_free(self->dbus_addr);
- g_free(self->id_list);
- g_free(self->data);
- }
- static char *
- get_dbus_addr(Object *o, Error **errp)
- {
- DBusVMState *self = DBUS_VMSTATE(o);
- return g_strdup(self->dbus_addr);
- }
- static void
- set_dbus_addr(Object *o, const char *str, Error **errp)
- {
- DBusVMState *self = DBUS_VMSTATE(o);
- g_free(self->dbus_addr);
- self->dbus_addr = g_strdup(str);
- }
- static char *
- get_id_list(Object *o, Error **errp)
- {
- DBusVMState *self = DBUS_VMSTATE(o);
- return g_strdup(self->id_list);
- }
- static void
- set_id_list(Object *o, const char *str, Error **errp)
- {
- DBusVMState *self = DBUS_VMSTATE(o);
- g_free(self->id_list);
- self->id_list = g_strdup(str);
- }
- static char *
- dbus_vmstate_get_id(VMStateIf *vmif)
- {
- return g_strdup(TYPE_DBUS_VMSTATE);
- }
- static void
- dbus_vmstate_class_init(ObjectClass *oc, void *data)
- {
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
- VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
- ucc->complete = dbus_vmstate_complete;
- vc->get_id = dbus_vmstate_get_id;
- object_class_property_add_str(oc, "addr",
- get_dbus_addr, set_dbus_addr);
- object_class_property_add_str(oc, "id-list",
- get_id_list, set_id_list);
- }
- static const TypeInfo dbus_vmstate_info = {
- .name = TYPE_DBUS_VMSTATE,
- .parent = TYPE_OBJECT,
- .instance_size = sizeof(DBusVMState),
- .instance_finalize = dbus_vmstate_finalize,
- .class_init = dbus_vmstate_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_USER_CREATABLE },
- { TYPE_VMSTATE_IF },
- { }
- }
- };
- static void
- register_types(void)
- {
- type_register_static(&dbus_vmstate_info);
- }
- type_init(register_types);
|