xref: /freebsd/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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