newfileops_on_fork_test.c revision 303975
1281348Scy/*- 2281348Scy * Copyright (c) 2009 Robert N. M. Watson 3281348Scy * All rights reserved. 4281348Scy * 5281348Scy * This software was developed at the University of Cambridge Computer 6281348Scy * Laboratory with support from a grant from Google, Inc. 7281348Scy * 8281348Scy * Redistribution and use in source and binary forms, with or without 9310419Sdelphij * modification, are permitted provided that the following conditions 10281348Scy * are met: 11281348Scy * 1. Redistributions of source code must retain the above copyright 12281348Scy * notice, this list of conditions and the following disclaimer. 13281348Scy * 2. Redistributions in binary form must reproduce the above copyright 14281348Scy * notice, this list of conditions and the following disclaimer in the 15281348Scy * documentation and/or other materials provided with the distribution. 16281348Scy * 17281348Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18281348Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19281348Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20281348Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21281348Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22281348Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23281348Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24281348Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25281348Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26281348Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27281348Scy * SUCH DAMAGE. 28281348Scy * 29281348Scy * $FreeBSD: releng/11.0/tests/sys/file/newfileops_on_fork_test.c 298196 2016-04-18 07:48:27Z ngie $ 30281348Scy */ 31281348Scy 32281348Scy/* 33281348Scy * When a multi-threaded application calls fork(2) from one thread while 34281348Scy * another thread is blocked in accept(2), we prefer that the file descriptor 35281348Scy * to be returned by accept(2) not appear in the child process. Test this by 36281348Scy * creating a thread blocked in accept(2), then forking a child and seeing if 37281348Scy * the fd it would have returned is defined in the child or not. 38281348Scy */ 39281348Scy 40281348Scy#include <sys/socket.h> 41281348Scy#include <sys/wait.h> 42281348Scy 43281348Scy#include <netinet/in.h> 44281348Scy 45281348Scy#include <err.h> 46281348Scy#include <errno.h> 47281348Scy#include <pthread.h> 48281348Scy#include <signal.h> 49281348Scy#include <stdlib.h> 50281348Scy#include <string.h> 51281348Scy#include <unistd.h> 52281348Scy 53281348Scy#define PORT 9000 54281348Scy 55281348Scystatic int listen_fd; 56281348Scy 57281348Scystatic void * 58281348Scydo_accept(__unused void *arg) 59281348Scy{ 60281348Scy int accept_fd; 61281348Scy 62281348Scy accept_fd = accept(listen_fd, NULL, NULL); 63281348Scy if (accept_fd < 0) 64281348Scy err(-1, "accept"); 65281348Scy 66281348Scy return (NULL); 67281348Scy} 68281348Scy 69281348Scystatic void 70281348Scydo_fork(void) 71281348Scy{ 72281348Scy int pid; 73290000Sglebius 74290000Sglebius pid = fork(); 75290000Sglebius if (pid < 0) 76290000Sglebius err(-1, "fork"); 77290000Sglebius if (pid > 0) { 78290000Sglebius waitpid(pid, NULL, 0); 79290000Sglebius exit(0); 80290000Sglebius } 81290000Sglebius 82290000Sglebius /* 83290000Sglebius * We will call ftruncate(2) on the next available file descriptor, 84290000Sglebius * listen_fd+1, and get back EBADF if it's not a valid descriptor, 85290000Sglebius * and EINVAL if it is. This (currently) works fine in practice. 86290000Sglebius */ 87290000Sglebius if (ftruncate(listen_fd + 1, 0 < 0)) { 88290000Sglebius if (errno == EBADF) 89290000Sglebius exit(0); 90290000Sglebius else if (errno == EINVAL) 91290000Sglebius errx(-1, "file descriptor still open in child"); 92290000Sglebius else 93290000Sglebius err(-1, "unexpected error"); 94290000Sglebius } else 95290000Sglebius errx(-1, "ftruncate succeeded"); 96290000Sglebius} 97290000Sglebius 98290000Sglebiusint 99290000Sglebiusmain(__unused int argc, __unused char *argv[]) 100290000Sglebius{ 101290000Sglebius struct sockaddr_in sin; 102290000Sglebius pthread_t accept_thread; 103290000Sglebius 104290000Sglebius listen_fd = socket(PF_INET, SOCK_STREAM, 0); 105290000Sglebius if (listen_fd < 0) 106290000Sglebius err(-1, "socket"); 107290000Sglebius bzero(&sin, sizeof(sin)); 108290000Sglebius sin.sin_family = AF_INET; 109290000Sglebius sin.sin_len = sizeof(sin); 110290000Sglebius sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 111290000Sglebius sin.sin_port = htons(PORT); 112290000Sglebius if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 113290000Sglebius err(-1, "bind"); 114290000Sglebius if (listen(listen_fd, -1) <0) 115290000Sglebius err(-1, "listen"); 116290000Sglebius if (pthread_create(&accept_thread, NULL, do_accept, NULL) != 0) 117290000Sglebius err(-1, "pthread_create"); 118290000Sglebius sleep(1); /* Easier than using a CV. */ 119290000Sglebius do_fork(); 120290000Sglebius exit(0); 121290000Sglebius} 122290000Sglebius