1 //===-- Linux implementation of posix_spawn -------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "src/spawn/posix_spawn.h"
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"
17 #include "hdr/fcntl_macros.h"
18 #include "hdr/types/mode_t.h"
19 #include <signal.h> // For SIGCHLD
21 #include <sys/syscall.h> // For syscall numbers.
23 namespace LIBC_NAMESPACE_DECL
{
28 // TODO: Use only the clone syscall and use a sperate small stack in the child
29 // to avoid duplicating the complete stack from the parent. A new stack will
30 // be created on exec anyway so duplicating the full stack is unnecessary.
32 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);
36 #error "fork or clone syscalls not available."
40 cpp::optional
<int> open(const char *path
, int oflags
, mode_t mode
) {
42 int fd
= LIBC_NAMESPACE::syscall_impl
<int>(SYS_open
, path
, oflags
, mode
);
44 int fd
= LIBC_NAMESPACE::syscall_impl
<int>(SYS_openat
, AT_FDCWD
, path
, oflags
,
49 // The open function is called as part of the child process' preparatory
50 // steps. If an open fails, the child process just exits. So, unlike
51 // the public open function, we do not need to set errno here.
55 void close(int fd
) { LIBC_NAMESPACE::syscall_impl
<long>(SYS_close
, fd
); }
57 // We use dup3 if dup2 is not available, similar to our implementation of dup2
58 bool dup2(int fd
, int newfd
) {
60 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);
64 #error "dup2 and dup3 syscalls not available."
66 return ret
< 0 ? false : true;
69 // All exits from child_process are error exits. So, we use a simple
70 // exit implementation which exits with code 127.
73 LIBC_NAMESPACE::syscall_impl
<long>(SYS_exit_group
, 127);
74 LIBC_NAMESPACE::syscall_impl
<long>(SYS_exit
, 127);
78 void child_process(const char *__restrict path
,
79 const posix_spawn_file_actions_t
*file_actions
,
80 const posix_spawnattr_t
*__restrict
, // For now unused
81 char *const *__restrict argv
, char *const *__restrict envp
) {
82 // TODO: In the code below, the child_process just exits on error during
83 // processing |file_actions| and |attr|. The correct way would be to exit
84 // after conveying the information about the failure to the parent process
85 // (via a pipe for example).
86 // TODO: Handle |attr|.
88 if (file_actions
!= nullptr) {
89 auto *act
= reinterpret_cast<BaseSpawnFileAction
*>(file_actions
->__front
);
90 while (act
!= nullptr) {
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
);
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.
106 case BaseSpawnFileAction::CLOSE
: {
107 auto *close_act
= reinterpret_cast<SpawnFileCloseAction
*>(act
);
108 close(close_act
->fd
);
111 case BaseSpawnFileAction::DUP2
: {
112 auto *dup2_act
= reinterpret_cast<SpawnFileDup2Action
*>(act
);
113 if (!dup2(dup2_act
->fd
, dup2_act
->newfd
))
122 if (LIBC_NAMESPACE::syscall_impl
<long>(SYS_execve
, path
, argv
, envp
) < 0)
126 } // anonymous namespace
128 LLVM_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
)) {
136 child_process(path
, file_actions
, attr
, argv
, envp
);
143 // TODO: Before returning, one should wait for the child_process to startup
144 // successfully. For now, we will just return. Future changes will add proper
145 // wait (using pipes for example).
150 } // namespace LIBC_NAMESPACE_DECL