./usr/tests/minix-posix/t67b minix-sys
./usr/tests/minix-posix/t68a minix-sys
./usr/tests/minix-posix/t68b minix-sys
+./usr/tests/minix-posix/t84_h_nonexec.sh minix-sys
+./usr/tests/minix-posix/t84_h_spawn minix-sys
+./usr/tests/minix-posix/t84_h_spawnattr minix-sys
./usr/tests/minix-posix/test10 minix-sys
./usr/tests/minix-posix/test1 minix-sys
./usr/tests/minix-posix/test11 minix-sys
./usr/tests/minix-posix/test77 minix-sys
./usr/tests/minix-posix/test78 minix-sys
./usr/tests/minix-posix/test79 minix-sys
-./usr/tests/minix-posix/test8 minix-sys
./usr/tests/minix-posix/test80 minix-sys
+./usr/tests/minix-posix/test8 minix-sys
./usr/tests/minix-posix/test81 minix-sys
./usr/tests/minix-posix/test82 minix-sys
./usr/tests/minix-posix/test83 minix-sys
+./usr/tests/minix-posix/test84 minix-sys
./usr/tests/minix-posix/test9 minix-sys
./usr/tests/minix-posix/testinterp minix-sys
./usr/tests/minix-posix/testisofs minix-sys
getusershell.c glob.c humanize_number.c initdir.c initgroups.c \
isascii.c isatty.c isctype.c lockf.c nftw.c \
nice.c \
- opendir.c pause.c popen.c \
+ opendir.c pause.c popen.c posix_spawn_sched.c \
+ posix_spawn_fileactions.c posix_spawnp.c \
psignal.c \
ptree.c pwcache.c pw_scan.c raise.c randomid.c rb.c readdir.c \
rewinddir.c scandir.c seekdir.c \
getvfsstat.c \
ioctl.c issetugid.c kill.c link.c listen.c loadname.c lseek.c \
minix_rs.c mkdir.c mkfifo.c mknod.c mmap.c mount.c nanosleep.c \
- open.c pathconf.c pipe.c poll.c pread.c ptrace.c pwrite.c \
+ open.c pathconf.c pipe.c poll.c posix_spawn.c pread.c ptrace.c pwrite.c \
read.c readlink.c reboot.c recvfrom.c recvmsg.c rename.c \
rmdir.c select.c sem.c sendmsg.c sendto.c setgroups.c setsid.c \
setgid.c settimeofday.c setuid.c shmat.c shmctl.c shmget.c stime.c \
--- /dev/null
+/*
+ * Taken from newlib/libc/posix/posix_spawn.c
+ */
+
+/*-
+ * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/queue.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <spawn.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern char **environ;
+
+/* Only deal with a pointer to environ, to work around subtle bugs with shared
+ libraries and/or small data systems where the user declares his own
+ 'environ'. */
+static char ***p_environ = &environ;
+
+/*
+ * Spawn routines
+ */
+
+static int
+process_spawnattr(const posix_spawnattr_t * sa)
+{
+ struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
+ int i;
+
+ /*
+ * POSIX doesn't really describe in which order everything
+ * should be set. We'll just set them in the order in which they
+ * are mentioned.
+ */
+
+ /* Set process group */
+ if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
+ if (setpgid(0, sa->sa_pgroup) != 0)
+ return errno;
+ }
+
+ /* Set scheduler policy */
+ /* XXX: We don't have scheduler policy for now */
+#if 0
+ if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) {
+ if (sched_setscheduler(0, sa->sa_schedpolicy,
+ &sa->sa_schedparam) != 0)
+ return errno;
+ } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
+ if (sched_setparam(0, &sa->sa_schedparam) != 0)
+ return errno;
+ }
+#endif
+
+ /* Reset user ID's */
+ if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
+ if (setegid(getgid()) != 0)
+ return errno;
+ if (seteuid(getuid()) != 0)
+ return errno;
+ }
+
+ /* Set signal masks/defaults */
+ if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) {
+ sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL);
+ }
+
+ if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) {
+ for (i = 1; i < NSIG; i++) {
+ if (sigismember(&sa->sa_sigdefault, i))
+ if (sigaction(i, &sigact, NULL) != 0)
+ return errno;
+ }
+ }
+
+ return 0;
+}
+
+static int
+move_fd_up(int * statusfd)
+{
+ /*
+ * Move given file descriptor on a higher fd number.
+ *
+ * This is used to hide the status file descriptor from the application
+ * by pushing it out of the way if it tries to use its number.
+ */
+ int newstatusfd;
+
+ newstatusfd = fcntl(*statusfd, F_DUPFD, *statusfd+1);
+ if (newstatusfd == -1)
+ return -1;
+
+ close(*statusfd);
+ *statusfd = newstatusfd;
+ return 0;
+}
+
+static int
+process_file_actions_entry(posix_spawn_file_actions_entry_t * fae,
+ int * statusfd)
+{
+ int fd;
+
+ switch (fae->fae_action) {
+ case FAE_OPEN:
+ /* Perform an open(), make it use the right fd */
+ fd = open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
+ if (fd < 0)
+ return errno;
+ if (fd != fae->fae_fildes) {
+ if (fae->fae_fildes == *statusfd) {
+ /* Move the status fd out of the way */
+ if (move_fd_up(statusfd) == -1)
+ return errno;
+ }
+ if (dup2(fd, fae->fae_fildes) == -1)
+ return errno;
+ if (close(fd) != 0) {
+ if (errno == EBADF)
+ return EBADF;
+ }
+ }
+ if (fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
+ return errno;
+ break;
+
+ case FAE_DUP2:
+ if (fae->fae_fildes == *statusfd) {
+ /* Nice try */
+ return EBADF;
+ }
+ if (fae->fae_newfildes == *statusfd) {
+ /* Move the status file descriptor out of the way */
+ if (move_fd_up(statusfd) == -1)
+ return errno;
+ }
+ /* Perform a dup2() */
+ if (dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
+ return errno;
+ if (fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
+ return errno;
+ break;
+
+ case FAE_CLOSE:
+ /* Perform a close(), do not fail if already closed */
+ if (fae->fae_fildes != *statusfd)
+ (void)close(fae->fae_fildes);
+ break;
+ }
+ return 0;
+}
+
+static int
+process_file_actions(const posix_spawn_file_actions_t * fa, int * statusfd)
+{
+ posix_spawn_file_actions_entry_t *fae;
+ int error;
+
+ /* Replay all file descriptor modifications */
+ for (unsigned i = 0; i < fa->len; i++) {
+ fae = &fa->fae[i];
+ error = process_file_actions_entry(fae, statusfd);
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+int
+posix_spawn(pid_t * __restrict pid, const char * __restrict path,
+ const posix_spawn_file_actions_t * fa,
+ const posix_spawnattr_t * __restrict sa,
+ char * const * __restrict argv, char * const * __restrict envp)
+{
+ pid_t p;
+ int r, error, pfd[2];
+
+ /*
+ * Due to the lack of vfork() in Minix, an alternative solution with
+ * pipes is used. The writing end is set to close on exec() and the
+ * parent performs a read() on it.
+ *
+ * On success, a successful 0-length read happens.
+ * On failure, the child writes the errno to the pipe before exiting,
+ * the error is thus transmitted to the parent.
+ *
+ * This solution was taken from stackoverflow.com question 3703013.
+ */
+ if (pipe(pfd) == -1)
+ return errno;
+
+ p = fork();
+ switch (p) {
+ case -1:
+ close(pfd[0]);
+ close(pfd[1]);
+
+ return errno;
+
+ case 0:
+ close(pfd[0]);
+
+ if (fcntl(pfd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ error = errno;
+ break;
+ }
+
+ if (sa != NULL) {
+ error = process_spawnattr(sa);
+ if (error)
+ break;
+ }
+ if (fa != NULL) {
+ error = process_file_actions(fa, &pfd[1]);
+ if (error)
+ break;
+ }
+
+ (void)execve(path, argv, envp != NULL ? envp : *p_environ);
+
+ error = errno;
+ break;
+
+ default:
+ close(pfd[1]);
+
+ /* Retrieve child process status through pipe. */
+ r = read(pfd[0], &error, sizeof(error));
+ if (r == 0)
+ error = 0;
+ else if (r == -1)
+ error = errno;
+ close(pfd[0]);
+
+ if (pid != NULL)
+ *pid = p;
+ return error;
+ }
+
+ /* Child failed somewhere, propagate error through pipe and exit. */
+ write(pfd[1], &error, sizeof(error));
+ close(pfd[1]);
+ _exit(127);
+}
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
41 42 43 44 45 46 48 49 50 52 53 54 55 56 58 59 60 \
61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
-81 82 83
+81 82 83 84
+
+FILES += t84_h_nonexec.sh
.if ${MACHINE_ARCH} == "i386"
MINIX_TESTS+= \
.endfor
PROGS+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t40g t60a t60b \
- t67a t67b t68a t68b tvnd
+ t67a t67b t68a t68b tvnd t84_h_spawn t84_h_spawnattr
SCRIPTS+= run check-install testinterp.sh testsh1.sh testsh2.sh testmfs.sh \
testisofs.sh testvnd.sh testkyua.sh testrelpol.sh
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 \
- 81 82 83 sh1 sh2 interp mfs isofs vnd"
+ 81 82 83 84 sh1 sh2 interp mfs isofs vnd"
tests_no=`expr 0`
# If root, make sure the setuid tests have the correct permissions
--- /dev/null
+#! /nonexistent
+
+# this is just a dummy script, trying to be non-executable
--- /dev/null
+/* $NetBSD: h_spawn.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles Zhang <charles@NetBSD.org> and
+ * Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int argc, char **argv)
+{
+ unsigned long ret;
+ char *endp;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage:\n\t%s (retcode)\n", getprogname());
+ exit(255);
+ }
+ ret = strtoul(argv[1], &endp, 10);
+#if DEBUG
+ fprintf(stderr, "%s exiting with status %lu\n", getprogname(), ret);
+#endif
+ return ret;
+}
--- /dev/null
+/* $NetBSD: h_spawnattr.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles Zhang <charles@NetBSD.org> and
+ * Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+/*
+ * Helper to test the hardcoded assumptions from t_spawnattr.c
+ * Exit with apropriate exit status and print diagnostics to
+ * stderr explaining what is wrong.
+ */
+int
+main(int argc, char **argv)
+{
+ int parent_pipe, res = EXIT_SUCCESS;
+ sigset_t sig;
+ struct sigaction act;
+ ssize_t rd;
+ char tmp;
+
+ sigemptyset(&sig);
+ if (sigprocmask(0, NULL, &sig) < 0) {
+ fprintf(stderr, "%s: sigprocmask error\n", getprogname());
+ res = EXIT_FAILURE;
+ }
+ if (!sigismember(&sig, SIGUSR1)) {
+ fprintf(stderr, "%s: SIGUSR not in procmask\n", getprogname());
+ res = EXIT_FAILURE;
+ }
+ if (sigaction(SIGUSR1, NULL, &act) < 0) {
+ fprintf(stderr, "%s: sigaction error\n", getprogname());
+ res = EXIT_FAILURE;
+ }
+ if (act.sa_sigaction != (void *)SIG_DFL) {
+ fprintf(stderr, "%s: SIGUSR1 action != SIG_DFL\n",
+ getprogname());
+ res = EXIT_FAILURE;
+ }
+
+ if (argc >= 2) {
+ parent_pipe = atoi(argv[1]);
+ if (parent_pipe > 2) {
+#if DEBUG
+ printf("%s: waiting for command from parent on pipe "
+ "%d\n", getprogname(), parent_pipe);
+#endif
+ rd = read(parent_pipe, &tmp, 1);
+ if (rd == 1) {
+#if DEBUG
+ printf("%s: got command %c from parent\n",
+ getprogname(), tmp);
+#endif
+ } else if (rd == -1) {
+ printf("%s: %d is no pipe, errno %d\n",
+ getprogname(), parent_pipe, errno);
+ res = EXIT_FAILURE;
+ }
+ }
+ }
+
+ return res;
+}
--- /dev/null
+/*
+ * Based off tests/lib/libc/gen/posix_spawn/t_spawn.c
+ */
+
+/* $NetBSD: t_spawn.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */
+
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles Zhang <charles@NetBSD.org> and
+ * Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <spawn.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "common.h"
+
+/* reroute stdout to /dev/null while returning another fd for the old stdout */
+/* this is just for aesthetics: we don't want to see the output of 'ls' */
+static int
+sink_stdout(void)
+{
+ int fd, fd2;
+
+ if ((fd = fcntl(1, F_DUPFD, 3)) == -1 || close(1) == -1) {
+ e(0);
+ quit();
+ }
+
+ if ((fd2 = open("/dev/null", O_WRONLY)) != 1) {
+ if (fd2 == -1 || dup2(fd2, 1) != 1) {
+ dup2(fd, 1);
+ e(0);
+ quit();
+ }
+ }
+
+ return fd;
+}
+
+/* restore stdout */
+static void
+restore_stdout(int fd)
+{
+
+ dup2(fd, 1);
+ close(fd);
+}
+
+/* tests a simple posix_spawn executing /bin/ls */
+static void
+test_posix_spawn_ls(void)
+{
+ char * const args[] = { "ls", "-la", NULL };
+ int err;
+
+ err = posix_spawn(NULL, "/bin/ls", NULL, NULL, args, NULL);
+ if (err != 0)
+ e(1);
+}
+
+/* tests a simple posix_spawnp executing ls via $PATH */
+static void
+test_posix_spawnp_ls(void)
+{
+ char * const args[] = { "ls", "-la", NULL };
+ int err;
+
+ err = posix_spawnp(NULL, "ls", NULL, NULL, args, NULL);
+ if(err != 0)
+ e(2);
+}
+
+/* posix_spawn a non existant binary */
+static void
+test_posix_spawn_missing(void)
+{
+ char * const args[] = { "t84_h_nonexist", NULL };
+ int err;
+
+ err = posix_spawn(NULL, "../t84_h_nonexist", NULL, NULL, args, NULL);
+ if (err != ENOENT)
+ e(4);
+}
+
+/* posix_spawn a script with non existing interpreter */
+static void
+test_posix_spawn_nonexec(void)
+{
+ char * const args[] = { "t84_h_nonexec", NULL };
+ int err;
+
+ err = posix_spawn(NULL, "../t84_h_nonexec", NULL, NULL, args, NULL);
+ if (err != ENOENT)
+ e(5);
+}
+
+/* posix_spawn a child and get it's return code */
+static void
+test_posix_spawn_child(void)
+{
+ char * const args0[] = { "t84_h_spawn", "0", NULL };
+ char * const args1[] = { "t84_h_spawn", "1", NULL };
+ char * const args7[] = { "t84_h_spawn", "7", NULL };
+ int err, status;
+ pid_t pid;
+
+ err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args0, NULL);
+ if (err != 0 || pid < 1)
+ e(1);
+ waitpid(pid, &status, 0);
+ if (! (WIFEXITED(status) && WEXITSTATUS(status) == 0))
+ e(2);
+
+ err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args1, NULL);
+ if (err != 0 || pid < 1)
+ e(3);
+ waitpid(pid, &status, 0);
+ if (! (WIFEXITED(status) && WEXITSTATUS(status) == 1))
+ e(4);
+
+ err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args7, NULL);
+ if (err != 0 || pid < 1)
+ e(5);
+ waitpid(pid, &status, 0);
+ if (! (WIFEXITED(status) && WEXITSTATUS(status) == 7))
+ e(6);
+}
+
+/* test spawn attributes */
+static void
+test_posix_spawnattr(void)
+{
+ int pid, status, err, pfd[2];
+ char helper_arg[128];
+ char * const args[] = { "t84_h_spawnattr", helper_arg, NULL };
+ sigset_t sig;
+ posix_spawnattr_t attr;
+
+ /*
+ * create a pipe to controll the child
+ */
+ err = pipe(pfd);
+ if (err != 0)
+ e(1);
+ sprintf(helper_arg, "%d", pfd[0]);
+
+ posix_spawnattr_init(&attr);
+
+ sigemptyset(&sig);
+ sigaddset(&sig, SIGUSR1);
+
+ posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDULER |
+ POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETPGROUP |
+ POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGDEF);
+ posix_spawnattr_setpgroup(&attr, 0);
+#if 0
+ posix_spawnattr_setschedparam(&attr, &sp);
+ posix_spawnattr_setschedpolicy(&attr, scheduler);
+#endif
+ posix_spawnattr_setsigmask(&attr, &sig);
+ posix_spawnattr_setsigdefault(&attr, &sig);
+
+ err = posix_spawn(&pid, "../t84_h_spawnattr", NULL, &attr, args, NULL);
+ if (err != 0)
+ e(2);
+
+ /* ready, let child go */
+ write(pfd[1], "q", 1);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ /* wait and check result from child */
+ waitpid(pid, &status, 0);
+ if (! (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS))
+ e(3);
+
+ posix_spawnattr_destroy(&attr);
+}
+
+/* tests a simple posix_spawn executing /bin/ls with file actions */
+static void
+test_posix_spawn_file_actions(void)
+{
+ char * const args[] = { "ls", "-la", NULL };
+ int err;
+ posix_spawn_file_actions_t file_actions;
+
+ /*
+ * Just do a bunch of random operations which should leave console
+ * output intact.
+ */
+ posix_spawn_file_actions_init(&file_actions);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 3);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 4);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 6);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 5);
+ posix_spawn_file_actions_addclose(&file_actions, 3);
+ posix_spawn_file_actions_addclose(&file_actions, 4);
+ posix_spawn_file_actions_addclose(&file_actions, 6);
+ posix_spawn_file_actions_addclose(&file_actions, 5);
+
+ posix_spawn_file_actions_addclose(&file_actions, 0);
+ posix_spawn_file_actions_addclose(&file_actions, 2);
+ posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null",
+ O_RDONLY, 0);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 2);
+ posix_spawn_file_actions_addclose(&file_actions, 1);
+ posix_spawn_file_actions_adddup2(&file_actions, 2, 1);
+
+ err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args, NULL);
+ posix_spawn_file_actions_destroy(&file_actions);
+
+ if (err != 0)
+ e(1);
+}
+
+/* tests failures with file actions */
+static void
+test_posix_spawn_file_actions_failures(void)
+{
+ char * const args[] = { "ls", "-la", NULL };
+ int err, i;
+ posix_spawn_file_actions_t file_actions;
+
+ /* Test bogus open */
+ posix_spawn_file_actions_init(&file_actions);
+ posix_spawn_file_actions_addclose(&file_actions, 0);
+ posix_spawn_file_actions_addopen(&file_actions, 0, "t84_h_nonexist",
+ O_RDONLY, 0);
+
+ err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args, NULL);
+ posix_spawn_file_actions_destroy(&file_actions);
+
+ if (err == 0)
+ e(1);
+
+ /* Test bogus dup2 */
+ for (i = 3; i < 10; i++) {
+ posix_spawn_file_actions_init(&file_actions);
+ posix_spawn_file_actions_adddup2(&file_actions, i, i+1);
+
+ err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args,
+ NULL);
+ posix_spawn_file_actions_destroy(&file_actions);
+
+ if (err == 0)
+ e(i-2);
+ }
+
+ /*
+ * Test bogus exec with dup2 (to mess with the pipe error reporting in
+ * posix_spawn.c)
+ */
+ posix_spawn_file_actions_init(&file_actions);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 3);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 4);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 6);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 5);
+ posix_spawn_file_actions_adddup2(&file_actions, 1, 7);
+
+ err = posix_spawn(NULL, "t84_h_nonexist", &file_actions, NULL, args,
+ NULL);
+ posix_spawn_file_actions_destroy(&file_actions);
+
+ if (err == 0)
+ e(9);
+}
+
+int
+main(void)
+{
+ int fd;
+
+ start(84);
+
+ subtest = 1;
+ fd = sink_stdout();
+ test_posix_spawn_ls();
+ test_posix_spawnp_ls();
+ restore_stdout(fd);
+
+ test_posix_spawn_missing();
+ test_posix_spawn_nonexec();
+
+ subtest = 2;
+ test_posix_spawn_child();
+
+ subtest = 3;
+ test_posix_spawnattr();
+ subtest = 4;
+ fd = sink_stdout();
+ test_posix_spawn_file_actions();
+ restore_stdout(fd);
+ subtest = 5;
+ test_posix_spawn_file_actions_failures();
+
+ /* TODO: Write/port more tests */
+
+ quit();
+
+ /* Not reached */
+ return -1;
+}