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
6188483Srwatson * 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$
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)
64188483Srwatson		err(-1, "accept");
65188483Srwatson
66188483Srwatson	return (NULL);
67188483Srwatson}
68188483Srwatson
69188483Srwatsonstatic void
70188483Srwatsondo_fork(void)
71188483Srwatson{
72188483Srwatson	int pid;
73188483Srwatson
74188483Srwatson	pid = fork();
75188483Srwatson	if (pid < 0)
76188483Srwatson		err(-1, "fork");
77188483Srwatson	if (pid > 0) {
78188483Srwatson		waitpid(pid, NULL, 0);
79188483Srwatson		exit(0);
80188483Srwatson	}
81188483Srwatson
82188483Srwatson	/*
83188483Srwatson	 * We will call ftruncate(2) on the next available file descriptor,
84188483Srwatson	 * listen_fd+1, and get back EBADF if it's not a valid descriptor,
85188483Srwatson	 * and EINVAL if it is.  This (currently) works fine in practice.
86188483Srwatson	 */
87188483Srwatson	if (ftruncate(listen_fd + 1, 0 < 0)) {
88188483Srwatson		if (errno == EBADF)
89188483Srwatson			exit(0);
90188483Srwatson		else if (errno == EINVAL)
91188483Srwatson			errx(-1, "file descriptor still open in child");
92188483Srwatson		else
93188483Srwatson			err(-1, "unexpected error");
94188483Srwatson	} else
95188483Srwatson		errx(-1, "ftruncate succeeded");
96188483Srwatson}
97188483Srwatson
98188483Srwatsonint
99188483Srwatsonmain(__unused int argc, __unused char *argv[])
100188483Srwatson{
101188483Srwatson	struct sockaddr_in sin;
102188483Srwatson	pthread_t accept_thread;
103188483Srwatson
104188483Srwatson	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
105188483Srwatson	if (listen_fd < 0)
106188483Srwatson		err(-1, "socket");
107188483Srwatson	bzero(&sin, sizeof(sin));
108188483Srwatson	sin.sin_family = AF_INET;
109188483Srwatson	sin.sin_len = sizeof(sin);
110188483Srwatson	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
111188483Srwatson	sin.sin_port = htons(PORT);
112188483Srwatson	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
113188483Srwatson		err(-1, "bind");
114188483Srwatson	if (listen(listen_fd, -1) <0)
115188483Srwatson		err(-1, "listen");
116203800Sru	if (pthread_create(&accept_thread, NULL, do_accept, NULL) != 0)
117188483Srwatson		err(-1, "pthread_create");
118188483Srwatson	sleep(1);	/* Easier than using a CV. */;
119188483Srwatson	do_fork();
120188483Srwatson	exit(0);
121188483Srwatson}
122