Home | History | Annotate | Line # | Download | only in posix_spawn
t_spawn.c revision 1.10
      1  1.10  riastrad /* $NetBSD: t_spawn.c,v 1.10 2025/03/13 12:48:22 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.10  riastrad __RCSID("$NetBSD: t_spawn.c,v 1.10 2025/03/13 12:48:22 riastradh Exp $");
     35   1.1    martin 
     36   1.4  christos #include <atf-c.h>
     37   1.4  christos 
     38   1.4  christos #include <sys/fcntl.h>
     39   1.9  riastrad #include <sys/stat.h>
     40   1.4  christos #include <sys/types.h>
     41   1.4  christos #include <sys/wait.h>
     42   1.1    martin 
     43   1.9  riastrad #include <errno.h>
     44   1.9  riastrad #include <fcntl.h>
     45   1.9  riastrad #include <limits.h>
     46   1.9  riastrad #include <signal.h>
     47   1.1    martin #include <spawn.h>
     48   1.9  riastrad #include <stdarg.h>
     49   1.1    martin #include <stdio.h>
     50   1.1    martin #include <stdlib.h>
     51   1.9  riastrad #include <string.h>
     52   1.9  riastrad #include <time.h>
     53   1.4  christos #include <unistd.h>
     54   1.4  christos 
     55   1.4  christos #include "fa_spawn_utils.h"
     56   1.9  riastrad #include "h_macros.h"
     57   1.4  christos 
     58   1.4  christos static void check_success(const char *, int, ...);
     59   1.1    martin 
     60   1.1    martin ATF_TC(t_spawn_ls);
     61   1.1    martin 
     62   1.1    martin ATF_TC_HEAD(t_spawn_ls, tc)
     63   1.1    martin {
     64   1.1    martin 	atf_tc_set_md_var(tc, "descr",
     65   1.1    martin 	    "Tests a simple posix_spawn executing /bin/ls");
     66   1.1    martin }
     67   1.1    martin 
     68   1.1    martin ATF_TC_BODY(t_spawn_ls, tc)
     69   1.1    martin {
     70   1.1    martin 	char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL };
     71   1.1    martin 	int err;
     72   1.1    martin 
     73   1.1    martin 	err = posix_spawn(NULL, "/bin/ls", NULL, NULL, args, NULL);
     74   1.1    martin 	ATF_REQUIRE(err == 0);
     75   1.1    martin }
     76   1.1    martin 
     77   1.1    martin ATF_TC(t_spawnp_ls);
     78   1.1    martin 
     79   1.1    martin ATF_TC_HEAD(t_spawnp_ls, tc)
     80   1.1    martin {
     81   1.1    martin 	atf_tc_set_md_var(tc, "descr",
     82   1.1    martin 	    "Tests a simple posix_spawnp executing ls via $PATH");
     83   1.1    martin }
     84   1.1    martin 
     85   1.1    martin ATF_TC_BODY(t_spawnp_ls, tc)
     86   1.1    martin {
     87   1.1    martin 	char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL };
     88   1.1    martin 	int err;
     89   1.1    martin 
     90   1.1    martin 	err = posix_spawnp(NULL, "ls", NULL, NULL, args, NULL);
     91   1.1    martin 	ATF_REQUIRE(err == 0);
     92   1.1    martin }
     93   1.1    martin 
     94   1.6  christos static void
     95   1.6  christos spawn_error(const atf_tc_t *tc, const char *name, int error)
     96   1.6  christos {
     97   1.6  christos 	char buf[FILENAME_MAX];
     98   1.6  christos 	char * const args[] = { __UNCONST(name), NULL };
     99   1.6  christos 	int err;
    100   1.6  christos 
    101   1.6  christos 	snprintf(buf, sizeof buf, "%s/%s",
    102   1.6  christos 	    atf_tc_get_config_var(tc, "srcdir"), name);
    103   1.6  christos 	err = posix_spawn(NULL, buf, NULL, NULL, args, NULL);
    104   1.6  christos 	ATF_REQUIRE_MSG(err == error, "expected error %d, "
    105   1.6  christos 	    "got %d when spawning %s", error, err, buf);
    106   1.6  christos }
    107   1.6  christos 
    108   1.1    martin ATF_TC(t_spawn_zero);
    109   1.1    martin 
    110   1.1    martin ATF_TC_HEAD(t_spawn_zero, tc)
    111   1.1    martin {
    112   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    113   1.1    martin 	    "posix_spawn an invalid binary");
    114   1.1    martin }
    115   1.1    martin 
    116   1.1    martin ATF_TC_BODY(t_spawn_zero, tc)
    117   1.1    martin {
    118   1.6  christos 	spawn_error(tc, "h_zero", ENOEXEC);
    119   1.1    martin }
    120   1.1    martin 
    121   1.1    martin ATF_TC(t_spawn_missing);
    122   1.1    martin 
    123   1.1    martin ATF_TC_HEAD(t_spawn_missing, tc)
    124   1.1    martin {
    125   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    126   1.4  christos 	    "posix_spawn a non existant binary");
    127   1.1    martin }
    128   1.1    martin 
    129   1.1    martin ATF_TC_BODY(t_spawn_missing, tc)
    130   1.1    martin {
    131   1.6  christos 	spawn_error(tc, "h_nonexist", ENOENT);
    132   1.1    martin }
    133   1.1    martin 
    134   1.1    martin ATF_TC(t_spawn_nonexec);
    135   1.1    martin 
    136   1.1    martin ATF_TC_HEAD(t_spawn_nonexec, tc)
    137   1.1    martin {
    138   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    139   1.4  christos 	    "posix_spawn a script with non existing interpreter");
    140   1.1    martin }
    141   1.1    martin 
    142   1.1    martin ATF_TC_BODY(t_spawn_nonexec, tc)
    143   1.1    martin {
    144   1.6  christos 	spawn_error(tc, "h_nonexec", ENOENT);
    145   1.1    martin }
    146   1.1    martin 
    147   1.1    martin ATF_TC(t_spawn_child);
    148   1.1    martin 
    149   1.1    martin ATF_TC_HEAD(t_spawn_child, tc)
    150   1.1    martin {
    151   1.1    martin 	atf_tc_set_md_var(tc, "descr",
    152   1.2       snj 	    "posix_spawn a child and get its return code");
    153   1.1    martin }
    154   1.1    martin 
    155   1.1    martin ATF_TC_BODY(t_spawn_child, tc)
    156   1.1    martin {
    157   1.1    martin 	char buf[FILENAME_MAX];
    158   1.6  christos 	char rv[2] = { '0', '\0' };
    159   1.6  christos 	char * const args0[] = { __UNCONST("h_spawn"), rv, NULL };
    160   1.6  christos 	int rets[] = { 0, 1, 7 };
    161   1.1    martin 	int err, status;
    162   1.1    martin 	pid_t pid;
    163   1.1    martin 
    164   1.1    martin 	snprintf(buf, sizeof buf, "%s/h_spawn",
    165   1.1    martin 	    atf_tc_get_config_var(tc, "srcdir"));
    166   1.1    martin 
    167   1.6  christos 	for (size_t i = 0; i < __arraycount(rets); i++) {
    168   1.6  christos 		rv[0] = rets[i] + '0';
    169   1.6  christos 		err = posix_spawn(&pid, buf, NULL, NULL, args0, NULL);
    170   1.6  christos 		ATF_REQUIRE(err == 0);
    171   1.6  christos 		ATF_REQUIRE(pid > 0);
    172   1.6  christos 		waitpid(pid, &status, 0);
    173   1.6  christos 		ATF_REQUIRE(WIFEXITED(status) &&
    174   1.6  christos 		    WEXITSTATUS(status) == rets[i]);
    175   1.6  christos 	}
    176   1.1    martin }
    177   1.1    martin 
    178   1.4  christos #define CHDIRPATH "/tmp"
    179   1.4  christos #define FILENAME "output"
    180   1.4  christos #define FILEPATH "/tmp/output"
    181   1.4  christos 
    182   1.6  christos #define CHDIR 1
    183   1.6  christos #define FCHDIR 2
    184   1.6  christos 
    185   1.4  christos static void
    186   1.4  christos check_success(const char *file, int argc, ...)
    187   1.4  christos {
    188   1.4  christos 	va_list ap;
    189   1.5  christos 	ssize_t bytesRead;
    190   1.5  christos 	int fd;
    191   1.4  christos 	size_t sizeOfFile = (size_t)filesize(file);
    192   1.4  christos 	size_t sizeOfStr;
    193   1.4  christos 	char *contents;
    194   1.4  christos 	const char *dir;
    195   1.4  christos 
    196   1.4  christos 	contents = malloc(sizeOfFile);
    197   1.4  christos 	ATF_REQUIRE(contents != NULL);
    198   1.4  christos 
    199   1.4  christos 	/*
    200   1.4  christos 	 * for now only 1 variadic argument expected
    201   1.5  christos 	 * only from t_spawn_[f]chdir_rel
    202   1.4  christos 	 */
    203   1.4  christos 	if (argc != 0) {
    204   1.4  christos 		va_start(ap, argc);
    205   1.4  christos 		dir = va_arg(ap, char *);
    206   1.4  christos 		ATF_REQUIRE(dir != NULL);
    207   1.4  christos 		va_end(ap);
    208   1.4  christos 	} else
    209   1.4  christos 		dir = CHDIRPATH;
    210   1.4  christos 
    211   1.4  christos 	fd = open(file, O_RDONLY);
    212   1.6  christos 	ATF_REQUIRE_MSG(fd != -1, "Can't open `%s' (%s)", file,
    213   1.6  christos 	    strerror(errno));
    214   1.4  christos 
    215   1.4  christos 	/*
    216   1.4  christos 	 * file contains form feed i.e ASCII - 10 at the end.
    217   1.4  christos 	 * Therefore sizeOfFile - 1
    218   1.4  christos 	 */
    219   1.4  christos 	sizeOfStr = strlen(dir);
    220   1.6  christos 	ATF_CHECK_MSG(sizeOfStr == sizeOfFile - 1, "%zu (%s) != %zu (%s)",
    221   1.4  christos 	    sizeOfStr, dir, sizeOfFile - 1, file);
    222   1.4  christos 
    223   1.4  christos 	bytesRead = read(fd, contents, sizeOfFile - 1);
    224   1.4  christos 	contents[sizeOfFile - 1] = '\0';
    225   1.4  christos 	ATF_REQUIRE_MSG(strcmp(dir, contents) == 0,
    226   1.4  christos 	    "[%s] != [%s] Directories dont match", dir, contents);
    227   1.4  christos 
    228   1.4  christos 	fd = close(fd);
    229   1.4  christos 	ATF_REQUIRE(fd == 0);
    230   1.4  christos 
    231   1.4  christos 	unlink(file);
    232   1.4  christos 	free(contents);
    233   1.4  christos 
    234   1.4  christos 	/* XXX not really required */
    235   1.5  christos 	ATF_REQUIRE((size_t)bytesRead == sizeOfStr);
    236   1.4  christos }
    237   1.4  christos 
    238   1.6  christos static void
    239   1.6  christos spawn_chdir(const char *dirpath, const char *filepath, int operation,
    240   1.6  christos     int expected_error)
    241   1.4  christos {
    242   1.6  christos 	int error, fd=-1, status;
    243   1.4  christos 	char * const args[2] = { __UNCONST("pwd"), NULL };
    244   1.4  christos 	pid_t pid;
    245   1.6  christos 	posix_spawnattr_t attr, *attr_p;
    246   1.4  christos 	posix_spawn_file_actions_t fa;
    247   1.4  christos 
    248   1.6  christos 	if (filepath)
    249   1.6  christos 		empty_outfile(filepath);
    250   1.4  christos 
    251   1.4  christos 	error = posix_spawn_file_actions_init(&fa);
    252   1.4  christos 	ATF_REQUIRE(error == 0);
    253   1.4  christos 
    254   1.6  christos 	switch(operation) {
    255   1.6  christos 	case CHDIR:
    256   1.6  christos 		error = posix_spawn_file_actions_addchdir(&fa, dirpath);
    257   1.6  christos 		break;
    258   1.6  christos 
    259   1.6  christos 	case FCHDIR:
    260   1.6  christos 		fd = open(dirpath, O_RDONLY);
    261   1.6  christos 		ATF_REQUIRE(fd != -1);
    262   1.6  christos 
    263   1.6  christos 		error = posix_spawn_file_actions_addfchdir(&fa, fd);
    264   1.6  christos 		break;
    265   1.6  christos 	}
    266   1.4  christos 	ATF_REQUIRE(error == 0);
    267   1.4  christos 
    268   1.6  christos 	/*
    269   1.6  christos 	 * if POSIX_SPAWN_RETURNERROR is expected, then no need to open the
    270   1.6  christos 	 * file
    271   1.6  christos 	 */
    272   1.6  christos 	if (expected_error == 0) {
    273   1.6  christos 		error = posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO,
    274   1.6  christos 		    FILENAME, O_WRONLY, 0);
    275   1.6  christos 		ATF_REQUIRE(error == 0);
    276   1.6  christos 		attr_p = NULL;
    277   1.6  christos 	} else {
    278   1.6  christos 		error = posix_spawnattr_init(&attr);
    279   1.6  christos 		ATF_REQUIRE(error == 0);
    280   1.6  christos 
    281   1.6  christos 		/*
    282   1.6  christos 		 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
    283   1.6  christos 		 * will cause a "proper" return value from posix_spawn(2)
    284   1.6  christos 		 * instead of a (potential) success there and a 127 exit
    285   1.6  christos 		 * status from the child process (c.f. the non-diag variant
    286   1.6  christos 		 * of this test).
    287   1.6  christos 		 */
    288   1.6  christos 		error = posix_spawnattr_setflags(&attr,
    289   1.6  christos 		    POSIX_SPAWN_RETURNERROR);
    290   1.6  christos 		ATF_REQUIRE(error == 0);
    291   1.6  christos 		attr_p = &attr;
    292   1.6  christos 	}
    293   1.6  christos 
    294   1.6  christos 	error = posix_spawn(&pid, "/bin/pwd", &fa, attr_p, args, NULL);
    295   1.6  christos 	ATF_REQUIRE(error == expected_error);
    296   1.6  christos 
    297   1.6  christos 	/* wait for the child to finish only when no spawnattr */
    298   1.6  christos 	if (attr_p) {
    299   1.6  christos 		posix_spawnattr_destroy(&attr);
    300   1.6  christos 	} else {
    301   1.6  christos 		waitpid(pid, &status, 0);
    302   1.6  christos 		ATF_REQUIRE_MSG(WIFEXITED(status) &&
    303   1.6  christos 		    WEXITSTATUS(status) == EXIT_SUCCESS,
    304   1.6  christos 		    "%s", "[f]chdir failed");
    305   1.6  christos 	}
    306   1.6  christos 
    307   1.6  christos 	posix_spawn_file_actions_destroy(&fa);
    308   1.6  christos 
    309   1.6  christos 	/*
    310   1.6  christos 	 * The file incase of fchdir(),
    311   1.6  christos 	 * should be closed before reopening in 'check_success'
    312   1.6  christos 	 */
    313   1.6  christos 	if (fd != -1) {
    314   1.6  christos 		error = close(fd);
    315   1.6  christos 		ATF_REQUIRE(error == 0);
    316   1.6  christos 	}
    317   1.6  christos }
    318   1.6  christos 
    319   1.6  christos ATF_TC(t_spawn_chdir_abs);
    320   1.4  christos 
    321   1.6  christos ATF_TC_HEAD(t_spawn_chdir_abs, tc)
    322   1.6  christos {
    323   1.6  christos 	atf_tc_set_md_var(tc, "descr",
    324   1.6  christos 	    "Test posix_spawn_fa_addchdir for absolute path");
    325   1.6  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    326   1.6  christos }
    327   1.4  christos 
    328   1.6  christos ATF_TC_BODY(t_spawn_chdir_abs, tc)
    329   1.6  christos {
    330   1.6  christos 	spawn_chdir(CHDIRPATH, FILEPATH, 1, 0);
    331   1.4  christos 
    332   1.4  christos 	/* finally cross check the output of "pwd" directory */
    333   1.4  christos 	check_success(FILEPATH, 0);
    334   1.4  christos }
    335   1.4  christos 
    336   1.4  christos ATF_TC(t_spawn_chdir_rel);
    337   1.4  christos 
    338   1.4  christos ATF_TC_HEAD(t_spawn_chdir_rel, tc)
    339   1.4  christos {
    340   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    341   1.4  christos 	    "Test posix_spawn_fa_addchdir for relative path");
    342   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    343   1.4  christos }
    344   1.4  christos 
    345   1.4  christos 
    346   1.4  christos ATF_TC_BODY(t_spawn_chdir_rel, tc)
    347   1.4  christos {
    348   1.6  christos 	int error;
    349   1.4  christos 	const char *relative_dir = "ch-dir";
    350   1.4  christos 	const char *testdir = getcwd(NULL, 0);
    351   1.4  christos 	char *chdirwd, *filepath;
    352   1.4  christos 
    353   1.4  christos 	/* cleanup previous */
    354   1.4  christos 	error = asprintf(&filepath, "%s/%s", relative_dir, FILENAME);
    355   1.4  christos 	ATF_CHECK(error != -1);
    356   1.4  christos 	unlink(filepath);
    357   1.4  christos 	free(filepath);
    358   1.6  christos 
    359   1.4  christos 	rmdir(relative_dir);
    360   1.4  christos 	error = mkdir(relative_dir, 0777);
    361   1.4  christos 	ATF_REQUIRE_MSG(error == 0, "mkdir `%s' (%s)", relative_dir,
    362   1.4  christos 	    strerror(errno));
    363   1.4  christos 
    364   1.4  christos 	error = asprintf(&chdirwd, "%s/%s", testdir, relative_dir);
    365   1.4  christos 	ATF_CHECK(error != -1);
    366   1.4  christos 
    367   1.4  christos 	error = asprintf(&filepath, "%s/%s", chdirwd, FILENAME);
    368   1.4  christos 	ATF_CHECK(error != -1);
    369   1.4  christos 
    370   1.4  christos #ifdef DEBUG
    371   1.4  christos 	printf("cwd: %s\n", testdir);
    372   1.4  christos 	printf("chdirwd: %s\n", chdirwd);
    373   1.4  christos 	printf("filepath: %s\n", filepath);
    374   1.4  christos #endif
    375   1.4  christos 
    376   1.6  christos 	spawn_chdir(relative_dir, filepath, 1, 0);
    377   1.4  christos 
    378   1.4  christos 	/* finally cross check the directory */
    379   1.4  christos 	check_success(filepath, 1, chdirwd);
    380   1.4  christos 	free(chdirwd);
    381   1.4  christos 	free(filepath);
    382   1.4  christos }
    383   1.4  christos 
    384   1.4  christos ATF_TC(t_spawn_chdir_file);
    385   1.4  christos 
    386   1.4  christos ATF_TC_HEAD(t_spawn_chdir_file, tc)
    387   1.4  christos {
    388   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    389   1.4  christos 	    "Test posix_spawn_fa_addchdir on plain file (not a directory)");
    390   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    391   1.4  christos }
    392   1.4  christos 
    393   1.4  christos ATF_TC_BODY(t_spawn_chdir_file, tc)
    394   1.4  christos {
    395   1.6  christos 	spawn_chdir(FILEPATH, FILEPATH, 1, ENOTDIR);
    396   1.4  christos 
    397   1.4  christos 	unlink(FILEPATH);
    398   1.4  christos }
    399   1.4  christos 
    400   1.4  christos ATF_TC(t_spawn_chdir_invalid);
    401   1.4  christos 
    402   1.4  christos ATF_TC_HEAD(t_spawn_chdir_invalid, tc)
    403   1.4  christos {
    404   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    405   1.4  christos 	    "Test posix_spawn_fa_addchdir for an invalid dir");
    406   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    407   1.4  christos }
    408   1.4  christos 
    409   1.4  christos ATF_TC_BODY(t_spawn_chdir_invalid, tc)
    410   1.4  christos {
    411   1.6  christos 	spawn_chdir("/not/a/valid/dir", NULL, 1, ENOENT);
    412   1.4  christos 
    413   1.4  christos }
    414   1.4  christos 
    415   1.4  christos ATF_TC(t_spawn_chdir_permissions);
    416   1.4  christos 
    417   1.4  christos ATF_TC_HEAD(t_spawn_chdir_permissions, tc)
    418   1.4  christos {
    419   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    420   1.4  christos 	    "Test posix_spawn_file_actions_addchdir for prohibited directory");
    421   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    422   1.4  christos 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
    423   1.4  christos }
    424   1.4  christos 
    425   1.4  christos ATF_TC_BODY(t_spawn_chdir_permissions, tc)
    426   1.4  christos {
    427   1.4  christos 	int error;
    428   1.4  christos 	const char *restrRelDir = "prohibited";
    429   1.4  christos 
    430   1.6  christos 	rmdir(restrRelDir);
    431   1.4  christos 	error = mkdir(restrRelDir, 0055);
    432   1.4  christos 	ATF_REQUIRE(error == 0);
    433   1.4  christos 
    434   1.6  christos 	spawn_chdir(restrRelDir, NULL, 1, EACCES);
    435   1.6  christos }
    436   1.4  christos 
    437   1.4  christos 
    438   1.5  christos ATF_TC(t_spawn_fchdir_abs);
    439   1.4  christos 
    440   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_abs, tc)
    441   1.4  christos {
    442   1.4  christos 	atf_tc_set_md_var(tc, "descr", "Test posix_spawn_fa_fchdir");
    443   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    444   1.4  christos }
    445   1.4  christos 
    446   1.5  christos ATF_TC_BODY(t_spawn_fchdir_abs, tc)
    447   1.4  christos {
    448   1.6  christos 	spawn_chdir(CHDIRPATH, FILEPATH, 2, 0);
    449   1.4  christos 
    450   1.4  christos 	/* finally cross check the directory */
    451   1.4  christos 	check_success(FILEPATH, 0);
    452   1.4  christos }
    453   1.4  christos 
    454   1.5  christos ATF_TC(t_spawn_fchdir_rel);
    455   1.4  christos 
    456   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_rel, tc)
    457   1.4  christos {
    458   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    459   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a relative "
    460   1.5  christos 	    "directory");
    461   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    462   1.4  christos }
    463   1.4  christos 
    464   1.5  christos ATF_TC_BODY(t_spawn_fchdir_rel, tc)
    465   1.4  christos {
    466   1.6  christos 	int error;
    467   1.5  christos 	const char *relative_dir = "ch-dir";
    468   1.5  christos 	const char *testdir = getcwd(NULL, 0);
    469   1.5  christos 	char *chdirwd, *filepath;
    470   1.4  christos 
    471   1.6  christos 	rmdir(relative_dir);
    472   1.5  christos 	error = mkdir(relative_dir, 0755);
    473   1.5  christos 	ATF_REQUIRE(error == 0);
    474   1.5  christos 
    475   1.5  christos 	/*
    476   1.5  christos 	 * This is done in parts purposely.
    477   1.8    andvar 	 * It enables the abs path of the relative dir
    478   1.5  christos 	 * to be passed to 'check_success()' for comparing
    479   1.5  christos 	 */
    480   1.5  christos 	error = asprintf(&chdirwd, "%s/%s", testdir, relative_dir);
    481   1.5  christos 	ATF_CHECK(error != -1);
    482   1.5  christos 
    483   1.5  christos 	error = asprintf(&filepath, "%s/%s", chdirwd, FILENAME);
    484   1.5  christos 	ATF_CHECK(error != -1);
    485   1.5  christos 
    486   1.6  christos 	spawn_chdir(relative_dir, filepath, 2, 0);
    487   1.5  christos 
    488   1.5  christos 	/* finally cross check the directory */
    489   1.5  christos 	check_success(filepath, 1, chdirwd);
    490   1.5  christos 	free(chdirwd);
    491   1.5  christos 	free(filepath);
    492   1.4  christos }
    493   1.4  christos 
    494   1.5  christos ATF_TC(t_spawn_fchdir_file);
    495   1.4  christos 
    496   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_file, tc)
    497   1.4  christos {
    498   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    499   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a "
    500   1.5  christos 	    "regular file (not a directory)");
    501   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    502   1.4  christos }
    503   1.4  christos 
    504   1.5  christos ATF_TC_BODY(t_spawn_fchdir_file, tc)
    505   1.4  christos {
    506   1.6  christos 	int error, fd;
    507   1.4  christos 
    508   1.5  christos 	fd = open(FILEPATH, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    509   1.5  christos 	ATF_REQUIRE_MSG(fd != -1, "Can't open `%s' (%s)", FILEPATH,
    510   1.5  christos 	    strerror(errno));
    511   1.4  christos 
    512   1.6  christos 	error = close(fd);
    513   1.4  christos 	ATF_REQUIRE(error == 0);
    514   1.4  christos 
    515   1.6  christos 	spawn_chdir(FILEPATH, NULL, 2, ENOTDIR);
    516   1.5  christos 
    517   1.5  christos 	unlink(FILEPATH);
    518   1.5  christos 
    519   1.5  christos }
    520   1.5  christos 
    521   1.5  christos ATF_TC(t_spawn_fchdir_neg_fd);
    522   1.5  christos 
    523   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_neg_fd, tc)
    524   1.5  christos {
    525   1.5  christos 	atf_tc_set_md_var(tc, "descr",
    526   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir on a negative file "
    527   1.5  christos 	    "descriptor");
    528   1.5  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    529   1.5  christos }
    530   1.5  christos 
    531   1.5  christos ATF_TC_BODY(t_spawn_fchdir_neg_fd, tc)
    532   1.5  christos {
    533   1.5  christos 	int error, fd;
    534   1.5  christos 	posix_spawn_file_actions_t fa;
    535   1.5  christos 
    536   1.5  christos 	fd = -1;
    537   1.5  christos 
    538   1.5  christos 	error = posix_spawn_file_actions_init(&fa);
    539   1.5  christos 	ATF_REQUIRE(error == 0);
    540   1.5  christos 
    541   1.5  christos 	error = posix_spawn_file_actions_addfchdir(&fa, fd);
    542   1.4  christos 	ATF_REQUIRE(error == EBADF);
    543   1.4  christos 
    544   1.4  christos 	posix_spawn_file_actions_destroy(&fa);
    545   1.4  christos }
    546   1.4  christos 
    547   1.5  christos ATF_TC(t_spawn_fchdir_closed);
    548   1.4  christos 
    549   1.5  christos ATF_TC_HEAD(t_spawn_fchdir_closed, tc)
    550   1.4  christos {
    551   1.4  christos 	atf_tc_set_md_var(tc, "descr",
    552   1.5  christos 	    "Testing posix_spawn_file_actions_addfchdir for a closed fd");
    553   1.4  christos 	atf_tc_set_md_var(tc, "require.progs", "/bin/pwd");
    554   1.4  christos }
    555   1.4  christos 
    556   1.5  christos ATF_TC_BODY(t_spawn_fchdir_closed, tc)
    557   1.4  christos {
    558   1.5  christos 	int error, fd;
    559   1.4  christos 	pid_t pid;
    560   1.4  christos 	char * const args[2] = { __UNCONST("pwd"), NULL };
    561   1.4  christos 	posix_spawnattr_t attr;
    562   1.4  christos 	posix_spawn_file_actions_t fa;
    563   1.4  christos 
    564   1.5  christos 	fd = 3;
    565   1.4  christos 	error = posix_spawnattr_init(&attr);
    566   1.5  christos 	ATF_CHECK(error == 0);
    567   1.4  christos 	/*
    568   1.4  christos 	 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that
    569   1.4  christos 	 * will cause a "proper" return value from posix_spawn(2)
    570   1.4  christos 	 * instead of a (potential) success there and a 127 exit
    571   1.4  christos 	 * status from the child process (c.f. the non-diag variant
    572   1.4  christos 	 * of this test).
    573   1.4  christos 	 */
    574   1.4  christos 	error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR);
    575   1.4  christos 	ATF_REQUIRE(error == 0);
    576   1.4  christos 
    577   1.4  christos 	error = posix_spawn_file_actions_init(&fa);
    578   1.4  christos 	ATF_REQUIRE(error == 0);
    579   1.4  christos 
    580   1.4  christos 	error = posix_spawn_file_actions_addfchdir(&fa, fd);
    581   1.4  christos 	ATF_REQUIRE(error == 0);
    582   1.4  christos 
    583   1.4  christos 	error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL);
    584   1.5  christos 	ATF_REQUIRE(error == EBADF);
    585   1.4  christos 
    586   1.4  christos 	posix_spawn_file_actions_destroy(&fa);
    587   1.4  christos 	posix_spawnattr_destroy(&attr);
    588   1.4  christos }
    589   1.4  christos 
    590   1.9  riastrad ATF_TC(t_spawn_sig);
    591   1.9  riastrad ATF_TC_HEAD(t_spawn_sig, tc)
    592   1.9  riastrad {
    593   1.9  riastrad 	atf_tc_set_md_var(tc, "descr",
    594   1.9  riastrad 	    "Checks that posix_spawn does not drop pending signals");
    595   1.9  riastrad }
    596   1.9  riastrad ATF_TC_BODY(t_spawn_sig, tc)
    597   1.9  riastrad {
    598   1.9  riastrad 	const char *srcdir = atf_tc_get_config_var(tc, "srcdir");
    599   1.9  riastrad 	char h_execsig[PATH_MAX];
    600   1.9  riastrad 	time_t start;
    601   1.9  riastrad 
    602   1.9  riastrad 	snprintf(h_execsig, sizeof(h_execsig), "%s/../h_execsig", srcdir);
    603   1.9  riastrad 	REQUIRE_LIBC(signal(SIGPIPE, SIG_IGN), SIG_ERR);
    604   1.9  riastrad 
    605   1.9  riastrad 	for (start = time(NULL); time(NULL) - start <= 10;) {
    606   1.9  riastrad 		int fd[2];
    607   1.9  riastrad 		char *const argv[] = {h_execsig, NULL};
    608   1.9  riastrad 		pid_t pid;
    609   1.9  riastrad 		int status;
    610   1.9  riastrad 
    611   1.9  riastrad 		RL(pipe(fd));
    612   1.9  riastrad 		RZ(posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL));
    613   1.9  riastrad 		RL(close(fd[0]));
    614   1.9  riastrad 		RL(kill(pid, SIGTERM));
    615   1.9  riastrad 		if (write(fd[1], (char[]){0}, 1) == -1 && errno != EPIPE)
    616   1.9  riastrad 			atf_tc_fail("write failed: %s", strerror(errno));
    617   1.9  riastrad 		RL(waitpid(pid, &status, 0));
    618   1.9  riastrad 		ATF_REQUIRE_MSG(WIFSIGNALED(status),
    619   1.9  riastrad 		    "child exited with status 0x%x", status);
    620   1.9  riastrad 		ATF_REQUIRE_EQ_MSG(WTERMSIG(status), SIGTERM,
    621   1.9  riastrad 		    "child exited on signal %d (%s)",
    622   1.9  riastrad 		    WTERMSIG(status), strsignal(WTERMSIG(status)));
    623   1.9  riastrad 		RL(close(fd[1]));
    624   1.9  riastrad 	}
    625   1.9  riastrad }
    626   1.9  riastrad 
    627   1.7  christos #undef CHDIR
    628   1.7  christos #undef FCHDIR
    629   1.4  christos #undef CHDIRPATH
    630   1.4  christos #undef FILENAME
    631   1.4  christos #undef FILEPATH
    632   1.4  christos 
    633   1.1    martin ATF_TP_ADD_TCS(tp)
    634   1.1    martin {
    635   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_ls);
    636   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawnp_ls);
    637   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_zero);
    638   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_missing);
    639   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_nonexec);
    640   1.1    martin 	ATF_TP_ADD_TC(tp, t_spawn_child);
    641   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_abs);
    642   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_rel);
    643   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_file);
    644   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_invalid);
    645   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_chdir_permissions);
    646   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_abs);
    647   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_rel);
    648   1.5  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_file);
    649   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_neg_fd);
    650   1.4  christos 	ATF_TP_ADD_TC(tp, t_spawn_fchdir_closed);
    651   1.9  riastrad 	ATF_TP_ADD_TC(tp, t_spawn_sig);
    652   1.1    martin 
    653   1.1    martin 	return atf_no_error();
    654   1.1    martin }
    655