1 | /* Test message queue passing. |
2 | Copyright (C) 2004-2024 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 <stdio.h> |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <sys/wait.h> |
26 | #include <time.h> |
27 | #include <unistd.h> |
28 | #include <stdint.h> |
29 | #include <support/check.h> |
30 | #include "tst-mqueue.h" |
31 | |
32 | static int |
33 | intcmp (const void *a, const void *b) |
34 | { |
35 | if (*(unsigned char *)a < *(unsigned char *)b) |
36 | return 1; |
37 | if (*(unsigned char *)a > *(unsigned char *)b) |
38 | return -1; |
39 | return 0; |
40 | } |
41 | |
42 | static int |
43 | check_attrs (struct mq_attr *attr, int nonblock, long cnt) |
44 | { |
45 | int result = 0; |
46 | |
47 | if (attr->mq_maxmsg != 10 || attr->mq_msgsize != 1) |
48 | { |
49 | printf (format: "attributes don't match those passed to mq_open\n" |
50 | "mq_maxmsg %jd, mq_msgsize %jd\n" , |
51 | (intmax_t) attr->mq_maxmsg, (intmax_t) attr->mq_msgsize); |
52 | result = 1; |
53 | } |
54 | |
55 | if ((attr->mq_flags & O_NONBLOCK) != nonblock) |
56 | { |
57 | printf (format: "mq_flags %jx != %x\n" , |
58 | (intmax_t) (attr->mq_flags & O_NONBLOCK), nonblock); |
59 | result = 1; |
60 | } |
61 | |
62 | if (attr->mq_curmsgs != cnt) |
63 | { |
64 | printf (format: "mq_curmsgs %jd != %ld\n" , (intmax_t) attr->mq_curmsgs, cnt); |
65 | result = 1; |
66 | } |
67 | |
68 | return result; |
69 | } |
70 | |
71 | static int |
72 | do_one_test (mqd_t q, const char *name, int nonblock) |
73 | { |
74 | int result = 0; |
75 | |
76 | unsigned char v [] |
77 | = { 0x32, 0x62, 0x22, 0x31, 0x11, 0x73, 0x61, 0x21, 0x72, 0x71, 0x81 }; |
78 | |
79 | struct mq_attr attr; |
80 | memset (&attr, 0xaa, sizeof (attr)); |
81 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
82 | { |
83 | printf (format: "mq_getattr failed: %m\n" ); |
84 | result = 1; |
85 | } |
86 | else |
87 | result |= check_attrs (attr: &attr, nonblock, cnt: 0); |
88 | |
89 | if (mq_receive (mqdes: q, msg_ptr: (char *) &v[0], msg_len: 1, NULL) != -1) |
90 | { |
91 | puts (s: "mq_receive on O_WRONLY mqd_t unexpectedly succeeded" ); |
92 | result = 1; |
93 | } |
94 | else if (errno != EBADF) |
95 | { |
96 | printf (format: "mq_receive on O_WRONLY mqd_t did not fail with EBADF: %m\n" ); |
97 | result = 1; |
98 | } |
99 | |
100 | struct timespec ts; |
101 | if (clock_gettime (CLOCK_REALTIME, tp: &ts) == 0) |
102 | --ts.tv_sec; |
103 | else |
104 | { |
105 | ts.tv_sec = time (NULL) - 1; |
106 | ts.tv_nsec = 0; |
107 | } |
108 | |
109 | int ret; |
110 | for (int i = 0; i < 10; ++i) |
111 | { |
112 | if (i & 1) |
113 | ret = mq_send (mqdes: q, msg_ptr: (char *) &v[i], msg_len: 1, msg_prio: v[i] >> 4); |
114 | else |
115 | ret = mq_timedsend (mqdes: q, msg_ptr: (char *) &v[i], msg_len: 1, msg_prio: v[i] >> 4, abs_timeout: &ts); |
116 | |
117 | if (ret) |
118 | { |
119 | printf (format: "mq_%ssend failed: %m\n" , (i & 1) ? "" : "timed" ); |
120 | result = 1; |
121 | } |
122 | } |
123 | |
124 | ret = mq_timedsend (mqdes: q, msg_ptr: (char *) &v[10], msg_len: 1, msg_prio: 8, abs_timeout: &ts); |
125 | if (ret != -1) |
126 | { |
127 | puts (s: "mq_timedsend on full queue did not fail" ); |
128 | result = 1; |
129 | } |
130 | else if (errno != (nonblock ? EAGAIN : ETIMEDOUT)) |
131 | { |
132 | printf (format: "mq_timedsend on full queue did not fail with %s: %m\n" , |
133 | nonblock ? "EAGAIN" : "ETIMEDOUT" ); |
134 | result = 1; |
135 | } |
136 | |
137 | if (nonblock) |
138 | { |
139 | ret = mq_send (mqdes: q, msg_ptr: (char *) &v[10], msg_len: 1, msg_prio: 8); |
140 | if (ret != -1) |
141 | { |
142 | puts (s: "mq_send on full non-blocking queue did not fail" ); |
143 | result = 1; |
144 | } |
145 | else if (errno != EAGAIN) |
146 | { |
147 | printf (format: "mq_send on full non-blocking queue did not fail" |
148 | "with EAGAIN: %m\n" ); |
149 | result = 1; |
150 | } |
151 | } |
152 | |
153 | memset (&attr, 0xaa, sizeof (attr)); |
154 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
155 | { |
156 | printf (format: "mq_getattr failed: %m\n" ); |
157 | result = 1; |
158 | } |
159 | else |
160 | result |= check_attrs (attr: &attr, nonblock, cnt: 10); |
161 | |
162 | pid_t pid = fork (); |
163 | if (pid == -1) |
164 | { |
165 | printf (format: "fork failed: %m\n" ); |
166 | result = 1; |
167 | } |
168 | else if (pid == 0) |
169 | { |
170 | result = 0; |
171 | |
172 | if (mq_close (mqdes: q) != 0) |
173 | { |
174 | printf (format: "mq_close in child failed: %m\n" ); |
175 | result = 1; |
176 | } |
177 | |
178 | q = mq_open (name: name, O_RDONLY | nonblock); |
179 | if (q == (mqd_t) -1) |
180 | { |
181 | printf (format: "mq_open in child failed: %m\n" ); |
182 | exit (1); |
183 | } |
184 | |
185 | memset (&attr, 0xaa, sizeof (attr)); |
186 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
187 | { |
188 | printf (format: "mq_getattr failed: %m\n" ); |
189 | result = 1; |
190 | } |
191 | else |
192 | result |= check_attrs (attr: &attr, nonblock, cnt: 10); |
193 | |
194 | unsigned char vr[11] = { }; |
195 | unsigned int prio; |
196 | ssize_t rets; |
197 | |
198 | if (mq_send (mqdes: q, msg_ptr: (char *) &v[0], msg_len: 1, msg_prio: 1) != -1) |
199 | { |
200 | puts (s: "mq_send on O_RDONLY mqd_t unexpectedly succeeded" ); |
201 | result = 1; |
202 | } |
203 | else if (errno != EBADF) |
204 | { |
205 | printf (format: "mq_send on O_WRONLY mqd_t did not fail with EBADF: %m\n" ); |
206 | result = 1; |
207 | } |
208 | |
209 | for (int i = 0; i < 10; ++i) |
210 | { |
211 | if (i & 1) |
212 | rets = mq_receive (mqdes: q, msg_ptr: (char *) &vr[i], msg_len: 1, msg_prio: &prio); |
213 | else |
214 | rets = mq_timedreceive (mqdes: q, msg_ptr: (char *) &vr[i], msg_len: 1, msg_prio: &prio, abs_timeout: &ts); |
215 | |
216 | if (rets != 1) |
217 | { |
218 | if (rets == -1) |
219 | printf (format: "mq_%sreceive failed: %m\n" , (i & 1) ? "" : "timed" ); |
220 | else |
221 | printf (format: "mq_%sreceive returned %zd != 1\n" , |
222 | (i & 1) ? "" : "timed" , rets); |
223 | result = 1; |
224 | } |
225 | else if (prio != (unsigned int) vr[i] >> 4) |
226 | { |
227 | printf (format: "unexpected priority %x for value %02x\n" , prio, |
228 | vr[i]); |
229 | result = 1; |
230 | } |
231 | } |
232 | |
233 | qsort (v, 10, 1, intcmp); |
234 | if (memcmp (v, vr, 10) != 0) |
235 | { |
236 | puts (s: "messages not received in expected order" ); |
237 | result = 1; |
238 | } |
239 | |
240 | rets = mq_timedreceive (mqdes: q, msg_ptr: (char *) &vr[10], msg_len: 1, msg_prio: &prio, abs_timeout: &ts); |
241 | if (rets != -1) |
242 | { |
243 | puts (s: "mq_timedreceive on empty queue did not fail" ); |
244 | result = 1; |
245 | } |
246 | else if (errno != (nonblock ? EAGAIN : ETIMEDOUT)) |
247 | { |
248 | printf (format: "mq_timedreceive on empty queue did not fail with %s: %m\n" , |
249 | nonblock ? "EAGAIN" : "ETIMEDOUT" ); |
250 | result = 1; |
251 | } |
252 | |
253 | if (nonblock) |
254 | { |
255 | ret = mq_receive (mqdes: q, msg_ptr: (char *) &vr[10], msg_len: 1, msg_prio: &prio); |
256 | if (ret != -1) |
257 | { |
258 | puts (s: "mq_receive on empty non-blocking queue did not fail" ); |
259 | result = 1; |
260 | } |
261 | else if (errno != EAGAIN) |
262 | { |
263 | printf (format: "mq_receive on empty non-blocking queue did not fail" |
264 | "with EAGAIN: %m\n" ); |
265 | result = 1; |
266 | } |
267 | } |
268 | |
269 | memset (&attr, 0xaa, sizeof (attr)); |
270 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
271 | { |
272 | printf (format: "mq_getattr failed: %m\n" ); |
273 | result = 1; |
274 | } |
275 | else |
276 | result |= check_attrs (attr: &attr, nonblock, cnt: 0); |
277 | |
278 | if (mq_close (mqdes: q) != 0) |
279 | { |
280 | printf (format: "mq_close in child failed: %m\n" ); |
281 | result = 1; |
282 | } |
283 | |
284 | exit (result); |
285 | } |
286 | |
287 | int status; |
288 | if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) |
289 | { |
290 | printf (format: "waitpid failed: %m\n" ); |
291 | kill (pid: pid, SIGKILL); |
292 | result = 1; |
293 | } |
294 | else if (!WIFEXITED (status) || WEXITSTATUS (status)) |
295 | { |
296 | printf (format: "child failed: %d\n" , status); |
297 | result = 1; |
298 | } |
299 | |
300 | memset (&attr, 0xaa, sizeof (attr)); |
301 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
302 | { |
303 | printf (format: "mq_getattr failed: %m\n" ); |
304 | result = 1; |
305 | } |
306 | else |
307 | result |= check_attrs (attr: &attr, nonblock, cnt: 0); |
308 | |
309 | return result; |
310 | } |
311 | |
312 | #define TEST_FUNCTION do_test () |
313 | static int |
314 | do_test (void) |
315 | { |
316 | int result = 0; |
317 | |
318 | char name[sizeof "/tst-mqueue1-" + sizeof (pid_t) * 3]; |
319 | snprintf (s: name, maxlen: sizeof (name), format: "/tst-mqueue1-%u" , getpid ()); |
320 | |
321 | struct mq_attr attr = { .mq_maxmsg = 10, .mq_msgsize = 1 }; |
322 | mqd_t q = mq_open (name: name, O_CREAT | O_EXCL | O_WRONLY, 0600, &attr); |
323 | |
324 | if (q == (mqd_t) -1) |
325 | { |
326 | if (errno == ENOSYS) |
327 | FAIL_UNSUPPORTED ("mq_open not supported" ); |
328 | |
329 | printf (format: "mq_open failed with: %m\n" ); |
330 | return 1; |
331 | } |
332 | |
333 | add_temp_mq (name); |
334 | |
335 | result |= do_one_test (q, name, nonblock: 0); |
336 | |
337 | mqd_t q2 = mq_open (name: name, O_WRONLY | O_NONBLOCK); |
338 | if (q2 == (mqd_t) -1) |
339 | { |
340 | printf (format: "mq_open failed with: %m\n" ); |
341 | q2 = q; |
342 | result = 1; |
343 | } |
344 | else |
345 | { |
346 | if (mq_close (mqdes: q) != 0) |
347 | { |
348 | printf (format: "mq_close in parent failed: %m\n" ); |
349 | result = 1; |
350 | } |
351 | |
352 | q = q2; |
353 | result |= do_one_test (q, name, O_NONBLOCK); |
354 | |
355 | if (mq_getattr (mqdes: q, mqstat: &attr) != 0) |
356 | { |
357 | printf (format: "mq_getattr failed: %m\n" ); |
358 | result = 1; |
359 | } |
360 | else |
361 | { |
362 | attr.mq_flags ^= O_NONBLOCK; |
363 | |
364 | struct mq_attr attr2; |
365 | memset (&attr2, 0x55, sizeof (attr2)); |
366 | if (mq_setattr (q, &attr, &attr2) != 0) |
367 | { |
368 | printf (format: "mq_setattr failed: %m\n" ); |
369 | result = 1; |
370 | } |
371 | else if (attr.mq_flags != (attr2.mq_flags ^ O_NONBLOCK) |
372 | || attr.mq_maxmsg != attr2.mq_maxmsg |
373 | || attr.mq_msgsize != attr2.mq_msgsize |
374 | || attr.mq_curmsgs != 0 |
375 | || attr2.mq_curmsgs != 0) |
376 | { |
377 | puts (s: "mq_setattr returned unexpected values in *omqstat" ); |
378 | result = 1; |
379 | } |
380 | else |
381 | { |
382 | result |= do_one_test (q, name, nonblock: 0); |
383 | |
384 | if (mq_setattr (q, &attr2, NULL) != 0) |
385 | { |
386 | printf (format: "mq_setattr failed: %m\n" ); |
387 | result = 1; |
388 | } |
389 | else |
390 | result |= do_one_test (q, name, O_NONBLOCK); |
391 | } |
392 | } |
393 | } |
394 | |
395 | if (mq_unlink (name: name) != 0) |
396 | { |
397 | printf (format: "mq_unlink failed: %m\n" ); |
398 | result = 1; |
399 | } |
400 | |
401 | if (mq_close (mqdes: q) != 0) |
402 | { |
403 | printf (format: "mq_close in parent failed: %m\n" ); |
404 | result = 1; |
405 | } |
406 | |
407 | if (mq_close (mqdes: q) != -1) |
408 | { |
409 | puts (s: "second mq_close did not fail" ); |
410 | result = 1; |
411 | } |
412 | else if (errno != EBADF) |
413 | { |
414 | printf (format: "second mq_close did not fail with EBADF: %m\n" ); |
415 | result = 1; |
416 | } |
417 | |
418 | return result; |
419 | } |
420 | |
421 | #include "../test-skeleton.c" |
422 | |