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