1149985Srwatson/*- 2149985Srwatson * Copyright (c) 2005 Robert N. M. Watson 3149985Srwatson * All rights reserved. 4149985Srwatson * 5149985Srwatson * Redistribution and use in source and binary forms, with or without 6149985Srwatson * modification, are permitted provided that the following conditions 7149985Srwatson * are met: 8149985Srwatson * 1. Redistributions of source code must retain the above copyright 9149985Srwatson * notice, this list of conditions and the following disclaimer. 10149985Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11149985Srwatson * notice, this list of conditions and the following disclaimer in the 12149985Srwatson * documentation and/or other materials provided with the distribution. 13149985Srwatson * 14149985Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15149985Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16149985Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17149985Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18149985Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19149985Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20149985Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21149985Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22149985Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23149985Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24149985Srwatson * SUCH DAMAGE. 25149985Srwatson * 26149985Srwatson * $FreeBSD$ 27149985Srwatson */ 28149985Srwatson 29149985Srwatson#include <sys/types.h> 30149985Srwatson#include <sys/stat.h> 31149985Srwatson#include <sys/wait.h> 32149985Srwatson 33149985Srwatson#include <err.h> 34149985Srwatson#include <errno.h> 35149985Srwatson#include <fcntl.h> 36149985Srwatson#include <limits.h> 37149985Srwatson#include <signal.h> 38149985Srwatson#include <stdio.h> 39149985Srwatson#include <stdlib.h> 40149985Srwatson#include <string.h> 41149985Srwatson#include <unistd.h> 42149985Srwatson 43149985Srwatson/* 44149985Srwatson * Regression test to exercise various POSIX-defined parts of fifo behavior 45149985Srwatson * described for open(2): 46149985Srwatson * 47149985Srwatson * O_NONBLOCK 48149985Srwatson * When opening a FIFO with O_RDONLY or O_WRONLY set: 49149985Srwatson * 50149985Srwatson * - If O_NONBLOCK is set, an open() for reading-only shall return without 51149985Srwatson * delay. An open() for writing-only shall return an error if no process 52149985Srwatson * currently has the file open for reading. 53149985Srwatson * 54149985Srwatson * - If O_NONBLOCK is clear, an open() for reading-only shall block the 55149985Srwatson * calling thread until a thread opens the file for writing. An open() 56149985Srwatson * for writing-only shall block the calling thread until a thread opens 57149985Srwatson * the file for reading. 58149985Srwatson * 59149985Srwatson * When opening a block special or character special file that supports 60149985Srwatson * non-blocking opens: 61149985Srwatson * 62149985Srwatson * - If O_NONBLOCK is set, the open() function shall return without blocking 63149985Srwatson * for the device to be ready or available. Subsequent behavior of the 64149985Srwatson * device is device-specific. 65149985Srwatson * 66149985Srwatson * - If O_NONBLOCK is clear, the open() function shall block the calling 67149985Srwatson * thread until the device is ready or available before returning. 68149985Srwatson * 69149985Srwatson * Special errors: 70149985Srwatson * 71149985Srwatson * [ENXIO] 72149985Srwatson * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no 73149985Srwatson * process has the file open for reading. 74149985Srwatson */ 75149985Srwatson 76149985Srwatson/* 77149985Srwatson * In order to test blocking/non-blocking behavior, test processes must 78149985Srwatson * potentially block themselves until released. As bugs in blocking result 79149985Srwatson * in processes that won't un-block, we must sacrifice a process to the task, 80149985Srwatson * watching and potentially killing it after a time-out. The main test 81149985Srwatson * process is never used to open or act directly on a fifo (other than to 82149985Srwatson * create or unlink it) in order to avoid the main test process being 83149985Srwatson * blocked. 84149985Srwatson */ 85149985Srwatson 86149985Srwatson/* 87149985Srwatson * All activity occurs within a temporary directory created early in the 88149985Srwatson * test. 89149985Srwatson */ 90281450Sngiestatic char temp_dir[PATH_MAX]; 91149985Srwatson 92149985Srwatsonstatic void __unused 93149985Srwatsonatexit_temp_dir(void) 94149985Srwatson{ 95149985Srwatson 96149985Srwatson rmdir(temp_dir); 97149985Srwatson} 98149985Srwatson 99149985Srwatson/* 100149985Srwatson * Run a function in a particular test process. 101149985Srwatson */ 102149985Srwatsonstatic int 103149985Srwatsonrun_in_process(int (*func)(void), pid_t *pidp, const char *errstr) 104149985Srwatson{ 105149985Srwatson pid_t pid; 106149985Srwatson 107149985Srwatson pid = fork(); 108149985Srwatson if (pid < 0) { 109149985Srwatson warn("%s: run_in_process: fork", errstr); 110149985Srwatson return (-1); 111149985Srwatson } 112149985Srwatson 113149985Srwatson if (pid == 0) 114149985Srwatson exit(func()); 115149985Srwatson 116149985Srwatson if (pidp != NULL) 117149985Srwatson *pidp = pid; 118149985Srwatson 119149985Srwatson return (0); 120149985Srwatson} 121149985Srwatson 122149985Srwatson/* 123149985Srwatson * Wait for a process on a timeout, and if the timeout expires, kill the 124149985Srwatson * process. Test each second rather than waiting the full timeout at once to 125149985Srwatson * minimize the amount of time spent hanging around unnecessarily. 126149985Srwatson */ 127149985Srwatsonstatic int 128149985Srwatsonwait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr) 129149985Srwatson{ 130149985Srwatson pid_t wpid; 131149985Srwatson int i; 132149985Srwatson 133149985Srwatson /* 134149985Srwatson * Count up to the timeout, but do a non-hanging waitpid() after each 135149985Srwatson * second so we can avoid waiting a lot of extra time. 136149985Srwatson */ 137149985Srwatson for (i = 0; i < timeout; i++) { 138149985Srwatson wpid = waitpid(pid, status, WNOHANG); 139149985Srwatson if (wpid < 0) { 140149985Srwatson warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 141149985Srwatson return (-1); 142149985Srwatson } 143149985Srwatson 144149985Srwatson if (wpid == pid) 145149985Srwatson return (0); 146149985Srwatson 147149985Srwatson sleep(1); 148149985Srwatson } 149149985Srwatson 150149985Srwatson wpid = waitpid(pid, status, WNOHANG); 151149985Srwatson if (wpid < 0) { 152149985Srwatson warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 153149985Srwatson return (-1); 154149985Srwatson } 155149985Srwatson 156149985Srwatson if (wpid == pid) 157149985Srwatson return (0); 158149985Srwatson 159149985Srwatson if (kill(pid, SIGTERM) < 0) { 160149985Srwatson warn("%s: wait_and_timeout: kill %d", errstr, pid); 161149985Srwatson return (-1); 162149985Srwatson } 163149985Srwatson 164149985Srwatson wpid = waitpid(pid, status, 0); 165149985Srwatson if (wpid < 0) { 166149985Srwatson warn("%s: wait_and_timeout: waitpid %d", errstr, pid); 167149985Srwatson return (-1); 168149985Srwatson } 169149985Srwatson 170149985Srwatson if (wpid != pid) { 171149985Srwatson warn("%s: waitpid: returned %d not %d", errstr, wpid, pid); 172149985Srwatson return (-1); 173149985Srwatson } 174149985Srwatson 175149985Srwatson warnx("%s: process blocked", errstr); 176149985Srwatson return (-1); 177149985Srwatson} 178149985Srwatson 179149985Srwatsonstatic int 180149985Srwatsonnon_blocking_open_reader(void) 181149985Srwatson{ 182149985Srwatson int fd; 183149985Srwatson 184149985Srwatson fd = open("testfifo", O_RDONLY | O_NONBLOCK); 185149985Srwatson if (fd < 0) 186149985Srwatson return (errno); 187149985Srwatson close(fd); 188149985Srwatson 189149985Srwatson return (0); 190149985Srwatson} 191149985Srwatson 192149985Srwatsonstatic int 193149985Srwatsonnon_blocking_open_writer(void) 194149985Srwatson{ 195149985Srwatson int fd; 196149985Srwatson 197149985Srwatson fd = open("testfifo", O_WRONLY | O_NONBLOCK); 198149985Srwatson if (fd < 0) 199149985Srwatson return (errno); 200149985Srwatson close(fd); 201149985Srwatson 202149985Srwatson return (0); 203149985Srwatson} 204149985Srwatson 205149985Srwatsonstatic int 206149985Srwatsonblocking_open_reader(void) 207149985Srwatson{ 208149985Srwatson int fd; 209149985Srwatson 210149985Srwatson fd = open("testfifo", O_RDONLY); 211149985Srwatson if (fd < 0) 212149985Srwatson return (errno); 213149985Srwatson close(fd); 214149985Srwatson 215149985Srwatson return (0); 216149985Srwatson} 217149985Srwatson 218149985Srwatsonstatic int 219149985Srwatsonblocking_open_writer(void) 220149985Srwatson{ 221149985Srwatson int fd; 222149985Srwatson 223149985Srwatson fd = open("testfifo", O_WRONLY); 224149985Srwatson if (fd < 0) 225149985Srwatson return (errno); 226149985Srwatson close(fd); 227149985Srwatson 228149985Srwatson return (0); 229149985Srwatson} 230149985Srwatson 231149985Srwatsonstatic void 232149985Srwatsontest_blocking_reader(void) 233149985Srwatson{ 234149985Srwatson pid_t reader_pid, writer_pid, wpid; 235149985Srwatson int error, status; 236149985Srwatson 237149985Srwatson if (mkfifo("testfifo", 0600) < 0) 238149985Srwatson err(-1, "test_blocking_reader: mkfifo: testfifo"); 239149985Srwatson 240149985Srwatson /* 241149985Srwatson * Block a process in opening the fifo. 242149985Srwatson */ 243149985Srwatson if (run_in_process(blocking_open_reader, &reader_pid, 244149985Srwatson "test_blocking_reader: blocking_open_reader") < 0) { 245149985Srwatson (void)unlink("testfifo"); 246149985Srwatson exit(-1); 247149985Srwatson } 248149985Srwatson 249149985Srwatson /* 250149985Srwatson * Test that it blocked. 251149985Srwatson */ 252149985Srwatson sleep(5); 253149985Srwatson wpid = waitpid(reader_pid, &status, WNOHANG); 254149985Srwatson if (wpid < 0) { 255149985Srwatson error = errno; 256149985Srwatson (void)unlink("testfifo"); 257149985Srwatson errno = error; 258149985Srwatson err(-1, "test_blocking_reader: waitpid %d", reader_pid); 259149985Srwatson } 260149985Srwatson 261149985Srwatson if (wpid != 0 && wpid != reader_pid) { 262149985Srwatson (void)unlink("testfifo"); 263149985Srwatson errx(-1, "test_blocking_reader: waitpid %d returned %d", 264149985Srwatson reader_pid, wpid); 265149985Srwatson } 266149985Srwatson 267149985Srwatson if (wpid == reader_pid) { 268149985Srwatson (void)unlink("testfifo"); 269149985Srwatson errx(-1, "test_blocking_reader: blocking child didn't " 270149985Srwatson "block"); 271149985Srwatson } 272149985Srwatson 273149985Srwatson /* 274149985Srwatson * Unblock the blocking reader. 275149985Srwatson */ 276149985Srwatson if (run_in_process(blocking_open_writer, &writer_pid, 277149985Srwatson "test_blocking_reader: blocking_open_writer") < 0) { 278149985Srwatson (void)unlink("testfifo"); 279149985Srwatson (void)kill(reader_pid, SIGTERM); 280149985Srwatson (void)waitpid(reader_pid, &status, 0); 281149985Srwatson exit(-1); 282149985Srwatson } 283149985Srwatson 284149985Srwatson /* 285149985Srwatson * Make sure both processes exited quickly (<1 second) to make sure 286149985Srwatson * they didn't block, and GC. 287149985Srwatson */ 288149985Srwatson if (wait_and_timeout(reader_pid, 1, &status, 289149985Srwatson "test_blocking_reader: blocking_open_reader") < 0) { 290149985Srwatson (void)unlink("testinfo"); 291149985Srwatson (void)kill(reader_pid, SIGTERM); 292149985Srwatson (void)kill(writer_pid, SIGTERM); 293149985Srwatson exit(-1); 294149985Srwatson } 295149985Srwatson 296149985Srwatson if (wait_and_timeout(writer_pid, 1, &status, 297149985Srwatson "test_blocking_reader: blocking_open_writer") < 0) { 298149985Srwatson (void)unlink("testinfo"); 299149985Srwatson (void)kill(writer_pid, SIGTERM); 300149985Srwatson exit(-1); 301149985Srwatson } 302149985Srwatson 303149985Srwatson if (unlink("testfifo") < 0) 304149985Srwatson err(-1, "test_blocking_reader: unlink: testfifo"); 305149985Srwatson} 306149985Srwatsonstatic void 307149985Srwatsontest_blocking_writer(void) 308149985Srwatson{ 309149985Srwatson pid_t reader_pid, writer_pid, wpid; 310149985Srwatson int error, status; 311149985Srwatson 312149985Srwatson if (mkfifo("testfifo", 0600) < 0) 313149985Srwatson err(-1, "test_blocking_writer: mkfifo: testfifo"); 314149985Srwatson 315149985Srwatson /* 316149985Srwatson * Block a process in opening the fifo. 317149985Srwatson */ 318149985Srwatson if (run_in_process(blocking_open_writer, &writer_pid, 319149985Srwatson "test_blocking_writer: blocking_open_writer") < 0) { 320149985Srwatson (void)unlink("testfifo"); 321149985Srwatson exit(-1); 322149985Srwatson } 323149985Srwatson 324149985Srwatson /* 325149985Srwatson * Test that it blocked. 326149985Srwatson */ 327149985Srwatson sleep(5); 328149985Srwatson wpid = waitpid(writer_pid, &status, WNOHANG); 329149985Srwatson if (wpid < 0) { 330149985Srwatson error = errno; 331149985Srwatson (void)unlink("testfifo"); 332149985Srwatson errno = error; 333149985Srwatson err(-1, "test_blocking_writer: waitpid %d", writer_pid); 334149985Srwatson } 335149985Srwatson 336149985Srwatson if (wpid != 0 && wpid != writer_pid) { 337149985Srwatson (void)unlink("testfifo"); 338149985Srwatson errx(-1, "test_blocking_writer: waitpid %d returned %d", 339149985Srwatson writer_pid, wpid); 340149985Srwatson } 341149985Srwatson 342149985Srwatson if (wpid == writer_pid) { 343149985Srwatson (void)unlink("testfifo"); 344149985Srwatson errx(-1, "test_blocking_writer: blocking child didn't " 345149985Srwatson "block"); 346149985Srwatson } 347149985Srwatson 348149985Srwatson /* 349149985Srwatson * Unblock the blocking writer. 350149985Srwatson */ 351149985Srwatson if (run_in_process(blocking_open_reader, &reader_pid, 352149985Srwatson "test_blocking_writer: blocking_open_reader") < 0) { 353149985Srwatson (void)unlink("testfifo"); 354149985Srwatson (void)kill(writer_pid, SIGTERM); 355149985Srwatson (void)waitpid(writer_pid, &status, 0); 356149985Srwatson exit(-1); 357149985Srwatson } 358149985Srwatson 359149985Srwatson /* 360149985Srwatson * Make sure both processes exited quickly (<1 second) to make sure 361149985Srwatson * they didn't block, and GC. 362149985Srwatson */ 363149985Srwatson if (wait_and_timeout(writer_pid, 1, &status, 364149985Srwatson "test_blocking_writer: blocking_open_writer") < 0) { 365149985Srwatson (void)unlink("testinfo"); 366149985Srwatson (void)kill(writer_pid, SIGTERM); 367149985Srwatson (void)kill(reader_pid, SIGTERM); 368149985Srwatson (void)waitpid(writer_pid, &status, 0); 369149985Srwatson (void)waitpid(reader_pid, &status, 0); 370149985Srwatson exit(-1); 371149985Srwatson } 372149985Srwatson 373149985Srwatson if (wait_and_timeout(reader_pid, 1, &status, 374149985Srwatson "test_blocking_writer: blocking_open_reader") < 0) { 375149985Srwatson (void)unlink("testinfo"); 376149985Srwatson (void)kill(reader_pid, SIGTERM); 377149985Srwatson (void)waitpid(reader_pid, &status, 0); 378149985Srwatson exit(-1); 379149985Srwatson } 380149985Srwatson 381149985Srwatson if (unlink("testfifo") < 0) 382149985Srwatson err(-1, "test_blocking_writer: unlink: testfifo"); 383149985Srwatson} 384149985Srwatson 385149985Srwatsonstatic void 386149985Srwatsontest_non_blocking_reader(void) 387149985Srwatson{ 388149985Srwatson int status; 389149985Srwatson pid_t pid; 390149985Srwatson 391149985Srwatson if (mkfifo("testfifo", 0600) < 0) 392149985Srwatson err(-1, "test_non_blocking_reader: mkfifo: testfifo"); 393149985Srwatson 394149985Srwatson if (run_in_process(non_blocking_open_reader, &pid, 395149985Srwatson "test_non_blocking_reader: non_blocking_open_reader") < 0) { 396149985Srwatson (void)unlink("testfifo"); 397149985Srwatson exit(-1); 398149985Srwatson } 399149985Srwatson 400149985Srwatson status = -1; 401149985Srwatson if (wait_and_timeout(pid, 5, &status, 402149985Srwatson "test_non_blocking_reader: non_blocking_open_reader") < 0) { 403149985Srwatson (void)unlink("testfifo"); 404149985Srwatson exit(-1); 405149985Srwatson } 406149985Srwatson 407149985Srwatson if (WEXITSTATUS(status) != 0) { 408149985Srwatson (void)unlink("testfifo"); 409149985Srwatson errno = WEXITSTATUS(status); 410149985Srwatson err(-1, "test_non_blocking_reader: " 411149985Srwatson "non_blocking_open_reader: open: testfifo"); 412149985Srwatson } 413149985Srwatson 414149985Srwatson if (unlink("testfifo") < 0) 415149985Srwatson err(-1, "test_non_blocking_reader: unlink: testfifo"); 416149985Srwatson} 417149985Srwatson 418149985Srwatsonstatic void 419149985Srwatsontest_non_blocking_writer(void) 420149985Srwatson{ 421149985Srwatson int status; 422149985Srwatson pid_t pid; 423149985Srwatson 424149985Srwatson if (mkfifo("testfifo", 0600) < 0) 425149985Srwatson err(-1, "test_non_blocking_writer: mkfifo: testfifo"); 426149985Srwatson 427149985Srwatson if (run_in_process(non_blocking_open_writer, &pid, 428149985Srwatson "test_non_blocking_writer: non_blocking_open_writer") < 0) { 429149985Srwatson (void)unlink("testfifo"); 430149985Srwatson exit(-1); 431149985Srwatson } 432149985Srwatson 433149985Srwatson status = -1; 434149985Srwatson if (wait_and_timeout(pid, 5, &status, 435149985Srwatson "test_non_blocking_writer: non_blocking_open_writer") < 0) { 436149985Srwatson (void)unlink("testfifo"); 437149985Srwatson exit(-1); 438149985Srwatson } 439149985Srwatson 440149985Srwatson if (WEXITSTATUS(status) != ENXIO) { 441149985Srwatson (void)unlink("testfifo"); 442149985Srwatson 443149985Srwatson errno = WEXITSTATUS(status); 444149985Srwatson if (errno == 0) 445149985Srwatson errx(-1, "test_non_blocking_writer: " 446149985Srwatson "non_blocking_open_writer: open succeeded"); 447149985Srwatson err(-1, "test_non_blocking_writer: " 448149985Srwatson "non_blocking_open_writer: open: testfifo"); 449149985Srwatson } 450149985Srwatson 451149985Srwatson if (unlink("testfifo") < 0) 452149985Srwatson err(-1, "test_non_blocking_writer: unlink: testfifo"); 453149985Srwatson} 454149985Srwatson 455149985Srwatsonint 456281450Sngiemain(void) 457149985Srwatson{ 458149985Srwatson 459149985Srwatson if (geteuid() != 0) 460149985Srwatson errx(-1, "must be run as root"); 461149985Srwatson 462281450Sngie strcpy(temp_dir, "fifo_open.XXXXXXXXXXX"); 463149985Srwatson if (mkdtemp(temp_dir) == NULL) 464149985Srwatson err(-1, "mkdtemp"); 465149998Srwatson if (chdir(temp_dir) < 0) 466149998Srwatson err(-1, "chdir: %s", temp_dir); 467149985Srwatson atexit(atexit_temp_dir); 468149985Srwatson 469149985Srwatson test_non_blocking_reader(); 470149985Srwatson test_non_blocking_writer(); 471149985Srwatson 472149985Srwatson test_blocking_reader(); 473149985Srwatson test_blocking_writer(); 474149985Srwatson 475149985Srwatson return (0); 476149985Srwatson} 477