1 | /* Test mq_notify. |
2 | Copyright (C) 2004-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <errno.h> |
20 | #include <fcntl.h> |
21 | #include <mqueue.h> |
22 | #include <limits.h> |
23 | #include <signal.h> |
24 | #include <stdint.h> |
25 | #include <stdio.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | #include <sys/mman.h> |
29 | #include <sys/time.h> |
30 | #include <sys/wait.h> |
31 | #include <time.h> |
32 | #include <unistd.h> |
33 | #include <support/check.h> |
34 | #include "tst-mqueue.h" |
35 | |
36 | #if _POSIX_THREADS && defined SIGRTMIN && defined SA_SIGINFO |
37 | # include <pthread.h> |
38 | |
39 | volatile int rtmin_cnt; |
40 | volatile pid_t rtmin_pid; |
41 | volatile uid_t rtmin_uid; |
42 | volatile int rtmin_code; |
43 | volatile union sigval rtmin_sigval; |
44 | |
45 | static void |
46 | rtmin_handler (int sig, siginfo_t *info, void *ctx) |
47 | { |
48 | if (sig != SIGRTMIN) |
49 | abort (); |
50 | ++rtmin_cnt; |
51 | rtmin_pid = info->si_pid; |
52 | rtmin_uid = info->si_uid; |
53 | rtmin_code = info->si_code; |
54 | rtmin_sigval = info->si_value; |
55 | } |
56 | |
57 | #define mqsend(q) (mqsend) (q, __LINE__) |
58 | static int |
59 | (mqsend) (mqd_t q, int line) |
60 | { |
61 | char c; |
62 | if (mq_send (mqdes: q, msg_ptr: &c, msg_len: 1, msg_prio: 1) != 0) |
63 | { |
64 | printf (format: "mq_send on line %d failed with: %m\n" , line); |
65 | return 1; |
66 | } |
67 | return 0; |
68 | } |
69 | |
70 | #define mqrecv(q) (mqrecv) (q, __LINE__) |
71 | static int |
72 | (mqrecv) (mqd_t q, int line) |
73 | { |
74 | char c; |
75 | ssize_t rets = TEMP_FAILURE_RETRY (mq_receive (q, &c, 1, NULL)); |
76 | if (rets != 1) |
77 | { |
78 | if (rets == -1) |
79 | printf (format: "mq_receive on line %d failed with: %m\n" , line); |
80 | else |
81 | printf (format: "mq_receive on line %d returned %zd != 1\n" , |
82 | line, rets); |
83 | return 1; |
84 | } |
85 | return 0; |
86 | } |
87 | |
88 | struct thr_data |
89 | { |
90 | const char *name; |
91 | pthread_barrier_t *b3; |
92 | mqd_t q; |
93 | }; |
94 | |
95 | static void * |
96 | thr (void *arg) |
97 | { |
98 | pthread_barrier_t *b3 = ((struct thr_data *)arg)->b3; |
99 | mqd_t q = ((struct thr_data *)arg)->q; |
100 | const char *name = ((struct thr_data *)arg)->name; |
101 | int result = 0; |
102 | |
103 | result |= mqrecv (q); |
104 | |
105 | (void) pthread_barrier_wait (barrier: b3); |
106 | |
107 | /* Child verifies SIGRTMIN has not been sent. */ |
108 | |
109 | (void) pthread_barrier_wait (barrier: b3); |
110 | |
111 | /* Parent calls mqsend (q), which should trigger notification. */ |
112 | |
113 | (void) pthread_barrier_wait (barrier: b3); |
114 | |
115 | if (rtmin_cnt != 2) |
116 | { |
117 | puts (s: "SIGRTMIN signal in thread did not arrive" ); |
118 | result = 1; |
119 | } |
120 | else if (rtmin_pid != getppid () |
121 | || rtmin_uid != getuid () |
122 | || rtmin_code != SI_MESGQ |
123 | || rtmin_sigval.sival_int != 0xdeadbeef) |
124 | { |
125 | printf (format: "unexpected siginfo_t fields: pid %u (%u), uid %u (%u), code %d (%d), si_int %d (%d)\n" , |
126 | rtmin_pid, getppid (), rtmin_uid, getuid (), |
127 | rtmin_code, SI_MESGQ, rtmin_sigval.sival_int, 0xdeadbeef); |
128 | result = 1; |
129 | } |
130 | |
131 | struct sigevent ev; |
132 | memset (&ev, 0x82, sizeof (ev)); |
133 | ev.sigev_notify = SIGEV_NONE; |
134 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
135 | { |
136 | printf (format: "mq_notify in thread (q, { SIGEV_NONE }) failed with: %m\n" ); |
137 | result = 1; |
138 | } |
139 | |
140 | if (mq_notify (mqdes: q, NULL) != 0) |
141 | { |
142 | printf (format: "mq_notify in thread (q, NULL) failed with: %m\n" ); |
143 | result = 1; |
144 | } |
145 | |
146 | result |= mqrecv (q); |
147 | |
148 | (void) pthread_barrier_wait (barrier: b3); |
149 | |
150 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
151 | |
152 | (void) pthread_barrier_wait (barrier: b3); |
153 | |
154 | if (mq_notify (mqdes: q, NULL) != 0) |
155 | { |
156 | printf (format: "second mq_notify in thread (q, NULL) failed with: %m\n" ); |
157 | result = 1; |
158 | } |
159 | |
160 | (void) pthread_barrier_wait (barrier: b3); |
161 | |
162 | /* Parent calls mqsend (q), which should not trigger notification. */ |
163 | |
164 | (void) pthread_barrier_wait (barrier: b3); |
165 | |
166 | /* Child verifies SIGRTMIN has not been received. */ |
167 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
168 | |
169 | (void) pthread_barrier_wait (barrier: b3); |
170 | |
171 | mqd_t q4 = mq_open (name: name, O_RDONLY); |
172 | if (q4 == (mqd_t) -1) |
173 | { |
174 | printf (format: "mq_open in thread failed with: %m\n" ); |
175 | result = 1; |
176 | } |
177 | |
178 | if (mq_notify (mqdes: q4, NULL) != 0) |
179 | { |
180 | printf (format: "mq_notify in thread (q4, NULL) failed with: %m\n" ); |
181 | result = 1; |
182 | } |
183 | |
184 | if (mq_close (mqdes: q4) != 0) |
185 | { |
186 | printf (format: "mq_close in thread failed with: %m\n" ); |
187 | result = 1; |
188 | } |
189 | |
190 | (void) pthread_barrier_wait (barrier: b3); |
191 | |
192 | /* Parent calls mqsend (q), which should not trigger notification. */ |
193 | |
194 | (void) pthread_barrier_wait (barrier: b3); |
195 | |
196 | /* Child verifies SIGRTMIN has not been received. */ |
197 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
198 | |
199 | (void) pthread_barrier_wait (barrier: b3); |
200 | |
201 | mqd_t q5 = mq_open (name: name, O_WRONLY); |
202 | if (q5 == (mqd_t) -1) |
203 | { |
204 | printf (format: "mq_open O_WRONLY in thread failed with: %m\n" ); |
205 | result = 1; |
206 | } |
207 | |
208 | if (mq_notify (mqdes: q5, NULL) != 0) |
209 | { |
210 | printf (format: "mq_notify in thread (q5, NULL) failed with: %m\n" ); |
211 | result = 1; |
212 | } |
213 | |
214 | if (mq_close (mqdes: q5) != 0) |
215 | { |
216 | printf (format: "mq_close in thread failed with: %m\n" ); |
217 | result = 1; |
218 | } |
219 | |
220 | (void) pthread_barrier_wait (barrier: b3); |
221 | |
222 | /* Parent calls mqsend (q), which should not trigger notification. */ |
223 | |
224 | (void) pthread_barrier_wait (barrier: b3); |
225 | |
226 | /* Child verifies SIGRTMIN has not been received. */ |
227 | |
228 | return (void *) (long) result; |
229 | } |
230 | |
231 | static void |
232 | do_child (const char *name, pthread_barrier_t *b2, pthread_barrier_t *b3, |
233 | mqd_t q) |
234 | { |
235 | int result = 0; |
236 | |
237 | struct sigevent ev; |
238 | memset (&ev, 0x55, sizeof (ev)); |
239 | ev.sigev_notify = SIGEV_SIGNAL; |
240 | ev.sigev_signo = SIGRTMIN; |
241 | ev.sigev_value.sival_ptr = &ev; |
242 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
243 | { |
244 | puts (s: "first mq_notify in child (q, { SIGEV_SIGNAL }) unexpectedly succeeded" ); |
245 | result = 1; |
246 | } |
247 | else if (errno != EBUSY) |
248 | { |
249 | printf (format: "first mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
250 | result = 1; |
251 | } |
252 | |
253 | (void) pthread_barrier_wait (barrier: b2); |
254 | |
255 | /* Parent calls mqsend (q), which makes notification available. */ |
256 | |
257 | (void) pthread_barrier_wait (barrier: b2); |
258 | |
259 | rtmin_cnt = 0; |
260 | |
261 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
262 | { |
263 | printf (format: "second mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
264 | result = 1; |
265 | } |
266 | |
267 | if (rtmin_cnt != 0) |
268 | { |
269 | puts (s: "SIGRTMIN signal in child caught too early" ); |
270 | result = 1; |
271 | } |
272 | |
273 | (void) pthread_barrier_wait (barrier: b2); |
274 | |
275 | /* Parent unsuccessfully attempts to mq_notify. */ |
276 | /* Parent calls mqsend (q), which makes notification available |
277 | and triggers a signal in the child. */ |
278 | /* Parent successfully calls mq_notify SIGEV_SIGNAL. */ |
279 | |
280 | (void) pthread_barrier_wait (barrier: b2); |
281 | |
282 | if (rtmin_cnt != 1) |
283 | { |
284 | puts (s: "SIGRTMIN signal in child did not arrive" ); |
285 | result = 1; |
286 | } |
287 | else if (rtmin_pid != getppid () |
288 | || rtmin_uid != getuid () |
289 | || rtmin_code != SI_MESGQ |
290 | || rtmin_sigval.sival_ptr != &ev) |
291 | { |
292 | printf (format: "unexpected siginfo_t fields: pid %u (%u), uid %u (%u), code %d (%d), si_ptr %p (%p)\n" , |
293 | rtmin_pid, getppid (), rtmin_uid, getuid (), |
294 | rtmin_code, SI_MESGQ, rtmin_sigval.sival_ptr, &ev); |
295 | result = 1; |
296 | } |
297 | |
298 | result |= mqsend (q); |
299 | |
300 | (void) pthread_barrier_wait (barrier: b2); |
301 | |
302 | /* Parent verifies caught SIGRTMIN. */ |
303 | |
304 | mqd_t q2 = mq_open (name: name, O_RDWR); |
305 | if (q2 == (mqd_t) -1) |
306 | { |
307 | printf (format: "mq_open in child failed with: %m\n" ); |
308 | result = 1; |
309 | } |
310 | |
311 | (void) pthread_barrier_wait (barrier: b2); |
312 | |
313 | /* Parent mq_open's another mqd_t for the same queue (q3). */ |
314 | |
315 | memset (&ev, 0x11, sizeof (ev)); |
316 | ev.sigev_notify = SIGEV_SIGNAL; |
317 | ev.sigev_signo = SIGRTMIN; |
318 | ev.sigev_value.sival_ptr = &ev; |
319 | if (mq_notify (mqdes: q2, notification: &ev) != 0) |
320 | { |
321 | printf (format: "mq_notify in child (q2, { SIGEV_SIGNAL }) failed with: %m\n" ); |
322 | result = 1; |
323 | } |
324 | |
325 | (void) pthread_barrier_wait (barrier: b2); |
326 | |
327 | /* Parent unsuccessfully attempts to mq_notify { SIGEV_NONE } on q. */ |
328 | |
329 | (void) pthread_barrier_wait (barrier: b2); |
330 | |
331 | if (mq_close (mqdes: q2) != 0) |
332 | { |
333 | printf (format: "mq_close failed: %m\n" ); |
334 | result = 1; |
335 | } |
336 | |
337 | (void) pthread_barrier_wait (barrier: b2); |
338 | |
339 | /* Parent successfully calls mq_notify { SIGEV_NONE } on q3. */ |
340 | |
341 | (void) pthread_barrier_wait (barrier: b2); |
342 | |
343 | memset (&ev, 0xbb, sizeof (ev)); |
344 | ev.sigev_notify = SIGEV_SIGNAL; |
345 | ev.sigev_signo = SIGRTMIN; |
346 | ev.sigev_value.sival_ptr = &b2; |
347 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
348 | { |
349 | puts (s: "third mq_notify in child (q, { SIGEV_SIGNAL }) unexpectedly succeeded" ); |
350 | result = 1; |
351 | } |
352 | else if (errno != EBUSY) |
353 | { |
354 | printf (format: "third mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
355 | result = 1; |
356 | } |
357 | |
358 | (void) pthread_barrier_wait (barrier: b2); |
359 | |
360 | /* Parent calls mq_close on q3, which makes the queue available again for |
361 | notification. */ |
362 | |
363 | (void) pthread_barrier_wait (barrier: b2); |
364 | |
365 | memset (&ev, 0x13, sizeof (ev)); |
366 | ev.sigev_notify = SIGEV_NONE; |
367 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
368 | { |
369 | printf (format: "mq_notify in child (q, { SIGEV_NONE }) failed with: %m\n" ); |
370 | result = 1; |
371 | } |
372 | |
373 | if (mq_notify (mqdes: q, NULL) != 0) |
374 | { |
375 | printf (format: "mq_notify in child (q, NULL) failed with: %m\n" ); |
376 | result = 1; |
377 | } |
378 | |
379 | (void) pthread_barrier_wait (barrier: b2); |
380 | |
381 | struct thr_data thr_data = { .name = name, .b3 = b3, .q = q }; |
382 | pthread_t th; |
383 | int ret = pthread_create (newthread: &th, NULL, start_routine: thr, arg: &thr_data); |
384 | if (ret) |
385 | { |
386 | errno = ret; |
387 | printf (format: "pthread_created failed with: %m\n" ); |
388 | result = 1; |
389 | } |
390 | |
391 | /* Wait till thr calls mq_receive on the empty queue q and blocks on it. */ |
392 | sleep (seconds: 1); |
393 | |
394 | memset (&ev, 0x5f, sizeof (ev)); |
395 | ev.sigev_notify = SIGEV_SIGNAL; |
396 | ev.sigev_signo = SIGRTMIN; |
397 | ev.sigev_value.sival_int = 0xdeadbeef; |
398 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
399 | { |
400 | printf (format: "fourth mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
401 | result = 1; |
402 | } |
403 | |
404 | /* Ensure the thr thread gets the signal, not us. */ |
405 | sigset_t set; |
406 | sigemptyset (&set); |
407 | sigaddset (&set, SIGRTMIN); |
408 | if (pthread_sigmask (SIG_BLOCK, newmask: &set, NULL)) |
409 | { |
410 | printf (format: "Failed to block SIGRTMIN in child: %m\n" ); |
411 | result = 1; |
412 | } |
413 | |
414 | (void) pthread_barrier_wait (barrier: b2); |
415 | |
416 | /* Parent calls mqsend (q), which should wake up mqrecv (q) |
417 | in the thread but no notification should be sent. */ |
418 | |
419 | (void) pthread_barrier_wait (barrier: b3); |
420 | |
421 | if (rtmin_cnt != 1) |
422 | { |
423 | puts (s: "SIGRTMIN signal caught while thr was blocked on mq_receive" ); |
424 | result = 1; |
425 | } |
426 | |
427 | (void) pthread_barrier_wait (barrier: b3); |
428 | |
429 | /* Parent calls mqsend (q), which should trigger notification. */ |
430 | |
431 | (void) pthread_barrier_wait (barrier: b3); |
432 | |
433 | /* Thread verifies SIGRTMIN has been received. */ |
434 | /* Thread calls mq_notify (q, { SIGEV_NONE }) to verify notification is now |
435 | available for registration. */ |
436 | /* Thread calls mq_notify (q, NULL). */ |
437 | |
438 | (void) pthread_barrier_wait (barrier: b3); |
439 | |
440 | memset (&ev, 0x6a, sizeof (ev)); |
441 | ev.sigev_notify = SIGEV_SIGNAL; |
442 | ev.sigev_signo = SIGRTMIN; |
443 | ev.sigev_value.sival_ptr = do_child; |
444 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
445 | { |
446 | printf (format: "fifth mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
447 | result = 1; |
448 | } |
449 | |
450 | (void) pthread_barrier_wait (barrier: b3); |
451 | |
452 | /* Thread calls mq_notify (q, NULL), which should unregister the above |
453 | notification. */ |
454 | |
455 | (void) pthread_barrier_wait (barrier: b3); |
456 | |
457 | /* Parent calls mqsend (q), which should not trigger notification. */ |
458 | |
459 | (void) pthread_barrier_wait (barrier: b3); |
460 | |
461 | if (rtmin_cnt != 2) |
462 | { |
463 | puts (s: "SIGRTMIN signal caught while notification has been disabled" ); |
464 | result = 1; |
465 | } |
466 | |
467 | memset (&ev, 0x7b, sizeof (ev)); |
468 | ev.sigev_notify = SIGEV_SIGNAL; |
469 | ev.sigev_signo = SIGRTMIN; |
470 | ev.sigev_value.sival_ptr = thr; |
471 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
472 | { |
473 | printf (format: "sixth mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
474 | result = 1; |
475 | } |
476 | |
477 | (void) pthread_barrier_wait (barrier: b3); |
478 | |
479 | /* Thread opens a new O_RDONLY mqd_t (q4). */ |
480 | /* Thread calls mq_notify (q4, NULL), which should unregister the above |
481 | notification. */ |
482 | /* Thread calls mq_close (q4). */ |
483 | |
484 | (void) pthread_barrier_wait (barrier: b3); |
485 | |
486 | /* Parent calls mqsend (q), which should not trigger notification. */ |
487 | |
488 | (void) pthread_barrier_wait (barrier: b3); |
489 | |
490 | if (rtmin_cnt != 2) |
491 | { |
492 | puts (s: "SIGRTMIN signal caught while notification has been disabled" ); |
493 | result = 1; |
494 | } |
495 | |
496 | memset (&ev, 0xe1, sizeof (ev)); |
497 | ev.sigev_notify = SIGEV_SIGNAL; |
498 | ev.sigev_signo = SIGRTMIN; |
499 | ev.sigev_value.sival_int = 127; |
500 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
501 | { |
502 | printf (format: "seventh mq_notify in child (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
503 | result = 1; |
504 | } |
505 | |
506 | (void) pthread_barrier_wait (barrier: b3); |
507 | |
508 | /* Thread opens a new O_WRONLY mqd_t (q5). */ |
509 | /* Thread calls mq_notify (q5, NULL), which should unregister the above |
510 | notification. */ |
511 | /* Thread calls mq_close (q5). */ |
512 | |
513 | (void) pthread_barrier_wait (barrier: b3); |
514 | |
515 | /* Parent calls mqsend (q), which should not trigger notification. */ |
516 | |
517 | (void) pthread_barrier_wait (barrier: b3); |
518 | |
519 | if (rtmin_cnt != 2) |
520 | { |
521 | puts (s: "SIGRTMIN signal caught while notification has been disabled" ); |
522 | result = 1; |
523 | } |
524 | |
525 | /* Reenable test signals before cleaning up the thread. */ |
526 | if (pthread_sigmask (SIG_UNBLOCK, newmask: &set, NULL)) |
527 | { |
528 | printf (format: "Failed to unblock SIGRTMIN in child: %m\n" ); |
529 | result = 1; |
530 | } |
531 | |
532 | void *thr_ret; |
533 | ret = pthread_join (th: th, thread_return: &thr_ret); |
534 | if (ret) |
535 | { |
536 | errno = ret; |
537 | printf (format: "pthread_join failed: %m\n" ); |
538 | result = 1; |
539 | } |
540 | else if (thr_ret) |
541 | result = 1; |
542 | |
543 | if (mq_close (mqdes: q) != 0) |
544 | { |
545 | printf (format: "mq_close failed: %m\n" ); |
546 | result = 1; |
547 | } |
548 | |
549 | exit (result); |
550 | } |
551 | |
552 | #define TEST_FUNCTION do_test () |
553 | static int |
554 | do_test (void) |
555 | { |
556 | int result = 0; |
557 | |
558 | char tmpfname[] = "/tmp/tst-mqueue5-barrier.XXXXXX" ; |
559 | int fd = mkstemp (template: tmpfname); |
560 | if (fd == -1) |
561 | { |
562 | printf (format: "cannot open temporary file: %m\n" ); |
563 | return 1; |
564 | } |
565 | |
566 | /* Make sure it is always removed. */ |
567 | unlink (name: tmpfname); |
568 | |
569 | /* Create one page of data. */ |
570 | size_t ps = sysconf (_SC_PAGESIZE); |
571 | char data[ps]; |
572 | memset (data, '\0', ps); |
573 | |
574 | /* Write the data to the file. */ |
575 | if (write (fd, data, ps) != (ssize_t) ps) |
576 | { |
577 | puts (s: "short write" ); |
578 | return 1; |
579 | } |
580 | |
581 | void *mem = mmap (NULL, len: ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0); |
582 | if (mem == MAP_FAILED) |
583 | { |
584 | printf (format: "mmap failed: %m\n" ); |
585 | return 1; |
586 | } |
587 | |
588 | pthread_barrier_t *b2; |
589 | b2 = (pthread_barrier_t *) (((uintptr_t) mem + __alignof (pthread_barrier_t)) |
590 | & ~(__alignof (pthread_barrier_t) - 1)); |
591 | |
592 | pthread_barrier_t *b3; |
593 | b3 = b2 + 1; |
594 | |
595 | pthread_barrierattr_t a; |
596 | if (pthread_barrierattr_init (attr: &a) != 0) |
597 | { |
598 | puts (s: "barrierattr_init failed" ); |
599 | return 1; |
600 | } |
601 | |
602 | if (pthread_barrierattr_setpshared (attr: &a, PTHREAD_PROCESS_SHARED) != 0) |
603 | { |
604 | puts (s: "barrierattr_setpshared failed, could not test" ); |
605 | return 0; |
606 | } |
607 | |
608 | if (pthread_barrier_init (barrier: b2, attr: &a, count: 2) != 0) |
609 | { |
610 | puts (s: "barrier_init failed" ); |
611 | return 1; |
612 | } |
613 | |
614 | if (pthread_barrier_init (barrier: b3, attr: &a, count: 3) != 0) |
615 | { |
616 | puts (s: "barrier_init failed" ); |
617 | return 1; |
618 | } |
619 | |
620 | if (pthread_barrierattr_destroy (attr: &a) != 0) |
621 | { |
622 | puts (s: "barrierattr_destroy failed" ); |
623 | return 1; |
624 | } |
625 | |
626 | char name[sizeof "/tst-mqueue5-" + sizeof (pid_t) * 3]; |
627 | snprintf (s: name, maxlen: sizeof (name), format: "/tst-mqueue5-%u" , getpid ()); |
628 | |
629 | struct mq_attr attr = { .mq_maxmsg = 1, .mq_msgsize = 1 }; |
630 | mqd_t q = mq_open (name: name, O_CREAT | O_EXCL | O_RDWR, 0600, &attr); |
631 | |
632 | if (q == (mqd_t) -1) |
633 | { |
634 | if (errno == ENOSYS) |
635 | FAIL_UNSUPPORTED ("mq_open not supported" ); |
636 | |
637 | printf (format: "mq_open failed with: %m\n" ); |
638 | return 1; |
639 | } |
640 | |
641 | add_temp_mq (name); |
642 | |
643 | struct sigevent ev; |
644 | memset (&ev, 0xaa, sizeof (ev)); |
645 | ev.sigev_notify = SIGEV_NONE; |
646 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
647 | { |
648 | printf (format: "mq_notify (q, { SIGEV_NONE }) failed with: %m\n" ); |
649 | result = 1; |
650 | } |
651 | |
652 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
653 | { |
654 | puts (s: "second mq_notify (q, { SIGEV_NONE }) unexpectedly succeeded" ); |
655 | result = 1; |
656 | } |
657 | else if (errno != EBUSY) |
658 | { |
659 | printf (format: "second mq_notify (q, { SIGEV_NONE }) failed with: %m\n" ); |
660 | result = 1; |
661 | } |
662 | |
663 | result |= mqsend (q); |
664 | |
665 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
666 | { |
667 | printf (format: "third mq_notify (q, { SIGEV_NONE }) failed with: %m\n" ); |
668 | result = 1; |
669 | } |
670 | |
671 | result |= mqrecv (q); |
672 | |
673 | if (mq_notify (mqdes: q, NULL) != 0) |
674 | { |
675 | printf (format: "mq_notify (q, NULL) failed with: %m\n" ); |
676 | result = 1; |
677 | } |
678 | |
679 | if (mq_notify (mqdes: q, NULL) != 0) |
680 | { |
681 | /* Implementation-defined behaviour, so don't fail, |
682 | just inform. */ |
683 | printf (format: "second mq_notify (q, NULL) failed with: %m\n" ); |
684 | } |
685 | |
686 | struct sigaction sa = { .sa_sigaction = rtmin_handler, |
687 | .sa_flags = SA_SIGINFO }; |
688 | sigemptyset (&sa.sa_mask); |
689 | sigaction (SIGRTMIN, act: &sa, NULL); |
690 | |
691 | memset (&ev, 0x55, sizeof (ev)); |
692 | ev.sigev_notify = SIGEV_SIGNAL; |
693 | ev.sigev_signo = SIGRTMIN; |
694 | ev.sigev_value.sival_int = 26; |
695 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
696 | { |
697 | printf (format: "mq_notify (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
698 | result = 1; |
699 | } |
700 | |
701 | ev.sigev_value.sival_ptr = &ev; |
702 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
703 | { |
704 | puts (s: "second mq_notify (q, { SIGEV_SIGNAL }) unexpectedly succeeded" ); |
705 | result = 1; |
706 | } |
707 | else if (errno != EBUSY) |
708 | { |
709 | printf (format: "second mq_notify (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
710 | result = 1; |
711 | } |
712 | |
713 | if (rtmin_cnt != 0) |
714 | { |
715 | puts (s: "SIGRTMIN signal caught too early" ); |
716 | result = 1; |
717 | } |
718 | |
719 | result |= mqsend (q); |
720 | |
721 | if (rtmin_cnt != 1) |
722 | { |
723 | puts (s: "SIGRTMIN signal did not arrive" ); |
724 | result = 1; |
725 | } |
726 | else if (rtmin_pid != getpid () |
727 | || rtmin_uid != getuid () |
728 | || rtmin_code != SI_MESGQ |
729 | || rtmin_sigval.sival_int != 26) |
730 | { |
731 | printf (format: "unexpected siginfo_t fields: pid %u (%u), uid %u (%u), code %d (%d), si_int %d (26)\n" , |
732 | rtmin_pid, getpid (), rtmin_uid, getuid (), |
733 | rtmin_code, SI_MESGQ, rtmin_sigval.sival_int); |
734 | result = 1; |
735 | } |
736 | |
737 | ev.sigev_value.sival_int = 75; |
738 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
739 | { |
740 | printf (format: "third mq_notify (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
741 | result = 1; |
742 | } |
743 | |
744 | result |= mqrecv (q); |
745 | |
746 | if (mq_notify (mqdes: q, NULL) != 0) |
747 | { |
748 | printf (format: "mq_notify (q, NULL) failed with: %m\n" ); |
749 | result = 1; |
750 | } |
751 | |
752 | memset (&ev, 0x33, sizeof (ev)); |
753 | ev.sigev_notify = SIGEV_NONE; |
754 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
755 | { |
756 | printf (format: "fourth mq_notify (q, { SIGEV_NONE }) failed with: %m\n" ); |
757 | result = 1; |
758 | } |
759 | |
760 | pid_t pid = fork (); |
761 | if (pid == -1) |
762 | { |
763 | printf (format: "fork () failed: %m\n" ); |
764 | mq_unlink (name: name); |
765 | return 1; |
766 | } |
767 | |
768 | if (pid == 0) |
769 | do_child (name, b2, b3, q); |
770 | |
771 | /* Child unsuccessfully attempts to mq_notify. */ |
772 | |
773 | (void) pthread_barrier_wait (barrier: b2); |
774 | |
775 | result |= mqsend (q); |
776 | |
777 | (void) pthread_barrier_wait (barrier: b2); |
778 | |
779 | /* Child successfully calls mq_notify SIGEV_SIGNAL now. */ |
780 | |
781 | result |= mqrecv (q); |
782 | |
783 | (void) pthread_barrier_wait (barrier: b2); |
784 | |
785 | memset (&ev, 0xbb, sizeof (ev)); |
786 | ev.sigev_notify = SIGEV_SIGNAL; |
787 | ev.sigev_signo = SIGRTMIN; |
788 | ev.sigev_value.sival_int = 15; |
789 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
790 | { |
791 | puts (s: "fourth mq_notify (q, { SIGEV_SIGNAL }) unexpectedly succeeded" ); |
792 | result = 1; |
793 | } |
794 | else if (errno != EBUSY) |
795 | { |
796 | printf (format: "fourth mq_notify (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
797 | result = 1; |
798 | } |
799 | |
800 | result |= mqsend (q); |
801 | |
802 | if (mq_notify (mqdes: q, notification: &ev) != 0) |
803 | { |
804 | printf (format: "fifth mq_notify (q, { SIGEV_SIGNAL }) failed with: %m\n" ); |
805 | result = 1; |
806 | } |
807 | |
808 | if (rtmin_cnt != 1) |
809 | { |
810 | puts (s: "SIGRTMIN signal caught too early" ); |
811 | result = 1; |
812 | } |
813 | |
814 | result |= mqrecv (q); |
815 | |
816 | (void) pthread_barrier_wait (barrier: b2); |
817 | |
818 | /* Child verifies caught SIGRTMIN signal. */ |
819 | /* Child calls mq_send (q) which triggers SIGRTMIN signal here. */ |
820 | |
821 | (void) pthread_barrier_wait (barrier: b2); |
822 | |
823 | /* Child mq_open's another mqd_t for the same queue (q2). */ |
824 | |
825 | if (rtmin_cnt != 2) |
826 | { |
827 | puts (s: "SIGRTMIN signal did not arrive" ); |
828 | result = 1; |
829 | } |
830 | else if (rtmin_pid != pid |
831 | || rtmin_uid != getuid () |
832 | || rtmin_code != SI_MESGQ |
833 | || rtmin_sigval.sival_int != 15) |
834 | { |
835 | printf (format: "unexpected siginfo_t fields: pid %u (%u), uid %u (%u), code %d (%d), si_int %d (15)\n" , |
836 | rtmin_pid, pid, rtmin_uid, getuid (), |
837 | rtmin_code, SI_MESGQ, rtmin_sigval.sival_int); |
838 | result = 1; |
839 | } |
840 | |
841 | result |= mqrecv (q); |
842 | |
843 | (void) pthread_barrier_wait (barrier: b2); |
844 | |
845 | /* Child successfully calls mq_notify { SIGEV_SIGNAL } on q2. */ |
846 | |
847 | (void) pthread_barrier_wait (barrier: b2); |
848 | |
849 | memset (&ev, 0xbb, sizeof (ev)); |
850 | ev.sigev_notify = SIGEV_NONE; |
851 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
852 | { |
853 | puts (s: "fifth mq_notify (q, { SIGEV_NONE }) unexpectedly succeeded" ); |
854 | result = 1; |
855 | } |
856 | else if (errno != EBUSY) |
857 | { |
858 | printf (format: "fifth mq_notify (q, { SIGEV_NONE }) failed with: %m\n" ); |
859 | result = 1; |
860 | } |
861 | |
862 | (void) pthread_barrier_wait (barrier: b2); |
863 | |
864 | /* Child calls mq_close on q2, which makes the queue available again for |
865 | notification. */ |
866 | |
867 | mqd_t q3 = mq_open (name: name, O_RDWR); |
868 | if (q3 == (mqd_t) -1) |
869 | { |
870 | printf (format: "mq_open q3 in parent failed with: %m\n" ); |
871 | result = 1; |
872 | } |
873 | |
874 | (void) pthread_barrier_wait (barrier: b2); |
875 | |
876 | memset (&ev, 0x12, sizeof (ev)); |
877 | ev.sigev_notify = SIGEV_NONE; |
878 | if (mq_notify (mqdes: q3, notification: &ev) != 0) |
879 | { |
880 | printf (format: "mq_notify (q3, { SIGEV_NONE }) failed with: %m\n" ); |
881 | result = 1; |
882 | } |
883 | |
884 | (void) pthread_barrier_wait (barrier: b2); |
885 | |
886 | /* Child unsuccessfully attempts to mq_notify { SIGEV_SIGNAL } on q. */ |
887 | |
888 | (void) pthread_barrier_wait (barrier: b2); |
889 | |
890 | if (mq_close (mqdes: q3) != 0) |
891 | { |
892 | printf (format: "mq_close failed: %m\n" ); |
893 | result = 1; |
894 | } |
895 | |
896 | (void) pthread_barrier_wait (barrier: b2); |
897 | |
898 | /* Child successfully calls mq_notify { SIGEV_NONE } on q. */ |
899 | /* Child successfully calls mq_notify NULL on q. */ |
900 | |
901 | (void) pthread_barrier_wait (barrier: b2); |
902 | |
903 | /* Child creates new thread. */ |
904 | /* Thread blocks on mqrecv (q). */ |
905 | /* Child sleeps for 1sec so that thread has time to reach that point. */ |
906 | /* Child successfully calls mq_notify { SIGEV_SIGNAL } on q. */ |
907 | |
908 | (void) pthread_barrier_wait (barrier: b2); |
909 | |
910 | result |= mqsend (q); |
911 | |
912 | (void) pthread_barrier_wait (barrier: b3); |
913 | |
914 | /* Child verifies SIGRTMIN has not been sent. */ |
915 | |
916 | (void) pthread_barrier_wait (barrier: b3); |
917 | |
918 | result |= mqsend (q); |
919 | |
920 | (void) pthread_barrier_wait (barrier: b3); |
921 | |
922 | /* Thread verifies SIGRTMIN has been caught. */ |
923 | /* Thread calls mq_notify (q, { SIGEV_NONE }) to verify notification is now |
924 | available for registration. */ |
925 | /* Thread calls mq_notify (q, NULL). */ |
926 | |
927 | (void) pthread_barrier_wait (barrier: b3); |
928 | |
929 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
930 | |
931 | (void) pthread_barrier_wait (barrier: b3); |
932 | |
933 | /* Thread calls mq_notify (q, NULL). */ |
934 | |
935 | (void) pthread_barrier_wait (barrier: b3); |
936 | |
937 | result |= mqsend (q); |
938 | result |= mqrecv (q); |
939 | |
940 | (void) pthread_barrier_wait (barrier: b3); |
941 | |
942 | /* Child verifies SIGRTMIN has not been sent. */ |
943 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
944 | |
945 | (void) pthread_barrier_wait (barrier: b3); |
946 | |
947 | /* Thread opens a new O_RDONLY mqd_t (q4). */ |
948 | /* Thread calls mq_notify (q4, NULL). */ |
949 | /* Thread calls mq_close (q4). */ |
950 | |
951 | (void) pthread_barrier_wait (barrier: b3); |
952 | |
953 | result |= mqsend (q); |
954 | result |= mqrecv (q); |
955 | |
956 | (void) pthread_barrier_wait (barrier: b3); |
957 | |
958 | /* Child verifies SIGRTMIN has not been sent. */ |
959 | /* Child calls mq_notify (q, { SIGEV_SIGNAL }). */ |
960 | |
961 | (void) pthread_barrier_wait (barrier: b3); |
962 | |
963 | /* Thread opens a new O_WRONLY mqd_t (q5). */ |
964 | /* Thread calls mq_notify (q5, NULL). */ |
965 | /* Thread calls mq_close (q5). */ |
966 | |
967 | (void) pthread_barrier_wait (barrier: b3); |
968 | |
969 | result |= mqsend (q); |
970 | result |= mqrecv (q); |
971 | |
972 | (void) pthread_barrier_wait (barrier: b3); |
973 | |
974 | /* Child verifies SIGRTMIN has not been sent. */ |
975 | |
976 | int status; |
977 | if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) |
978 | { |
979 | puts (s: "waitpid failed" ); |
980 | kill (pid: pid, SIGKILL); |
981 | result = 1; |
982 | } |
983 | else if (!WIFEXITED (status) || WEXITSTATUS (status)) |
984 | { |
985 | printf (format: "child failed with status %d\n" , status); |
986 | result = 1; |
987 | } |
988 | |
989 | if (mq_unlink (name: name) != 0) |
990 | { |
991 | printf (format: "mq_unlink failed: %m\n" ); |
992 | result = 1; |
993 | } |
994 | |
995 | if (mq_close (mqdes: q) != 0) |
996 | { |
997 | printf (format: "mq_close failed: %m\n" ); |
998 | result = 1; |
999 | } |
1000 | |
1001 | if (mq_notify (mqdes: q, NULL) == 0) |
1002 | { |
1003 | puts (s: "mq_notify on closed mqd_t unexpectedly succeeded" ); |
1004 | result = 1; |
1005 | } |
1006 | else if (errno != EBADF) |
1007 | { |
1008 | printf (format: "mq_notify on closed mqd_t did not fail with EBADF: %m\n" ); |
1009 | result = 1; |
1010 | } |
1011 | |
1012 | memset (&ev, 0x55, sizeof (ev)); |
1013 | ev.sigev_notify = SIGEV_NONE; |
1014 | if (mq_notify (mqdes: q, notification: &ev) == 0) |
1015 | { |
1016 | puts (s: "mq_notify on closed mqd_t unexpectedly succeeded" ); |
1017 | result = 1; |
1018 | } |
1019 | else if (errno != EBADF) |
1020 | { |
1021 | printf (format: "mq_notify on closed mqd_t did not fail with EBADF: %m\n" ); |
1022 | result = 1; |
1023 | } |
1024 | |
1025 | return result; |
1026 | } |
1027 | #else |
1028 | # define TEST_FUNCTION 0 |
1029 | #endif |
1030 | |
1031 | #include "../test-skeleton.c" |
1032 | |