1 /* $NetBSD: t_fileactions.c,v 1.5 2012/04/09 19:42:07 martin Exp $ */
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles Zhang <charles@NetBSD.org> and
9 * Martin Husemann <martin@NetBSD.org>.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
45 ATF_TC(t_spawn_openmode
);
47 ATF_TC_HEAD(t_spawn_openmode
, tc
)
49 atf_tc_set_md_var(tc
, "descr",
50 "Test the proper handling of 'mode' for 'open' fileactions");
51 atf_tc_set_md_var(tc
, "require.progs", "/bin/cat");
55 filesize(const char * restrict fname
)
60 err
= stat(fname
, &st
);
61 ATF_REQUIRE(err
== 0);
65 #define TESTFILE "./the_input_data"
66 #define CHECKFILE "./the_output_data"
67 #define TESTCONTENT "marry has a little lamb"
70 make_testfile(const char *restrict file
)
76 ATF_REQUIRE(f
!= NULL
);
77 written
= fwrite(TESTCONTENT
, 1, strlen(TESTCONTENT
), f
);
79 ATF_REQUIRE(written
== strlen(TESTCONTENT
));
83 empty_outfile(const char *restrict filename
)
87 f
= fopen(filename
, "w");
88 ATF_REQUIRE(f
!= NULL
);
92 ATF_TC_BODY(t_spawn_openmode
, tc
)
96 size_t insize
, outsize
;
97 char * const args
[2] = { __UNCONST("cat"), NULL
};
98 posix_spawn_file_actions_t fa
;
101 * try a "cat < testfile > checkfile"
103 make_testfile(TESTFILE
);
106 posix_spawn_file_actions_init(&fa
);
107 posix_spawn_file_actions_addopen(&fa
, fileno(stdin
),
108 TESTFILE
, O_RDONLY
, 0);
109 posix_spawn_file_actions_addopen(&fa
, fileno(stdout
),
110 CHECKFILE
, O_WRONLY
|O_CREAT
, 0600);
111 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
112 posix_spawn_file_actions_destroy(&fa
);
114 ATF_REQUIRE(err
== 0);
116 /* ok, wait for the child to finish */
117 waitpid(pid
, &status
, 0);
118 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
);
120 /* now check that input and output have the same size */
121 insize
= filesize(TESTFILE
);
122 outsize
= filesize(CHECKFILE
);
123 ATF_REQUIRE(insize
== strlen(TESTCONTENT
));
124 ATF_REQUIRE(insize
== outsize
);
127 * try a "cat < testfile >> checkfile"
129 make_testfile(TESTFILE
);
130 make_testfile(CHECKFILE
);
132 posix_spawn_file_actions_init(&fa
);
133 posix_spawn_file_actions_addopen(&fa
, fileno(stdin
),
134 TESTFILE
, O_RDONLY
, 0);
135 posix_spawn_file_actions_addopen(&fa
, fileno(stdout
),
136 CHECKFILE
, O_WRONLY
|O_APPEND
, 0);
137 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
138 posix_spawn_file_actions_destroy(&fa
);
140 ATF_REQUIRE(err
== 0);
142 /* ok, wait for the child to finish */
143 waitpid(pid
, &status
, 0);
144 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
);
146 /* now check that output is twice as long as input */
147 insize
= filesize(TESTFILE
);
148 outsize
= filesize(CHECKFILE
);
149 ATF_REQUIRE(insize
== strlen(TESTCONTENT
));
150 ATF_REQUIRE(insize
*2 == outsize
);
153 * try a "cat < testfile > checkfile" with input and output swapped
155 make_testfile(TESTFILE
);
156 empty_outfile(CHECKFILE
);
158 posix_spawn_file_actions_init(&fa
);
159 posix_spawn_file_actions_addopen(&fa
, fileno(stdout
),
160 TESTFILE
, O_RDONLY
, 0);
161 posix_spawn_file_actions_addopen(&fa
, fileno(stdin
),
162 CHECKFILE
, O_WRONLY
, 0);
163 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
164 posix_spawn_file_actions_destroy(&fa
);
166 ATF_REQUIRE(err
== 0);
168 /* ok, wait for the child to finish */
169 waitpid(pid
, &status
, 0);
170 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_FAILURE
);
172 /* now check that input and output are still the same size */
173 insize
= filesize(TESTFILE
);
174 outsize
= filesize(CHECKFILE
);
175 ATF_REQUIRE(insize
== strlen(TESTCONTENT
));
176 ATF_REQUIRE(outsize
== 0);
179 ATF_TC(t_spawn_reopen
);
181 ATF_TC_HEAD(t_spawn_reopen
, tc
)
183 atf_tc_set_md_var(tc
, "descr",
184 "an open filehandle can be replaced by a 'open' fileaction");
185 atf_tc_set_md_var(tc
, "require.progs", "/bin/cat");
188 ATF_TC_BODY(t_spawn_reopen
, tc
)
192 char * const args
[2] = { __UNCONST("cat"), NULL
};
193 posix_spawn_file_actions_t fa
;
196 * make sure stdin is open in the parent
198 freopen("/dev/zero", "r", stdin
);
200 * now request an open for this fd again in the child
202 posix_spawn_file_actions_init(&fa
);
203 posix_spawn_file_actions_addopen(&fa
, fileno(stdin
),
204 "/dev/null", O_RDONLY
, 0);
205 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
206 posix_spawn_file_actions_destroy(&fa
);
208 ATF_REQUIRE(err
== 0);
210 waitpid(pid
, &status
, 0);
211 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
);
214 ATF_TC(t_spawn_open_nonexistent
);
216 ATF_TC_HEAD(t_spawn_open_nonexistent
, tc
)
218 atf_tc_set_md_var(tc
, "descr",
219 "posix_spawn fails when a file to open does not exist");
220 atf_tc_set_md_var(tc
, "require.progs", "/bin/cat");
223 ATF_TC_BODY(t_spawn_open_nonexistent
, tc
)
227 char * const args
[2] = { __UNCONST("cat"), NULL
};
228 posix_spawn_file_actions_t fa
;
230 posix_spawn_file_actions_init(&fa
);
231 posix_spawn_file_actions_addopen(&fa
, STDIN_FILENO
,
232 "./non/ex/ist/ent", O_RDONLY
, 0);
233 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
236 * The child has been created - it should fail and
237 * return exit code 127
239 waitpid(pid
, &status
, 0);
240 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == 127);
243 * The error has been noticed early enough, no child has
246 ATF_REQUIRE(err
== ENOENT
);
248 posix_spawn_file_actions_destroy(&fa
);
251 ATF_TC(t_spawn_open_nonexistent_diag
);
253 ATF_TC_HEAD(t_spawn_open_nonexistent_diag
, tc
)
255 atf_tc_set_md_var(tc
, "descr",
256 "posix_spawn fails when a file to open does not exist "
257 "and delivers proper diagnostic");
258 atf_tc_set_md_var(tc
, "require.progs", "/bin/cat");
261 ATF_TC_BODY(t_spawn_open_nonexistent_diag
, tc
)
265 char * const args
[2] = { __UNCONST("cat"), NULL
};
266 posix_spawnattr_t attr
;
267 posix_spawn_file_actions_t fa
;
269 posix_spawnattr_init(&attr
);
271 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
272 * will cause a "proper" return value from posix_spawn(2)
273 * instead of a (potential) success there and a 127 exit
274 * status from the child process (c.f. the non-diag variant
277 posix_spawnattr_setflags(&attr
, POSIX_SPAWN_RETURNERROR
);
278 posix_spawn_file_actions_init(&fa
);
279 posix_spawn_file_actions_addopen(&fa
, STDIN_FILENO
,
280 "./non/ex/ist/ent", O_RDONLY
, 0);
281 err
= posix_spawn(&pid
, "/bin/cat", &fa
, &attr
, args
, NULL
);
282 ATF_REQUIRE(err
== ENOENT
);
283 posix_spawn_file_actions_destroy(&fa
);
284 posix_spawnattr_destroy(&attr
);
287 ATF_TC(t_spawn_fileactions
);
289 ATF_TC_HEAD(t_spawn_fileactions
, tc
)
291 atf_tc_set_md_var(tc
, "descr",
292 "Tests various complex fileactions");
295 ATF_TC_BODY(t_spawn_fileactions
, tc
)
297 int fd1
, fd2
, fd3
, status
, err
;
299 char * const args
[2] = { __UNCONST("h_fileactions"), NULL
};
300 char helper
[FILENAME_MAX
];
301 posix_spawn_file_actions_t fa
;
303 posix_spawn_file_actions_init(&fa
);
305 closefrom(fileno(stderr
)+1);
307 fd1
= open("/dev/null", O_RDONLY
);
308 ATF_REQUIRE(fd1
== 3);
310 fd2
= open("/dev/null", O_WRONLY
, O_CLOEXEC
);
311 ATF_REQUIRE(fd2
== 4);
313 fd3
= open("/dev/null", O_WRONLY
);
314 ATF_REQUIRE(fd3
== 5);
316 posix_spawn_file_actions_addclose(&fa
, fd1
);
317 posix_spawn_file_actions_addopen(&fa
, 6, "/dev/null", O_RDWR
, 0);
318 posix_spawn_file_actions_adddup2(&fa
, 1, 7);
320 snprintf(helper
, sizeof helper
, "%s/h_fileactions",
321 atf_tc_get_config_var(tc
, "srcdir"));
322 err
= posix_spawn(&pid
, helper
, &fa
, NULL
, args
, NULL
);
323 posix_spawn_file_actions_destroy(&fa
);
325 ATF_REQUIRE(err
== 0);
327 waitpid(pid
, &status
, 0);
328 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
);
331 ATF_TC(t_spawn_empty_fileactions
);
333 ATF_TC_HEAD(t_spawn_empty_fileactions
, tc
)
335 atf_tc_set_md_var(tc
, "descr",
336 "posix_spawn with empty fileactions (PR kern/46038)");
337 atf_tc_set_md_var(tc
, "require.progs", "/bin/cat");
340 ATF_TC_BODY(t_spawn_empty_fileactions
, tc
)
344 char * const args
[2] = { __UNCONST("cat"), NULL
};
345 posix_spawn_file_actions_t fa
;
346 size_t insize
, outsize
;
349 * try a "cat < testfile > checkfile", but set up stdin/stdout
350 * already in the parent and pass empty file actions to the child.
352 make_testfile(TESTFILE
);
355 freopen(TESTFILE
, "r", stdin
);
356 freopen(CHECKFILE
, "w", stdout
);
358 posix_spawn_file_actions_init(&fa
);
359 err
= posix_spawn(&pid
, "/bin/cat", &fa
, NULL
, args
, NULL
);
360 posix_spawn_file_actions_destroy(&fa
);
362 ATF_REQUIRE(err
== 0);
364 /* ok, wait for the child to finish */
365 waitpid(pid
, &status
, 0);
366 ATF_REQUIRE(WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
);
368 /* now check that input and output have the same size */
369 insize
= filesize(TESTFILE
);
370 outsize
= filesize(CHECKFILE
);
371 ATF_REQUIRE(insize
== strlen(TESTCONTENT
));
372 ATF_REQUIRE(insize
== outsize
);
377 ATF_TP_ADD_TC(tp
, t_spawn_fileactions
);
378 ATF_TP_ADD_TC(tp
, t_spawn_open_nonexistent
);
379 ATF_TP_ADD_TC(tp
, t_spawn_open_nonexistent_diag
);
380 ATF_TP_ADD_TC(tp
, t_spawn_reopen
);
381 ATF_TP_ADD_TC(tp
, t_spawn_openmode
);
382 ATF_TP_ADD_TC(tp
, t_spawn_empty_fileactions
);
384 return atf_no_error();