1 //===-- PipePosix.cpp -----------------------------------------------------===//
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 "lldb/Host/posix/PipePosix.h"
10 #include "lldb/Host/FileSystem.h"
11 #include "lldb/Host/HostInfo.h"
12 #include "lldb/Utility/SelectHelper.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/Errno.h"
15 #include "llvm/Support/Error.h"
16 #include <functional>
17 #include <system_error>
18 #include <thread>
19
20 #include <cerrno>
21 #include <climits>
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 using namespace lldb;
28 using namespace lldb_private;
29
30 int PipePosix::kInvalidDescriptor = -1;
31
32 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
33
34 // pipe2 is supported by a limited set of platforms
35 // TODO: Add more platforms that support pipe2.
36 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
37 defined(__OpenBSD__)
38 #define PIPE2_SUPPORTED 1
39 #else
40 #define PIPE2_SUPPORTED 0
41 #endif
42
43 static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
44
45 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
SetCloexecFlag(int fd)46 static bool SetCloexecFlag(int fd) {
47 int flags = ::fcntl(fd, F_GETFD);
48 if (flags == -1)
49 return false;
50 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
51 }
52 #endif
53
Now()54 static std::chrono::time_point<std::chrono::steady_clock> Now() {
55 return std::chrono::steady_clock::now();
56 }
57
PipePosix()58 PipePosix::PipePosix()
59 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
60
PipePosix(lldb::pipe_t read,lldb::pipe_t write)61 PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
62 : m_fds{read, write} {}
63
PipePosix(PipePosix && pipe_posix)64 PipePosix::PipePosix(PipePosix &&pipe_posix)
65 : PipeBase{std::move(pipe_posix)},
66 m_fds{pipe_posix.ReleaseReadFileDescriptor(),
67 pipe_posix.ReleaseWriteFileDescriptor()} {}
68
operator =(PipePosix && pipe_posix)69 PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
70 std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(
71 m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,
72 pipe_posix.m_write_mutex);
73
74 PipeBase::operator=(std::move(pipe_posix));
75 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();
76 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();
77 return *this;
78 }
79
~PipePosix()80 PipePosix::~PipePosix() { Close(); }
81
CreateNew()82 Status PipePosix::CreateNew() {
83 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
84 if (CanReadUnlocked() || CanWriteUnlocked())
85 return Status(EINVAL, eErrorTypePOSIX);
86
87 Status error;
88 #if PIPE2_SUPPORTED
89 if (::pipe2(m_fds, O_CLOEXEC) == 0)
90 return error;
91 #else
92 if (::pipe(m_fds) == 0) {
93 #ifdef FD_CLOEXEC
94 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
95 error = Status::FromErrno();
96 CloseUnlocked();
97 return error;
98 }
99 #endif
100 return error;
101 }
102 #endif
103
104 error = Status::FromErrno();
105 m_fds[READ] = PipePosix::kInvalidDescriptor;
106 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
107 return error;
108 }
109
CreateNew(llvm::StringRef name)110 Status PipePosix::CreateNew(llvm::StringRef name) {
111 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
112 if (CanReadUnlocked() || CanWriteUnlocked())
113 return Status::FromErrorString("Pipe is already opened");
114
115 Status error;
116 if (::mkfifo(name.str().c_str(), 0660) != 0)
117 error = Status::FromErrno();
118 return error;
119 }
120
CreateWithUniqueName(llvm::StringRef prefix,llvm::SmallVectorImpl<char> & name)121 Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
122 llvm::SmallVectorImpl<char> &name) {
123 llvm::SmallString<128> named_pipe_path;
124 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
125 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
126 if (!tmpdir_file_spec)
127 tmpdir_file_spec.AppendPathComponent("/tmp");
128 tmpdir_file_spec.AppendPathComponent(pipe_spec);
129
130 // It's possible that another process creates the target path after we've
131 // verified it's available but before we create it, in which case we should
132 // try again.
133 Status error;
134 do {
135 llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
136 /*MakeAbsolute=*/false);
137 error = CreateNew(named_pipe_path);
138 } while (error.GetError() == EEXIST);
139
140 if (error.Success())
141 name = named_pipe_path;
142 return error;
143 }
144
OpenAsReader(llvm::StringRef name)145 Status PipePosix::OpenAsReader(llvm::StringRef name) {
146 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
147
148 if (CanReadUnlocked() || CanWriteUnlocked())
149 return Status::FromErrorString("Pipe is already opened");
150
151 int flags = O_RDONLY | O_NONBLOCK | O_CLOEXEC;
152
153 Status error;
154 int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
155 if (fd != -1)
156 m_fds[READ] = fd;
157 else
158 error = Status::FromErrno();
159
160 return error;
161 }
162
OpenAsWriter(llvm::StringRef name,const Timeout<std::micro> & timeout)163 llvm::Error PipePosix::OpenAsWriter(llvm::StringRef name,
164 const Timeout<std::micro> &timeout) {
165 std::lock_guard<std::mutex> guard(m_write_mutex);
166 if (CanReadUnlocked() || CanWriteUnlocked())
167 return llvm::createStringError("Pipe is already opened");
168
169 int flags = O_WRONLY | O_NONBLOCK | O_CLOEXEC;
170
171 using namespace std::chrono;
172 std::optional<time_point<steady_clock>> finish_time;
173 if (timeout)
174 finish_time = Now() + *timeout;
175
176 while (!CanWriteUnlocked()) {
177 if (timeout) {
178 if (Now() > finish_time)
179 return llvm::createStringError(
180 std::make_error_code(std::errc::timed_out),
181 "timeout exceeded - reader hasn't opened so far");
182 }
183
184 errno = 0;
185 int fd = ::open(name.str().c_str(), flags);
186 if (fd == -1) {
187 const auto errno_copy = errno;
188 // We may get ENXIO if a reader side of the pipe hasn't opened yet.
189 if (errno_copy != ENXIO && errno_copy != EINTR)
190 return llvm::errorCodeToError(
191 std::error_code(errno_copy, std::generic_category()));
192
193 std::this_thread::sleep_for(
194 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
195 } else {
196 m_fds[WRITE] = fd;
197 }
198 }
199
200 return llvm::Error::success();
201 }
202
GetReadFileDescriptor() const203 int PipePosix::GetReadFileDescriptor() const {
204 std::lock_guard<std::mutex> guard(m_read_mutex);
205 return GetReadFileDescriptorUnlocked();
206 }
207
GetReadFileDescriptorUnlocked() const208 int PipePosix::GetReadFileDescriptorUnlocked() const {
209 return m_fds[READ];
210 }
211
GetWriteFileDescriptor() const212 int PipePosix::GetWriteFileDescriptor() const {
213 std::lock_guard<std::mutex> guard(m_write_mutex);
214 return GetWriteFileDescriptorUnlocked();
215 }
216
GetWriteFileDescriptorUnlocked() const217 int PipePosix::GetWriteFileDescriptorUnlocked() const {
218 return m_fds[WRITE];
219 }
220
ReleaseReadFileDescriptor()221 int PipePosix::ReleaseReadFileDescriptor() {
222 std::lock_guard<std::mutex> guard(m_read_mutex);
223 return ReleaseReadFileDescriptorUnlocked();
224 }
225
ReleaseReadFileDescriptorUnlocked()226 int PipePosix::ReleaseReadFileDescriptorUnlocked() {
227 const int fd = m_fds[READ];
228 m_fds[READ] = PipePosix::kInvalidDescriptor;
229 return fd;
230 }
231
ReleaseWriteFileDescriptor()232 int PipePosix::ReleaseWriteFileDescriptor() {
233 std::lock_guard<std::mutex> guard(m_write_mutex);
234 return ReleaseWriteFileDescriptorUnlocked();
235 }
236
ReleaseWriteFileDescriptorUnlocked()237 int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
238 const int fd = m_fds[WRITE];
239 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
240 return fd;
241 }
242
Close()243 void PipePosix::Close() {
244 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
245 CloseUnlocked();
246 }
247
CloseUnlocked()248 void PipePosix::CloseUnlocked() {
249 CloseReadFileDescriptorUnlocked();
250 CloseWriteFileDescriptorUnlocked();
251 }
252
Delete(llvm::StringRef name)253 Status PipePosix::Delete(llvm::StringRef name) {
254 return llvm::sys::fs::remove(name);
255 }
256
CanRead() const257 bool PipePosix::CanRead() const {
258 std::lock_guard<std::mutex> guard(m_read_mutex);
259 return CanReadUnlocked();
260 }
261
CanReadUnlocked() const262 bool PipePosix::CanReadUnlocked() const {
263 return m_fds[READ] != PipePosix::kInvalidDescriptor;
264 }
265
CanWrite() const266 bool PipePosix::CanWrite() const {
267 std::lock_guard<std::mutex> guard(m_write_mutex);
268 return CanWriteUnlocked();
269 }
270
CanWriteUnlocked() const271 bool PipePosix::CanWriteUnlocked() const {
272 return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
273 }
274
CloseReadFileDescriptor()275 void PipePosix::CloseReadFileDescriptor() {
276 std::lock_guard<std::mutex> guard(m_read_mutex);
277 CloseReadFileDescriptorUnlocked();
278 }
CloseReadFileDescriptorUnlocked()279 void PipePosix::CloseReadFileDescriptorUnlocked() {
280 if (CanReadUnlocked()) {
281 close(m_fds[READ]);
282 m_fds[READ] = PipePosix::kInvalidDescriptor;
283 }
284 }
285
CloseWriteFileDescriptor()286 void PipePosix::CloseWriteFileDescriptor() {
287 std::lock_guard<std::mutex> guard(m_write_mutex);
288 CloseWriteFileDescriptorUnlocked();
289 }
290
CloseWriteFileDescriptorUnlocked()291 void PipePosix::CloseWriteFileDescriptorUnlocked() {
292 if (CanWriteUnlocked()) {
293 close(m_fds[WRITE]);
294 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
295 }
296 }
297
Read(void * buf,size_t size,const Timeout<std::micro> & timeout)298 llvm::Expected<size_t> PipePosix::Read(void *buf, size_t size,
299 const Timeout<std::micro> &timeout) {
300 std::lock_guard<std::mutex> guard(m_read_mutex);
301 if (!CanReadUnlocked())
302 return llvm::errorCodeToError(
303 std::make_error_code(std::errc::invalid_argument));
304
305 const int fd = GetReadFileDescriptorUnlocked();
306
307 SelectHelper select_helper;
308 if (timeout)
309 select_helper.SetTimeout(*timeout);
310 select_helper.FDSetRead(fd);
311
312 if (llvm::Error error = select_helper.Select().takeError())
313 return error;
314
315 ssize_t result = ::read(fd, buf, size);
316 if (result == -1)
317 return llvm::errorCodeToError(
318 std::error_code(errno, std::generic_category()));
319
320 return result;
321 }
322
Write(const void * buf,size_t size,const Timeout<std::micro> & timeout)323 llvm::Expected<size_t> PipePosix::Write(const void *buf, size_t size,
324 const Timeout<std::micro> &timeout) {
325 std::lock_guard<std::mutex> guard(m_write_mutex);
326 if (!CanWriteUnlocked())
327 return llvm::errorCodeToError(
328 std::make_error_code(std::errc::invalid_argument));
329
330 const int fd = GetWriteFileDescriptorUnlocked();
331 SelectHelper select_helper;
332 if (timeout)
333 select_helper.SetTimeout(*timeout);
334 select_helper.FDSetWrite(fd);
335
336 if (llvm::Error error = select_helper.Select().takeError())
337 return error;
338
339 ssize_t result = ::write(fd, buf, size);
340 if (result == -1)
341 return llvm::errorCodeToError(
342 std::error_code(errno, std::generic_category()));
343
344 return result;
345 }
346