qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dbus-vmstate.c (14902B)


      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"
     26 OBJECT_DECLARE_SIMPLE_TYPE(DBusVMState,
     27                            DBUS_VMSTATE)
     28 
     29 
     30 struct 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 
     41 static const GDBusPropertyInfo vmstate_property_info[] = {
     42     { -1, (char *) "Id", (char *) "s",
     43       G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
     44 };
     45 
     46 static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
     47     &vmstate_property_info[0],
     48     NULL
     49 };
     50 
     51 static 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 
     62 static GHashTable *
     63 get_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 
     83 static GHashTable *
     84 dbus_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             if (err != NULL && *err != NULL) {
    118                 warn_report("%s: Failed to create proxy: %s",
    119                             __func__, (*err)->message);
    120                 g_clear_error(err);
    121             }
    122             continue;
    123         }
    124 
    125         result = g_dbus_proxy_get_cached_property(proxy, "Id");
    126         if (!result) {
    127             warn_report("%s: VMState Id property is missing.", __func__);
    128             g_clear_object(&proxy);
    129             continue;
    130         }
    131 
    132         id = g_variant_dup_string(result, &size);
    133         if (ids && !g_hash_table_remove(ids, id)) {
    134             g_clear_pointer(&id, g_free);
    135             g_clear_object(&proxy);
    136             continue;
    137         }
    138         if (size == 0 || size >= 256) {
    139             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    140                         "VMState Id '%s' is invalid.", id);
    141             return NULL;
    142         }
    143 
    144         if (!g_hash_table_insert(proxies, id, proxy)) {
    145             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    146                         "Duplicated VMState Id '%s'", id);
    147             return NULL;
    148         }
    149         id = NULL;
    150         proxy = NULL;
    151 
    152         g_clear_pointer(&result, g_variant_unref);
    153     }
    154 
    155     if (ids) {
    156         g_autofree char **left = NULL;
    157 
    158         left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
    159         if (*left) {
    160             g_autofree char *leftids = g_strjoinv(",", left);
    161             g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    162                         "Required VMState Id are missing: %s", leftids);
    163             return NULL;
    164         }
    165     }
    166 
    167     return g_steal_pointer(&proxies);
    168 }
    169 
    170 static int
    171 dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
    172 {
    173     g_autoptr(GError) err = NULL;
    174     g_autoptr(GVariant) result = NULL;
    175     g_autoptr(GVariant) value = NULL;
    176 
    177     value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
    178                                       data, size, sizeof(char));
    179     result = g_dbus_proxy_call_sync(proxy, "Load",
    180                                     g_variant_new("(@ay)",
    181                                                   g_steal_pointer(&value)),
    182                                     G_DBUS_CALL_FLAGS_NO_AUTO_START,
    183                                     -1, NULL, &err);
    184     if (!result) {
    185         error_report("%s: Failed to Load: %s", __func__, err->message);
    186         return -1;
    187     }
    188 
    189     return 0;
    190 }
    191 
    192 static int dbus_vmstate_post_load(void *opaque, int version_id)
    193 {
    194     DBusVMState *self = DBUS_VMSTATE(opaque);
    195     g_autoptr(GInputStream) m = NULL;
    196     g_autoptr(GDataInputStream) s = NULL;
    197     g_autoptr(GError) err = NULL;
    198     g_autoptr(GHashTable) proxies = NULL;
    199     uint32_t nelem;
    200 
    201     trace_dbus_vmstate_post_load(version_id);
    202 
    203     proxies = dbus_get_proxies(self, &err);
    204     if (!proxies) {
    205         error_report("%s: Failed to get proxies: %s", __func__, err->message);
    206         return -1;
    207     }
    208 
    209     m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
    210     s = g_data_input_stream_new(m);
    211     g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
    212     g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s),
    213                                             DBUS_VMSTATE_SIZE_LIMIT);
    214 
    215     nelem = g_data_input_stream_read_uint32(s, NULL, &err);
    216     if (err) {
    217         goto error;
    218     }
    219 
    220     while (nelem > 0) {
    221         GDBusProxy *proxy = NULL;
    222         uint32_t len;
    223         gsize bytes_read, avail;
    224         char id[256];
    225 
    226         len = g_data_input_stream_read_uint32(s, NULL, &err);
    227         if (err) {
    228             goto error;
    229         }
    230         if (len >= 256) {
    231             error_report("%s: Invalid DBus vmstate proxy name %u",
    232                          __func__, len);
    233             return -1;
    234         }
    235         if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
    236                                      &bytes_read, NULL, &err)) {
    237             goto error;
    238         }
    239         if (bytes_read != len) {
    240             error_report("%s: Short read", __func__);
    241             return -1;
    242         }
    243         id[len] = 0;
    244 
    245         trace_dbus_vmstate_loading(id);
    246 
    247         proxy = g_hash_table_lookup(proxies, id);
    248         if (!proxy) {
    249             error_report("%s: Failed to find proxy Id '%s'", __func__, id);
    250             return -1;
    251         }
    252 
    253         len = g_data_input_stream_read_uint32(s, NULL, &err);
    254         if (len > DBUS_VMSTATE_SIZE_LIMIT) {
    255             error_report("%s: Invalid vmstate size: %u", __func__, len);
    256             return -1;
    257         }
    258 
    259         g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL,
    260                                      &err);
    261         if (err) {
    262             goto error;
    263         }
    264 
    265         avail = g_buffered_input_stream_get_available(
    266             G_BUFFERED_INPUT_STREAM(s));
    267         if (len > avail) {
    268             error_report("%s: Not enough data available to load for Id: '%s'. "
    269                 "Available data size: %zu, Actual vmstate size: %u",
    270                 __func__, id, avail, len);
    271             return -1;
    272         }
    273 
    274         if (dbus_load_state_proxy(proxy,
    275                 g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
    276                                                     NULL),
    277                 len) < 0) {
    278             error_report("%s: Failed to restore Id '%s'", __func__, id);
    279             return -1;
    280         }
    281 
    282         if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
    283             goto error;
    284         }
    285 
    286         nelem -= 1;
    287     }
    288 
    289     return 0;
    290 
    291 error:
    292     error_report("%s: Failed to read from stream: %s", __func__, err->message);
    293     return -1;
    294 }
    295 
    296 static void
    297 dbus_save_state_proxy(gpointer key,
    298                       gpointer value,
    299                       gpointer user_data)
    300 {
    301     GDataOutputStream *s = user_data;
    302     const char *id = key;
    303     GDBusProxy *proxy = value;
    304     g_autoptr(GVariant) result = NULL;
    305     g_autoptr(GVariant) child = NULL;
    306     g_autoptr(GError) err = NULL;
    307     const uint8_t *data;
    308     gsize size;
    309 
    310     trace_dbus_vmstate_saving(id);
    311 
    312     result = g_dbus_proxy_call_sync(proxy, "Save",
    313                                     NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
    314                                     -1, NULL, &err);
    315     if (!result) {
    316         error_report("%s: Failed to Save: %s", __func__, err->message);
    317         return;
    318     }
    319 
    320     child = g_variant_get_child_value(result, 0);
    321     data = g_variant_get_fixed_array(child, &size, sizeof(char));
    322     if (!data) {
    323         error_report("%s: Failed to Save: not a byte array", __func__);
    324         return;
    325     }
    326     if (size > DBUS_VMSTATE_SIZE_LIMIT) {
    327         error_report("%s: Too large vmstate data to save: %zu",
    328                      __func__, (size_t)size);
    329         return;
    330     }
    331 
    332     if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
    333         !g_data_output_stream_put_string(s, id, NULL, &err) ||
    334         !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
    335         !g_output_stream_write_all(G_OUTPUT_STREAM(s),
    336                                    data, size, NULL, NULL, &err)) {
    337         error_report("%s: Failed to write to stream: %s",
    338                      __func__, err->message);
    339     }
    340 }
    341 
    342 static int dbus_vmstate_pre_save(void *opaque)
    343 {
    344     DBusVMState *self = DBUS_VMSTATE(opaque);
    345     g_autoptr(GOutputStream) m = NULL;
    346     g_autoptr(GDataOutputStream) s = NULL;
    347     g_autoptr(GHashTable) proxies = NULL;
    348     g_autoptr(GError) err = NULL;
    349 
    350     trace_dbus_vmstate_pre_save();
    351 
    352     proxies = dbus_get_proxies(self, &err);
    353     if (!proxies) {
    354         error_report("%s: Failed to get proxies: %s", __func__, err->message);
    355         return -1;
    356     }
    357 
    358     m = g_memory_output_stream_new_resizable();
    359     s = g_data_output_stream_new(m);
    360     g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
    361 
    362     if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
    363                                          NULL, &err)) {
    364         error_report("%s: Failed to write to stream: %s",
    365                      __func__, err->message);
    366         return -1;
    367     }
    368 
    369     g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
    370 
    371     if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
    372         > UINT32_MAX) {
    373         error_report("%s: DBus vmstate buffer is too large", __func__);
    374         return -1;
    375     }
    376 
    377     if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
    378         error_report("%s: Failed to close stream: %s", __func__, err->message);
    379         return -1;
    380     }
    381 
    382     g_free(self->data);
    383     self->data_size =
    384         g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
    385     self->data =
    386         g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
    387 
    388     return 0;
    389 }
    390 
    391 static const VMStateDescription dbus_vmstate = {
    392     .name = TYPE_DBUS_VMSTATE,
    393     .version_id = 0,
    394     .pre_save = dbus_vmstate_pre_save,
    395     .post_load = dbus_vmstate_post_load,
    396     .fields = (VMStateField[]) {
    397         VMSTATE_UINT32(data_size, DBusVMState),
    398         VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
    399         VMSTATE_END_OF_LIST()
    400     }
    401 };
    402 
    403 static void
    404 dbus_vmstate_complete(UserCreatable *uc, Error **errp)
    405 {
    406     DBusVMState *self = DBUS_VMSTATE(uc);
    407     g_autoptr(GError) err = NULL;
    408 
    409     if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
    410         error_setg(errp, "There is already an instance of %s",
    411                    TYPE_DBUS_VMSTATE);
    412         return;
    413     }
    414 
    415     if (!self->dbus_addr) {
    416         error_setg(errp, QERR_MISSING_PARAMETER, "addr");
    417         return;
    418     }
    419 
    420     self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
    421                     G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
    422                     G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
    423                     NULL, NULL, &err);
    424     if (err) {
    425         error_setg(errp, "failed to connect to DBus: '%s'", err->message);
    426         return;
    427     }
    428 
    429     if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
    430                          &dbus_vmstate, self) < 0) {
    431         error_setg(errp, "Failed to register vmstate");
    432     }
    433 }
    434 
    435 static void
    436 dbus_vmstate_finalize(Object *o)
    437 {
    438     DBusVMState *self = DBUS_VMSTATE(o);
    439 
    440     vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
    441 
    442     g_clear_object(&self->bus);
    443     g_free(self->dbus_addr);
    444     g_free(self->id_list);
    445     g_free(self->data);
    446 }
    447 
    448 static char *
    449 get_dbus_addr(Object *o, Error **errp)
    450 {
    451     DBusVMState *self = DBUS_VMSTATE(o);
    452 
    453     return g_strdup(self->dbus_addr);
    454 }
    455 
    456 static void
    457 set_dbus_addr(Object *o, const char *str, Error **errp)
    458 {
    459     DBusVMState *self = DBUS_VMSTATE(o);
    460 
    461     g_free(self->dbus_addr);
    462     self->dbus_addr = g_strdup(str);
    463 }
    464 
    465 static char *
    466 get_id_list(Object *o, Error **errp)
    467 {
    468     DBusVMState *self = DBUS_VMSTATE(o);
    469 
    470     return g_strdup(self->id_list);
    471 }
    472 
    473 static void
    474 set_id_list(Object *o, const char *str, Error **errp)
    475 {
    476     DBusVMState *self = DBUS_VMSTATE(o);
    477 
    478     g_free(self->id_list);
    479     self->id_list = g_strdup(str);
    480 }
    481 
    482 static char *
    483 dbus_vmstate_get_id(VMStateIf *vmif)
    484 {
    485     return g_strdup(TYPE_DBUS_VMSTATE);
    486 }
    487 
    488 static void
    489 dbus_vmstate_class_init(ObjectClass *oc, void *data)
    490 {
    491     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
    492     VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
    493 
    494     ucc->complete = dbus_vmstate_complete;
    495     vc->get_id = dbus_vmstate_get_id;
    496 
    497     object_class_property_add_str(oc, "addr",
    498                                   get_dbus_addr, set_dbus_addr);
    499     object_class_property_add_str(oc, "id-list",
    500                                   get_id_list, set_id_list);
    501 }
    502 
    503 static const TypeInfo dbus_vmstate_info = {
    504     .name = TYPE_DBUS_VMSTATE,
    505     .parent = TYPE_OBJECT,
    506     .instance_size = sizeof(DBusVMState),
    507     .instance_finalize = dbus_vmstate_finalize,
    508     .class_init = dbus_vmstate_class_init,
    509     .interfaces = (InterfaceInfo[]) {
    510         { TYPE_USER_CREATABLE },
    511         { TYPE_VMSTATE_IF },
    512         { }
    513     }
    514 };
    515 
    516 static void
    517 register_types(void)
    518 {
    519     type_register_static(&dbus_vmstate_info);
    520 }
    521 
    522 type_init(register_types);