1 //===-- Linux implementation of posix_spawn -------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "src/spawn/posix_spawn.h" 10 11 #include "src/__support/CPP/optional.h" 12 #include "src/__support/OSUtil/syscall.h" // For internal syscall function. 13 #include "src/__support/common.h" 14 #include "src/__support/macros/config.h" 15 #include "src/spawn/file_actions.h" 16 17 #include "hdr/fcntl_macros.h" 18 #include "hdr/types/mode_t.h" 19 #include <signal.h> // For SIGCHLD 20 #include <spawn.h> 21 #include <sys/syscall.h> // For syscall numbers. 22 23 namespace LIBC_NAMESPACE_DECL { 24 25 namespace { 26 27 pid_t fork() { 28 // TODO: Use only the clone syscall and use a sperate small stack in the child 29 // to avoid duplicating the complete stack from the parent. A new stack will 30 // be created on exec anyway so duplicating the full stack is unnecessary. 31 #ifdef SYS_fork 32 return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_fork); 33 #elif defined(SYS_clone) 34 return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_clone, SIGCHLD, 0); 35 #else 36 #error "fork or clone syscalls not available." 37 #endif 38 } 39 40 cpp::optional<int> open(const char *path, int oflags, mode_t mode) { 41 #ifdef SYS_open 42 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, oflags, mode); 43 #else 44 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags, 45 mode); 46 #endif 47 if (fd > 0) 48 return fd; 49 // The open function is called as part of the child process' preparatory 50 // steps. If an open fails, the child process just exits. So, unlike 51 // the public open function, we do not need to set errno here. 52 return cpp::nullopt; 53 } 54 55 void close(int fd) { LIBC_NAMESPACE::syscall_impl<long>(SYS_close, fd); } 56 57 // We use dup3 if dup2 is not available, similar to our implementation of dup2 58 bool dup2(int fd, int newfd) { 59 #ifdef SYS_dup2 60 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup2, fd, newfd); 61 #elif defined(SYS_dup3) 62 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup3, fd, newfd, 0); 63 #else 64 #error "dup2 and dup3 syscalls not available." 65 #endif 66 return ret < 0 ? false : true; 67 } 68 69 // All exits from child_process are error exits. So, we use a simple 70 // exit implementation which exits with code 127. 71 void exit() { 72 for (;;) { 73 LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, 127); 74 LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 127); 75 } 76 } 77 78 void child_process(const char *__restrict path, 79 const posix_spawn_file_actions_t *file_actions, 80 const posix_spawnattr_t *__restrict, // For now unused 81 char *const *__restrict argv, char *const *__restrict envp) { 82 // TODO: In the code below, the child_process just exits on error during 83 // processing |file_actions| and |attr|. The correct way would be to exit 84 // after conveying the information about the failure to the parent process 85 // (via a pipe for example). 86 // TODO: Handle |attr|. 87 88 if (file_actions != nullptr) { 89 auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front); 90 while (act != nullptr) { 91 switch (act->type) { 92 case BaseSpawnFileAction::OPEN: { 93 auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act); 94 auto fd = open(open_act->path, open_act->oflag, open_act->mode); 95 if (!fd) 96 exit(); 97 int actual_fd = *fd; 98 if (actual_fd != open_act->fd) { 99 bool dup2_result = dup2(actual_fd, open_act->fd); 100 close(actual_fd); // The old fd is not needed anymore. 101 if (!dup2_result) 102 exit(); 103 } 104 break; 105 } 106 case BaseSpawnFileAction::CLOSE: { 107 auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act); 108 close(close_act->fd); 109 break; 110 } 111 case BaseSpawnFileAction::DUP2: { 112 auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act); 113 if (!dup2(dup2_act->fd, dup2_act->newfd)) 114 exit(); 115 break; 116 } 117 } 118 act = act->next; 119 } 120 } 121 122 if (LIBC_NAMESPACE::syscall_impl<long>(SYS_execve, path, argv, envp) < 0) 123 exit(); 124 } 125 126 } // anonymous namespace 127 128 LLVM_LIBC_FUNCTION(int, posix_spawn, 129 (pid_t *__restrict pid, const char *__restrict path, 130 const posix_spawn_file_actions_t *file_actions, 131 const posix_spawnattr_t *__restrict attr, 132 char *const *__restrict argv, 133 char *const *__restrict envp)) { 134 pid_t cpid = fork(); 135 if (cpid == 0) 136 child_process(path, file_actions, attr, argv, envp); 137 else if (cpid < 0) 138 return -cpid; 139 140 if (pid != nullptr) 141 *pid = cpid; 142 143 // TODO: Before returning, one should wait for the child_process to startup 144 // successfully. For now, we will just return. Future changes will add proper 145 // wait (using pipes for example). 146 147 return 0; 148 } 149 150 } // namespace LIBC_NAMESPACE_DECL 151