From 1facb0487c831b9ead49737c23d6f936b553009b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Boric Date: Sun, 14 Jun 2015 12:29:01 +0200 Subject: [PATCH] libc: add posix_spawn family of functions 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 | 6 +- lib/libc/gen/Makefile.inc | 3 +- minix/lib/libc/sys/Makefile.inc | 2 +- minix/lib/libc/sys/posix_spawn.c | 277 +++++++++++++++++++++++++ minix/tests/Makefile | 6 +- minix/tests/run | 2 +- minix/tests/t84_h_nonexec.sh | 3 + minix/tests/t84_h_spawn.c | 51 +++++ minix/tests/t84_h_spawnattr.c | 94 +++++++++ minix/tests/test84.c | 335 +++++++++++++++++++++++++++++++ 10 files changed, 773 insertions(+), 6 deletions(-) create mode 100644 minix/lib/libc/sys/posix_spawn.c create mode 100644 minix/tests/t84_h_nonexec.sh create mode 100644 minix/tests/t84_h_spawn.c create mode 100644 minix/tests/t84_h_spawnattr.c create mode 100644 minix/tests/test84.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 46a12c5ff..45b63a498 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -6219,6 +6219,9 @@ ./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 @@ -6293,11 +6296,12 @@ ./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 diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index aa0cca5e7..e3af4ce3a 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -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 \ diff --git a/minix/lib/libc/sys/Makefile.inc b/minix/lib/libc/sys/Makefile.inc index 2b38e2aa4..fe07b4841 100644 --- a/minix/lib/libc/sys/Makefile.inc +++ b/minix/lib/libc/sys/Makefile.inc @@ -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 index 000000000..0141fcafe --- /dev/null +++ b/minix/lib/libc/sys/posix_spawn.c @@ -0,0 +1,277 @@ +/* + * Taken from newlib/libc/posix/posix_spawn.c + */ + +/*- + * Copyright (c) 2008 Ed Schouten + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/minix/tests/Makefile b/minix/tests/Makefile index 8a1d6af7d..fde0607cb 100644 --- a/minix/tests/Makefile +++ b/minix/tests/Makefile @@ -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 diff --git a/minix/tests/run b/minix/tests/run index 40b8d7f97..bc0b807de 100755 --- a/minix/tests/run +++ b/minix/tests/run @@ -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 index 000000000..deee6fe72 --- /dev/null +++ b/minix/tests/t84_h_nonexec.sh @@ -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 index 000000000..5c4dc24cf --- /dev/null +++ b/minix/tests/t84_h_spawn.c @@ -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 and + * Martin Husemann . + * + * 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 +#include + +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 index 000000000..377a71c21 --- /dev/null +++ b/minix/tests/t84_h_spawnattr.c @@ -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 and + * Martin Husemann . + * + * 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 +#include +#include +#include +#include + +/* + * 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 index 000000000..68f571f3e --- /dev/null +++ b/minix/tests/test84.c @@ -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 and + * Martin Husemann . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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; +}