1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Various tests for posix_spawn(3C). Currently this mostly focuses on
18 * functionality added in POSIX 2024 which relates to SETSID and changing
19 * directories.
20 */
21
22 #include <err.h>
23 #include <stdlib.h>
24 #include <spawn.h>
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <unistd.h>
28 #include <sys/sysmacros.h>
29 #include <sys/debug.h>
30 #include <wait.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <inttypes.h>
37
38 /*
39 * This isn't const so we can refer to it in the argv arrays.
40 */
41 static char *spawn_pwd = "/usr/bin/pwd";
42 static char spawn_getsid[PATH_MAX];
43
44 /*
45 * This is an arbitrary fd that we believe will be okay to use in fchdir tests
46 * to overwrite.
47 */
48 #define SPAWN_FD 23
49
50 typedef struct spawn_dir_test {
51 const char *sdt_desc;
52 bool sdt_pass;
53 const char *sdt_pwd;
54 const char *sdt_dirs[16];
55 } spawn_dir_test_t;
56
57 static const spawn_dir_test_t spawn_dir_tests[] = {
58 {
59 .sdt_desc = "no chdir",
60 .sdt_pass = true,
61 .sdt_pwd = "/var/tmp"
62 }, {
63 .sdt_desc = "absolute path: /etc",
64 .sdt_pass = true,
65 .sdt_pwd = "/etc",
66 .sdt_dirs = { "/etc" }
67 }, {
68 .sdt_desc = "multiple absolute paths (1)",
69 .sdt_pass = true,
70 .sdt_pwd = "/dev/net",
71 .sdt_dirs = { "/etc", "/dev/net" }
72 }, {
73 .sdt_desc = "multiple absolute paths (2)",
74 .sdt_pass = true,
75 .sdt_pwd = "/var/svc",
76 .sdt_dirs = { "/proc/self", "/var/svc" }
77 }, {
78 .sdt_desc = "single relative path (1)",
79 .sdt_pass = true,
80 .sdt_pwd = "/var/tmp",
81 .sdt_dirs = { "." },
82 }, {
83 .sdt_desc = "single relative path (2)",
84 .sdt_pass = true,
85 .sdt_pwd = "/var",
86 .sdt_dirs = { ".." },
87 }, {
88 .sdt_desc = "multiple relative paths (1)",
89 .sdt_pass = true,
90 .sdt_pwd = "/usr/lib/dtrace",
91 .sdt_dirs = { "..", "..", "usr", "lib", "dtrace" },
92 }, {
93 .sdt_desc = "multiple relative paths (2)",
94 .sdt_pass = true,
95 .sdt_pwd = "/var/tmp",
96 .sdt_dirs = { "..", "tmp" },
97 }, {
98 .sdt_desc = "mixing absolute and relative paths (1)",
99 .sdt_pass = true,
100 .sdt_pwd = "/usr/lib/fm/fmd",
101 .sdt_dirs = { "..", "/usr/lib/fm", "fmd" },
102 }, {
103 .sdt_desc = "mixing absolute and relative paths (2)",
104 .sdt_pass = true,
105 .sdt_pwd = "/usr/bin",
106 .sdt_dirs = { "/usr/lib/64", "..", "..", "bin" },
107 }, {
108 .sdt_desc = "mixing absolute and relative paths (3)",
109 .sdt_pass = true,
110 .sdt_pwd = "/etc/svc/volatile",
111 .sdt_dirs = { "/usr/lib/64", "..", "..", "bin",
112 "/etc/svc/volatile" },
113 }, {
114 /*
115 * Note, these bad path tests will not be terribly meaningful
116 * for fchdir because the open will fail.
117 */
118 .sdt_desc = "bad path 1",
119 .sdt_pass = false,
120 .sdt_dirs = { "/#error//?*!@#$!asdf/please/don't/exist" }
121 }, {
122 .sdt_desc = "bad path 2",
123 .sdt_pass = false,
124 .sdt_dirs = { "/tmp", "\x001\x002\x003\x004\x003\x042" }
125 }
126 };
127
128 typedef struct spawn_flags_test {
129 const char *sft_desc;
130 int sft_ret;
131 short sft_flags;
132 } spawn_flags_test_t;
133
134 static const spawn_flags_test_t spawn_flags_tests[] = {
135 {
136 .sft_desc = "no flags",
137 .sft_ret = 0,
138 .sft_flags = 0
139 }, {
140 .sft_desc = "flag SETPGROUP",
141 .sft_ret = 0,
142 .sft_flags = POSIX_SPAWN_SETPGROUP
143 }, {
144 .sft_desc = "flag SETSID",
145 .sft_ret = 0,
146 .sft_flags = POSIX_SPAWN_SETSID
147 }, {
148 .sft_desc = "flags SETSID | SETPGROUP",
149 .sft_ret = EPERM,
150 .sft_flags = POSIX_SPAWN_SETSID | POSIX_SPAWN_SETPGROUP
151 }
152 };
153
154 /*
155 * Add standard actions to capture stdout but nothing else.
156 */
157 static void
posix_spawn_setup_fds(posix_spawn_file_actions_t * acts,int pipes[2])158 posix_spawn_setup_fds(posix_spawn_file_actions_t *acts, int pipes[2])
159 {
160 int ret;
161
162 if (pipe2(pipes, O_NONBLOCK) != 0) {
163 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to create a "
164 "pipe");
165 }
166
167 VERIFY3S(pipes[0], >, STDERR_FILENO);
168 VERIFY3S(pipes[1], >, STDERR_FILENO);
169
170 if ((ret = posix_spawn_file_actions_init(acts)) != 0) {
171 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to "
172 "initialize posix_spawn file actions");
173 }
174
175 if ((ret = posix_spawn_file_actions_addopen(acts, STDIN_FILENO,
176 "/dev/null", O_RDONLY, 0)) != 0) {
177 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to add "
178 "/dev/null open action");
179 }
180
181 if ((ret = posix_spawn_file_actions_adddup2(acts, STDIN_FILENO,
182 STDERR_FILENO)) != 0) {
183 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to add "
184 "stderr dup action");
185 }
186
187 if ((ret = posix_spawn_file_actions_adddup2(acts, pipes[1],
188 STDOUT_FILENO)) != 0) {
189 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to add "
190 "stdout dup action");
191 }
192
193 if ((ret = posix_spawn_file_actions_addclose(acts, pipes[0])) != 0) {
194 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to add "
195 "pipes[0] close action");
196 }
197
198 if ((ret = posix_spawn_file_actions_addclose(acts, pipes[1])) != 0) {
199 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to add "
200 "pipes[1] close action");
201 }
202 }
203
204 static bool
posix_spawn_test_one_dir(const spawn_dir_test_t * test,int pipes[2],posix_spawn_file_actions_t * acts,const char * desc)205 posix_spawn_test_one_dir(const spawn_dir_test_t *test, int pipes[2],
206 posix_spawn_file_actions_t *acts, const char *desc)
207 {
208 int ret;
209 bool bret = false;
210 char *const argv[2] = { spawn_pwd, NULL };
211 char *const envp[1] = { NULL };
212 pid_t pid;
213 siginfo_t sig;
214 char pwd[PATH_MAX];
215 ssize_t pwd_len;
216
217 if ((ret = posix_spawn(&pid, spawn_pwd, acts, NULL, argv, envp)) != 0) {
218 if (!test->sdt_pass) {
219 (void) printf("TEST PASSED: %s (%s): posix_spawn "
220 "failed as expected\n", test->sdt_desc, desc);
221 bret = true;
222 goto out;
223 } else {
224 warnx("TEST FAILED: %s posix_spawn() failed with %s, "
225 "but expected success", test->sdt_desc,
226 strerrorname_np(ret));
227 goto out;
228 }
229 }
230
231 if (waitid(P_PID, pid, &sig, WEXITED) != 0) {
232 err(EXIT_FAILURE, "INTERNAL TEST ERROR: %s: failed to wait on "
233 "pid %" _PRIdID ", but posix_spawn executed it",
234 test->sdt_desc, pid);
235 }
236
237 if (sig.si_code != CLD_EXITED) {
238 warnx("TEST FAILED: %s: child did not successfully exit: "
239 "foud si_code: %d", test->sdt_desc, sig.si_code);
240 goto out;
241 }
242
243 if (sig.si_status != 0) {
244 if (!test->sdt_pass) {
245 (void) printf("TEST PASSED: %s (%s): child process "
246 "failed", test->sdt_desc, desc);
247 bret = true;
248 goto out;
249 }
250
251 warnx("TEST FAILED: %s: child exited with status %d, expected "
252 "success", test->sdt_desc, sig.si_status);
253 goto out;
254 } else if (!test->sdt_pass) {
255 warnx("TEST FAILED: %s: child exited successfully, but "
256 "expected failure", test->sdt_desc);
257 goto out;
258 }
259
260 /*
261 * At this point we know that we have a pwd process that has
262 * successfully exited. We should be able to perform a non-blocking read
263 * from the pipe successfully and get its working directory. pwd(1)
264 * appends a new line. We remove it.
265 */
266 pwd[0] = 0;
267 pwd_len = read(pipes[0], pwd, sizeof (pwd));
268 if (pwd_len < 0) {
269 warn("TEST FAILED: %s: failed to read pwd from pipe",
270 test->sdt_desc);
271 goto out;
272 } else if (pwd_len == 0) {
273 warn("TEST FAILED: %s: got zero byte read from pipe?!",
274 test->sdt_desc);
275 goto out;
276 }
277 pwd[pwd_len - 1] = '\0';
278
279 if (strcmp(pwd, test->sdt_pwd) != 0) {
280 warnx("TEST FAILED: %s: found pwd '%s', expected '%s'",
281 test->sdt_desc, pwd, test->sdt_pwd);
282 goto out;
283 }
284
285 (void) printf("TEST PASSED: %s (%s)\n", test->sdt_desc, desc);
286
287 bret = true;
288 out:
289 return (bret);
290 }
291
292 static bool
posix_spawn_test_one_chdir(const spawn_dir_test_t * test)293 posix_spawn_test_one_chdir(const spawn_dir_test_t *test)
294 {
295 int ret, pipes[2];
296 bool bret = false;
297 posix_spawn_file_actions_t acts;
298
299 /*
300 * We set up a pipe to act as stdout so we can capture the output from
301 * pwd. While we could use /proc to try and do this, we prefer this
302 * mechanism.
303 */
304 posix_spawn_setup_fds(&acts, pipes);
305
306 for (size_t i = 0; i < ARRAY_SIZE(test->sdt_dirs); i++) {
307 if (test->sdt_dirs[i] == NULL)
308 break;
309
310 ret = posix_spawn_file_actions_addchdir(&acts,
311 test->sdt_dirs[i]);
312 if (ret != 0) {
313 warnc(ret, "TEST FAILED: %s: adding path '%s' "
314 "(%zu) failed unexpectedly", test->sdt_desc,
315 test->sdt_dirs[i], i);
316 goto out;
317 }
318 }
319
320 bret = posix_spawn_test_one_dir(test, pipes, &acts, "chdir");
321 out:
322 VERIFY0(posix_spawn_file_actions_destroy(&acts));
323 VERIFY0(close(pipes[1]));
324 VERIFY0(close(pipes[0]));
325 return (bret);
326 }
327
328 static bool
posix_spawn_test_one_fchdir(const spawn_dir_test_t * test)329 posix_spawn_test_one_fchdir(const spawn_dir_test_t *test)
330 {
331 int ret, pipes[2];
332 bool bret = false;
333 posix_spawn_file_actions_t acts;
334
335 /*
336 * We set up a pipe to act as stdout so we can capture the output from
337 * pwd. While we could use /proc to try and do this, we prefer this
338 * mechanism.
339 */
340 posix_spawn_setup_fds(&acts, pipes);
341
342 /*
343 * For the fchdir tests we go in a loop over these directories opening
344 * an fd, doing an fchdir to it, and then closing it.
345 */
346 for (size_t i = 0; i < ARRAY_SIZE(test->sdt_dirs); i++) {
347 if (test->sdt_dirs[i] == NULL)
348 break;
349
350 ret = posix_spawn_file_actions_addopen(&acts, SPAWN_FD,
351 test->sdt_dirs[i], O_RDONLY | O_DIRECTORY, 0);
352 if (ret != 0) {
353 warnc(ret, "TEST FAILED: %s: adding open action for "
354 "path '%s' (%zu) failed unexpectedly",
355 test->sdt_desc, test->sdt_dirs[i], i);
356 goto out;
357 }
358
359 ret = posix_spawn_file_actions_addfchdir(&acts, SPAWN_FD);
360 if (ret != 0) {
361 warnc(ret, "TEST FAILED: %s: adding fchdir action for "
362 "path '%s' (%zu) failed unexpectedly",
363 test->sdt_desc, test->sdt_dirs[i], i);
364 goto out;
365 }
366
367 ret = posix_spawn_file_actions_addclose(&acts, SPAWN_FD);
368 if (ret != 0) {
369 warnc(ret, "TEST FAILED: %s: adding close action for "
370 "path '%s' (%zu) failed unexpectedly",
371 test->sdt_desc, test->sdt_dirs[i], i);
372 goto out;
373 }
374 }
375
376 bret = posix_spawn_test_one_dir(test, pipes, &acts, "fchdir");
377 out:
378 VERIFY0(posix_spawn_file_actions_destroy(&acts));
379 VERIFY0(close(pipes[1]));
380 VERIFY0(close(pipes[0]));
381 return (bret);
382 }
383
384 /*
385 * Test a few different bad file actions.
386 */
387 static bool
posix_spawn_test_bad_actions(void)388 posix_spawn_test_bad_actions(void)
389 {
390 int ret;
391 bool bret = true;
392 posix_spawn_file_actions_t acts;
393
394 if ((ret = posix_spawn_file_actions_init(&acts)) != 0) {
395 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to "
396 "initialize posix_spawn file actions");
397 }
398
399 if ((ret = posix_spawn_file_actions_addfchdir(&acts, -23)) == 0) {
400 warnx("TEST FAILED: addfchdir() with bad fd: expected EBADF, "
401 "but returned successfully");
402 bret = false;
403 } else if (ret != EBADF) {
404 warnx("TEST FAILED: addfchdir with bad fd: failed with %s, "
405 "but expected EBADF", strerrorname_np(ret));
406 bret = false;
407 } else {
408 (void) printf("TEST PASSED: addfchdir() with bad fd: correctly "
409 "got EBADF\n");
410 }
411
412 if ((ret = posix_spawn_file_actions_addopen(&acts, -23, "/dev/null",
413 O_RDONLY, 0)) == 0) {
414 warnx("TEST FAILED: addopen() with bad fd: expected EBADF, "
415 "but returned successfully");
416 bret = false;
417 } else if (ret != EBADF) {
418 warnx("TEST FAILED: addopen with bad fd: failed with %s, "
419 "but expected EBADF", strerrorname_np(ret));
420 bret = false;
421 } else {
422 (void) printf("TEST PASSED: addopen() with bad fd: correctly "
423 "got EBADF\n");
424 }
425
426 if ((ret = posix_spawn_file_actions_addclose(&acts, -23)) == 0) {
427 warnx("TEST FAILED: addclose() with bad fd: expected EBADF, "
428 "but returned successfully");
429 bret = false;
430 } else if (ret != EBADF) {
431 warnx("TEST FAILED: addclose with bad fd: failed with %s, "
432 "but expected EBADF", strerrorname_np(ret));
433 bret = false;
434 } else {
435 (void) printf("TEST PASSED: addclose() with bad fd: correctly "
436 "got EBADF\n");
437 }
438
439 VERIFY0(posix_spawn_file_actions_destroy(&acts));
440 return (bret);
441 }
442
443 /*
444 * Verify that if we try to do an fchdir to an invalid fd that everything fails.
445 */
446 static bool
posix_spawn_test_bad_fchdir(void)447 posix_spawn_test_bad_fchdir(void)
448 {
449 int ret, pipes[2];
450 bool bret = false;
451 posix_spawn_file_actions_t acts;
452 spawn_dir_test_t test;
453
454 (void) memset(&test, 0, sizeof (test));
455 test.sdt_desc = "fchdir to closed fd";
456 test.sdt_pass = false;
457 test.sdt_pwd = "/nope";
458
459 /*
460 * We set up a pipe to act as stdout so we can capture the output from
461 * pwd. While we could use /proc to try and do this, we prefer this
462 * mechanism.
463 */
464 posix_spawn_setup_fds(&acts, pipes);
465
466 ret = posix_spawn_file_actions_addclose(&acts, SPAWN_FD);
467 if (ret != 0) {
468 warnc(ret, "TEST FAILED: %s: adding close action failed "
469 "unexpectedly", test.sdt_desc);
470 goto out;
471 }
472
473 ret = posix_spawn_file_actions_addfchdir(&acts, SPAWN_FD);
474 if (ret != 0) {
475 warnc(ret, "TEST FAILED: %s: adding close action failed "
476 "unexpectedly", test.sdt_desc);
477 goto out;
478 }
479
480 bret = posix_spawn_test_one_dir(&test, pipes, &acts, "fchdir");
481 out:
482 VERIFY0(posix_spawn_file_actions_destroy(&acts));
483 VERIFY0(close(pipes[1]));
484 VERIFY0(close(pipes[0]));
485 return (bret);
486 }
487
488
489 static bool
posix_spawn_test_one_flags(const spawn_flags_test_t * test)490 posix_spawn_test_one_flags(const spawn_flags_test_t *test)
491 {
492 int ret, pipes[2];
493 bool bret = true;
494 char *const argv[2] = { spawn_getsid, NULL };
495 char *const envp[1] = { NULL };
496 pid_t buf[2];
497 posix_spawn_file_actions_t acts;
498 posix_spawnattr_t attr;
499 short flags;
500 pid_t pid, exp_sid, exp_pgid;
501 siginfo_t sig;
502 ssize_t buf_len;
503 const char *sid_desc, *pgid_desc;
504
505 posix_spawn_setup_fds(&acts, pipes);
506
507 if ((ret = posix_spawnattr_init(&attr)) != 0) {
508 errc(EXIT_FAILURE, ret, "INTERNAL TEST FAILURE: failed to "
509 "initialize posix_spawn attributes");
510 }
511
512 VERIFY0(posix_spawnattr_getflags(&attr, &flags));
513 if (flags != 0) {
514 warnx("TEST FAILED: %s: initial flags are not zero, found 0x%x",
515 test->sft_desc, flags);
516 bret = false;
517 }
518 VERIFY0(posix_spawnattr_setflags(&attr, test->sft_flags));
519 VERIFY0(posix_spawnattr_getflags(&attr, &flags));
520 if (flags != test->sft_flags) {
521 warnx("TEST FAILED: %s: flags are don't match what we set: "
522 "found 0x%x, expected 0x%x", test->sft_desc, flags,
523 test->sft_flags);
524 bret = false;
525 }
526
527 ret = posix_spawn(&pid, spawn_getsid, &acts, &attr, argv, envp);
528 if (ret != test->sft_ret) {
529 if (test->sft_ret == 0) {
530 warnx("TEST FAILED: %s posix_spawn() failed with %s, "
531 "but expected success", test->sft_desc,
532 strerrorname_np(ret));
533 } else {
534 warnx("TEST FAILED: %s posix_spawn() failed with %s, "
535 "but expected %s", test->sft_desc,
536 strerrorname_np(ret),
537 strerrorname_np(test->sft_ret));
538 }
539 bret = false;
540 goto out;
541 }
542
543 if (test->sft_ret != 0) {
544 (void) printf("TEST PASSED: %s: posix_spawn() failed correctly "
545 "with %s\n", test->sft_desc, strerrorname_np(ret));
546 goto out;
547 }
548
549 if (waitid(P_PID, pid, &sig, WEXITED) != 0) {
550 err(EXIT_FAILURE, "INTERNAL TEST ERROR: %s: failed to wait on "
551 "pid %" _PRIdID ", but posix_spawn executed it",
552 test->sft_desc, pid);
553 }
554
555 if (sig.si_code != CLD_EXITED) {
556 warnx("TEST FAILED: %s: child did not successfully exit: "
557 "foud si_code: %d", test->sft_desc, sig.si_code);
558 bret = false;
559 goto out;
560 }
561
562 if (sig.si_status != 0) {
563 warnx("TEST FAILED: %s: child exited with status %d, expected "
564 "success", test->sft_desc, sig.si_status);
565 bret = false;
566 goto out;
567 }
568
569 /*
570 * The getsid.64 process writes as binary data the results of getsid(2)
571 * and getpgid(2) to our pipe. We should be able to read all of this in
572 * one swoop.
573 */
574 buf_len = read(pipes[0], buf, sizeof (buf));
575 if (buf_len < 0) {
576 warn("TEST FAILED: %s: failed to read IDs from pipe",
577 test->sft_desc);
578 bret = false;
579 goto out;
580 } else if (buf_len == 0) {
581 warn("TEST FAILED: %s: got zero byte read from pipe?!",
582 test->sft_desc);
583 bret = false;
584 goto out;
585 }
586
587 /*
588 * Now we need to check the various process group and session IDs. We
589 * expect the following values:
590 *
591 * If the SETSID flag was set then the session ID should match the
592 * child's pid. Otherwise it should match our value.
593 *
594 * If the SETSID or SETPGROUP flag was set then the process group ID
595 * should match the child's pid. Otherwise it should match our value.
596 */
597 if ((test->sft_flags & POSIX_SPAWN_SETSID) != 0) {
598 exp_sid = pid;
599 sid_desc = "child's ID";
600 } else {
601 exp_sid = getsid(0);
602 sid_desc = "test's SID";
603 }
604
605 if ((test->sft_flags & (POSIX_SPAWN_SETSID |
606 POSIX_SPAWN_SETPGROUP)) != 0) {
607 exp_pgid = pid;
608 pgid_desc = "child's ID";
609 } else {
610 exp_pgid = getpgid(0);
611 pgid_desc = "test's PGID";
612 }
613
614 if (buf[0] != exp_sid) {
615 warnx("TEST FAILED: %s: session ID mismatch: expected 0x%"
616 _PRIxID " (%s), found 0x%" _PRIxID, test->sft_desc, exp_sid,
617 sid_desc, buf[0]);
618 bret = false;
619 }
620
621 if (buf[1] != exp_pgid) {
622 warnx("TEST FAILED: %s: process group ID mismatch: expected "
623 "0x%" _PRIxID " (%s), found 0x%" _PRIxID, test->sft_desc,
624 exp_pgid, pgid_desc, buf[1]);
625 bret = false;
626 }
627
628 if (bret) {
629 (void) printf("TEST PASSED: %s\n", test->sft_desc);
630 }
631
632 out:
633 VERIFY0(posix_spawnattr_destroy(&attr));
634 VERIFY0(posix_spawn_file_actions_destroy(&acts));
635 VERIFY0(close(pipes[1]));
636 VERIFY0(close(pipes[0]));
637 return (bret);
638 }
639
640 /*
641 * Set up paths that are dependent on where our binary is found.
642 */
643 static void
posix_spawn_test_paths(void)644 posix_spawn_test_paths(void)
645 {
646 ssize_t ret;
647 char origin[PATH_MAX];
648
649 ret = readlink("/proc/self/path/a.out", origin, PATH_MAX - 1);
650 if (ret < 0) {
651 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to read "
652 "a.out path");
653 }
654
655 origin[ret] = '\0';
656 if (snprintf(spawn_getsid, sizeof (spawn_getsid), "%s/getsid.64",
657 dirname(origin)) >= sizeof (spawn_getsid)) {
658 errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to assemble "
659 "getsid.64 path");
660 }
661
662 if (access(spawn_getsid, X_OK) != 0) {
663 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to access %s",
664 spawn_getsid);
665 }
666 }
667
668 int
main(void)669 main(void)
670 {
671 int ret = EXIT_SUCCESS;
672
673 /*
674 * Because this test wants to rely on a known starting directory, we're
675 * going to chdir into /var/tmp at the start of this.
676 */
677 if (chdir("/var/tmp") != 0) {
678 err(EXIT_FAILURE, "INTERNAL TEST ERROR: failed to cd into "
679 "/var/tmp");
680 }
681
682 posix_spawn_test_paths();
683
684 for (size_t i = 0; i < ARRAY_SIZE(spawn_dir_tests); i++) {
685 if (!posix_spawn_test_one_chdir(&spawn_dir_tests[i]))
686 ret = EXIT_FAILURE;
687
688 if (!posix_spawn_test_one_fchdir(&spawn_dir_tests[i]))
689 ret = EXIT_FAILURE;
690 }
691
692 if (!posix_spawn_test_bad_actions()) {
693 ret = EXIT_FAILURE;
694 }
695
696 if (!posix_spawn_test_bad_fchdir()) {
697 ret = EXIT_FAILURE;
698 }
699
700 for (size_t i = 0; i < ARRAY_SIZE(spawn_flags_tests); i++) {
701 if (!posix_spawn_test_one_flags(&spawn_flags_tests[i]))
702 ret = EXIT_FAILURE;
703 }
704
705 if (ret == EXIT_SUCCESS) {
706 (void) printf("All tests passed successfully!\n");
707 }
708
709 return (ret);
710 }
711