Merge branch 'vendor/GCC50'
[dragonfly.git] / test / testcases / posixipc / sem / posixsem.c
1 /*-
2  * Copyright (c) 2008 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 #include <sys/mman.h>
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/types.h>
40 #include <sys/user.h>
41 #include <sys/wait.h>
42
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <kvm.h>
46 #include <limits.h>
47 #include <semaphore.h>
48 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54
55 #include "test.h"
56
57
58 /* Cut and pasted from kernel header, bah! */
59
60 /* Operations on timespecs */
61 #define timespecclear(tvp)      ((tvp)->tv_sec = (tvp)->tv_nsec = 0)
62 #define timespecisset(tvp)      ((tvp)->tv_sec || (tvp)->tv_nsec)
63 #define timespeccmp(tvp, uvp, cmp)                                      \
64         (((tvp)->tv_sec == (uvp)->tv_sec) ?                             \
65             ((tvp)->tv_nsec cmp (uvp)->tv_nsec) :                       \
66             ((tvp)->tv_sec cmp (uvp)->tv_sec))
67 #define timespecadd(vvp, uvp)                                           \
68         do {                                                            \
69                 (vvp)->tv_sec += (uvp)->tv_sec;                         \
70                 (vvp)->tv_nsec += (uvp)->tv_nsec;                       \
71                 if ((vvp)->tv_nsec >= 1000000000) {                     \
72                         (vvp)->tv_sec++;                                \
73                         (vvp)->tv_nsec -= 1000000000;                   \
74                 }                                                       \
75         } while (0)
76 #define timespecsub(vvp, uvp)                                           \
77         do {                                                            \
78                 (vvp)->tv_sec -= (uvp)->tv_sec;                         \
79                 (vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
80                 if ((vvp)->tv_nsec < 0) {                               \
81                         (vvp)->tv_sec--;                                \
82                         (vvp)->tv_nsec += 1000000000;                   \
83                 }                                                       \
84         } while (0)
85
86
87 #define TEST_PATH       "/posixsem_regression_test"
88
89 #define ELAPSED(elapsed, limit)         (abs((elapsed) - (limit)) < 100)
90
91 /* Macros for passing child status to parent over a pipe. */
92 #define CSTAT(class, error)             ((class) << 16 | (error))
93 #define CSTAT_CLASS(stat)               ((stat) >> 16)
94 #define CSTAT_ERROR(stat)               ((stat) & 0xffff)
95
96 static sem_t *
97 construct_shared_unnamed_sem(unsigned int count)
98 {
99         sem_t *id = mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE,
100                          MAP_SHARED|MAP_ANON, -1, 0);
101         if (id == MAP_FAILED) {
102                 fail_errno("mmap");
103                 return SEM_FAILED;
104         }
105
106         if (sem_init(id, 1, count) < 0) {
107                 fail_errno("sem_init");
108                 munmap(id, sizeof(sem_t));
109                 return SEM_FAILED;
110         }
111
112         return id;
113 }
114
115 static void
116 destruct_shared_unnamed_sem(sem_t *id)
117 {
118         if (sem_destroy(id) < 0)
119                 fail_errno("sem_destroy");
120
121         if (munmap(id, sizeof(sem_t)) < 0)
122                 fail_errno("munmap");
123 }
124
125 /*
126  * Helper routine for tests that use a child process.  This routine
127  * creates a pipe and forks a child process.  The child process runs
128  * the 'func' routine which returns a status integer.  The status
129  * integer gets written over the pipe to the parent and returned in
130  * '*stat'.  If there is an error in pipe(), fork(), or wait() this
131  * returns -1 and fails the test.
132  */
133 static int
134 child_worker(int (*func)(void *arg), void *arg, int *stat)
135 {
136         pid_t pid;
137         int pfd[2], cstat;
138
139         if (pipe(pfd) < 0) {
140                 fail_errno("pipe");
141                 return (-1);
142         }
143
144         pid = fork();
145         switch (pid) {
146         case -1:
147                 /* Error. */
148                 fail_errno("fork");
149                 close(pfd[0]);
150                 close(pfd[1]);
151                 return (-1);
152         case 0:
153                 /* Child. */
154                 cstat = func(arg);
155                 write(pfd[1], &cstat, sizeof(cstat));
156                 exit(0);
157         }
158
159         if (read(pfd[0], stat, sizeof(*stat)) < 0) {
160                 fail_errno("read(pipe)");
161                 close(pfd[0]);
162                 close(pfd[1]);
163                 return (-1);
164         }
165         if (waitpid(pid, NULL, 0) < 0) {
166                 fail_errno("wait");
167                 close(pfd[0]);
168                 close(pfd[1]);
169                 return (-1);
170         }
171         close(pfd[0]);
172         close(pfd[1]);
173         return (0);
174 }
175
176 /*
177  * Attempt a sem_open() that should fail with an expected error of
178  * 'error'.
179  */
180 static void
181 sem_open_should_fail(const char *path, int flags, mode_t mode,
182                      unsigned int value, int error)
183 {
184         sem_t *id;
185
186         id = sem_open(path, flags, mode, value);
187         if (id != SEM_FAILED) {
188                 fail_err("sem_open() didn't fail");
189                 sem_close(id);
190                 return;
191         }
192         if (errno != error) {
193                 fail_errno("sem_open");
194                 return;
195         }
196         pass();
197 }
198
199 /*
200  * Attempt a sem_unlink() that should fail with an expected error of
201  * 'error'.
202  */
203 static void
204 sem_unlink_should_fail(const char *path, int error)
205 {
206
207         if (sem_unlink(path) >= 0) {
208                 fail_err("sem_unlink() didn't fail");
209                 return;
210         }
211         if (errno != error) {
212                 fail_errno("sem_unlink");
213                 return;
214         }
215         pass();
216 }
217
218 /*
219  * Attempt a sem_close() that should fail with an expected error of
220  * 'error'.
221  */
222 static void
223 sem_close_should_fail(sem_t *id, int error)
224 {
225
226         if (sem_close(id) >= 0) {
227                 fail_err("sem_close() didn't fail");
228                 return;
229         }
230         if (errno != error) {
231                 fail_errno("sem_close");
232                 return;
233         }
234         pass();
235 }
236
237 /*
238  * Attempt a sem_init() that should fail with an expected error of
239  * 'error'.
240  */
241 static void
242 sem_init_should_fail(unsigned int value, int error)
243 {
244         sem_t id;
245
246         if (sem_init(&id, 0, value) >= 0) {
247                 fail_err("sem_init() didn't fail");
248                 sem_destroy(&id);
249                 return;
250         }
251         if (errno != error) {
252                 fail_errno("sem_init");
253                 return;
254         }
255         pass();
256 }
257
258 /*
259  * Attempt a sem_destroy() that should fail with an expected error of
260  * 'error'.
261  */
262 static void
263 sem_destroy_should_fail(sem_t *id, int error)
264 {
265
266         if (sem_destroy(id) >= 0) {
267                 fail_err("sem_destroy() didn't fail");
268                 return;
269         }
270         if (errno != error) {
271                 fail_errno("sem_destroy");
272                 return;
273         }
274         pass();
275 }
276
277 static void
278 open_after_unlink(void)
279 {
280         sem_t *id;
281
282         id = sem_open(TEST_PATH, O_CREAT, 0777, 1);
283         if (id == SEM_FAILED) {
284                 fail_errno("sem_open(1)");
285                 return;
286         }
287         sem_close(id);
288
289         if (sem_unlink(TEST_PATH) < 0) {
290                 fail_errno("sem_unlink");
291                 return;
292         }
293
294         sem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
295 }
296 TEST(open_after_unlink, "open after unlink");
297
298 static void
299 open_invalid_path(void)
300 {
301
302         sem_open_should_fail("blah", 0, 0777, 1, ENOENT);
303 }
304 TEST(open_invalid_path, "open invalid path");
305
306 static void
307 open_extra_flags(void)
308 {
309
310         sem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
311 }
312 TEST(open_extra_flags, "open with extra flags");
313
314 static void
315 open_bad_value(void)
316 {
317
318         (void)sem_unlink(TEST_PATH);
319
320         sem_open_should_fail(TEST_PATH, O_CREAT, 0777, SEM_VALUE_MAX+1U, EINVAL);
321 }
322 TEST(open_bad_value, "open with invalid initial value");
323
324 static void
325 open_path_too_long(void)
326 {
327         char *page;
328
329         page = malloc(MAXPATHLEN + 1);
330         memset(page, 'a', MAXPATHLEN);
331         page[0] = '/';
332         page[MAXPATHLEN] = '\0';
333         sem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
334         free(page);
335 }
336 TEST(open_path_too_long, "open pathname too long");
337
338 static void
339 open_nonexisting_semaphore(void)
340 {
341         sem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
342 }
343
344 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
345
346 static void
347 exclusive_create_existing_semaphore(void)
348 {
349         sem_t *id;
350
351         id = sem_open(TEST_PATH, O_CREAT, 0777, 1);
352         if (id == SEM_FAILED) {
353                 fail_errno("sem_open(O_CREAT)");
354                 return;
355         }
356         sem_close(id);
357
358         sem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
359
360         sem_unlink(TEST_PATH);
361 }
362 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
363
364 static void
365 init_bad_value(void)
366 {
367
368         sem_init_should_fail(SEM_VALUE_MAX+1U, EINVAL);
369 }
370 TEST(init_bad_value, "init with invalid initial value");
371
372 static void
373 unlink_path_too_long(void)
374 {
375         char *page;
376
377         page = malloc(MAXPATHLEN + 1);
378         memset(page, 'a', MAXPATHLEN);
379         page[MAXPATHLEN] = '\0';
380         sem_unlink_should_fail(page, ENAMETOOLONG);
381         free(page);
382 }
383 TEST(unlink_path_too_long, "unlink pathname too long");
384
385 static void
386 destroy_named_semaphore(void)
387 {
388         sem_t *id;
389
390         id = sem_open(TEST_PATH, O_CREAT, 0777, 1);
391         if (id == SEM_FAILED) {
392                 fail_errno("sem_open(O_CREAT)");
393                 return;
394         }
395
396         sem_destroy_should_fail(id, EINVAL);
397
398         sem_close(id);
399         sem_unlink(TEST_PATH);
400 }
401 TEST(destroy_named_semaphore, "destroy named semaphore");
402
403 static void
404 close_unnamed_semaphore(void)
405 {
406         sem_t id;
407
408         if (sem_init(&id, 0, 1) < 0) {
409                 fail_errno("sem_init");
410                 return;
411         }
412
413         sem_close_should_fail(&id, EINVAL);
414
415         sem_destroy(&id);
416 }
417 TEST(close_unnamed_semaphore, "close unnamed semaphore");
418
419 static void
420 create_unnamed_semaphore(void)
421 {
422         sem_t id;
423
424         if (sem_init(&id, 0, 1) < 0) {
425                 fail_errno("sem_init");
426                 return;
427         }
428
429         if (sem_destroy(&id) < 0) {
430                 fail_errno("sem_destroy");
431                 return;
432         }
433         pass();
434 }
435 TEST(create_unnamed_semaphore, "create unnamed semaphore");
436
437 static void
438 open_named_semaphore(void)
439 {
440         sem_t *id;
441
442         id = sem_open(TEST_PATH, O_CREAT, 0777, 1);
443         if (id == SEM_FAILED) {
444                 fail_errno("sem_open(O_CREAT)");
445                 return;
446         }
447
448         if (sem_close(id) < 0) {
449                 fail_errno("sem_close");
450                 return;
451         }
452
453         if (sem_unlink(TEST_PATH) < 0) {
454                 fail_errno("sem_unlink");
455                 return;
456         }
457         pass();
458 }
459 TEST(open_named_semaphore, "create named semaphore");
460
461 static int
462 checkvalue(sem_t *id, int expected)
463 {
464         int val;
465
466         if (sem_getvalue(id, &val) < 0) {
467                 fail_errno("sem_getvalue");
468                 return (-1);
469         }
470         if (val != expected) {
471                 fail_err("sem value should be %d instead of %d", expected, val);
472                 return (-1);
473         }
474         return (0);
475 }
476
477 static void
478 post_test(void)
479 {
480         sem_t id;
481
482         if (sem_init(&id, 0, 1) < 0) {
483                 fail_errno("sem_init");
484                 return;
485         }
486         if (checkvalue(&id, 1) < 0) {
487                 sem_destroy(&id);
488                 return;
489         }
490         if (sem_post(&id) < 0) {
491                 fail_errno("sem_post");
492                 sem_destroy(&id);
493                 return;
494         }
495         if (checkvalue(&id, 2) < 0) {
496                 sem_destroy(&id);
497                 return;
498         }
499         if (sem_destroy(&id) < 0) {
500                 fail_errno("sem_destroy");
501                 return;
502         }
503         pass();
504 }
505 TEST(post_test, "simple post");
506
507 static void
508 use_after_unlink_test(void)
509 {
510         sem_t *id;
511
512         /*
513          * Create named semaphore with value of 1 and then unlink it
514          * while still retaining the initial reference.
515          */
516         id = sem_open(TEST_PATH, O_CREAT | O_EXCL, 0777, 1);
517         if (id == SEM_FAILED) {
518                 fail_errno("sem_open(O_CREAT | O_EXCL)");
519                 return;
520         }
521         if (sem_unlink(TEST_PATH) < 0) {
522                 fail_errno("sem_unlink");
523                 sem_close(id);
524                 return;
525         }
526         if (checkvalue(id, 1) < 0) {
527                 sem_close(id);
528                 return;
529         }
530
531         /* Post the semaphore to set its value to 2. */
532         if (sem_post(id) < 0) {
533                 fail_errno("sem_post");
534                 sem_close(id);
535                 return;
536         }
537         if (checkvalue(id, 2) < 0) {
538                 sem_close(id);
539                 return;
540         }
541
542         /* Wait on the semaphore which should set its value to 1. */
543         if (sem_wait(id) < 0) {
544                 fail_errno("sem_wait");
545                 sem_close(id);
546                 return;
547         }
548         if (checkvalue(id, 1) < 0) {
549                 sem_close(id);
550                 return;
551         }
552
553         if (sem_close(id) < 0) {
554                 fail_errno("sem_close");
555                 return;
556         }
557         pass();
558 }
559 TEST(use_after_unlink_test, "use named semaphore after unlink");
560
561 static void
562 unlocked_trywait(void)
563 {
564         sem_t id;
565
566         if (sem_init(&id, 0, 1) < 0) {
567                 fail_errno("sem_init");
568                 return;
569         }
570
571         /* This should succeed and decrement the value to 0. */
572         if (sem_trywait(&id) < 0) {
573                 fail_errno("sem_trywait()");
574                 sem_destroy(&id);
575                 return;
576         }
577         if (checkvalue(&id, 0) < 0) {
578                 sem_destroy(&id);
579                 return;
580         }
581
582         if (sem_destroy(&id) < 0) {
583                 fail_errno("sem_destroy");
584                 return;
585         }
586         pass();
587 }
588 TEST(unlocked_trywait, "unlocked trywait");
589
590 static void
591 locked_trywait(void)
592 {
593         sem_t id;
594
595         if (sem_init(&id, 0, 0) < 0) {
596                 fail_errno("sem_init");
597                 return;
598         }
599
600         /* This should fail with EAGAIN and leave the value at 0. */
601         if (sem_trywait(&id) >= 0) {
602                 fail_err("sem_trywait() didn't fail");
603                 sem_destroy(&id);
604                 return;
605         }
606         if (errno != EAGAIN) {
607                 fail_errno("wrong error from sem_trywait()");
608                 sem_destroy(&id);
609                 return;
610         }
611         if (checkvalue(&id, 0) < 0) {
612                 sem_destroy(&id);
613                 return;
614         }
615
616         if (sem_destroy(&id) < 0) {
617                 fail_errno("sem_destroy");
618                 return;
619         }
620         pass();
621 }
622 TEST(locked_trywait, "locked trywait");
623
624 /*
625  * Use a timer to post a specific semaphore after a timeout.  A timer
626  * is scheduled via schedule_post().  check_alarm() must be called
627  * afterwards to clean up and check for errors.
628  */
629 static sem_t *alarm_id = SEM_FAILED;
630 static int alarm_errno;
631 static int alarm_handler_installed;
632
633 static void
634 alarm_handler(int signo)
635 {
636
637         if (sem_post(alarm_id) < 0)
638                 alarm_errno = errno;
639 }
640
641 static int
642 check_alarm(int just_clear)
643 {
644         struct itimerval it;
645
646         bzero(&it, sizeof(it));
647         if (just_clear) {
648                 setitimer(ITIMER_REAL, &it, NULL);
649                 alarm_errno = 0;
650                 alarm_id = SEM_FAILED;
651                 return (0);
652         }
653         if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
654                 fail_errno("setitimer");
655                 return (-1);
656         }
657         if (alarm_errno != 0 && !just_clear) {
658                 errno = alarm_errno;
659                 fail_errno("sem_post() (via timeout)");
660                 alarm_errno = 0;
661                 return (-1);
662         }
663         alarm_id = SEM_FAILED;
664
665         return (0);
666 }
667
668 static int
669 schedule_post(sem_t *id, u_int msec)
670 {
671         struct itimerval it;
672
673         if (!alarm_handler_installed) {
674                 if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
675                         fail_errno("signal(SIGALRM)");
676                         return (-1);
677                 }
678                 alarm_handler_installed = 1;
679         }
680         if (alarm_id != SEM_FAILED) {
681                 fail_err("sem_post() already scheduled");
682                 return (-1);
683         }
684         alarm_id = id;
685         bzero(&it, sizeof(it));
686         it.it_value.tv_sec = msec / 1000;
687         it.it_value.tv_usec = (msec % 1000) * 1000;
688         if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
689                 fail_errno("setitimer");
690                 return (-1);
691         }
692         return (0);
693 }
694
695 static int
696 timedwait(sem_t *id, u_int msec, u_int *delta, int error)
697 {
698         struct timespec start, end;
699
700         if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
701                 fail_errno("clock_gettime(CLOCK_REALTIME)");
702                 return (-1);
703         }
704         end.tv_sec = msec / 1000;
705         end.tv_nsec = msec % 1000 * 1000000;
706         timespecadd(&end, &start);
707         if (sem_timedwait(id, &end) < 0) {
708                 if (errno != error) {
709                         fail_errno("sem_timedwait");
710                         return (-1);
711                 }
712         } else if (error != 0) {
713                 fail_err("sem_timedwait() didn't fail");
714                 return (-1);
715         }
716         if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
717                 fail_errno("clock_gettime(CLOCK_REALTIME)");
718                 return (-1);
719         }
720         timespecsub(&end, &start);
721         *delta = end.tv_nsec / 1000000;
722         *delta += end.tv_sec * 1000;
723         return (0);
724 }
725
726 static void
727 unlocked_timedwait(void)
728 {
729         sem_t id;
730         u_int elapsed;
731
732         if (sem_init(&id, 0, 1) < 0) {
733                 fail_errno("sem_init");
734                 return;
735         }
736
737         /* This should succeed right away and set the value to 0. */
738         if (timedwait(&id, 5000, &elapsed, 0) < 0) {
739                 sem_destroy(&id);
740                 return;
741         }
742         if (!ELAPSED(elapsed, 0)) {
743                 fail_err("sem_timedwait() of unlocked sem took %ums", elapsed);
744                 sem_destroy(&id);
745                 return;
746         }
747         if (checkvalue(&id, 0) < 0) {
748                 sem_destroy(&id);
749                 return;
750         }
751
752         if (sem_destroy(&id) < 0) {
753                 fail_errno("sem_destroy");
754                 return;
755         }
756         pass();
757 }
758 TEST(unlocked_timedwait, "unlocked timedwait");
759
760 static void
761 expired_timedwait(void)
762 {
763         sem_t id;
764         u_int elapsed;
765
766         if (sem_init(&id, 0, 0) < 0) {
767                 fail_errno("sem_init");
768                 return;
769         }
770
771         /* This should fail with a timeout and leave the value at 0. */
772         if (timedwait(&id, 2500, &elapsed, ETIMEDOUT) < 0) {
773                 sem_destroy(&id);
774                 return;
775         }
776         if (!ELAPSED(elapsed, 2500)) {
777                 fail_err("sem_timedwait() of locked sem took %ums instead of 2500ms",
778                          elapsed);
779                 sem_destroy(&id);
780                 return;
781         }
782         if (checkvalue(&id, 0) < 0) {
783                 sem_destroy(&id);
784                 return;
785         }
786
787         if (sem_destroy(&id) < 0) {
788                 fail_errno("sem_destroy");
789                 return;
790         }
791         pass();
792 }
793 TEST(expired_timedwait, "locked timedwait timeout");
794
795 static void
796 locked_timedwait(void)
797 {
798         sem_t *id;
799         u_int elapsed;
800         pid_t pid;
801
802         id = construct_shared_unnamed_sem(0);
803         if (id == SEM_FAILED) {
804                 fail_err("construct sem");
805                 return;
806         }
807
808         pid = fork();
809         switch (pid) {
810         case -1:
811                 /* Error. */
812                 fail_errno("fork");
813                 destruct_shared_unnamed_sem(id);
814                 return;
815         case 0:
816                 /* Child. */
817                 sleep(1);
818                 sem_post(id);
819                 exit(0);
820         }
821
822         if (timedwait(id, 2000, &elapsed, 0) < 0) {
823                 destruct_shared_unnamed_sem(id);
824                 return;
825         }
826         if (!ELAPSED(elapsed, 1000)) {
827                 fail_err("sem_timedwait() with delayed post took %ums instead of 1000ms",
828                          elapsed);
829                 destruct_shared_unnamed_sem(id);
830                 return;
831         }
832
833         destruct_shared_unnamed_sem(id);
834
835         pass();
836 }
837 TEST(locked_timedwait, "locked timedwait");
838
839 static int
840 testwait(sem_t *id, u_int *delta)
841 {
842         struct timespec start, end;
843
844         if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
845                 fail_errno("clock_gettime(CLOCK_REALTIME)");
846                 return (-1);
847         }
848         if (sem_wait(id) < 0) {
849                 fail_errno("sem_wait");
850                 return (-1);
851         }
852         if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
853                 fail_errno("clock_gettime(CLOCK_REALTIME)");
854                 return (-1);
855         }
856         timespecsub(&end, &start);
857         *delta = end.tv_nsec / 1000000;
858         *delta += end.tv_sec * 1000;
859         return (0);
860 }
861
862 static void
863 unlocked_wait(void)
864 {
865         sem_t id;
866         u_int elapsed;
867
868         if (sem_init(&id, 0, 1) < 0) {
869                 fail_errno("sem_init");
870                 return;
871         }
872
873         /* This should succeed right away and set the value to 0. */
874         if (testwait(&id, &elapsed) < 0) {
875                 sem_destroy(&id);
876                 return;
877         }
878         if (!ELAPSED(elapsed, 0)) {
879                 fail_err("sem_wait() of unlocked sem took %ums", elapsed);
880                 sem_destroy(&id);
881                 return;
882         }
883         if (checkvalue(&id, 0) < 0) {
884                 sem_destroy(&id);
885                 return;
886         }
887
888         if (sem_destroy(&id) < 0) {
889                 fail_errno("sem_destroy");
890                 return;
891         }
892         pass();
893 }
894 TEST(unlocked_wait, "unlocked wait");
895
896 static void
897 locked_wait(void)
898 {
899         sem_t *id;
900         u_int elapsed;
901         pid_t pid;
902
903         id = construct_shared_unnamed_sem(0);
904
905         pid = fork();
906         switch (pid) {
907         case -1:
908                 /* Error. */
909                 fail_errno("fork");
910                 destruct_shared_unnamed_sem(id);
911                 return;
912         case 0:
913                 /* Child. */
914                 sleep(1);
915                 sem_post(id);
916                 exit(0);
917         }
918
919         if (testwait(id, &elapsed) < 0) {
920                 destruct_shared_unnamed_sem(id);
921                 return;
922         }
923         if (!ELAPSED(elapsed, 1000)) {
924                 fail_err("sem_wait() with delayed post took %ums instead of 1000ms",
925                          elapsed);
926                 destruct_shared_unnamed_sem(id);
927                 return;
928         }
929
930         destruct_shared_unnamed_sem(id);
931
932         pass();
933 }
934 TEST(locked_wait, "locked wait");
935
936 /*
937  * Fork off a child process.  The child will open the semaphore via
938  * the same name.  The child will then block on the semaphore waiting
939  * for the parent to post it.
940  */
941 static int
942 wait_twoproc_child(void *arg)
943 {
944         sem_t *id;
945
946         id = sem_open(TEST_PATH, 0, 0, 0);
947         if (id == SEM_FAILED)
948                 return (CSTAT(1, errno));
949         if (sem_wait(id) < 0)
950                 return (CSTAT(2, errno));
951         if (sem_close(id) < 0)
952                 return (CSTAT(3, errno));
953         return (CSTAT(0, 0));
954 }
955
956 static void
957 wait_twoproc_test(void)
958 {
959         sem_t *id;
960         int stat;
961
962         id = sem_open(TEST_PATH, O_CREAT, 0777, 0);
963         if (id == SEM_FAILED) {
964                 fail_errno("sem_open");
965                 return;
966         }
967
968         if (schedule_post(id, 500) < 0) {
969                 sem_close(id);
970                 sem_unlink(TEST_PATH);
971                 return;
972         }               
973
974         if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
975                 check_alarm(1);
976                 sem_close(id);
977                 sem_unlink(TEST_PATH);
978                 return;
979         }
980
981         errno = CSTAT_ERROR(stat);
982         switch (CSTAT_CLASS(stat)) {
983         case 0:
984                 pass();
985                 break;
986         case 1:
987                 fail_errno("child sem_open()");
988                 break;
989         case 2:
990                 fail_errno("child sem_wait()");
991                 break;
992         case 3:
993                 fail_errno("child sem_close()");
994                 break;
995         default:
996                 fail_err("bad child state %#x", stat);
997                 break;
998         }
999
1000         check_alarm(1);
1001         sem_close(id);
1002         sem_unlink(TEST_PATH);
1003 }
1004 TEST(wait_twoproc_test, "two proc wait");
1005
1006 static void
1007 maxvalue_test(void)
1008 {
1009         sem_t id;
1010         int val;
1011
1012         if (sem_init(&id, 0, SEM_VALUE_MAX) < 0) {
1013                 fail_errno("sem_init");
1014                 return;
1015         }
1016         if (sem_getvalue(&id, &val) < 0) {
1017                 fail_errno("sem_getvalue");
1018                 sem_destroy(&id);
1019                 return;
1020         }
1021         if (val != SEM_VALUE_MAX) {
1022                 fail_err("value %d != SEM_VALUE_MAX");
1023                 sem_destroy(&id);
1024                 return;
1025         }
1026         if (val < 0) {
1027                 fail_err("value < 0");
1028                 sem_destroy(&id);
1029                 return;
1030         }
1031         if (sem_destroy(&id) < 0) {
1032                 fail_errno("sem_destroy");
1033                 return;
1034         }
1035         pass();
1036 }
1037 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1038
1039 static void
1040 file_test(void)
1041 {
1042         struct stat sb;
1043         sem_t *id;
1044         int error;
1045
1046         id = sem_open(TEST_PATH, O_CREAT, 0777, 0);
1047         if (id == SEM_FAILED) {
1048                 fail_errno("sem_open");
1049                 return;
1050         }
1051
1052         error = stat("/var/run/sem", &sb);
1053         if (error) {
1054                 fail_errno("stat");
1055                 return;
1056         }
1057         if ((sb.st_mode & ALLPERMS) != (S_IRWXU|S_IRWXG|S_IRWXO|S_ISTXT)) {
1058                 fail_err("semaphore dir has incorrect mode: 0%o\n",
1059                          (sb.st_mode & ALLPERMS));
1060                 return;
1061         }
1062
1063         sem_close(id);
1064         pass();
1065 }
1066
1067 TEST(file_test, "check semaphore directory has correct mode bits");
1068
1069 int
1070 main(int argc, char *argv[])
1071 {
1072
1073         signal(SIGSYS, SIG_IGN);
1074         run_tests();
1075         return (test_exit_value);
1076 }