blob: e0a74bd207a547c865f2e352f186a5b4cdf14da6 [file] [log] [blame]
Doug Ledford50069a52012-05-31 16:26:34 -07001/*
2 * This application is Copyright 2012 Red Hat, Inc.
3 * Doug Ledford <dledford@redhat.com>
4 *
5 * mq_open_tests is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * mq_open_tests is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * For the full text of the license, see <http://www.gnu.org/licenses/>.
15 *
16 * mq_open_tests.c
17 * Tests the various situations that should either succeed or fail to
18 * open a posix message queue and then reports whether or not they
19 * did as they were supposed to.
20 *
21 */
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <string.h>
27#include <limits.h>
28#include <errno.h>
29#include <sys/types.h>
30#include <sys/time.h>
31#include <sys/resource.h>
32#include <sys/stat.h>
33#include <mqueue.h>
Ben Hutchingsc1ee4832015-11-02 12:21:46 +000034#include <error.h>
Doug Ledford50069a52012-05-31 16:26:34 -070035
36static char *usage =
37"Usage:\n"
38" %s path\n"
39"\n"
40" path Path name of the message queue to create\n"
41"\n"
42" Note: this program must be run as root in order to enable all tests\n"
43"\n";
44
45char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default";
46char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default";
47char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
48char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
49
50int default_settings;
51struct rlimit saved_limits, cur_limits;
52int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize;
53int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize;
54FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize;
55char *queue_path;
56mqd_t queue = -1;
57
58static inline void __set(FILE *stream, int value, char *err_msg);
59void shutdown(int exit_val, char *err_cause, int line_no);
60static inline int get(FILE *stream);
61static inline void set(FILE *stream, int value);
62static inline void getr(int type, struct rlimit *rlim);
63static inline void setr(int type, struct rlimit *rlim);
64void validate_current_settings();
65static inline void test_queue(struct mq_attr *attr, struct mq_attr *result);
66static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result);
67
68static inline void __set(FILE *stream, int value, char *err_msg)
69{
70 rewind(stream);
71 if (fprintf(stream, "%d", value) < 0)
72 perror(err_msg);
73}
74
75
76void shutdown(int exit_val, char *err_cause, int line_no)
77{
78 static int in_shutdown = 0;
79
80 /* In case we get called recursively by a set() call below */
81 if (in_shutdown++)
82 return;
83
Shuah Khanef9feb62014-06-25 10:03:33 -060084 if (seteuid(0) == -1)
85 perror("seteuid() failed");
Doug Ledford50069a52012-05-31 16:26:34 -070086
87 if (queue != -1)
88 if (mq_close(queue))
89 perror("mq_close() during shutdown");
90 if (queue_path)
91 /*
92 * Be silent if this fails, if we cleaned up already it's
93 * expected to fail
94 */
95 mq_unlink(queue_path);
96 if (default_settings) {
97 if (saved_def_msgs)
98 __set(def_msgs, saved_def_msgs,
99 "failed to restore saved_def_msgs");
100 if (saved_def_msgsize)
101 __set(def_msgsize, saved_def_msgsize,
102 "failed to restore saved_def_msgsize");
103 }
104 if (saved_max_msgs)
105 __set(max_msgs, saved_max_msgs,
106 "failed to restore saved_max_msgs");
107 if (saved_max_msgsize)
108 __set(max_msgsize, saved_max_msgsize,
109 "failed to restore saved_max_msgsize");
110 if (exit_val)
111 error(exit_val, errno, "%s at %d", err_cause, line_no);
112 exit(0);
113}
114
115static inline int get(FILE *stream)
116{
117 int value;
118 rewind(stream);
119 if (fscanf(stream, "%d", &value) != 1)
120 shutdown(4, "Error reading /proc entry", __LINE__ - 1);
121 return value;
122}
123
124static inline void set(FILE *stream, int value)
125{
126 int new_value;
127
128 rewind(stream);
129 if (fprintf(stream, "%d", value) < 0)
130 return shutdown(5, "Failed writing to /proc file",
131 __LINE__ - 1);
132 new_value = get(stream);
133 if (new_value != value)
134 return shutdown(5, "We didn't get what we wrote to /proc back",
135 __LINE__ - 1);
136}
137
138static inline void getr(int type, struct rlimit *rlim)
139{
140 if (getrlimit(type, rlim))
141 shutdown(6, "getrlimit()", __LINE__ - 1);
142}
143
144static inline void setr(int type, struct rlimit *rlim)
145{
146 if (setrlimit(type, rlim))
147 shutdown(7, "setrlimit()", __LINE__ - 1);
148}
149
150void validate_current_settings()
151{
152 int rlim_needed;
153
154 if (cur_limits.rlim_cur < 4096) {
155 printf("Current rlimit value for POSIX message queue bytes is "
156 "unreasonably low,\nincreasing.\n\n");
157 cur_limits.rlim_cur = 8192;
158 cur_limits.rlim_max = 16384;
159 setr(RLIMIT_MSGQUEUE, &cur_limits);
160 }
161
162 if (default_settings) {
163 rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 +
164 2 * sizeof(void *));
165 if (rlim_needed > cur_limits.rlim_cur) {
166 printf("Temporarily lowering default queue parameters "
167 "to something that will work\n"
168 "with the current rlimit values.\n\n");
169 set(def_msgs, 10);
170 cur_def_msgs = 10;
171 set(def_msgsize, 128);
172 cur_def_msgsize = 128;
173 }
174 } else {
175 rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 +
176 2 * sizeof(void *));
177 if (rlim_needed > cur_limits.rlim_cur) {
178 printf("Temporarily lowering maximum queue parameters "
179 "to something that will work\n"
180 "with the current rlimit values in case this is "
181 "a kernel that ties the default\n"
182 "queue parameters to the maximum queue "
183 "parameters.\n\n");
184 set(max_msgs, 10);
185 cur_max_msgs = 10;
186 set(max_msgsize, 128);
187 cur_max_msgsize = 128;
188 }
189 }
190}
191
192/*
193 * test_queue - Test opening a queue, shutdown if we fail. This should
194 * only be called in situations that should never fail. We clean up
195 * after ourselves and return the queue attributes in *result.
196 */
197static inline void test_queue(struct mq_attr *attr, struct mq_attr *result)
198{
199 int flags = O_RDWR | O_EXCL | O_CREAT;
200 int perms = DEFFILEMODE;
201
202 if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
203 shutdown(1, "mq_open()", __LINE__);
204 if (mq_getattr(queue, result))
205 shutdown(1, "mq_getattr()", __LINE__);
206 if (mq_close(queue))
207 shutdown(1, "mq_close()", __LINE__);
208 queue = -1;
209 if (mq_unlink(queue_path))
210 shutdown(1, "mq_unlink()", __LINE__);
211}
212
213/*
214 * Same as test_queue above, but failure is not fatal.
215 * Returns:
216 * 0 - Failed to create a queue
217 * 1 - Created a queue, attributes in *result
218 */
219static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result)
220{
221 int flags = O_RDWR | O_EXCL | O_CREAT;
222 int perms = DEFFILEMODE;
223
224 if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
225 return 0;
226 if (mq_getattr(queue, result))
227 shutdown(1, "mq_getattr()", __LINE__);
228 if (mq_close(queue))
229 shutdown(1, "mq_close()", __LINE__);
230 queue = -1;
231 if (mq_unlink(queue_path))
232 shutdown(1, "mq_unlink()", __LINE__);
233 return 1;
234}
235
236int main(int argc, char *argv[])
237{
238 struct mq_attr attr, result;
239
240 if (argc != 2) {
241 fprintf(stderr, "Must pass a valid queue name\n\n");
242 fprintf(stderr, usage, argv[0]);
243 exit(1);
244 }
245
246 /*
247 * Although we can create a msg queue with a non-absolute path name,
248 * unlink will fail. So, if the name doesn't start with a /, add one
249 * when we save it.
250 */
251 if (*argv[1] == '/')
252 queue_path = strdup(argv[1]);
253 else {
254 queue_path = malloc(strlen(argv[1]) + 2);
255 if (!queue_path) {
256 perror("malloc()");
257 exit(1);
258 }
259 queue_path[0] = '/';
260 queue_path[1] = 0;
261 strcat(queue_path, argv[1]);
262 }
263
264 if (getuid() != 0) {
265 fprintf(stderr, "Not running as root, but almost all tests "
266 "require root in order to modify\nsystem settings. "
267 "Exiting.\n");
268 exit(1);
269 }
270
271 /* Find out what files there are for us to make tweaks in */
272 def_msgs = fopen(DEF_MSGS, "r+");
273 def_msgsize = fopen(DEF_MSGSIZE, "r+");
274 max_msgs = fopen(MAX_MSGS, "r+");
275 max_msgsize = fopen(MAX_MSGSIZE, "r+");
276
277 if (!max_msgs)
278 shutdown(2, "Failed to open msg_max", __LINE__);
279 if (!max_msgsize)
280 shutdown(2, "Failed to open msgsize_max", __LINE__);
281 if (def_msgs || def_msgsize)
282 default_settings = 1;
283
284 /* Load up the current system values for everything we can */
285 getr(RLIMIT_MSGQUEUE, &saved_limits);
286 cur_limits = saved_limits;
287 if (default_settings) {
288 saved_def_msgs = cur_def_msgs = get(def_msgs);
289 saved_def_msgsize = cur_def_msgsize = get(def_msgsize);
290 }
291 saved_max_msgs = cur_max_msgs = get(max_msgs);
292 saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
293
294 /* Tell the user our initial state */
295 printf("\nInitial system state:\n");
296 printf("\tUsing queue path:\t\t%s\n", queue_path);
Shuah Khanef9feb62014-06-25 10:03:33 -0600297 printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n",
298 (long) saved_limits.rlim_cur);
299 printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n",
300 (long) saved_limits.rlim_max);
Doug Ledford50069a52012-05-31 16:26:34 -0700301 printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize);
302 printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs);
303 if (default_settings) {
304 printf("\tDefault Message Size:\t\t%d\n", saved_def_msgsize);
305 printf("\tDefault Queue Size:\t\t%d\n", saved_def_msgs);
306 } else {
307 printf("\tDefault Message Size:\t\tNot Supported\n");
308 printf("\tDefault Queue Size:\t\tNot Supported\n");
309 }
310 printf("\n");
311
312 validate_current_settings();
313
314 printf("Adjusted system state for testing:\n");
Shuah Khanef9feb62014-06-25 10:03:33 -0600315 printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", (long) cur_limits.rlim_cur);
316 printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", (long) cur_limits.rlim_max);
Doug Ledford50069a52012-05-31 16:26:34 -0700317 printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize);
318 printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs);
319 if (default_settings) {
320 printf("\tDefault Message Size:\t\t%d\n", cur_def_msgsize);
321 printf("\tDefault Queue Size:\t\t%d\n", cur_def_msgs);
322 }
323
324 printf("\n\nTest series 1, behavior when no attr struct "
325 "passed to mq_open:\n");
326 if (!default_settings) {
327 test_queue(NULL, &result);
328 printf("Given sane system settings, mq_open without an attr "
329 "struct succeeds:\tPASS\n");
330 if (result.mq_maxmsg != cur_max_msgs ||
331 result.mq_msgsize != cur_max_msgsize) {
332 printf("Kernel does not support setting the default "
333 "mq attributes,\nbut also doesn't tie the "
334 "defaults to the maximums:\t\t\tPASS\n");
335 } else {
336 set(max_msgs, ++cur_max_msgs);
337 set(max_msgsize, ++cur_max_msgsize);
338 test_queue(NULL, &result);
339 if (result.mq_maxmsg == cur_max_msgs &&
340 result.mq_msgsize == cur_max_msgsize)
341 printf("Kernel does not support setting the "
342 "default mq attributes and\n"
343 "also ties system wide defaults to "
344 "the system wide maximums:\t\t"
345 "FAIL\n");
346 else
347 printf("Kernel does not support setting the "
348 "default mq attributes,\n"
349 "but also doesn't tie the defaults to "
350 "the maximums:\t\t\tPASS\n");
351 }
352 } else {
353 printf("Kernel supports setting defaults separately from "
354 "maximums:\t\tPASS\n");
355 /*
356 * While we are here, go ahead and test that the kernel
357 * properly follows the default settings
358 */
359 test_queue(NULL, &result);
360 printf("Given sane values, mq_open without an attr struct "
361 "succeeds:\t\tPASS\n");
362 if (result.mq_maxmsg != cur_def_msgs ||
363 result.mq_msgsize != cur_def_msgsize)
364 printf("Kernel supports setting defaults, but does "
365 "not actually honor them:\tFAIL\n\n");
366 else {
367 set(def_msgs, ++cur_def_msgs);
368 set(def_msgsize, ++cur_def_msgsize);
369 /* In case max was the same as the default */
370 set(max_msgs, ++cur_max_msgs);
371 set(max_msgsize, ++cur_max_msgsize);
372 test_queue(NULL, &result);
373 if (result.mq_maxmsg != cur_def_msgs ||
374 result.mq_msgsize != cur_def_msgsize)
375 printf("Kernel supports setting defaults, but "
376 "does not actually honor them:\t"
377 "FAIL\n");
378 else
379 printf("Kernel properly honors default setting "
380 "knobs:\t\t\t\tPASS\n");
381 }
382 set(def_msgs, cur_max_msgs + 1);
383 cur_def_msgs = cur_max_msgs + 1;
384 set(def_msgsize, cur_max_msgsize + 1);
385 cur_def_msgsize = cur_max_msgsize + 1;
386 if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >=
387 cur_limits.rlim_cur) {
388 cur_limits.rlim_cur = (cur_def_msgs + 2) *
389 (cur_def_msgsize + 2 * sizeof(void *));
390 cur_limits.rlim_max = 2 * cur_limits.rlim_cur;
391 setr(RLIMIT_MSGQUEUE, &cur_limits);
392 }
393 if (test_queue_fail(NULL, &result)) {
394 if (result.mq_maxmsg == cur_max_msgs &&
395 result.mq_msgsize == cur_max_msgsize)
396 printf("Kernel properly limits default values "
397 "to lesser of default/max:\t\tPASS\n");
398 else
399 printf("Kernel does not properly set default "
400 "queue parameters when\ndefaults > "
401 "max:\t\t\t\t\t\t\t\tFAIL\n");
402 } else
403 printf("Kernel fails to open mq because defaults are "
404 "greater than maximums:\tFAIL\n");
405 set(def_msgs, --cur_def_msgs);
406 set(def_msgsize, --cur_def_msgsize);
407 cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs *
408 cur_def_msgsize;
409 setr(RLIMIT_MSGQUEUE, &cur_limits);
410 if (test_queue_fail(NULL, &result))
411 printf("Kernel creates queue even though defaults "
412 "would exceed\nrlimit setting:"
413 "\t\t\t\t\t\t\t\tFAIL\n");
414 else
415 printf("Kernel properly fails to create queue when "
416 "defaults would\nexceed rlimit:"
417 "\t\t\t\t\t\t\t\tPASS\n");
418 }
419
420 /*
421 * Test #2 - open with an attr struct that exceeds rlimit
422 */
423 printf("\n\nTest series 2, behavior when attr struct is "
424 "passed to mq_open:\n");
425 cur_max_msgs = 32;
426 cur_max_msgsize = cur_limits.rlim_max >> 4;
427 set(max_msgs, cur_max_msgs);
428 set(max_msgsize, cur_max_msgsize);
429 attr.mq_maxmsg = cur_max_msgs;
430 attr.mq_msgsize = cur_max_msgsize;
431 if (test_queue_fail(&attr, &result))
432 printf("Queue open in excess of rlimit max when euid = 0 "
433 "succeeded:\t\tFAIL\n");
434 else
435 printf("Queue open in excess of rlimit max when euid = 0 "
436 "failed:\t\tPASS\n");
437 attr.mq_maxmsg = cur_max_msgs + 1;
438 attr.mq_msgsize = 10;
439 if (test_queue_fail(&attr, &result))
440 printf("Queue open with mq_maxmsg > limit when euid = 0 "
441 "succeeded:\t\tPASS\n");
442 else
443 printf("Queue open with mq_maxmsg > limit when euid = 0 "
444 "failed:\t\tFAIL\n");
445 attr.mq_maxmsg = 1;
446 attr.mq_msgsize = cur_max_msgsize + 1;
447 if (test_queue_fail(&attr, &result))
448 printf("Queue open with mq_msgsize > limit when euid = 0 "
449 "succeeded:\t\tPASS\n");
450 else
451 printf("Queue open with mq_msgsize > limit when euid = 0 "
452 "failed:\t\tFAIL\n");
453 attr.mq_maxmsg = 65536;
454 attr.mq_msgsize = 65536;
455 if (test_queue_fail(&attr, &result))
456 printf("Queue open with total size > 2GB when euid = 0 "
457 "succeeded:\t\tFAIL\n");
458 else
459 printf("Queue open with total size > 2GB when euid = 0 "
460 "failed:\t\t\tPASS\n");
Shuah Khanef9feb62014-06-25 10:03:33 -0600461
462 if (seteuid(99) == -1) {
463 perror("seteuid() failed");
464 exit(1);
465 }
466
Doug Ledford50069a52012-05-31 16:26:34 -0700467 attr.mq_maxmsg = cur_max_msgs;
468 attr.mq_msgsize = cur_max_msgsize;
469 if (test_queue_fail(&attr, &result))
470 printf("Queue open in excess of rlimit max when euid = 99 "
471 "succeeded:\t\tFAIL\n");
472 else
473 printf("Queue open in excess of rlimit max when euid = 99 "
474 "failed:\t\tPASS\n");
475 attr.mq_maxmsg = cur_max_msgs + 1;
476 attr.mq_msgsize = 10;
477 if (test_queue_fail(&attr, &result))
478 printf("Queue open with mq_maxmsg > limit when euid = 99 "
479 "succeeded:\t\tFAIL\n");
480 else
481 printf("Queue open with mq_maxmsg > limit when euid = 99 "
482 "failed:\t\tPASS\n");
483 attr.mq_maxmsg = 1;
484 attr.mq_msgsize = cur_max_msgsize + 1;
485 if (test_queue_fail(&attr, &result))
486 printf("Queue open with mq_msgsize > limit when euid = 99 "
487 "succeeded:\t\tFAIL\n");
488 else
489 printf("Queue open with mq_msgsize > limit when euid = 99 "
490 "failed:\t\tPASS\n");
491 attr.mq_maxmsg = 65536;
492 attr.mq_msgsize = 65536;
493 if (test_queue_fail(&attr, &result))
494 printf("Queue open with total size > 2GB when euid = 99 "
495 "succeeded:\t\tFAIL\n");
496 else
497 printf("Queue open with total size > 2GB when euid = 99 "
498 "failed:\t\t\tPASS\n");
499
500 shutdown(0,"",0);
501}