xref: /aosp_15_r20/external/virglrenderer/vtest/vtest_server.c (revision bbecb9d118dfdb95f99bd754f8fa9be01f189df3)
1 /**************************************************************************
2  *
3  * Copyright (C) 2015 Red Hat Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  **************************************************************************/
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <stdio.h>
30 #include <signal.h>
31 #include <stdbool.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <sys/un.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <string.h>
41 
42 #include "util.h"
43 #include "util/u_double_list.h"
44 #include "util/u_math.h"
45 #include "util/u_memory.h"
46 #include "vtest.h"
47 #include "vtest_protocol.h"
48 #include "virglrenderer.h"
49 #ifdef HAVE_SYS_SELECT_H
50 #include <sys/select.h>
51 #endif
52 
53 enum vtest_client_error {
54    VTEST_CLIENT_ERROR_INPUT_READ = 2, /* for backward compatibility */
55    VTEST_CLIENT_ERROR_CONTEXT_MISSING,
56    VTEST_CLIENT_ERROR_CONTEXT_FAILED,
57    VTEST_CLIENT_ERROR_COMMAND_ID,
58    VTEST_CLIENT_ERROR_COMMAND_UNEXPECTED,
59    VTEST_CLIENT_ERROR_COMMAND_DISPATCH,
60 };
61 
62 struct vtest_client
63 {
64    int in_fd;
65    int out_fd;
66    struct vtest_input input;
67 
68    struct list_head head;
69 
70    bool in_fd_ready;
71    struct vtest_context *context;
72    int context_poll_fd;
73    bool context_need_poll;
74 };
75 
76 struct vtest_server
77 {
78    const char *socket_name;
79    int socket;
80    const char *read_file;
81 
82    const char *render_device;
83 
84    bool main_server;
85    bool do_fork;
86    bool loop;
87    bool multi_clients;
88 
89    bool use_glx;
90    bool use_egl_surfaceless;
91    bool use_gles;
92 
93    bool venus;
94    bool render_server;
95 
96    int ctx_flags;
97 
98    struct list_head new_clients;
99    struct list_head active_clients;
100    struct list_head inactive_clients;
101 };
102 
103 struct vtest_server server = {
104    .socket_name = VTEST_DEFAULT_SOCKET_NAME,
105    .socket = -1,
106 
107    .read_file = NULL,
108 
109    .render_device = 0,
110 
111    .main_server = true,
112    .do_fork = true,
113    .loop = true,
114    .multi_clients = false,
115 
116    .ctx_flags = 0,
117 };
118 
119 static void vtest_server_getenv(void);
120 static void vtest_server_parse_args(int argc, char **argv);
121 static void vtest_server_set_signal_child(void);
122 static void vtest_server_set_signal_segv(void);
123 static void vtest_server_open_read_file(void);
124 static void vtest_server_open_socket(void);
125 static void vtest_server_run(void);
126 static void vtest_server_close_socket(void);
127 static int vtest_client_dispatch_commands(struct vtest_client *client);
128 
129 
main(int argc,char ** argv)130 int main(int argc, char **argv)
131 {
132 #ifdef __AFL_LOOP
133 while (__AFL_LOOP(1000)) {
134 #endif
135 
136    vtest_server_getenv();
137    vtest_server_parse_args(argc, argv);
138 
139    list_inithead(&server.new_clients);
140    list_inithead(&server.active_clients);
141    list_inithead(&server.inactive_clients);
142 
143    if (server.do_fork) {
144       vtest_server_set_signal_child();
145    } else {
146       vtest_server_set_signal_segv();
147    }
148 
149    vtest_server_run();
150 
151 #ifdef __AFL_LOOP
152    if (!server.main_server) {
153       exit(0);
154    }
155 }
156 #endif
157 }
158 
159 #define OPT_NO_FORK 'f'
160 #define OPT_NO_LOOP_OR_FORK 'l'
161 #define OPT_MULTI_CLIENTS 'm'
162 #define OPT_USE_GLX 'x'
163 #define OPT_USE_EGL_SURFACELESS 's'
164 #define OPT_USE_GLES 'e'
165 #define OPT_RENDERNODE 'r'
166 #define OPT_VENUS 'v'
167 #define OPT_RENDER_SERVER 'n'
168 #define OPT_SOCKET_PATH 'p'
169 
vtest_server_parse_args(int argc,char ** argv)170 static void vtest_server_parse_args(int argc, char **argv)
171 {
172    int ret;
173 
174    static struct option long_options[] = {
175       {"no-fork",             no_argument, NULL, OPT_NO_FORK},
176       {"no-loop-or-fork",     no_argument, NULL, OPT_NO_LOOP_OR_FORK},
177       {"multi-clients",       no_argument, NULL, OPT_MULTI_CLIENTS},
178       {"use-glx",             no_argument, NULL, OPT_USE_GLX},
179       {"use-egl-surfaceless", no_argument, NULL, OPT_USE_EGL_SURFACELESS},
180       {"use-gles",            no_argument, NULL, OPT_USE_GLES},
181       {"rendernode",          required_argument, NULL, OPT_RENDERNODE},
182       {"venus",               no_argument, NULL, OPT_VENUS},
183       {"render-server",       no_argument, NULL, OPT_RENDER_SERVER},
184       {"socket-path",         optional_argument, NULL, OPT_SOCKET_PATH},
185       {0, 0, 0, 0}
186    };
187 
188    /* getopt_long stores the option index here. */
189    int option_index = 0;
190 
191    do {
192       ret = getopt_long(argc, argv, "", long_options, &option_index);
193 
194       switch (ret) {
195       case -1:
196          break;
197       case OPT_NO_FORK:
198          server.do_fork = false;
199          break;
200       case OPT_NO_LOOP_OR_FORK:
201          server.do_fork = false;
202          server.loop = false;
203          break;
204       case OPT_MULTI_CLIENTS:
205          printf("multi-clients enabled: clients must trust each other\n");
206          server.multi_clients = true;
207          break;
208       case OPT_USE_GLX:
209          server.use_glx = true;
210          break;
211       case OPT_USE_EGL_SURFACELESS:
212          server.use_egl_surfaceless = true;
213          break;
214       case OPT_USE_GLES:
215          server.use_gles = true;
216          break;
217       case OPT_RENDERNODE:
218          server.render_device = optarg;
219          break;
220 #ifdef ENABLE_VENUS
221       case OPT_VENUS:
222          server.venus = true;
223          break;
224 #endif
225 #ifdef ENABLE_RENDER_SERVER
226       case OPT_RENDER_SERVER:
227          server.render_server = true;
228          break;
229 #endif
230       case OPT_SOCKET_PATH:
231          server.socket_name = optarg;
232          break;
233       default:
234          printf("Usage: %s [--no-fork] [--no-loop-or-fork] [--multi-clients] "
235                 "[--use-glx] [--use-egl-surfaceless] [--use-gles] "
236                 "[--rendernode <dev>] [--socket-path <path>] "
237 #ifdef ENABLE_VENUS
238                 " [--venus]"
239 #endif
240 #ifdef ENABLE_RENDER_SERVER
241                 " [--render-server]"
242 #endif
243                 " [file]\n", argv[0]);
244          exit(EXIT_FAILURE);
245          break;
246       }
247 
248    } while (ret >= 0);
249 
250    if (optind < argc) {
251       server.read_file = argv[optind];
252       server.loop = false;
253       server.do_fork = false;
254       server.multi_clients = false;
255    }
256 
257    server.ctx_flags = VIRGL_RENDERER_USE_EGL;
258    if (server.use_glx) {
259       if (server.use_egl_surfaceless || server.use_gles) {
260          fprintf(stderr, "Cannot use surfaceless or GLES with GLX.\n");
261          exit(EXIT_FAILURE);
262       }
263       server.ctx_flags = VIRGL_RENDERER_USE_GLX;
264    } else {
265       if (server.use_egl_surfaceless)
266          server.ctx_flags |= VIRGL_RENDERER_USE_SURFACELESS;
267       if (server.use_gles)
268          server.ctx_flags |= VIRGL_RENDERER_USE_GLES;
269    }
270 
271    if (server.venus) {
272       server.ctx_flags |= VIRGL_RENDERER_VENUS;
273    }
274    if (server.render_server) {
275       server.ctx_flags |= VIRGL_RENDERER_RENDER_SERVER;
276    }
277 }
278 
vtest_server_getenv(void)279 static void vtest_server_getenv(void)
280 {
281    server.use_glx = getenv("VTEST_USE_GLX") != NULL;
282    server.use_egl_surfaceless = getenv("VTEST_USE_EGL_SURFACELESS") != NULL;
283    server.use_gles = getenv("VTEST_USE_GLES") != NULL;
284    server.render_device = getenv("VTEST_RENDERNODE");
285 }
286 
handler(int sig,siginfo_t * si,void * unused)287 static void handler(int sig, siginfo_t *si, void *unused)
288 {
289    (void)sig; (void)si, (void)unused;
290 
291    printf("SIGSEGV!\n");
292    exit(EXIT_FAILURE);
293 }
294 
vtest_server_set_signal_child(void)295 static void vtest_server_set_signal_child(void)
296 {
297    struct sigaction sa;
298    int ret;
299 
300    memset(&sa, 0, sizeof(sa));
301    sigemptyset(&sa.sa_mask);
302    sa.sa_handler = SIG_IGN;
303    sa.sa_flags = 0;
304 
305    ret = sigaction(SIGCHLD, &sa, NULL);
306    if (ret == -1) {
307       perror("Failed to set SIGCHLD");
308       exit(1);
309    }
310 }
311 
vtest_server_set_signal_segv(void)312 static void vtest_server_set_signal_segv(void)
313 {
314    struct sigaction sa;
315    int ret;
316 
317    memset(&sa, 0, sizeof(sa));
318    sigemptyset(&sa.sa_mask);
319    sa.sa_flags = SA_SIGINFO;
320    sa.sa_sigaction = handler;
321 
322    ret = sigaction(SIGSEGV, &sa, NULL);
323    if (ret == -1) {
324       perror("Failed to set SIGSEGV");
325       exit(1);
326    }
327 }
328 
vtest_server_add_client(int in_fd,int out_fd)329 static int vtest_server_add_client(int in_fd, int out_fd)
330 {
331    struct vtest_client *client;
332 
333    client = calloc(1, sizeof(*client));
334    if (!client)
335       return -1;
336 
337    client->in_fd = in_fd;
338    client->out_fd = out_fd;
339 
340    client->input.data.fd = in_fd;
341    client->input.read = vtest_block_read;
342 
343    client->context_poll_fd = -1;
344 
345    list_addtail(&client->head, &server.new_clients);
346 
347    return 0;
348 }
349 
vtest_server_open_read_file(void)350 static void vtest_server_open_read_file(void)
351 {
352    int in_fd;
353    int out_fd;
354 
355    in_fd = open(server.read_file, O_RDONLY);
356    if (in_fd == -1) {
357       perror(NULL);
358       exit(1);
359    }
360 
361    out_fd = open("/dev/null", O_WRONLY);
362    if (out_fd == -1) {
363       perror(NULL);
364       exit(1);
365    }
366 
367    if (vtest_server_add_client(in_fd, out_fd)) {
368       perror(NULL);
369       exit(1);
370    }
371 }
372 
vtest_server_open_socket(void)373 static void vtest_server_open_socket(void)
374 {
375    struct sockaddr_un un;
376 
377    server.socket = socket(PF_UNIX, SOCK_STREAM, 0);
378    if (server.socket < 0) {
379       goto err;
380    }
381 
382    memset(&un, 0, sizeof(un));
383    un.sun_family = AF_UNIX;
384 
385    snprintf(un.sun_path, sizeof(un.sun_path), "%s", server.socket_name);
386 
387    unlink(un.sun_path);
388 
389    if (bind(server.socket, (struct sockaddr *)&un, sizeof(un)) < 0) {
390       goto err;
391    }
392 
393    if (listen(server.socket, 1) < 0){
394       goto err;
395    }
396 
397    return;
398 
399 err:
400    perror("Failed to setup socket.");
401    exit(1);
402 }
403 
vtest_server_wait_clients(void)404 static void vtest_server_wait_clients(void)
405 {
406    struct vtest_client *client;
407    fd_set read_fds;
408    int max_fd = -1;
409    int ret;
410 
411    FD_ZERO(&read_fds);
412 
413    LIST_FOR_EACH_ENTRY(client, &server.active_clients, head) {
414       FD_SET(client->in_fd, &read_fds);
415       max_fd = MAX2(client->in_fd, max_fd);
416 
417       if (client->context_poll_fd >= 0) {
418          FD_SET(client->context_poll_fd, &read_fds);
419          max_fd = MAX2(client->context_poll_fd, max_fd);
420       }
421    }
422 
423    /* accept new clients when there is none or when multi_clients is set */
424    if (server.socket >= 0 && (max_fd < 0 || server.multi_clients)) {
425       FD_SET(server.socket, &read_fds);
426       max_fd = MAX2(server.socket, max_fd);
427    }
428 
429    if (max_fd < 0) {
430       if (!LIST_IS_EMPTY(&server.new_clients)) {
431          return;
432       }
433 
434       fprintf(stderr, "server has no fd to wait\n");
435       exit(1);
436    }
437 
438    ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
439    if (ret < 0) {
440       perror("Failed to select on socket!");
441       exit(1);
442    }
443 
444    LIST_FOR_EACH_ENTRY(client, &server.active_clients, head) {
445       if (FD_ISSET(client->in_fd, &read_fds)) {
446          client->in_fd_ready = true;
447       }
448 
449       if (client->context_poll_fd >= 0) {
450          if (FD_ISSET(client->context_poll_fd, &read_fds)) {
451             client->context_need_poll = true;
452          }
453       } else if (client->context) {
454          client->context_need_poll = true;
455       }
456    }
457 
458    if (server.socket >= 0 && FD_ISSET(server.socket, &read_fds)) {
459       int new_fd = accept(server.socket, NULL, NULL);
460       if (new_fd < 0) {
461          perror("Failed to accept socket.");
462          exit(1);
463       }
464 
465       if (vtest_server_add_client(new_fd, new_fd)) {
466          perror("Failed to add client.");
467          exit(1);
468       }
469    }
470 }
471 
vtest_client_error_string(enum vtest_client_error err)472 static const char *vtest_client_error_string(enum vtest_client_error err)
473 {
474    switch (err) {
475 #define CASE(e) case e: return #e;
476    CASE(VTEST_CLIENT_ERROR_INPUT_READ)
477    CASE(VTEST_CLIENT_ERROR_CONTEXT_MISSING)
478    CASE(VTEST_CLIENT_ERROR_CONTEXT_FAILED)
479    CASE(VTEST_CLIENT_ERROR_COMMAND_ID)
480    CASE(VTEST_CLIENT_ERROR_COMMAND_UNEXPECTED)
481    CASE(VTEST_CLIENT_ERROR_COMMAND_DISPATCH)
482 #undef CASE
483    default: return "VTEST_CLIENT_ERROR_UNKNOWN";
484    }
485 }
486 
vtest_server_dispatch_clients(void)487 static void vtest_server_dispatch_clients(void)
488 {
489    struct vtest_client *client, *tmp;
490 
491    LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.active_clients, head) {
492       int err;
493 
494       if (client->context_need_poll) {
495          vtest_poll_context(client->context);
496          client->context_need_poll = false;
497       }
498 
499       if (!client->in_fd_ready)
500          continue;
501       client->in_fd_ready = false;
502 
503       err = vtest_client_dispatch_commands(client);
504       if (err) {
505          fprintf(stderr, "client failed: %s\n",
506                  vtest_client_error_string(err));
507          list_del(&client->head);
508          list_addtail(&client->head, &server.inactive_clients);
509       }
510    }
511 }
512 
vtest_server_fork(void)513 static pid_t vtest_server_fork(void)
514 {
515    pid_t pid = fork();
516 
517    if (pid == 0) {
518       /* child */
519       vtest_server_set_signal_segv();
520       vtest_server_close_socket();
521       server.main_server = false;
522       server.do_fork = false;
523       server.loop = false;
524       server.multi_clients = false;
525    }
526 
527    return pid;
528 }
529 
vtest_server_fork_clients(void)530 static void vtest_server_fork_clients(void)
531 {
532    struct vtest_client *client, *tmp;
533 
534    LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
535       if (vtest_server_fork()) {
536          /* parent: move new clients to the inactive list */
537          list_del(&client->head);
538          list_addtail(&client->head, &server.inactive_clients);
539       } else {
540          /* child: move the first new client to the active list */
541          list_del(&client->head);
542          list_addtail(&client->head, &server.active_clients);
543 
544          /* move the rest new clients to the inactive list */
545          LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
546             list_del(&client->head);
547             list_addtail(&client->head, &server.inactive_clients);
548          }
549       }
550    }
551 }
552 
vtest_server_activate_clients(void)553 static void vtest_server_activate_clients(void)
554 {
555    struct vtest_client *client, *tmp;
556 
557    /* move new clients to the active list */
558    LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.new_clients, head) {
559       list_addtail(&client->head, &server.active_clients);
560    }
561    list_inithead(&server.new_clients);
562 }
563 
vtest_server_inactivate_clients(void)564 static void vtest_server_inactivate_clients(void)
565 {
566    struct vtest_client *client, *tmp;
567 
568    /* move active clients to the inactive list */
569    LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.active_clients, head) {
570       list_addtail(&client->head, &server.inactive_clients);
571    }
572    list_inithead(&server.active_clients);
573 }
574 
vtest_server_tidy_clients(void)575 static void vtest_server_tidy_clients(void)
576 {
577    struct vtest_client *client, *tmp;
578 
579    LIST_FOR_EACH_ENTRY_SAFE(client, tmp, &server.inactive_clients, head) {
580       if (client->context) {
581          vtest_destroy_context(client->context);
582       }
583 
584       if (client->in_fd >= 0) {
585          close(client->in_fd);
586       }
587 
588       if (client->out_fd >= 0 && client->out_fd != client->in_fd) {
589          close(client->out_fd);
590       }
591 
592       free(client);
593    }
594 
595    list_inithead(&server.inactive_clients);
596 }
597 
vtest_server_run(void)598 static void vtest_server_run(void)
599 {
600    bool run = true;
601 
602    if (server.read_file) {
603       vtest_server_open_read_file();
604    } else {
605       vtest_server_open_socket();
606    }
607 
608    while (run) {
609       const bool was_empty = LIST_IS_EMPTY(&server.active_clients);
610       bool is_empty;
611 
612       vtest_server_wait_clients();
613       vtest_server_dispatch_clients();
614 
615       if (server.do_fork) {
616          vtest_server_fork_clients();
617       } else {
618          vtest_server_activate_clients();
619       }
620 
621       /* init renderer after the first active client is added */
622       is_empty = LIST_IS_EMPTY(&server.active_clients);
623       if (was_empty && !is_empty) {
624          int ret = vtest_init_renderer(server.multi_clients,
625                                        server.ctx_flags,
626                                        server.render_device);
627          if (ret) {
628             vtest_server_inactivate_clients();
629             run = false;
630          }
631       }
632 
633       vtest_server_tidy_clients();
634 
635       /* clean up renderer after the last active client is removed */
636       if (!was_empty && is_empty) {
637          vtest_cleanup_renderer();
638          if (!server.loop) {
639             run = false;
640          }
641       }
642    }
643 
644    vtest_server_close_socket();
645 }
646 
647 static const struct vtest_command {
648    int (*dispatch)(uint32_t);
649    bool init_context;
650 } vtest_commands[] = {
651    /* CMD ids starts at 1 */
652    [0]                          = { NULL,                        false },
653    [VCMD_GET_CAPS]              = { vtest_send_caps,             false },
654    [VCMD_RESOURCE_CREATE]       = { vtest_create_resource,       true  },
655    [VCMD_RESOURCE_UNREF]        = { vtest_resource_unref,        true  },
656    [VCMD_TRANSFER_GET]          = { vtest_transfer_get,          true  },
657    [VCMD_TRANSFER_PUT]          = { vtest_transfer_put,          true  },
658    [VCMD_SUBMIT_CMD]            = { vtest_submit_cmd,            true  },
659    [VCMD_RESOURCE_BUSY_WAIT]    = { vtest_resource_busy_wait,    false },
660    /* VCMD_CREATE_RENDERER is a special case */
661    [VCMD_CREATE_RENDERER]       = { NULL,                        false },
662    [VCMD_GET_CAPS2]             = { vtest_send_caps2,            false },
663    [VCMD_PING_PROTOCOL_VERSION] = { vtest_ping_protocol_version, false },
664    [VCMD_PROTOCOL_VERSION]      = { vtest_protocol_version,      false },
665 
666    /* since protocol version 2 */
667    [VCMD_RESOURCE_CREATE2]      = { vtest_create_resource2,      true  },
668    [VCMD_TRANSFER_GET2]         = { vtest_transfer_get2,         true  },
669    [VCMD_TRANSFER_PUT2]         = { vtest_transfer_put2,         true  },
670 
671    /* since protocol version 3 */
672    [VCMD_GET_PARAM]             = { vtest_get_param,             false },
673    [VCMD_GET_CAPSET]            = { vtest_get_capset,            false },
674    [VCMD_CONTEXT_INIT]          = { vtest_context_init,          false },
675    [VCMD_RESOURCE_CREATE_BLOB]  = { vtest_resource_create_blob,  true  },
676    [VCMD_SYNC_CREATE]           = { vtest_sync_create,           true },
677    [VCMD_SYNC_UNREF]            = { vtest_sync_unref,            true },
678    [VCMD_SYNC_READ]             = { vtest_sync_read,             true },
679    [VCMD_SYNC_WRITE]            = { vtest_sync_write,            true },
680    [VCMD_SYNC_WAIT]             = { vtest_sync_wait,             true },
681    [VCMD_SUBMIT_CMD2]           = { vtest_submit_cmd2,           true },
682 };
683 
vtest_client_dispatch_commands(struct vtest_client * client)684 static int vtest_client_dispatch_commands(struct vtest_client *client)
685 {
686    const struct vtest_command *cmd;
687    int ret;
688    uint32_t header[VTEST_HDR_SIZE];
689 
690    ret = client->input.read(&client->input, &header, sizeof(header));
691    if (ret < 0 || (size_t)ret < sizeof(header)) {
692       return VTEST_CLIENT_ERROR_INPUT_READ;
693    }
694 
695    if (!client->context) {
696       /* The first command MUST be VCMD_CREATE_RENDERER */
697       if (header[1] != VCMD_CREATE_RENDERER) {
698          return VTEST_CLIENT_ERROR_CONTEXT_MISSING;
699       }
700 
701       ret = vtest_create_context(&client->input, client->out_fd,
702                                  header[0], &client->context);
703       if (ret < 0) {
704          return VTEST_CLIENT_ERROR_CONTEXT_FAILED;
705       }
706       printf("%s: client context created.\n", __func__);
707       vtest_poll_resource_busy_wait();
708 
709       return 0;
710    }
711 
712    vtest_poll_resource_busy_wait();
713    if (header[1] <= 0 || header[1] >= ARRAY_SIZE(vtest_commands)) {
714       return VTEST_CLIENT_ERROR_COMMAND_ID;
715    }
716 
717    cmd = &vtest_commands[header[1]];
718    if (cmd->dispatch == NULL) {
719       return VTEST_CLIENT_ERROR_COMMAND_UNEXPECTED;
720    }
721 
722    /* we should consider per-context dispatch table to get rid of if's */
723    if (cmd->init_context) {
724       ret = vtest_lazy_init_context(client->context);
725       if (ret) {
726          return VTEST_CLIENT_ERROR_CONTEXT_FAILED;
727       }
728       client->context_poll_fd = vtest_get_context_poll_fd(client->context);
729    }
730 
731    vtest_set_current_context(client->context);
732 
733    ret = cmd->dispatch(header[0]);
734    if (ret < 0) {
735       return VTEST_CLIENT_ERROR_COMMAND_DISPATCH;
736    }
737 
738    return 0;
739 }
740 
vtest_server_close_socket(void)741 static void vtest_server_close_socket(void)
742 {
743    if (server.socket != -1) {
744       close(server.socket);
745       server.socket = -1;
746    }
747 }
748