1 /* $NetBSD: t_spawn.c,v 1.4 2021/11/07 15:46:20 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles Zhang <charles@NetBSD.org> and 9 * Martin Husemann <martin@NetBSD.org>. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: t_spawn.c,v 1.4 2021/11/07 15:46:20 christos Exp $"); 34 35 #include <atf-c.h> 36 37 #include <sys/fcntl.h> 38 #include <sys/types.h> 39 #include <sys/wait.h> 40 #include <sys/stat.h> 41 42 #include <spawn.h> 43 #include <string.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <errno.h> 47 #include <stdarg.h> 48 #include <fcntl.h> 49 #include <unistd.h> 50 51 #include "fa_spawn_utils.h" 52 53 54 static void check_success(const char *, int, ...); 55 56 ATF_TC(t_spawn_ls); 57 58 ATF_TC_HEAD(t_spawn_ls, tc) 59 { 60 atf_tc_set_md_var(tc, "descr", 61 "Tests a simple posix_spawn executing /bin/ls"); 62 } 63 64 ATF_TC_BODY(t_spawn_ls, tc) 65 { 66 char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL }; 67 int err; 68 69 err = posix_spawn(NULL, "/bin/ls", NULL, NULL, args, NULL); 70 ATF_REQUIRE(err == 0); 71 } 72 73 ATF_TC(t_spawnp_ls); 74 75 ATF_TC_HEAD(t_spawnp_ls, tc) 76 { 77 atf_tc_set_md_var(tc, "descr", 78 "Tests a simple posix_spawnp executing ls via $PATH"); 79 } 80 81 ATF_TC_BODY(t_spawnp_ls, tc) 82 { 83 char * const args[] = { __UNCONST("ls"), __UNCONST("-la"), NULL }; 84 int err; 85 86 err = posix_spawnp(NULL, "ls", NULL, NULL, args, NULL); 87 ATF_REQUIRE(err == 0); 88 } 89 90 ATF_TC(t_spawn_zero); 91 92 ATF_TC_HEAD(t_spawn_zero, tc) 93 { 94 atf_tc_set_md_var(tc, "descr", 95 "posix_spawn an invalid binary"); 96 } 97 98 ATF_TC_BODY(t_spawn_zero, tc) 99 { 100 char buf[FILENAME_MAX]; 101 char * const args[] = { __UNCONST("h_zero"), NULL }; 102 int err; 103 104 snprintf(buf, sizeof buf, "%s/h_zero", atf_tc_get_config_var(tc, "srcdir")); 105 err = posix_spawn(NULL, buf, NULL, NULL, args, NULL); 106 ATF_REQUIRE_MSG(err == ENOEXEC, "expected error %d, got %d when spawning %s", ENOEXEC, err, buf); 107 } 108 109 ATF_TC(t_spawn_missing); 110 111 ATF_TC_HEAD(t_spawn_missing, tc) 112 { 113 atf_tc_set_md_var(tc, "descr", 114 "posix_spawn a non existant binary"); 115 } 116 117 ATF_TC_BODY(t_spawn_missing, tc) 118 { 119 char buf[FILENAME_MAX]; 120 char * const args[] = { __UNCONST("h_nonexist"), NULL }; 121 int err; 122 123 snprintf(buf, sizeof buf, "%s/h_nonexist", 124 atf_tc_get_config_var(tc, "srcdir")); 125 err = posix_spawn(NULL, buf, NULL, NULL, args, NULL); 126 ATF_REQUIRE_MSG(err == ENOENT, "expected error %d, got %d when spawning %s", ENOENT, err, buf); 127 } 128 129 ATF_TC(t_spawn_nonexec); 130 131 ATF_TC_HEAD(t_spawn_nonexec, tc) 132 { 133 atf_tc_set_md_var(tc, "descr", 134 "posix_spawn a script with non existing interpreter"); 135 } 136 137 ATF_TC_BODY(t_spawn_nonexec, tc) 138 { 139 char buf[FILENAME_MAX]; 140 char * const args[] = { __UNCONST("h_nonexec"), NULL }; 141 int err; 142 143 snprintf(buf, sizeof buf, "%s/h_nonexec", 144 atf_tc_get_config_var(tc, "srcdir")); 145 err = posix_spawn(NULL, buf, NULL, NULL, args, NULL); 146 ATF_REQUIRE_MSG(err == ENOENT, "expected error %d, got %d when spawning %s", ENOENT, err, buf); 147 } 148 149 ATF_TC(t_spawn_child); 150 151 ATF_TC_HEAD(t_spawn_child, tc) 152 { 153 atf_tc_set_md_var(tc, "descr", 154 "posix_spawn a child and get its return code"); 155 } 156 157 ATF_TC_BODY(t_spawn_child, tc) 158 { 159 char buf[FILENAME_MAX]; 160 char * const args0[] = { __UNCONST("h_spawn"), __UNCONST("0"), NULL }; 161 char * const args1[] = { __UNCONST("h_spawn"), __UNCONST("1"), NULL }; 162 char * const args7[] = { __UNCONST("h_spawn"), __UNCONST("7"), NULL }; 163 int err, status; 164 pid_t pid; 165 166 snprintf(buf, sizeof buf, "%s/h_spawn", 167 atf_tc_get_config_var(tc, "srcdir")); 168 169 err = posix_spawn(&pid, buf, NULL, NULL, args0, NULL); 170 ATF_REQUIRE(err == 0); 171 ATF_REQUIRE(pid > 0); 172 waitpid(pid, &status, 0); 173 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 0); 174 175 err = posix_spawn(&pid, buf, NULL, NULL, args1, NULL); 176 ATF_REQUIRE(err == 0); 177 ATF_REQUIRE(pid > 0); 178 waitpid(pid, &status, 0); 179 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 1); 180 181 err = posix_spawn(&pid, buf, NULL, NULL, args7, NULL); 182 ATF_REQUIRE(err == 0); 183 ATF_REQUIRE(pid > 0); 184 waitpid(pid, &status, 0); 185 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 7); 186 } 187 188 #define CHDIRPATH "/tmp" 189 #define FILENAME "output" 190 #define FILEPATH "/tmp/output" 191 192 static void 193 check_success(const char *file, int argc, ...) 194 { 195 va_list ap; 196 int bytesRead, fd; 197 size_t sizeOfFile = (size_t)filesize(file); 198 size_t sizeOfStr; 199 char *contents; 200 const char *dir; 201 202 contents = malloc(sizeOfFile); 203 ATF_REQUIRE(contents != NULL); 204 205 /* 206 * for now only 1 variadic argument expected 207 * only from t_spawn_chdir_rel 208 */ 209 if (argc != 0) { 210 va_start(ap, argc); 211 dir = va_arg(ap, char *); 212 ATF_REQUIRE(dir != NULL); 213 va_end(ap); 214 } else 215 dir = CHDIRPATH; 216 217 fd = open(file, O_RDONLY); 218 ATF_REQUIRE_MSG(fd != -1, "Can't open `%s' (%s)", file, strerror(errno)); 219 220 /* 221 * file contains form feed i.e ASCII - 10 at the end. 222 * Therefore sizeOfFile - 1 223 */ 224 sizeOfStr = strlen(dir); 225 ATF_CHECK_MSG(sizeOfStr == sizeOfFile - 1, "%zu (%s) != %zu (%s)", 226 sizeOfStr, dir, sizeOfFile - 1, file); 227 228 bytesRead = read(fd, contents, sizeOfFile - 1); 229 contents[sizeOfFile - 1] = '\0'; 230 ATF_REQUIRE_MSG(strcmp(dir, contents) == 0, 231 "[%s] != [%s] Directories dont match", dir, contents); 232 233 fd = close(fd); 234 ATF_REQUIRE(fd == 0); 235 236 unlink(file); 237 free(contents); 238 239 /* XXX not really required */ 240 ATF_REQUIRE(bytesRead = sizeOfStr); 241 } 242 243 ATF_TC(t_spawn_chdir_abs); 244 245 ATF_TC_HEAD(t_spawn_chdir_abs, tc) 246 { 247 atf_tc_set_md_var(tc, "descr", 248 "Test posix_spawn_fa_addchdir for absolute path"); 249 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 250 } 251 252 ATF_TC_BODY(t_spawn_chdir_abs, tc) 253 { 254 char * const args[2] = { __UNCONST("pwd"), NULL }; 255 int error, status; 256 pid_t pid; 257 posix_spawn_file_actions_t fa; 258 259 empty_outfile(FILEPATH); 260 261 error = posix_spawn_file_actions_init(&fa); 262 ATF_REQUIRE(error == 0); 263 264 error = posix_spawn_file_actions_addchdir(&fa, CHDIRPATH); 265 ATF_REQUIRE(error == 0); 266 267 error = posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, FILENAME, 268 O_WRONLY, 0); 269 ATF_REQUIRE(error == 0); 270 271 error = posix_spawn(&pid, "/bin/pwd", &fa, NULL, args, NULL); 272 ATF_REQUIRE(error == 0); 273 274 /* wait for the child to finish */ 275 waitpid(pid, &status, 0); 276 ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS, 277 "%s", "chdir failed"); 278 posix_spawn_file_actions_destroy(&fa); 279 280 /* finally cross check the output of "pwd" directory */ 281 check_success(FILEPATH, 0); 282 } 283 284 ATF_TC(t_spawn_chdir_rel); 285 286 ATF_TC_HEAD(t_spawn_chdir_rel, tc) 287 { 288 atf_tc_set_md_var(tc, "descr", 289 "Test posix_spawn_fa_addchdir for relative path"); 290 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 291 } 292 293 294 ATF_TC_BODY(t_spawn_chdir_rel, tc) 295 { 296 const char *relative_dir = "ch-dir"; 297 const char *testdir = getcwd(NULL, 0); 298 char * const args[2] = { __UNCONST("pwd"), NULL }; 299 char *chdirwd, *filepath; 300 int error, status; 301 pid_t pid; 302 posix_spawn_file_actions_t fa; 303 304 /* cleanup previous */ 305 error = asprintf(&filepath, "%s/%s", relative_dir, FILENAME); 306 ATF_CHECK(error != -1); 307 unlink(filepath); 308 free(filepath); 309 rmdir(relative_dir); 310 311 error = mkdir(relative_dir, 0777); 312 ATF_REQUIRE_MSG(error == 0, "mkdir `%s' (%s)", relative_dir, 313 strerror(errno)); 314 315 error = asprintf(&chdirwd, "%s/%s", testdir, relative_dir); 316 ATF_CHECK(error != -1); 317 318 error = asprintf(&filepath, "%s/%s", chdirwd, FILENAME); 319 ATF_CHECK(error != -1); 320 321 #ifdef DEBUG 322 printf("cwd: %s\n", testdir); 323 printf("chdirwd: %s\n", chdirwd); 324 printf("filepath: %s\n", filepath); 325 #endif 326 327 empty_outfile(filepath); 328 329 error = posix_spawn_file_actions_init(&fa); 330 ATF_REQUIRE(error == 0); 331 332 error = posix_spawn_file_actions_addchdir(&fa, relative_dir); 333 ATF_REQUIRE(error == 0); 334 335 error = posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, FILENAME, 336 O_WRONLY, 0); 337 ATF_REQUIRE(error == 0); 338 339 error = posix_spawn(&pid, "/bin/pwd", &fa, NULL, args, NULL); 340 ATF_REQUIRE(error == 0); 341 342 /* wait for the child to finish */ 343 waitpid(pid, &status, 0); 344 ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS, 345 "%s", "chdir failed"); 346 posix_spawn_file_actions_destroy(&fa); 347 348 /* finally cross check the directory */ 349 check_success(filepath, 1, chdirwd); 350 free(chdirwd); 351 free(filepath); 352 353 rmdir(relative_dir); 354 } 355 356 ATF_TC(t_spawn_chdir_file); 357 358 ATF_TC_HEAD(t_spawn_chdir_file, tc) 359 { 360 atf_tc_set_md_var(tc, "descr", 361 "Test posix_spawn_fa_addchdir on plain file (not a directory)"); 362 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 363 } 364 365 ATF_TC_BODY(t_spawn_chdir_file, tc) 366 { 367 int error; 368 pid_t pid; 369 char * const args[2] = { __UNCONST("pwd"), NULL }; 370 posix_spawnattr_t attr; 371 posix_spawn_file_actions_t fa; 372 373 empty_outfile(FILEPATH); 374 375 error = posix_spawnattr_init(&attr); 376 ATF_REQUIRE(error == 0); 377 /* 378 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 379 * will cause a "proper" return value from posix_spawn(2) 380 * instead of a (potential) success there and a 127 exit 381 * status from the child process (c.f. the non-diag variant 382 * of this test). 383 */ 384 error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 385 ATF_REQUIRE(error == 0); 386 387 error = posix_spawn_file_actions_init(&fa); 388 ATF_REQUIRE(error == 0); 389 390 error = posix_spawn_file_actions_addchdir(&fa, FILEPATH); 391 ATF_REQUIRE(error == 0); 392 393 error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL); 394 ATF_REQUIRE(error == ENOTDIR); 395 396 posix_spawn_file_actions_destroy(&fa); 397 posix_spawnattr_destroy(&attr); 398 399 unlink(FILEPATH); 400 } 401 402 ATF_TC(t_spawn_chdir_invalid); 403 404 ATF_TC_HEAD(t_spawn_chdir_invalid, tc) 405 { 406 atf_tc_set_md_var(tc, "descr", 407 "Test posix_spawn_fa_addchdir for an invalid dir"); 408 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 409 } 410 411 ATF_TC_BODY(t_spawn_chdir_invalid, tc) 412 { 413 int error; 414 pid_t pid; 415 const char *dirpath = "/not/a/valid/dir"; 416 char * const args[2] = { __UNCONST("pwd"), NULL }; 417 posix_spawnattr_t attr; 418 posix_spawn_file_actions_t fa; 419 420 error = posix_spawnattr_init(&attr); 421 ATF_REQUIRE(error == 0); 422 /* 423 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 424 * will cause a "proper" return value from posix_spawn(2) 425 * instead of a (potential) success there and a 127 exit 426 * status from the child process (c.f. the non-diag variant 427 * of this test). 428 */ 429 error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 430 ATF_REQUIRE(error == 0); 431 432 error = posix_spawn_file_actions_init(&fa); 433 ATF_REQUIRE(error == 0); 434 435 error = posix_spawn_file_actions_addchdir(&fa, dirpath); 436 ATF_REQUIRE(error == 0); 437 438 error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL); 439 ATF_REQUIRE(error == ENOENT); 440 441 posix_spawn_file_actions_destroy(&fa); 442 posix_spawnattr_destroy(&attr); 443 } 444 445 ATF_TC(t_spawn_chdir_permissions); 446 447 ATF_TC_HEAD(t_spawn_chdir_permissions, tc) 448 { 449 atf_tc_set_md_var(tc, "descr", 450 "Test posix_spawn_file_actions_addchdir for prohibited directory"); 451 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 452 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 453 } 454 455 ATF_TC_BODY(t_spawn_chdir_permissions, tc) 456 { 457 int error; 458 pid_t pid; 459 const char *restrRelDir = "prohibited"; 460 char * const args[2] = { __UNCONST("pwd"), NULL }; 461 posix_spawnattr_t attr; 462 posix_spawn_file_actions_t fa; 463 464 error = mkdir(restrRelDir, 0055); 465 ATF_REQUIRE(error == 0); 466 467 posix_spawnattr_init(&attr); 468 ATF_REQUIRE(error == 0); 469 /* 470 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 471 * will cause a "proper" return value from posix_spawn(2) 472 * instead of a (potential) success there and a 127 exit 473 * status from the child process (c.f. the non-diag variant 474 * of this test). 475 */ 476 error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 477 ATF_REQUIRE(error == 0); 478 479 error = posix_spawn_file_actions_init(&fa); 480 ATF_REQUIRE(error == 0); 481 482 error = posix_spawn_file_actions_addchdir(&fa, restrRelDir); 483 ATF_REQUIRE(error == 0); 484 485 error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL); 486 ATF_CHECK(error == EACCES); 487 488 posix_spawn_file_actions_destroy(&fa); 489 posix_spawnattr_destroy(&attr); 490 491 rmdir(restrRelDir); 492 } 493 494 495 ATF_TC(t_spawn_fchdir); 496 497 ATF_TC_HEAD(t_spawn_fchdir, tc) 498 { 499 atf_tc_set_md_var(tc, "descr", "Test posix_spawn_fa_fchdir"); 500 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 501 } 502 503 ATF_TC_BODY(t_spawn_fchdir, tc) 504 { 505 int error, fd, status; 506 pid_t pid; 507 char * const args[2] = { __UNCONST("pwd"), NULL }; 508 posix_spawn_file_actions_t fa; 509 510 empty_outfile(FILEPATH); 511 512 fd = open(CHDIRPATH, O_RDONLY); 513 ATF_REQUIRE(fd >= 0); 514 515 error = posix_spawn_file_actions_init(&fa); 516 ATF_REQUIRE(error == 0); 517 518 error = posix_spawn_file_actions_addfchdir(&fa, fd); 519 ATF_REQUIRE(error == 0); 520 521 error = posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, FILENAME, 522 O_WRONLY, 0); 523 ATF_REQUIRE(error == 0); 524 525 error = posix_spawn(&pid, "/bin/pwd", &fa, NULL, args, NULL); 526 ATF_REQUIRE(error == 0); 527 528 /* wait for the child to finish */ 529 waitpid(pid, &status, 0); 530 ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS, 531 "%s", "chdir failed"); 532 posix_spawn_file_actions_destroy(&fa); 533 534 /* The file should be closed before reopening in 'check_success' */ 535 error = close(fd); 536 ATF_REQUIRE(error == 0); 537 538 /* finally cross check the directory */ 539 check_success(FILEPATH, 0); 540 } 541 542 ATF_TC(t_spawn_fchdir_neg_fd); 543 544 ATF_TC_HEAD(t_spawn_fchdir_neg_fd, tc) 545 { 546 atf_tc_set_md_var(tc, "descr", 547 "Testing posix_spawn_file_actions_addfchdir on a negative file " 548 "descriptor"); 549 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 550 } 551 552 ATF_TC_BODY(t_spawn_fchdir_neg_fd, tc) 553 { 554 int error, fd; 555 posix_spawn_file_actions_t fa; 556 557 fd = -1; 558 559 error = posix_spawn_file_actions_init(&fa); 560 ATF_REQUIRE(error == 0); 561 562 error = posix_spawn_file_actions_addfchdir(&fa, fd); 563 ATF_REQUIRE(error == EBADF); 564 565 posix_spawn_file_actions_destroy(&fa); 566 } 567 568 ATF_TC(t_spawn_fchdir_closed); 569 570 ATF_TC_HEAD(t_spawn_fchdir_closed, tc) 571 { 572 atf_tc_set_md_var(tc, "descr", 573 "Testing posix_spawn_file_actions_addfchdir for a closed fd"); 574 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 575 } 576 577 ATF_TC_BODY(t_spawn_fchdir_closed, tc) 578 { 579 int error, fd; 580 pid_t pid; 581 char * const args[2] = { __UNCONST("pwd"), NULL }; 582 posix_spawnattr_t attr; 583 posix_spawn_file_actions_t fa; 584 585 /* 586 * fd = open(CHDIRPATH, O_RDONLY); 587 * ATF_REQUIRE(fd >= 0); 588 * error = close(fd); 589 * ATF_REQUIRE(error == 0); 590 */ 591 fd = 3; 592 593 error = posix_spawnattr_init(&attr); 594 ATF_CHECK(error == 0); 595 /* 596 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 597 * will cause a "proper" return value from posix_spawn(2) 598 * instead of a (potential) success there and a 127 exit 599 * status from the child process (c.f. the non-diag variant 600 * of this test). 601 */ 602 error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 603 ATF_REQUIRE(error == 0); 604 605 error = posix_spawn_file_actions_init(&fa); 606 ATF_REQUIRE(error == 0); 607 608 error = posix_spawn_file_actions_addfchdir(&fa, fd); 609 ATF_REQUIRE(error == 0); 610 611 error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL); 612 ATF_REQUIRE(error == EBADF); 613 614 posix_spawn_file_actions_destroy(&fa); 615 posix_spawnattr_destroy(&attr); 616 } 617 618 ATF_TC(t_spawn_fchdir_file); 619 620 ATF_TC_HEAD(t_spawn_fchdir_file, tc) 621 { 622 atf_tc_set_md_var(tc, "descr", 623 "Testing posix_spawn_file_actions_addfchdir on a " 624 "regular file (not a directory)"); 625 atf_tc_set_md_var(tc, "require.progs", "/bin/pwd"); 626 } 627 628 ATF_TC_BODY(t_spawn_fchdir_file, tc) 629 { 630 int error, fd; 631 pid_t pid; 632 char * const args[2] = { __UNCONST("pwd"), NULL }; 633 posix_spawnattr_t attr; 634 posix_spawn_file_actions_t fa; 635 636 fd = open(FILEPATH, O_WRONLY | O_CREAT | O_TRUNC, 0644); 637 ATF_REQUIRE_MSG(fd != -1, "Can't open `%s' (%s)", FILEPATH, 638 strerror(errno)); 639 640 error = posix_spawnattr_init(&attr); 641 ATF_REQUIRE(error == 0); 642 /* 643 * POSIX_SPAWN_RETURNERROR is a NetBSD specific flag that 644 * will cause a "proper" return value from posix_spawn(2) 645 * instead of a (potential) success there and a 127 exit 646 * status from the child process (c.f. the non-diag variant 647 * of this test). 648 */ 649 error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RETURNERROR); 650 ATF_REQUIRE(error == 0); 651 652 error = posix_spawn_file_actions_init(&fa); 653 ATF_REQUIRE(error == 0); 654 655 error = posix_spawn_file_actions_addfchdir(&fa, fd); 656 ATF_REQUIRE(error == 0); 657 658 error = posix_spawn(&pid, "/bin/pwd", &fa, &attr, args, NULL); 659 ATF_CHECK(error == ENOTDIR); 660 661 posix_spawn_file_actions_destroy(&fa); 662 posix_spawnattr_destroy(&attr); 663 664 error = close(fd); 665 ATF_REQUIRE(error == 0); 666 667 unlink(FILEPATH); 668 669 } 670 671 #undef CHDIRPATH 672 #undef FILENAME 673 #undef FILEPATH 674 675 ATF_TP_ADD_TCS(tp) 676 { 677 ATF_TP_ADD_TC(tp, t_spawn_ls); 678 ATF_TP_ADD_TC(tp, t_spawnp_ls); 679 ATF_TP_ADD_TC(tp, t_spawn_zero); 680 ATF_TP_ADD_TC(tp, t_spawn_missing); 681 ATF_TP_ADD_TC(tp, t_spawn_nonexec); 682 ATF_TP_ADD_TC(tp, t_spawn_child); 683 ATF_TP_ADD_TC(tp, t_spawn_chdir_abs); 684 ATF_TP_ADD_TC(tp, t_spawn_chdir_rel); 685 ATF_TP_ADD_TC(tp, t_spawn_chdir_file); 686 ATF_TP_ADD_TC(tp, t_spawn_chdir_invalid); 687 ATF_TP_ADD_TC(tp, t_spawn_chdir_permissions); 688 ATF_TP_ADD_TC(tp, t_spawn_fchdir); 689 ATF_TP_ADD_TC(tp, t_spawn_fchdir_neg_fd); 690 ATF_TP_ADD_TC(tp, t_spawn_fchdir_closed); 691 ATF_TP_ADD_TC(tp, t_spawn_fchdir_file); 692 693 return atf_no_error(); 694 } 695