1194262Sjhb/*-
2194262Sjhb * Copyright (c) 2009 Advanced Computing Technologies LLC
3194262Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org>
4194262Sjhb * All rights reserved.
5194262Sjhb *
6194262Sjhb * Redistribution and use in source and binary forms, with or without
7194262Sjhb * modification, are permitted provided that the following conditions
8194262Sjhb * are met:
9194262Sjhb * 1. Redistributions of source code must retain the above copyright
10194262Sjhb *    notice, this list of conditions and the following disclaimer.
11194262Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12194262Sjhb *    notice, this list of conditions and the following disclaimer in the
13194262Sjhb *    documentation and/or other materials provided with the distribution.
14194262Sjhb *
15194262Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16194262Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17194262Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18194262Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19194262Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20194262Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21194262Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22194262Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23194262Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24194262Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25194262Sjhb * SUCH DAMAGE.
26194262Sjhb */
27194262Sjhb
28194262Sjhb#include <sys/cdefs.h>
29194262Sjhb__FBSDID("$FreeBSD$");
30194262Sjhb
31194262Sjhb/*
32194262Sjhb * Regression tests for the closefrom(2) system call.
33194262Sjhb */
34194262Sjhb
35194262Sjhb#include <sys/param.h>
36194262Sjhb#include <sys/mman.h>
37194262Sjhb#include <sys/user.h>
38194262Sjhb#include <sys/wait.h>
39194262Sjhb#include <errno.h>
40194262Sjhb#include <fcntl.h>
41194262Sjhb#include <libutil.h>
42194262Sjhb#include <stdarg.h>
43194262Sjhb#include <stdio.h>
44194262Sjhb#include <stdlib.h>
45194262Sjhb#include <string.h>
46194262Sjhb#include <unistd.h>
47194262Sjhb
48194262Sjhbstruct shared_info {
49194262Sjhb	int	failed;
50194262Sjhb	char	tag[64];
51194262Sjhb	char	message[0];
52194262Sjhb};
53194262Sjhb
54194262Sjhbstatic int test = 1;
55194262Sjhb
56194262Sjhbstatic void
57194262Sjhbok(const char *descr)
58194262Sjhb{
59194262Sjhb
60194262Sjhb	printf("ok %d - %s\n", test, descr);
61194262Sjhb	test++;
62194262Sjhb}
63194262Sjhb
64194262Sjhbstatic void
65194262Sjhbfail(const char *descr, const char *fmt, ...)
66194262Sjhb{
67194262Sjhb	va_list ap;
68194262Sjhb
69194262Sjhb	printf("not ok %d - %s", test, descr);
70194262Sjhb	test++;
71194262Sjhb	if (fmt) {
72194262Sjhb		va_start(ap, fmt);
73194262Sjhb		printf(" # ");
74194262Sjhb		vprintf(fmt, ap);
75194262Sjhb		va_end(ap);
76194262Sjhb	}
77194262Sjhb	printf("\n");
78194262Sjhb	exit(1);
79194262Sjhb}
80194262Sjhb
81194262Sjhb#define	fail_err(descr)		fail((descr), "%s", strerror(errno))
82194262Sjhb
83194262Sjhbstatic void
84194262Sjhbcok(struct shared_info *info, const char *descr)
85194262Sjhb{
86194262Sjhb
87194262Sjhb	info->failed = 0;
88194262Sjhb	strlcpy(info->tag, descr, sizeof(info->tag));
89194262Sjhb	exit(0);
90194262Sjhb}
91194262Sjhb
92194262Sjhbstatic void
93194262Sjhbcfail(struct shared_info *info, const char *descr, const char *fmt, ...)
94194262Sjhb{
95194262Sjhb	va_list ap;
96194262Sjhb
97194262Sjhb	info->failed = 1;
98194262Sjhb	strlcpy(info->tag, descr, sizeof(info->tag));
99194262Sjhb	if (fmt) {
100194262Sjhb		va_start(ap, fmt);
101194262Sjhb		vsprintf(info->message, fmt, ap);
102194262Sjhb		va_end(ap);
103194262Sjhb	}
104194262Sjhb	exit(0);
105194262Sjhb}
106194262Sjhb
107194262Sjhb#define	cfail_err(info, descr)	cfail((info), (descr), "%s", strerror(errno))
108194262Sjhb
109194262Sjhb/*
110194262Sjhb * Use kinfo_getfile() to fetch the list of file descriptors and figure out
111194262Sjhb * the highest open file descriptor.
112194262Sjhb */
113194262Sjhbstatic int
114194262Sjhbhighest_fd(void)
115194262Sjhb{
116194262Sjhb	struct kinfo_file *kif;
117194262Sjhb	int cnt, i, highest;
118194262Sjhb
119194262Sjhb	kif = kinfo_getfile(getpid(), &cnt);
120194262Sjhb	if (kif == NULL)
121194262Sjhb		fail_err("kinfo_getfile");
122194262Sjhb	highest = INT_MIN;
123194262Sjhb	for (i = 0; i < cnt; i++)
124194262Sjhb		if (kif[i].kf_fd > highest)
125194262Sjhb			highest = kif[i].kf_fd;
126194262Sjhb	free(kif);
127194262Sjhb	return (highest);
128194262Sjhb}
129194262Sjhb
130194262Sjhbstatic int
131194262Sjhbdevnull(void)
132194262Sjhb{
133194262Sjhb	int fd;
134194262Sjhb
135194262Sjhb	fd = open("/dev/null", O_RDONLY);
136194262Sjhb	if (fd < 0)
137194262Sjhb		fail_err("open(\"/dev/null\")");
138194262Sjhb	return (fd);
139194262Sjhb}
140194262Sjhb
141194262Sjhbint
142194262Sjhbmain(int __unused argc, char __unused *argv[])
143194262Sjhb{
144194262Sjhb	struct shared_info *info;
145194262Sjhb	pid_t pid;
146194262Sjhb	int fd, i;
147194262Sjhb
148194262Sjhb	printf("1..15\n");
149194262Sjhb
150194262Sjhb	/* We better start up with fd's 0, 1, and 2 open. */
151194262Sjhb	fd = devnull();
152194262Sjhb	if (fd != 3)
153194262Sjhb		fail("open", "bad descriptor %d", fd);
154194262Sjhb	ok("open");
155194262Sjhb
156194262Sjhb	/* Make sure highest_fd() works. */
157194262Sjhb	fd = highest_fd();
158194262Sjhb	if (fd != 3)
159194262Sjhb		fail("highest_fd", "bad descriptor %d", fd);
160194262Sjhb	ok("highest_fd");
161194262Sjhb
162194262Sjhb	/* Try to use closefrom() for just closing fd 3. */
163194262Sjhb	closefrom(3);
164194262Sjhb	fd = highest_fd();
165194262Sjhb	if (fd != 2)
166194262Sjhb		fail("closefrom", "highest fd %d", fd);
167194262Sjhb	ok("closefrom");
168194262Sjhb
169194262Sjhb	/* Eat up 16 descriptors. */
170194262Sjhb	for (i = 0; i < 16; i++)
171194262Sjhb		(void)devnull();
172194262Sjhb	fd = highest_fd();
173194262Sjhb	if (fd != 18)
174194262Sjhb		fail("open 16", "highest fd %d", fd);
175194262Sjhb	ok("open 16");
176194262Sjhb
177194262Sjhb	/* Close half of them. */
178194262Sjhb	closefrom(11);
179194262Sjhb	fd = highest_fd();
180194262Sjhb	if (fd != 10)
181194262Sjhb		fail("closefrom", "highest fd %d", fd);
182194262Sjhb	ok("closefrom");
183194262Sjhb
184194262Sjhb	/* Explicitly close descriptors 6 and 8 to create holes. */
185194262Sjhb	if (close(6) < 0 || close(8) < 0)
186194262Sjhb		fail_err("close2 ");
187194262Sjhb	ok("close 2");
188194262Sjhb
189194262Sjhb	/* Verify that close on 6 and 8 fails with EBADF. */
190194262Sjhb	if (close(6) == 0)
191194262Sjhb		fail("close(6)", "did not fail");
192194262Sjhb	if (errno != EBADF)
193194262Sjhb		fail_err("close(6)");
194194262Sjhb	ok("close(6)");
195194262Sjhb	if (close(8) == 0)
196194262Sjhb		fail("close(8)", "did not fail");
197194262Sjhb	if (errno != EBADF)
198194262Sjhb		fail_err("close(8)");
199194262Sjhb	ok("close(8)");
200194262Sjhb
201194262Sjhb	/* Close from 4 on. */
202194262Sjhb	closefrom(4);
203194262Sjhb	fd = highest_fd();
204194262Sjhb	if (fd != 3)
205194262Sjhb		fail("closefrom", "highest fd %d", fd);
206194262Sjhb	ok("closefrom");
207194262Sjhb
208194262Sjhb	/* Allocate a small SHM region for IPC with our child. */
209194262Sjhb	info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON |
210194262Sjhb	    MAP_SHARED, -1, 0);
211194262Sjhb	if (info == MAP_FAILED)
212194262Sjhb		fail_err("mmap");
213194262Sjhb	ok("mmap");
214194262Sjhb
215194262Sjhb	/* Fork a child process to test closefrom(0). */
216194262Sjhb	pid = fork();
217194262Sjhb	if (pid < 0)
218194262Sjhb		fail_err("fork");
219194262Sjhb	if (pid == 0) {
220194262Sjhb		/* Child. */
221194262Sjhb		closefrom(0);
222194262Sjhb		fd = highest_fd();
223194262Sjhb		if (fd >= 0)
224194262Sjhb			cfail(info, "closefrom(0)", "highest fd %d", fd);
225194262Sjhb		cok(info, "closefrom(0)");
226194262Sjhb	}
227194262Sjhb	if (wait(NULL) < 0)
228194262Sjhb		fail_err("wait");
229194262Sjhb	if (info->failed)
230194262Sjhb		fail(info->tag, "%s", info->message);
231194262Sjhb	ok(info->tag);
232194262Sjhb
233194262Sjhb	/* Fork a child process to test closefrom(-1). */
234194262Sjhb	pid = fork();
235194262Sjhb	if (pid < 0)
236194262Sjhb		fail_err("fork");
237194262Sjhb	if (pid == 0) {
238194262Sjhb		/* Child. */
239194262Sjhb		closefrom(-1);
240194262Sjhb		fd = highest_fd();
241194262Sjhb		if (fd >= 0)
242194262Sjhb			cfail(info, "closefrom(-1)", "highest fd %d", fd);
243194262Sjhb		cok(info, "closefrom(-1)");
244194262Sjhb	}
245194262Sjhb	if (wait(NULL) < 0)
246194262Sjhb		fail_err("wait");
247194262Sjhb	if (info->failed)
248194262Sjhb		fail(info->tag, "%s", info->message);
249194262Sjhb	ok(info->tag);
250194262Sjhb
251194262Sjhb	/* Dup stdout to 6. */
252194262Sjhb	if (dup2(1, 6) < 0)
253194262Sjhb		fail_err("dup2");
254194262Sjhb	fd = highest_fd();
255194262Sjhb	if (fd != 6)
256194262Sjhb		fail("dup2", "highest fd %d", fd);
257194262Sjhb	ok("dup2");
258194262Sjhb
259194262Sjhb	/* Do a closefrom() starting in a hole. */
260194262Sjhb	closefrom(4);
261194262Sjhb	fd = highest_fd();
262194262Sjhb	if (fd != 3)
263194262Sjhb		fail("closefrom", "highest fd %d", fd);
264194262Sjhb	ok("closefrom");
265194262Sjhb
266194262Sjhb	/* Do a closefrom() beyond our highest open fd. */
267194262Sjhb	closefrom(32);
268194262Sjhb	fd = highest_fd();
269194262Sjhb	if (fd != 3)
270194262Sjhb		fail("closefrom", "highest fd %d", fd);
271194262Sjhb	ok("closefrom");
272194262Sjhb
273194262Sjhb	return (0);
274194262Sjhb}
275