]> Zhao Yanbai Git Server - minix.git/commitdiff
libc: add posix_spawn family of functions 42/3042/2
authorJean-Baptiste Boric <jblbeurope@gmail.com>
Sun, 14 Jun 2015 10:29:01 +0000 (12:29 +0200)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 28 Jul 2015 14:18:03 +0000 (14:18 +0000)
The implementation is taken from newlib (BSD licensed) and test84 is based
on NetBSD's t_spawn.c

Change-Id: Ia4e9dd5204a0b4ef241a451978057e11fb29e3d6

distrib/sets/lists/minix/mi
lib/libc/gen/Makefile.inc
minix/lib/libc/sys/Makefile.inc
minix/lib/libc/sys/posix_spawn.c [new file with mode: 0644]
minix/tests/Makefile
minix/tests/run
minix/tests/t84_h_nonexec.sh [new file with mode: 0644]
minix/tests/t84_h_spawn.c [new file with mode: 0644]
minix/tests/t84_h_spawnattr.c [new file with mode: 0644]
minix/tests/test84.c [new file with mode: 0644]

index 46a12c5ff5b170a48d71f0f8ea5d026b3b67cb0d..45b63a4988c090c7b86f332b598437fd1b34202d 100644 (file)
 ./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
index aa0cca5e72b53818104877e4ee1f98d36e79a4df..e3af4ce3a6ecd7cb637d75391f816f69602c9c3f 100644 (file)
@@ -42,7 +42,8 @@ SRCS+=        _errno.c alarm.c alphasort.c arc4random.c assert.c basename.c clock.c \
        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 \
index 2b38e2aa41bd5be850032b0993218cd7a1be1694..fe07b48417e892e1bad791bd86a02d94a201a6c7 100644 (file)
@@ -12,7 +12,7 @@ SRCS+=        accept.c access.c adjtime.c bind.c brk.c sbrk.c m_closefrom.c getsid.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 \
diff --git a/minix/lib/libc/sys/posix_spawn.c b/minix/lib/libc/sys/posix_spawn.c
new file mode 100644 (file)
index 0000000..0141fca
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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);
+}
index 8a1d6af7d86c9e918b0facb68ce296ded695340e..fde0607cbf7b0da9c3fd0148dccbff6df9d4eed3 100644 (file)
@@ -59,7 +59,9 @@ MINIX_TESTS= \
 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+= \
@@ -72,7 +74,7 @@ PROGS+= test${t}
 .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
index 40b8d7f972f05c3d0619f481648257fa7b42076d..bc0b807de2ee5322277e592170703d51d1e4add0 100755 (executable)
@@ -30,7 +30,7 @@ alltests="1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
          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
diff --git a/minix/tests/t84_h_nonexec.sh b/minix/tests/t84_h_nonexec.sh
new file mode 100644 (file)
index 0000000..deee6fe
--- /dev/null
@@ -0,0 +1,3 @@
+#! /nonexistent
+
+# this is just a dummy script, trying to be non-executable
diff --git a/minix/tests/t84_h_spawn.c b/minix/tests/t84_h_spawn.c
new file mode 100644 (file)
index 0000000..5c4dc24
--- /dev/null
@@ -0,0 +1,51 @@
+/* $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;
+}
diff --git a/minix/tests/t84_h_spawnattr.c b/minix/tests/t84_h_spawnattr.c
new file mode 100644 (file)
index 0000000..377a71c
--- /dev/null
@@ -0,0 +1,94 @@
+/* $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;
+}
diff --git a/minix/tests/test84.c b/minix/tests/test84.c
new file mode 100644 (file)
index 0000000..68f571f
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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;
+}