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