xref: /freebsd-src/contrib/netbsd-tests/lib/libc/gen/posix_spawn/t_fileactions.c (revision f16ec9c6e36f1d481468e7301d6f3b857db17561)
1ddba0402SEnji Cooper /* $NetBSD: t_fileactions.c,v 1.6 2017/01/10 22:36:29 christos Exp $ */
257718be8SEnji Cooper 
357718be8SEnji Cooper /*-
457718be8SEnji Cooper  * Copyright (c) 2012 The NetBSD Foundation, Inc.
557718be8SEnji Cooper  * All rights reserved.
657718be8SEnji Cooper  *
757718be8SEnji Cooper  * This code is derived from software contributed to The NetBSD Foundation
857718be8SEnji Cooper  * by Charles Zhang <charles@NetBSD.org> and
957718be8SEnji Cooper  * Martin Husemann <martin@NetBSD.org>.
1057718be8SEnji Cooper  *
1157718be8SEnji Cooper  * Redistribution and use in source and binary forms, with or without
1257718be8SEnji Cooper  * modification, are permitted provided that the following conditions
1357718be8SEnji Cooper  * are met:
1457718be8SEnji Cooper  * 1. Redistributions of source code must retain the above copyright
1557718be8SEnji Cooper  *    notice, this list of conditions and the following disclaimer.
1657718be8SEnji Cooper  * 2. Redistributions in binary form must reproduce the above copyright
1757718be8SEnji Cooper  *    notice, this list of conditions and the following disclaimer in the
1857718be8SEnji Cooper  *    documentation and/or other materials provided with the distribution.
1957718be8SEnji Cooper  *
2057718be8SEnji Cooper  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2157718be8SEnji Cooper  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2257718be8SEnji Cooper  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2357718be8SEnji Cooper  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2457718be8SEnji Cooper  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2557718be8SEnji Cooper  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2657718be8SEnji Cooper  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2757718be8SEnji Cooper  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2857718be8SEnji Cooper  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2957718be8SEnji Cooper  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3057718be8SEnji Cooper  * POSSIBILITY OF SUCH DAMAGE.
3157718be8SEnji Cooper  */
3257718be8SEnji Cooper 
3357718be8SEnji Cooper 
3457718be8SEnji Cooper #include <atf-c.h>
35ddba0402SEnji Cooper 
36ddba0402SEnji Cooper #include <sys/wait.h>
37ddba0402SEnji Cooper #include <sys/stat.h>
38ddba0402SEnji Cooper 
3957718be8SEnji Cooper #include <stdio.h>
4057718be8SEnji Cooper #include <stdlib.h>
4157718be8SEnji Cooper #include <string.h>
4257718be8SEnji Cooper #include <errno.h>
4357718be8SEnji Cooper #include <fcntl.h>
4457718be8SEnji Cooper #include <spawn.h>
4557718be8SEnji Cooper #include <unistd.h>
4657718be8SEnji Cooper 
4757718be8SEnji Cooper 
4857718be8SEnji Cooper ATF_TC(t_spawn_openmode);
4957718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_openmode,tc)5057718be8SEnji Cooper ATF_TC_HEAD(t_spawn_openmode, tc)
5157718be8SEnji Cooper {
5257718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
5357718be8SEnji Cooper 	    "Test the proper handling of 'mode' for 'open' fileactions");
5457718be8SEnji Cooper 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
5557718be8SEnji Cooper }
5657718be8SEnji Cooper 
5757718be8SEnji Cooper static off_t
filesize(const char * restrict fname)5857718be8SEnji Cooper filesize(const char * restrict fname)
5957718be8SEnji Cooper {
6057718be8SEnji Cooper 	struct stat st;
6157718be8SEnji Cooper 	int err;
6257718be8SEnji Cooper 
6357718be8SEnji Cooper 	err = stat(fname, &st);
6457718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
6557718be8SEnji Cooper 	return st.st_size;
6657718be8SEnji Cooper }
6757718be8SEnji Cooper 
6857718be8SEnji Cooper #define TESTFILE	"./the_input_data"
6957718be8SEnji Cooper #define CHECKFILE	"./the_output_data"
7057718be8SEnji Cooper #define TESTCONTENT	"marry has a little lamb"
7157718be8SEnji Cooper 
7257718be8SEnji Cooper static void
make_testfile(const char * restrict file)7357718be8SEnji Cooper make_testfile(const char *restrict file)
7457718be8SEnji Cooper {
7557718be8SEnji Cooper 	FILE *f;
7657718be8SEnji Cooper 	size_t written;
7757718be8SEnji Cooper 
7857718be8SEnji Cooper 	f = fopen(file, "w");
7957718be8SEnji Cooper 	ATF_REQUIRE(f != NULL);
8057718be8SEnji Cooper 	written = fwrite(TESTCONTENT, 1, strlen(TESTCONTENT), f);
8157718be8SEnji Cooper 	fclose(f);
8257718be8SEnji Cooper 	ATF_REQUIRE(written == strlen(TESTCONTENT));
8357718be8SEnji Cooper }
8457718be8SEnji Cooper 
8557718be8SEnji Cooper static void
empty_outfile(const char * restrict filename)8657718be8SEnji Cooper empty_outfile(const char *restrict filename)
8757718be8SEnji Cooper {
8857718be8SEnji Cooper 	FILE *f;
8957718be8SEnji Cooper 
9057718be8SEnji Cooper 	f = fopen(filename, "w");
9157718be8SEnji Cooper 	ATF_REQUIRE(f != NULL);
9257718be8SEnji Cooper 	fclose(f);
9357718be8SEnji Cooper }
9457718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_openmode,tc)9557718be8SEnji Cooper ATF_TC_BODY(t_spawn_openmode, tc)
9657718be8SEnji Cooper {
9757718be8SEnji Cooper 	int status, err;
9857718be8SEnji Cooper 	pid_t pid;
9957718be8SEnji Cooper 	size_t insize, outsize;
10057718be8SEnji Cooper 	char * const args[2] = { __UNCONST("cat"), NULL };
10157718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
10257718be8SEnji Cooper 
10357718be8SEnji Cooper 	/*
10457718be8SEnji Cooper 	 * try a "cat < testfile > checkfile"
10557718be8SEnji Cooper 	 */
10657718be8SEnji Cooper 	make_testfile(TESTFILE);
10757718be8SEnji Cooper 	unlink(CHECKFILE);
10857718be8SEnji Cooper 
10957718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
11057718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
11157718be8SEnji Cooper 	    TESTFILE, O_RDONLY, 0);
11257718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
11357718be8SEnji Cooper 	    CHECKFILE, O_WRONLY|O_CREAT, 0600);
11457718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
11557718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
11657718be8SEnji Cooper 
11757718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
11857718be8SEnji Cooper 
11957718be8SEnji Cooper 	/* ok, wait for the child to finish */
12057718be8SEnji Cooper 	waitpid(pid, &status, 0);
12157718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
12257718be8SEnji Cooper 
12357718be8SEnji Cooper 	/* now check that input and output have the same size */
12457718be8SEnji Cooper 	insize = filesize(TESTFILE);
12557718be8SEnji Cooper 	outsize = filesize(CHECKFILE);
12657718be8SEnji Cooper 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
12757718be8SEnji Cooper 	ATF_REQUIRE(insize == outsize);
12857718be8SEnji Cooper 
12957718be8SEnji Cooper 	/*
13057718be8SEnji Cooper 	 * try a "cat < testfile >> checkfile"
13157718be8SEnji Cooper 	 */
13257718be8SEnji Cooper 	make_testfile(TESTFILE);
13357718be8SEnji Cooper 	make_testfile(CHECKFILE);
13457718be8SEnji Cooper 
13557718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
13657718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
13757718be8SEnji Cooper 	    TESTFILE, O_RDONLY, 0);
13857718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
13957718be8SEnji Cooper 	    CHECKFILE, O_WRONLY|O_APPEND, 0);
14057718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
14157718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
14257718be8SEnji Cooper 
14357718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
14457718be8SEnji Cooper 
14557718be8SEnji Cooper 	/* ok, wait for the child to finish */
14657718be8SEnji Cooper 	waitpid(pid, &status, 0);
14757718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
14857718be8SEnji Cooper 
14957718be8SEnji Cooper 	/* now check that output is twice as long as input */
15057718be8SEnji Cooper 	insize = filesize(TESTFILE);
15157718be8SEnji Cooper 	outsize = filesize(CHECKFILE);
15257718be8SEnji Cooper 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
15357718be8SEnji Cooper 	ATF_REQUIRE(insize*2 == outsize);
15457718be8SEnji Cooper 
15557718be8SEnji Cooper 	/*
15657718be8SEnji Cooper 	 * try a "cat < testfile  > checkfile" with input and output swapped
15757718be8SEnji Cooper 	 */
15857718be8SEnji Cooper 	make_testfile(TESTFILE);
15957718be8SEnji Cooper 	empty_outfile(CHECKFILE);
16057718be8SEnji Cooper 
16157718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
16257718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
16357718be8SEnji Cooper 	    TESTFILE, O_RDONLY, 0);
16457718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
16557718be8SEnji Cooper 	    CHECKFILE, O_WRONLY, 0);
16657718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
16757718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
16857718be8SEnji Cooper 
16957718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
17057718be8SEnji Cooper 
17157718be8SEnji Cooper 	/* ok, wait for the child to finish */
17257718be8SEnji Cooper 	waitpid(pid, &status, 0);
17357718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_FAILURE);
17457718be8SEnji Cooper 
17557718be8SEnji Cooper 	/* now check that input and output are still the same size */
17657718be8SEnji Cooper 	insize = filesize(TESTFILE);
17757718be8SEnji Cooper 	outsize = filesize(CHECKFILE);
17857718be8SEnji Cooper 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
17957718be8SEnji Cooper 	ATF_REQUIRE(outsize == 0);
18057718be8SEnji Cooper }
18157718be8SEnji Cooper 
18257718be8SEnji Cooper ATF_TC(t_spawn_reopen);
18357718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_reopen,tc)18457718be8SEnji Cooper ATF_TC_HEAD(t_spawn_reopen, tc)
18557718be8SEnji Cooper {
18657718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
18757718be8SEnji Cooper 	    "an open filehandle can be replaced by a 'open' fileaction");
18857718be8SEnji Cooper 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
18957718be8SEnji Cooper }
19057718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_reopen,tc)19157718be8SEnji Cooper ATF_TC_BODY(t_spawn_reopen, tc)
19257718be8SEnji Cooper {
19357718be8SEnji Cooper 	int status, err;
19457718be8SEnji Cooper 	pid_t pid;
19557718be8SEnji Cooper 	char * const args[2] = { __UNCONST("cat"), NULL };
19657718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
19757718be8SEnji Cooper 
19857718be8SEnji Cooper 	/*
19957718be8SEnji Cooper 	 * make sure stdin is open in the parent
20057718be8SEnji Cooper 	 */
20157718be8SEnji Cooper 	freopen("/dev/zero", "r", stdin);
20257718be8SEnji Cooper 	/*
20357718be8SEnji Cooper 	 * now request an open for this fd again in the child
20457718be8SEnji Cooper 	 */
20557718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
20657718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, fileno(stdin),
20757718be8SEnji Cooper 	    "/dev/null", O_RDONLY, 0);
20857718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
20957718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
21057718be8SEnji Cooper 
21157718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
21257718be8SEnji Cooper 
21357718be8SEnji Cooper 	waitpid(pid, &status, 0);
21457718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
21557718be8SEnji Cooper }
21657718be8SEnji Cooper 
21757718be8SEnji Cooper ATF_TC(t_spawn_open_nonexistent);
21857718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_open_nonexistent,tc)21957718be8SEnji Cooper ATF_TC_HEAD(t_spawn_open_nonexistent, tc)
22057718be8SEnji Cooper {
22157718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
22257718be8SEnji Cooper 	    "posix_spawn fails when a file to open does not exist");
22357718be8SEnji Cooper 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
22457718be8SEnji Cooper }
22557718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_open_nonexistent,tc)22657718be8SEnji Cooper ATF_TC_BODY(t_spawn_open_nonexistent, tc)
22757718be8SEnji Cooper {
22857718be8SEnji Cooper 	int err, status;
22957718be8SEnji Cooper 	pid_t pid;
23057718be8SEnji Cooper 	char * const args[2] = { __UNCONST("cat"), NULL };
23157718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
23257718be8SEnji Cooper 
23357718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
23457718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
23557718be8SEnji Cooper 	    "./non/ex/ist/ent", O_RDONLY, 0);
23657718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
23757718be8SEnji Cooper 	if (err == 0) {
23857718be8SEnji Cooper 		/*
23957718be8SEnji Cooper 		 * The child has been created - it should fail and
24057718be8SEnji Cooper 		 * return exit code 127
24157718be8SEnji Cooper 		 */
24257718be8SEnji Cooper 		waitpid(pid, &status, 0);
24357718be8SEnji Cooper 		ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127);
24457718be8SEnji Cooper 	} else {
24557718be8SEnji Cooper 		/*
24657718be8SEnji Cooper 		 * The error has been noticed early enough, no child has
24757718be8SEnji Cooper 		 * been run
24857718be8SEnji Cooper 		 */
24957718be8SEnji Cooper 		ATF_REQUIRE(err == ENOENT);
25057718be8SEnji Cooper 	}
25157718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
25257718be8SEnji Cooper }
25357718be8SEnji Cooper 
254ff0ba872SEnji Cooper #ifdef __NetBSD__
25557718be8SEnji Cooper ATF_TC(t_spawn_open_nonexistent_diag);
25657718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_open_nonexistent_diag,tc)25757718be8SEnji Cooper ATF_TC_HEAD(t_spawn_open_nonexistent_diag, tc)
25857718be8SEnji Cooper {
25957718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
26057718be8SEnji Cooper 	    "posix_spawn fails when a file to open does not exist "
26157718be8SEnji Cooper 	    "and delivers proper diagnostic");
26257718be8SEnji Cooper 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
26357718be8SEnji Cooper }
26457718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_open_nonexistent_diag,tc)26557718be8SEnji Cooper ATF_TC_BODY(t_spawn_open_nonexistent_diag, tc)
26657718be8SEnji Cooper {
26757718be8SEnji Cooper 	int err;
26857718be8SEnji Cooper 	pid_t pid;
26957718be8SEnji Cooper 	char * const args[2] = { __UNCONST("cat"), NULL };
27057718be8SEnji Cooper 	posix_spawnattr_t attr;
27157718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
27257718be8SEnji Cooper 
27357718be8SEnji Cooper 	posix_spawnattr_init(&attr);
27457718be8SEnji Cooper 	/*
27557718be8SEnji Cooper 	 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
27657718be8SEnji Cooper 	 * will cause a "proper" return value from posix_spawn(2)
27757718be8SEnji Cooper 	 * instead of a (potential) success there and a 127 exit
27857718be8SEnji Cooper 	 * status from the child process (c.f. the non-diag variant
27957718be8SEnji Cooper 	 * of this test).
28057718be8SEnji Cooper 	 */
28157718be8SEnji Cooper 	posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR);
28257718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
28357718be8SEnji Cooper 	posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
28457718be8SEnji Cooper 	    "./non/ex/ist/ent", O_RDONLY, 0);
28557718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, &attr, args, NULL);
28657718be8SEnji Cooper 	ATF_REQUIRE(err == ENOENT);
28757718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
28857718be8SEnji Cooper 	posix_spawnattr_destroy(&attr);
28957718be8SEnji Cooper }
2901068aa42SEnji Cooper #endif
29157718be8SEnji Cooper 
29257718be8SEnji Cooper ATF_TC(t_spawn_fileactions);
29357718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_fileactions,tc)29457718be8SEnji Cooper ATF_TC_HEAD(t_spawn_fileactions, tc)
29557718be8SEnji Cooper {
29657718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
29757718be8SEnji Cooper 	    "Tests various complex fileactions");
29857718be8SEnji Cooper }
29957718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_fileactions,tc)30057718be8SEnji Cooper ATF_TC_BODY(t_spawn_fileactions, tc)
30157718be8SEnji Cooper {
30257718be8SEnji Cooper 	int fd1, fd2, fd3, status, err;
30357718be8SEnji Cooper 	pid_t pid;
3042aa3ef28SAlex Richardson 	char *args[3] = { __UNCONST("h_fileactions"), NULL, NULL };
3052aa3ef28SAlex Richardson 	int lowfd;
3062aa3ef28SAlex Richardson 	char lowfdstr[32];
30757718be8SEnji Cooper 	char helper[FILENAME_MAX];
30857718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
30957718be8SEnji Cooper 
31057718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
31157718be8SEnji Cooper 
3122aa3ef28SAlex Richardson 	/* Note: this assumes no gaps in the fd table */
3132aa3ef28SAlex Richardson 	lowfd = open("/", O_RDONLY);
3142aa3ef28SAlex Richardson 	ATF_REQUIRE(lowfd > 0);
3152aa3ef28SAlex Richardson 	ATF_REQUIRE_EQ(0, close(lowfd));
3162aa3ef28SAlex Richardson 	snprintf(lowfdstr, sizeof(lowfdstr), "%d", lowfd);
3172aa3ef28SAlex Richardson 	args[1] = lowfdstr;
31857718be8SEnji Cooper 
31957718be8SEnji Cooper 	fd1 = open("/dev/null", O_RDONLY);
3202aa3ef28SAlex Richardson 	ATF_REQUIRE_EQ(fd1, lowfd);
32157718be8SEnji Cooper 
32257718be8SEnji Cooper 	fd2 = open("/dev/null", O_WRONLY, O_CLOEXEC);
3232aa3ef28SAlex Richardson 	ATF_REQUIRE_EQ(fd2, lowfd + 1);
32457718be8SEnji Cooper 
32557718be8SEnji Cooper 	fd3 = open("/dev/null", O_WRONLY);
3262aa3ef28SAlex Richardson 	ATF_REQUIRE_EQ(fd3, lowfd + 2);
32757718be8SEnji Cooper 
32857718be8SEnji Cooper 	posix_spawn_file_actions_addclose(&fa, fd1);
3292aa3ef28SAlex Richardson 	posix_spawn_file_actions_addopen(&fa, lowfd + 3, "/dev/null", O_RDWR,
3302aa3ef28SAlex Richardson 	    0);
3312aa3ef28SAlex Richardson 	posix_spawn_file_actions_adddup2(&fa, 1, lowfd + 4);
33257718be8SEnji Cooper 
33357718be8SEnji Cooper 	snprintf(helper, sizeof helper, "%s/h_fileactions",
33457718be8SEnji Cooper 	    atf_tc_get_config_var(tc, "srcdir"));
33557718be8SEnji Cooper 	err = posix_spawn(&pid, helper, &fa, NULL, args, NULL);
33657718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
33757718be8SEnji Cooper 
33857718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
33957718be8SEnji Cooper 
34057718be8SEnji Cooper 	waitpid(pid, &status, 0);
34157718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
34257718be8SEnji Cooper }
34357718be8SEnji Cooper 
34457718be8SEnji Cooper ATF_TC(t_spawn_empty_fileactions);
34557718be8SEnji Cooper 
ATF_TC_HEAD(t_spawn_empty_fileactions,tc)34657718be8SEnji Cooper ATF_TC_HEAD(t_spawn_empty_fileactions, tc)
34757718be8SEnji Cooper {
34857718be8SEnji Cooper 	atf_tc_set_md_var(tc, "descr",
34957718be8SEnji Cooper 	    "posix_spawn with empty fileactions (PR kern/46038)");
35057718be8SEnji Cooper 	atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
35157718be8SEnji Cooper }
35257718be8SEnji Cooper 
ATF_TC_BODY(t_spawn_empty_fileactions,tc)35357718be8SEnji Cooper ATF_TC_BODY(t_spawn_empty_fileactions, tc)
35457718be8SEnji Cooper {
35557718be8SEnji Cooper 	int status, err;
35657718be8SEnji Cooper 	pid_t pid;
35757718be8SEnji Cooper 	char * const args[2] = { __UNCONST("cat"), NULL };
35857718be8SEnji Cooper 	posix_spawn_file_actions_t fa;
35957718be8SEnji Cooper 	size_t insize, outsize;
36057718be8SEnji Cooper 
36157718be8SEnji Cooper 	/*
36257718be8SEnji Cooper 	 * try a "cat < testfile > checkfile", but set up stdin/stdout
36357718be8SEnji Cooper 	 * already in the parent and pass empty file actions to the child.
36457718be8SEnji Cooper 	 */
36557718be8SEnji Cooper 	make_testfile(TESTFILE);
36657718be8SEnji Cooper 	unlink(CHECKFILE);
36757718be8SEnji Cooper 
36857718be8SEnji Cooper 	freopen(TESTFILE, "r", stdin);
36957718be8SEnji Cooper 	freopen(CHECKFILE, "w", stdout);
37057718be8SEnji Cooper 
37157718be8SEnji Cooper 	posix_spawn_file_actions_init(&fa);
37257718be8SEnji Cooper 	err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
37357718be8SEnji Cooper 	posix_spawn_file_actions_destroy(&fa);
37457718be8SEnji Cooper 
37557718be8SEnji Cooper 	ATF_REQUIRE(err == 0);
37657718be8SEnji Cooper 
37757718be8SEnji Cooper 	/* ok, wait for the child to finish */
37857718be8SEnji Cooper 	waitpid(pid, &status, 0);
37957718be8SEnji Cooper 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
38057718be8SEnji Cooper 
38157718be8SEnji Cooper 	/* now check that input and output have the same size */
38257718be8SEnji Cooper 	insize = filesize(TESTFILE);
38357718be8SEnji Cooper 	outsize = filesize(CHECKFILE);
38457718be8SEnji Cooper 	ATF_REQUIRE(insize == strlen(TESTCONTENT));
38557718be8SEnji Cooper 	ATF_REQUIRE(insize == outsize);
38657718be8SEnji Cooper }
38757718be8SEnji Cooper 
388*f16ec9c6SKonstantin Belousov static const char bin_pwd[] = "/bin/pwd";
389*f16ec9c6SKonstantin Belousov 
390*f16ec9c6SKonstantin Belousov static void
t_spawn_chdir_impl(bool chdir)391*f16ec9c6SKonstantin Belousov t_spawn_chdir_impl(bool chdir)
392*f16ec9c6SKonstantin Belousov {
393*f16ec9c6SKonstantin Belousov 	int status, err, tmpdir_fd;
394*f16ec9c6SKonstantin Belousov 	pid_t pid;
395*f16ec9c6SKonstantin Belousov 	char * const args[2] = { __UNCONST("pwd"), NULL };
396*f16ec9c6SKonstantin Belousov 	posix_spawn_file_actions_t fa;
397*f16ec9c6SKonstantin Belousov 	FILE *f;
398*f16ec9c6SKonstantin Belousov 	char read_pwd[128];
399*f16ec9c6SKonstantin Belousov 	size_t ss;
400*f16ec9c6SKonstantin Belousov 	static const char tmp_path[] = "/tmp";
401*f16ec9c6SKonstantin Belousov 
402*f16ec9c6SKonstantin Belousov 	unlink(TESTFILE);
403*f16ec9c6SKonstantin Belousov 
404*f16ec9c6SKonstantin Belousov 	posix_spawn_file_actions_init(&fa);
405*f16ec9c6SKonstantin Belousov 	posix_spawn_file_actions_addopen(&fa, fileno(stdout),
406*f16ec9c6SKonstantin Belousov 	    TESTFILE, O_WRONLY | O_CREAT, 0600);
407*f16ec9c6SKonstantin Belousov 	if (chdir) {
408*f16ec9c6SKonstantin Belousov 		ATF_REQUIRE(posix_spawn_file_actions_addchdir_np(&fa,
409*f16ec9c6SKonstantin Belousov 		    tmp_path) == 0);
410*f16ec9c6SKonstantin Belousov 	} else {
411*f16ec9c6SKonstantin Belousov 		tmpdir_fd = open(tmp_path, O_DIRECTORY | O_RDONLY);
412*f16ec9c6SKonstantin Belousov 		ATF_REQUIRE(tmpdir_fd > 0);
413*f16ec9c6SKonstantin Belousov 		ATF_REQUIRE(posix_spawn_file_actions_addfchdir_np(&fa,
414*f16ec9c6SKonstantin Belousov 		    tmpdir_fd) == 0);
415*f16ec9c6SKonstantin Belousov 	}
416*f16ec9c6SKonstantin Belousov 	err = posix_spawn(&pid, bin_pwd, &fa, NULL, args, NULL);
417*f16ec9c6SKonstantin Belousov 	posix_spawn_file_actions_destroy(&fa);
418*f16ec9c6SKonstantin Belousov 	if (!chdir)
419*f16ec9c6SKonstantin Belousov 		close(tmpdir_fd);
420*f16ec9c6SKonstantin Belousov 
421*f16ec9c6SKonstantin Belousov 	ATF_REQUIRE(err == 0);
422*f16ec9c6SKonstantin Belousov 	waitpid(pid, &status, 0);
423*f16ec9c6SKonstantin Belousov 	ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
424*f16ec9c6SKonstantin Belousov 
425*f16ec9c6SKonstantin Belousov 	f = fopen(TESTFILE, "r");
426*f16ec9c6SKonstantin Belousov 	ATF_REQUIRE(f != NULL);
427*f16ec9c6SKonstantin Belousov 	ss = fread(read_pwd, 1, sizeof(read_pwd), f);
428*f16ec9c6SKonstantin Belousov 	fclose(f);
429*f16ec9c6SKonstantin Belousov 	ATF_REQUIRE(ss == strlen(tmp_path) + 1);
430*f16ec9c6SKonstantin Belousov 	ATF_REQUIRE(strncmp(read_pwd, tmp_path, strlen(tmp_path)) == 0);
431*f16ec9c6SKonstantin Belousov }
432*f16ec9c6SKonstantin Belousov 
433*f16ec9c6SKonstantin Belousov ATF_TC(t_spawn_chdir);
434*f16ec9c6SKonstantin Belousov 
ATF_TC_HEAD(t_spawn_chdir,tc)435*f16ec9c6SKonstantin Belousov ATF_TC_HEAD(t_spawn_chdir, tc)
436*f16ec9c6SKonstantin Belousov {
437*f16ec9c6SKonstantin Belousov 	atf_tc_set_md_var(tc, "descr",
438*f16ec9c6SKonstantin Belousov 	    "posix_spawn changes directory for the spawned program");
439*f16ec9c6SKonstantin Belousov 	atf_tc_set_md_var(tc, "require.progs", bin_pwd);
440*f16ec9c6SKonstantin Belousov }
441*f16ec9c6SKonstantin Belousov 
ATF_TC_BODY(t_spawn_chdir,tc)442*f16ec9c6SKonstantin Belousov ATF_TC_BODY(t_spawn_chdir, tc)
443*f16ec9c6SKonstantin Belousov {
444*f16ec9c6SKonstantin Belousov 	t_spawn_chdir_impl(true);
445*f16ec9c6SKonstantin Belousov }
446*f16ec9c6SKonstantin Belousov 
447*f16ec9c6SKonstantin Belousov ATF_TC(t_spawn_fchdir);
448*f16ec9c6SKonstantin Belousov 
ATF_TC_HEAD(t_spawn_fchdir,tc)449*f16ec9c6SKonstantin Belousov ATF_TC_HEAD(t_spawn_fchdir, tc)
450*f16ec9c6SKonstantin Belousov {
451*f16ec9c6SKonstantin Belousov 	atf_tc_set_md_var(tc, "descr",
452*f16ec9c6SKonstantin Belousov 	    "posix_spawn changes directory for the spawned program");
453*f16ec9c6SKonstantin Belousov 	atf_tc_set_md_var(tc, "require.progs", bin_pwd);
454*f16ec9c6SKonstantin Belousov }
455*f16ec9c6SKonstantin Belousov 
ATF_TC_BODY(t_spawn_fchdir,tc)456*f16ec9c6SKonstantin Belousov ATF_TC_BODY(t_spawn_fchdir, tc)
457*f16ec9c6SKonstantin Belousov {
458*f16ec9c6SKonstantin Belousov 	t_spawn_chdir_impl(false);
459*f16ec9c6SKonstantin Belousov }
460*f16ec9c6SKonstantin Belousov 
ATF_TP_ADD_TCS(tp)46157718be8SEnji Cooper ATF_TP_ADD_TCS(tp)
46257718be8SEnji Cooper {
46357718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_fileactions);
46457718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent);
465ff0ba872SEnji Cooper #ifdef __NetBSD__
46657718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent_diag);
4671068aa42SEnji Cooper #endif
46857718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_reopen);
46957718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_openmode);
47057718be8SEnji Cooper 	ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions);
471*f16ec9c6SKonstantin Belousov 	ATF_TP_ADD_TC(tp, t_spawn_chdir);
472*f16ec9c6SKonstantin Belousov 	ATF_TP_ADD_TC(tp, t_spawn_fchdir);
47357718be8SEnji Cooper 
47457718be8SEnji Cooper 	return atf_no_error();
47557718be8SEnji Cooper }
476