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