brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.0 KiB · fe82ba2 Raw
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