1#include <fcntl.h> 2#include <unistd.h> 3#include <errno.h> 4#include <string.h> 5#include <spawn.h> 6#include "stdio_impl.h" 7#include "syscall.h" 8 9extern char **__environ; 10 11FILE *popen(const char *cmd, const char *mode) 12{ 13 int p[2], op, e; 14 pid_t pid; 15 FILE *f; 16 posix_spawn_file_actions_t fa; 17 18 if (*mode == 'r') { 19 op = 0; 20 } else if (*mode == 'w') { 21 op = 1; 22 } else { 23 errno = EINVAL; 24 return 0; 25 } 26 27 if (pipe2(p, O_CLOEXEC)) return NULL; 28 f = fdopen(p[op], mode); 29 if (!f) { 30 __syscall(SYS_close, p[0]); 31 __syscall(SYS_close, p[1]); 32 return NULL; 33 } 34 FLOCK(f); 35 36 /* If the child's end of the pipe happens to already be on the final 37 * fd number to which it will be assigned (either 0 or 1), it must 38 * be moved to a different fd. Otherwise, there is no safe way to 39 * remove the close-on-exec flag in the child without also creating 40 * a file descriptor leak race condition in the parent. */ 41 if (p[1-op] == 1-op) { 42 int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0); 43 if (tmp < 0) { 44 e = errno; 45 goto fail; 46 } 47 __syscall(SYS_close, p[1-op]); 48 p[1-op] = tmp; 49 } 50 51 e = ENOMEM; 52 if (!posix_spawn_file_actions_init(&fa)) { 53 if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) { 54 if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0, 55 (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) { 56 posix_spawn_file_actions_destroy(&fa); 57 f->pipe_pid = pid; 58 if (!strchr(mode, 'e')) 59 fcntl(p[op], F_SETFD, 0); 60 __syscall(SYS_close, p[1-op]); 61 FUNLOCK(f); 62 return f; 63 } 64 } 65 posix_spawn_file_actions_destroy(&fa); 66 } 67fail: 68 fclose(f); 69 __syscall(SYS_close, p[1-op]); 70 71 errno = e; 72 return 0; 73} 74