102a543dbSSiva Chandra Reddy //===-- Linux implementation of posix_spawn -------------------------------===// 202a543dbSSiva Chandra Reddy // 302a543dbSSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 402a543dbSSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information. 502a543dbSSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 602a543dbSSiva Chandra Reddy // 702a543dbSSiva Chandra Reddy //===----------------------------------------------------------------------===// 802a543dbSSiva Chandra Reddy 902a543dbSSiva Chandra Reddy #include "src/spawn/posix_spawn.h" 1002a543dbSSiva Chandra Reddy 1102a543dbSSiva Chandra Reddy #include "src/__support/CPP/optional.h" 1202a543dbSSiva Chandra Reddy #include "src/__support/OSUtil/syscall.h" // For internal syscall function. 1302a543dbSSiva Chandra Reddy #include "src/__support/common.h" 145ff3ff33SPetr Hosek #include "src/__support/macros/config.h" 1502a543dbSSiva Chandra Reddy #include "src/spawn/file_actions.h" 1602a543dbSSiva Chandra Reddy 17*e873b415SJob Henandez Lara #include "hdr/fcntl_macros.h" 18abc49cc1SJob Henandez Lara #include "hdr/types/mode_t.h" 19fe99de31SMikhail R. Gadelha #include <signal.h> // For SIGCHLD 2002a543dbSSiva Chandra Reddy #include <spawn.h> 2102a543dbSSiva Chandra Reddy #include <sys/syscall.h> // For syscall numbers. 2202a543dbSSiva Chandra Reddy 235ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL { 2402a543dbSSiva Chandra Reddy 2502a543dbSSiva Chandra Reddy namespace { 2602a543dbSSiva Chandra Reddy 2702a543dbSSiva Chandra Reddy pid_t fork() { 2802a543dbSSiva Chandra Reddy // TODO: Use only the clone syscall and use a sperate small stack in the child 2902a543dbSSiva Chandra Reddy // to avoid duplicating the complete stack from the parent. A new stack will 3002a543dbSSiva Chandra Reddy // be created on exec anyway so duplicating the full stack is unnecessary. 3102a543dbSSiva Chandra Reddy #ifdef SYS_fork 32b6bc9d72SGuillaume Chatelet return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_fork); 3302a543dbSSiva Chandra Reddy #elif defined(SYS_clone) 34b6bc9d72SGuillaume Chatelet return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_clone, SIGCHLD, 0); 3502a543dbSSiva Chandra Reddy #else 365b22df99SMikhail R. Gadelha #error "fork or clone syscalls not available." 3702a543dbSSiva Chandra Reddy #endif 3802a543dbSSiva Chandra Reddy } 3902a543dbSSiva Chandra Reddy 4002a543dbSSiva Chandra Reddy cpp::optional<int> open(const char *path, int oflags, mode_t mode) { 4102a543dbSSiva Chandra Reddy #ifdef SYS_open 42b6bc9d72SGuillaume Chatelet int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, oflags, mode); 4302a543dbSSiva Chandra Reddy #else 44b6bc9d72SGuillaume Chatelet int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags, 45b6bc9d72SGuillaume Chatelet mode); 4602a543dbSSiva Chandra Reddy #endif 4702a543dbSSiva Chandra Reddy if (fd > 0) 4802a543dbSSiva Chandra Reddy return fd; 4902a543dbSSiva Chandra Reddy // The open function is called as part of the child process' preparatory 5002a543dbSSiva Chandra Reddy // steps. If an open fails, the child process just exits. So, unlike 5102a543dbSSiva Chandra Reddy // the public open function, we do not need to set errno here. 5202a543dbSSiva Chandra Reddy return cpp::nullopt; 5302a543dbSSiva Chandra Reddy } 5402a543dbSSiva Chandra Reddy 55b6bc9d72SGuillaume Chatelet void close(int fd) { LIBC_NAMESPACE::syscall_impl<long>(SYS_close, fd); } 5602a543dbSSiva Chandra Reddy 57fe99de31SMikhail R. Gadelha // We use dup3 if dup2 is not available, similar to our implementation of dup2 5802a543dbSSiva Chandra Reddy bool dup2(int fd, int newfd) { 59fe99de31SMikhail R. Gadelha #ifdef SYS_dup2 60b6bc9d72SGuillaume Chatelet int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup2, fd, newfd); 61fe99de31SMikhail R. Gadelha #elif defined(SYS_dup3) 62b6bc9d72SGuillaume Chatelet int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup3, fd, newfd, 0); 63fe99de31SMikhail R. Gadelha #else 645b22df99SMikhail R. Gadelha #error "dup2 and dup3 syscalls not available." 65fe99de31SMikhail R. Gadelha #endif 6602a543dbSSiva Chandra Reddy return ret < 0 ? false : true; 6702a543dbSSiva Chandra Reddy } 6802a543dbSSiva Chandra Reddy 6902a543dbSSiva Chandra Reddy // All exits from child_process are error exits. So, we use a simple 7002a543dbSSiva Chandra Reddy // exit implementation which exits with code 127. 7102a543dbSSiva Chandra Reddy void exit() { 7202a543dbSSiva Chandra Reddy for (;;) { 73b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, 127); 74b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, 127); 7502a543dbSSiva Chandra Reddy } 7602a543dbSSiva Chandra Reddy } 7702a543dbSSiva Chandra Reddy 7802a543dbSSiva Chandra Reddy void child_process(const char *__restrict path, 7902a543dbSSiva Chandra Reddy const posix_spawn_file_actions_t *file_actions, 8002a543dbSSiva Chandra Reddy const posix_spawnattr_t *__restrict, // For now unused 8102a543dbSSiva Chandra Reddy char *const *__restrict argv, char *const *__restrict envp) { 8202a543dbSSiva Chandra Reddy // TODO: In the code below, the child_process just exits on error during 8302a543dbSSiva Chandra Reddy // processing |file_actions| and |attr|. The correct way would be to exit 8402a543dbSSiva Chandra Reddy // after conveying the information about the failure to the parent process 8502a543dbSSiva Chandra Reddy // (via a pipe for example). 8602a543dbSSiva Chandra Reddy // TODO: Handle |attr|. 8702a543dbSSiva Chandra Reddy 8802a543dbSSiva Chandra Reddy if (file_actions != nullptr) { 8902a543dbSSiva Chandra Reddy auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front); 9002a543dbSSiva Chandra Reddy while (act != nullptr) { 9102a543dbSSiva Chandra Reddy switch (act->type) { 9202a543dbSSiva Chandra Reddy case BaseSpawnFileAction::OPEN: { 9302a543dbSSiva Chandra Reddy auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act); 9402a543dbSSiva Chandra Reddy auto fd = open(open_act->path, open_act->oflag, open_act->mode); 9502a543dbSSiva Chandra Reddy if (!fd) 9602a543dbSSiva Chandra Reddy exit(); 9702a543dbSSiva Chandra Reddy int actual_fd = *fd; 9802a543dbSSiva Chandra Reddy if (actual_fd != open_act->fd) { 9902a543dbSSiva Chandra Reddy bool dup2_result = dup2(actual_fd, open_act->fd); 10002a543dbSSiva Chandra Reddy close(actual_fd); // The old fd is not needed anymore. 10102a543dbSSiva Chandra Reddy if (!dup2_result) 10202a543dbSSiva Chandra Reddy exit(); 10302a543dbSSiva Chandra Reddy } 10402a543dbSSiva Chandra Reddy break; 10502a543dbSSiva Chandra Reddy } 10602a543dbSSiva Chandra Reddy case BaseSpawnFileAction::CLOSE: { 10702a543dbSSiva Chandra Reddy auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act); 10802a543dbSSiva Chandra Reddy close(close_act->fd); 10902a543dbSSiva Chandra Reddy break; 11002a543dbSSiva Chandra Reddy } 11102a543dbSSiva Chandra Reddy case BaseSpawnFileAction::DUP2: { 11202a543dbSSiva Chandra Reddy auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act); 11302a543dbSSiva Chandra Reddy if (!dup2(dup2_act->fd, dup2_act->newfd)) 11402a543dbSSiva Chandra Reddy exit(); 11502a543dbSSiva Chandra Reddy break; 11602a543dbSSiva Chandra Reddy } 11702a543dbSSiva Chandra Reddy } 11802a543dbSSiva Chandra Reddy act = act->next; 11902a543dbSSiva Chandra Reddy } 12002a543dbSSiva Chandra Reddy } 12102a543dbSSiva Chandra Reddy 122b6bc9d72SGuillaume Chatelet if (LIBC_NAMESPACE::syscall_impl<long>(SYS_execve, path, argv, envp) < 0) 12302a543dbSSiva Chandra Reddy exit(); 12402a543dbSSiva Chandra Reddy } 12502a543dbSSiva Chandra Reddy 12602a543dbSSiva Chandra Reddy } // anonymous namespace 12702a543dbSSiva Chandra Reddy 12802a543dbSSiva Chandra Reddy LLVM_LIBC_FUNCTION(int, posix_spawn, 12902a543dbSSiva Chandra Reddy (pid_t *__restrict pid, const char *__restrict path, 13002a543dbSSiva Chandra Reddy const posix_spawn_file_actions_t *file_actions, 13102a543dbSSiva Chandra Reddy const posix_spawnattr_t *__restrict attr, 13202a543dbSSiva Chandra Reddy char *const *__restrict argv, 13302a543dbSSiva Chandra Reddy char *const *__restrict envp)) { 13402a543dbSSiva Chandra Reddy pid_t cpid = fork(); 13502a543dbSSiva Chandra Reddy if (cpid == 0) 13602a543dbSSiva Chandra Reddy child_process(path, file_actions, attr, argv, envp); 13702a543dbSSiva Chandra Reddy else if (cpid < 0) 13802a543dbSSiva Chandra Reddy return -cpid; 13902a543dbSSiva Chandra Reddy 14002a543dbSSiva Chandra Reddy if (pid != nullptr) 14102a543dbSSiva Chandra Reddy *pid = cpid; 14202a543dbSSiva Chandra Reddy 14302a543dbSSiva Chandra Reddy // TODO: Before returning, one should wait for the child_process to startup 14402a543dbSSiva Chandra Reddy // successfully. For now, we will just return. Future changes will add proper 14502a543dbSSiva Chandra Reddy // wait (using pipes for example). 14602a543dbSSiva Chandra Reddy 14702a543dbSSiva Chandra Reddy return 0; 14802a543dbSSiva Chandra Reddy } 14902a543dbSSiva Chandra Reddy 1505ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL 151