qemu

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

test-yank.c (8016B)


      1 /*
      2  * Tests for QEMU yank feature
      3  *
      4  * Copyright (c) Lukas Straub <lukasstraub2@web.de>
      5  *
      6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
      7  * See the COPYING file in the top-level directory.
      8  */
      9 
     10 #include "qemu/osdep.h"
     11 #include <glib/gstdio.h>
     12 
     13 #include "qemu/config-file.h"
     14 #include "qemu/module.h"
     15 #include "qemu/option.h"
     16 #include "chardev/char-fe.h"
     17 #include "sysemu/sysemu.h"
     18 #include "qapi/error.h"
     19 #include "qapi/qapi-commands-char.h"
     20 #include "qapi/qapi-types-char.h"
     21 #include "qapi/qapi-commands-yank.h"
     22 #include "qapi/qapi-types-yank.h"
     23 #include "io/channel-socket.h"
     24 #include "socket-helpers.h"
     25 
     26 typedef struct {
     27     SocketAddress *addr;
     28     bool old_yank;
     29     bool new_yank;
     30     bool fail;
     31 } CharChangeTestConfig;
     32 
     33 static int chardev_change(void *opaque)
     34 {
     35     return 0;
     36 }
     37 
     38 static bool is_yank_instance_registered(void)
     39 {
     40     YankInstanceList *list;
     41     bool ret;
     42 
     43     list = qmp_query_yank(&error_abort);
     44 
     45     ret = !!list;
     46 
     47     qapi_free_YankInstanceList(list);
     48 
     49     return ret;
     50 }
     51 
     52 static gpointer accept_thread(gpointer data)
     53 {
     54     QIOChannelSocket *ioc = data;
     55     QIOChannelSocket *cioc;
     56 
     57     cioc = qio_channel_socket_accept(ioc, &error_abort);
     58     object_unref(OBJECT(cioc));
     59 
     60     return NULL;
     61 }
     62 
     63 static void char_change_test(gconstpointer opaque)
     64 {
     65     CharChangeTestConfig *conf = (gpointer) opaque;
     66     SocketAddress *addr;
     67     Chardev *chr;
     68     CharBackend be;
     69     ChardevReturn *ret;
     70     QIOChannelSocket *ioc;
     71     QemuThread thread;
     72 
     73     /*
     74      * Setup a listener socket and determine its address
     75      * so we know the TCP port for the client later
     76      */
     77     ioc = qio_channel_socket_new();
     78     g_assert_nonnull(ioc);
     79     qio_channel_socket_listen_sync(ioc, conf->addr, 1, &error_abort);
     80     addr = qio_channel_socket_get_local_address(ioc, &error_abort);
     81     g_assert_nonnull(addr);
     82 
     83     ChardevBackend backend[2] = {
     84         /* doesn't support yank */
     85         { .type = CHARDEV_BACKEND_KIND_NULL },
     86         /* supports yank */
     87         {
     88             .type = CHARDEV_BACKEND_KIND_SOCKET,
     89             .u.socket.data = &(ChardevSocket) {
     90                 .addr = &(SocketAddressLegacy) {
     91                     .type = SOCKET_ADDRESS_TYPE_INET,
     92                     .u.inet.data = &addr->u.inet
     93                 },
     94                 .has_server = true,
     95                 .server = false
     96             }
     97         } };
     98 
     99     ChardevBackend fail_backend[2] = {
    100         /* doesn't support yank */
    101         {
    102             .type = CHARDEV_BACKEND_KIND_UDP,
    103             .u.udp.data = &(ChardevUdp) {
    104                 .remote = &(SocketAddressLegacy) {
    105                     .type = SOCKET_ADDRESS_TYPE_UNIX,
    106                     .u.q_unix.data = &(UnixSocketAddress) {
    107                         .path = (char *)""
    108                     }
    109                 }
    110             }
    111         },
    112         /* supports yank */
    113         {
    114             .type = CHARDEV_BACKEND_KIND_SOCKET,
    115             .u.socket.data = &(ChardevSocket) {
    116                 .addr = &(SocketAddressLegacy) {
    117                     .type = SOCKET_ADDRESS_TYPE_INET,
    118                     .u.inet.data = &(InetSocketAddress) {
    119                         .host = (char *)"127.0.0.1",
    120                         .port = (char *)"0"
    121                     }
    122                 },
    123                 .has_server = true,
    124                 .server = false
    125             }
    126         } };
    127 
    128     g_assert(!is_yank_instance_registered());
    129 
    130     if (conf->old_yank) {
    131         qemu_thread_create(&thread, "accept", accept_thread,
    132                            ioc, QEMU_THREAD_JOINABLE);
    133     }
    134 
    135     ret = qmp_chardev_add("chardev", &backend[conf->old_yank], &error_abort);
    136     qapi_free_ChardevReturn(ret);
    137     chr = qemu_chr_find("chardev");
    138     g_assert_nonnull(chr);
    139 
    140     g_assert(is_yank_instance_registered() == conf->old_yank);
    141 
    142     qemu_chr_wait_connected(chr, &error_abort);
    143     if (conf->old_yank) {
    144         qemu_thread_join(&thread);
    145     }
    146 
    147     qemu_chr_fe_init(&be, chr, &error_abort);
    148     /* allow chardev-change */
    149     qemu_chr_fe_set_handlers(&be, NULL, NULL,
    150                              NULL, chardev_change, NULL, NULL, true);
    151 
    152     if (conf->fail) {
    153         g_setenv("QTEST_SILENT_ERRORS", "1", 1);
    154         ret = qmp_chardev_change("chardev", &fail_backend[conf->new_yank],
    155                                  NULL);
    156         g_assert_null(ret);
    157         g_assert(be.chr == chr);
    158         g_assert(is_yank_instance_registered() == conf->old_yank);
    159         g_unsetenv("QTEST_SILENT_ERRORS");
    160     } else {
    161         if (conf->new_yank) {
    162                 qemu_thread_create(&thread, "accept", accept_thread,
    163                                    ioc, QEMU_THREAD_JOINABLE);
    164         }
    165         ret = qmp_chardev_change("chardev", &backend[conf->new_yank],
    166                                  &error_abort);
    167         if (conf->new_yank) {
    168             qemu_thread_join(&thread);
    169         }
    170         g_assert_nonnull(ret);
    171         g_assert(be.chr != chr);
    172         g_assert(is_yank_instance_registered() == conf->new_yank);
    173     }
    174 
    175     object_unparent(OBJECT(be.chr));
    176     object_unref(OBJECT(ioc));
    177     qapi_free_ChardevReturn(ret);
    178     qapi_free_SocketAddress(addr);
    179 }
    180 
    181 static SocketAddress tcpaddr = {
    182     .type = SOCKET_ADDRESS_TYPE_INET,
    183     .u.inet.host = (char *)"127.0.0.1",
    184     .u.inet.port = (char *)"0",
    185 };
    186 
    187 int main(int argc, char **argv)
    188 {
    189     bool has_ipv4, has_ipv6;
    190 
    191     qemu_init_main_loop(&error_abort);
    192     socket_init();
    193 
    194     g_test_init(&argc, &argv, NULL);
    195 
    196     if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
    197         g_printerr("socket_check_protocol_support() failed\n");
    198         goto end;
    199     }
    200 
    201     if (!has_ipv4) {
    202         goto end;
    203     }
    204 
    205     module_call_init(MODULE_INIT_QOM);
    206     qemu_add_opts(&qemu_chardev_opts);
    207 
    208     g_test_add_data_func("/yank/char_change/success/to_yank",
    209                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    210                                                    .old_yank = false,
    211                                                    .new_yank = true,
    212                                                    .fail = false },
    213                          char_change_test);
    214     g_test_add_data_func("/yank/char_change/fail/to_yank",
    215                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    216                                                    .old_yank = false,
    217                                                    .new_yank = true,
    218                                                    .fail = true },
    219                          char_change_test);
    220 
    221     g_test_add_data_func("/yank/char_change/success/yank_to_yank",
    222                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    223                                                    .old_yank = true,
    224                                                    .new_yank = true,
    225                                                    .fail = false },
    226                          char_change_test);
    227     g_test_add_data_func("/yank/char_change/fail/yank_to_yank",
    228                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    229                                                    .old_yank = true,
    230                                                    .new_yank = true,
    231                                                    .fail = true },
    232                          char_change_test);
    233 
    234     g_test_add_data_func("/yank/char_change/success/from_yank",
    235                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    236                                                    .old_yank = true,
    237                                                    .new_yank = false,
    238                                                    .fail = false },
    239                          char_change_test);
    240     g_test_add_data_func("/yank/char_change/fail/from_yank",
    241                          &(CharChangeTestConfig) { .addr = &tcpaddr,
    242                                                    .old_yank = true,
    243                                                    .new_yank = false,
    244                                                    .fail = true },
    245                          char_change_test);
    246 
    247 end:
    248     return g_test_run();
    249 }