Cygwin: mmap: allow remapping part of an existing anonymous mapping
[newlib-cygwin.git] / newlib / libc / posix / posix_spawn.c
blob6fd6159d054d11cb7a0aa2d5b8784c9ae5f4c1a9
1 /*-
2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
24 * SUCH DAMAGE.
28 FUNCTION
29 <<posix_spawn>>, <<posix_spawnp>>---spawn a process
31 INDEX
32 posix_spawn
33 INDEX
34 posix_spawnp
36 SYNOPSIS
37 #include <spawn.h>
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]>[]);
48 DESCRIPTION
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
53 new program.
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>>.
71 RETURNS
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.
80 PORTABILITY
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>
95 #include <sys/wait.h>
97 #include <errno.h>
98 #include <fcntl.h>
99 #include <sched.h>
100 #include <spawn.h>
101 #include <signal.h>
102 #include <stdlib.h>
103 #include <string.h>
104 #include <unistd.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
108 'environ'. */
109 static char ***p_environ = &environ;
111 struct __posix_spawnattr {
112 short sa_flags;
113 pid_t sa_pgroup;
114 struct sched_param sa_schedparam;
115 int sa_schedpolicy;
116 sigset_t sa_sigdefault;
117 sigset_t sa_sigmask;
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;
126 enum {
127 FAE_OPEN,
128 FAE_DUP2,
129 FAE_CLOSE,
130 FAE_CHDIR,
131 FAE_FCHDIR
132 } fae_action;
134 int fae_fildes;
135 union {
136 struct {
137 char *path;
138 #define fae_path fae_data.open.path
139 int oflag;
140 #define fae_oflag fae_data.open.oflag
141 mode_t mode;
142 #define fae_mode fae_data.open.mode
143 } open;
144 struct {
145 int newfildes;
146 #define fae_newfildes fae_data.dup2.newfildes
147 } dup2;
148 char *dir;
149 #define fae_dir fae_data.dir
150 int dirfd;
151 #define fae_dirfd fae_data.dirfd
152 } fae_data;
153 } posix_spawn_file_actions_entry_t;
156 * Spawn routines
159 static int
160 process_spawnattr(const posix_spawnattr_t sa)
162 struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL };
163 int i;
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
168 * are mentioned.
171 /* Set process group */
172 if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) {
173 if (setpgid(0, sa->sa_pgroup) != 0)
174 return (errno);
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)
181 return (errno);
182 } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) {
183 if (sched_setparam(0, &sa->sa_schedparam) != 0)
184 return (errno);
187 /* Reset user ID's */
188 if (sa->sa_flags & POSIX_SPAWN_RESETIDS) {
189 if (setegid(getgid()) != 0)
190 return (errno);
191 if (seteuid(getuid()) != 0)
192 return (errno);
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)
204 return (errno);
208 return (0);
211 static int
212 process_file_actions_entry(posix_spawn_file_actions_entry_t *fae)
214 int fd;
216 switch (fae->fae_action) {
217 case FAE_OPEN:
218 /* Perform an open(), make it use the right fd */
219 fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode);
220 if (fd < 0)
221 return (errno);
222 if (fd != fae->fae_fildes) {
223 if (dup2(fd, fae->fae_fildes) == -1)
224 return (errno);
225 if (_close(fd) != 0) {
226 if (errno == EBADF)
227 return (EBADF);
230 #ifdef HAVE_FCNTL
231 if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1)
232 return (errno);
233 #endif /* HAVE_FCNTL */
234 break;
235 case FAE_DUP2:
236 /* Perform a dup2() */
237 if (dup2(fae->fae_fildes, fae->fae_newfildes) == -1)
238 return (errno);
239 #ifdef HAVE_FCNTL
240 if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1)
241 return (errno);
242 #endif /* HAVE_FCNTL */
243 break;
244 case FAE_CLOSE:
245 /* Perform a close(), do not fail if already closed */
246 (void)_close(fae->fae_fildes);
247 break;
248 #ifdef HAVE_CHDIR
249 case FAE_CHDIR:
250 /* Perform a chdir. */
251 if (chdir (fae->fae_dir) == -1)
252 return (errno);
253 break;
254 #endif
255 #ifdef HAVE_FCHDIR
256 case FAE_FCHDIR:
257 /* Perform a chdir. */
258 if (fchdir (fae->fae_dirfd) == -1)
259 return (errno);
260 break;
262 #endif
263 return (0);
266 static int
267 process_file_actions(const posix_spawn_file_actions_t fa)
269 posix_spawn_file_actions_entry_t *fae;
270 int error;
272 /* Replay all file descriptor modifications */
273 STAILQ_FOREACH(fae, &fa->fa_list, fae_list) {
274 error = process_file_actions_entry(fae);
275 if (error)
276 return (error);
278 return (0);
281 #ifdef __CYGWIN__
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,
294 int use_env_path);
297 static int
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)
303 int error;
304 void *sem, *proc;
305 pid_t p;
307 error = __posix_spawn_sem_create(&sem);
308 if (error)
309 return error;
311 p = __posix_spawn_fork(&proc);
312 switch (p) {
313 case -1:
314 return (errno);
315 case 0:
316 if (sa != NULL) {
317 error = process_spawnattr(*sa);
318 if (error) {
319 __posix_spawn_sem_release(sem, error);
320 _exit(127);
323 if (fa != NULL) {
324 error = process_file_actions(*fa);
325 if (error) {
326 __posix_spawn_sem_release(sem, error);
327 _exit(127);
330 __posix_spawn_execvpe(path, argv,
331 envp != NULL ? envp : *p_environ,
332 sem, use_env_path);
333 _exit(127);
334 default:
335 error = __posix_spawn_sem_wait_and_close(sem, proc);
336 if (error != 0)
337 waitpid(p, NULL, WNOHANG);
338 else if (pid != NULL)
339 *pid = p;
340 return (error);
343 #else
344 static int
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)
350 pid_t p;
351 volatile int error = 0;
353 p = vfork();
354 switch (p) {
355 case -1:
356 return (errno);
357 case 0:
358 if (sa != NULL) {
359 error = process_spawnattr(*sa);
360 if (error)
361 _exit(127);
363 if (fa != NULL) {
364 error = process_file_actions(*fa);
365 if (error)
366 _exit(127);
368 if (use_env_path)
369 execvpe(path, argv, envp != NULL ? envp : *p_environ);
370 else
371 _execve(path, argv, envp != NULL ? envp : *p_environ);
372 error = errno;
373 _exit(127);
374 default:
375 if (error != 0)
376 waitpid(p, NULL, WNOHANG);
377 else if (pid != NULL)
378 *pid = p;
379 return (error);
382 #endif
385 posix_spawn (pid_t *pid,
386 const char *path,
387 const posix_spawn_file_actions_t *fa,
388 const posix_spawnattr_t *sa,
389 char * const argv[],
390 char * const envp[])
392 return do_posix_spawn(pid, path, fa, sa, argv, envp, 0);
396 posix_spawnp (pid_t *pid,
397 const char *path,
398 const posix_spawn_file_actions_t *fa,
399 const posix_spawnattr_t *sa,
400 char * const argv[],
401 char * const envp[])
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));
416 if (fa == NULL)
417 return (-1);
419 STAILQ_INIT(&fa->fa_list);
420 *ret = fa;
421 return (0);
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) {
435 case FAE_OPEN:
436 free(fae->fae_path);
437 break;
438 #ifdef HAVE_CHDIR
439 case FAE_CHDIR:
440 free(fae->fae_dir);
441 break;
442 #endif
443 default:
444 break;
446 free(fae);
449 free(*fa);
450 return (0);
454 posix_spawn_file_actions_addopen (posix_spawn_file_actions_t * __restrict fa,
455 int fildes,
456 const char * __restrict path,
457 int oflag,
458 mode_t mode)
460 posix_spawn_file_actions_entry_t *fae;
461 int error;
463 if (fildes < 0)
464 return (EBADF);
466 /* Allocate object */
467 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
468 if (fae == NULL)
469 return (errno);
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) {
475 error = errno;
476 free(fae);
477 return (error);
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);
484 return (0);
488 posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *fa,
489 int fildes,
490 int newfildes)
492 posix_spawn_file_actions_entry_t *fae;
494 if (fildes < 0 || newfildes < 0)
495 return (EBADF);
497 /* Allocate object */
498 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
499 if (fae == NULL)
500 return (errno);
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);
508 return (0);
512 posix_spawn_file_actions_addclose (posix_spawn_file_actions_t *fa,
513 int fildes)
515 posix_spawn_file_actions_entry_t *fae;
517 if (fildes < 0)
518 return (EBADF);
520 /* Allocate object */
521 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
522 if (fae == NULL)
523 return (errno);
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);
530 return (0);
533 #ifdef HAVE_CHDIR
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;
540 int error;
542 /* Allocate object */
543 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
544 if (fae == NULL)
545 return (errno);
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) {
551 error = errno;
552 free(fae);
553 return (error);
556 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
557 return (0);
559 #endif
561 #ifdef HAVE_FCHDIR
563 posix_spawn_file_actions_addfchdir_np (
564 posix_spawn_file_actions_t * __restrict fa,
565 int fd)
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 */
572 if (fd < 0)
573 return EBADF;
575 /* Allocate object */
576 fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
577 if (fae == NULL)
578 return (errno);
580 /* Set values and store in queue */
581 fae->fae_action = FAE_FCHDIR;
582 fae->fae_dirfd = fd;
584 STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
585 return (0);
587 #endif
590 * Spawn attributes
594 posix_spawnattr_init (posix_spawnattr_t *ret)
596 posix_spawnattr_t sa;
598 sa = calloc(1, sizeof(struct __posix_spawnattr));
599 if (sa == NULL)
600 return (errno);
602 /* Set defaults as specified by POSIX, cleared above */
603 *ret = sa;
604 return (0);
608 posix_spawnattr_destroy (posix_spawnattr_t *sa)
610 free(*sa);
611 return (0);
615 posix_spawnattr_getflags (const posix_spawnattr_t * __restrict sa,
616 short * __restrict flags)
618 *flags = (*sa)->sa_flags;
619 return (0);
623 posix_spawnattr_getpgroup (const posix_spawnattr_t * __restrict sa,
624 pid_t * __restrict pgroup)
626 *pgroup = (*sa)->sa_pgroup;
627 return (0);
631 posix_spawnattr_getschedparam (const posix_spawnattr_t * __restrict sa,
632 struct sched_param * __restrict schedparam)
634 *schedparam = (*sa)->sa_schedparam;
635 return (0);
639 posix_spawnattr_getschedpolicy (const posix_spawnattr_t * __restrict sa,
640 int * __restrict schedpolicy)
642 *schedpolicy = (*sa)->sa_schedpolicy;
643 return (0);
647 posix_spawnattr_getsigdefault (const posix_spawnattr_t * __restrict sa,
648 sigset_t * __restrict sigdefault)
650 *sigdefault = (*sa)->sa_sigdefault;
651 return (0);
655 posix_spawnattr_getsigmask (const posix_spawnattr_t * __restrict sa,
656 sigset_t * __restrict sigmask)
658 *sigmask = (*sa)->sa_sigmask;
659 return (0);
663 posix_spawnattr_setflags (posix_spawnattr_t *sa,
664 short flags)
666 (*sa)->sa_flags = flags;
667 return (0);
671 posix_spawnattr_setpgroup (posix_spawnattr_t *sa,
672 pid_t pgroup)
674 (*sa)->sa_pgroup = pgroup;
675 return (0);
679 posix_spawnattr_setschedparam (posix_spawnattr_t * __restrict sa,
680 const struct sched_param * __restrict schedparam)
682 (*sa)->sa_schedparam = *schedparam;
683 return (0);
687 posix_spawnattr_setschedpolicy (posix_spawnattr_t *sa,
688 int schedpolicy)
690 (*sa)->sa_schedpolicy = schedpolicy;
691 return (0);
695 posix_spawnattr_setsigdefault (posix_spawnattr_t * __restrict sa,
696 const sigset_t * __restrict sigdefault)
698 (*sa)->sa_sigdefault = *sigdefault;
699 return (0);
703 posix_spawnattr_setsigmask (posix_spawnattr_t * __restrict sa,
704 const sigset_t * __restrict sigmask)
706 (*sa)->sa_sigmask = *sigmask;
707 return (0);
710 #endif /* !_NO_POSIX_SPAWN */