1 | /* |
2 | * Copyright © 2012 Intel Corporation |
3 | * Copyright © 2013 Jason Ekstrand |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sublicense, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the |
14 | * next paragraph) shall be included in all copies or substantial |
15 | * portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | * SOFTWARE. |
25 | */ |
26 | |
27 | #include <stdbool.h> |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <stdarg.h> |
31 | #include <stdint.h> |
32 | #include <string.h> |
33 | #include <assert.h> |
34 | #include <sys/socket.h> |
35 | #include <unistd.h> |
36 | #include <errno.h> |
37 | #include <sys/types.h> |
38 | #include <sys/stat.h> |
39 | #include <sys/mman.h> |
40 | |
41 | #include <pthread.h> |
42 | #include <poll.h> |
43 | |
44 | #include "wayland-private.h" |
45 | #include "wayland-server.h" |
46 | #include "wayland-client.h" |
47 | #include "test-runner.h" |
48 | #include "test-compositor.h" |
49 | |
50 | #include "tests-server-protocol.h" |
51 | #include "tests-client-protocol.h" |
52 | |
53 | struct display_destroy_listener { |
54 | struct wl_listener listener; |
55 | int done; |
56 | }; |
57 | |
58 | static void |
59 | display_destroy_notify(struct wl_listener *l, void *data) |
60 | { |
61 | struct display_destroy_listener *listener; |
62 | |
63 | listener = wl_container_of(l, listener, listener); |
64 | listener->done = 1; |
65 | } |
66 | |
67 | TEST(display_destroy_listener) |
68 | { |
69 | struct wl_display *display; |
70 | struct display_destroy_listener a, b; |
71 | |
72 | display = wl_display_create(); |
73 | assert(display); |
74 | |
75 | a.listener.notify = &display_destroy_notify; |
76 | a.done = 0; |
77 | wl_display_add_destroy_listener(display, listener: &a.listener); |
78 | |
79 | assert(wl_display_get_destroy_listener(display, display_destroy_notify) == |
80 | &a.listener); |
81 | |
82 | b.listener.notify = display_destroy_notify; |
83 | b.done = 0; |
84 | wl_display_add_destroy_listener(display, listener: &b.listener); |
85 | |
86 | wl_list_remove(elm: &a.listener.link); |
87 | |
88 | wl_display_destroy(display); |
89 | |
90 | assert(!a.done); |
91 | assert(b.done); |
92 | } |
93 | |
94 | /* Fake 'client' which does not use wl_display_connect, and thus leaves the |
95 | * file descriptor passed through WAYLAND_SOCKET intact. This should not |
96 | * trigger an assertion in the leak check. */ |
97 | static void |
98 | empty_client(void) |
99 | { |
100 | return; |
101 | } |
102 | |
103 | TEST(tc_leaks_tests) |
104 | { |
105 | struct display *d = display_create(); |
106 | client_create_noarg(d, empty_client); |
107 | display_run(d); |
108 | display_destroy(d); |
109 | } |
110 | |
111 | /* This is how pre proxy-version registry binds worked, |
112 | * this should create a proxy that shares the display's |
113 | * version number: 0 */ |
114 | static void * |
115 | old_registry_bind(struct wl_registry *wl_registry, |
116 | uint32_t name, |
117 | const struct wl_interface *interface, |
118 | uint32_t version) |
119 | { |
120 | struct wl_proxy *id; |
121 | |
122 | id = wl_proxy_marshal_constructor( |
123 | proxy: (struct wl_proxy *) wl_registry, WL_REGISTRY_BIND, |
124 | interface, name, interface->name, version, NULL); |
125 | |
126 | return (void *) id; |
127 | } |
128 | |
129 | struct handler_info { |
130 | struct wl_seat *seat; |
131 | uint32_t bind_version; |
132 | bool use_unversioned; |
133 | }; |
134 | |
135 | static void |
136 | registry_handle_globals(void *data, struct wl_registry *registry, |
137 | uint32_t id, const char *intf, uint32_t ver) |
138 | { |
139 | struct handler_info *hi = data; |
140 | |
141 | /* This is only for the proxy version test */ |
142 | if (hi->bind_version) |
143 | ver = hi->bind_version; |
144 | |
145 | if (strcmp(s1: intf, s2: "wl_seat" ) == 0) { |
146 | if (hi->use_unversioned) |
147 | hi->seat = old_registry_bind(wl_registry: registry, name: id, |
148 | interface: &wl_seat_interface, version: ver); |
149 | else |
150 | hi->seat = wl_registry_bind(wl_registry: registry, name: id, |
151 | interface: &wl_seat_interface, version: ver); |
152 | assert(hi->seat); |
153 | } |
154 | } |
155 | |
156 | static const struct wl_registry_listener registry_listener = { |
157 | registry_handle_globals, |
158 | NULL |
159 | }; |
160 | |
161 | static struct wl_seat * |
162 | client_get_seat_with_info(struct client *c, struct handler_info *hi) |
163 | { |
164 | struct wl_registry *reg = wl_display_get_registry(wl_display: c->wl_display); |
165 | assert(reg); |
166 | |
167 | assert(hi); |
168 | hi->seat = NULL; |
169 | wl_registry_add_listener(wl_registry: reg, listener: ®istry_listener, data: hi); |
170 | wl_display_roundtrip(display: c->wl_display); |
171 | assert(hi->seat); |
172 | |
173 | wl_registry_destroy(wl_registry: reg); |
174 | |
175 | return hi->seat; |
176 | } |
177 | |
178 | static struct wl_seat * |
179 | client_get_seat(struct client *c) |
180 | { |
181 | struct handler_info hi; |
182 | |
183 | hi.use_unversioned = false; |
184 | hi.bind_version = 0; |
185 | |
186 | return client_get_seat_with_info(c, hi: &hi); |
187 | } |
188 | |
189 | static void |
190 | check_pending_error(struct client *c, struct wl_proxy *proxy) |
191 | { |
192 | uint32_t ec, id; |
193 | int err; |
194 | const struct wl_interface *intf; |
195 | |
196 | err = wl_display_get_error(display: c->wl_display); |
197 | assert(err == EPROTO); |
198 | |
199 | ec = wl_display_get_protocol_error(display: c->wl_display, interface: &intf, id: &id); |
200 | assert(ec == 23); |
201 | assert(intf == &wl_seat_interface); |
202 | assert(id == wl_proxy_get_id(proxy)); |
203 | } |
204 | |
205 | static void |
206 | check_for_error(struct client *c, struct wl_proxy *proxy) |
207 | { |
208 | /* client should be disconnected */ |
209 | assert(wl_display_roundtrip(c->wl_display) == -1); |
210 | |
211 | check_pending_error(c, proxy); |
212 | } |
213 | |
214 | static struct client_info * |
215 | find_client_info(struct display *d, struct wl_client *client) |
216 | { |
217 | struct client_info *ci; |
218 | |
219 | wl_list_for_each(ci, &d->clients, link) { |
220 | if (ci->wl_client == client) |
221 | return ci; |
222 | } |
223 | |
224 | return NULL; |
225 | } |
226 | |
227 | static void |
228 | bind_seat(struct wl_client *client, void *data, |
229 | uint32_t vers, uint32_t id) |
230 | { |
231 | struct display *d = data; |
232 | struct client_info *ci; |
233 | struct wl_resource *res; |
234 | |
235 | ci = find_client_info(d, client); |
236 | assert(ci); |
237 | |
238 | res = wl_resource_create(client, interface: &wl_seat_interface, version: vers, id); |
239 | assert(res); |
240 | |
241 | /* save the resource as client's info data, |
242 | * so that we can use it later */ |
243 | ci->data = res; |
244 | } |
245 | |
246 | static void |
247 | client_disconnect_nocheck(struct client *c) |
248 | { |
249 | wl_proxy_destroy(proxy: (struct wl_proxy *) c->tc); |
250 | wl_display_disconnect(display: c->wl_display); |
251 | free(ptr: c); |
252 | } |
253 | |
254 | static void |
255 | post_error_main(void) |
256 | { |
257 | struct client *c = client_connect(); |
258 | struct wl_seat *seat = client_get_seat(c); |
259 | |
260 | /* stop display so that it can post the error. |
261 | * The function should return -1, because of the posted error */ |
262 | assert(stop_display(c, 1) == -1); |
263 | |
264 | /* display should have posted error, check it! */ |
265 | check_for_error(c, proxy: (struct wl_proxy *) seat); |
266 | |
267 | /* don't call client_disconnect(c), because then the test would be |
268 | * aborted due to checks for error in this function */ |
269 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
270 | client_disconnect_nocheck(c); |
271 | } |
272 | |
273 | TEST(post_error_to_one_client) |
274 | { |
275 | struct display *d = display_create(); |
276 | struct client_info *cl; |
277 | |
278 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
279 | version: 1, data: d, bind: bind_seat); |
280 | |
281 | cl = client_create_noarg(d, post_error_main); |
282 | display_run(d); |
283 | |
284 | /* the display was stopped by client, so it can |
285 | * proceed in the code and post an error */ |
286 | assert(cl->data); |
287 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
288 | code: 23, msg: "Dummy error" ); |
289 | |
290 | /* this one should be ignored */ |
291 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
292 | code: 21, msg: "Dummy error (ignore)" ); |
293 | |
294 | display_resume(d); |
295 | display_destroy(d); |
296 | } |
297 | |
298 | static void |
299 | post_error_main2(void) |
300 | { |
301 | struct client *c = client_connect(); |
302 | struct wl_seat *seat = client_get_seat(c); |
303 | |
304 | /* the error should not be posted for this client */ |
305 | assert(stop_display(c, 2) >= 0); |
306 | |
307 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
308 | client_disconnect(c); |
309 | } |
310 | |
311 | static void |
312 | post_error_main3(void) |
313 | { |
314 | struct client *c = client_connect(); |
315 | struct wl_seat *seat = client_get_seat(c); |
316 | |
317 | assert(stop_display(c, 2) == -1); |
318 | check_for_error(c, proxy: (struct wl_proxy *) seat); |
319 | |
320 | /* don't call client_disconnect(c), because then the test would be |
321 | * aborted due to checks for error in this function */ |
322 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
323 | client_disconnect_nocheck(c); |
324 | } |
325 | |
326 | /* all the testcases could be in one TEST, but splitting it |
327 | * apart is better for debugging when the test fails */ |
328 | TEST(post_error_to_one_from_two_clients) |
329 | { |
330 | struct display *d = display_create(); |
331 | struct client_info *cl; |
332 | |
333 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
334 | version: 1, data: d, bind: bind_seat); |
335 | |
336 | client_create_noarg(d, post_error_main2); |
337 | cl = client_create_noarg(d, post_error_main3); |
338 | display_run(d); |
339 | |
340 | /* post error only to the second client */ |
341 | assert(cl->data); |
342 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
343 | code: 23, msg: "Dummy error" ); |
344 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
345 | code: 21, msg: "Dummy error (ignore)" ); |
346 | |
347 | display_resume(d); |
348 | display_destroy(d); |
349 | } |
350 | |
351 | /* all the testcases could be in one TEST, but splitting it |
352 | * apart is better for debugging when the test fails */ |
353 | TEST(post_error_to_two_clients) |
354 | { |
355 | struct display *d = display_create(); |
356 | struct client_info *cl, *cl2; |
357 | |
358 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
359 | version: 1, data: d, bind: bind_seat); |
360 | |
361 | cl = client_create_noarg(d, post_error_main3); |
362 | cl2 = client_create_noarg(d, post_error_main3); |
363 | |
364 | display_run(d); |
365 | |
366 | /* Try to send the error to both clients */ |
367 | assert(cl->data && cl2->data); |
368 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
369 | code: 23, msg: "Dummy error" ); |
370 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
371 | code: 21, msg: "Dummy error (ignore)" ); |
372 | |
373 | wl_resource_post_error(resource: (struct wl_resource *) cl2->data, |
374 | code: 23, msg: "Dummy error" ); |
375 | wl_resource_post_error(resource: (struct wl_resource *) cl2->data, |
376 | code: 21, msg: "Dummy error (ignore)" ); |
377 | |
378 | display_resume(d); |
379 | display_destroy(d); |
380 | } |
381 | |
382 | static void |
383 | post_nomem_main(void) |
384 | { |
385 | struct client *c = client_connect(); |
386 | struct wl_seat *seat = client_get_seat(c); |
387 | |
388 | assert(stop_display(c, 1) == -1); |
389 | assert(wl_display_get_error(c->wl_display) == ENOMEM); |
390 | |
391 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
392 | client_disconnect_nocheck(c); |
393 | } |
394 | |
395 | TEST(post_nomem_tst) |
396 | { |
397 | struct display *d = display_create(); |
398 | struct client_info *cl; |
399 | |
400 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
401 | version: 1, data: d, bind: bind_seat); |
402 | |
403 | cl = client_create_noarg(d, post_nomem_main); |
404 | display_run(d); |
405 | |
406 | assert(cl->data); |
407 | wl_resource_post_no_memory(resource: (struct wl_resource *) cl->data); |
408 | display_resume(d); |
409 | |
410 | /* first client terminated. Run it again, |
411 | * but post no memory to client */ |
412 | cl = client_create_noarg(d, post_nomem_main); |
413 | display_run(d); |
414 | |
415 | assert(cl->data); |
416 | wl_client_post_no_memory(client: cl->wl_client); |
417 | display_resume(d); |
418 | |
419 | display_destroy(d); |
420 | } |
421 | |
422 | static void |
423 | post_implementation_error_main(void) |
424 | { |
425 | struct client *c = client_connect(); |
426 | struct wl_seat *seat = client_get_seat(c); |
427 | uint32_t object_id, protocol_error; |
428 | const struct wl_interface *interface; |
429 | |
430 | assert(stop_display(c, 1) == -1); |
431 | int err = wl_display_get_error(display: c->wl_display); |
432 | fprintf(stderr, format: "Err is %i\n" , err); |
433 | assert(err == EPROTO); |
434 | protocol_error = wl_display_get_protocol_error(display: c->wl_display, |
435 | interface: &interface, |
436 | id: &object_id); |
437 | assert(protocol_error == WL_DISPLAY_ERROR_IMPLEMENTATION); |
438 | assert(interface == &wl_display_interface); |
439 | |
440 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
441 | client_disconnect_nocheck(c); |
442 | } |
443 | |
444 | TEST(post_internal_error_tst) |
445 | { |
446 | struct display *d = display_create(); |
447 | struct client_info *cl; |
448 | |
449 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
450 | version: 1, data: d, bind: bind_seat); |
451 | |
452 | cl = client_create_noarg(d, post_implementation_error_main); |
453 | display_run(d); |
454 | |
455 | wl_client_post_implementation_error(client: cl->wl_client, msg: "Error %i" , 20); |
456 | |
457 | display_resume(d); |
458 | |
459 | display_destroy(d); |
460 | } |
461 | |
462 | static void |
463 | register_reading(struct wl_display *display) |
464 | { |
465 | while(wl_display_prepare_read(display) != 0 && errno == EAGAIN) |
466 | assert(wl_display_dispatch_pending(display) >= 0); |
467 | assert(wl_display_flush(display) >= 0); |
468 | } |
469 | |
470 | /* create thread that will call prepare+read so that |
471 | * it will block */ |
472 | static pthread_t |
473 | create_thread(struct client *c, void *(*func)(void*)) |
474 | { |
475 | pthread_t thread; |
476 | |
477 | c->display_stopped = 0; |
478 | /* func must set display->stopped to 1 before sleeping */ |
479 | assert(pthread_create(&thread, NULL, func, c) == 0); |
480 | |
481 | /* make sure the thread is sleeping. It's a little bit racy |
482 | * (setting display_stopped to 1 and calling wl_display_read_events) |
483 | * so call usleep once again after the loop ends - it should |
484 | * be sufficient... */ |
485 | while (c->display_stopped == 0) |
486 | test_usleep(500); |
487 | test_usleep(10000); |
488 | |
489 | return thread; |
490 | } |
491 | |
492 | static void * |
493 | thread_read_error(void *data) |
494 | { |
495 | struct client *c = data; |
496 | |
497 | register_reading(display: c->wl_display); |
498 | |
499 | /* |
500 | * Calling the read right now will block this thread |
501 | * until the other thread will read the data. |
502 | * However, after invoking an error, this |
503 | * thread should be woken up or it will block indefinitely. |
504 | */ |
505 | c->display_stopped = 1; |
506 | assert(wl_display_read_events(c->wl_display) == -1); |
507 | |
508 | assert(wl_display_dispatch_pending(c->wl_display) == -1); |
509 | assert(wl_display_get_error(c->wl_display)); |
510 | |
511 | pthread_exit(NULL); |
512 | } |
513 | |
514 | /* test posting an error in multi-threaded environment. */ |
515 | static void |
516 | threading_post_err(void) |
517 | { |
518 | DISABLE_LEAK_CHECKS; |
519 | |
520 | struct client *c = client_connect(); |
521 | pthread_t thread; |
522 | |
523 | /* register read intention */ |
524 | register_reading(display: c->wl_display); |
525 | |
526 | /* use this var as an indicator that thread is sleeping */ |
527 | c->display_stopped = 0; |
528 | |
529 | /* create new thread that will register its intention too */ |
530 | thread = create_thread(c, func: thread_read_error); |
531 | |
532 | /* so now we have sleeping thread waiting for a pthread_cond signal. |
533 | * The main thread must call wl_display_read_events(). |
534 | * If this call fails, then it won't call broadcast at the |
535 | * end of the function and the sleeping thread will block indefinitely. |
536 | * Make the call fail and watch if libwayland will unblock the thread! */ |
537 | |
538 | /* create error on fd, so that wl_display_read_events will fail. |
539 | * The same can happen when server hangs up */ |
540 | close(fd: wl_display_get_fd(display: c->wl_display)); |
541 | /* this read events will fail and will |
542 | * post an error that should wake the sleeping thread |
543 | * and dispatch the incoming events */ |
544 | assert(wl_display_read_events(c->wl_display) == -1); |
545 | |
546 | /* kill test in 3 seconds. This should be enough time for the |
547 | * thread to exit if it's not blocking. If everything is OK, than |
548 | * the thread was woken up and the test will end before the SIGALRM */ |
549 | test_set_timeout(3); |
550 | pthread_join(th: thread, NULL); |
551 | |
552 | client_disconnect_nocheck(c); |
553 | } |
554 | |
555 | TEST(threading_errors_tst) |
556 | { |
557 | struct display *d = display_create(); |
558 | |
559 | client_create_noarg(d, threading_post_err); |
560 | display_run(d); |
561 | |
562 | display_destroy(d); |
563 | } |
564 | |
565 | static void * |
566 | thread_prepare_and_read(void *data) |
567 | { |
568 | struct client *c = data; |
569 | |
570 | register_reading(display: c->wl_display); |
571 | |
572 | c->display_stopped = 1; |
573 | |
574 | assert(wl_display_read_events(c->wl_display) == 0); |
575 | assert(wl_display_dispatch_pending(c->wl_display) == 0); |
576 | |
577 | pthread_exit(NULL); |
578 | } |
579 | |
580 | /* test cancel read*/ |
581 | static void |
582 | threading_cancel_read(void) |
583 | { |
584 | DISABLE_LEAK_CHECKS; |
585 | |
586 | struct client *c = client_connect(); |
587 | pthread_t th1, th2, th3; |
588 | |
589 | register_reading(display: c->wl_display); |
590 | |
591 | th1 = create_thread(c, func: thread_prepare_and_read); |
592 | th2 = create_thread(c, func: thread_prepare_and_read); |
593 | th3 = create_thread(c, func: thread_prepare_and_read); |
594 | |
595 | /* all the threads are sleeping, waiting until read or cancel |
596 | * is called. Cancel the read and let the threads proceed */ |
597 | wl_display_cancel_read(display: c->wl_display); |
598 | |
599 | /* kill test in 3 seconds. This should be enough time for the |
600 | * thread to exit if it's not blocking. If everything is OK, than |
601 | * the thread was woken up and the test will end before the SIGALRM */ |
602 | test_set_timeout(3); |
603 | pthread_join(th: th1, NULL); |
604 | pthread_join(th: th2, NULL); |
605 | pthread_join(th: th3, NULL); |
606 | |
607 | client_disconnect(c); |
608 | } |
609 | |
610 | TEST(threading_cancel_read_tst) |
611 | { |
612 | struct display *d = display_create(); |
613 | |
614 | client_create_noarg(d, threading_cancel_read); |
615 | display_run(d); |
616 | |
617 | display_destroy(d); |
618 | } |
619 | |
620 | static void |
621 | threading_read_eagain(void) |
622 | { |
623 | DISABLE_LEAK_CHECKS; |
624 | |
625 | struct client *c = client_connect(); |
626 | pthread_t th1, th2, th3; |
627 | |
628 | register_reading(display: c->wl_display); |
629 | |
630 | th1 = create_thread(c, func: thread_prepare_and_read); |
631 | th2 = create_thread(c, func: thread_prepare_and_read); |
632 | th3 = create_thread(c, func: thread_prepare_and_read); |
633 | |
634 | /* All the threads are sleeping, waiting until read or cancel |
635 | * is called. Since we have no data on socket waiting, |
636 | * the wl_connection_read should end up with error and set errno |
637 | * to EAGAIN. Check if the threads are woken up in this case. */ |
638 | assert(wl_display_read_events(c->wl_display) == 0); |
639 | /* errno should be still set to EAGAIN if wl_connection_read |
640 | * set it - check if we're testing the right case */ |
641 | assert(errno == EAGAIN); |
642 | |
643 | test_set_timeout(3); |
644 | pthread_join(th: th1, NULL); |
645 | pthread_join(th: th2, NULL); |
646 | pthread_join(th: th3, NULL); |
647 | |
648 | client_disconnect(c); |
649 | } |
650 | |
651 | TEST(threading_read_eagain_tst) |
652 | { |
653 | struct display *d = display_create(); |
654 | client_create_noarg(d, threading_read_eagain); |
655 | |
656 | display_run(d); |
657 | |
658 | display_destroy(d); |
659 | } |
660 | |
661 | static void * |
662 | thread_prepare_and_read2(void *data) |
663 | { |
664 | struct client *c = data; |
665 | |
666 | while(wl_display_prepare_read(display: c->wl_display) != 0 && errno == EAGAIN) |
667 | assert(wl_display_dispatch_pending(c->wl_display) == -1); |
668 | assert(wl_display_flush(c->wl_display) == -1); |
669 | |
670 | c->display_stopped = 1; |
671 | |
672 | assert(wl_display_read_events(c->wl_display) == -1); |
673 | assert(wl_display_dispatch_pending(c->wl_display) == -1); |
674 | |
675 | pthread_exit(NULL); |
676 | } |
677 | |
678 | static void |
679 | threading_read_after_error(void) |
680 | { |
681 | DISABLE_LEAK_CHECKS; |
682 | |
683 | struct client *c = client_connect(); |
684 | pthread_t thread; |
685 | |
686 | /* create an error */ |
687 | close(fd: wl_display_get_fd(display: c->wl_display)); |
688 | assert(wl_display_dispatch(c->wl_display) == -1); |
689 | |
690 | /* try to prepare for reading */ |
691 | while(wl_display_prepare_read(display: c->wl_display) != 0 && errno == EAGAIN) |
692 | assert(wl_display_dispatch_pending(c->wl_display) == -1); |
693 | assert(wl_display_flush(c->wl_display) == -1); |
694 | |
695 | assert(pthread_create(&thread, NULL, |
696 | thread_prepare_and_read2, c) == 0); |
697 | |
698 | /* make sure thread is sleeping */ |
699 | while (c->display_stopped == 0) |
700 | test_usleep(500); |
701 | test_usleep(10000); |
702 | |
703 | assert(wl_display_read_events(c->wl_display) == -1); |
704 | |
705 | /* kill test in 3 seconds */ |
706 | test_set_timeout(3); |
707 | pthread_join(th: thread, NULL); |
708 | |
709 | client_disconnect_nocheck(c); |
710 | } |
711 | |
712 | TEST(threading_read_after_error_tst) |
713 | { |
714 | struct display *d = display_create(); |
715 | |
716 | client_create_noarg(d, threading_read_after_error); |
717 | display_run(d); |
718 | |
719 | display_destroy(d); |
720 | } |
721 | |
722 | static void |
723 | wait_for_error_using_dispatch(struct client *c, struct wl_proxy *proxy) |
724 | { |
725 | int ret; |
726 | |
727 | while (true) { |
728 | /* Dispatching should eventually hit the protocol error before |
729 | * any other error. */ |
730 | ret = wl_display_dispatch(display: c->wl_display); |
731 | if (ret == 0) { |
732 | continue; |
733 | } else { |
734 | assert(errno == EPROTO); |
735 | break; |
736 | } |
737 | } |
738 | |
739 | check_pending_error(c, proxy); |
740 | } |
741 | |
742 | static void |
743 | wait_for_error_using_prepare_read(struct client *c, struct wl_proxy *proxy) |
744 | { |
745 | int ret = 0; |
746 | struct pollfd pfd[2]; |
747 | |
748 | while (true) { |
749 | while (wl_display_prepare_read(display: c->wl_display) != 0 && |
750 | errno == EAGAIN) { |
751 | assert(wl_display_dispatch_pending(c->wl_display) >= 0); |
752 | } |
753 | |
754 | /* Flush may fail due to EPIPE if the connection is broken, but |
755 | * this must not set a fatal display error because that would |
756 | * result in it being impossible to read a potential protocol |
757 | * error. */ |
758 | do { |
759 | ret = wl_display_flush(display: c->wl_display); |
760 | } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); |
761 | assert(ret >= 0 || errno == EPIPE); |
762 | assert(wl_display_get_error(c->wl_display) == 0); |
763 | |
764 | pfd[0].fd = wl_display_get_fd(display: c->wl_display); |
765 | pfd[0].events = POLLIN; |
766 | do { |
767 | ret = poll(fds: pfd, nfds: 1, timeout: -1); |
768 | } while (ret == -1 && errno == EINTR); |
769 | assert(ret != -1); |
770 | |
771 | /* We should always manage to read the error before the EPIPE |
772 | * comes this way. */ |
773 | assert(wl_display_read_events(c->wl_display) == 0); |
774 | |
775 | /* Dispatching should eventually hit the protocol error before |
776 | * any other error. */ |
777 | ret = wl_display_dispatch_pending(display: c->wl_display); |
778 | if (ret == 0) { |
779 | continue; |
780 | } else { |
781 | assert(errno == EPROTO); |
782 | break; |
783 | } |
784 | } |
785 | |
786 | check_pending_error(c, proxy); |
787 | } |
788 | |
789 | static void |
790 | check_error_after_epipe(void *data) |
791 | { |
792 | bool use_dispatch_helpers = *(bool *) data; |
793 | struct client *client; |
794 | struct wl_seat *seat; |
795 | struct wl_callback *callback; |
796 | |
797 | client = client_connect(); |
798 | |
799 | /* This will, according to the implementation below, cause the server |
800 | * to post an error. */ |
801 | seat = client_get_seat(c: client); |
802 | wl_display_flush(display: client->wl_display); |
803 | |
804 | /* The server will not actually destroy the client until it receives |
805 | * input, so send something to trigger the client destruction. */ |
806 | callback = wl_display_sync(wl_display: client->wl_display); |
807 | wl_callback_destroy(wl_callback: callback); |
808 | |
809 | /* Sleep some to give the server a chance to react and destroy the |
810 | * client. */ |
811 | test_usleep(200000); |
812 | |
813 | /* Wait for the protocol error and check that we reached it before |
814 | * EPIPE. */ |
815 | if (use_dispatch_helpers) { |
816 | wait_for_error_using_dispatch(c: client, proxy: (struct wl_proxy *) seat); |
817 | } else { |
818 | wait_for_error_using_prepare_read(c: client, |
819 | proxy: (struct wl_proxy *) seat); |
820 | } |
821 | |
822 | wl_seat_destroy(wl_seat: seat); |
823 | client_disconnect_nocheck(c: client); |
824 | } |
825 | |
826 | static void |
827 | bind_seat_and_post_error(struct wl_client *client, void *data, |
828 | uint32_t version, uint32_t id) |
829 | { |
830 | struct display *d = data; |
831 | struct client_info *ci; |
832 | struct wl_resource *resource; |
833 | |
834 | ci = find_client_info(d, client); |
835 | assert(ci); |
836 | |
837 | resource = wl_resource_create(client, interface: &wl_seat_interface, version, id); |
838 | assert(resource); |
839 | ci->data = resource; |
840 | |
841 | wl_resource_post_error(resource: ci->data, code: 23, msg: "Dummy error" ); |
842 | } |
843 | |
844 | TEST(error_code_after_epipe) |
845 | { |
846 | struct display *d = display_create(); |
847 | bool use_dispatch_helpers; |
848 | |
849 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
850 | version: 1, data: d, bind: bind_seat_and_post_error); |
851 | |
852 | use_dispatch_helpers = true; |
853 | client_create(d, check_error_after_epipe, &use_dispatch_helpers); |
854 | display_run(d); |
855 | |
856 | use_dispatch_helpers = false; |
857 | client_create(d, check_error_after_epipe, &use_dispatch_helpers); |
858 | display_run(d); |
859 | |
860 | display_destroy(d); |
861 | } |
862 | |
863 | static void |
864 | check_seat_versions(struct wl_seat *seat, uint32_t ev) |
865 | { |
866 | struct wl_pointer *pointer; |
867 | |
868 | assert(wl_proxy_get_version((struct wl_proxy *) seat) == ev); |
869 | assert(wl_seat_get_version(seat) == ev); |
870 | |
871 | pointer = wl_seat_get_pointer(wl_seat: seat); |
872 | assert(wl_pointer_get_version(pointer) == ev); |
873 | assert(wl_proxy_get_version((struct wl_proxy *) pointer) == ev); |
874 | wl_proxy_destroy(proxy: (struct wl_proxy *) pointer); |
875 | } |
876 | |
877 | /* Normal client with proxy versions available. */ |
878 | static void |
879 | seat_version(void *data) |
880 | { |
881 | struct handler_info *hi = data; |
882 | struct client *c = client_connect(); |
883 | struct wl_seat *seat; |
884 | |
885 | /* display proxy should always be version 0 */ |
886 | assert(wl_proxy_get_version((struct wl_proxy *) c->wl_display) == 0); |
887 | |
888 | seat = client_get_seat_with_info(c, hi); |
889 | if (hi->use_unversioned) |
890 | check_seat_versions(seat, ev: 0); |
891 | else |
892 | check_seat_versions(seat, ev: hi->bind_version); |
893 | |
894 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
895 | |
896 | client_disconnect_nocheck(c); |
897 | } |
898 | |
899 | TEST(versions) |
900 | { |
901 | struct display *d = display_create(); |
902 | struct wl_global *global; |
903 | int i; |
904 | |
905 | global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
906 | version: 5, data: d, bind: bind_seat); |
907 | |
908 | for (i = 1; i <= 5; i++) { |
909 | struct handler_info hi; |
910 | |
911 | hi.bind_version = i; |
912 | hi.use_unversioned = false; |
913 | client_create(d, seat_version, &hi); |
914 | hi.use_unversioned = true; |
915 | client_create(d, seat_version, &hi); |
916 | } |
917 | |
918 | display_run(d); |
919 | |
920 | wl_global_destroy(global); |
921 | |
922 | display_destroy(d); |
923 | } |
924 | |
925 | static void |
926 | check_error_on_destroyed_object(void *data) |
927 | { |
928 | struct client *c; |
929 | struct wl_seat *seat; |
930 | uint32_t id; |
931 | const struct wl_interface *intf; |
932 | |
933 | c = client_connect(); |
934 | seat = client_get_seat(c); |
935 | |
936 | /* destroy the seat proxy. The display won't know |
937 | * about it yet, so it will post the error as usual */ |
938 | wl_proxy_destroy(proxy: (struct wl_proxy *) seat); |
939 | |
940 | /* let display post the error. The error will |
941 | * be caught in stop_display while dispatching */ |
942 | assert(stop_display(c, 1) == -1); |
943 | |
944 | /* check the returned error. Since the object was destroyed, |
945 | * we don't know the interface and id */ |
946 | assert(wl_display_get_error(c->wl_display) == EPROTO); |
947 | assert(wl_display_get_protocol_error(c->wl_display, &intf, &id) == 23); |
948 | assert(intf == NULL); |
949 | assert(id == 0); |
950 | |
951 | client_disconnect_nocheck(c); |
952 | } |
953 | |
954 | TEST(error_on_destroyed_object) |
955 | { |
956 | struct client_info *cl; |
957 | struct display *d = display_create(); |
958 | |
959 | wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
960 | version: 1, data: d, bind: bind_seat); |
961 | |
962 | cl = client_create_noarg(d, check_error_on_destroyed_object); |
963 | display_run(d); |
964 | |
965 | /* did client bind to the seat? */ |
966 | assert(cl->data); |
967 | |
968 | /* post error on the destroyed object */ |
969 | wl_resource_post_error(resource: (struct wl_resource *) cl->data, |
970 | code: 23, msg: "Dummy error" ); |
971 | display_resume(d); |
972 | display_destroy(d); |
973 | } |
974 | |
975 | static bool |
976 | global_filter(const struct wl_client *client, |
977 | const struct wl_global *global, |
978 | void *data) |
979 | { |
980 | /* Hide the wl_data_offer interface if no data was provided */ |
981 | if (wl_global_get_interface(global) == &wl_data_offer_interface) |
982 | return data != NULL; |
983 | |
984 | /* Show all the others */ |
985 | return true; |
986 | } |
987 | |
988 | static void |
989 | bind_data_offer(struct wl_client *client, void *data, |
990 | uint32_t vers, uint32_t id) |
991 | { |
992 | /* Client should not be able to bind to this interface! */ |
993 | assert(false); |
994 | } |
995 | |
996 | static void |
997 | registry_handle_filtered(void *data, struct wl_registry *registry, |
998 | uint32_t id, const char *intf, uint32_t ver) |
999 | { |
1000 | uint32_t *name = data; |
1001 | |
1002 | if (strcmp (s1: intf, s2: "wl_data_offer" ) == 0) { |
1003 | assert(name); |
1004 | *name = id; |
1005 | } |
1006 | } |
1007 | |
1008 | static const struct wl_registry_listener registry_listener_filtered = { |
1009 | registry_handle_filtered, |
1010 | NULL |
1011 | }; |
1012 | |
1013 | static void |
1014 | get_globals(void *data) |
1015 | { |
1016 | struct client *c = client_connect(); |
1017 | struct wl_registry *registry; |
1018 | |
1019 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1020 | wl_registry_add_listener(wl_registry: registry, listener: ®istry_listener_filtered, data); |
1021 | wl_display_roundtrip(display: c->wl_display); |
1022 | |
1023 | wl_registry_destroy(wl_registry: registry); |
1024 | client_disconnect_nocheck(c); |
1025 | } |
1026 | |
1027 | TEST(filtered_global_is_hidden) |
1028 | { |
1029 | struct display *d; |
1030 | struct wl_global *g; |
1031 | |
1032 | d = display_create(); |
1033 | |
1034 | g = wl_global_create(display: d->wl_display, interface: &wl_data_offer_interface, |
1035 | version: 1, data: d, bind: bind_data_offer); |
1036 | wl_display_set_global_filter(display: d->wl_display, filter: global_filter, NULL); |
1037 | |
1038 | client_create_noarg(d, get_globals); |
1039 | display_run(d); |
1040 | |
1041 | wl_global_destroy(global: g); |
1042 | |
1043 | display_destroy(d); |
1044 | } |
1045 | |
1046 | static void |
1047 | check_bind_error(struct client *c) |
1048 | { |
1049 | uint32_t errorcode, id; |
1050 | int err; |
1051 | const struct wl_interface *intf; |
1052 | |
1053 | err = wl_display_get_error(display: c->wl_display); |
1054 | assert(err == EPROTO); |
1055 | |
1056 | errorcode = wl_display_get_protocol_error(display: c->wl_display, interface: &intf, id: &id); |
1057 | assert(errorcode == WL_DISPLAY_ERROR_INVALID_OBJECT); |
1058 | } |
1059 | |
1060 | static void |
1061 | force_bind(void *data) |
1062 | { |
1063 | struct client *c = client_connect(); |
1064 | struct wl_registry *registry; |
1065 | void *ptr; |
1066 | uint32_t *name = data; |
1067 | |
1068 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1069 | |
1070 | ptr = wl_registry_bind (wl_registry: registry, name: *name, interface: &wl_data_offer_interface, version: 1); |
1071 | wl_display_roundtrip(display: c->wl_display); |
1072 | check_bind_error(c); |
1073 | |
1074 | wl_proxy_destroy(proxy: (struct wl_proxy *) ptr); |
1075 | wl_registry_destroy(wl_registry: registry); |
1076 | |
1077 | client_disconnect_nocheck(c); |
1078 | } |
1079 | |
1080 | TEST(bind_fails_on_filtered_global) |
1081 | { |
1082 | struct display *d; |
1083 | struct wl_global *g; |
1084 | uint32_t *name; |
1085 | |
1086 | /* Create a anonymous shared memory to pass the interface name */ |
1087 | name = mmap(NULL, len: sizeof(uint32_t), |
1088 | PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, fd: -1, offset: 0); |
1089 | |
1090 | d = display_create(); |
1091 | |
1092 | g = wl_global_create(display: d->wl_display, interface: &wl_data_offer_interface, |
1093 | version: 1, data: d, bind: bind_data_offer); |
1094 | wl_display_set_global_filter(display: d->wl_display, filter: global_filter, data: name); |
1095 | |
1096 | client_create(d, get_globals, name); |
1097 | *name = 0; |
1098 | |
1099 | display_run(d); |
1100 | /* wl_data_offer should be 2 */ |
1101 | assert(*name == 2); |
1102 | wl_display_set_global_filter(display: d->wl_display, filter: global_filter, NULL); |
1103 | |
1104 | /* Try to bind to the interface name when a global filter is in place */ |
1105 | client_create(d, force_bind, name); |
1106 | display_run(d); |
1107 | |
1108 | wl_global_destroy(global: g); |
1109 | |
1110 | display_destroy(d); |
1111 | } |
1112 | |
1113 | static void |
1114 | pre_fd(void *data, struct fd_passer *fdp) |
1115 | { |
1116 | fd_passer_destroy(fdp); |
1117 | } |
1118 | |
1119 | static void |
1120 | fd(void *data, struct fd_passer *fdp, int32_t fd) |
1121 | { |
1122 | /* We destroyed the resource before this event */ |
1123 | assert(false); |
1124 | } |
1125 | |
1126 | struct fd_passer_listener fd_passer_listener = { |
1127 | pre_fd, |
1128 | fd, |
1129 | }; |
1130 | |
1131 | static void |
1132 | zombie_fd_handle_globals(void *data, struct wl_registry *registry, |
1133 | uint32_t id, const char *intf, uint32_t ver) |
1134 | { |
1135 | struct fd_passer *fdp; |
1136 | |
1137 | if (!strcmp(s1: intf, s2: "fd_passer" )) { |
1138 | fdp = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 1); |
1139 | fd_passer_add_listener(fdp, &fd_passer_listener, NULL); |
1140 | } |
1141 | } |
1142 | |
1143 | static const struct wl_registry_listener zombie_fd_registry_listener = { |
1144 | zombie_fd_handle_globals, |
1145 | NULL |
1146 | }; |
1147 | |
1148 | static void |
1149 | zombie_client(void *data) |
1150 | { |
1151 | struct client *c = client_connect(); |
1152 | struct wl_registry *registry; |
1153 | |
1154 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1155 | wl_registry_add_listener(wl_registry: registry, listener: &zombie_fd_registry_listener, NULL); |
1156 | |
1157 | /* Gets the registry */ |
1158 | wl_display_roundtrip(display: c->wl_display); |
1159 | |
1160 | /* push out the fd_passer bind */ |
1161 | wl_display_roundtrip(display: c->wl_display); |
1162 | |
1163 | /* push out our fd_passer.destroy */ |
1164 | wl_display_roundtrip(display: c->wl_display); |
1165 | |
1166 | wl_registry_destroy(wl_registry: registry); |
1167 | |
1168 | client_disconnect_nocheck(c); |
1169 | } |
1170 | |
1171 | struct passer_data { |
1172 | struct wl_resource *conjoined_passer; |
1173 | }; |
1174 | |
1175 | static void |
1176 | feed_pipe(int fd, char tosend) |
1177 | { |
1178 | int count; |
1179 | |
1180 | do { |
1181 | count = write(fd: fd, buf: &tosend, n: 1); |
1182 | } while (count != 1 && errno == EAGAIN); |
1183 | assert(count == 1); |
1184 | close(fd: fd); |
1185 | } |
1186 | |
1187 | static void |
1188 | fd_passer_clobber(struct wl_client *client, struct wl_resource *res) |
1189 | { |
1190 | struct passer_data *pdata = wl_resource_get_user_data(resource: res); |
1191 | int pipes1[2], pipes2[2], ret; |
1192 | |
1193 | if (pdata->conjoined_passer) { |
1194 | ret = pipe(pipedes: pipes1); |
1195 | assert(ret == 0); |
1196 | ret = pipe(pipedes: pipes2); |
1197 | assert(ret == 0); |
1198 | |
1199 | wl_resource_queue_event(resource: res, opcode: FD_PASSER_FD, pipes1[0]); |
1200 | fd_passer_send_fd(pdata->conjoined_passer, pipes2[0]); |
1201 | feed_pipe(fd: pipes1[1], tosend: '1'); |
1202 | feed_pipe(fd: pipes2[1], tosend: '2'); |
1203 | close(fd: pipes1[0]); |
1204 | close(fd: pipes2[0]); |
1205 | } |
1206 | wl_resource_destroy(resource: res); |
1207 | } |
1208 | |
1209 | static void |
1210 | fd_passer_twin(struct wl_client *client, struct wl_resource *res, struct wl_resource *passer) |
1211 | { |
1212 | struct passer_data *pdata = wl_resource_get_user_data(resource: res); |
1213 | |
1214 | pdata->conjoined_passer = passer; |
1215 | } |
1216 | |
1217 | static const struct fd_passer_interface fdp_interface = { |
1218 | fd_passer_clobber, |
1219 | fd_passer_twin |
1220 | }; |
1221 | |
1222 | static void |
1223 | pdata_destroy(struct wl_resource *res) |
1224 | { |
1225 | struct passer_data *pdata = wl_resource_get_user_data(resource: res); |
1226 | |
1227 | free(ptr: pdata); |
1228 | } |
1229 | |
1230 | static void |
1231 | bind_fd_passer(struct wl_client *client, void *data, |
1232 | uint32_t vers, uint32_t id) |
1233 | { |
1234 | struct wl_resource *res; |
1235 | struct passer_data *pdata; |
1236 | |
1237 | pdata = malloc(size: sizeof(*pdata)); |
1238 | assert(pdata); |
1239 | pdata->conjoined_passer = NULL; |
1240 | |
1241 | res = wl_resource_create(client, interface: &fd_passer_interface, version: vers, id); |
1242 | wl_resource_set_implementation(resource: res, implementation: &fdp_interface, data: pdata, destroy: pdata_destroy); |
1243 | assert(res); |
1244 | if (vers == 1) { |
1245 | fd_passer_send_pre_fd(res); |
1246 | fd_passer_send_fd(res, fileno(stdin)); |
1247 | } |
1248 | } |
1249 | |
1250 | TEST(zombie_fd) |
1251 | { |
1252 | struct display *d; |
1253 | struct wl_global *g; |
1254 | |
1255 | d = display_create(); |
1256 | |
1257 | g = wl_global_create(display: d->wl_display, interface: &fd_passer_interface, |
1258 | version: 1, data: d, bind: bind_fd_passer); |
1259 | |
1260 | client_create_noarg(d, zombie_client); |
1261 | display_run(d); |
1262 | |
1263 | wl_global_destroy(global: g); |
1264 | |
1265 | display_destroy(d); |
1266 | } |
1267 | |
1268 | |
1269 | static void |
1270 | double_pre_fd(void *data, struct fd_passer *fdp) |
1271 | { |
1272 | assert(false); |
1273 | } |
1274 | |
1275 | static void |
1276 | double_fd(void *data, struct fd_passer *fdp, int32_t fd) |
1277 | { |
1278 | char buf; |
1279 | int count; |
1280 | |
1281 | do { |
1282 | count = read(fd: fd, buf: &buf, nbytes: 1); |
1283 | } while (count != 1 && errno == EAGAIN); |
1284 | assert(count == 1); |
1285 | |
1286 | close(fd: fd); |
1287 | fd_passer_destroy(fdp); |
1288 | assert(buf == '2'); |
1289 | } |
1290 | |
1291 | struct fd_passer_listener double_fd_passer_listener = { |
1292 | double_pre_fd, |
1293 | double_fd, |
1294 | }; |
1295 | |
1296 | |
1297 | static void |
1298 | double_zombie_fd_handle_globals(void *data, struct wl_registry *registry, |
1299 | uint32_t id, const char *intf, uint32_t ver) |
1300 | { |
1301 | struct fd_passer *fdp1, *fdp2; |
1302 | |
1303 | if (!strcmp(s1: intf, s2: "fd_passer" )) { |
1304 | fdp1 = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 2); |
1305 | fd_passer_add_listener(fdp1, &double_fd_passer_listener, NULL); |
1306 | fdp2 = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 2); |
1307 | fd_passer_add_listener(fdp2, &double_fd_passer_listener, NULL); |
1308 | fd_passer_conjoin(fdp1, fdp2); |
1309 | fd_passer_destroy(fdp1); |
1310 | } |
1311 | } |
1312 | |
1313 | static const struct wl_registry_listener double_zombie_fd_registry_listener = { |
1314 | double_zombie_fd_handle_globals, |
1315 | NULL |
1316 | }; |
1317 | |
1318 | static void |
1319 | double_zombie_client(void *data) |
1320 | { |
1321 | struct client *c = client_connect(); |
1322 | struct wl_registry *registry; |
1323 | |
1324 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1325 | wl_registry_add_listener(wl_registry: registry, listener: &double_zombie_fd_registry_listener, NULL); |
1326 | |
1327 | /* Gets the registry */ |
1328 | wl_display_roundtrip(display: c->wl_display); |
1329 | |
1330 | /* One more so server can respond to conjoin+destroy */ |
1331 | wl_display_roundtrip(display: c->wl_display); |
1332 | |
1333 | /* And finally push out our last fd_passer.destroy */ |
1334 | wl_display_roundtrip(display: c->wl_display); |
1335 | |
1336 | wl_registry_destroy(wl_registry: registry); |
1337 | |
1338 | client_disconnect_nocheck(c); |
1339 | } |
1340 | |
1341 | TEST(zombie_fd_errant_consumption) |
1342 | { |
1343 | struct display *d; |
1344 | struct wl_global *g; |
1345 | |
1346 | d = display_create(); |
1347 | |
1348 | g = wl_global_create(display: d->wl_display, interface: &fd_passer_interface, |
1349 | version: 2, data: d, bind: bind_fd_passer); |
1350 | |
1351 | client_create_noarg(d, double_zombie_client); |
1352 | display_run(d); |
1353 | |
1354 | wl_global_destroy(global: g); |
1355 | |
1356 | display_destroy(d); |
1357 | } |
1358 | |
1359 | |
1360 | static void |
1361 | registry_bind_interface_mismatch_handle_global(void *data, |
1362 | struct wl_registry *registry, |
1363 | uint32_t id, const char *intf, |
1364 | uint32_t ver) |
1365 | { |
1366 | uint32_t *seat_id_ptr = data; |
1367 | |
1368 | if (strcmp(s1: intf, s2: wl_seat_interface.name) == 0) { |
1369 | *seat_id_ptr = id; |
1370 | } |
1371 | } |
1372 | |
1373 | static const struct wl_registry_listener bind_interface_mismatch_registry_listener = { |
1374 | registry_bind_interface_mismatch_handle_global, |
1375 | NULL |
1376 | }; |
1377 | |
1378 | static void |
1379 | registry_bind_interface_mismatch_client(void *data) |
1380 | { |
1381 | struct client *c = client_connect(); |
1382 | struct wl_registry *registry; |
1383 | uint32_t seat_id = 0; |
1384 | void *ptr; |
1385 | int ret; |
1386 | |
1387 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1388 | wl_registry_add_listener(wl_registry: registry, |
1389 | listener: &bind_interface_mismatch_registry_listener, |
1390 | data: &seat_id); |
1391 | |
1392 | ret = wl_display_roundtrip(display: c->wl_display); |
1393 | assert(ret >= 0); |
1394 | assert(seat_id != 0); |
1395 | |
1396 | /* Bind with a different interface */ |
1397 | ptr = wl_registry_bind(wl_registry: registry, name: seat_id, interface: &wl_output_interface, version: 1); |
1398 | ret = wl_display_roundtrip(display: c->wl_display); |
1399 | assert(ret < 0); |
1400 | check_bind_error(c); |
1401 | |
1402 | wl_proxy_destroy(proxy: (struct wl_proxy *) ptr); |
1403 | wl_registry_destroy(wl_registry: registry); |
1404 | |
1405 | client_disconnect_nocheck(c); |
1406 | } |
1407 | |
1408 | TEST(registry_bind_interface_mismatch) |
1409 | { |
1410 | struct display *d; |
1411 | struct wl_global *seat_global; |
1412 | |
1413 | d = display_create(); |
1414 | |
1415 | seat_global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
1416 | version: 1, NULL, NULL); |
1417 | |
1418 | client_create_noarg(d, registry_bind_interface_mismatch_client); |
1419 | display_run(d); |
1420 | |
1421 | wl_global_destroy(global: seat_global); |
1422 | |
1423 | display_destroy(d); |
1424 | } |
1425 | |
1426 | static void |
1427 | send_overflow_client(void *data) |
1428 | { |
1429 | struct client *c = client_connect(); |
1430 | int i, err = 0; |
1431 | int *pipes = data; |
1432 | char tmp = '\0'; |
1433 | int sock, optval = 16384; |
1434 | |
1435 | /* Limit the send buffer size for the display socket to guarantee |
1436 | * that the test will cause an overflow. */ |
1437 | sock = wl_display_get_fd(display: c->wl_display); |
1438 | assert(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == 0); |
1439 | |
1440 | /* Request to break out of 'display_run' in the main process */ |
1441 | assert(stop_display(c, 1) >= 0); |
1442 | |
1443 | /* On Linux, the actual socket data + metadata space is twice `optval`; |
1444 | * since each noop request requires 8 bytes, the buffer should overflow |
1445 | * within <=4096 iterations. */ |
1446 | for (i = 0; i < 1000000; i++) { |
1447 | noop_request(c); |
1448 | err = wl_display_get_error(display: c->wl_display); |
1449 | if (err) |
1450 | break; |
1451 | } |
1452 | |
1453 | /* Do not close the pipe file descriptors afterwards, because the leak |
1454 | * check verifies that the initial/final FD counts are the same */ |
1455 | assert(write(pipes[1], &tmp, sizeof(tmp)) == (ssize_t)sizeof(tmp)); |
1456 | |
1457 | /* Expect an error */ |
1458 | fprintf(stderr, format: "Send loop failed on try %d, err = %d, %s\n" , i, err, strerror(errnum: err)); |
1459 | assert(err == EAGAIN); |
1460 | |
1461 | client_disconnect_nocheck(c); |
1462 | } |
1463 | |
1464 | TEST(send_overflow_disconnection) |
1465 | { |
1466 | struct display *d; |
1467 | char tmp; |
1468 | int rpipe[2]; |
1469 | ssize_t ret; |
1470 | |
1471 | assert(pipe(rpipe) != -1); |
1472 | |
1473 | d = display_create(); |
1474 | |
1475 | (void) client_create(d, send_overflow_client, &rpipe); |
1476 | |
1477 | /* Close write end of the pipe, so that the later read() call gets |
1478 | * interrupted if the client dies */ |
1479 | close(fd: rpipe[1]); |
1480 | |
1481 | /* Run the display until the client sends a `stop_display`, then |
1482 | * send a resume message but don't actually look at new messages */ |
1483 | display_run(d); |
1484 | display_post_resume_events(d); |
1485 | wl_display_flush_clients(display: d->wl_display); |
1486 | |
1487 | /* Wait until all noop requests have been sent (read returns 1), or |
1488 | * until client process aborts (read returns 0) */ |
1489 | do { |
1490 | ret = read(fd: rpipe[0], buf: &tmp, nbytes: sizeof(tmp)); |
1491 | } while (ret == -1 && errno == EINTR); |
1492 | assert(ret != -1); |
1493 | close(fd: rpipe[0]); |
1494 | |
1495 | /* For a clean shutdown */ |
1496 | display_run(d); |
1497 | |
1498 | display_destroy(d); |
1499 | } |
1500 | |
1501 | static void |
1502 | registry_global_remove_before_handle_global(void *data, |
1503 | struct wl_registry *registry, |
1504 | uint32_t id, const char *intf, |
1505 | uint32_t ver) |
1506 | { |
1507 | uint32_t *id_ptr = data; |
1508 | |
1509 | if (strcmp(s1: intf, s2: wl_seat_interface.name) == 0) { |
1510 | assert(*id_ptr == 0); |
1511 | *id_ptr = id; |
1512 | } |
1513 | } |
1514 | |
1515 | static void |
1516 | registry_global_remove_before_handle_global_remove(void *data, |
1517 | struct wl_registry *registry, |
1518 | uint32_t id) |
1519 | { |
1520 | uint32_t *id_ptr = data; |
1521 | |
1522 | if (*id_ptr == id) { |
1523 | *id_ptr = 0; |
1524 | } |
1525 | } |
1526 | |
1527 | /* This listener expects a uint32_t user data pointer, sets it to the wl_seat |
1528 | * global ID when receiving a "global" event, and sets it to zero when receiving |
1529 | * a "global_remove" event. */ |
1530 | static const struct wl_registry_listener global_remove_before_registry_listener = { |
1531 | registry_global_remove_before_handle_global, |
1532 | registry_global_remove_before_handle_global_remove, |
1533 | }; |
1534 | |
1535 | static void |
1536 | global_remove_before_client(void *data) |
1537 | { |
1538 | struct client *c = client_connect(); |
1539 | struct wl_registry *registry; |
1540 | uint32_t global_id = 0, saved_global_id; |
1541 | struct wl_seat *seat; |
1542 | int ret; |
1543 | |
1544 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1545 | wl_registry_add_listener(wl_registry: registry, |
1546 | listener: &global_remove_before_registry_listener, |
1547 | data: &global_id); |
1548 | |
1549 | ret = wl_display_roundtrip(display: c->wl_display); |
1550 | assert(ret >= 0); |
1551 | assert(global_id != 0); |
1552 | saved_global_id = global_id; |
1553 | |
1554 | /* Wait for the compositor to remove the global */ |
1555 | assert(stop_display(c, 1) >= 0); |
1556 | |
1557 | /* Check binding still works after the global has been removed. Also |
1558 | * check we get the global_remove event. */ |
1559 | seat = wl_registry_bind(wl_registry: registry, name: saved_global_id, interface: &wl_seat_interface, version: 1); |
1560 | ret = wl_display_roundtrip(display: c->wl_display); |
1561 | assert(ret >= 0); |
1562 | assert(global_id == 0); |
1563 | |
1564 | wl_seat_destroy(wl_seat: seat); |
1565 | wl_registry_destroy(wl_registry: registry); |
1566 | |
1567 | client_disconnect(c); |
1568 | } |
1569 | |
1570 | static void |
1571 | registry_global_remove_after_handle_global(void *data, |
1572 | struct wl_registry *registry, |
1573 | uint32_t id, const char *intf, |
1574 | uint32_t ver) |
1575 | { |
1576 | /* Make sure the global isn't advertised anymore after being removed */ |
1577 | assert(strcmp(intf, wl_seat_interface.name) != 0); |
1578 | } |
1579 | |
1580 | static const struct wl_registry_listener global_remove_after_registry_listener = { |
1581 | registry_global_remove_after_handle_global, |
1582 | NULL, |
1583 | }; |
1584 | |
1585 | static void |
1586 | global_remove_after_client(void *data) |
1587 | { |
1588 | struct client *c = client_connect(); |
1589 | struct wl_registry *registry; |
1590 | uint32_t global_id = 0; |
1591 | int ret; |
1592 | |
1593 | registry = wl_display_get_registry(wl_display: c->wl_display); |
1594 | wl_registry_add_listener(wl_registry: registry, |
1595 | listener: &global_remove_after_registry_listener, |
1596 | data: &global_id); |
1597 | |
1598 | ret = wl_display_roundtrip(display: c->wl_display); |
1599 | assert(ret >= 0); |
1600 | |
1601 | wl_registry_destroy(wl_registry: registry); |
1602 | |
1603 | client_disconnect(c); |
1604 | } |
1605 | |
1606 | TEST(global_remove) |
1607 | { |
1608 | struct display *d; |
1609 | struct wl_global *global; |
1610 | |
1611 | d = display_create(); |
1612 | |
1613 | global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface, |
1614 | version: 1, data: d, bind: bind_seat); |
1615 | |
1616 | /* Create a client before removing the global */ |
1617 | client_create_noarg(d, global_remove_before_client); |
1618 | |
1619 | display_run(d); |
1620 | |
1621 | wl_global_remove(global); |
1622 | |
1623 | /* Create another client after removing the global */ |
1624 | client_create_noarg(d, global_remove_after_client); |
1625 | |
1626 | display_resume(d); |
1627 | |
1628 | wl_global_destroy(global); |
1629 | |
1630 | display_destroy(d); |
1631 | } |
1632 | |
1633 | static void |
1634 | terminate_display(void *arg) |
1635 | { |
1636 | struct wl_display *wl_display = arg; |
1637 | wl_display_terminate(display: wl_display); |
1638 | } |
1639 | |
1640 | TEST(no_source_terminate) |
1641 | { |
1642 | struct display *d; |
1643 | struct wl_event_loop *loop; |
1644 | |
1645 | d = display_create(); |
1646 | loop = wl_display_get_event_loop(display: d->wl_display); |
1647 | |
1648 | wl_event_loop_add_idle(loop, func: terminate_display, data: d->wl_display); |
1649 | |
1650 | display_run(d); |
1651 | display_destroy(d); |
1652 | } |
1653 | |