1229997Sken/*- 2229997Sken * Copyright (c) 2005 Robert N. M. Watson 3229997Sken * All rights reserved. 4232604Strasz * 5229997Sken * Redistribution and use in source and binary forms, with or without 6229997Sken * modification, are permitted provided that the following conditions 7232604Strasz * are met: 8232604Strasz * 1. Redistributions of source code must retain the above copyright 9232604Strasz * notice, this list of conditions and the following disclaimer. 10229997Sken * 2. Redistributions in binary form must reproduce the above copyright 11229997Sken * notice, this list of conditions and the following disclaimer in the 12229997Sken * documentation and/or other materials provided with the distribution. 13229997Sken * 14229997Sken * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15229997Sken * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16229997Sken * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17229997Sken * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18229997Sken * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19229997Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20229997Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21229997Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22229997Sken * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23229997Sken * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24229997Sken * SUCH DAMAGE. 25229997Sken * 26229997Sken * $FreeBSD: releng/10.3/tests/sys/fifo/fifo_open.c 281450 2015-04-12 06:18:24Z ngie $ 27229997Sken */ 28229997Sken 29229997Sken#include <sys/types.h> 30229997Sken#include <sys/stat.h> 31229997Sken#include <sys/wait.h> 32229997Sken 33229997Sken#include <err.h> 34229997Sken#include <errno.h> 35229997Sken#include <fcntl.h> 36229997Sken#include <limits.h> 37229997Sken#include <signal.h> 38229997Sken#include <stdio.h> 39229997Sken#include <stdlib.h> 40229997Sken#include <string.h> 41229997Sken#include <unistd.h> 42229997Sken 43229997Sken/* 44229997Sken * Regression test to exercise various POSIX-defined parts of fifo behavior 45229997Sken * described for open(2): 46229997Sken * 47229997Sken * O_NONBLOCK 48229997Sken * When opening a FIFO with O_RDONLY or O_WRONLY set: 49229997Sken * 50229997Sken * - If O_NONBLOCK is set, an open() for reading-only shall return without 51229997Sken * delay. An open() for writing-only shall return an error if no process 52229997Sken * currently has the file open for reading. 53229997Sken * 54265634Smav * - If O_NONBLOCK is clear, an open() for reading-only shall block the 55229997Sken * calling thread until a thread opens the file for writing. An open() 56229997Sken * for writing-only shall block the calling thread until a thread opens 57229997Sken * the file for reading. 58229997Sken * 59229997Sken * When opening a block special or character special file that supports 60229997Sken * non-blocking opens: 61229997Sken * 62229997Sken * - If O_NONBLOCK is set, the open() function shall return without blocking 63229997Sken * for the device to be ready or available. Subsequent behavior of the 64229997Sken * device is device-specific. 65229997Sken * 66229997Sken * - If O_NONBLOCK is clear, the open() function shall block the calling 67229997Sken * thread until the device is ready or available before returning. 68229997Sken * 69229997Sken * Special errors: 70229997Sken * 71229997Sken * [ENXIO] 72229997Sken * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no 73275892Smav * process has the file open for reading. 74229997Sken */ 75229997Sken 76229997Sken/* 77229997Sken * In order to test blocking/non-blocking behavior, test processes must 78229997Sken * potentially block themselves until released. As bugs in blocking result 79229997Sken * in processes that won't un-block, we must sacrifice a process to the task, 80229997Sken * watching and potentially killing it after a time-out. The main test 81229997Sken * process is never used to open or act directly on a fifo (other than to 82229997Sken * create or unlink it) in order to avoid the main test process being 83229997Sken * blocked. 84229997Sken */ 85229997Sken 86229997Sken/* 87229997Sken * All activity occurs within a temporary directory created early in the 88229997Sken * test. 89229997Sken */ 90229997Skenstatic char temp_dir[PATH_MAX]; 91229997Sken 92229997Skenstatic void __unused 93229997Skenatexit_temp_dir(void) 94229997Sken{ 95265642Smav 96265642Smav rmdir(temp_dir); 97229997Sken} 98268151Smav 99268151Smav/* 100265642Smav * Run a function in a particular test process. 101268151Smav */ 102268151Smavstatic int 103229997Skenrun_in_process(int (*func)(void), pid_t *pidp, const char *errstr) 104229997Sken{ 105229997Sken pid_t pid; 106229997Sken 107229997Sken pid = fork(); 108229997Sken if (pid < 0) { 109229997Sken warn("%s: run_in_process: fork", errstr); 110229997Sken return (-1); 111268150Smav } 112268150Smav 113268151Smav if (pid == 0) 114268151Smav exit(func()); 115268150Smav 116229997Sken if (pidp != NULL) 117229997Sken *pidp = pid; 118229997Sken 119229997Sken return (0); 120229997Sken} 121229997Sken 122229997Sken/* 123229997Sken * Wait for a process on a timeout, and if the timeout expires, kill the 124229997Sken * process. Test each second rather than waiting the full timeout at once to 125229997Sken * minimize the amount of time spent hanging around unnecessarily. 126229997Sken */ 127229997Skenstatic int 128229997Skenwait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr) 129229997Sken{ 130229997Sken pid_t wpid; 131229997Sken int i; 132229997Sken 133229997Sken /* 134229997Sken * Count up to the timeout, but do a non-hanging waitpid() after each 135229997Sken * second so we can avoid waiting a lot of extra time. 136229997Sken */ 137229997Sken for (i = 0; i < timeout; i++) { 138229997Sken wpid = waitpid(pid, status, WNOHANG); 139229997Sken if (wpid < 0) { 140229997Sken warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 141229997Sken return (-1); 142229997Sken } 143229997Sken 144229997Sken if (wpid == pid) 145229997Sken return (0); 146229997Sken 147229997Sken sleep(1); 148229997Sken } 149229997Sken 150229997Sken wpid = waitpid(pid, status, WNOHANG); 151274732Smav if (wpid < 0) { 152274732Smav warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 153229997Sken return (-1); 154229997Sken } 155229997Sken 156229997Sken if (wpid == pid) 157229997Sken return (0); 158229997Sken 159273315Smav if (kill(pid, SIGTERM) < 0) { 160229997Sken warn("%s: wait_and_timeout: kill %d", errstr, pid); 161229997Sken return (-1); 162229997Sken } 163229997Sken 164229997Sken wpid = waitpid(pid, status, 0); 165229997Sken if (wpid < 0) { 166229997Sken warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 167229997Sken return (-1); 168265634Smav } 169275892Smav 170274732Smav if (wpid != pid) { 171229997Sken warn("%s: waitpid: returned %d not %d", errstr, wpid, pid); 172229997Sken return (-1); 173229997Sken } 174229997Sken 175229997Sken warnx("%s: process blocked", errstr); 176264727Smav return (-1); 177264727Smav} 178229997Sken 179229997Skenstatic int 180229997Skennon_blocking_open_reader(void) 181229997Sken{ 182229997Sken int fd; 183229997Sken 184229997Sken fd = open("testfifo", O_RDONLY | O_NONBLOCK); 185229997Sken if (fd < 0) 186229997Sken return (errno); 187275892Smav close(fd); 188229997Sken 189229997Sken return (0); 190268549Smav} 191268549Smav 192229997Skenstatic int 193229997Skennon_blocking_open_writer(void) 194229997Sken{ 195229997Sken int fd; 196229997Sken 197229997Sken fd = open("testfifo", O_WRONLY | O_NONBLOCK); 198229997Sken if (fd < 0) 199229997Sken return (errno); 200229997Sken close(fd); 201229997Sken 202229997Sken return (0); 203229997Sken} 204229997Sken 205229997Skenstatic int 206229997Skenblocking_open_reader(void) 207229997Sken{ 208229997Sken int fd; 209229997Sken 210229997Sken fd = open("testfifo", O_RDONLY); 211229997Sken if (fd < 0) 212229997Sken return (errno); 213229997Sken close(fd); 214229997Sken 215229997Sken return (0); 216229997Sken} 217229997Sken 218229997Skenstatic int 219229997Skenblocking_open_writer(void) 220229997Sken{ 221229997Sken int fd; 222229997Sken 223229997Sken fd = open("testfifo", O_WRONLY); 224229997Sken if (fd < 0) 225229997Sken return (errno); 226229997Sken close(fd); 227265634Smav 228229997Sken return (0); 229229997Sken} 230229997Sken 231229997Skenstatic void 232229997Skentest_blocking_reader(void) 233229997Sken{ 234229997Sken pid_t reader_pid, writer_pid, wpid; 235229997Sken int error, status; 236229997Sken 237229997Sken if (mkfifo("testfifo", 0600) < 0) 238229997Sken err(-1, "test_blocking_reader: mkfifo: testfifo"); 239229997Sken 240229997Sken /* 241229997Sken * Block a process in opening the fifo. 242229997Sken */ 243229997Sken if (run_in_process(blocking_open_reader, &reader_pid, 244229997Sken "test_blocking_reader: blocking_open_reader") < 0) { 245229997Sken (void)unlink("testfifo"); 246275892Smav exit(-1); 247275892Smav } 248275893Smav 249275893Smav /* 250229997Sken * Test that it blocked. 251229997Sken */ 252265634Smav sleep(5); 253265634Smav wpid = waitpid(reader_pid, &status, WNOHANG); 254229997Sken if (wpid < 0) { 255229997Sken error = errno; 256274732Smav (void)unlink("testfifo"); 257274732Smav errno = error; 258275892Smav err(-1, "test_blocking_reader: waitpid %d", reader_pid); 259275892Smav } 260229997Sken 261229997Sken if (wpid != 0 && wpid != reader_pid) { 262229997Sken (void)unlink("testfifo"); 263229997Sken errx(-1, "test_blocking_reader: waitpid %d returned %d", 264229997Sken reader_pid, wpid); 265229997Sken } 266229997Sken 267229997Sken if (wpid == reader_pid) { 268229997Sken (void)unlink("testfifo"); 269229997Sken errx(-1, "test_blocking_reader: blocking child didn't " 270229997Sken "block"); 271229997Sken } 272229997Sken 273229997Sken /* 274229997Sken * Unblock the blocking reader. 275229997Sken */ 276229997Sken if (run_in_process(blocking_open_writer, &writer_pid, 277229997Sken "test_blocking_reader: blocking_open_writer") < 0) { 278229997Sken (void)unlink("testfifo"); 279229997Sken (void)kill(reader_pid, SIGTERM); 280232604Strasz (void)waitpid(reader_pid, &status, 0); 281232604Strasz exit(-1); 282232604Strasz } 283232604Strasz 284232604Strasz /* 285232604Strasz * Make sure both processes exited quickly (<1 second) to make sure 286229997Sken * they didn't block, and GC. 287229997Sken */ 288229997Sken if (wait_and_timeout(reader_pid, 1, &status, 289229997Sken "test_blocking_reader: blocking_open_reader") < 0) { 290229997Sken (void)unlink("testinfo"); 291229997Sken (void)kill(reader_pid, SIGTERM); 292274732Smav (void)kill(writer_pid, SIGTERM); 293229997Sken exit(-1); 294229997Sken } 295229997Sken 296229997Sken if (wait_and_timeout(writer_pid, 1, &status, 297230334Sken "test_blocking_reader: blocking_open_writer") < 0) { 298230334Sken (void)unlink("testinfo"); 299230334Sken (void)kill(writer_pid, SIGTERM); 300230334Sken exit(-1); 301230334Sken } 302230334Sken 303230334Sken if (unlink("testfifo") < 0) 304230334Sken err(-1, "test_blocking_reader: unlink: testfifo"); 305274732Smav} 306274732Smavstatic void 307229997Skentest_blocking_writer(void) 308229997Sken{ 309229997Sken pid_t reader_pid, writer_pid, wpid; 310229997Sken int error, status; 311229997Sken 312265494Strasz if (mkfifo("testfifo", 0600) < 0) 313265494Strasz err(-1, "test_blocking_writer: mkfifo: testfifo"); 314229997Sken 315229997Sken /* 316229997Sken * Block a process in opening the fifo. 317229997Sken */ 318229997Sken if (run_in_process(blocking_open_writer, &writer_pid, 319265494Strasz "test_blocking_writer: blocking_open_writer") < 0) { 320265494Strasz (void)unlink("testfifo"); 321229997Sken exit(-1); 322229997Sken } 323229997Sken 324229997Sken /* 325229997Sken * Test that it blocked. 326229997Sken */ 327229997Sken sleep(5); 328229997Sken wpid = waitpid(writer_pid, &status, WNOHANG); 329229997Sken if (wpid < 0) { 330229997Sken error = errno; 331229997Sken (void)unlink("testfifo"); 332229997Sken errno = error; 333229997Sken err(-1, "test_blocking_writer: waitpid %d", writer_pid); 334229997Sken } 335229997Sken 336229997Sken if (wpid != 0 && wpid != writer_pid) { 337229997Sken (void)unlink("testfifo"); 338268151Smav errx(-1, "test_blocking_writer: waitpid %d returned %d", 339268151Smav writer_pid, wpid); 340268151Smav } 341268151Smav 342268151Smav if (wpid == writer_pid) { 343268151Smav (void)unlink("testfifo"); 344268151Smav errx(-1, "test_blocking_writer: blocking child didn't " 345229997Sken "block"); 346229997Sken } 347229997Sken 348229997Sken /* 349229997Sken * Unblock the blocking writer. 350229997Sken */ 351229997Sken if (run_in_process(blocking_open_reader, &reader_pid, 352265494Strasz "test_blocking_writer: blocking_open_reader") < 0) { 353229997Sken (void)unlink("testfifo"); 354229997Sken (void)kill(writer_pid, SIGTERM); 355229997Sken (void)waitpid(writer_pid, &status, 0); 356229997Sken exit(-1); 357229997Sken } 358268549Smav 359229997Sken /* 360265634Smav * Make sure both processes exited quickly (<1 second) to make sure 361265634Smav * they didn't block, and GC. 362265634Smav */ 363265634Smav if (wait_and_timeout(writer_pid, 1, &status, 364268151Smav "test_blocking_writer: blocking_open_writer") < 0) { 365265634Smav (void)unlink("testinfo"); 366229997Sken (void)kill(writer_pid, SIGTERM); 367229997Sken (void)kill(reader_pid, SIGTERM); 368229997Sken (void)waitpid(writer_pid, &status, 0); 369229997Sken (void)waitpid(reader_pid, &status, 0); 370229997Sken exit(-1); 371229997Sken } 372229997Sken 373268151Smav if (wait_and_timeout(reader_pid, 1, &status, 374229997Sken "test_blocking_writer: blocking_open_reader") < 0) { 375229997Sken (void)unlink("testinfo"); 376268151Smav (void)kill(reader_pid, SIGTERM); 377268151Smav (void)waitpid(reader_pid, &status, 0); 378229997Sken exit(-1); 379268150Smav } 380229997Sken 381229997Sken if (unlink("testfifo") < 0) 382229997Sken err(-1, "test_blocking_writer: unlink: testfifo"); 383229997Sken} 384229997Sken 385229997Skenstatic void 386229997Skentest_non_blocking_reader(void) 387229997Sken{ 388229997Sken int status; 389229997Sken pid_t pid; 390268151Smav 391229997Sken if (mkfifo("testfifo", 0600) < 0) 392229997Sken err(-1, "test_non_blocking_reader: mkfifo: testfifo"); 393229997Sken 394229997Sken if (run_in_process(non_blocking_open_reader, &pid, 395229997Sken "test_non_blocking_reader: non_blocking_open_reader") < 0) { 396275881Smav (void)unlink("testfifo"); 397275881Smav exit(-1); 398275881Smav } 399268151Smav 400268151Smav status = -1; 401268151Smav if (wait_and_timeout(pid, 5, &status, 402268151Smav "test_non_blocking_reader: non_blocking_open_reader") < 0) { 403268151Smav (void)unlink("testfifo"); 404268151Smav exit(-1); 405268151Smav } 406268151Smav 407268151Smav if (WEXITSTATUS(status) != 0) { 408268151Smav (void)unlink("testfifo"); 409268151Smav errno = WEXITSTATUS(status); 410268151Smav err(-1, "test_non_blocking_reader: " 411268151Smav "non_blocking_open_reader: open: testfifo"); 412268151Smav } 413268151Smav 414268151Smav if (unlink("testfifo") < 0) 415268151Smav err(-1, "test_non_blocking_reader: unlink: testfifo"); 416268151Smav} 417268151Smav 418268151Smavstatic void 419268151Smavtest_non_blocking_writer(void) 420268151Smav{ 421275881Smav int status; 422275881Smav pid_t pid; 423275881Smav 424229997Sken if (mkfifo("testfifo", 0600) < 0) 425229997Sken err(-1, "test_non_blocking_writer: mkfifo: testfifo"); 426229997Sken 427229997Sken if (run_in_process(non_blocking_open_writer, &pid, 428229997Sken "test_non_blocking_writer: non_blocking_open_writer") < 0) { 429229997Sken (void)unlink("testfifo"); 430229997Sken exit(-1); 431229997Sken } 432229997Sken 433229997Sken status = -1; 434229997Sken if (wait_and_timeout(pid, 5, &status, 435229997Sken "test_non_blocking_writer: non_blocking_open_writer") < 0) { 436229997Sken (void)unlink("testfifo"); 437229997Sken exit(-1); 438229997Sken } 439229997Sken 440229997Sken if (WEXITSTATUS(status) != ENXIO) { 441229997Sken (void)unlink("testfifo"); 442229997Sken 443229997Sken errno = WEXITSTATUS(status); 444229997Sken if (errno == 0) 445229997Sken errx(-1, "test_non_blocking_writer: " 446229997Sken "non_blocking_open_writer: open succeeded"); 447229997Sken err(-1, "test_non_blocking_writer: " 448229997Sken "non_blocking_open_writer: open: testfifo"); 449229997Sken } 450229997Sken 451229997Sken if (unlink("testfifo") < 0) 452229997Sken err(-1, "test_non_blocking_writer: unlink: testfifo"); 453229997Sken} 454229997Sken 455229997Skenint 456268549Smavmain(void) 457229997Sken{ 458229997Sken 459229997Sken if (geteuid() != 0) 460229997Sken errx(-1, "must be run as root"); 461229997Sken 462229997Sken strcpy(temp_dir, "fifo_open.XXXXXXXXXXX"); 463268549Smav if (mkdtemp(temp_dir) == NULL) 464229997Sken err(-1, "mkdtemp"); 465229997Sken if (chdir(temp_dir) < 0) 466229997Sken err(-1, "chdir: %s", temp_dir); 467229997Sken atexit(atexit_temp_dir); 468229997Sken 469229997Sken test_non_blocking_reader(); 470229997Sken test_non_blocking_writer(); 471229997Sken 472229997Sken test_blocking_reader(); 473229997Sken test_blocking_writer(); 474229997Sken 475229997Sken return (0); 476262299Smav} 477229997Sken