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