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