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
37 # include <pthread.h>
38 
39 # define mqsend(q) (mqsend) (q, __LINE__)
40 static int
41 (mqsend) (mqd_t q, int line)
42 {
43   char c;
44   if (mq_send (q, &c, 1, 1) != 0)
45     {
46       printf ("mq_send on line %d failed with: %m\n", line);
47       return 1;
48     }
49   return 0;
50 }
51 
52 # define mqrecv(q) (mqrecv) (q, __LINE__)
53 static int
54 (mqrecv) (mqd_t q, int line)
55 {
56   char c;
57   ssize_t rets = TEMP_FAILURE_RETRY (mq_receive (q, &c, 1, NULL));
58   if (rets != 1)
59     {
60       if (rets == -1)
61 	printf ("mq_receive on line %d failed with: %m\n", line);
62       else
63 	printf ("mq_receive on line %d returned %zd != 1\n",
64 		line, rets);
65       return 1;
66     }
67   return 0;
68 }
69 
70 volatile int fct_cnt, fct_err;
71 size_t fct_guardsize;
72 
73 static void
fct(union sigval s)74 fct (union sigval s)
75 {
76   mqd_t q = *(mqd_t *) s.sival_ptr;
77 
78   pthread_attr_t nattr;
79   int ret = pthread_getattr_np (pthread_self (), &nattr);
80   if (ret)
81     {
82       errno = ret;
83       printf ("pthread_getattr_np failed: %m\n");
84       fct_err = 1;
85     }
86   else
87     {
88       ret = pthread_attr_getguardsize (&nattr, &fct_guardsize);
89       if (ret)
90 	{
91 	  errno = ret;
92 	  printf ("pthread_attr_getguardsize failed: %m\n");
93 	  fct_err = 1;
94 	}
95       if (pthread_attr_destroy (&nattr) != 0)
96 	{
97 	  puts ("pthread_attr_destroy failed");
98 	  fct_err = 1;
99 	}
100     }
101 
102   ++fct_cnt;
103   fct_err |= mqsend (q);
104 }
105 
106 # define TEST_FUNCTION do_test ()
107 static int
do_test(void)108 do_test (void)
109 {
110   int result = 0;
111 
112   char name[sizeof "/tst-mqueue6-" + sizeof (pid_t) * 3];
113   snprintf (name, sizeof (name), "/tst-mqueue6-%u", getpid ());
114 
115   struct mq_attr attr = { .mq_maxmsg = 1, .mq_msgsize = 1 };
116   mqd_t q = mq_open (name, O_CREAT | O_EXCL | O_RDWR, 0600, &attr);
117 
118   if (q == (mqd_t) -1)
119     {
120       if (errno == ENOSYS)
121 	FAIL_UNSUPPORTED ("mq_open not supported");
122 
123       printf ("mq_open failed with: %m\n");
124       return 1;
125     }
126 
127   add_temp_mq (name);
128 
129   pthread_attr_t nattr;
130   if (pthread_attr_init (&nattr)
131       || pthread_attr_setguardsize (&nattr, 0))
132     {
133       puts ("pthread_attr_t setup failed");
134       result = 1;
135     }
136 
137   fct_guardsize = 1;
138 
139   struct sigevent ev;
140   memset (&ev, 0xaa, sizeof (ev));
141   ev.sigev_notify = SIGEV_THREAD;
142   ev.sigev_notify_function = fct;
143   ev.sigev_notify_attributes = &nattr;
144   ev.sigev_value.sival_ptr = &q;
145   if (mq_notify (q, &ev) != 0)
146     {
147       printf ("mq_notify (q, { SIGEV_THREAD }) failed with: %m\n");
148       result = 1;
149     }
150 
151   size_t ps = sysconf (_SC_PAGESIZE);
152   if (pthread_attr_setguardsize (&nattr, 32 * ps))
153     {
154       puts ("pthread_attr_t setup failed");
155       result = 1;
156     }
157 
158   if (mq_notify (q, &ev) == 0)
159     {
160       puts ("second mq_notify (q, { SIGEV_NONE }) unexpectedly succeeded");
161       result = 1;
162     }
163   else if (errno != EBUSY)
164     {
165       printf ("second mq_notify (q, { SIGEV_NONE }) failed with: %m\n");
166       result = 1;
167     }
168 
169   if (fct_cnt != 0)
170     {
171       printf ("fct called too early (%d on %d)\n", fct_cnt, __LINE__);
172       result = 1;
173     }
174 
175   result |= mqsend (q);
176 
177   result |= mqrecv (q);
178   result |= mqrecv (q);
179 
180   if (fct_cnt != 1)
181     {
182       printf ("fct not called (%d on %d)\n", fct_cnt, __LINE__);
183       result = 1;
184     }
185   else if (fct_guardsize != 0)
186     {
187       printf ("fct_guardsize %zd != 0\n", fct_guardsize);
188       result = 1;
189     }
190 
191   if (mq_notify (q, &ev) != 0)
192     {
193       printf ("third mq_notify (q, { SIGEV_NONE }) failed with: %m\n");
194       result = 1;
195     }
196 
197   if (mq_notify (q, NULL) != 0)
198     {
199       printf ("mq_notify (q, NULL) failed with: %m\n");
200       result = 1;
201     }
202 
203   memset (&ev, 0x11, sizeof (ev));
204   ev.sigev_notify = SIGEV_THREAD;
205   ev.sigev_notify_function = fct;
206   ev.sigev_notify_attributes = &nattr;
207   ev.sigev_value.sival_ptr = &q;
208   if (mq_notify (q, &ev) != 0)
209     {
210       printf ("mq_notify (q, { SIGEV_THREAD }) failed with: %m\n");
211       result = 1;
212     }
213 
214   if (pthread_attr_setguardsize (&nattr, 0))
215     {
216       puts ("pthread_attr_t setup failed");
217       result = 1;
218     }
219 
220   if (mq_notify (q, &ev) == 0)
221     {
222       puts ("second mq_notify (q, { SIGEV_NONE }) unexpectedly succeeded");
223       result = 1;
224     }
225   else if (errno != EBUSY)
226     {
227       printf ("second mq_notify (q, { SIGEV_NONE }) failed with: %m\n");
228       result = 1;
229     }
230 
231   if (fct_cnt != 1)
232     {
233       printf ("fct called too early (%d on %d)\n", fct_cnt, __LINE__);
234       result = 1;
235     }
236 
237   result |= mqsend (q);
238 
239   result |= mqrecv (q);
240   result |= mqrecv (q);
241 
242   if (fct_cnt != 2)
243     {
244       printf ("fct not called (%d on %d)\n", fct_cnt, __LINE__);
245       result = 1;
246     }
247   else if (fct_guardsize != 32 * ps)
248     {
249       printf ("fct_guardsize %zd != %zd\n", fct_guardsize, 32 * ps);
250       result = 1;
251     }
252 
253   if (mq_notify (q, &ev) != 0)
254     {
255       printf ("third mq_notify (q, { SIGEV_NONE }) failed with: %m\n");
256       result = 1;
257     }
258 
259   if (mq_notify (q, NULL) != 0)
260     {
261       printf ("mq_notify (q, NULL) failed with: %m\n");
262       result = 1;
263     }
264 
265   if (pthread_attr_destroy (&nattr) != 0)
266     {
267       puts ("pthread_attr_destroy failed");
268       result = 1;
269     }
270 
271   if (mq_unlink (name) != 0)
272     {
273       printf ("mq_unlink failed: %m\n");
274       result = 1;
275     }
276 
277   if (mq_close (q) != 0)
278     {
279       printf ("mq_close failed: %m\n");
280       result = 1;
281     }
282 
283   memset (&ev, 0x55, sizeof (ev));
284   ev.sigev_notify = SIGEV_THREAD;
285   ev.sigev_notify_function = fct;
286   ev.sigev_notify_attributes = NULL;
287   ev.sigev_value.sival_int = 0;
288   if (mq_notify (q, &ev) == 0)
289     {
290       puts ("mq_notify on closed mqd_t unexpectedly succeeded");
291       result = 1;
292     }
293   else if (errno != EBADF)
294     {
295       printf ("mq_notify on closed mqd_t did not fail with EBADF: %m\n");
296       result = 1;
297     }
298 
299   if (fct_err)
300     result = 1;
301   return result;
302 }
303 #else
304 # define TEST_FUNCTION 0
305 #endif
306 
307 #include "../test-skeleton.c"
308