2 * Taken from newlib/libc/posix/posix_spawn.c
6 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
33 #include <sys/queue.h>
45 extern char **environ
;
47 /* Only deal with a pointer to environ, to work around subtle bugs with shared
48 libraries and/or small data systems where the user declares his own
50 static char ***p_environ
= &environ
;
57 process_spawnattr(const posix_spawnattr_t
* sa
)
59 struct sigaction sigact
= { .sa_flags
= 0, .sa_handler
= SIG_DFL
};
63 * POSIX doesn't really describe in which order everything
64 * should be set. We'll just set them in the order in which they
68 /* Set process group */
69 if (sa
->sa_flags
& POSIX_SPAWN_SETPGROUP
) {
70 if (setpgid(0, sa
->sa_pgroup
) != 0)
74 /* Set scheduler policy */
75 /* XXX: We don't have scheduler policy for now */
77 if (sa
->sa_flags
& POSIX_SPAWN_SETSCHEDULER
) {
78 if (sched_setscheduler(0, sa
->sa_schedpolicy
,
79 &sa
->sa_schedparam
) != 0)
81 } else if (sa
->sa_flags
& POSIX_SPAWN_SETSCHEDPARAM
) {
82 if (sched_setparam(0, &sa
->sa_schedparam
) != 0)
88 if (sa
->sa_flags
& POSIX_SPAWN_RESETIDS
) {
89 if (setegid(getgid()) != 0)
91 if (seteuid(getuid()) != 0)
95 /* Set signal masks/defaults */
96 if (sa
->sa_flags
& POSIX_SPAWN_SETSIGMASK
) {
97 sigprocmask(SIG_SETMASK
, &sa
->sa_sigmask
, NULL
);
100 if (sa
->sa_flags
& POSIX_SPAWN_SETSIGDEF
) {
101 for (i
= 1; i
< NSIG
; i
++) {
102 if (sigismember(&sa
->sa_sigdefault
, i
))
103 if (sigaction(i
, &sigact
, NULL
) != 0)
112 move_fd_up(int * statusfd
)
115 * Move given file descriptor on a higher fd number.
117 * This is used to hide the status file descriptor from the application
118 * by pushing it out of the way if it tries to use its number.
122 newstatusfd
= fcntl(*statusfd
, F_DUPFD
, *statusfd
+1);
123 if (newstatusfd
== -1)
127 *statusfd
= newstatusfd
;
132 process_file_actions_entry(posix_spawn_file_actions_entry_t
* fae
,
137 switch (fae
->fae_action
) {
139 /* Perform an open(), make it use the right fd */
140 fd
= open(fae
->fae_path
, fae
->fae_oflag
, fae
->fae_mode
);
143 if (fd
!= fae
->fae_fildes
) {
144 if (fae
->fae_fildes
== *statusfd
) {
145 /* Move the status fd out of the way */
146 if (move_fd_up(statusfd
) == -1)
149 if (dup2(fd
, fae
->fae_fildes
) == -1)
151 if (close(fd
) != 0) {
156 if (fcntl(fae
->fae_fildes
, F_SETFD
, 0) == -1)
161 if (fae
->fae_fildes
== *statusfd
) {
165 if (fae
->fae_newfildes
== *statusfd
) {
166 /* Move the status file descriptor out of the way */
167 if (move_fd_up(statusfd
) == -1)
170 /* Perform a dup2() */
171 if (dup2(fae
->fae_fildes
, fae
->fae_newfildes
) == -1)
173 if (fcntl(fae
->fae_newfildes
, F_SETFD
, 0) == -1)
178 /* Perform a close(), do not fail if already closed */
179 if (fae
->fae_fildes
!= *statusfd
)
180 (void)close(fae
->fae_fildes
);
187 process_file_actions(const posix_spawn_file_actions_t
* fa
, int * statusfd
)
189 posix_spawn_file_actions_entry_t
*fae
;
192 /* Replay all file descriptor modifications */
193 for (unsigned i
= 0; i
< fa
->len
; i
++) {
195 error
= process_file_actions_entry(fae
, statusfd
);
203 posix_spawn(pid_t
* __restrict pid
, const char * __restrict path
,
204 const posix_spawn_file_actions_t
* fa
,
205 const posix_spawnattr_t
* __restrict sa
,
206 char * const * __restrict argv
, char * const * __restrict envp
)
209 int r
, error
, pfd
[2];
212 * Due to the lack of vfork() in Minix, an alternative solution with
213 * pipes is used. The writing end is set to close on exec() and the
214 * parent performs a read() on it.
216 * On success, a successful 0-length read happens.
217 * On failure, the child writes the errno to the pipe before exiting,
218 * the error is thus transmitted to the parent.
220 * This solution was taken from stackoverflow.com question 3703013.
236 if (fcntl(pfd
[1], F_SETFD
, FD_CLOEXEC
) != 0) {
242 error
= process_spawnattr(sa
);
247 error
= process_file_actions(fa
, &pfd
[1]);
252 (void)execve(path
, argv
, envp
!= NULL
? envp
: *p_environ
);
260 /* Retrieve child process status through pipe. */
261 r
= read(pfd
[0], &error
, sizeof(error
));
269 (void)waitpid(p
, NULL
, 0);
276 /* Child failed somewhere, propagate error through pipe and exit. */
277 write(pfd
[1], &error
, sizeof(error
));