diff options
| author | Siva Chandra Reddy <sivachandra@google.com> | 2022-10-11 00:38:29 +0000 |
|---|---|---|
| committer | Siva Chandra Reddy <sivachandra@google.com> | 2022-10-13 18:47:47 +0000 |
| commit | 02a543db66518b93b126feed43080feca0b14bd2 (patch) | |
| tree | b461c9778fbcd57ae16b5437141dbbeb6c147423 /libc/src/spawn | |
| parent | dde9db5f9313b4c63cd1c9a28d122dd5d9901737 (diff) | |
[libc] Add a simple implementation of the posix_spawn function.
The implementation currently ignores all spawn attributes. Support for
them will be added in future changes.
A simple allocator for integration tests has been added so that the
integration test for posix_spawn can use the
posix_spawn_file_actions_add* functions.
Reviewed By: michaelrj
Differential Revision: https://reviews.llvm.org/D135752
Diffstat (limited to 'libc/src/spawn')
| -rw-r--r-- | libc/src/spawn/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | libc/src/spawn/linux/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | libc/src/spawn/linux/posix_spawn.cpp | 139 | ||||
| -rw-r--r-- | libc/src/spawn/posix_spawn.h | 23 |
4 files changed, 187 insertions, 0 deletions
diff --git a/libc/src/spawn/CMakeLists.txt b/libc/src/spawn/CMakeLists.txt index 95a0c7436d26..fd929fa0cee1 100644 --- a/libc/src/spawn/CMakeLists.txt +++ b/libc/src/spawn/CMakeLists.txt @@ -1,3 +1,7 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + add_header_library( file_actions HDRS @@ -63,3 +67,10 @@ add_entrypoint_object( libc.include.errno libc.include.spawn ) + +add_entrypoint_object( + posix_spawn + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.posix_spawn +) diff --git a/libc/src/spawn/linux/CMakeLists.txt b/libc/src/spawn/linux/CMakeLists.txt new file mode 100644 index 000000000000..9bd3ac50415b --- /dev/null +++ b/libc/src/spawn/linux/CMakeLists.txt @@ -0,0 +1,14 @@ +add_entrypoint_object( + posix_spawn + SRCS + posix_spawn.cpp + HDRS + ../posix_spawn.h + DEPENDS + libc.include.fcntl + libc.include.spawn + libc.include.sys_syscall + libc.src.__support.CPP.optional + libc.src.__support.OSUtil.osutil + libc.src.spawn.file_actions +) diff --git a/libc/src/spawn/linux/posix_spawn.cpp b/libc/src/spawn/linux/posix_spawn.cpp new file mode 100644 index 000000000000..2f8287238ec8 --- /dev/null +++ b/libc/src/spawn/linux/posix_spawn.cpp @@ -0,0 +1,139 @@ +//===-- Linux implementation of posix_spawn -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/spawn/posix_spawn.h" + +#include "src/__support/CPP/optional.h" +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/spawn/file_actions.h" + +#include <fcntl.h> +#include <spawn.h> +#include <sys/syscall.h> // For syscall numbers. + +namespace __llvm_libc { + +namespace { + +pid_t fork() { + // TODO: Use only the clone syscall and use a sperate small stack in the child + // to avoid duplicating the complete stack from the parent. A new stack will + // be created on exec anyway so duplicating the full stack is unnecessary. +#ifdef SYS_fork + return __llvm_libc::syscall_impl(SYS_fork); +#elif defined(SYS_clone) + return __llvm_libc::syscall_impl(SYS_clone, SIGCHLD, 0); +#else +#error "SYS_fork or SYS_clone not available." +#endif +} + +cpp::optional<int> open(const char *path, int oflags, mode_t mode) { +#ifdef SYS_open + int fd = __llvm_libc::syscall_impl(SYS_open, path, oflags, mode); +#else + int fd = __llvm_libc::syscall_impl(SYS_openat, AT_FDCWD, path, oflags, mode); +#endif + if (fd > 0) + return fd; + // The open function is called as part of the child process' preparatory + // steps. If an open fails, the child process just exits. So, unlike + // the public open function, we do not need to set errno here. + return cpp::nullopt; +} + +void close(int fd) { __llvm_libc::syscall_impl(SYS_close, fd); } + +bool dup2(int fd, int newfd) { + long ret = __llvm_libc::syscall_impl(SYS_dup2, fd, newfd); + return ret < 0 ? false : true; +} + +// All exits from child_process are error exits. So, we use a simple +// exit implementation which exits with code 127. +void exit() { + for (;;) { + __llvm_libc::syscall_impl(SYS_exit_group, 127); + __llvm_libc::syscall_impl(SYS_exit, 127); + } +} + +void child_process(const char *__restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *__restrict, // For now unused + char *const *__restrict argv, char *const *__restrict envp) { + // TODO: In the code below, the child_process just exits on error during + // processing |file_actions| and |attr|. The correct way would be to exit + // after conveying the information about the failure to the parent process + // (via a pipe for example). + // TODO: Handle |attr|. + + if (file_actions != nullptr) { + auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front); + while (act != nullptr) { + switch (act->type) { + case BaseSpawnFileAction::OPEN: { + auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act); + auto fd = open(open_act->path, open_act->oflag, open_act->mode); + if (!fd) + exit(); + int actual_fd = *fd; + if (actual_fd != open_act->fd) { + bool dup2_result = dup2(actual_fd, open_act->fd); + close(actual_fd); // The old fd is not needed anymore. + if (!dup2_result) + exit(); + } + break; + } + case BaseSpawnFileAction::CLOSE: { + auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act); + close(close_act->fd); + break; + } + case BaseSpawnFileAction::DUP2: { + auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act); + if (!dup2(dup2_act->fd, dup2_act->newfd)) + exit(); + break; + } + } + act = act->next; + } + } + + if (__llvm_libc::syscall_impl(SYS_execve, path, argv, envp) < 0) + exit(); +} + +} // anonymous namespace + +LLVM_LIBC_FUNCTION(int, posix_spawn, + (pid_t *__restrict pid, const char *__restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *__restrict attr, + char *const *__restrict argv, + char *const *__restrict envp)) { + pid_t cpid = fork(); + if (cpid == 0) + child_process(path, file_actions, attr, argv, envp); + else if (cpid < 0) + return -cpid; + + if (pid != nullptr) + *pid = cpid; + + // TODO: Before returning, one should wait for the child_process to startup + // successfully. For now, we will just return. Future changes will add proper + // wait (using pipes for example). + + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/src/spawn/posix_spawn.h b/libc/src/spawn/posix_spawn.h new file mode 100644 index 000000000000..721462701b25 --- /dev/null +++ b/libc/src/spawn/posix_spawn.h @@ -0,0 +1,23 @@ +//===-- Implementation header for posix_spawn -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SPAWN_POSIX_SPAWN_H +#define LLVM_LIBC_SRC_SPAWN_POSIX_SPAWN_H + +#include <spawn.h> + +namespace __llvm_libc { + +int posix_spawn(pid_t *__restrict pid, const char *__restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *__restrict attr, + char *const *__restrict argv, char *const *__restrict envp); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SPAWN_POSIX_SPAWN_H |
