xref: /llvm-project/libc/src/spawn/linux/posix_spawn.cpp (revision e873b415a2bfbbc2e5e2e70d77b61b7883cbf949)
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