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
23namespace LIBC_NAMESPACE_DECL {
24
25namespace {
26
27pid_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
40cpp::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
55void 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
58bool 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.
71void 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
78void 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
128LLVM_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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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