t_spawn.c revision 1.4 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 (at) NetBSD.org> and
9 * Martin Husemann <martin (at) 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