1 /* Test SIGEV_THREAD handling for POSIX message queues.
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 <mqueue.h>
21 #include <signal.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/mman.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include <support/check.h>
31 
32 #if _POSIX_THREADS
33 # include <pthread.h>
34 
35 static pid_t pid;
36 static mqd_t m;
37 static const char message[] = "hello";
38 
39 # define MAXMSG 10
40 # define MSGSIZE 10
41 # define UNIQUE 42
42 
43 
44 static void
fct(union sigval s)45 fct (union sigval s)
46 {
47   /* Put the mq in non-blocking mode.  */
48   struct mq_attr attr;
49   if (mq_getattr (m, &attr) != 0)
50     {
51       printf ("%s: mq_getattr failed: %m\n", __FUNCTION__);
52       exit (1);
53     }
54   attr.mq_flags |= O_NONBLOCK;
55   if (mq_setattr (m, &attr, NULL) != 0)
56     {
57       printf ("%s: mq_setattr failed: %m\n", __FUNCTION__);
58       exit (1);
59     }
60 
61   /* Check the values.  */
62   if (attr.mq_maxmsg != MAXMSG)
63     {
64       printf ("%s: mq_maxmsg wrong: is %jd, expecte %d\n",
65 	      __FUNCTION__, (intmax_t) attr.mq_maxmsg, MAXMSG);
66       exit (1);
67     }
68   if (attr.mq_msgsize != MAXMSG)
69     {
70       printf ("%s: mq_msgsize wrong: is %jd, expecte %d\n",
71 	      __FUNCTION__, (intmax_t) attr.mq_msgsize, MSGSIZE);
72       exit (1);
73     }
74 
75   /* Read the message.  */
76   char buf[attr.mq_msgsize];
77   ssize_t n = TEMP_FAILURE_RETRY (mq_receive (m, buf, attr.mq_msgsize, NULL));
78   if (n != sizeof (message))
79     {
80       printf ("%s: length of message wrong: is %zd, expected %zu\n",
81 	      __FUNCTION__, n, sizeof (message));
82       exit (1);
83     }
84   if (memcmp (buf, message, sizeof (message)) != 0)
85     {
86       printf ("%s: message wrong: is \"%s\", expected \"%s\"\n",
87 	      __FUNCTION__, buf, message);
88       exit (1);
89     }
90 
91   exit (UNIQUE);
92 }
93 
94 
95 int
do_test(void)96 do_test (void)
97 {
98   char tmpfname[] = "/tmp/tst-mqueue3-barrier.XXXXXX";
99   int fd = mkstemp (tmpfname);
100   if (fd == -1)
101     {
102       printf ("cannot open temporary file: %m\n");
103       return 1;
104     }
105 
106   /* Make sure it is always removed.  */
107   unlink (tmpfname);
108 
109   /* Create one page of data.  */
110   size_t ps = sysconf (_SC_PAGESIZE);
111   char data[ps];
112   memset (data, '\0', ps);
113 
114   /* Write the data to the file.  */
115   if (write (fd, data, ps) != (ssize_t) ps)
116     {
117       puts ("short write");
118       return 1;
119     }
120 
121   void *mem = mmap (NULL, ps, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
122   if (mem == MAP_FAILED)
123     {
124       printf ("mmap failed: %m\n");
125       return 1;
126     }
127 
128   pthread_barrier_t *b;
129   b = (pthread_barrier_t *) (((uintptr_t) mem + __alignof (pthread_barrier_t))
130                              & ~(__alignof (pthread_barrier_t) - 1));
131 
132   pthread_barrierattr_t a;
133   if (pthread_barrierattr_init (&a) != 0)
134     {
135       puts ("barrierattr_init failed");
136       return 1;
137     }
138 
139   if (pthread_barrierattr_setpshared (&a, PTHREAD_PROCESS_SHARED) != 0)
140     {
141       puts ("barrierattr_setpshared failed, could not test");
142       return 0;
143     }
144 
145   if (pthread_barrier_init (b, &a, 2) != 0)
146     {
147       puts ("barrier_init failed");
148       return 1;
149     }
150 
151   if (pthread_barrierattr_destroy (&a) != 0)
152     {
153       puts ("barrierattr_destroy failed");
154       return 1;
155     }
156 
157   /* Name for the message queue.  */
158   char mqname[sizeof ("/tst-mqueue3-") + 3 * sizeof (pid_t)];
159   snprintf (mqname, sizeof (mqname) - 1, "/tst-mqueue3-%ld",
160 	    (long int) getpid ());
161 
162   /* Create the message queue.  */
163   struct mq_attr attr = { .mq_maxmsg = MAXMSG, .mq_msgsize = MSGSIZE };
164   m = mq_open (mqname, O_CREAT | O_EXCL | O_RDWR, 0600, &attr);
165 
166   if (m == -1)
167     {
168       if (errno == ENOSYS)
169 	FAIL_UNSUPPORTED ("mq_open not supported");
170 
171       printf ("mq_open failed with: %m\n");
172       return 1;
173     }
174 
175   /* Unlink the message queue right away.  */
176   if (mq_unlink (mqname) != 0)
177     {
178       puts ("mq_unlink failed");
179       return 1;
180     }
181 
182   pid = fork ();
183   if (pid == -1)
184     {
185       puts ("fork failed");
186       return 1;
187     }
188   if (pid == 0)
189     {
190       /* Request notification via thread.  */
191       struct sigevent ev;
192       ev.sigev_notify = SIGEV_THREAD;
193       ev.sigev_notify_function = fct;
194       ev.sigev_value.sival_ptr = NULL;
195       ev.sigev_notify_attributes = NULL;
196 
197       /* Tell the kernel.  */
198       if (mq_notify (m,&ev) != 0)
199 	{
200 	  puts ("mq_notify failed");
201 	  exit (1);
202 	}
203 
204       /* Tell the parent we are ready.  */
205       (void) pthread_barrier_wait (b);
206 
207       /* Make sure the process goes away eventually.  */
208       alarm (10);
209 
210       /* Do nothing forever.  */
211       while (1)
212 	pause ();
213     }
214 
215   /* Wait for the child process to register to notification method.  */
216   (void) pthread_barrier_wait (b);
217 
218   /* Send the message.  */
219   if (mq_send (m, message, sizeof (message), 1) != 0)
220     {
221       kill (pid, SIGKILL);
222       puts ("mq_send failed");
223       return 1;
224     }
225 
226   int r;
227   if (TEMP_FAILURE_RETRY (waitpid (pid, &r, 0)) != pid)
228     {
229       kill (pid, SIGKILL);
230       puts ("waitpid failed");
231       return 1;
232     }
233 
234   return WIFEXITED (r) && WEXITSTATUS (r) == UNIQUE ? 0 : 1;
235 }
236 # define TEST_FUNCTION do_test ()
237 #else
238 # define TEST_FUNCTION 0
239 #endif
240 
241 #include "../test-skeleton.c"
242