]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/regression/posixsem/posixsem.c
zfs: merge openzfs/zfs@688514e47
[FreeBSD/FreeBSD.git] / tools / regression / posixsem / posixsem.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2008 Yahoo!, Inc.
5  * All rights reserved.
6  * Written by: John Baldwin <jhb@FreeBSD.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/_semaphore.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 #include <sys/user.h>
39 #include <sys/wait.h>
40
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <kvm.h>
44 #include <limits.h>
45 #include <semaphore.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51 #include <unistd.h>
52
53 #include "test.h"
54
55 #define TEST_PATH       "/tmp/posixsem_regression_test"
56
57 #define ELAPSED(elapsed, limit)         (abs((elapsed) - (limit)) < 100)
58
59 /* Macros for passing child status to parent over a pipe. */
60 #define CSTAT(class, error)             ((class) << 16 | (error))
61 #define CSTAT_CLASS(stat)               ((stat) >> 16)
62 #define CSTAT_ERROR(stat)               ((stat) & 0xffff)
63
64 /*
65  * Helper routine for tests that use a child process.  This routine
66  * creates a pipe and forks a child process.  The child process runs
67  * the 'func' routine which returns a status integer.  The status
68  * integer gets written over the pipe to the parent and returned in
69  * '*stat'.  If there is an error in pipe(), fork(), or wait() this
70  * returns -1 and fails the test.
71  */
72 static int
73 child_worker(int (*func)(void *arg), void *arg, int *stat)
74 {
75         pid_t pid;
76         int pfd[2], cstat;
77
78         if (pipe(pfd) < 0) {
79                 fail_errno("pipe");
80                 return (-1);
81         }
82
83         pid = fork();
84         switch (pid) {
85         case -1:
86                 /* Error. */
87                 fail_errno("fork");
88                 close(pfd[0]);
89                 close(pfd[1]);
90                 return (-1);
91         case 0:
92                 /* Child. */
93                 cstat = func(arg);
94                 write(pfd[1], &cstat, sizeof(cstat));
95                 exit(0);
96         }
97
98         if (read(pfd[0], stat, sizeof(*stat)) < 0) {
99                 fail_errno("read(pipe)");
100                 close(pfd[0]);
101                 close(pfd[1]);
102                 return (-1);
103         }
104         if (waitpid(pid, NULL, 0) < 0) {
105                 fail_errno("wait");
106                 close(pfd[0]);
107                 close(pfd[1]);
108                 return (-1);
109         }
110         close(pfd[0]);
111         close(pfd[1]);
112         return (0);
113 }
114
115 /*
116  * Attempt a ksem_open() that should fail with an expected error of
117  * 'error'.
118  */
119 static void
120 ksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
121     value, int error)
122 {
123         semid_t id;
124
125         if (ksem_open(&id, path, flags, mode, value) >= 0) {
126                 fail_err("ksem_open() didn't fail");
127                 ksem_close(id);
128                 return;
129         }
130         if (errno != error) {
131                 fail_errno("ksem_open");
132                 return;
133         }
134         pass();
135 }
136
137 /*
138  * Attempt a ksem_unlink() that should fail with an expected error of
139  * 'error'.
140  */
141 static void
142 ksem_unlink_should_fail(const char *path, int error)
143 {
144
145         if (ksem_unlink(path) >= 0) {
146                 fail_err("ksem_unlink() didn't fail");
147                 return;
148         }
149         if (errno != error) {
150                 fail_errno("ksem_unlink");
151                 return;
152         }
153         pass();
154 }
155
156 /*
157  * Attempt a ksem_close() that should fail with an expected error of
158  * 'error'.
159  */
160 static void
161 ksem_close_should_fail(semid_t id, int error)
162 {
163
164         if (ksem_close(id) >= 0) {
165                 fail_err("ksem_close() didn't fail");
166                 return;
167         }
168         if (errno != error) {
169                 fail_errno("ksem_close");
170                 return;
171         }
172         pass();
173 }
174
175 /*
176  * Attempt a ksem_init() that should fail with an expected error of
177  * 'error'.
178  */
179 static void
180 ksem_init_should_fail(unsigned int value, int error)
181 {
182         semid_t id;
183
184         if (ksem_init(&id, value) >= 0) {
185                 fail_err("ksem_init() didn't fail");
186                 ksem_destroy(id);
187                 return;
188         }
189         if (errno != error) {
190                 fail_errno("ksem_init");
191                 return;
192         }
193         pass();
194 }
195
196 /*
197  * Attempt a ksem_destroy() that should fail with an expected error of
198  * 'error'.
199  */
200 static void
201 ksem_destroy_should_fail(semid_t id, int error)
202 {
203
204         if (ksem_destroy(id) >= 0) {
205                 fail_err("ksem_destroy() didn't fail");
206                 return;
207         }
208         if (errno != error) {
209                 fail_errno("ksem_destroy");
210                 return;
211         }
212         pass();
213 }
214
215 /*
216  * Attempt a ksem_post() that should fail with an expected error of
217  * 'error'.
218  */
219 static void
220 ksem_post_should_fail(semid_t id, int error)
221 {
222
223         if (ksem_post(id) >= 0) {
224                 fail_err("ksem_post() didn't fail");
225                 return;
226         }
227         if (errno != error) {
228                 fail_errno("ksem_post");
229                 return;
230         }
231         pass();
232 }
233
234 static void
235 open_after_unlink(void)
236 {
237         semid_t id;
238
239         if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
240                 fail_errno("ksem_open(1)");
241                 return;
242         }
243         ksem_close(id);
244
245         if (ksem_unlink(TEST_PATH) < 0) {
246                 fail_errno("ksem_unlink");
247                 return;
248         }
249
250         ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
251 }
252 TEST(open_after_unlink, "open after unlink");
253
254 static void
255 open_invalid_path(void)
256 {
257
258         ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
259 }
260 TEST(open_invalid_path, "open invalid path");
261
262 static void
263 open_extra_flags(void)
264 {
265
266         ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
267 }
268 TEST(open_extra_flags, "open with extra flags");
269
270 static void
271 open_bad_value(void)
272 {
273
274         (void)ksem_unlink(TEST_PATH);
275
276         ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
277 }
278 TEST(open_bad_value, "open with invalid initial value");
279
280 static void
281 open_bad_path_pointer(void)
282 {
283
284         ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
285 }
286 TEST(open_bad_path_pointer, "open bad path pointer");
287
288 static void
289 open_path_too_long(void)
290 {
291         char *page;
292
293         page = malloc(MAXPATHLEN + 1);
294         memset(page, 'a', MAXPATHLEN);
295         page[MAXPATHLEN] = '\0';
296         ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
297         free(page);
298 }
299 TEST(open_path_too_long, "open pathname too long");
300
301 static void
302 open_nonexisting_semaphore(void)
303 {
304
305         ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
306 }
307 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
308
309 static void
310 exclusive_create_existing_semaphore(void)
311 {
312         semid_t id;
313
314         if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
315                 fail_errno("ksem_open(O_CREAT)");
316                 return;
317         }
318         ksem_close(id);
319
320         ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
321
322         ksem_unlink(TEST_PATH);
323 }
324 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
325
326 static void
327 init_bad_value(void)
328 {
329
330         ksem_init_should_fail(UINT_MAX, EINVAL);
331 }
332 TEST(init_bad_value, "init with invalid initial value");
333
334 static void
335 unlink_bad_path_pointer(void)
336 {
337
338         ksem_unlink_should_fail((char *)1024, EFAULT);
339 }
340 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
341
342 static void
343 unlink_path_too_long(void)
344 {
345         char *page;
346
347         page = malloc(MAXPATHLEN + 1);
348         memset(page, 'a', MAXPATHLEN);
349         page[MAXPATHLEN] = '\0';
350         ksem_unlink_should_fail(page, ENAMETOOLONG);
351         free(page);
352 }
353 TEST(unlink_path_too_long, "unlink pathname too long");
354
355 static void
356 destroy_named_semaphore(void)
357 {
358         semid_t id;
359
360         if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
361                 fail_errno("ksem_open(O_CREAT)");
362                 return;
363         }
364
365         ksem_destroy_should_fail(id, EINVAL);
366
367         ksem_close(id);
368         ksem_unlink(TEST_PATH);
369 }
370 TEST(destroy_named_semaphore, "destroy named semaphore");
371
372 static void
373 close_unnamed_semaphore(void)
374 {
375         semid_t id;
376
377         if (ksem_init(&id, 1) < 0) {
378                 fail_errno("ksem_init");
379                 return;
380         }
381
382         ksem_close_should_fail(id, EINVAL);
383
384         ksem_destroy(id);
385 }
386 TEST(close_unnamed_semaphore, "close unnamed semaphore");
387
388 static void
389 destroy_invalid_fd(void)
390 {
391
392         ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
393 }
394 TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
395
396 static void
397 close_invalid_fd(void)
398 {
399
400         ksem_close_should_fail(STDERR_FILENO, EINVAL);
401 }
402 TEST(close_invalid_fd, "close non-semaphore file descriptor");
403
404 static void
405 create_unnamed_semaphore(void)
406 {
407         semid_t id;
408
409         if (ksem_init(&id, 1) < 0) {
410                 fail_errno("ksem_init");
411                 return;
412         }
413
414         if (ksem_destroy(id) < 0) {
415                 fail_errno("ksem_destroy");
416                 return;
417         }
418         pass();
419 }
420 TEST(create_unnamed_semaphore, "create unnamed semaphore");
421
422 static void
423 open_named_semaphore(void)
424 {
425         semid_t id;
426
427         if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
428                 fail_errno("ksem_open(O_CREAT)");
429                 return;
430         }
431
432         if (ksem_close(id) < 0) {
433                 fail_errno("ksem_close");
434                 return;
435         }
436
437         if (ksem_unlink(TEST_PATH) < 0) {
438                 fail_errno("ksem_unlink");
439                 return;
440         }
441         pass();
442 }
443 TEST(open_named_semaphore, "create named semaphore");
444
445 static void
446 getvalue_invalid_semaphore(void)
447 {
448         int val;
449
450         if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
451                 fail_err("ksem_getvalue() didn't fail");
452                 return;
453         }
454         if (errno != EINVAL) {
455                 fail_errno("ksem_getvalue");
456                 return;
457         }
458         pass();
459 }
460 TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
461
462 static void
463 post_invalid_semaphore(void)
464 {
465
466         ksem_post_should_fail(STDERR_FILENO, EINVAL);
467 }
468 TEST(post_invalid_semaphore, "post of invalid semaphore");
469
470 static void
471 wait_invalid_semaphore(void)
472 {
473
474         if (ksem_wait(STDERR_FILENO) >= 0) {
475                 fail_err("ksem_wait() didn't fail");
476                 return;
477         }
478         if (errno != EINVAL) {
479                 fail_errno("ksem_wait");
480                 return;
481         }
482         pass();
483 }
484 TEST(wait_invalid_semaphore, "wait for invalid semaphore");
485
486 static void
487 trywait_invalid_semaphore(void)
488 {
489
490         if (ksem_trywait(STDERR_FILENO) >= 0) {
491                 fail_err("ksem_trywait() didn't fail");
492                 return;
493         }
494         if (errno != EINVAL) {
495                 fail_errno("ksem_trywait");
496                 return;
497         }
498         pass();
499 }
500 TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
501
502 static void
503 timedwait_invalid_semaphore(void)
504 {
505
506         if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
507                 fail_err("ksem_timedwait() didn't fail");
508                 return;
509         }
510         if (errno != EINVAL) {
511                 fail_errno("ksem_timedwait");
512                 return;
513         }
514         pass();
515 }
516 TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
517
518 static int
519 checkvalue(semid_t id, int expected)
520 {
521         int val;
522
523         if (ksem_getvalue(id, &val) < 0) {
524                 fail_errno("ksem_getvalue");
525                 return (-1);
526         }
527         if (val != expected) {
528                 fail_err("sem value should be %d instead of %d", expected, val);
529                 return (-1);
530         }
531         return (0);
532 }
533
534 static void
535 post_test(void)
536 {
537         semid_t id;
538
539         if (ksem_init(&id, 1) < 0) {
540                 fail_errno("ksem_init");
541                 return;
542         }
543         if (checkvalue(id, 1) < 0) {
544                 ksem_destroy(id);
545                 return;
546         }
547         if (ksem_post(id) < 0) {
548                 fail_errno("ksem_post");
549                 ksem_destroy(id);
550                 return;
551         }
552         if (checkvalue(id, 2) < 0) {
553                 ksem_destroy(id);
554                 return;
555         }
556         if (ksem_destroy(id) < 0) {
557                 fail_errno("ksem_destroy");
558                 return;
559         }
560         pass();
561 }
562 TEST(post_test, "simple post");
563
564 static void
565 use_after_unlink_test(void)
566 {
567         semid_t id;
568
569         /*
570          * Create named semaphore with value of 1 and then unlink it
571          * while still retaining the initial reference.
572          */
573         if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
574                 fail_errno("ksem_open(O_CREAT | O_EXCL)");
575                 return;
576         }
577         if (ksem_unlink(TEST_PATH) < 0) {
578                 fail_errno("ksem_unlink");
579                 ksem_close(id);
580                 return;
581         }
582         if (checkvalue(id, 1) < 0) {
583                 ksem_close(id);
584                 return;
585         }
586
587         /* Post the semaphore to set its value to 2. */
588         if (ksem_post(id) < 0) {
589                 fail_errno("ksem_post");
590                 ksem_close(id);
591                 return;
592         }
593         if (checkvalue(id, 2) < 0) {
594                 ksem_close(id);
595                 return;
596         }
597
598         /* Wait on the semaphore which should set its value to 1. */
599         if (ksem_wait(id) < 0) {
600                 fail_errno("ksem_wait");
601                 ksem_close(id);
602                 return;
603         }
604         if (checkvalue(id, 1) < 0) {
605                 ksem_close(id);
606                 return;
607         }
608
609         if (ksem_close(id) < 0) {
610                 fail_errno("ksem_close");
611                 return;
612         }
613         pass();
614 }
615 TEST(use_after_unlink_test, "use named semaphore after unlink");
616
617 static void
618 unlocked_trywait(void)
619 {
620         semid_t id;
621
622         if (ksem_init(&id, 1) < 0) {
623                 fail_errno("ksem_init");
624                 return;
625         }
626
627         /* This should succeed and decrement the value to 0. */
628         if (ksem_trywait(id) < 0) {
629                 fail_errno("ksem_trywait()");
630                 ksem_destroy(id);
631                 return;
632         }
633         if (checkvalue(id, 0) < 0) {
634                 ksem_destroy(id);
635                 return;
636         }
637
638         if (ksem_destroy(id) < 0) {
639                 fail_errno("ksem_destroy");
640                 return;
641         }
642         pass();
643 }
644 TEST(unlocked_trywait, "unlocked trywait");
645
646 static void
647 locked_trywait(void)
648 {
649         semid_t id;
650
651         if (ksem_init(&id, 0) < 0) {
652                 fail_errno("ksem_init");
653                 return;
654         }
655
656         /* This should fail with EAGAIN and leave the value at 0. */
657         if (ksem_trywait(id) >= 0) {
658                 fail_err("ksem_trywait() didn't fail");
659                 ksem_destroy(id);
660                 return;
661         }
662         if (errno != EAGAIN) {
663                 fail_errno("wrong error from ksem_trywait()");
664                 ksem_destroy(id);
665                 return;
666         }
667         if (checkvalue(id, 0) < 0) {
668                 ksem_destroy(id);
669                 return;
670         }
671
672         if (ksem_destroy(id) < 0) {
673                 fail_errno("ksem_destroy");
674                 return;
675         }
676         pass();
677 }
678 TEST(locked_trywait, "locked trywait");
679
680 /*
681  * Use a timer to post a specific semaphore after a timeout.  A timer
682  * is scheduled via schedule_post().  check_alarm() must be called
683  * afterwards to clean up and check for errors.
684  */
685 static semid_t alarm_id = -1;
686 static int alarm_errno;
687 static int alarm_handler_installed;
688
689 static void
690 alarm_handler(int signo)
691 {
692
693         if (ksem_post(alarm_id) < 0)
694                 alarm_errno = errno;
695 }
696
697 static int
698 check_alarm(int just_clear)
699 {
700         struct itimerval it;
701
702         bzero(&it, sizeof(it));
703         if (just_clear) {
704                 setitimer(ITIMER_REAL, &it, NULL);
705                 alarm_errno = 0;
706                 alarm_id = -1;
707                 return (0);
708         }
709         if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
710                 fail_errno("setitimer");
711                 return (-1);
712         }
713         if (alarm_errno != 0 && !just_clear) {
714                 errno = alarm_errno;
715                 fail_errno("ksem_post() (via timeout)");
716                 alarm_errno = 0;
717                 return (-1);
718         }
719         alarm_id = -1;
720         
721         return (0);
722 }
723
724 static int
725 schedule_post(semid_t id, u_int msec)
726 {
727         struct itimerval it;
728
729         if (!alarm_handler_installed) {
730                 if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
731                         fail_errno("signal(SIGALRM)");
732                         return (-1);
733                 }
734                 alarm_handler_installed = 1;
735         }
736         if (alarm_id != -1) {
737                 fail_err("ksem_post() already scheduled");
738                 return (-1);
739         }
740         alarm_id = id;
741         bzero(&it, sizeof(it));
742         it.it_value.tv_sec = msec / 1000;
743         it.it_value.tv_usec = (msec % 1000) * 1000;
744         if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
745                 fail_errno("setitimer");
746                 return (-1);
747         }
748         return (0);
749 }
750
751 static int
752 timedwait(semid_t id, u_int msec, u_int *delta, int error)
753 {
754         struct timespec start, end;
755
756         if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
757                 fail_errno("clock_gettime(CLOCK_REALTIME)");
758                 return (-1);
759         }
760         end.tv_sec = msec / 1000;
761         end.tv_nsec = msec % 1000 * 1000000;
762         timespecadd(&end, &start, &end);
763         if (ksem_timedwait(id, &end) < 0) {
764                 if (errno != error) {
765                         fail_errno("ksem_timedwait");
766                         return (-1);
767                 }
768         } else if (error != 0) {
769                 fail_err("ksem_timedwait() didn't fail");
770                 return (-1);
771         }
772         if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
773                 fail_errno("clock_gettime(CLOCK_REALTIME)");
774                 return (-1);
775         }
776         timespecsub(&end, &start, &end);
777         *delta = end.tv_nsec / 1000000;
778         *delta += end.tv_sec * 1000;
779         return (0);
780 }
781
782 static void
783 unlocked_timedwait(void)
784 {
785         semid_t id;
786         u_int elapsed;
787
788         if (ksem_init(&id, 1) < 0) {
789                 fail_errno("ksem_init");
790                 return;
791         }
792
793         /* This should succeed right away and set the value to 0. */
794         if (timedwait(id, 5000, &elapsed, 0) < 0) {
795                 ksem_destroy(id);
796                 return;
797         }
798         if (!ELAPSED(elapsed, 0)) {
799                 fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
800                 ksem_destroy(id);
801                 return;
802         }
803         if (checkvalue(id, 0) < 0) {
804                 ksem_destroy(id);
805                 return;
806         }
807
808         if (ksem_destroy(id) < 0) {
809                 fail_errno("ksem_destroy");
810                 return;
811         }
812         pass();
813 }
814 TEST(unlocked_timedwait, "unlocked timedwait");
815
816 static void
817 expired_timedwait(void)
818 {
819         semid_t id;
820         u_int elapsed;
821
822         if (ksem_init(&id, 0) < 0) {
823                 fail_errno("ksem_init");
824                 return;
825         }
826
827         /* This should fail with a timeout and leave the value at 0. */
828         if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
829                 ksem_destroy(id);
830                 return;
831         }
832         if (!ELAPSED(elapsed, 2500)) {
833                 fail_err(
834             "ksem_timedwait() of locked sem took %ums instead of 2500ms",
835                     elapsed);
836                 ksem_destroy(id);
837                 return;
838         }
839         if (checkvalue(id, 0) < 0) {
840                 ksem_destroy(id);
841                 return;
842         }
843
844         if (ksem_destroy(id) < 0) {
845                 fail_errno("ksem_destroy");
846                 return;
847         }
848         pass();
849 }
850 TEST(expired_timedwait, "locked timedwait timeout");
851
852 static void
853 locked_timedwait(void)
854 {
855         semid_t id;
856         u_int elapsed;
857
858         if (ksem_init(&id, 0) < 0) {
859                 fail_errno("ksem_init");
860                 return;
861         }
862
863         /*
864          * Schedule a post to trigger after 1000 ms.  The subsequent
865          * timedwait should succeed after 1000 ms as a result w/o
866          * timing out.
867          */
868         if (schedule_post(id, 1000) < 0) {
869                 ksem_destroy(id);
870                 return;
871         }
872         if (timedwait(id, 2000, &elapsed, 0) < 0) {
873                 check_alarm(1);
874                 ksem_destroy(id);
875                 return;
876         }
877         if (!ELAPSED(elapsed, 1000)) {
878                 fail_err(
879             "ksem_timedwait() with delayed post took %ums instead of 1000ms",
880                     elapsed);
881                 check_alarm(1);
882                 ksem_destroy(id);
883                 return;
884         }
885         if (check_alarm(0) < 0) {
886                 ksem_destroy(id);
887                 return;
888         }
889
890         if (ksem_destroy(id) < 0) {
891                 fail_errno("ksem_destroy");
892                 return;
893         }
894         pass();
895 }
896 TEST(locked_timedwait, "locked timedwait");
897
898 static int
899 testwait(semid_t id, u_int *delta)
900 {
901         struct timespec start, end;
902
903         if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
904                 fail_errno("clock_gettime(CLOCK_REALTIME)");
905                 return (-1);
906         }
907         if (ksem_wait(id) < 0) {
908                 fail_errno("ksem_wait");
909                 return (-1);
910         }
911         if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
912                 fail_errno("clock_gettime(CLOCK_REALTIME)");
913                 return (-1);
914         }
915         timespecsub(&end, &start, &end);
916         *delta = end.tv_nsec / 1000000;
917         *delta += end.tv_sec * 1000;
918         return (0);
919 }
920
921 static void
922 unlocked_wait(void)
923 {
924         semid_t id;
925         u_int elapsed;
926
927         if (ksem_init(&id, 1) < 0) {
928                 fail_errno("ksem_init");
929                 return;
930         }
931
932         /* This should succeed right away and set the value to 0. */
933         if (testwait(id, &elapsed) < 0) {
934                 ksem_destroy(id);
935                 return;
936         }
937         if (!ELAPSED(elapsed, 0)) {
938                 fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
939                 ksem_destroy(id);
940                 return;
941         }
942         if (checkvalue(id, 0) < 0) {
943                 ksem_destroy(id);
944                 return;
945         }
946
947         if (ksem_destroy(id) < 0) {
948                 fail_errno("ksem_destroy");
949                 return;
950         }
951         pass();
952 }
953 TEST(unlocked_wait, "unlocked wait");
954
955 static void
956 locked_wait(void)
957 {
958         semid_t id;
959         u_int elapsed;
960
961         if (ksem_init(&id, 0) < 0) {
962                 fail_errno("ksem_init");
963                 return;
964         }
965
966         /*
967          * Schedule a post to trigger after 1000 ms.  The subsequent
968          * wait should succeed after 1000 ms as a result.
969          */
970         if (schedule_post(id, 1000) < 0) {
971                 ksem_destroy(id);
972                 return;
973         }
974         if (testwait(id, &elapsed) < 0) {
975                 check_alarm(1);
976                 ksem_destroy(id);
977                 return;
978         }
979         if (!ELAPSED(elapsed, 1000)) {
980                 fail_err(
981             "ksem_wait() with delayed post took %ums instead of 1000ms",
982                     elapsed);
983                 check_alarm(1);
984                 ksem_destroy(id);
985                 return;
986         }
987         if (check_alarm(0) < 0) {
988                 ksem_destroy(id);
989                 return;
990         }
991
992         if (ksem_destroy(id) < 0) {
993                 fail_errno("ksem_destroy");
994                 return;
995         }
996         pass();
997 }
998 TEST(locked_wait, "locked wait");
999
1000 /*
1001  * Fork off a child process.  The child will open the semaphore via
1002  * the same name.  The child will then block on the semaphore waiting
1003  * for the parent to post it.
1004  */
1005 static int
1006 wait_twoproc_child(void *arg)
1007 {
1008         semid_t id;
1009
1010         if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
1011                 return (CSTAT(1, errno));
1012         if (ksem_wait(id) < 0)
1013                 return (CSTAT(2, errno));
1014         if (ksem_close(id) < 0)
1015                 return (CSTAT(3, errno));
1016         return (CSTAT(0, 0));
1017 }
1018
1019 static void
1020 wait_twoproc_test(void)
1021 {
1022         semid_t id;
1023         int stat;
1024
1025         if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
1026                 fail_errno("ksem_open");
1027                 return;
1028         }
1029
1030         if (schedule_post(id, 500) < 0) {
1031                 ksem_close(id);
1032                 ksem_unlink(TEST_PATH);
1033                 return;
1034         }               
1035
1036         if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
1037                 check_alarm(1);
1038                 ksem_close(id);
1039                 ksem_unlink(TEST_PATH);
1040                 return;
1041         }
1042
1043         errno = CSTAT_ERROR(stat);
1044         switch (CSTAT_CLASS(stat)) {
1045         case 0:
1046                 pass();
1047                 break;
1048         case 1:
1049                 fail_errno("child ksem_open()");
1050                 break;
1051         case 2:
1052                 fail_errno("child ksem_wait()");
1053                 break;
1054         case 3:
1055                 fail_errno("child ksem_close()");
1056                 break;
1057         default:
1058                 fail_err("bad child state %#x", stat);
1059                 break;
1060         }
1061
1062         check_alarm(1);
1063         ksem_close(id);
1064         ksem_unlink(TEST_PATH);
1065 }
1066 TEST(wait_twoproc_test, "two proc wait");
1067
1068 static void
1069 maxvalue_test(void)
1070 {
1071         semid_t id;
1072         int val;
1073
1074         if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1075                 fail_errno("ksem_init");
1076                 return;
1077         }
1078         if (ksem_getvalue(id, &val) < 0) {
1079                 fail_errno("ksem_getvalue");
1080                 ksem_destroy(id);
1081                 return;
1082         }
1083         if (val != SEM_VALUE_MAX) {
1084                 fail_err("value %d != SEM_VALUE_MAX");
1085                 ksem_destroy(id);
1086                 return;
1087         }
1088         if (val < 0) {
1089                 fail_err("value < 0");
1090                 ksem_destroy(id);
1091                 return;
1092         }
1093         if (ksem_destroy(id) < 0) {
1094                 fail_errno("ksem_destroy");
1095                 return;
1096         }
1097         pass();
1098 }
1099 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1100
1101 static void
1102 maxvalue_post_test(void)
1103 {
1104         semid_t id;
1105
1106         if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1107                 fail_errno("ksem_init");
1108                 return;
1109         }
1110
1111         ksem_post_should_fail(id, EOVERFLOW);
1112
1113         ksem_destroy(id);
1114 }
1115 TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1116
1117 static void
1118 busy_destroy_test(void)
1119 {
1120         char errbuf[_POSIX2_LINE_MAX];
1121         struct kinfo_proc *kp;
1122         semid_t id;
1123         pid_t pid;
1124         kvm_t *kd;
1125         int count;
1126
1127         kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
1128         if (kd == NULL) {
1129                 fail_err("kvm_openfiles: %s", errbuf);
1130                 return;
1131         }
1132
1133         if (ksem_init(&id, 0) < 0) {
1134                 fail_errno("ksem_init");
1135                 kvm_close(kd);
1136                 return;
1137         }
1138
1139         pid = fork();
1140         switch (pid) {
1141         case -1:
1142                 /* Error. */
1143                 fail_errno("fork");
1144                 ksem_destroy(id);
1145                 kvm_close(kd);
1146                 return;
1147         case 0:
1148                 /* Child. */
1149                 ksem_wait(id);
1150                 exit(0);
1151         }
1152
1153         /*
1154          * Wait for the child process to block on the semaphore.  This
1155          * is a bit gross.
1156          */
1157         for (;;) {
1158                 kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
1159                 if (kp == NULL) {
1160                         fail_err("kvm_getprocs: %s", kvm_geterr(kd));
1161                         kvm_close(kd);
1162                         ksem_destroy(id);
1163                         return;
1164                 }
1165                 if (kp->ki_stat == SSLEEP &&
1166                     (strcmp(kp->ki_wmesg, "sem") == 0 ||
1167                     strcmp(kp->ki_wmesg, "ksem") == 0))
1168                         break;
1169                 usleep(1000);
1170         }
1171         kvm_close(kd);
1172
1173         ksem_destroy_should_fail(id, EBUSY);
1174
1175         /* Cleanup. */
1176         ksem_post(id);
1177         waitpid(pid, NULL, 0);
1178         ksem_destroy(id);
1179 }
1180 TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1181
1182 static int
1183 exhaust_unnamed_child(void *arg)
1184 {
1185         semid_t id;
1186         int i, max;
1187
1188         max = (intptr_t)arg;
1189         for (i = 0; i < max + 1; i++) {
1190                 if (ksem_init(&id, 1) < 0) {
1191                         if (errno == ENOSPC)
1192                                 return (CSTAT(0, 0));
1193                         return (CSTAT(1, errno));
1194                 }
1195         }
1196         return (CSTAT(2, 0));
1197 }
1198
1199 static void
1200 exhaust_unnamed_sems(void)
1201 {
1202         size_t len;
1203         int nsems_max, stat;
1204
1205         len = sizeof(nsems_max);
1206         if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1207             0) {
1208                 fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1209                 return;
1210         }
1211
1212         if (child_worker(exhaust_unnamed_child, (void *)(uintptr_t)nsems_max,
1213             &stat))
1214                 return;
1215         errno = CSTAT_ERROR(stat);
1216         switch (CSTAT_CLASS(stat)) {
1217         case 0:
1218                 pass();
1219                 break;
1220         case 1:
1221                 fail_errno("ksem_init");
1222                 break;
1223         case 2:
1224                 fail_err("Limit of %d semaphores not enforced", nsems_max);
1225                 break;
1226         default:
1227                 fail_err("bad child state %#x", stat);
1228                 break;
1229         }
1230 }
1231 TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1232
1233 static int
1234 exhaust_named_child(void *arg)
1235 {
1236         char buffer[64];
1237         semid_t id;
1238         int i, max;
1239
1240         max = (intptr_t)arg;
1241         for (i = 0; i < max + 1; i++) {
1242                 snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1243                 if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
1244                         if (errno == ENOSPC || errno == EMFILE ||
1245                             errno == ENFILE)
1246                                 return (CSTAT(0, 0));
1247                         return (CSTAT(1, errno));
1248                 }
1249         }
1250         return (CSTAT(2, errno));
1251 }
1252
1253 static void
1254 exhaust_named_sems(void)
1255 {
1256         char buffer[64];
1257         size_t len;
1258         int i, nsems_max, stat;
1259
1260         len = sizeof(nsems_max);
1261         if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1262             0) {
1263                 fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1264                 return;
1265         }
1266
1267         if (child_worker(exhaust_named_child, (void *)(uintptr_t)nsems_max,
1268             &stat) < 0)
1269                 return;
1270         errno = CSTAT_ERROR(stat);
1271         switch (CSTAT_CLASS(stat)) {
1272         case 0:
1273                 pass();
1274                 break;
1275         case 1:
1276                 fail_errno("ksem_open");
1277                 break;
1278         case 2:
1279                 fail_err("Limit of %d semaphores not enforced", nsems_max);
1280                 break;
1281         default:
1282                 fail_err("bad child state %#x", stat);
1283                 break;
1284         }
1285
1286         /* Cleanup any semaphores created by the child. */
1287         for (i = 0; i < nsems_max + 1; i++) {
1288                 snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1289                 ksem_unlink(buffer);
1290         }
1291 }
1292 TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1293
1294 static int
1295 fdlimit_set(void *arg)
1296 {
1297         struct rlimit rlim;
1298         int max;
1299
1300         max = (intptr_t)arg;
1301         if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
1302                 return (CSTAT(3, errno));
1303         rlim.rlim_cur = max;
1304         if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
1305                 return (CSTAT(4, errno));
1306         return (0);
1307 }
1308
1309 static int
1310 fdlimit_unnamed_child(void *arg)
1311 {
1312         int stat;
1313
1314         stat = fdlimit_set(arg);
1315         if (stat == 0)
1316                 stat = exhaust_unnamed_child(arg);
1317         return (stat);
1318 }
1319
1320 static void
1321 fdlimit_unnamed_sems(void)
1322 {
1323         int nsems_max, stat;
1324
1325         nsems_max = 10;
1326         if (child_worker(fdlimit_unnamed_child, (void *)(uintptr_t)nsems_max,
1327             &stat))
1328                 return;
1329         errno = CSTAT_ERROR(stat);
1330         switch (CSTAT_CLASS(stat)) {
1331         case 0:
1332                 pass();
1333                 break;
1334         case 1:
1335                 fail_errno("ksem_init");
1336                 break;
1337         case 2:
1338                 fail_err("Limit of %d semaphores not enforced", nsems_max);
1339                 break;
1340         case 3:
1341                 fail_errno("getrlimit");
1342                 break;
1343         case 4:
1344                 fail_errno("getrlimit");
1345                 break;
1346         default:
1347                 fail_err("bad child state %#x", stat);
1348                 break;
1349         }
1350 }
1351 TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1352
1353 static int
1354 fdlimit_named_child(void *arg)
1355 {
1356         int stat;
1357
1358         stat = fdlimit_set(arg);
1359         if (stat == 0)
1360                 stat = exhaust_named_child(arg);
1361         return (stat);
1362 }
1363
1364 static void
1365 fdlimit_named_sems(void)
1366 {
1367         char buffer[64];
1368         int i, nsems_max, stat;
1369
1370         nsems_max = 10;
1371         if (child_worker(fdlimit_named_child, (void *)(uintptr_t)nsems_max,
1372             &stat) < 0)
1373                 return;
1374         errno = CSTAT_ERROR(stat);
1375         switch (CSTAT_CLASS(stat)) {
1376         case 0:
1377                 pass();
1378                 break;
1379         case 1:
1380                 fail_errno("ksem_open");
1381                 break;
1382         case 2:
1383                 fail_err("Limit of %d semaphores not enforced", nsems_max);
1384                 break;
1385         case 3:
1386                 fail_errno("getrlimit");
1387                 break;
1388         case 4:
1389                 fail_errno("getrlimit");
1390                 break;
1391         default:
1392                 fail_err("bad child state %#x", stat);
1393                 break;
1394         }
1395
1396         /* Cleanup any semaphores created by the child. */
1397         for (i = 0; i < nsems_max + 1; i++) {
1398                 snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1399                 ksem_unlink(buffer);
1400         }
1401 }
1402 TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1403
1404 int
1405 main(int argc, char *argv[])
1406 {
1407
1408         signal(SIGSYS, SIG_IGN);
1409         run_tests();
1410         return (0);
1411 }