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