1*c0746c1eSchristos /* $NetBSD: t_fileactions.c,v 1.7 2021/11/07 15:46:20 christos Exp $ */
20ce98f42Smartin
30ce98f42Smartin /*-
40ce98f42Smartin * Copyright (c) 2012 The NetBSD Foundation, Inc.
50ce98f42Smartin * All rights reserved.
60ce98f42Smartin *
70ce98f42Smartin * This code is derived from software contributed to The NetBSD Foundation
80ce98f42Smartin * by Charles Zhang <charles@NetBSD.org> and
90ce98f42Smartin * Martin Husemann <martin@NetBSD.org>.
100ce98f42Smartin *
110ce98f42Smartin * Redistribution and use in source and binary forms, with or without
120ce98f42Smartin * modification, are permitted provided that the following conditions
130ce98f42Smartin * are met:
140ce98f42Smartin * 1. Redistributions of source code must retain the above copyright
150ce98f42Smartin * notice, this list of conditions and the following disclaimer.
160ce98f42Smartin * 2. Redistributions in binary form must reproduce the above copyright
170ce98f42Smartin * notice, this list of conditions and the following disclaimer in the
180ce98f42Smartin * documentation and/or other materials provided with the distribution.
190ce98f42Smartin *
200ce98f42Smartin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
210ce98f42Smartin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
220ce98f42Smartin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
230ce98f42Smartin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
240ce98f42Smartin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
250ce98f42Smartin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
260ce98f42Smartin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
270ce98f42Smartin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
280ce98f42Smartin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
290ce98f42Smartin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
300ce98f42Smartin * POSSIBILITY OF SUCH DAMAGE.
310ce98f42Smartin */
32*c0746c1eSchristos #include <sys/cdefs.h>
33*c0746c1eSchristos __RCSID("$NetBSD: t_fileactions.c,v 1.7 2021/11/07 15:46:20 christos Exp $");
340ce98f42Smartin
350ce98f42Smartin
360ce98f42Smartin #include <atf-c.h>
3725f1087aSchristos
3825f1087aSchristos #include <sys/wait.h>
3925f1087aSchristos #include <sys/stat.h>
4025f1087aSchristos
410ce98f42Smartin #include <stdio.h>
420ce98f42Smartin #include <stdlib.h>
430ce98f42Smartin #include <string.h>
441e1d6ed3Smartin #include <errno.h>
450ce98f42Smartin #include <fcntl.h>
460ce98f42Smartin #include <spawn.h>
470ce98f42Smartin #include <unistd.h>
480ce98f42Smartin
49*c0746c1eSchristos #include "fa_spawn_utils.h"
50*c0746c1eSchristos
511e1d6ed3Smartin
521e1d6ed3Smartin ATF_TC(t_spawn_openmode);
531e1d6ed3Smartin
ATF_TC_HEAD(t_spawn_openmode,tc)541e1d6ed3Smartin ATF_TC_HEAD(t_spawn_openmode, tc)
551e1d6ed3Smartin {
561e1d6ed3Smartin atf_tc_set_md_var(tc, "descr",
571e1d6ed3Smartin "Test the proper handling of 'mode' for 'open' fileactions");
581e1d6ed3Smartin atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
591e1d6ed3Smartin }
601e1d6ed3Smartin
611e1d6ed3Smartin #define TESTFILE "./the_input_data"
621e1d6ed3Smartin #define CHECKFILE "./the_output_data"
631e1d6ed3Smartin #define TESTCONTENT "marry has a little lamb"
641e1d6ed3Smartin
651e1d6ed3Smartin static void
make_testfile(const char * restrict file)661e1d6ed3Smartin make_testfile(const char *restrict file)
671e1d6ed3Smartin {
681e1d6ed3Smartin FILE *f;
691e1d6ed3Smartin size_t written;
701e1d6ed3Smartin
711e1d6ed3Smartin f = fopen(file, "w");
721e1d6ed3Smartin ATF_REQUIRE(f != NULL);
731e1d6ed3Smartin written = fwrite(TESTCONTENT, 1, strlen(TESTCONTENT), f);
741e1d6ed3Smartin fclose(f);
751e1d6ed3Smartin ATF_REQUIRE(written == strlen(TESTCONTENT));
761e1d6ed3Smartin }
771e1d6ed3Smartin
ATF_TC_BODY(t_spawn_openmode,tc)781e1d6ed3Smartin ATF_TC_BODY(t_spawn_openmode, tc)
791e1d6ed3Smartin {
801e1d6ed3Smartin int status, err;
811e1d6ed3Smartin pid_t pid;
821e1d6ed3Smartin size_t insize, outsize;
831e1d6ed3Smartin char * const args[2] = { __UNCONST("cat"), NULL };
841e1d6ed3Smartin posix_spawn_file_actions_t fa;
851e1d6ed3Smartin
861e1d6ed3Smartin /*
871e1d6ed3Smartin * try a "cat < testfile > checkfile"
881e1d6ed3Smartin */
891e1d6ed3Smartin make_testfile(TESTFILE);
901e1d6ed3Smartin unlink(CHECKFILE);
911e1d6ed3Smartin
921e1d6ed3Smartin posix_spawn_file_actions_init(&fa);
931e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdin),
941e1d6ed3Smartin TESTFILE, O_RDONLY, 0);
951e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdout),
961e1d6ed3Smartin CHECKFILE, O_WRONLY|O_CREAT, 0600);
971e1d6ed3Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
981e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
991e1d6ed3Smartin
1001e1d6ed3Smartin ATF_REQUIRE(err == 0);
1011e1d6ed3Smartin
1021e1d6ed3Smartin /* ok, wait for the child to finish */
1031e1d6ed3Smartin waitpid(pid, &status, 0);
1041e1d6ed3Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
1051e1d6ed3Smartin
1061e1d6ed3Smartin /* now check that input and output have the same size */
1071e1d6ed3Smartin insize = filesize(TESTFILE);
1081e1d6ed3Smartin outsize = filesize(CHECKFILE);
1091e1d6ed3Smartin ATF_REQUIRE(insize == strlen(TESTCONTENT));
1101e1d6ed3Smartin ATF_REQUIRE(insize == outsize);
1111e1d6ed3Smartin
1121e1d6ed3Smartin /*
1131e1d6ed3Smartin * try a "cat < testfile >> checkfile"
1141e1d6ed3Smartin */
1151e1d6ed3Smartin make_testfile(TESTFILE);
1161e1d6ed3Smartin make_testfile(CHECKFILE);
1171e1d6ed3Smartin
1181e1d6ed3Smartin posix_spawn_file_actions_init(&fa);
1191e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdin),
1201e1d6ed3Smartin TESTFILE, O_RDONLY, 0);
1211e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdout),
1221e1d6ed3Smartin CHECKFILE, O_WRONLY|O_APPEND, 0);
1231e1d6ed3Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
1241e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
1251e1d6ed3Smartin
1261e1d6ed3Smartin ATF_REQUIRE(err == 0);
1271e1d6ed3Smartin
1281e1d6ed3Smartin /* ok, wait for the child to finish */
1291e1d6ed3Smartin waitpid(pid, &status, 0);
1301e1d6ed3Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
1311e1d6ed3Smartin
1321e1d6ed3Smartin /* now check that output is twice as long as input */
1331e1d6ed3Smartin insize = filesize(TESTFILE);
1341e1d6ed3Smartin outsize = filesize(CHECKFILE);
1351e1d6ed3Smartin ATF_REQUIRE(insize == strlen(TESTCONTENT));
1361e1d6ed3Smartin ATF_REQUIRE(insize*2 == outsize);
1371e1d6ed3Smartin
1381e1d6ed3Smartin /*
1391e1d6ed3Smartin * try a "cat < testfile > checkfile" with input and output swapped
1401e1d6ed3Smartin */
1411e1d6ed3Smartin make_testfile(TESTFILE);
1421e1d6ed3Smartin empty_outfile(CHECKFILE);
1431e1d6ed3Smartin
1441e1d6ed3Smartin posix_spawn_file_actions_init(&fa);
1451e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdout),
1461e1d6ed3Smartin TESTFILE, O_RDONLY, 0);
1471e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdin),
1481e1d6ed3Smartin CHECKFILE, O_WRONLY, 0);
1491e1d6ed3Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
1501e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
1511e1d6ed3Smartin
1521e1d6ed3Smartin ATF_REQUIRE(err == 0);
1531e1d6ed3Smartin
1541e1d6ed3Smartin /* ok, wait for the child to finish */
1551e1d6ed3Smartin waitpid(pid, &status, 0);
1561e1d6ed3Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_FAILURE);
1571e1d6ed3Smartin
1581e1d6ed3Smartin /* now check that input and output are still the same size */
1591e1d6ed3Smartin insize = filesize(TESTFILE);
1601e1d6ed3Smartin outsize = filesize(CHECKFILE);
1611e1d6ed3Smartin ATF_REQUIRE(insize == strlen(TESTCONTENT));
1621e1d6ed3Smartin ATF_REQUIRE(outsize == 0);
1631e1d6ed3Smartin }
1641e1d6ed3Smartin
1651e1d6ed3Smartin ATF_TC(t_spawn_reopen);
1661e1d6ed3Smartin
ATF_TC_HEAD(t_spawn_reopen,tc)1671e1d6ed3Smartin ATF_TC_HEAD(t_spawn_reopen, tc)
1681e1d6ed3Smartin {
1691e1d6ed3Smartin atf_tc_set_md_var(tc, "descr",
1701e1d6ed3Smartin "an open filehandle can be replaced by a 'open' fileaction");
1711e1d6ed3Smartin atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
1721e1d6ed3Smartin }
1731e1d6ed3Smartin
ATF_TC_BODY(t_spawn_reopen,tc)1741e1d6ed3Smartin ATF_TC_BODY(t_spawn_reopen, tc)
1751e1d6ed3Smartin {
1761e1d6ed3Smartin int status, err;
1771e1d6ed3Smartin pid_t pid;
1781e1d6ed3Smartin char * const args[2] = { __UNCONST("cat"), NULL };
1791e1d6ed3Smartin posix_spawn_file_actions_t fa;
1801e1d6ed3Smartin
1811e1d6ed3Smartin /*
1821e1d6ed3Smartin * make sure stdin is open in the parent
1831e1d6ed3Smartin */
1841e1d6ed3Smartin freopen("/dev/zero", "r", stdin);
1851e1d6ed3Smartin /*
1861e1d6ed3Smartin * now request an open for this fd again in the child
1871e1d6ed3Smartin */
1881e1d6ed3Smartin posix_spawn_file_actions_init(&fa);
1891e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, fileno(stdin),
1901e1d6ed3Smartin "/dev/null", O_RDONLY, 0);
1911e1d6ed3Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
1921e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
1931e1d6ed3Smartin
1941e1d6ed3Smartin ATF_REQUIRE(err == 0);
1951e1d6ed3Smartin
1961e1d6ed3Smartin waitpid(pid, &status, 0);
1971e1d6ed3Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
1981e1d6ed3Smartin }
1991e1d6ed3Smartin
2001e1d6ed3Smartin ATF_TC(t_spawn_open_nonexistent);
2011e1d6ed3Smartin
ATF_TC_HEAD(t_spawn_open_nonexistent,tc)2021e1d6ed3Smartin ATF_TC_HEAD(t_spawn_open_nonexistent, tc)
2031e1d6ed3Smartin {
2041e1d6ed3Smartin atf_tc_set_md_var(tc, "descr",
2051e1d6ed3Smartin "posix_spawn fails when a file to open does not exist");
2061e1d6ed3Smartin atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
2071e1d6ed3Smartin }
2081e1d6ed3Smartin
ATF_TC_BODY(t_spawn_open_nonexistent,tc)2091e1d6ed3Smartin ATF_TC_BODY(t_spawn_open_nonexistent, tc)
2101e1d6ed3Smartin {
21194b761b6Smartin int err, status;
2121e1d6ed3Smartin pid_t pid;
2131e1d6ed3Smartin char * const args[2] = { __UNCONST("cat"), NULL };
2141e1d6ed3Smartin posix_spawn_file_actions_t fa;
2151e1d6ed3Smartin
2161e1d6ed3Smartin posix_spawn_file_actions_init(&fa);
2171e1d6ed3Smartin posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
2181e1d6ed3Smartin "./non/ex/ist/ent", O_RDONLY, 0);
2191e1d6ed3Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
22094b761b6Smartin if (err == 0) {
22194b761b6Smartin /*
22294b761b6Smartin * The child has been created - it should fail and
22394b761b6Smartin * return exit code 127
22494b761b6Smartin */
22594b761b6Smartin waitpid(pid, &status, 0);
2264e00857fSmartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127);
22794b761b6Smartin } else {
22894b761b6Smartin /*
22994b761b6Smartin * The error has been noticed early enough, no child has
23094b761b6Smartin * been run
23194b761b6Smartin */
23294b761b6Smartin ATF_REQUIRE(err == ENOENT);
23394b761b6Smartin }
23494b761b6Smartin posix_spawn_file_actions_destroy(&fa);
23594b761b6Smartin }
23694b761b6Smartin
23794b761b6Smartin ATF_TC(t_spawn_open_nonexistent_diag);
23894b761b6Smartin
ATF_TC_HEAD(t_spawn_open_nonexistent_diag,tc)23994b761b6Smartin ATF_TC_HEAD(t_spawn_open_nonexistent_diag, tc)
24094b761b6Smartin {
24194b761b6Smartin atf_tc_set_md_var(tc, "descr",
24294b761b6Smartin "posix_spawn fails when a file to open does not exist "
24394b761b6Smartin "and delivers proper diagnostic");
24494b761b6Smartin atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
24594b761b6Smartin }
24694b761b6Smartin
ATF_TC_BODY(t_spawn_open_nonexistent_diag,tc)24794b761b6Smartin ATF_TC_BODY(t_spawn_open_nonexistent_diag, tc)
24894b761b6Smartin {
24994b761b6Smartin int err;
25094b761b6Smartin pid_t pid;
25194b761b6Smartin char * const args[2] = { __UNCONST("cat"), NULL };
25294b761b6Smartin posix_spawnattr_t attr;
25394b761b6Smartin posix_spawn_file_actions_t fa;
25494b761b6Smartin
25594b761b6Smartin posix_spawnattr_init(&attr);
25694b761b6Smartin /*
25794b761b6Smartin * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
25894b761b6Smartin * will cause a "proper" return value from posix_spawn(2)
25994b761b6Smartin * instead of a (potential) success there and a 127 exit
26094b761b6Smartin * status from the child process (c.f. the non-diag variant
26194b761b6Smartin * of this test).
26294b761b6Smartin */
26394b761b6Smartin posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR);
26494b761b6Smartin posix_spawn_file_actions_init(&fa);
26594b761b6Smartin posix_spawn_file_actions_addopen(&fa, STDIN_FILENO,
26694b761b6Smartin "./non/ex/ist/ent", O_RDONLY, 0);
26794b761b6Smartin err = posix_spawn(&pid, "/bin/cat", &fa, &attr, args, NULL);
2681e1d6ed3Smartin ATF_REQUIRE(err == ENOENT);
2691e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
27094b761b6Smartin posix_spawnattr_destroy(&attr);
2711e1d6ed3Smartin }
2721e1d6ed3Smartin
2730ce98f42Smartin ATF_TC(t_spawn_fileactions);
2740ce98f42Smartin
ATF_TC_HEAD(t_spawn_fileactions,tc)2750ce98f42Smartin ATF_TC_HEAD(t_spawn_fileactions, tc)
2760ce98f42Smartin {
2770ce98f42Smartin atf_tc_set_md_var(tc, "descr",
2781e1d6ed3Smartin "Tests various complex fileactions");
2790ce98f42Smartin }
2800ce98f42Smartin
ATF_TC_BODY(t_spawn_fileactions,tc)2810ce98f42Smartin ATF_TC_BODY(t_spawn_fileactions, tc)
2820ce98f42Smartin {
2830ce98f42Smartin int fd1, fd2, fd3, status, err;
2840ce98f42Smartin pid_t pid;
2850ce98f42Smartin char * const args[2] = { __UNCONST("h_fileactions"), NULL };
2860ce98f42Smartin char helper[FILENAME_MAX];
2870ce98f42Smartin posix_spawn_file_actions_t fa;
2880ce98f42Smartin
2890ce98f42Smartin posix_spawn_file_actions_init(&fa);
2900ce98f42Smartin
2910ce98f42Smartin closefrom(fileno(stderr)+1);
2920ce98f42Smartin
2930ce98f42Smartin fd1 = open("/dev/null", O_RDONLY);
2940ce98f42Smartin ATF_REQUIRE(fd1 == 3);
2950ce98f42Smartin
2960ce98f42Smartin fd2 = open("/dev/null", O_WRONLY, O_CLOEXEC);
2970ce98f42Smartin ATF_REQUIRE(fd2 == 4);
2980ce98f42Smartin
2990ce98f42Smartin fd3 = open("/dev/null", O_WRONLY);
3000ce98f42Smartin ATF_REQUIRE(fd3 == 5);
3010ce98f42Smartin
3020ce98f42Smartin posix_spawn_file_actions_addclose(&fa, fd1);
3030ce98f42Smartin posix_spawn_file_actions_addopen(&fa, 6, "/dev/null", O_RDWR, 0);
3040ce98f42Smartin posix_spawn_file_actions_adddup2(&fa, 1, 7);
3050ce98f42Smartin
3060ce98f42Smartin snprintf(helper, sizeof helper, "%s/h_fileactions",
3070ce98f42Smartin atf_tc_get_config_var(tc, "srcdir"));
3080ce98f42Smartin err = posix_spawn(&pid, helper, &fa, NULL, args, NULL);
3091e1d6ed3Smartin posix_spawn_file_actions_destroy(&fa);
3101e1d6ed3Smartin
3110ce98f42Smartin ATF_REQUIRE(err == 0);
3120ce98f42Smartin
3130ce98f42Smartin waitpid(pid, &status, 0);
3140ce98f42Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
3150ce98f42Smartin }
3160ce98f42Smartin
31745d535a7Smartin ATF_TC(t_spawn_empty_fileactions);
31845d535a7Smartin
ATF_TC_HEAD(t_spawn_empty_fileactions,tc)31945d535a7Smartin ATF_TC_HEAD(t_spawn_empty_fileactions, tc)
32045d535a7Smartin {
32145d535a7Smartin atf_tc_set_md_var(tc, "descr",
32245d535a7Smartin "posix_spawn with empty fileactions (PR kern/46038)");
32345d535a7Smartin atf_tc_set_md_var(tc, "require.progs", "/bin/cat");
32445d535a7Smartin }
32545d535a7Smartin
ATF_TC_BODY(t_spawn_empty_fileactions,tc)32645d535a7Smartin ATF_TC_BODY(t_spawn_empty_fileactions, tc)
32745d535a7Smartin {
32845d535a7Smartin int status, err;
32945d535a7Smartin pid_t pid;
33045d535a7Smartin char * const args[2] = { __UNCONST("cat"), NULL };
33145d535a7Smartin posix_spawn_file_actions_t fa;
33245d535a7Smartin size_t insize, outsize;
33345d535a7Smartin
33445d535a7Smartin /*
33545d535a7Smartin * try a "cat < testfile > checkfile", but set up stdin/stdout
33645d535a7Smartin * already in the parent and pass empty file actions to the child.
33745d535a7Smartin */
33845d535a7Smartin make_testfile(TESTFILE);
33945d535a7Smartin unlink(CHECKFILE);
34045d535a7Smartin
34145d535a7Smartin freopen(TESTFILE, "r", stdin);
34245d535a7Smartin freopen(CHECKFILE, "w", stdout);
34345d535a7Smartin
34445d535a7Smartin posix_spawn_file_actions_init(&fa);
34545d535a7Smartin err = posix_spawn(&pid, "/bin/cat", &fa, NULL, args, NULL);
34645d535a7Smartin posix_spawn_file_actions_destroy(&fa);
34745d535a7Smartin
34845d535a7Smartin ATF_REQUIRE(err == 0);
34945d535a7Smartin
35045d535a7Smartin /* ok, wait for the child to finish */
35145d535a7Smartin waitpid(pid, &status, 0);
35245d535a7Smartin ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
35345d535a7Smartin
35445d535a7Smartin /* now check that input and output have the same size */
35545d535a7Smartin insize = filesize(TESTFILE);
35645d535a7Smartin outsize = filesize(CHECKFILE);
35745d535a7Smartin ATF_REQUIRE(insize == strlen(TESTCONTENT));
35845d535a7Smartin ATF_REQUIRE(insize == outsize);
35945d535a7Smartin }
36045d535a7Smartin
ATF_TP_ADD_TCS(tp)3610ce98f42Smartin ATF_TP_ADD_TCS(tp)
3620ce98f42Smartin {
3630ce98f42Smartin ATF_TP_ADD_TC(tp, t_spawn_fileactions);
3641e1d6ed3Smartin ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent);
36594b761b6Smartin ATF_TP_ADD_TC(tp, t_spawn_open_nonexistent_diag);
3661e1d6ed3Smartin ATF_TP_ADD_TC(tp, t_spawn_reopen);
3671e1d6ed3Smartin ATF_TP_ADD_TC(tp, t_spawn_openmode);
36845d535a7Smartin ATF_TP_ADD_TC(tp, t_spawn_empty_fileactions);
3690ce98f42Smartin
3700ce98f42Smartin return atf_no_error();
3710ce98f42Smartin }
372