/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be * avoided. Since this isn't called on those targets, the function * doesn't even need to be defined for them. */ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { sigset_t set; int close_fd; int use_fd; int err; int fd; int n; #if defined(__linux__) || defined(__FreeBSD__) int r; int i; int cpumask_size; uv__cpu_set_t cpuset; #endif if (options->flags & UV_PROCESS_DETACHED) setsid(); /* First duplicate low numbered fds, since it's not safe to duplicate them, * they could get replaced. Example: swapping stdout and stderr; without * this fd 2 (stderr) would be duplicated into fd 1, thus making both * stdout and stderr go to the same fd, which was not the intention. */ for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd < 0 || use_fd >= fd) continue; pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); if (pipes[fd][1] == -1) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } } for (fd = 0; fd < stdio_count; fd++) { close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is * set */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; if (use_fd == -1) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } } } if (fd == use_fd) uv__cloexec_fcntl(use_fd, 0); else fd = dup2(use_fd, fd); if (fd == -1) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } if (fd <= 2) uv__nonblock_fcntl(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); } for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd >= stdio_count) uv__close(use_fd); } if (options->cwd != NULL && chdir(options->cwd)) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will * remove any extraneous groups. If we don't call this, then * even though our uid has dropped, we may still have groups * that enable us to do super-user things. This will fail if we * aren't root, so don't bother checking the return value, this * is just done as an optimistic privilege dropping function. */ SAVE_ERRNO(setgroups(0, NULL)); } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } #if defined(__linux__) || defined(__FreeBSD__) if (options->cpumask != NULL) { cpumask_size = uv_cpumask_size(); assert(options->cpumask_size >= (size_t)cpumask_size); CPU_ZERO(&cpuset); for (i = 0; i < cpumask_size; ++i) { if (options->cpumask[i]) { CPU_SET(i, &cpuset); } } r = -pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); if (r != 0) { uv__write_int(error_fd, r); _exit(127); } } #endif if (options->env != NULL) { environ = options->env; } /* Reset signal disposition. Use a hard-coded limit because NSIG * is not fixed on Linux: it's either 32, 34 or 64, depending on * whether RT signals are enabled. We are not allowed to touch * RT signal handlers, glibc uses them internally. */ for (n = 1; n < 32; n += 1) { if (n == SIGKILL || n == SIGSTOP) continue; /* Can't be changed. */ if (SIG_ERR != signal(n, SIG_DFL)) continue; uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } /* Reset signal mask. */ sigemptyset(&set); err = pthread_sigmask(SIG_SETMASK, &set, NULL); if (err != 0) { uv__write_int(error_fd, UV__ERR(err)); _exit(127); } execvp(options->file, options->args); uv__write_int(error_fd, UV__ERR(errno)); _exit(127); }
static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { int close_fd; int use_fd; int fd; if (options->flags & UV_PROCESS_DETACHED) setsid(); for (fd = 0; fd < stdio_count; fd++) { close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is * set */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; if (use_fd == -1) { uv__write_int(error_fd, -errno); perror("failed to open stdio"); _exit(127); } } } if (close_fd != -1) { /* Can't call uv__close, since that might fail an assertion if close_fd=0 */ close(close_fd); } if (fd == use_fd) uv__cloexec(use_fd, 0); else dup2(use_fd, fd); if (fd <= 2) uv__nonblock(fd, 0); } for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd >= 0 && fd != use_fd) close(use_fd); } if (options->cwd != NULL && chdir(options->cwd)) { uv__write_int(error_fd, -errno); perror("chdir()"); _exit(127); } if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will * remove any extraneous groups. If we don't call this, then * even though our uid has dropped, we may still have groups * that enable us to do super-user things. This will fail if we * aren't root, so don't bother checking the return value, this * is just done as an optimistic privilege dropping function. */ SAVE_ERRNO(setgroups(0, NULL)); } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { uv__write_int(error_fd, -errno); perror("setgid()"); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { uv__write_int(error_fd, -errno); perror("setuid()"); _exit(127); } if (options->env != NULL) { environ = options->env; } execvp(options->file, options->args); uv__write_int(error_fd, -errno); perror("execvp()"); _exit(127); }
static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { int close_fd; int use_fd; int fd; if (options->flags & UV_PROCESS_DETACHED) setsid(); /* First duplicate low numbered fds, since it's not safe to duplicate them, * they could get replaced. Example: swapping stdout and stderr; without * this fd 2 (stderr) would be duplicated into fd 1, thus making both * stdout and stderr go to the same fd, which was not the intention. */ for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd < 0 || use_fd >= fd) continue; pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); if (pipes[fd][1] == -1) { uv__write_int(error_fd, -errno); _exit(127); } } for (fd = 0; fd < stdio_count; fd++) { close_fd = pipes[fd][0]; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is * set */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; if (use_fd == -1) { uv__write_int(error_fd, -errno); _exit(127); } } } if (fd == use_fd) uv__cloexec(use_fd, 0); else fd = dup2(use_fd, fd); if (fd == -1) { uv__write_int(error_fd, -errno); _exit(127); } if (fd <= 2) uv__nonblock(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); } for (fd = 0; fd < stdio_count; fd++) { use_fd = pipes[fd][1]; if (use_fd >= stdio_count) uv__close(use_fd); } if (options->cwd != NULL && chdir(options->cwd)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will * remove any extraneous groups. If we don't call this, then * even though our uid has dropped, we may still have groups * that enable us to do super-user things. This will fail if we * aren't root, so don't bother checking the return value, this * is just done as an optimistic privilege dropping function. */ SAVE_ERRNO(setgroups(0, NULL)); } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { uv__write_int(error_fd, -errno); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { uv__write_int(error_fd, -errno); _exit(127); } if (options->env != NULL) { environ = options->env; } execvp(options->file, options->args); uv__write_int(error_fd, -errno); _exit(127); }