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
This commit is contained in:
Jean-Baptiste Boric 2015-06-14 12:29:01 +02:00 committed by David van Moolenbroek
parent 7b2da7b2c7
commit 1facb0487c
10 changed files with 773 additions and 6 deletions

View file

@ -6219,6 +6219,9 @@
./usr/tests/minix-posix/t67b minix-sys ./usr/tests/minix-posix/t67b minix-sys
./usr/tests/minix-posix/t68a minix-sys ./usr/tests/minix-posix/t68a minix-sys
./usr/tests/minix-posix/t68b 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/test10 minix-sys
./usr/tests/minix-posix/test1 minix-sys ./usr/tests/minix-posix/test1 minix-sys
./usr/tests/minix-posix/test11 minix-sys ./usr/tests/minix-posix/test11 minix-sys
@ -6293,11 +6296,12 @@
./usr/tests/minix-posix/test77 minix-sys ./usr/tests/minix-posix/test77 minix-sys
./usr/tests/minix-posix/test78 minix-sys ./usr/tests/minix-posix/test78 minix-sys
./usr/tests/minix-posix/test79 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/test80 minix-sys
./usr/tests/minix-posix/test8 minix-sys
./usr/tests/minix-posix/test81 minix-sys ./usr/tests/minix-posix/test81 minix-sys
./usr/tests/minix-posix/test82 minix-sys ./usr/tests/minix-posix/test82 minix-sys
./usr/tests/minix-posix/test83 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/test9 minix-sys
./usr/tests/minix-posix/testinterp minix-sys ./usr/tests/minix-posix/testinterp minix-sys
./usr/tests/minix-posix/testisofs minix-sys ./usr/tests/minix-posix/testisofs minix-sys

View 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 \ getusershell.c glob.c humanize_number.c initdir.c initgroups.c \
isascii.c isatty.c isctype.c lockf.c nftw.c \ isascii.c isatty.c isctype.c lockf.c nftw.c \
nice.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 \ psignal.c \
ptree.c pwcache.c pw_scan.c raise.c randomid.c rb.c readdir.c \ ptree.c pwcache.c pw_scan.c raise.c randomid.c rb.c readdir.c \
rewinddir.c scandir.c seekdir.c \ rewinddir.c scandir.c seekdir.c \

View 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 \ getvfsstat.c \
ioctl.c issetugid.c kill.c link.c listen.c loadname.c lseek.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 \ 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 \ 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 \ 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 \ setgid.c settimeofday.c setuid.c shmat.c shmctl.c shmget.c stime.c \

View file

@ -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);
}

View 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 \ 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 \ 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 \ 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" .if ${MACHINE_ARCH} == "i386"
MINIX_TESTS+= \ MINIX_TESTS+= \
@ -72,7 +74,7 @@ PROGS+= test${t}
.endfor .endfor
PROGS+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t40g t60a t60b \ 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 \ SCRIPTS+= run check-install testinterp.sh testsh1.sh testsh2.sh testmfs.sh \
testisofs.sh testvnd.sh testkyua.sh testrelpol.sh testisofs.sh testvnd.sh testkyua.sh testrelpol.sh

View file

@ -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 \ 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 \ 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 \ 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` tests_no=`expr 0`
# If root, make sure the setuid tests have the correct permissions # If root, make sure the setuid tests have the correct permissions

View file

@ -0,0 +1,3 @@
#! /nonexistent
# this is just a dummy script, trying to be non-executable

51
minix/tests/t84_h_spawn.c Normal file
View file

@ -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;
}

View file

@ -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;
}

335
minix/tests/test84.c Normal file
View file

@ -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;
}