1180059Sjhb/*-
2180059Sjhb * Copyright (c) 2008 Yahoo!, Inc.
3180059Sjhb * All rights reserved.
4180059Sjhb * Written by: John Baldwin <jhb@FreeBSD.org>
5180059Sjhb *
6180059Sjhb * Redistribution and use in source and binary forms, with or without
7180059Sjhb * modification, are permitted provided that the following conditions
8180059Sjhb * are met:
9180059Sjhb * 1. Redistributions of source code must retain the above copyright
10180059Sjhb *    notice, this list of conditions and the following disclaimer.
11180059Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12180059Sjhb *    notice, this list of conditions and the following disclaimer in the
13180059Sjhb *    documentation and/or other materials provided with the distribution.
14180059Sjhb * 3. Neither the name of the author nor the names of any co-contributors
15180059Sjhb *    may be used to endorse or promote products derived from this software
16180059Sjhb *    without specific prior written permission.
17180059Sjhb *
18180059Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19180059Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20180059Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21180059Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22180059Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23180059Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24180059Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25180059Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26180059Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27180059Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28180059Sjhb * SUCH DAMAGE.
29180059Sjhb */
30180059Sjhb
31180059Sjhb#include <sys/cdefs.h>
32180059Sjhb__FBSDID("$FreeBSD$");
33180059Sjhb
34180059Sjhb#include <sys/param.h>
35180059Sjhb#include <sys/queue.h>
36180059Sjhb#include <sys/_semaphore.h>
37180059Sjhb#include <sys/sysctl.h>
38180059Sjhb#include <sys/time.h>
39180059Sjhb#include <sys/user.h>
40180059Sjhb#include <sys/wait.h>
41180059Sjhb
42180059Sjhb#include <errno.h>
43180059Sjhb#include <fcntl.h>
44180059Sjhb#include <kvm.h>
45180059Sjhb#include <limits.h>
46180059Sjhb#include <semaphore.h>
47180059Sjhb#include <signal.h>
48180059Sjhb#include <stdio.h>
49180059Sjhb#include <stdlib.h>
50180059Sjhb#include <string.h>
51180059Sjhb#include <time.h>
52180059Sjhb#include <unistd.h>
53180059Sjhb
54180059Sjhb#include "test.h"
55180059Sjhb
56180059Sjhb/* Cut and pasted from kernel header, bah! */
57180059Sjhb
58180059Sjhb/* Operations on timespecs */
59180059Sjhb#define	timespecclear(tvp)	((tvp)->tv_sec = (tvp)->tv_nsec = 0)
60180059Sjhb#define	timespecisset(tvp)	((tvp)->tv_sec || (tvp)->tv_nsec)
61180059Sjhb#define	timespeccmp(tvp, uvp, cmp)					\
62180059Sjhb	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
63180059Sjhb	    ((tvp)->tv_nsec cmp (uvp)->tv_nsec) :			\
64180059Sjhb	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
65180059Sjhb#define timespecadd(vvp, uvp)						\
66180059Sjhb	do {								\
67180059Sjhb		(vvp)->tv_sec += (uvp)->tv_sec;				\
68180059Sjhb		(vvp)->tv_nsec += (uvp)->tv_nsec;			\
69180059Sjhb		if ((vvp)->tv_nsec >= 1000000000) {			\
70180059Sjhb			(vvp)->tv_sec++;				\
71180059Sjhb			(vvp)->tv_nsec -= 1000000000;			\
72180059Sjhb		}							\
73180059Sjhb	} while (0)
74180059Sjhb#define timespecsub(vvp, uvp)						\
75180059Sjhb	do {								\
76180059Sjhb		(vvp)->tv_sec -= (uvp)->tv_sec;				\
77180059Sjhb		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
78180059Sjhb		if ((vvp)->tv_nsec < 0) {				\
79180059Sjhb			(vvp)->tv_sec--;				\
80180059Sjhb			(vvp)->tv_nsec += 1000000000;			\
81180059Sjhb		}							\
82180059Sjhb	} while (0)
83180059Sjhb
84180059Sjhb
85180059Sjhb#define	TEST_PATH	"/tmp/posixsem_regression_test"
86180059Sjhb
87180059Sjhb#define	ELAPSED(elapsed, limit)		(abs((elapsed) - (limit)) < 100)
88180059Sjhb
89180059Sjhb/* Macros for passing child status to parent over a pipe. */
90180059Sjhb#define	CSTAT(class, error)		((class) << 16 | (error))
91180059Sjhb#define	CSTAT_CLASS(stat)		((stat) >> 16)
92180059Sjhb#define	CSTAT_ERROR(stat)		((stat) & 0xffff)
93180059Sjhb
94180059Sjhb/*
95180059Sjhb * Helper routine for tests that use a child process.  This routine
96180059Sjhb * creates a pipe and forks a child process.  The child process runs
97180059Sjhb * the 'func' routine which returns a status integer.  The status
98180059Sjhb * integer gets written over the pipe to the parent and returned in
99180059Sjhb * '*stat'.  If there is an error in pipe(), fork(), or wait() this
100180059Sjhb * returns -1 and fails the test.
101180059Sjhb */
102180059Sjhbstatic int
103180059Sjhbchild_worker(int (*func)(void *arg), void *arg, int *stat)
104180059Sjhb{
105180059Sjhb	pid_t pid;
106180059Sjhb	int pfd[2], cstat;
107180059Sjhb
108180059Sjhb	if (pipe(pfd) < 0) {
109180059Sjhb		fail_errno("pipe");
110180059Sjhb		return (-1);
111180059Sjhb	}
112180059Sjhb
113180059Sjhb	pid = fork();
114180059Sjhb	switch (pid) {
115180059Sjhb	case -1:
116180059Sjhb		/* Error. */
117180059Sjhb		fail_errno("fork");
118180059Sjhb		close(pfd[0]);
119180059Sjhb		close(pfd[1]);
120180059Sjhb		return (-1);
121180059Sjhb	case 0:
122180059Sjhb		/* Child. */
123180059Sjhb		cstat = func(arg);
124180059Sjhb		write(pfd[1], &cstat, sizeof(cstat));
125180059Sjhb		exit(0);
126180059Sjhb	}
127180059Sjhb
128180059Sjhb	if (read(pfd[0], stat, sizeof(*stat)) < 0) {
129180059Sjhb		fail_errno("read(pipe)");
130180059Sjhb		close(pfd[0]);
131180059Sjhb		close(pfd[1]);
132180059Sjhb		return (-1);
133180059Sjhb	}
134180059Sjhb	if (waitpid(pid, NULL, 0) < 0) {
135180059Sjhb		fail_errno("wait");
136180059Sjhb		close(pfd[0]);
137180059Sjhb		close(pfd[1]);
138180059Sjhb		return (-1);
139180059Sjhb	}
140180059Sjhb	close(pfd[0]);
141180059Sjhb	close(pfd[1]);
142180059Sjhb	return (0);
143180059Sjhb}
144180059Sjhb
145180059Sjhb/*
146180059Sjhb * Attempt a ksem_open() that should fail with an expected error of
147180059Sjhb * 'error'.
148180059Sjhb */
149180059Sjhbstatic void
150180059Sjhbksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
151180059Sjhb    value, int error)
152180059Sjhb{
153180059Sjhb	semid_t id;
154180059Sjhb
155180059Sjhb	if (ksem_open(&id, path, flags, mode, value) >= 0) {
156180059Sjhb		fail_err("ksem_open() didn't fail");
157180059Sjhb		ksem_close(id);
158180059Sjhb		return;
159180059Sjhb	}
160180059Sjhb	if (errno != error) {
161180059Sjhb		fail_errno("ksem_open");
162180059Sjhb		return;
163180059Sjhb	}
164180059Sjhb	pass();
165180059Sjhb}
166180059Sjhb
167180059Sjhb/*
168180059Sjhb * Attempt a ksem_unlink() that should fail with an expected error of
169180059Sjhb * 'error'.
170180059Sjhb */
171180059Sjhbstatic void
172180059Sjhbksem_unlink_should_fail(const char *path, int error)
173180059Sjhb{
174180059Sjhb
175180059Sjhb	if (ksem_unlink(path) >= 0) {
176180059Sjhb		fail_err("ksem_unlink() didn't fail");
177180059Sjhb		return;
178180059Sjhb	}
179180059Sjhb	if (errno != error) {
180180059Sjhb		fail_errno("ksem_unlink");
181180059Sjhb		return;
182180059Sjhb	}
183180059Sjhb	pass();
184180059Sjhb}
185180059Sjhb
186180059Sjhb/*
187180059Sjhb * Attempt a ksem_close() that should fail with an expected error of
188180059Sjhb * 'error'.
189180059Sjhb */
190180059Sjhbstatic void
191180059Sjhbksem_close_should_fail(semid_t id, int error)
192180059Sjhb{
193180059Sjhb
194180059Sjhb	if (ksem_close(id) >= 0) {
195180059Sjhb		fail_err("ksem_close() didn't fail");
196180059Sjhb		return;
197180059Sjhb	}
198180059Sjhb	if (errno != error) {
199180059Sjhb		fail_errno("ksem_close");
200180059Sjhb		return;
201180059Sjhb	}
202180059Sjhb	pass();
203180059Sjhb}
204180059Sjhb
205180059Sjhb/*
206180059Sjhb * Attempt a ksem_init() that should fail with an expected error of
207180059Sjhb * 'error'.
208180059Sjhb */
209180059Sjhbstatic void
210180059Sjhbksem_init_should_fail(unsigned int value, int error)
211180059Sjhb{
212180059Sjhb	semid_t id;
213180059Sjhb
214180059Sjhb	if (ksem_init(&id, value) >= 0) {
215180059Sjhb		fail_err("ksem_init() didn't fail");
216180059Sjhb		ksem_destroy(id);
217180059Sjhb		return;
218180059Sjhb	}
219180059Sjhb	if (errno != error) {
220180059Sjhb		fail_errno("ksem_init");
221180059Sjhb		return;
222180059Sjhb	}
223180059Sjhb	pass();
224180059Sjhb}
225180059Sjhb
226180059Sjhb/*
227180059Sjhb * Attempt a ksem_destroy() that should fail with an expected error of
228180059Sjhb * 'error'.
229180059Sjhb */
230180059Sjhbstatic void
231180059Sjhbksem_destroy_should_fail(semid_t id, int error)
232180059Sjhb{
233180059Sjhb
234180059Sjhb	if (ksem_destroy(id) >= 0) {
235180059Sjhb		fail_err("ksem_destroy() didn't fail");
236180059Sjhb		return;
237180059Sjhb	}
238180059Sjhb	if (errno != error) {
239180059Sjhb		fail_errno("ksem_destroy");
240180059Sjhb		return;
241180059Sjhb	}
242180059Sjhb	pass();
243180059Sjhb}
244180059Sjhb
245180059Sjhb/*
246180059Sjhb * Attempt a ksem_post() that should fail with an expected error of
247180059Sjhb * 'error'.
248180059Sjhb */
249180059Sjhbstatic void
250180059Sjhbksem_post_should_fail(semid_t id, int error)
251180059Sjhb{
252180059Sjhb
253180059Sjhb	if (ksem_post(id) >= 0) {
254180059Sjhb		fail_err("ksem_post() didn't fail");
255180059Sjhb		return;
256180059Sjhb	}
257180059Sjhb	if (errno != error) {
258180059Sjhb		fail_errno("ksem_post");
259180059Sjhb		return;
260180059Sjhb	}
261180059Sjhb	pass();
262180059Sjhb}
263180059Sjhb
264180059Sjhbstatic void
265180059Sjhbopen_after_unlink(void)
266180059Sjhb{
267180059Sjhb	semid_t id;
268180059Sjhb
269180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
270180059Sjhb		fail_errno("ksem_open(1)");
271180059Sjhb		return;
272180059Sjhb	}
273180059Sjhb	ksem_close(id);
274180059Sjhb
275180059Sjhb	if (ksem_unlink(TEST_PATH) < 0) {
276180059Sjhb		fail_errno("ksem_unlink");
277180059Sjhb		return;
278180059Sjhb	}
279180059Sjhb
280180059Sjhb	ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
281180059Sjhb}
282180059SjhbTEST(open_after_unlink, "open after unlink");
283180059Sjhb
284180059Sjhbstatic void
285180059Sjhbopen_invalid_path(void)
286180059Sjhb{
287180059Sjhb
288180059Sjhb	ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
289180059Sjhb}
290180059SjhbTEST(open_invalid_path, "open invalid path");
291180059Sjhb
292180059Sjhbstatic void
293180059Sjhbopen_extra_flags(void)
294180059Sjhb{
295180059Sjhb
296180059Sjhb	ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
297180059Sjhb}
298180059SjhbTEST(open_extra_flags, "open with extra flags");
299180059Sjhb
300180059Sjhbstatic void
301180059Sjhbopen_bad_value(void)
302180059Sjhb{
303180059Sjhb
304180059Sjhb	(void)ksem_unlink(TEST_PATH);
305180059Sjhb
306180059Sjhb	ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
307180059Sjhb}
308180059SjhbTEST(open_bad_value, "open with invalid initial value");
309180059Sjhb
310180059Sjhbstatic void
311180059Sjhbopen_bad_path_pointer(void)
312180059Sjhb{
313180059Sjhb
314180059Sjhb	ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
315180059Sjhb}
316180059SjhbTEST(open_bad_path_pointer, "open bad path pointer");
317180059Sjhb
318180059Sjhbstatic void
319180059Sjhbopen_path_too_long(void)
320180059Sjhb{
321180059Sjhb	char *page;
322180059Sjhb
323180059Sjhb	page = malloc(MAXPATHLEN + 1);
324180059Sjhb	memset(page, 'a', MAXPATHLEN);
325180059Sjhb	page[MAXPATHLEN] = '\0';
326180059Sjhb	ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
327180059Sjhb	free(page);
328180059Sjhb}
329180059SjhbTEST(open_path_too_long, "open pathname too long");
330180059Sjhb
331180059Sjhbstatic void
332180059Sjhbopen_nonexisting_semaphore(void)
333180059Sjhb{
334180059Sjhb
335180059Sjhb	ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
336180059Sjhb}
337180059SjhbTEST(open_nonexisting_semaphore, "open nonexistent semaphore");
338180059Sjhb
339180059Sjhbstatic void
340180059Sjhbexclusive_create_existing_semaphore(void)
341180059Sjhb{
342180059Sjhb	semid_t id;
343180059Sjhb
344180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
345180059Sjhb		fail_errno("ksem_open(O_CREAT)");
346180059Sjhb		return;
347180059Sjhb	}
348180059Sjhb	ksem_close(id);
349180059Sjhb
350180059Sjhb	ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
351180059Sjhb
352180059Sjhb	ksem_unlink(TEST_PATH);
353180059Sjhb}
354180059SjhbTEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
355180059Sjhb
356180059Sjhbstatic void
357180059Sjhbinit_bad_value(void)
358180059Sjhb{
359180059Sjhb
360180059Sjhb	ksem_init_should_fail(UINT_MAX, EINVAL);
361180059Sjhb}
362180059SjhbTEST(init_bad_value, "init with invalid initial value");
363180059Sjhb
364180059Sjhbstatic void
365180059Sjhbunlink_bad_path_pointer(void)
366180059Sjhb{
367180059Sjhb
368180059Sjhb	ksem_unlink_should_fail((char *)1024, EFAULT);
369180059Sjhb}
370180059SjhbTEST(unlink_bad_path_pointer, "unlink bad path pointer");
371180059Sjhb
372180059Sjhbstatic void
373180059Sjhbunlink_path_too_long(void)
374180059Sjhb{
375180059Sjhb	char *page;
376180059Sjhb
377180059Sjhb	page = malloc(MAXPATHLEN + 1);
378180059Sjhb	memset(page, 'a', MAXPATHLEN);
379180059Sjhb	page[MAXPATHLEN] = '\0';
380180059Sjhb	ksem_unlink_should_fail(page, ENAMETOOLONG);
381180059Sjhb	free(page);
382180059Sjhb}
383180059SjhbTEST(unlink_path_too_long, "unlink pathname too long");
384180059Sjhb
385180059Sjhbstatic void
386180059Sjhbdestroy_named_semaphore(void)
387180059Sjhb{
388180059Sjhb	semid_t id;
389180059Sjhb
390180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
391180059Sjhb		fail_errno("ksem_open(O_CREAT)");
392180059Sjhb		return;
393180059Sjhb	}
394180059Sjhb
395180059Sjhb	ksem_destroy_should_fail(id, EINVAL);
396180059Sjhb
397180059Sjhb	ksem_close(id);
398180059Sjhb	ksem_unlink(TEST_PATH);
399180059Sjhb}
400180059SjhbTEST(destroy_named_semaphore, "destroy named semaphore");
401180059Sjhb
402180059Sjhbstatic void
403180059Sjhbclose_unnamed_semaphore(void)
404180059Sjhb{
405180059Sjhb	semid_t id;
406180059Sjhb
407180059Sjhb	if (ksem_init(&id, 1) < 0) {
408180059Sjhb		fail_errno("ksem_init");
409180059Sjhb		return;
410180059Sjhb	}
411180059Sjhb
412180059Sjhb	ksem_close_should_fail(id, EINVAL);
413180059Sjhb
414180059Sjhb	ksem_destroy(id);
415180059Sjhb}
416180059SjhbTEST(close_unnamed_semaphore, "close unnamed semaphore");
417180059Sjhb
418180059Sjhbstatic void
419180059Sjhbdestroy_invalid_fd(void)
420180059Sjhb{
421180059Sjhb
422180059Sjhb	ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
423180059Sjhb}
424180059SjhbTEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
425180059Sjhb
426180059Sjhbstatic void
427180059Sjhbclose_invalid_fd(void)
428180059Sjhb{
429180059Sjhb
430180059Sjhb	ksem_close_should_fail(STDERR_FILENO, EINVAL);
431180059Sjhb}
432180059SjhbTEST(close_invalid_fd, "close non-semaphore file descriptor");
433180059Sjhb
434180059Sjhbstatic void
435180059Sjhbcreate_unnamed_semaphore(void)
436180059Sjhb{
437180059Sjhb	semid_t id;
438180059Sjhb
439180059Sjhb	if (ksem_init(&id, 1) < 0) {
440180059Sjhb		fail_errno("ksem_init");
441180059Sjhb		return;
442180059Sjhb	}
443180059Sjhb
444180059Sjhb	if (ksem_destroy(id) < 0) {
445180059Sjhb		fail_errno("ksem_destroy");
446180059Sjhb		return;
447180059Sjhb	}
448180059Sjhb	pass();
449180059Sjhb}
450180059SjhbTEST(create_unnamed_semaphore, "create unnamed semaphore");
451180059Sjhb
452180059Sjhbstatic void
453180059Sjhbopen_named_semaphore(void)
454180059Sjhb{
455180059Sjhb	semid_t id;
456180059Sjhb
457180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
458180059Sjhb		fail_errno("ksem_open(O_CREAT)");
459180059Sjhb		return;
460180059Sjhb	}
461180059Sjhb
462180059Sjhb	if (ksem_close(id) < 0) {
463180059Sjhb		fail_errno("ksem_close");
464180059Sjhb		return;
465180059Sjhb	}
466180059Sjhb
467180059Sjhb	if (ksem_unlink(TEST_PATH) < 0) {
468180059Sjhb		fail_errno("ksem_unlink");
469180059Sjhb		return;
470180059Sjhb	}
471180059Sjhb	pass();
472180059Sjhb}
473180059SjhbTEST(open_named_semaphore, "create named semaphore");
474180059Sjhb
475180059Sjhbstatic void
476180059Sjhbgetvalue_invalid_semaphore(void)
477180059Sjhb{
478180059Sjhb	int val;
479180059Sjhb
480180059Sjhb	if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
481180059Sjhb		fail_err("ksem_getvalue() didn't fail");
482180059Sjhb		return;
483180059Sjhb	}
484180059Sjhb	if (errno != EINVAL) {
485180059Sjhb		fail_errno("ksem_getvalue");
486180059Sjhb		return;
487180059Sjhb	}
488180059Sjhb	pass();
489180059Sjhb}
490180059SjhbTEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
491180059Sjhb
492180059Sjhbstatic void
493180059Sjhbpost_invalid_semaphore(void)
494180059Sjhb{
495180059Sjhb
496180059Sjhb	ksem_post_should_fail(STDERR_FILENO, EINVAL);
497180059Sjhb}
498180059SjhbTEST(post_invalid_semaphore, "post of invalid semaphore");
499180059Sjhb
500180059Sjhbstatic void
501180059Sjhbwait_invalid_semaphore(void)
502180059Sjhb{
503180059Sjhb
504180059Sjhb	if (ksem_wait(STDERR_FILENO) >= 0) {
505180059Sjhb		fail_err("ksem_wait() didn't fail");
506180059Sjhb		return;
507180059Sjhb	}
508180059Sjhb	if (errno != EINVAL) {
509180059Sjhb		fail_errno("ksem_wait");
510180059Sjhb		return;
511180059Sjhb	}
512180059Sjhb	pass();
513180059Sjhb}
514180059SjhbTEST(wait_invalid_semaphore, "wait for invalid semaphore");
515180059Sjhb
516180059Sjhbstatic void
517180059Sjhbtrywait_invalid_semaphore(void)
518180059Sjhb{
519180059Sjhb
520180059Sjhb	if (ksem_trywait(STDERR_FILENO) >= 0) {
521180059Sjhb		fail_err("ksem_trywait() didn't fail");
522180059Sjhb		return;
523180059Sjhb	}
524180059Sjhb	if (errno != EINVAL) {
525180059Sjhb		fail_errno("ksem_trywait");
526180059Sjhb		return;
527180059Sjhb	}
528180059Sjhb	pass();
529180059Sjhb}
530180059SjhbTEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
531180059Sjhb
532180059Sjhbstatic void
533180059Sjhbtimedwait_invalid_semaphore(void)
534180059Sjhb{
535180059Sjhb
536180059Sjhb	if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
537180059Sjhb		fail_err("ksem_timedwait() didn't fail");
538180059Sjhb		return;
539180059Sjhb	}
540180059Sjhb	if (errno != EINVAL) {
541180059Sjhb		fail_errno("ksem_timedwait");
542180059Sjhb		return;
543180059Sjhb	}
544180059Sjhb	pass();
545180059Sjhb}
546180059SjhbTEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
547180059Sjhb
548180059Sjhbstatic int
549180059Sjhbcheckvalue(semid_t id, int expected)
550180059Sjhb{
551180059Sjhb	int val;
552180059Sjhb
553180059Sjhb	if (ksem_getvalue(id, &val) < 0) {
554180059Sjhb		fail_errno("ksem_getvalue");
555180059Sjhb		return (-1);
556180059Sjhb	}
557180059Sjhb	if (val != expected) {
558180059Sjhb		fail_err("sem value should be %d instead of %d", expected, val);
559180059Sjhb		return (-1);
560180059Sjhb	}
561180059Sjhb	return (0);
562180059Sjhb}
563180059Sjhb
564180059Sjhbstatic void
565180059Sjhbpost_test(void)
566180059Sjhb{
567180059Sjhb	semid_t id;
568180059Sjhb
569180059Sjhb	if (ksem_init(&id, 1) < 0) {
570180059Sjhb		fail_errno("ksem_init");
571180059Sjhb		return;
572180059Sjhb	}
573180059Sjhb	if (checkvalue(id, 1) < 0) {
574180059Sjhb		ksem_destroy(id);
575180059Sjhb		return;
576180059Sjhb	}
577180059Sjhb	if (ksem_post(id) < 0) {
578180059Sjhb		fail_errno("ksem_post");
579180059Sjhb		ksem_destroy(id);
580180059Sjhb		return;
581180059Sjhb	}
582180059Sjhb	if (checkvalue(id, 2) < 0) {
583180059Sjhb		ksem_destroy(id);
584180059Sjhb		return;
585180059Sjhb	}
586180059Sjhb	if (ksem_destroy(id) < 0) {
587180059Sjhb		fail_errno("ksem_destroy");
588180059Sjhb		return;
589180059Sjhb	}
590180059Sjhb	pass();
591180059Sjhb}
592180059SjhbTEST(post_test, "simple post");
593180059Sjhb
594180059Sjhbstatic void
595180059Sjhbuse_after_unlink_test(void)
596180059Sjhb{
597180059Sjhb	semid_t id;
598180059Sjhb
599180059Sjhb	/*
600180059Sjhb	 * Create named semaphore with value of 1 and then unlink it
601180059Sjhb	 * while still retaining the initial reference.
602180059Sjhb	 */
603180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
604180059Sjhb		fail_errno("ksem_open(O_CREAT | O_EXCL)");
605180059Sjhb		return;
606180059Sjhb	}
607180059Sjhb	if (ksem_unlink(TEST_PATH) < 0) {
608180059Sjhb		fail_errno("ksem_unlink");
609180059Sjhb		ksem_close(id);
610180059Sjhb		return;
611180059Sjhb	}
612180059Sjhb	if (checkvalue(id, 1) < 0) {
613180059Sjhb		ksem_close(id);
614180059Sjhb		return;
615180059Sjhb	}
616180059Sjhb
617180059Sjhb	/* Post the semaphore to set its value to 2. */
618180059Sjhb	if (ksem_post(id) < 0) {
619180059Sjhb		fail_errno("ksem_post");
620180059Sjhb		ksem_close(id);
621180059Sjhb		return;
622180059Sjhb	}
623180059Sjhb	if (checkvalue(id, 2) < 0) {
624180059Sjhb		ksem_close(id);
625180059Sjhb		return;
626180059Sjhb	}
627180059Sjhb
628180059Sjhb	/* Wait on the semaphore which should set its value to 1. */
629180059Sjhb	if (ksem_wait(id) < 0) {
630180059Sjhb		fail_errno("ksem_wait");
631180059Sjhb		ksem_close(id);
632180059Sjhb		return;
633180059Sjhb	}
634180059Sjhb	if (checkvalue(id, 1) < 0) {
635180059Sjhb		ksem_close(id);
636180059Sjhb		return;
637180059Sjhb	}
638180059Sjhb
639180059Sjhb	if (ksem_close(id) < 0) {
640180059Sjhb		fail_errno("ksem_close");
641180059Sjhb		return;
642180059Sjhb	}
643180059Sjhb	pass();
644180059Sjhb}
645180059SjhbTEST(use_after_unlink_test, "use named semaphore after unlink");
646180059Sjhb
647180059Sjhbstatic void
648180059Sjhbunlocked_trywait(void)
649180059Sjhb{
650180059Sjhb	semid_t id;
651180059Sjhb
652180059Sjhb	if (ksem_init(&id, 1) < 0) {
653180059Sjhb		fail_errno("ksem_init");
654180059Sjhb		return;
655180059Sjhb	}
656180059Sjhb
657180059Sjhb	/* This should succeed and decrement the value to 0. */
658180059Sjhb	if (ksem_trywait(id) < 0) {
659180059Sjhb		fail_errno("ksem_trywait()");
660180059Sjhb		ksem_destroy(id);
661180059Sjhb		return;
662180059Sjhb	}
663180059Sjhb	if (checkvalue(id, 0) < 0) {
664180059Sjhb		ksem_destroy(id);
665180059Sjhb		return;
666180059Sjhb	}
667180059Sjhb
668180059Sjhb	if (ksem_destroy(id) < 0) {
669180059Sjhb		fail_errno("ksem_destroy");
670180059Sjhb		return;
671180059Sjhb	}
672180059Sjhb	pass();
673180059Sjhb}
674180059SjhbTEST(unlocked_trywait, "unlocked trywait");
675180059Sjhb
676180059Sjhbstatic void
677180059Sjhblocked_trywait(void)
678180059Sjhb{
679180059Sjhb	semid_t id;
680180059Sjhb
681180059Sjhb	if (ksem_init(&id, 0) < 0) {
682180059Sjhb		fail_errno("ksem_init");
683180059Sjhb		return;
684180059Sjhb	}
685180059Sjhb
686180059Sjhb	/* This should fail with EAGAIN and leave the value at 0. */
687180059Sjhb	if (ksem_trywait(id) >= 0) {
688180059Sjhb		fail_err("ksem_trywait() didn't fail");
689180059Sjhb		ksem_destroy(id);
690180059Sjhb		return;
691180059Sjhb	}
692180059Sjhb	if (errno != EAGAIN) {
693180059Sjhb		fail_errno("wrong error from ksem_trywait()");
694180059Sjhb		ksem_destroy(id);
695180059Sjhb		return;
696180059Sjhb	}
697180059Sjhb	if (checkvalue(id, 0) < 0) {
698180059Sjhb		ksem_destroy(id);
699180059Sjhb		return;
700180059Sjhb	}
701180059Sjhb
702180059Sjhb	if (ksem_destroy(id) < 0) {
703180059Sjhb		fail_errno("ksem_destroy");
704180059Sjhb		return;
705180059Sjhb	}
706180059Sjhb	pass();
707180059Sjhb}
708180059SjhbTEST(locked_trywait, "locked trywait");
709180059Sjhb
710180059Sjhb/*
711180059Sjhb * Use a timer to post a specific semaphore after a timeout.  A timer
712180059Sjhb * is scheduled via schedule_post().  check_alarm() must be called
713180059Sjhb * afterwards to clean up and check for errors.
714180059Sjhb */
715180059Sjhbstatic semid_t alarm_id = -1;
716180059Sjhbstatic int alarm_errno;
717180059Sjhbstatic int alarm_handler_installed;
718180059Sjhb
719180059Sjhbstatic void
720180059Sjhbalarm_handler(int signo)
721180059Sjhb{
722180059Sjhb
723180059Sjhb	if (ksem_post(alarm_id) < 0)
724180059Sjhb		alarm_errno = errno;
725180059Sjhb}
726180059Sjhb
727180059Sjhbstatic int
728180059Sjhbcheck_alarm(int just_clear)
729180059Sjhb{
730180059Sjhb	struct itimerval it;
731180059Sjhb
732180059Sjhb	bzero(&it, sizeof(it));
733180059Sjhb	if (just_clear) {
734180059Sjhb		setitimer(ITIMER_REAL, &it, NULL);
735180059Sjhb		alarm_errno = 0;
736180059Sjhb		alarm_id = -1;
737180059Sjhb		return (0);
738180059Sjhb	}
739180059Sjhb	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
740180059Sjhb		fail_errno("setitimer");
741180059Sjhb		return (-1);
742180059Sjhb	}
743180059Sjhb	if (alarm_errno != 0 && !just_clear) {
744180059Sjhb		errno = alarm_errno;
745180059Sjhb		fail_errno("ksem_post() (via timeout)");
746180059Sjhb		alarm_errno = 0;
747180059Sjhb		return (-1);
748180059Sjhb	}
749180059Sjhb	alarm_id = -1;
750180059Sjhb
751180059Sjhb	return (0);
752180059Sjhb}
753180059Sjhb
754180059Sjhbstatic int
755180059Sjhbschedule_post(semid_t id, u_int msec)
756180059Sjhb{
757180059Sjhb	struct itimerval it;
758180059Sjhb
759180059Sjhb	if (!alarm_handler_installed) {
760180059Sjhb		if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
761180059Sjhb			fail_errno("signal(SIGALRM)");
762180059Sjhb			return (-1);
763180059Sjhb		}
764180059Sjhb		alarm_handler_installed = 1;
765180059Sjhb	}
766180059Sjhb	if (alarm_id != -1) {
767180059Sjhb		fail_err("ksem_post() already scheduled");
768180059Sjhb		return (-1);
769180059Sjhb	}
770180059Sjhb	alarm_id = id;
771180059Sjhb	bzero(&it, sizeof(it));
772180059Sjhb	it.it_value.tv_sec = msec / 1000;
773180059Sjhb	it.it_value.tv_usec = (msec % 1000) * 1000;
774180059Sjhb	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
775180059Sjhb		fail_errno("setitimer");
776180059Sjhb		return (-1);
777180059Sjhb	}
778180059Sjhb	return (0);
779180059Sjhb}
780180059Sjhb
781180059Sjhbstatic int
782180059Sjhbtimedwait(semid_t id, u_int msec, u_int *delta, int error)
783180059Sjhb{
784180059Sjhb	struct timespec start, end;
785180059Sjhb
786180059Sjhb	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
787180059Sjhb		fail_errno("clock_gettime(CLOCK_REALTIME)");
788180059Sjhb		return (-1);
789180059Sjhb	}
790180059Sjhb	end.tv_sec = msec / 1000;
791180059Sjhb	end.tv_nsec = msec % 1000 * 1000000;
792180059Sjhb	timespecadd(&end, &start);
793180059Sjhb	if (ksem_timedwait(id, &end) < 0) {
794180059Sjhb		if (errno != error) {
795180059Sjhb			fail_errno("ksem_timedwait");
796180059Sjhb			return (-1);
797180059Sjhb		}
798180059Sjhb	} else if (error != 0) {
799180059Sjhb		fail_err("ksem_timedwait() didn't fail");
800180059Sjhb		return (-1);
801180059Sjhb	}
802180059Sjhb	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
803180059Sjhb		fail_errno("clock_gettime(CLOCK_REALTIME)");
804180059Sjhb		return (-1);
805180059Sjhb	}
806180059Sjhb	timespecsub(&end, &start);
807180059Sjhb	*delta = end.tv_nsec / 1000000;
808180059Sjhb	*delta += end.tv_sec * 1000;
809180059Sjhb	return (0);
810180059Sjhb}
811180059Sjhb
812180059Sjhbstatic void
813180059Sjhbunlocked_timedwait(void)
814180059Sjhb{
815180059Sjhb	semid_t id;
816180059Sjhb	u_int elapsed;
817180059Sjhb
818180059Sjhb	if (ksem_init(&id, 1) < 0) {
819180059Sjhb		fail_errno("ksem_init");
820180059Sjhb		return;
821180059Sjhb	}
822180059Sjhb
823180059Sjhb	/* This should succeed right away and set the value to 0. */
824180059Sjhb	if (timedwait(id, 5000, &elapsed, 0) < 0) {
825180059Sjhb		ksem_destroy(id);
826180059Sjhb		return;
827180059Sjhb	}
828180059Sjhb	if (!ELAPSED(elapsed, 0)) {
829180059Sjhb		fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
830180059Sjhb		ksem_destroy(id);
831180059Sjhb		return;
832180059Sjhb	}
833180059Sjhb	if (checkvalue(id, 0) < 0) {
834180059Sjhb		ksem_destroy(id);
835180059Sjhb		return;
836180059Sjhb	}
837180059Sjhb
838180059Sjhb	if (ksem_destroy(id) < 0) {
839180059Sjhb		fail_errno("ksem_destroy");
840180059Sjhb		return;
841180059Sjhb	}
842180059Sjhb	pass();
843180059Sjhb}
844180059SjhbTEST(unlocked_timedwait, "unlocked timedwait");
845180059Sjhb
846180059Sjhbstatic void
847180059Sjhbexpired_timedwait(void)
848180059Sjhb{
849180059Sjhb	semid_t id;
850180059Sjhb	u_int elapsed;
851180059Sjhb
852180059Sjhb	if (ksem_init(&id, 0) < 0) {
853180059Sjhb		fail_errno("ksem_init");
854180059Sjhb		return;
855180059Sjhb	}
856180059Sjhb
857180059Sjhb	/* This should fail with a timeout and leave the value at 0. */
858180059Sjhb	if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
859180059Sjhb		ksem_destroy(id);
860180059Sjhb		return;
861180059Sjhb	}
862180059Sjhb	if (!ELAPSED(elapsed, 2500)) {
863180059Sjhb		fail_err(
864180059Sjhb	    "ksem_timedwait() of locked sem took %ums instead of 2500ms",
865180059Sjhb		    elapsed);
866180059Sjhb		ksem_destroy(id);
867180059Sjhb		return;
868180059Sjhb	}
869180059Sjhb	if (checkvalue(id, 0) < 0) {
870180059Sjhb		ksem_destroy(id);
871180059Sjhb		return;
872180059Sjhb	}
873180059Sjhb
874180059Sjhb	if (ksem_destroy(id) < 0) {
875180059Sjhb		fail_errno("ksem_destroy");
876180059Sjhb		return;
877180059Sjhb	}
878180059Sjhb	pass();
879180059Sjhb}
880180059SjhbTEST(expired_timedwait, "locked timedwait timeout");
881180059Sjhb
882180059Sjhbstatic void
883180059Sjhblocked_timedwait(void)
884180059Sjhb{
885180059Sjhb	semid_t id;
886180059Sjhb	u_int elapsed;
887180059Sjhb
888180059Sjhb	if (ksem_init(&id, 0) < 0) {
889180059Sjhb		fail_errno("ksem_init");
890180059Sjhb		return;
891180059Sjhb	}
892180059Sjhb
893180059Sjhb	/*
894180059Sjhb	 * Schedule a post to trigger after 1000 ms.  The subsequent
895180059Sjhb	 * timedwait should succeed after 1000 ms as a result w/o
896180059Sjhb	 * timing out.
897180059Sjhb	 */
898180059Sjhb	if (schedule_post(id, 1000) < 0) {
899180059Sjhb		ksem_destroy(id);
900180059Sjhb		return;
901180059Sjhb	}
902180059Sjhb	if (timedwait(id, 2000, &elapsed, 0) < 0) {
903180059Sjhb		check_alarm(1);
904180059Sjhb		ksem_destroy(id);
905180059Sjhb		return;
906180059Sjhb	}
907180059Sjhb	if (!ELAPSED(elapsed, 1000)) {
908180059Sjhb		fail_err(
909180059Sjhb	    "ksem_timedwait() with delayed post took %ums instead of 1000ms",
910180059Sjhb		    elapsed);
911180059Sjhb		check_alarm(1);
912180059Sjhb		ksem_destroy(id);
913180059Sjhb		return;
914180059Sjhb	}
915180059Sjhb	if (check_alarm(0) < 0) {
916180059Sjhb		ksem_destroy(id);
917180059Sjhb		return;
918180059Sjhb	}
919180059Sjhb
920180059Sjhb	if (ksem_destroy(id) < 0) {
921180059Sjhb		fail_errno("ksem_destroy");
922180059Sjhb		return;
923180059Sjhb	}
924180059Sjhb	pass();
925180059Sjhb}
926180059SjhbTEST(locked_timedwait, "locked timedwait");
927180059Sjhb
928180059Sjhbstatic int
929180059Sjhbtestwait(semid_t id, u_int *delta)
930180059Sjhb{
931180059Sjhb	struct timespec start, end;
932180059Sjhb
933180059Sjhb	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
934180059Sjhb		fail_errno("clock_gettime(CLOCK_REALTIME)");
935180059Sjhb		return (-1);
936180059Sjhb	}
937180059Sjhb	if (ksem_wait(id) < 0) {
938180059Sjhb		fail_errno("ksem_wait");
939180059Sjhb		return (-1);
940180059Sjhb	}
941180059Sjhb	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
942180059Sjhb		fail_errno("clock_gettime(CLOCK_REALTIME)");
943180059Sjhb		return (-1);
944180059Sjhb	}
945180059Sjhb	timespecsub(&end, &start);
946180059Sjhb	*delta = end.tv_nsec / 1000000;
947180059Sjhb	*delta += end.tv_sec * 1000;
948180059Sjhb	return (0);
949180059Sjhb}
950180059Sjhb
951180059Sjhbstatic void
952180059Sjhbunlocked_wait(void)
953180059Sjhb{
954180059Sjhb	semid_t id;
955180059Sjhb	u_int elapsed;
956180059Sjhb
957180059Sjhb	if (ksem_init(&id, 1) < 0) {
958180059Sjhb		fail_errno("ksem_init");
959180059Sjhb		return;
960180059Sjhb	}
961180059Sjhb
962180059Sjhb	/* This should succeed right away and set the value to 0. */
963180059Sjhb	if (testwait(id, &elapsed) < 0) {
964180059Sjhb		ksem_destroy(id);
965180059Sjhb		return;
966180059Sjhb	}
967180059Sjhb	if (!ELAPSED(elapsed, 0)) {
968180059Sjhb		fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
969180059Sjhb		ksem_destroy(id);
970180059Sjhb		return;
971180059Sjhb	}
972180059Sjhb	if (checkvalue(id, 0) < 0) {
973180059Sjhb		ksem_destroy(id);
974180059Sjhb		return;
975180059Sjhb	}
976180059Sjhb
977180059Sjhb	if (ksem_destroy(id) < 0) {
978180059Sjhb		fail_errno("ksem_destroy");
979180059Sjhb		return;
980180059Sjhb	}
981180059Sjhb	pass();
982180059Sjhb}
983180059SjhbTEST(unlocked_wait, "unlocked wait");
984180059Sjhb
985180059Sjhbstatic void
986180059Sjhblocked_wait(void)
987180059Sjhb{
988180059Sjhb	semid_t id;
989180059Sjhb	u_int elapsed;
990180059Sjhb
991180059Sjhb	if (ksem_init(&id, 0) < 0) {
992180059Sjhb		fail_errno("ksem_init");
993180059Sjhb		return;
994180059Sjhb	}
995180059Sjhb
996180059Sjhb	/*
997180059Sjhb	 * Schedule a post to trigger after 1000 ms.  The subsequent
998180059Sjhb	 * wait should succeed after 1000 ms as a result.
999180059Sjhb	 */
1000180059Sjhb	if (schedule_post(id, 1000) < 0) {
1001180059Sjhb		ksem_destroy(id);
1002180059Sjhb		return;
1003180059Sjhb	}
1004180059Sjhb	if (testwait(id, &elapsed) < 0) {
1005180059Sjhb		check_alarm(1);
1006180059Sjhb		ksem_destroy(id);
1007180059Sjhb		return;
1008180059Sjhb	}
1009180059Sjhb	if (!ELAPSED(elapsed, 1000)) {
1010180059Sjhb		fail_err(
1011180059Sjhb	    "ksem_wait() with delayed post took %ums instead of 1000ms",
1012180059Sjhb		    elapsed);
1013180059Sjhb		check_alarm(1);
1014180059Sjhb		ksem_destroy(id);
1015180059Sjhb		return;
1016180059Sjhb	}
1017180059Sjhb	if (check_alarm(0) < 0) {
1018180059Sjhb		ksem_destroy(id);
1019180059Sjhb		return;
1020180059Sjhb	}
1021180059Sjhb
1022180059Sjhb	if (ksem_destroy(id) < 0) {
1023180059Sjhb		fail_errno("ksem_destroy");
1024180059Sjhb		return;
1025180059Sjhb	}
1026180059Sjhb	pass();
1027180059Sjhb}
1028180059SjhbTEST(locked_wait, "locked wait");
1029180059Sjhb
1030180059Sjhb/*
1031180059Sjhb * Fork off a child process.  The child will open the semaphore via
1032180059Sjhb * the same name.  The child will then block on the semaphore waiting
1033180059Sjhb * for the parent to post it.
1034180059Sjhb */
1035180059Sjhbstatic int
1036180059Sjhbwait_twoproc_child(void *arg)
1037180059Sjhb{
1038180059Sjhb	semid_t id;
1039180059Sjhb
1040180059Sjhb	if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
1041180059Sjhb		return (CSTAT(1, errno));
1042180059Sjhb	if (ksem_wait(id) < 0)
1043180059Sjhb		return (CSTAT(2, errno));
1044180059Sjhb	if (ksem_close(id) < 0)
1045180059Sjhb		return (CSTAT(3, errno));
1046180059Sjhb	return (CSTAT(0, 0));
1047180059Sjhb}
1048180059Sjhb
1049180059Sjhbstatic void
1050180059Sjhbwait_twoproc_test(void)
1051180059Sjhb{
1052180059Sjhb	semid_t id;
1053180059Sjhb	int stat;
1054180059Sjhb
1055180059Sjhb	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
1056180059Sjhb		fail_errno("ksem_open");
1057180059Sjhb		return;
1058180059Sjhb	}
1059180059Sjhb
1060180059Sjhb	if (schedule_post(id, 500) < 0) {
1061180059Sjhb		ksem_close(id);
1062180059Sjhb		ksem_unlink(TEST_PATH);
1063180059Sjhb		return;
1064180059Sjhb	}
1065180059Sjhb
1066180059Sjhb	if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
1067180059Sjhb		check_alarm(1);
1068180059Sjhb		ksem_close(id);
1069180059Sjhb		ksem_unlink(TEST_PATH);
1070180059Sjhb		return;
1071180059Sjhb	}
1072180059Sjhb
1073180059Sjhb	errno = CSTAT_ERROR(stat);
1074180059Sjhb	switch (CSTAT_CLASS(stat)) {
1075180059Sjhb	case 0:
1076180059Sjhb		pass();
1077180059Sjhb		break;
1078180059Sjhb	case 1:
1079180059Sjhb		fail_errno("child ksem_open()");
1080180059Sjhb		break;
1081180059Sjhb	case 2:
1082180059Sjhb		fail_errno("child ksem_wait()");
1083180059Sjhb		break;
1084180059Sjhb	case 3:
1085180059Sjhb		fail_errno("child ksem_close()");
1086180059Sjhb		break;
1087180059Sjhb	default:
1088180059Sjhb		fail_err("bad child state %#x", stat);
1089180059Sjhb		break;
1090180059Sjhb	}
1091180059Sjhb
1092180059Sjhb	check_alarm(1);
1093180059Sjhb	ksem_close(id);
1094180059Sjhb	ksem_unlink(TEST_PATH);
1095180059Sjhb}
1096180059SjhbTEST(wait_twoproc_test, "two proc wait");
1097180059Sjhb
1098180059Sjhbstatic void
1099180059Sjhbmaxvalue_test(void)
1100180059Sjhb{
1101180059Sjhb	semid_t id;
1102180059Sjhb	int val;
1103180059Sjhb
1104180059Sjhb	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1105180059Sjhb		fail_errno("ksem_init");
1106180059Sjhb		return;
1107180059Sjhb	}
1108180059Sjhb	if (ksem_getvalue(id, &val) < 0) {
1109180059Sjhb		fail_errno("ksem_getvalue");
1110180059Sjhb		ksem_destroy(id);
1111180059Sjhb		return;
1112180059Sjhb	}
1113180059Sjhb	if (val != SEM_VALUE_MAX) {
1114180059Sjhb		fail_err("value %d != SEM_VALUE_MAX");
1115180059Sjhb		ksem_destroy(id);
1116180059Sjhb		return;
1117180059Sjhb	}
1118180059Sjhb	if (val < 0) {
1119180059Sjhb		fail_err("value < 0");
1120180059Sjhb		ksem_destroy(id);
1121180059Sjhb		return;
1122180059Sjhb	}
1123180059Sjhb	if (ksem_destroy(id) < 0) {
1124180059Sjhb		fail_errno("ksem_destroy");
1125180059Sjhb		return;
1126180059Sjhb	}
1127180059Sjhb	pass();
1128180059Sjhb}
1129180059SjhbTEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1130180059Sjhb
1131180059Sjhbstatic void
1132180059Sjhbmaxvalue_post_test(void)
1133180059Sjhb{
1134180059Sjhb	semid_t id;
1135180059Sjhb
1136180059Sjhb	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1137180059Sjhb		fail_errno("ksem_init");
1138180059Sjhb		return;
1139180059Sjhb	}
1140180059Sjhb
1141180059Sjhb	ksem_post_should_fail(id, EOVERFLOW);
1142180059Sjhb
1143180059Sjhb	ksem_destroy(id);
1144180059Sjhb}
1145180059SjhbTEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1146180059Sjhb
1147180059Sjhbstatic void
1148180059Sjhbbusy_destroy_test(void)
1149180059Sjhb{
1150180059Sjhb	char errbuf[_POSIX2_LINE_MAX];
1151180059Sjhb	struct kinfo_proc *kp;
1152180059Sjhb	semid_t id;
1153180059Sjhb	pid_t pid;
1154180059Sjhb	kvm_t *kd;
1155180059Sjhb	int count;
1156180059Sjhb
1157180059Sjhb	kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
1158180059Sjhb	if (kd == NULL) {
1159180059Sjhb		fail_err("kvm_openfiles: %s", errbuf);
1160180059Sjhb		return;
1161180059Sjhb	}
1162180059Sjhb
1163180059Sjhb	if (ksem_init(&id, 0) < 0) {
1164180059Sjhb		fail_errno("ksem_init");
1165180059Sjhb		kvm_close(kd);
1166180059Sjhb		return;
1167180059Sjhb	}
1168180059Sjhb
1169180059Sjhb	pid = fork();
1170180059Sjhb	switch (pid) {
1171180059Sjhb	case -1:
1172180059Sjhb		/* Error. */
1173180059Sjhb		fail_errno("fork");
1174180059Sjhb		ksem_destroy(id);
1175180059Sjhb		kvm_close(kd);
1176180059Sjhb		return;
1177180059Sjhb	case 0:
1178180059Sjhb		/* Child. */
1179180059Sjhb		ksem_wait(id);
1180180059Sjhb		exit(0);
1181180059Sjhb	}
1182180059Sjhb
1183180059Sjhb	/*
1184180059Sjhb	 * Wait for the child process to block on the semaphore.  This
1185180059Sjhb	 * is a bit gross.
1186180059Sjhb	 */
1187180059Sjhb	for (;;) {
1188180059Sjhb		kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
1189180059Sjhb		if (kp == NULL) {
1190180059Sjhb			fail_err("kvm_getprocs: %s", kvm_geterr(kd));
1191180059Sjhb			kvm_close(kd);
1192180059Sjhb			ksem_destroy(id);
1193180059Sjhb			return;
1194180059Sjhb		}
1195180059Sjhb		if (kp->ki_stat == SSLEEP &&
1196180059Sjhb		    (strcmp(kp->ki_wmesg, "sem") == 0 ||
1197180059Sjhb		    strcmp(kp->ki_wmesg, "ksem") == 0))
1198180059Sjhb			break;
1199180059Sjhb		usleep(1000);
1200180059Sjhb	}
1201180059Sjhb	kvm_close(kd);
1202180059Sjhb
1203180059Sjhb	ksem_destroy_should_fail(id, EBUSY);
1204180059Sjhb
1205180059Sjhb	/* Cleanup. */
1206180059Sjhb	ksem_post(id);
1207180059Sjhb	waitpid(pid, NULL, 0);
1208180059Sjhb	ksem_destroy(id);
1209180059Sjhb}
1210180059SjhbTEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1211180059Sjhb
1212180059Sjhbstatic int
1213180059Sjhbexhaust_unnamed_child(void *arg)
1214180059Sjhb{
1215180059Sjhb	semid_t id;
1216180059Sjhb	int i, max;
1217180059Sjhb
1218180059Sjhb	max = (intptr_t)arg;
1219180059Sjhb	for (i = 0; i < max + 1; i++) {
1220180059Sjhb		if (ksem_init(&id, 1) < 0) {
1221180059Sjhb			if (errno == ENOSPC)
1222180059Sjhb				return (CSTAT(0, 0));
1223180059Sjhb			return (CSTAT(1, errno));
1224180059Sjhb		}
1225180059Sjhb	}
1226180059Sjhb	return (CSTAT(2, 0));
1227180059Sjhb}
1228180059Sjhb
1229180059Sjhbstatic void
1230180059Sjhbexhaust_unnamed_sems(void)
1231180059Sjhb{
1232180059Sjhb	size_t len;
1233180059Sjhb	int nsems_max, stat;
1234180059Sjhb
1235180059Sjhb	len = sizeof(nsems_max);
1236180059Sjhb	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1237180059Sjhb	    0) {
1238180059Sjhb		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1239180059Sjhb		return;
1240180059Sjhb	}
1241180059Sjhb
1242205148Skib	if (child_worker(exhaust_unnamed_child, (void *)(uintptr_t)nsems_max,
1243205148Skib	    &stat))
1244180059Sjhb		return;
1245180059Sjhb	errno = CSTAT_ERROR(stat);
1246180059Sjhb	switch (CSTAT_CLASS(stat)) {
1247180059Sjhb	case 0:
1248180059Sjhb		pass();
1249180059Sjhb		break;
1250180059Sjhb	case 1:
1251180059Sjhb		fail_errno("ksem_init");
1252180059Sjhb		break;
1253180059Sjhb	case 2:
1254180059Sjhb		fail_err("Limit of %d semaphores not enforced", nsems_max);
1255180059Sjhb		break;
1256180059Sjhb	default:
1257180059Sjhb		fail_err("bad child state %#x", stat);
1258180059Sjhb		break;
1259180059Sjhb	}
1260180059Sjhb}
1261180059SjhbTEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1262180059Sjhb
1263180059Sjhbstatic int
1264180059Sjhbexhaust_named_child(void *arg)
1265180059Sjhb{
1266180059Sjhb	char buffer[64];
1267180059Sjhb	semid_t id;
1268180059Sjhb	int i, max;
1269180059Sjhb
1270180059Sjhb	max = (intptr_t)arg;
1271180059Sjhb	for (i = 0; i < max + 1; i++) {
1272180059Sjhb		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1273180059Sjhb		if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
1274180059Sjhb			if (errno == ENOSPC || errno == EMFILE ||
1275180059Sjhb			    errno == ENFILE)
1276180059Sjhb				return (CSTAT(0, 0));
1277180059Sjhb			return (CSTAT(1, errno));
1278180059Sjhb		}
1279180059Sjhb	}
1280180059Sjhb	return (CSTAT(2, errno));
1281180059Sjhb}
1282180059Sjhb
1283180059Sjhbstatic void
1284180059Sjhbexhaust_named_sems(void)
1285180059Sjhb{
1286180059Sjhb	char buffer[64];
1287180059Sjhb	size_t len;
1288180059Sjhb	int i, nsems_max, stat;
1289180059Sjhb
1290180059Sjhb	len = sizeof(nsems_max);
1291180059Sjhb	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1292180059Sjhb	    0) {
1293180059Sjhb		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1294180059Sjhb		return;
1295180059Sjhb	}
1296180059Sjhb
1297205148Skib	if (child_worker(exhaust_named_child, (void *)(uintptr_t)nsems_max,
1298205148Skib	    &stat) < 0)
1299180059Sjhb		return;
1300180059Sjhb	errno = CSTAT_ERROR(stat);
1301180059Sjhb	switch (CSTAT_CLASS(stat)) {
1302180059Sjhb	case 0:
1303180059Sjhb		pass();
1304180059Sjhb		break;
1305180059Sjhb	case 1:
1306180059Sjhb		fail_errno("ksem_open");
1307180059Sjhb		break;
1308180059Sjhb	case 2:
1309180059Sjhb		fail_err("Limit of %d semaphores not enforced", nsems_max);
1310180059Sjhb		break;
1311180059Sjhb	default:
1312180059Sjhb		fail_err("bad child state %#x", stat);
1313180059Sjhb		break;
1314180059Sjhb	}
1315180059Sjhb
1316180059Sjhb	/* Cleanup any semaphores created by the child. */
1317180059Sjhb	for (i = 0; i < nsems_max + 1; i++) {
1318180059Sjhb		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1319180059Sjhb		ksem_unlink(buffer);
1320180059Sjhb	}
1321180059Sjhb}
1322180059SjhbTEST(exhaust_named_sems, "exhaust named semaphores (1)");
1323180059Sjhb
1324180059Sjhbstatic int
1325180059Sjhbfdlimit_set(void *arg)
1326180059Sjhb{
1327180059Sjhb	struct rlimit rlim;
1328180059Sjhb	int max;
1329180059Sjhb
1330180059Sjhb	max = (intptr_t)arg;
1331180059Sjhb	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
1332180059Sjhb		return (CSTAT(3, errno));
1333180059Sjhb	rlim.rlim_cur = max;
1334180059Sjhb	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
1335180059Sjhb		return (CSTAT(4, errno));
1336180059Sjhb	return (0);
1337180059Sjhb}
1338180059Sjhb
1339180059Sjhbstatic int
1340180059Sjhbfdlimit_unnamed_child(void *arg)
1341180059Sjhb{
1342180059Sjhb	int stat;
1343180059Sjhb
1344180059Sjhb	stat = fdlimit_set(arg);
1345180059Sjhb	if (stat == 0)
1346180059Sjhb		stat = exhaust_unnamed_child(arg);
1347180059Sjhb	return (stat);
1348180059Sjhb}
1349180059Sjhb
1350180059Sjhbstatic void
1351180059Sjhbfdlimit_unnamed_sems(void)
1352180059Sjhb{
1353180059Sjhb	int nsems_max, stat;
1354180059Sjhb
1355180059Sjhb	nsems_max = 10;
1356205148Skib	if (child_worker(fdlimit_unnamed_child, (void *)(uintptr_t)nsems_max,
1357205148Skib	    &stat))
1358180059Sjhb		return;
1359180059Sjhb	errno = CSTAT_ERROR(stat);
1360180059Sjhb	switch (CSTAT_CLASS(stat)) {
1361180059Sjhb	case 0:
1362180059Sjhb		pass();
1363180059Sjhb		break;
1364180059Sjhb	case 1:
1365180059Sjhb		fail_errno("ksem_init");
1366180059Sjhb		break;
1367180059Sjhb	case 2:
1368180059Sjhb		fail_err("Limit of %d semaphores not enforced", nsems_max);
1369180059Sjhb		break;
1370180059Sjhb	case 3:
1371180059Sjhb		fail_errno("getrlimit");
1372180059Sjhb		break;
1373180059Sjhb	case 4:
1374180059Sjhb		fail_errno("getrlimit");
1375180059Sjhb		break;
1376180059Sjhb	default:
1377180059Sjhb		fail_err("bad child state %#x", stat);
1378180059Sjhb		break;
1379180059Sjhb	}
1380180059Sjhb}
1381180059SjhbTEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1382180059Sjhb
1383180059Sjhbstatic int
1384180059Sjhbfdlimit_named_child(void *arg)
1385180059Sjhb{
1386180059Sjhb	int stat;
1387180059Sjhb
1388180059Sjhb	stat = fdlimit_set(arg);
1389180059Sjhb	if (stat == 0)
1390180059Sjhb		stat = exhaust_named_child(arg);
1391180059Sjhb	return (stat);
1392180059Sjhb}
1393180059Sjhb
1394180059Sjhbstatic void
1395180059Sjhbfdlimit_named_sems(void)
1396180059Sjhb{
1397180059Sjhb	char buffer[64];
1398180059Sjhb	int i, nsems_max, stat;
1399180059Sjhb
1400180059Sjhb	nsems_max = 10;
1401205148Skib	if (child_worker(fdlimit_named_child, (void *)(uintptr_t)nsems_max,
1402205148Skib	    &stat) < 0)
1403180059Sjhb		return;
1404180059Sjhb	errno = CSTAT_ERROR(stat);
1405180059Sjhb	switch (CSTAT_CLASS(stat)) {
1406180059Sjhb	case 0:
1407180059Sjhb		pass();
1408180059Sjhb		break;
1409180059Sjhb	case 1:
1410180059Sjhb		fail_errno("ksem_open");
1411180059Sjhb		break;
1412180059Sjhb	case 2:
1413180059Sjhb		fail_err("Limit of %d semaphores not enforced", nsems_max);
1414180059Sjhb		break;
1415180059Sjhb	case 3:
1416180059Sjhb		fail_errno("getrlimit");
1417180059Sjhb		break;
1418180059Sjhb	case 4:
1419180059Sjhb		fail_errno("getrlimit");
1420180059Sjhb		break;
1421180059Sjhb	default:
1422180059Sjhb		fail_err("bad child state %#x", stat);
1423180059Sjhb		break;
1424180059Sjhb	}
1425180059Sjhb
1426180059Sjhb	/* Cleanup any semaphores created by the child. */
1427180059Sjhb	for (i = 0; i < nsems_max + 1; i++) {
1428180059Sjhb		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1429180059Sjhb		ksem_unlink(buffer);
1430180059Sjhb	}
1431180059Sjhb}
1432180059SjhbTEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1433180059Sjhb
1434180059Sjhbint
1435180059Sjhbmain(int argc, char *argv[])
1436180059Sjhb{
1437180059Sjhb
1438180059Sjhb	signal(SIGSYS, SIG_IGN);
1439180059Sjhb	run_tests();
1440180059Sjhb	return (0);
1441180059Sjhb}
1442