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/spawn/file_actions.h"
15
16#include <fcntl.h>
17#include <signal.h> // For SIGCHLD
18#include <spawn.h>
19#include <sys/syscall.h> // For syscall numbers.
20
21namespace LIBC_NAMESPACE {
22
23namespace {
24
25pid_t fork() {
26 // TODO: Use only the clone syscall and use a sperate small stack in the child
27 // to avoid duplicating the complete stack from the parent. A new stack will
28 // be created on exec anyway so duplicating the full stack is unnecessary.
29#ifdef SYS_fork
30 return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_fork);
31#elif defined(SYS_clone)
32 return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_clone, SIGCHLD, 0);
33#else
34#error "fork or clone syscalls not available."
35#endif
36}
37
38cpp::optional<int> open(const char *path, int oflags, mode_t mode) {
39#ifdef SYS_open
40 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_open, ts: path, ts: oflags, ts: mode);
41#else
42 int fd = LIBC_NAMESPACE::syscall_impl<int>(SYS_openat, AT_FDCWD, path, oflags,
43 mode);
44#endif
45 if (fd > 0)
46 return fd;
47 // The open function is called as part of the child process' preparatory
48 // steps. If an open fails, the child process just exits. So, unlike
49 // the public open function, we do not need to set errno here.
50 return cpp::nullopt;
51}
52
53void close(int fd) { LIBC_NAMESPACE::syscall_impl<long>(SYS_close, ts: fd); }
54
55// We use dup3 if dup2 is not available, similar to our implementation of dup2
56bool dup2(int fd, int newfd) {
57#ifdef SYS_dup2
58 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup2, ts: fd, ts: newfd);
59#elif defined(SYS_dup3)
60 int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_dup3, fd, newfd, 0);
61#else
62#error "dup2 and dup3 syscalls not available."
63#endif
64 return ret < 0 ? false : true;
65}
66
67// All exits from child_process are error exits. So, we use a simple
68// exit implementation which exits with code 127.
69void exit() {
70 for (;;) {
71 LIBC_NAMESPACE::syscall_impl<long>(SYS_exit_group, ts: 127);
72 LIBC_NAMESPACE::syscall_impl<long>(SYS_exit, ts: 127);
73 }
74}
75
76void child_process(const char *__restrict path,
77 const posix_spawn_file_actions_t *file_actions,
78 const posix_spawnattr_t *__restrict, // For now unused
79 char *const *__restrict argv, char *const *__restrict envp) {
80 // TODO: In the code below, the child_process just exits on error during
81 // processing |file_actions| and |attr|. The correct way would be to exit
82 // after conveying the information about the failure to the parent process
83 // (via a pipe for example).
84 // TODO: Handle |attr|.
85
86 if (file_actions != nullptr) {
87 auto *act = reinterpret_cast<BaseSpawnFileAction *>(file_actions->__front);
88 while (act != nullptr) {
89 switch (act->type) {
90 case BaseSpawnFileAction::OPEN: {
91 auto *open_act = reinterpret_cast<SpawnFileOpenAction *>(act);
92 auto fd = open(open_act->path, open_act->oflag, open_act->mode);
93 if (!fd)
94 exit();
95 int actual_fd = *fd;
96 if (actual_fd != open_act->fd) {
97 bool dup2_result = dup2(actual_fd, open_act->fd);
98 close(fd: actual_fd); // The old fd is not needed anymore.
99 if (!dup2_result)
100 exit();
101 }
102 break;
103 }
104 case BaseSpawnFileAction::CLOSE: {
105 auto *close_act = reinterpret_cast<SpawnFileCloseAction *>(act);
106 close(close_act->fd);
107 break;
108 }
109 case BaseSpawnFileAction::DUP2: {
110 auto *dup2_act = reinterpret_cast<SpawnFileDup2Action *>(act);
111 if (!dup2(dup2_act->fd, dup2_act->newfd))
112 exit();
113 break;
114 }
115 }
116 act = act->next;
117 }
118 }
119
120 if (LIBC_NAMESPACE::syscall_impl<long>(SYS_execve, ts: path, ts: argv, ts: envp) < 0)
121 exit();
122}
123
124} // anonymous namespace
125
126LLVM_LIBC_FUNCTION(int, posix_spawn,
127 (pid_t *__restrict pid, const char *__restrict path,
128 const posix_spawn_file_actions_t *file_actions,
129 const posix_spawnattr_t *__restrict attr,
130 char *const *__restrict argv,
131 char *const *__restrict envp)) {
132 pid_t cpid = fork();
133 if (cpid == 0)
134 child_process(path, file_actions, attr, argv, envp);
135 else if (cpid < 0)
136 return -cpid;
137
138 if (pid != nullptr)
139 *pid = cpid;
140
141 // TODO: Before returning, one should wait for the child_process to startup
142 // successfully. For now, we will just return. Future changes will add proper
143 // wait (using pipes for example).
144
145 return 0;
146}
147
148} // namespace LIBC_NAMESPACE
149

source code of libc/src/spawn/linux/posix_spawn.cpp