2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 <<posix_spawn>>, <<posix_spawnp>>---spawn a process
39 int posix_spawn(pid_t *<[pid]>, const char *<[path]>,
40 const posix_spawn_file_actions_t *<[file_actions]>,
41 const posix_spawnattr_t *<[attrp]>,
42 char *const <[argv]>[], char *const <[envp]>[]);
43 int posix_spawnp(pid_t *<[pid]>, const char *<[file]>,
44 const posix_spawn_file_actions_t *<[file_actions]>,
45 const posix_spawnattr_t *<[attrp]>,
46 char *const <[argv]>[], char *const <[envp]>[]);
49 Use <<posix_spawn>> and <<posix_spawnp>> to create a new child process
50 from the specified process image file. <<argc>> is the argument count
51 and <<argv>> is an array of argument strings passed to the new program.
52 <<envp>> is an array of stings, which are passed as environment to the
55 The <<path>> argument to <<posix_spawn>> identifies the new process
56 image file to execute. The <<file>> argument to <<posix_spawnp>> is
57 used to construct a pathname that identifies the new process image
58 file by duplicating the actions of the shell in searching for an
59 executable file if the specified filename does not contain a `<</>>'
60 character. The <<file>> is sought in the colon-separated list of
61 directory pathnames specified in the <<PATH>> environment variable.
63 The file descriptors remain open across <<posix_spawn>> and
64 <<posix_spawnp>> except for those marked as close-on-exec. The open
65 file descriptors in the child process can be modified by the spawn file
66 actions object pointed to by <<file_actions>>.
68 The spawn attributes object type pointed to by <<attrp>> argument
69 may contain any of the attributes defined in <<spawn.h>>.
72 <<posix_spawn>> and <<posix_spawnp>> return the process ID of the newly
73 spawned child process in the variable pointed by a non-NULL <<*<[pid]>>>
74 argument and zero as the function return value upon successful
75 completion. Otherwise, <<posix_spawn>> and <<posix_spawnp>> return an
76 error number as the function return value to indicate the error; the
77 value stored into the variable pointed to by a non-NULL <<*<[pid]>>>
78 argument is unspecified.
81 POSIX.1-2008 requires <<posix_spawn>> and <<posix_spawnp>>.
83 Supporting OS subroutines required: <<_close>>, <<dup2>>, <<_fcntl>>,
84 <<_execve>>, <<execvpe>>, <<_exit>>, <<_open>>, <<sigaction>>,
85 <<sigprocmask>>, <<waitpid>>, <<sched_setscheduler>>,
86 <<sched_setparam>>, <<setegid>>, <<seteuid>>, <<setpgid>>, <<vfork>>.
89 #ifndef _NO_POSIX_SPAWN
91 #include <sys/cdefs.h>
93 #include <sys/signal.h>
94 #include <sys/queue.h>
106 /* Only deal with a pointer to environ, to work around subtle bugs with shared
107 libraries and/or small data systems where the user declares his own
109 static char ***p_environ
= &environ
;
111 struct __posix_spawnattr
{
114 struct sched_param sa_schedparam
;
116 sigset_t sa_sigdefault
;
120 struct __posix_spawn_file_actions
{
121 STAILQ_HEAD(, __posix_spawn_file_actions_entry
) fa_list
;
124 typedef struct __posix_spawn_file_actions_entry
{
125 STAILQ_ENTRY(__posix_spawn_file_actions_entry
) fae_list
;
138 #define fae_path fae_data.open.path
140 #define fae_oflag fae_data.open.oflag
142 #define fae_mode fae_data.open.mode
146 #define fae_newfildes fae_data.dup2.newfildes
149 #define fae_dir fae_data.dir
151 #define fae_dirfd fae_data.dirfd
153 } posix_spawn_file_actions_entry_t
;
160 process_spawnattr(const posix_spawnattr_t sa
)
162 struct sigaction sigact
= { .sa_flags
= 0, .sa_handler
= SIG_DFL
};
166 * POSIX doesn't really describe in which order everything
167 * should be set. We'll just set them in the order in which they
171 /* Set process group */
172 if (sa
->sa_flags
& POSIX_SPAWN_SETPGROUP
) {
173 if (setpgid(0, sa
->sa_pgroup
) != 0)
177 /* Set scheduler policy */
178 if (sa
->sa_flags
& POSIX_SPAWN_SETSCHEDULER
) {
179 if (sched_setscheduler(0, sa
->sa_schedpolicy
,
180 &sa
->sa_schedparam
) != 0)
182 } else if (sa
->sa_flags
& POSIX_SPAWN_SETSCHEDPARAM
) {
183 if (sched_setparam(0, &sa
->sa_schedparam
) != 0)
187 /* Reset user ID's */
188 if (sa
->sa_flags
& POSIX_SPAWN_RESETIDS
) {
189 if (setegid(getgid()) != 0)
191 if (seteuid(getuid()) != 0)
195 /* Set signal masks/defaults */
196 if (sa
->sa_flags
& POSIX_SPAWN_SETSIGMASK
) {
197 sigprocmask(SIG_SETMASK
, &sa
->sa_sigmask
, NULL
);
200 if (sa
->sa_flags
& POSIX_SPAWN_SETSIGDEF
) {
201 for (i
= 1; i
< NSIG
; i
++) {
202 if (sigismember(&sa
->sa_sigdefault
, i
))
203 if (sigaction(i
, &sigact
, NULL
) != 0)
212 process_file_actions_entry(posix_spawn_file_actions_entry_t
*fae
)
216 switch (fae
->fae_action
) {
218 /* Perform an open(), make it use the right fd */
219 fd
= _open(fae
->fae_path
, fae
->fae_oflag
, fae
->fae_mode
);
222 if (fd
!= fae
->fae_fildes
) {
223 if (dup2(fd
, fae
->fae_fildes
) == -1)
225 if (_close(fd
) != 0) {
231 if (_fcntl(fae
->fae_fildes
, F_SETFD
, 0) == -1)
233 #endif /* HAVE_FCNTL */
236 /* Perform a dup2() */
237 if (dup2(fae
->fae_fildes
, fae
->fae_newfildes
) == -1)
240 if (_fcntl(fae
->fae_newfildes
, F_SETFD
, 0) == -1)
242 #endif /* HAVE_FCNTL */
245 /* Perform a close(), do not fail if already closed */
246 (void)_close(fae
->fae_fildes
);
250 /* Perform a chdir. */
251 if (chdir (fae
->fae_dir
) == -1)
257 /* Perform a chdir. */
258 if (fchdir (fae
->fae_dirfd
) == -1)
267 process_file_actions(const posix_spawn_file_actions_t fa
)
269 posix_spawn_file_actions_entry_t
*fae
;
272 /* Replay all file descriptor modifications */
273 STAILQ_FOREACH(fae
, &fa
->fa_list
, fae_list
) {
274 error
= process_file_actions_entry(fae
);
282 /* Cygwin's vfork does not follow BSD vfork semantics. Rather it's equivalent
283 to fork. While that's POSIX compliant, the below FreeBSD implementation
284 relying on BSD vfork semantics doesn't work as expected on Cygwin. The
285 following Cygwin-specific code handles the synchronization FreeBSD gets
286 for free by using vfork. */
288 extern int __posix_spawn_sem_create (void **semp
);
289 extern void __posix_spawn_sem_release (void *sem
, int error
);
290 extern int __posix_spawn_sem_wait_and_close (void *sem
, void *proc
);
291 extern int __posix_spawn_fork (void **proc
);
292 extern int __posix_spawn_execvpe (const char *path
, char * const *argv
,
293 char *const *envp
, void *sem
,
298 do_posix_spawn(pid_t
*pid
, const char *path
,
299 const posix_spawn_file_actions_t
*fa
,
300 const posix_spawnattr_t
*sa
,
301 char * const argv
[], char * const envp
[], int use_env_path
)
307 error
= __posix_spawn_sem_create(&sem
);
311 p
= __posix_spawn_fork(&proc
);
317 error
= process_spawnattr(*sa
);
319 __posix_spawn_sem_release(sem
, error
);
324 error
= process_file_actions(*fa
);
326 __posix_spawn_sem_release(sem
, error
);
330 __posix_spawn_execvpe(path
, argv
,
331 envp
!= NULL
? envp
: *p_environ
,
335 error
= __posix_spawn_sem_wait_and_close(sem
, proc
);
337 waitpid(p
, NULL
, WNOHANG
);
338 else if (pid
!= NULL
)
345 do_posix_spawn(pid_t
*pid
, const char *path
,
346 const posix_spawn_file_actions_t
*fa
,
347 const posix_spawnattr_t
*sa
,
348 char * const argv
[], char * const envp
[], int use_env_path
)
351 volatile int error
= 0;
359 error
= process_spawnattr(*sa
);
364 error
= process_file_actions(*fa
);
369 execvpe(path
, argv
, envp
!= NULL
? envp
: *p_environ
);
371 _execve(path
, argv
, envp
!= NULL
? envp
: *p_environ
);
376 waitpid(p
, NULL
, WNOHANG
);
377 else if (pid
!= NULL
)
385 posix_spawn (pid_t
*pid
,
387 const posix_spawn_file_actions_t
*fa
,
388 const posix_spawnattr_t
*sa
,
392 return do_posix_spawn(pid
, path
, fa
, sa
, argv
, envp
, 0);
396 posix_spawnp (pid_t
*pid
,
398 const posix_spawn_file_actions_t
*fa
,
399 const posix_spawnattr_t
*sa
,
403 return do_posix_spawn(pid
, path
, fa
, sa
, argv
, envp
, 1);
407 * File descriptor actions
411 posix_spawn_file_actions_init (posix_spawn_file_actions_t
*ret
)
413 posix_spawn_file_actions_t fa
;
415 fa
= malloc(sizeof(struct __posix_spawn_file_actions
));
419 STAILQ_INIT(&fa
->fa_list
);
425 posix_spawn_file_actions_destroy (posix_spawn_file_actions_t
*fa
)
427 posix_spawn_file_actions_entry_t
*fae
;
429 while ((fae
= STAILQ_FIRST(&(*fa
)->fa_list
)) != NULL
) {
430 /* Remove file action entry from the queue */
431 STAILQ_REMOVE_HEAD(&(*fa
)->fa_list
, fae_list
);
433 /* Deallocate file action entry */
434 switch (fae
->fae_action
) {
454 posix_spawn_file_actions_addopen (posix_spawn_file_actions_t
* __restrict fa
,
456 const char * __restrict path
,
460 posix_spawn_file_actions_entry_t
*fae
;
466 /* Allocate object */
467 fae
= malloc(sizeof(posix_spawn_file_actions_entry_t
));
471 /* Set values and store in queue */
472 fae
->fae_action
= FAE_OPEN
;
473 fae
->fae_path
= strdup(path
);
474 if (fae
->fae_path
== NULL
) {
479 fae
->fae_fildes
= fildes
;
480 fae
->fae_oflag
= oflag
;
481 fae
->fae_mode
= mode
;
483 STAILQ_INSERT_TAIL(&(*fa
)->fa_list
, fae
, fae_list
);
488 posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t
*fa
,
492 posix_spawn_file_actions_entry_t
*fae
;
494 if (fildes
< 0 || newfildes
< 0)
497 /* Allocate object */
498 fae
= malloc(sizeof(posix_spawn_file_actions_entry_t
));
502 /* Set values and store in queue */
503 fae
->fae_action
= FAE_DUP2
;
504 fae
->fae_fildes
= fildes
;
505 fae
->fae_newfildes
= newfildes
;
507 STAILQ_INSERT_TAIL(&(*fa
)->fa_list
, fae
, fae_list
);
512 posix_spawn_file_actions_addclose (posix_spawn_file_actions_t
*fa
,
515 posix_spawn_file_actions_entry_t
*fae
;
520 /* Allocate object */
521 fae
= malloc(sizeof(posix_spawn_file_actions_entry_t
));
525 /* Set values and store in queue */
526 fae
->fae_action
= FAE_CLOSE
;
527 fae
->fae_fildes
= fildes
;
529 STAILQ_INSERT_TAIL(&(*fa
)->fa_list
, fae
, fae_list
);
535 posix_spawn_file_actions_addchdir_np (
536 posix_spawn_file_actions_t
* __restrict fa
,
537 const char * __restrict path
)
539 posix_spawn_file_actions_entry_t
*fae
;
542 /* Allocate object */
543 fae
= malloc(sizeof(posix_spawn_file_actions_entry_t
));
547 /* Set values and store in queue */
548 fae
->fae_action
= FAE_CHDIR
;
549 fae
->fae_dir
= strdup(path
);
550 if (fae
->fae_dir
== NULL
) {
556 STAILQ_INSERT_TAIL(&(*fa
)->fa_list
, fae
, fae_list
);
563 posix_spawn_file_actions_addfchdir_np (
564 posix_spawn_file_actions_t
* __restrict fa
,
567 posix_spawn_file_actions_entry_t
*fae
;
569 /* POSIX proposal documents it as implemented in FreeBSD and Musl.
570 Return EBADF if fd is negative.
571 https://www.austingroupbugs.net/view.php?id=1208 */
575 /* Allocate object */
576 fae
= malloc(sizeof(posix_spawn_file_actions_entry_t
));
580 /* Set values and store in queue */
581 fae
->fae_action
= FAE_FCHDIR
;
584 STAILQ_INSERT_TAIL(&(*fa
)->fa_list
, fae
, fae_list
);
594 posix_spawnattr_init (posix_spawnattr_t
*ret
)
596 posix_spawnattr_t sa
;
598 sa
= calloc(1, sizeof(struct __posix_spawnattr
));
602 /* Set defaults as specified by POSIX, cleared above */
608 posix_spawnattr_destroy (posix_spawnattr_t
*sa
)
615 posix_spawnattr_getflags (const posix_spawnattr_t
* __restrict sa
,
616 short * __restrict flags
)
618 *flags
= (*sa
)->sa_flags
;
623 posix_spawnattr_getpgroup (const posix_spawnattr_t
* __restrict sa
,
624 pid_t
* __restrict pgroup
)
626 *pgroup
= (*sa
)->sa_pgroup
;
631 posix_spawnattr_getschedparam (const posix_spawnattr_t
* __restrict sa
,
632 struct sched_param
* __restrict schedparam
)
634 *schedparam
= (*sa
)->sa_schedparam
;
639 posix_spawnattr_getschedpolicy (const posix_spawnattr_t
* __restrict sa
,
640 int * __restrict schedpolicy
)
642 *schedpolicy
= (*sa
)->sa_schedpolicy
;
647 posix_spawnattr_getsigdefault (const posix_spawnattr_t
* __restrict sa
,
648 sigset_t
* __restrict sigdefault
)
650 *sigdefault
= (*sa
)->sa_sigdefault
;
655 posix_spawnattr_getsigmask (const posix_spawnattr_t
* __restrict sa
,
656 sigset_t
* __restrict sigmask
)
658 *sigmask
= (*sa
)->sa_sigmask
;
663 posix_spawnattr_setflags (posix_spawnattr_t
*sa
,
666 (*sa
)->sa_flags
= flags
;
671 posix_spawnattr_setpgroup (posix_spawnattr_t
*sa
,
674 (*sa
)->sa_pgroup
= pgroup
;
679 posix_spawnattr_setschedparam (posix_spawnattr_t
* __restrict sa
,
680 const struct sched_param
* __restrict schedparam
)
682 (*sa
)->sa_schedparam
= *schedparam
;
687 posix_spawnattr_setschedpolicy (posix_spawnattr_t
*sa
,
690 (*sa
)->sa_schedpolicy
= schedpolicy
;
695 posix_spawnattr_setsigdefault (posix_spawnattr_t
* __restrict sa
,
696 const sigset_t
* __restrict sigdefault
)
698 (*sa
)->sa_sigdefault
= *sigdefault
;
703 posix_spawnattr_setsigmask (posix_spawnattr_t
* __restrict sa
,
704 const sigset_t
* __restrict sigmask
)
706 (*sa
)->sa_sigmask
= *sigmask
;
710 #endif /* !_NO_POSIX_SPAWN */