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