Home | History | Annotate | Line # | Download | only in posix_spawn
      1  1.12  riastrad /* $NetBSD: t_spawn.c,v 1.12 2025/03/16 15:35:36 riastradh Exp $ */
      2   1.1    martin 
      3   1.1    martin /*-
      4   1.6  christos  * Copyright (c) 2012, 2021 The NetBSD Foundation, Inc.
      5   1.1    martin  * All rights reserved.
      6   1.1    martin  *
      7   1.1    martin  * This code is derived from software contributed to The NetBSD Foundation
      8   1.1    martin  * by Charles Zhang <charles (at) NetBSD.org> and
      9   1.1    martin  * Martin Husemann <martin (at) NetBSD.org>.
     10   1.1    martin  *
     11   1.1    martin  * Redistribution and use in source and binary forms, with or without
     12   1.1    martin  * modification, are permitted provided that the following conditions
     13   1.1    martin  * are met:
     14   1.1    martin  * 1. Redistributions of source code must retain the above copyright
     15   1.1    martin  *    notice, this list of conditions and the following disclaimer.
     16   1.1    martin  * 2. Redistributions in binary form must reproduce the above copyright
     17   1.1    martin  *    notice, this list of conditions and the following disclaimer in the
     18   1.1    martin  *    documentation and/or other materials provided with the distribution.
     19   1.1    martin  *
     20   1.1    martin  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21   1.1    martin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22   1.1    martin  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23   1.1    martin  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24   1.1    martin  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25   1.1    martin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26   1.1    martin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27   1.1    martin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28   1.1    martin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29   1.1    martin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30   1.1    martin  * POSSIBILITY OF SUCH DAMAGE.
     31   1.1    martin  */
     32   1.9  riastrad 
     33   1.4  christos #include <sys/cdefs.h>
     34  1.12  riastrad __RCSID("$NetBSD: t_spawn.c,v 1.12 2025/03/16 15:35:36 riastradh Exp $");
     35   1.4  christos 
     36   1.4  christos #include <sys/fcntl.h>
     37   1.9  riastrad #include <sys/stat.h>
     38   1.4  christos #include <sys/types.h>
     39   1.4  christos #include <sys/wait.h>
     40   1.1    martin 
     41  1.12  riastrad #include <atf-c.h>
     42   1.9  riastrad #include <errno.h>
     43   1.9  riastrad #include <fcntl.h>
     44   1.9  riastrad #include <limits.h>
     45   1.9  riastrad #include <signal.h>
     46   1.1    martin #include <spawn.h>
     47   1.1    martin #include <stdio.h>
     48   1.1    martin #include <stdlib.h>
     49   1.9  riastrad #include <string.h>
     50   1.9  riastrad #include <time.h>
     51   1.4  christos #include <unistd.h>
     52   1.4  christos 
     53   1.4  christos #include "fa_spawn_utils.h"
     54   1.9  riastrad #include "h_macros.h"
     55   1.4  christos 
     56  1.12  riastrad static void check_success(const char *, const char *);
     57   1.1    martin 
     58   1.1    martin ATF_TC(t_spawn_ls);
     59   1.1    martin ATF_TC_HEAD(t_spawn_ls, tc)
     60   1.1    martin {
     61   1.1    martin 	atf_tc_set_md_var(tc, "descr",
     62   1.1    martin 	    "Tests a simple posix_spawn executing /bin/ls");
     63   1.1    martin }
     64   1.1    martin ATF_TC_BODY(t_spawn_ls, tc)
     65   1.1    martin {
     66   1.1    martin 	char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL };
     67   1.1    martin 
     68  1.12  riastrad 	RZ(posix_spawn(NULL, "/bin/ls", NULL, NULL, args, NULL));
     69   1.1    martin }
     70   1.1    martin 
     71   1.1    martin ATF_TC(t_spawnp_ls);
     72   1.1    martin ATF_TC_HEAD(t_spawnp_ls, tc)
     73   1.1    martin {
     74   1.1    martin 	atf_tc_set_md_var(tc, "descr",
     75   1.1    martin 	    "Tests a simple posix_spawnp executing ls via $PATH");
     76   1.1    martin }
     77   1.1    martin ATF_TC_BODY(t_spawnp_ls, tc)
     78   1.1    martin {
     79   1.1    martin 	char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL };
     80   1.1    martin 
     81  1.12  riastrad 	RZ(posix_spawnp(NULL, "ls", NULL, NULL, args, NULL));
     82   1.1    martin }
     83   1.1    martin 
     84   1.6  christos static void
     85   1.6  christos spawn_error(const atf_tc_t *tc, const char *name, int error)
     86   1.6  christos {
     87  1.12  riastrad 	char buf[PATH_MAX];
     88   1.6  christos 	char * const args[] = { __UNCONST(name), NULL };
     89   1.6  christos 	int err;
     90   1.6  christos 
     91  1.12  riastrad 	snprintf(buf, sizeof(buf), "%s/%s",
     92   1.6  christos 	    atf_tc_get_config_var(tc, "srcdir"), name);
     93   1.6  christos 	err = posix_spawn(NULL, buf, NULL, NULL, args, NULL);
     94   1.6  christos 	ATF_REQUIRE_MSG(err == error, "expected error %d, "
     95   1.6  christos 	    "got %d when spawning %s", error, err, buf);
     96   1.6  christos }
     97   1.6  christos 
     98   1.1    martin ATF_TC(t_spawn_zero);
     99   1.1    martin ATF_TC_HEAD(t_spawn_zero, tc)
    100   1.1    martin {
    101   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    102   1.1    martin 	    "posix_spawn an invalid binary");
    103   1.1    martin }
    104   1.1    martin ATF_TC_BODY(t_spawn_zero, tc)
    105   1.1    martin {
    106   1.6  christos 	spawn_error(tc, "h_zero", ENOEXEC);
    107   1.1    martin }
    108   1.1    martin 
    109   1.1    martin ATF_TC(t_spawn_missing);
    110   1.1    martin ATF_TC_HEAD(t_spawn_missing, tc)
    111   1.1    martin {
    112   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    113  1.12  riastrad 	    "posix_spawn a nonexistent binary");
    114   1.1    martin }
    115   1.1    martin ATF_TC_BODY(t_spawn_missing, tc)
    116   1.1    martin {
    117   1.6  christos 	spawn_error(tc, "h_nonexist", ENOENT);
    118   1.1    martin }
    119   1.1    martin 
    120   1.1    martin ATF_TC(t_spawn_nonexec);
    121   1.1    martin ATF_TC_HEAD(t_spawn_nonexec, tc)
    122   1.1    martin {
    123   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    124   1.4  christos 	    "posix_spawn a script with non existing interpreter");
    125   1.1    martin }
    126   1.1    martin ATF_TC_BODY(t_spawn_nonexec, tc)
    127   1.1    martin {
    128   1.6  christos 	spawn_error(tc, "h_nonexec", ENOENT);
    129   1.1    martin }
    130   1.1    martin 
    131   1.1    martin ATF_TC(t_spawn_child);
    132   1.1    martin ATF_TC_HEAD(t_spawn_child, tc)
    133   1.1    martin {
    134   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    135   1.2       snj 	    "posix_spawn a child and get its return code");
    136   1.1    martin }
    137   1.1    martin 
    138   1.1    martin ATF_TC_BODY(t_spawn_child, tc)
    139   1.1    martin {
    140  1.12  riastrad 	char buf[PATH_MAX];
    141   1.6  christos 	char rv[2] = { '0', '\0' };
    142   1.6  christos 	char * const args0[] = { __UNCONST("h_spawn"), rv, NULL };
    143   1.6  christos 	int rets[] = { 0, 1, 7 };
    144  1.12  riastrad 	int status;
    145   1.1    martin 	pid_t pid;
    146   1.1    martin 
    147  1.12  riastrad 	snprintf(buf, sizeof(buf), "%s/h_spawn",
    148   1.1    martin 	    atf_tc_get_config_var(tc, "srcdir"));
    149   1.1    martin 
    150   1.6  christos 	for (size_t i = 0; i < __arraycount(rets); i++) {
    151   1.6  christos 		rv[0] = rets[i] + '0';
    152  1.12  riastrad 		RZ(posix_spawn(&pid, buf, NULL, NULL, args0, NULL));
    153  1.12  riastrad 		ATF_REQUIRE_MSG(pid > 0, "pid=%lld", (long long)pid);
    154  1.12  riastrad 		RL(waitpid(pid, &status, 0));
    155  1.12  riastrad 		ATF_REQUIRE_MSG((WIFEXITED(status) &&
    156  1.12  riastrad 			WEXITSTATUS(status) == rets[i]),
    157  1.12  riastrad 		    "status=0x%x", status);
    158   1.6  christos 	}
    159   1.1    martin }
    160   1.1    martin 
    161   1.4  christos #define FILENAME "output"
    162   1.4  christos 
    163  1.12  riastrad enum chdirop {
    164  1.12  riastrad 	OP_CHDIR = 1,
    165  1.12  riastrad 	OP_FCHDIR = 2,
    166  1.12  riastrad };
    167   1.6  christos 
    168   1.4  christos static void
    169  1.12  riastrad check_success(const char *dir, const char *file)
    170   1.4  christos {
    171  1.12  riastrad 	ssize_t bytes_read;
    172   1.5  christos 	int fd;
    173  1.12  riastrad 	size_t sizeof_file = (size_t)filesize(file);
    174  1.12  riastrad 	size_t sizeof_str;
    175   1.4  christos 	char *contents;
    176   1.4  christos 
    177  1.12  riastrad 	REQUIRE_LIBC(contents = malloc(sizeof_file), NULL);
    178   1.4  christos 
    179  1.12  riastrad 	RL(fd = open(file, O_RDONLY));
    180   1.4  christos 
    181   1.4  christos 	/*
    182   1.4  christos 	 * file contains form feed i.e ASCII - 10 at the end.
    183  1.12  riastrad 	 * Therefore sizeof_file - 1
    184   1.4  christos 	 */
    185  1.12  riastrad 	sizeof_str = strlen(dir);
    186  1.12  riastrad 	ATF_CHECK_MSG(sizeof_str == sizeof_file - 1, "%zu (%s) != %zu (%s)",
    187  1.12  riastrad 	    sizeof_str, dir, sizeof_file - 1, file);
    188   1.4  christos 
    189  1.12  riastrad 	bytes_read = read(fd, contents, sizeof_file - 1);
    190  1.12  riastrad 	contents[sizeof_file - 1] = '\0';
    191   1.4  christos 	ATF_REQUIRE_MSG(strcmp(dir, contents) == 0,
    192  1.12  riastrad 	    "[%s] != [%s] Directories don't match", dir, contents);
    193   1.4  christos 
    194  1.12  riastrad 	RL(close(fd));
    195   1.4  christos 
    196  1.12  riastrad 	RL(unlink(file));
    197   1.4  christos 	free(contents);
    198   1.4  christos 
    199   1.4  christos 	/* XXX not really required */
    200  1.12  riastrad 	ATF_REQUIRE_MSG((size_t)bytes_read == sizeof_str,
    201  1.12  riastrad 	    "bytes_read=%zu sizeof_str=%zu", bytes_read, sizeof_str);
    202   1.4  christos }
    203   1.4  christos 
    204   1.6  christos static void
    205  1.12  riastrad spawn_chdir(const char *dirpath, const char *filepath, enum chdirop operation,
    206   1.6  christos     int expected_error)
    207   1.4  christos {
    208  1.12  riastrad 	int error, fd = -1, status;
    209   1.4  christos 	char * const args[2] = { __UNCONST("pwd"), NULL };
    210   1.4  christos 	pid_t pid;
    211   1.6  christos 	posix_spawnattr_t attr, *attr_p;
    212   1.4  christos 	posix_spawn_file_actions_t fa;
    213   1.4  christos 
    214   1.6  christos 	if (filepath)
    215   1.6  christos 		empty_outfile(filepath);
    216   1.4  christos 
    217  1.12  riastrad 	RZ(posix_spawn_file_actions_init(&fa));
    218   1.4  christos 
    219  1.12  riastrad 	switch (operation) {
    220  1.12  riastrad 	case OP_CHDIR:
    221  1.12  riastrad 		RZ(posix_spawn_file_actions_addchdir(&fa, dirpath));
    222   1.6  christos 		break;
    223   1.6  christos 
    224  1.12  riastrad 	case OP_FCHDIR:
    225  1.12  riastrad 		RL(fd = open(dirpath, O_RDONLY));
    226  1.12  riastrad 		RZ(posix_spawn_file_actions_addfchdir(&fa, fd));
    227   1.6  christos 		break;
    228   1.6  christos 	}
    229   1.4  christos 
    230   1.6  christos 	/*
    231   1.6  christos 	 * if POSIX_SPAWN_RETURNERROR is expected, then no need to open the
    232   1.6  christos 	 * file
    233   1.6  christos 	 */
    234   1.6  christos 	if (expected_error == 0) {
    235  1.12  riastrad 		RZ(posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO,
    236  1.12  riastrad 		    FILENAME, O_WRONLY, 0));
    237   1.6  christos 		attr_p = NULL;
    238   1.6  christos 	} else {
    239  1.12  riastrad 		RZ(posix_spawnattr_init(&attr));
    240   1.6  christos 
    241   1.6  christos 		/*
    242   1.6  christos 		 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
    243   1.6  christos 		 * will cause a "proper" return value from posix_spawn(2)
    244   1.6  christos 		 * instead of a (potential) success there and a 127 exit
    245   1.6  christos 		 * status from the child process (c.f. the non-diag variant
    246   1.6  christos 		 * of this test).
    247   1.6  christos 		 */
    248  1.12  riastrad 		RZ(posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR));
    249   1.6  christos 		attr_p = &attr;
    250   1.6  christos 	}
    251   1.6  christos 
    252   1.6  christos 	error = posix_spawn(&pid, "/bin/pwd", &fa, attr_p, args, NULL);
    253  1.12  riastrad 	ATF_REQUIRE_MSG(error == expected_error, "error=%d expected_error=%d",
    254  1.12  riastrad 	    error, expected_error);
    255   1.6  christos 
    256   1.6  christos 	/* wait for the child to finish only when no spawnattr */
    257   1.6  christos 	if (attr_p) {
    258  1.12  riastrad 		RZ(posix_spawnattr_destroy(&attr));
    259   1.6  christos 	} else {
    260  1.12  riastrad 		RL(waitpid(pid, &status, 0));
    261  1.12  riastrad 		ATF_REQUIRE_MSG((WIFEXITED(status) &&
    262  1.12  riastrad 			WEXITSTATUS(status) == EXIT_SUCCESS),
    263  1.12  riastrad 		    "[f]chdir failed");
    264   1.6  christos 	}
    265   1.6  christos 
    266  1.12  riastrad 	RZ(posix_spawn_file_actions_destroy(&fa));
    267   1.6  christos 
    268   1.6  christos 	/*
    269   1.6  christos 	 * The file incase of fchdir(),
    270   1.6  christos 	 * should be closed before reopening in 'check_success'
    271   1.6  christos 	 */
    272   1.6  christos 	if (fd != -1) {
    273  1.12  riastrad 		RL(close(fd));
    274   1.6  christos 	}
    275   1.6  christos }
    276   1.6  christos 
    277   1.6  christos ATF_TC(t_spawn_chdir_abs);
    278   1.6  christos ATF_TC_HEAD(t_spawn_chdir_abs, tc)
    279   1.6  christos {
    280   1.6  christos 	atf_tc_set_md_var(tc, "descr",
    281   1.6  christos 	    "Test posix_spawn_fa_addchdir for absolute path");
    282   1.6  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    283   1.6  christos }
    284   1.6  christos ATF_TC_BODY(t_spawn_chdir_abs, tc)
    285   1.6  christos {
    286  1.12  riastrad 	char chdirpath[PATH_MAX], filepath[PATH_MAX];
    287  1.12  riastrad 
    288  1.12  riastrad 	REQUIRE_LIBC(getcwd(chdirpath, sizeof(chdirpath)), NULL);
    289  1.12  riastrad 	RL(chdir("/"));
    290  1.12  riastrad 	if (snprintf(filepath, sizeof(filepath), "%s/%s", chdirpath, FILENAME)
    291  1.12  riastrad 	    >= (int)sizeof(filepath))
    292  1.12  riastrad 		atf_tc_fail("too deep: %s", chdirpath);
    293  1.12  riastrad 
    294  1.12  riastrad 	spawn_chdir(chdirpath, filepath, OP_CHDIR, 0);
    295   1.4  christos 
    296   1.4  christos 	/* finally cross check the output of "pwd" directory */
    297  1.12  riastrad 	check_success(chdirpath, filepath);
    298   1.4  christos }
    299   1.4  christos 
    300   1.4  christos ATF_TC(t_spawn_chdir_rel);
    301   1.4  christos 
    302   1.4  christos ATF_TC_HEAD(t_spawn_chdir_rel, tc)
    303   1.4  christos {
    304   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    305   1.4  christos 	    "Test posix_spawn_fa_addchdir for relative path");
    306   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    307   1.4  christos }
    308   1.4  christos 
    309   1.4  christos ATF_TC_BODY(t_spawn_chdir_rel, tc)
    310   1.4  christos {
    311   1.4  christos 	const char *relative_dir = "ch-dir";
    312   1.4  christos 	const char *testdir = getcwd(NULL, 0);
    313   1.4  christos 	char *chdirwd, *filepath;
    314   1.4  christos 
    315  1.12  riastrad 	RL(mkdir(relative_dir, 0777));
    316  1.12  riastrad 	RL(asprintf(&chdirwd, "%s/%s", testdir, relative_dir));
    317  1.12  riastrad 	RL(asprintf(&filepath, "%s/%s", chdirwd, FILENAME));
    318   1.4  christos 
    319   1.4  christos #ifdef DEBUG
    320   1.4  christos 	printf("cwd: %s\n", testdir);
    321   1.4  christos 	printf("chdirwd: %s\n", chdirwd);
    322   1.4  christos 	printf("filepath: %s\n", filepath);
    323   1.4  christos #endif
    324   1.4  christos 
    325   1.6  christos 	spawn_chdir(relative_dir, filepath, 1, 0);
    326   1.4  christos 
    327   1.4  christos 	/* finally cross check the directory */
    328  1.12  riastrad 	check_success(chdirwd, filepath);
    329   1.4  christos 	free(chdirwd);
    330   1.4  christos 	free(filepath);
    331   1.4  christos }
    332   1.4  christos 
    333   1.4  christos ATF_TC(t_spawn_chdir_file);
    334   1.4  christos ATF_TC_HEAD(t_spawn_chdir_file, tc)
    335   1.4  christos {
    336   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    337   1.4  christos 	    "Test posix_spawn_fa_addchdir on plain file (not a directory)");
    338   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    339   1.4  christos }
    340   1.4  christos ATF_TC_BODY(t_spawn_chdir_file, tc)
    341   1.4  christos {
    342  1.12  riastrad 	char cwd[PATH_MAX], filepath[PATH_MAX];
    343  1.12  riastrad 
    344  1.12  riastrad 	REQUIRE_LIBC(getcwd(cwd, sizeof(cwd)), NULL);
    345  1.12  riastrad 	if (snprintf(filepath, sizeof(filepath), "%s/%s", cwd, FILENAME)
    346  1.12  riastrad 	    >= (int)sizeof(filepath))
    347  1.12  riastrad 		atf_tc_fail("too deep: %s", cwd);
    348   1.4  christos 
    349  1.12  riastrad 	spawn_chdir(filepath, filepath, 1, ENOTDIR);
    350   1.4  christos }
    351   1.4  christos 
    352   1.4  christos ATF_TC(t_spawn_chdir_invalid);
    353   1.4  christos ATF_TC_HEAD(t_spawn_chdir_invalid, tc)
    354   1.4  christos {
    355   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    356   1.4  christos 	    "Test posix_spawn_fa_addchdir for an invalid dir");
    357   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    358   1.4  christos }
    359   1.4  christos ATF_TC_BODY(t_spawn_chdir_invalid, tc)
    360   1.4  christos {
    361   1.6  christos 	spawn_chdir("/not/a/valid/dir", NULL, 1, ENOENT);
    362   1.4  christos }
    363   1.4  christos 
    364   1.4  christos ATF_TC(t_spawn_chdir_permissions);
    365   1.4  christos ATF_TC_HEAD(t_spawn_chdir_permissions, tc)
    366   1.4  christos {
    367   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    368   1.4  christos 	    "Test posix_spawn_file_actions_addchdir for prohibited directory");
    369   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    370   1.4  christos 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
    371   1.4  christos }
    372   1.4  christos ATF_TC_BODY(t_spawn_chdir_permissions, tc)
    373   1.4  christos {
    374  1.12  riastrad 	const char *restricted_dir = "prohibited";
    375   1.4  christos 
    376  1.12  riastrad 	RL(mkdir(restricted_dir, 0055));
    377   1.4  christos 
    378  1.12  riastrad 	spawn_chdir(restricted_dir, NULL, 1, EACCES);
    379   1.6  christos }
    380   1.4  christos 
    381   1.5  christos ATF_TC(t_spawn_fchdir_abs);
    382   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_abs, tc)
    383   1.4  christos {
    384   1.4  christos 	atf_tc_set_md_var(tc, "descr", "Test posix_spawn_fa_fchdir");
    385   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    386   1.4  christos }
    387   1.5  christos ATF_TC_BODY(t_spawn_fchdir_abs, tc)
    388   1.4  christos {
    389  1.12  riastrad 	char chdirpath[PATH_MAX], filepath[PATH_MAX];
    390  1.12  riastrad 
    391  1.12  riastrad 	REQUIRE_LIBC(getcwd(chdirpath, sizeof(chdirpath)), NULL);
    392  1.12  riastrad 	RL(chdir("/"));
    393  1.12  riastrad 	if (snprintf(filepath, sizeof(filepath), "%s/%s", chdirpath, FILENAME)
    394  1.12  riastrad 	    >= (int)sizeof(filepath))
    395  1.12  riastrad 		atf_tc_fail("too deep: %s", chdirpath);
    396  1.12  riastrad 
    397  1.12  riastrad 	spawn_chdir(chdirpath, filepath, OP_FCHDIR, 0);
    398   1.4  christos 
    399   1.4  christos 	/* finally cross check the directory */
    400  1.12  riastrad 	check_success(chdirpath, filepath);
    401   1.4  christos }
    402   1.4  christos 
    403   1.5  christos ATF_TC(t_spawn_fchdir_rel);
    404   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_rel, tc)
    405   1.4  christos {
    406   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    407   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a relative "
    408   1.5  christos 	    "directory");
    409   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    410   1.4  christos }
    411   1.5  christos ATF_TC_BODY(t_spawn_fchdir_rel, tc)
    412   1.4  christos {
    413   1.5  christos 	const char *relative_dir = "ch-dir";
    414   1.5  christos 	const char *testdir = getcwd(NULL, 0);
    415   1.5  christos 	char *chdirwd, *filepath;
    416   1.4  christos 
    417  1.12  riastrad 	RL(mkdir(relative_dir, 0755));
    418   1.5  christos 
    419   1.5  christos 	/*
    420   1.5  christos 	 * This is done in parts purposely.
    421   1.8    andvar 	 * It enables the abs path of the relative dir
    422   1.5  christos 	 * to be passed to 'check_success()' for comparing
    423   1.5  christos 	 */
    424  1.12  riastrad 	RL(asprintf(&chdirwd, "%s/%s", testdir, relative_dir));
    425  1.12  riastrad 	RL(asprintf(&filepath, "%s/%s", chdirwd, FILENAME));
    426   1.5  christos 
    427   1.6  christos 	spawn_chdir(relative_dir, filepath, 2, 0);
    428   1.5  christos 
    429   1.5  christos 	/* finally cross check the directory */
    430  1.12  riastrad 	check_success(chdirwd, filepath);
    431   1.5  christos 	free(chdirwd);
    432   1.5  christos 	free(filepath);
    433   1.4  christos }
    434   1.4  christos 
    435   1.5  christos ATF_TC(t_spawn_fchdir_file);
    436   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_file, tc)
    437   1.4  christos {
    438   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    439   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a "
    440   1.5  christos 	    "regular file (not a directory)");
    441   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    442   1.4  christos }
    443   1.5  christos ATF_TC_BODY(t_spawn_fchdir_file, tc)
    444   1.4  christos {
    445  1.12  riastrad 	char cwd[PATH_MAX], filepath[PATH_MAX];
    446  1.12  riastrad 	int fd;
    447   1.4  christos 
    448  1.12  riastrad 	REQUIRE_LIBC(getcwd(cwd, sizeof(cwd)), NULL);
    449  1.12  riastrad 	if (snprintf(filepath, sizeof(filepath), "%s/%s", cwd, FILENAME)
    450  1.12  riastrad 	    >= (int)sizeof(filepath))
    451  1.12  riastrad 		atf_tc_fail("too deep: %s", cwd);
    452  1.12  riastrad 	RL(fd = open(FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0644));
    453  1.12  riastrad 	RL(close(fd));
    454   1.5  christos 
    455  1.12  riastrad 	spawn_chdir(filepath, NULL, 2, ENOTDIR);
    456   1.5  christos }
    457   1.5  christos 
    458   1.5  christos ATF_TC(t_spawn_fchdir_neg_fd);
    459   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_neg_fd, tc)
    460   1.5  christos {
    461   1.5  christos 	atf_tc_set_md_var(tc, "descr",
    462   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a negative file "
    463   1.5  christos 	    "descriptor");
    464   1.5  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    465   1.5  christos }
    466   1.5  christos ATF_TC_BODY(t_spawn_fchdir_neg_fd, tc)
    467   1.5  christos {
    468   1.5  christos 	int error, fd;
    469   1.5  christos 	posix_spawn_file_actions_t fa;
    470   1.5  christos 
    471   1.5  christos 	fd = -1;
    472   1.5  christos 
    473  1.12  riastrad 	RZ(posix_spawn_file_actions_init(&fa));
    474   1.5  christos 
    475   1.5  christos 	error = posix_spawn_file_actions_addfchdir(&fa, fd);
    476  1.12  riastrad 	ATF_REQUIRE_MSG(error == EBADF, "error=%d", error);
    477   1.4  christos 
    478  1.12  riastrad 	RZ(posix_spawn_file_actions_destroy(&fa));
    479   1.4  christos }
    480   1.4  christos 
    481   1.5  christos ATF_TC(t_spawn_fchdir_closed);
    482   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_closed, tc)
    483   1.4  christos {
    484   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    485   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir for a closed fd");
    486   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    487   1.4  christos }
    488   1.5  christos ATF_TC_BODY(t_spawn_fchdir_closed, tc)
    489   1.4  christos {
    490   1.5  christos 	int error, fd;
    491   1.4  christos 	pid_t pid;
    492   1.4  christos 	char * const args[2] = { __UNCONST("pwd"), NULL };
    493   1.4  christos 	posix_spawnattr_t attr;
    494   1.4  christos 	posix_spawn_file_actions_t fa;
    495   1.4  christos 
    496   1.5  christos 	fd = 3;
    497  1.12  riastrad 	RZ(posix_spawnattr_init(&attr));
    498   1.4  christos 	/*
    499   1.4  christos 	 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
    500   1.4  christos 	 * will cause a "proper" return value from posix_spawn(2)
    501   1.4  christos 	 * instead of a (potential) success there and a 127 exit
    502   1.4  christos 	 * status from the child process (c.f. the non-diag variant
    503   1.4  christos 	 * of this test).
    504   1.4  christos 	 */
    505  1.12  riastrad 	RZ(posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR));
    506  1.12  riastrad 	RZ(posix_spawn_file_actions_init(&fa));
    507  1.12  riastrad 	RZ(posix_spawn_file_actions_addfchdir(&fa, fd));
    508   1.4  christos 
    509   1.4  christos 	error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL);
    510  1.12  riastrad 	ATF_REQUIRE_MSG(error == EBADF, "error=%d", error);
    511   1.4  christos 
    512  1.12  riastrad 	RZ(posix_spawn_file_actions_destroy(&fa));
    513  1.12  riastrad 	RZ(posix_spawnattr_destroy(&attr));
    514   1.4  christos }
    515   1.4  christos 
    516   1.9  riastrad ATF_TC(t_spawn_sig);
    517   1.9  riastrad ATF_TC_HEAD(t_spawn_sig, tc)
    518   1.9  riastrad {
    519   1.9  riastrad 	atf_tc_set_md_var(tc, "descr",
    520   1.9  riastrad 	    "Checks that posix_spawn does not drop pending signals");
    521   1.9  riastrad }
    522   1.9  riastrad ATF_TC_BODY(t_spawn_sig, tc)
    523   1.9  riastrad {
    524   1.9  riastrad 	const char *srcdir = atf_tc_get_config_var(tc, "srcdir");
    525   1.9  riastrad 	char h_execsig[PATH_MAX];
    526   1.9  riastrad 	time_t start;
    527   1.9  riastrad 
    528   1.9  riastrad 	snprintf(h_execsig, sizeof(h_execsig), "%s/../h_execsig", srcdir);
    529   1.9  riastrad 	REQUIRE_LIBC(signal(SIGPIPE, SIG_IGN), SIG_ERR);
    530   1.9  riastrad 
    531   1.9  riastrad 	for (start = time(NULL); time(NULL) - start <= 10;) {
    532   1.9  riastrad 		int fd[2];
    533   1.9  riastrad 		char *const argv[] = {h_execsig, NULL};
    534  1.11  riastrad 		posix_spawn_file_actions_t fa;
    535   1.9  riastrad 		pid_t pid;
    536   1.9  riastrad 		int status;
    537   1.9  riastrad 
    538  1.11  riastrad 		RL(pipe2(fd, O_CLOEXEC));
    539  1.11  riastrad 		RZ(posix_spawn_file_actions_init(&fa));
    540  1.11  riastrad 		RZ(posix_spawn_file_actions_adddup2(&fa, fd[0], STDIN_FILENO));
    541  1.11  riastrad 		RZ(posix_spawn(&pid, argv[0], &fa, NULL, argv, NULL));
    542   1.9  riastrad 		RL(close(fd[0]));
    543   1.9  riastrad 		RL(kill(pid, SIGTERM));
    544   1.9  riastrad 		if (write(fd[1], (char[]){0}, 1) == -1 && errno != EPIPE)
    545   1.9  riastrad 			atf_tc_fail("write failed: %s", strerror(errno));
    546   1.9  riastrad 		RL(waitpid(pid, &status, 0));
    547   1.9  riastrad 		ATF_REQUIRE_MSG(WIFSIGNALED(status),
    548   1.9  riastrad 		    "child exited with status 0x%x", status);
    549   1.9  riastrad 		ATF_REQUIRE_EQ_MSG(WTERMSIG(status), SIGTERM,
    550   1.9  riastrad 		    "child exited on signal %d (%s)",
    551   1.9  riastrad 		    WTERMSIG(status), strsignal(WTERMSIG(status)));
    552   1.9  riastrad 		RL(close(fd[1]));
    553  1.11  riastrad 		RZ(posix_spawn_file_actions_destroy(&fa));
    554   1.9  riastrad 	}
    555   1.9  riastrad }
    556   1.9  riastrad 
    557   1.1    martin ATF_TP_ADD_TCS(tp)
    558   1.1    martin {
    559   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_ls);
    560   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawnp_ls);
    561   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_zero);
    562   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_missing);
    563   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_nonexec);
    564   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_child);
    565   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_abs);
    566   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_rel);
    567   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_file);
    568   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_invalid);
    569   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_permissions);
    570   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_abs);
    571   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_rel);
    572   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_file);
    573   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_neg_fd);
    574   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_closed);
    575   1.9  riastrad 	ATF_TP_ADD_TC(tp, t_spawn_sig);
    576   1.1    martin 
    577   1.1    martin 	return atf_no_error();
    578   1.1    martin }
    579