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