1188483Srwatson/*-
2188483Srwatson * Copyright (c) 2009 Robert N. M. Watson
3188483Srwatson * All rights reserved.
4188483Srwatson *
5188483Srwatson * This software was developed at the University of Cambridge Computer
6298490Sngie * Laboratory with support from a grant from Google, Inc.
7188483Srwatson *
8188483Srwatson * Redistribution and use in source and binary forms, with or without
9188483Srwatson * modification, are permitted provided that the following conditions
10188483Srwatson * are met:
11188483Srwatson * 1. Redistributions of source code must retain the above copyright
12188483Srwatson *    notice, this list of conditions and the following disclaimer.
13188483Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14188483Srwatson *    notice, this list of conditions and the following disclaimer in the
15188483Srwatson *    documentation and/or other materials provided with the distribution.
16188483Srwatson *
17188483Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18188483Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19188483Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20188483Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21188483Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22188483Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23188483Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24188483Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25188483Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26188483Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27188483Srwatson * SUCH DAMAGE.
28188483Srwatson *
29188483Srwatson * $FreeBSD: stable/10/tests/sys/file/newfileops_on_fork_test.c 319301 2017-05-31 08:36:47Z ngie $
30188483Srwatson */
31188483Srwatson
32188483Srwatson/*
33188483Srwatson * When a multi-threaded application calls fork(2) from one thread while
34188483Srwatson * another thread is blocked in accept(2), we prefer that the file descriptor
35188483Srwatson * to be returned by accept(2) not appear in the child process.  Test this by
36188483Srwatson * creating a thread blocked in accept(2), then forking a child and seeing if
37188483Srwatson * the fd it would have returned is defined in the child or not.
38188483Srwatson */
39188483Srwatson
40188483Srwatson#include <sys/socket.h>
41188483Srwatson#include <sys/wait.h>
42188483Srwatson
43188483Srwatson#include <netinet/in.h>
44188483Srwatson
45188483Srwatson#include <err.h>
46188483Srwatson#include <errno.h>
47188483Srwatson#include <pthread.h>
48188483Srwatson#include <signal.h>
49188483Srwatson#include <stdlib.h>
50188483Srwatson#include <string.h>
51188483Srwatson#include <unistd.h>
52188483Srwatson
53188483Srwatson#define	PORT	9000
54188483Srwatson
55188483Srwatsonstatic int listen_fd;
56188483Srwatson
57188483Srwatsonstatic void *
58188483Srwatsondo_accept(__unused void *arg)
59188483Srwatson{
60188483Srwatson	int accept_fd;
61188483Srwatson
62188483Srwatson	accept_fd = accept(listen_fd, NULL, NULL);
63188483Srwatson	if (accept_fd < 0)
64319301Sngie		err(1, "accept");
65188483Srwatson
66319301Sngie	close(accept_fd);
67188483Srwatson	return (NULL);
68188483Srwatson}
69188483Srwatson
70188483Srwatsonstatic void
71188483Srwatsondo_fork(void)
72188483Srwatson{
73188483Srwatson	int pid;
74188483Srwatson
75188483Srwatson	pid = fork();
76188483Srwatson	if (pid < 0)
77319301Sngie		err(1, "fork");
78188483Srwatson	if (pid > 0) {
79188483Srwatson		waitpid(pid, NULL, 0);
80188483Srwatson		exit(0);
81188483Srwatson	}
82188483Srwatson
83188483Srwatson	/*
84188483Srwatson	 * We will call ftruncate(2) on the next available file descriptor,
85188483Srwatson	 * listen_fd+1, and get back EBADF if it's not a valid descriptor,
86188483Srwatson	 * and EINVAL if it is.  This (currently) works fine in practice.
87188483Srwatson	 */
88319301Sngie	if (ftruncate(listen_fd + 1, 0) < 0) {
89188483Srwatson		if (errno == EBADF)
90188483Srwatson			exit(0);
91188483Srwatson		else if (errno == EINVAL)
92319301Sngie			errx(1, "file descriptor still open in child");
93188483Srwatson		else
94319301Sngie			err(1, "unexpected error");
95188483Srwatson	} else
96319301Sngie		errx(1, "ftruncate succeeded");
97188483Srwatson}
98188483Srwatson
99188483Srwatsonint
100319301Sngiemain(void)
101188483Srwatson{
102188483Srwatson	struct sockaddr_in sin;
103188483Srwatson	pthread_t accept_thread;
104188483Srwatson
105188483Srwatson	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
106188483Srwatson	if (listen_fd < 0)
107319301Sngie		err(1, "socket");
108188483Srwatson	bzero(&sin, sizeof(sin));
109188483Srwatson	sin.sin_family = AF_INET;
110188483Srwatson	sin.sin_len = sizeof(sin);
111188483Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
112188483Srwatson	sin.sin_port = htons(PORT);
113188483Srwatson	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
114319301Sngie		err(1, "bind");
115188483Srwatson	if (listen(listen_fd, -1) <0)
116319301Sngie		err(1, "listen");
117203800Sru	if (pthread_create(&accept_thread, NULL, do_accept, NULL) != 0)
118319301Sngie		err(1, "pthread_create");
119298490Sngie	sleep(1);	/* Easier than using a CV. */
120188483Srwatson	do_fork();
121188483Srwatson	exit(0);
122188483Srwatson}
123