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-exception |
6 | // |
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 SIGCHLD |
20 | #include <spawn.h> |
21 | #include <sys/syscall.h> // For syscall numbers. |
22 | |
23 | namespace LIBC_NAMESPACE_DECL { |
24 | |
25 | namespace { |
26 | |
27 | pid_t fork() { |
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. |
31 | #ifdef SYS_fork |
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); |
35 | #else |
36 | #error "fork or clone syscalls not available." |
37 | #endif |
38 | } |
39 | |
40 | cpp::optional<int> open(const char *path, int oflags, mode_t mode) { |
41 | #ifdef SYS_open |
42 | int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, path, oflags, mode); |
43 | #else |
44 | int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags, |
45 | mode); |
46 | #endif |
47 | if (fd > 0) |
48 | return fd; |
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. |
52 | return cpp::nullopt; |
53 | } |
54 | |
55 | void 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 dup2 |
58 | bool dup2(int fd, int newfd) { |
59 | #ifdef SYS_dup2 |
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); |
63 | #else |
64 | #error "dup2 and dup3 syscalls not available." |
65 | #endif |
66 | return ret < 0 ? false : true; |
67 | } |
68 | |
69 | // All exits from child_process are error exits. So, we use a simple |
70 | // exit implementation which exits with code 127. |
71 | void 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 | |
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|. |
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(fd: 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 namespace |
127 | |
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)) { |
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 startup |
144 | // successfully. For now, we will just return. Future changes will add proper |
145 | // wait (using pipes for example). |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | } // namespace LIBC_NAMESPACE_DECL |
151 | |