github.com/afumu/libc@v0.0.6/musl/src/process/posix_spawn.c (about)

     1  #define _GNU_SOURCE
     2  #include <spawn.h>
     3  #include <sched.h>
     4  #include <unistd.h>
     5  #include <signal.h>
     6  #include <fcntl.h>
     7  #include <sys/wait.h>
     8  #include "syscall.h"
     9  #include "pthread_impl.h"
    10  #include "fdop.h"
    11  
    12  struct args {
    13  	int p[2];
    14  	sigset_t oldmask;
    15  	const char *path;
    16  	const posix_spawn_file_actions_t *fa;
    17  	const posix_spawnattr_t *restrict attr;
    18  	char *const *argv, *const *envp;
    19  };
    20  
    21  static int __sys_dup2(int old, int new)
    22  {
    23  #ifdef SYS_dup2
    24  	return __syscall(SYS_dup2, old, new);
    25  #else
    26  	return __syscall(SYS_dup3, old, new, 0);
    27  #endif
    28  }
    29  
    30  static int child(void *args_vp)
    31  {
    32  	int i, ret;
    33  	struct sigaction sa = {0};
    34  	struct args *args = args_vp;
    35  	int p = args->p[1];
    36  	const posix_spawn_file_actions_t *fa = args->fa;
    37  	const posix_spawnattr_t *restrict attr = args->attr;
    38  	sigset_t hset;
    39  
    40  	close(args->p[0]);
    41  
    42  	/* All signal dispositions must be either SIG_DFL or SIG_IGN
    43  	 * before signals are unblocked. Otherwise a signal handler
    44  	 * from the parent might get run in the child while sharing
    45  	 * memory, with unpredictable and dangerous results. To
    46  	 * reduce overhead, sigaction has tracked for us which signals
    47  	 * potentially have a signal handler. */
    48  	__get_handler_set(&hset);
    49  	for (i=1; i<_NSIG; i++) {
    50  		if ((attr->__flags & POSIX_SPAWN_SETSIGDEF)
    51  		     && sigismember(&attr->__def, i)) {
    52  			sa.sa_handler = SIG_DFL;
    53  		} else if (sigismember(&hset, i)) {
    54  			if (i-32<3U) {
    55  				sa.sa_handler = SIG_IGN;
    56  			} else {
    57  				__libc_sigaction(i, 0, &sa);
    58  				if (sa.sa_handler==SIG_IGN) continue;
    59  				sa.sa_handler = SIG_DFL;
    60  			}
    61  		} else {
    62  			continue;
    63  		}
    64  		__libc_sigaction(i, &sa, 0);
    65  	}
    66  
    67  	if (attr->__flags & POSIX_SPAWN_SETSID)
    68  		if ((ret=__syscall(SYS_setsid)) < 0)
    69  			goto fail;
    70  
    71  	if (attr->__flags & POSIX_SPAWN_SETPGROUP)
    72  		if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp)))
    73  			goto fail;
    74  
    75  	/* Use syscalls directly because the library functions attempt
    76  	 * to do a multi-threaded synchronized id-change, which would
    77  	 * trash the parent's state. */
    78  	if (attr->__flags & POSIX_SPAWN_RESETIDS)
    79  		if ((ret=__syscall(SYS_setgid, __syscall(SYS_getgid))) ||
    80  		    (ret=__syscall(SYS_setuid, __syscall(SYS_getuid))) )
    81  			goto fail;
    82  
    83  	if (fa && fa->__actions) {
    84  		struct fdop *op;
    85  		int fd;
    86  		for (op = fa->__actions; op->next; op = op->next);
    87  		for (; op; op = op->prev) {
    88  			/* It's possible that a file operation would clobber
    89  			 * the pipe fd used for synchronizing with the
    90  			 * parent. To avoid that, we dup the pipe onto
    91  			 * an unoccupied fd. */
    92  			if (op->fd == p) {
    93  				ret = __syscall(SYS_dup, p);
    94  				if (ret < 0) goto fail;
    95  				__syscall(SYS_close, p);
    96  				p = ret;
    97  			}
    98  			switch(op->cmd) {
    99  			case FDOP_CLOSE:
   100  				__syscall(SYS_close, op->fd);
   101  				break;
   102  			case FDOP_DUP2:
   103  				fd = op->srcfd;
   104  				if (fd == p) {
   105  					ret = -EBADF;
   106  					goto fail;
   107  				}
   108  				if (fd != op->fd) {
   109  					if ((ret=__sys_dup2(fd, op->fd))<0)
   110  						goto fail;
   111  				} else {
   112  					ret = __syscall(SYS_fcntl, fd, F_GETFD);
   113  					ret = __syscall(SYS_fcntl, fd, F_SETFD,
   114  					                ret & ~FD_CLOEXEC);
   115  					if (ret<0)
   116  						goto fail;
   117  				}
   118  				break;
   119  			case FDOP_OPEN:
   120  				fd = __sys_open(op->path, op->oflag, op->mode);
   121  				if ((ret=fd) < 0) goto fail;
   122  				if (fd != op->fd) {
   123  					if ((ret=__sys_dup2(fd, op->fd))<0)
   124  						goto fail;
   125  					__syscall(SYS_close, fd);
   126  				}
   127  				break;
   128  			case FDOP_CHDIR:
   129  				ret = __syscall(SYS_chdir, op->path);
   130  				if (ret<0) goto fail;
   131  				break;
   132  			case FDOP_FCHDIR:
   133  				ret = __syscall(SYS_fchdir, op->fd);
   134  				if (ret<0) goto fail;
   135  				break;
   136  			}
   137  		}
   138  	}
   139  
   140  	/* Close-on-exec flag may have been lost if we moved the pipe
   141  	 * to a different fd. We don't use F_DUPFD_CLOEXEC above because
   142  	 * it would fail on older kernels and atomicity is not needed --
   143  	 * in this process there are no threads or signal handlers. */
   144  	__syscall(SYS_fcntl, p, F_SETFD, FD_CLOEXEC);
   145  
   146  	pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
   147  		? &attr->__mask : &args->oldmask, 0);
   148  
   149  	int (*exec)(const char *, char *const *, char *const *) =
   150  		attr->__fn ? (int (*)())attr->__fn : execve;
   151  
   152  	exec(args->path, args->argv, args->envp);
   153  	ret = -errno;
   154  
   155  fail:
   156  	/* Since sizeof errno < PIPE_BUF, the write is atomic. */
   157  	ret = -ret;
   158  	if (ret) while (__syscall(SYS_write, p, &ret, sizeof ret) < 0);
   159  	_exit(127);
   160  }
   161  
   162  
   163  int posix_spawn(pid_t *restrict res, const char *restrict path,
   164  	const posix_spawn_file_actions_t *fa,
   165  	const posix_spawnattr_t *restrict attr,
   166  	char *const argv[restrict], char *const envp[restrict])
   167  {
   168  	pid_t pid;
   169  	char stack[1024+PATH_MAX];
   170  	int ec=0, cs;
   171  	struct args args;
   172  
   173  	if (pipe2(args.p, O_CLOEXEC))
   174  		return errno;
   175  
   176  	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
   177  
   178  	args.path = path;
   179  	args.fa = fa;
   180  	args.attr = attr ? attr : &(const posix_spawnattr_t){0};
   181  	args.argv = argv;
   182  	args.envp = envp;
   183  	pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask);
   184  
   185  	pid = __clone(child, stack+sizeof stack,
   186  		CLONE_VM|CLONE_VFORK|SIGCHLD, &args);
   187  	close(args.p[1]);
   188  
   189  	if (pid > 0) {
   190  		if (read(args.p[0], &ec, sizeof ec) != sizeof ec) ec = 0;
   191  		else waitpid(pid, &(int){0}, 0);
   192  	} else {
   193  		ec = -pid;
   194  	}
   195  
   196  	close(args.p[0]);
   197  
   198  	if (!ec && res) *res = pid;
   199  
   200  	pthread_sigmask(SIG_SETMASK, &args.oldmask, 0);
   201  	pthread_setcancelstate(cs, 0);
   202  
   203  	return ec;
   204  }