xref: /minix3/tests/lib/libc/gen/posix_spawn/t_fileactions.c (revision 11be35a165022172ed3cea20f2b5df0307540b0e)
1*11be35a1SLionel Sambuc /* $NetBSD: t_fileactions.c,v 1.5 2012/04/09 19:42:07 martin Exp $ */
2*11be35a1SLionel Sambuc 
3*11be35a1SLionel Sambuc /*-
4*11be35a1SLionel Sambuc  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5*11be35a1SLionel Sambuc  * All rights reserved.
6*11be35a1SLionel Sambuc  *
7*11be35a1SLionel Sambuc  * This code is derived from software contributed to The NetBSD Foundation
8*11be35a1SLionel Sambuc  * by Charles Zhang <charles@NetBSD.org> and
9*11be35a1SLionel Sambuc  * Martin Husemann <martin@NetBSD.org>.
10*11be35a1SLionel Sambuc  *
11*11be35a1SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
12*11be35a1SLionel Sambuc  * modification, are permitted provided that the following conditions
13*11be35a1SLionel Sambuc  * are met:
14*11be35a1SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
15*11be35a1SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
16*11be35a1SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
17*11be35a1SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
18*11be35a1SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
19*11be35a1SLionel Sambuc  *
20*11be35a1SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21*11be35a1SLionel Sambuc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22*11be35a1SLionel Sambuc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23*11be35a1SLionel Sambuc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24*11be35a1SLionel Sambuc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25*11be35a1SLionel Sambuc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26*11be35a1SLionel Sambuc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27*11be35a1SLionel Sambuc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28*11be35a1SLionel Sambuc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*11be35a1SLionel Sambuc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30*11be35a1SLionel Sambuc  * POSSIBILITY OF SUCH DAMAGE.
31*11be35a1SLionel Sambuc  */
32*11be35a1SLionel Sambuc 
33*11be35a1SLionel Sambuc 
34*11be35a1SLionel Sambuc #include <atf-c.h>
35*11be35a1SLionel Sambuc #include <stdio.h>
36*11be35a1SLionel Sambuc #include <stdlib.h>
37*11be35a1SLionel Sambuc #include <string.h>
38*11be35a1SLionel Sambuc #include <errno.h>
39*11be35a1SLionel Sambuc #include <fcntl.h>
40*11be35a1SLionel Sambuc #include <spawn.h>
41*11be35a1SLionel Sambuc #include <unistd.h>
42*11be35a1SLionel Sambuc #include <sys/wait.h>
43*11be35a1SLionel Sambuc 
44*11be35a1SLionel Sambuc 
45*11be35a1SLionel Sambuc ATF_TC(t_spawn_openmode);
46*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_openmode,tc)47*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_openmode, tc)
48*11be35a1SLionel Sambuc {
49*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
50*11be35a1SLionel Sambuc 	    "Test the proper handling of 'mode' for 'open' fileactions");
51*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
52*11be35a1SLionel Sambuc }
53*11be35a1SLionel Sambuc 
54*11be35a1SLionel Sambuc static off_t
filesize(const char * restrict fname)55*11be35a1SLionel Sambuc filesize(const char * restrict fname)
56*11be35a1SLionel Sambuc {
57*11be35a1SLionel Sambuc 	struct stat st;
58*11be35a1SLionel Sambuc 	int err;
59*11be35a1SLionel Sambuc 
60*11be35a1SLionel Sambuc 	err = stat(fname, &st);
61*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
62*11be35a1SLionel Sambuc 	return st.st_size;
63*11be35a1SLionel Sambuc }
64*11be35a1SLionel Sambuc 
65*11be35a1SLionel Sambuc #define TESTFILE	"./the_input_data"
66*11be35a1SLionel Sambuc #define CHECKFILE	"./the_output_data"
67*11be35a1SLionel Sambuc #define TESTCONTENT	"marry has a little lamb"
68*11be35a1SLionel Sambuc 
69*11be35a1SLionel Sambuc static void
make_testfile(const char * restrict file)70*11be35a1SLionel Sambuc make_testfile(const char *restrict file)
71*11be35a1SLionel Sambuc {
72*11be35a1SLionel Sambuc 	FILE *f;
73*11be35a1SLionel Sambuc 	size_t written;
74*11be35a1SLionel Sambuc 
75*11be35a1SLionel Sambuc 	f = fopen(file, "w");
76*11be35a1SLionel Sambuc 	ATF_REQUIRE(f != NULL);
77*11be35a1SLionel Sambuc 	written = fwrite(TESTCONTENT, 1, strlen(TESTCONTENT), f);
78*11be35a1SLionel Sambuc 	fclose(f);
79*11be35a1SLionel Sambuc 	ATF_REQUIRE(written == strlen(TESTCONTENT));
80*11be35a1SLionel Sambuc }
81*11be35a1SLionel Sambuc 
82*11be35a1SLionel Sambuc static void
empty_outfile(const char * restrict filename)83*11be35a1SLionel Sambuc empty_outfile(const char *restrict filename)
84*11be35a1SLionel Sambuc {
85*11be35a1SLionel Sambuc 	FILE *f;
86*11be35a1SLionel Sambuc 
87*11be35a1SLionel Sambuc 	f = fopen(filename, "w");
88*11be35a1SLionel Sambuc 	ATF_REQUIRE(f != NULL);
89*11be35a1SLionel Sambuc 	fclose(f);
90*11be35a1SLionel Sambuc }
91*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_openmode,tc)92*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_openmode, tc)
93*11be35a1SLionel Sambuc {
94*11be35a1SLionel Sambuc 	int status, err;
95*11be35a1SLionel Sambuc 	pid_t pid;
96*11be35a1SLionel Sambuc 	size_t insize, outsize;
97*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("cat"), NULL };
98*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
99*11be35a1SLionel Sambuc 
100*11be35a1SLionel Sambuc 	/*
101*11be35a1SLionel Sambuc 	 * try a "cat < testfile > checkfile"
102*11be35a1SLionel Sambuc 	 */
103*11be35a1SLionel Sambuc 	make_testfile(TESTFILE);
104*11be35a1SLionel Sambuc 	unlink(CHECKFILE);
105*11be35a1SLionel Sambuc 
106*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
107*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
108*11be35a1SLionel Sambuc 	    TESTFILE, O_RDONLY, 0);
109*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
110*11be35a1SLionel Sambuc 	    CHECKFILE, O_WRONLY|O_CREAT, 0600);
111*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
112*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
113*11be35a1SLionel Sambuc 
114*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
115*11be35a1SLionel Sambuc 
116*11be35a1SLionel Sambuc 	/* ok, wait for the child to finish */
117*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
118*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
119*11be35a1SLionel Sambuc 
120*11be35a1SLionel Sambuc 	/* now check that input and output have the same size */
121*11be35a1SLionel Sambuc 	insize = filesize(TESTFILE);
122*11be35a1SLionel Sambuc 	outsize = filesize(CHECKFILE);
123*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
124*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == outsize);
125*11be35a1SLionel Sambuc 
126*11be35a1SLionel Sambuc 	/*
127*11be35a1SLionel Sambuc 	 * try a "cat < testfile >> checkfile"
128*11be35a1SLionel Sambuc 	 */
129*11be35a1SLionel Sambuc 	make_testfile(TESTFILE);
130*11be35a1SLionel Sambuc 	make_testfile(CHECKFILE);
131*11be35a1SLionel Sambuc 
132*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
133*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
134*11be35a1SLionel Sambuc 	    TESTFILE, O_RDONLY, 0);
135*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
136*11be35a1SLionel Sambuc 	    CHECKFILE, O_WRONLY|O_APPEND, 0);
137*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
138*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
139*11be35a1SLionel Sambuc 
140*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
141*11be35a1SLionel Sambuc 
142*11be35a1SLionel Sambuc 	/* ok, wait for the child to finish */
143*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
144*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
145*11be35a1SLionel Sambuc 
146*11be35a1SLionel Sambuc 	/* now check that output is twice as long as input */
147*11be35a1SLionel Sambuc 	insize = filesize(TESTFILE);
148*11be35a1SLionel Sambuc 	outsize = filesize(CHECKFILE);
149*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
150*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize*2 == outsize);
151*11be35a1SLionel Sambuc 
152*11be35a1SLionel Sambuc 	/*
153*11be35a1SLionel Sambuc 	 * try a "cat < testfile  > checkfile" with input and output swapped
154*11be35a1SLionel Sambuc 	 */
155*11be35a1SLionel Sambuc 	make_testfile(TESTFILE);
156*11be35a1SLionel Sambuc 	empty_outfile(CHECKFILE);
157*11be35a1SLionel Sambuc 
158*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
159*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
160*11be35a1SLionel Sambuc 	    TESTFILE, O_RDONLY, 0);
161*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
162*11be35a1SLionel Sambuc 	    CHECKFILE, O_WRONLY, 0);
163*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
164*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
165*11be35a1SLionel Sambuc 
166*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
167*11be35a1SLionel Sambuc 
168*11be35a1SLionel Sambuc 	/* ok, wait for the child to finish */
169*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
170*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_FAILURE);
171*11be35a1SLionel Sambuc 
172*11be35a1SLionel Sambuc 	/* now check that input and output are still the same size */
173*11be35a1SLionel Sambuc 	insize = filesize(TESTFILE);
174*11be35a1SLionel Sambuc 	outsize = filesize(CHECKFILE);
175*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
176*11be35a1SLionel Sambuc 	ATF_REQUIRE(outsize == 0);
177*11be35a1SLionel Sambuc }
178*11be35a1SLionel Sambuc 
179*11be35a1SLionel Sambuc ATF_TC(t_spawn_reopen);
180*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_reopen,tc)181*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_reopen, tc)
182*11be35a1SLionel Sambuc {
183*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
184*11be35a1SLionel Sambuc 	    "an open filehandle can be replaced by a 'open' fileaction");
185*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
186*11be35a1SLionel Sambuc }
187*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_reopen,tc)188*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_reopen, tc)
189*11be35a1SLionel Sambuc {
190*11be35a1SLionel Sambuc 	int status, err;
191*11be35a1SLionel Sambuc 	pid_t pid;
192*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("cat"), NULL };
193*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
194*11be35a1SLionel Sambuc 
195*11be35a1SLionel Sambuc 	/*
196*11be35a1SLionel Sambuc 	 * make sure stdin is open in the parent
197*11be35a1SLionel Sambuc 	 */
198*11be35a1SLionel Sambuc 	freopen("/dev/zero", "r", stdin);
199*11be35a1SLionel Sambuc 	/*
200*11be35a1SLionel Sambuc 	 * now request an open for this fd again in the child
201*11be35a1SLionel Sambuc 	 */
202*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
203*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
204*11be35a1SLionel Sambuc 	    "/dev/null", O_RDONLY, 0);
205*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
206*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
207*11be35a1SLionel Sambuc 
208*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
209*11be35a1SLionel Sambuc 
210*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
211*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
212*11be35a1SLionel Sambuc }
213*11be35a1SLionel Sambuc 
214*11be35a1SLionel Sambuc ATF_TC(t_spawn_open_nonexistent);
215*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_open_nonexistent,tc)216*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_open_nonexistent, tc)
217*11be35a1SLionel Sambuc {
218*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
219*11be35a1SLionel Sambuc 	    "posix_spawn fails when a file to open does not exist");
220*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
221*11be35a1SLionel Sambuc }
222*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_open_nonexistent,tc)223*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_open_nonexistent, tc)
224*11be35a1SLionel Sambuc {
225*11be35a1SLionel Sambuc 	int err, status;
226*11be35a1SLionel Sambuc 	pid_t pid;
227*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("cat"), NULL };
228*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
229*11be35a1SLionel Sambuc 
230*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
231*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
232*11be35a1SLionel Sambuc 	    "./non/ex/ist/ent", O_RDONLY, 0);
233*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
234*11be35a1SLionel Sambuc 	if (err == 0) {
235*11be35a1SLionel Sambuc 		/*
236*11be35a1SLionel Sambuc 		 * The child has been created - it should fail and
237*11be35a1SLionel Sambuc 		 * return exit code 127
238*11be35a1SLionel Sambuc 		 */
239*11be35a1SLionel Sambuc 		waitpid(pid, &status, 0);
240*11be35a1SLionel Sambuc 		ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127);
241*11be35a1SLionel Sambuc 	} else {
242*11be35a1SLionel Sambuc 		/*
243*11be35a1SLionel Sambuc 		 * The error has been noticed early enough, no child has
244*11be35a1SLionel Sambuc 		 * been run
245*11be35a1SLionel Sambuc 		 */
246*11be35a1SLionel Sambuc 		ATF_REQUIRE(err == ENOENT);
247*11be35a1SLionel Sambuc 	}
248*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
249*11be35a1SLionel Sambuc }
250*11be35a1SLionel Sambuc 
251*11be35a1SLionel Sambuc ATF_TC(t_spawn_open_nonexistent_diag);
252*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_open_nonexistent_diag,tc)253*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_open_nonexistent_diag, tc)
254*11be35a1SLionel Sambuc {
255*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
256*11be35a1SLionel Sambuc 	    "posix_spawn fails when a file to open does not exist "
257*11be35a1SLionel Sambuc 	    "and delivers proper diagnostic");
258*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
259*11be35a1SLionel Sambuc }
260*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_open_nonexistent_diag,tc)261*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_open_nonexistent_diag, tc)
262*11be35a1SLionel Sambuc {
263*11be35a1SLionel Sambuc 	int err;
264*11be35a1SLionel Sambuc 	pid_t pid;
265*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("cat"), NULL };
266*11be35a1SLionel Sambuc 	posix_spawnattr_t attr;
267*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
268*11be35a1SLionel Sambuc 
269*11be35a1SLionel Sambuc 	posix_spawnattr_init(&attr);
270*11be35a1SLionel Sambuc 	/*
271*11be35a1SLionel Sambuc 	 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
272*11be35a1SLionel Sambuc 	 * will cause a "proper" return value from posix_spawn(2)
273*11be35a1SLionel Sambuc 	 * instead of a (potential) success there and a 127 exit
274*11be35a1SLionel Sambuc 	 * status from the child process (c.f. the non-diag variant
275*11be35a1SLionel Sambuc 	 * of this test).
276*11be35a1SLionel Sambuc 	 */
277*11be35a1SLionel Sambuc 	posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR);
278*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
279*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
280*11be35a1SLionel Sambuc 	    "./non/ex/ist/ent", O_RDONLY, 0);
281*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, &attr, args, NULL);
282*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == ENOENT);
283*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
284*11be35a1SLionel Sambuc 	posix_spawnattr_destroy(&attr);
285*11be35a1SLionel Sambuc }
286*11be35a1SLionel Sambuc 
287*11be35a1SLionel Sambuc ATF_TC(t_spawn_fileactions);
288*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_fileactions,tc)289*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_fileactions, tc)
290*11be35a1SLionel Sambuc {
291*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
292*11be35a1SLionel Sambuc 	    "Tests various complex fileactions");
293*11be35a1SLionel Sambuc }
294*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_fileactions,tc)295*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_fileactions, tc)
296*11be35a1SLionel Sambuc {
297*11be35a1SLionel Sambuc 	int fd1, fd2, fd3, status, err;
298*11be35a1SLionel Sambuc 	pid_t pid;
299*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("h_fileactions"), NULL };
300*11be35a1SLionel Sambuc 	char helper[FILENAME_MAX];
301*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
302*11be35a1SLionel Sambuc 
303*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
304*11be35a1SLionel Sambuc 
305*11be35a1SLionel Sambuc 	closefrom(fileno(stderr)+1);
306*11be35a1SLionel Sambuc 
307*11be35a1SLionel Sambuc 	fd1 = open("/dev/null", O_RDONLY);
308*11be35a1SLionel Sambuc 	ATF_REQUIRE(fd1 == 3);
309*11be35a1SLionel Sambuc 
310*11be35a1SLionel Sambuc 	fd2 = open("/dev/null", O_WRONLY, O_CLOEXEC);
311*11be35a1SLionel Sambuc 	ATF_REQUIRE(fd2 == 4);
312*11be35a1SLionel Sambuc 
313*11be35a1SLionel Sambuc 	fd3 = open("/dev/null", O_WRONLY);
314*11be35a1SLionel Sambuc 	ATF_REQUIRE(fd3 == 5);
315*11be35a1SLionel Sambuc 
316*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addclose(&fa, fd1);
317*11be35a1SLionel Sambuc 	posix_spawn_file_actions_addopen(&fa, 6, "/dev/null", O_RDWR, 0);
318*11be35a1SLionel Sambuc 	posix_spawn_file_actions_adddup2(&fa, 1, 7);
319*11be35a1SLionel Sambuc 
320*11be35a1SLionel Sambuc 	snprintf(helper, sizeof helper, "%s/h_fileactions",
321*11be35a1SLionel Sambuc 	    atf_tc_get_config_var(tc, "srcdir"));
322*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, helper, &fa, NULL, args, NULL);
323*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
324*11be35a1SLionel Sambuc 
325*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
326*11be35a1SLionel Sambuc 
327*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
328*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
329*11be35a1SLionel Sambuc }
330*11be35a1SLionel Sambuc 
331*11be35a1SLionel Sambuc ATF_TC(t_spawn_empty_fileactions);
332*11be35a1SLionel Sambuc 
ATF_TC_HEAD(t_spawn_empty_fileactions,tc)333*11be35a1SLionel Sambuc ATF_TC_HEAD(t_spawn_empty_fileactions, tc)
334*11be35a1SLionel Sambuc {
335*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "descr",
336*11be35a1SLionel Sambuc 	    "posix_spawn with empty fileactions (PR kern/46038)");
337*11be35a1SLionel Sambuc 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
338*11be35a1SLionel Sambuc }
339*11be35a1SLionel Sambuc 
ATF_TC_BODY(t_spawn_empty_fileactions,tc)340*11be35a1SLionel Sambuc ATF_TC_BODY(t_spawn_empty_fileactions, tc)
341*11be35a1SLionel Sambuc {
342*11be35a1SLionel Sambuc 	int status, err;
343*11be35a1SLionel Sambuc 	pid_t pid;
344*11be35a1SLionel Sambuc 	char * const args[2] = { __UNCONST("cat"), NULL };
345*11be35a1SLionel Sambuc 	posix_spawn_file_actions_t fa;
346*11be35a1SLionel Sambuc 	size_t insize, outsize;
347*11be35a1SLionel Sambuc 
348*11be35a1SLionel Sambuc 	/*
349*11be35a1SLionel Sambuc 	 * try a "cat < testfile > checkfile", but set up stdin/stdout
350*11be35a1SLionel Sambuc 	 * already in the parent and pass empty file actions to the child.
351*11be35a1SLionel Sambuc 	 */
352*11be35a1SLionel Sambuc 	make_testfile(TESTFILE);
353*11be35a1SLionel Sambuc 	unlink(CHECKFILE);
354*11be35a1SLionel Sambuc 
355*11be35a1SLionel Sambuc 	freopen(TESTFILE, "r", stdin);
356*11be35a1SLionel Sambuc 	freopen(CHECKFILE, "w", stdout);
357*11be35a1SLionel Sambuc 
358*11be35a1SLionel Sambuc 	posix_spawn_file_actions_init(&fa);
359*11be35a1SLionel Sambuc 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
360*11be35a1SLionel Sambuc 	posix_spawn_file_actions_destroy(&fa);
361*11be35a1SLionel Sambuc 
362*11be35a1SLionel Sambuc 	ATF_REQUIRE(err == 0);
363*11be35a1SLionel Sambuc 
364*11be35a1SLionel Sambuc 	/* ok, wait for the child to finish */
365*11be35a1SLionel Sambuc 	waitpid(pid, &status, 0);
366*11be35a1SLionel Sambuc 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
367*11be35a1SLionel Sambuc 
368*11be35a1SLionel Sambuc 	/* now check that input and output have the same size */
369*11be35a1SLionel Sambuc 	insize = filesize(TESTFILE);
370*11be35a1SLionel Sambuc 	outsize = filesize(CHECKFILE);
371*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
372*11be35a1SLionel Sambuc 	ATF_REQUIRE(insize == outsize);
373*11be35a1SLionel Sambuc }
374*11be35a1SLionel Sambuc 
ATF_TP_ADD_TCS(tp)375*11be35a1SLionel Sambuc ATF_TP_ADD_TCS(tp)
376*11be35a1SLionel Sambuc {
377*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_fileactions);
378*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent);
379*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent_diag);
380*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_reopen);
381*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_openmode);
382*11be35a1SLionel Sambuc 	ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions);
383*11be35a1SLionel Sambuc 
384*11be35a1SLionel Sambuc 	return atf_no_error();
385*11be35a1SLionel Sambuc }
386