151 lines · cpp
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-exception6//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 SIGCHLD20#include <spawn.h>21#include <sys/syscall.h> // For syscall numbers.22 23namespace LIBC_NAMESPACE_DECL {24 25namespace {26 27pid_t fork() {28 // TODO: Use only the clone syscall and use a sperate small stack in the child29 // to avoid duplicating the complete stack from the parent. A new stack will30 // be created on exec anyway so duplicating the full stack is unnecessary.31#ifdef SYS_fork32 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#else36#error "fork or clone syscalls not available."37#endif38}39 40cpp::optional<int> open(const char *path, int oflags, mode_t mode) {41#ifdef SYS_open42 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, oflags, mode);43#else44 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags,45 mode);46#endif47 if (fd > 0)48 return fd;49 // The open function is called as part of the child process' preparatory50 // steps. If an open fails, the child process just exits. So, unlike51 // the public open function, we do not need to set errno here.52 return cpp::nullopt;53}54 55void 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 dup258bool dup2(int fd, int newfd) {59#ifdef SYS_dup260 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#else64#error "dup2 and dup3 syscalls not available."65#endif66 return ret < 0 ? false : true;67}68 69// All exits from child_process are error exits. So, we use a simple70// exit implementation which exits with code 127.71void 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 78void child_process(const char *__restrict path,79 const posix_spawn_file_actions_t *file_actions,80 const posix_spawnattr_t *__restrict, // For now unused81 char *const *__restrict argv, char *const *__restrict envp) {82 // TODO: In the code below, the child_process just exits on error during83 // processing |file_actions| and |attr|. The correct way would be to exit84 // after conveying the information about the failure to the parent process85 // (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 namespace127 128LLVM_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 startup144 // successfully. For now, we will just return. Future changes will add proper145 // wait (using pipes for example).146 147 return 0;148}149 150} // namespace LIBC_NAMESPACE_DECL151