xref: /NextBSD/contrib/llvm/tools/lldb/source/Host/posix/PipePosix.cpp (revision 84d351007654069f9643c8e4b4802a7f5f08ee42)
1 //===-- PipePosix.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Host/posix/PipePosix.h"
11 #include "lldb/Host/FileSystem.h"
12 #include "lldb/Host/HostInfo.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/FileSystem.h"
15 
16 #include <functional>
17 #include <thread>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 int PipePosix::kInvalidDescriptor = -1;
30 
31 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
32 
33 // pipe2 is supported by Linux, FreeBSD v10 and higher.
34 // TODO: Add more platforms that support pipe2.
35 #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10)
36 #define PIPE2_SUPPORTED 1
37 #else
38 #define PIPE2_SUPPORTED 0
39 #endif
40 
41 namespace
42 {
43 
44 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
45 
46 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
SetCloexecFlag(int fd)47 bool SetCloexecFlag(int fd)
48 {
49     int flags = ::fcntl(fd, F_GETFD);
50     if (flags == -1)
51         return false;
52     return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
53 }
54 #endif
55 
56 std::chrono::time_point<std::chrono::steady_clock>
Now()57 Now()
58 {
59     return std::chrono::steady_clock::now();
60 }
61 
62 Error
SelectIO(int handle,bool is_read,const std::function<Error (bool &)> & io_handler,const std::chrono::microseconds & timeout)63 SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout)
64 {
65     Error error;
66     fd_set fds;
67     bool done = false;
68 
69     using namespace std::chrono;
70 
71     const auto finish_time = Now() + timeout;
72 
73     while (!done)
74     {
75         struct timeval tv = {0, 0};
76         if (timeout != microseconds::zero())
77         {
78             const auto remaining_dur = duration_cast<microseconds>(finish_time - Now());
79             if (remaining_dur.count() <= 0)
80             {
81                 error.SetErrorString("timeout exceeded");
82                 break;
83             }
84             const auto dur_secs = duration_cast<seconds>(remaining_dur);
85             const auto dur_usecs = remaining_dur % seconds(1);
86 
87             tv.tv_sec = dur_secs.count();
88             tv.tv_usec = dur_usecs.count();
89         }
90         else
91             tv.tv_sec = 1;
92 
93         FD_ZERO(&fds);
94         FD_SET(handle, &fds);
95 
96         const auto retval = ::select(handle + 1,
97                                      (is_read) ? &fds : nullptr,
98                                      (is_read) ? nullptr : &fds,
99                                      nullptr, &tv);
100         if (retval == -1)
101         {
102             if (errno == EINTR)
103                 continue;
104             error.SetErrorToErrno();
105             break;
106         }
107         if (retval == 0)
108         {
109             error.SetErrorString("timeout exceeded");
110             break;
111         }
112         if (!FD_ISSET(handle, &fds))
113         {
114             error.SetErrorString("invalid state");
115             break;
116         }
117 
118         error = io_handler(done);
119         if (error.Fail())
120         {
121           if (error.GetError() == EINTR)
122               continue;
123             break;
124         }
125     }
126     return error;
127 }
128 
129 }
130 
PipePosix()131 PipePosix::PipePosix()
132     : m_fds{
133         PipePosix::kInvalidDescriptor,
134         PipePosix::kInvalidDescriptor
135     } {}
136 
PipePosix(int read_fd,int write_fd)137 PipePosix::PipePosix(int read_fd, int write_fd)
138     : m_fds{read_fd, write_fd} {}
139 
PipePosix(PipePosix && pipe_posix)140 PipePosix::PipePosix(PipePosix &&pipe_posix)
141     : PipeBase{std::move(pipe_posix)},
142       m_fds{
143         pipe_posix.ReleaseReadFileDescriptor(),
144         pipe_posix.ReleaseWriteFileDescriptor()
145       } {}
146 
operator =(PipePosix && pipe_posix)147 PipePosix &PipePosix::operator=(PipePosix &&pipe_posix)
148 {
149     PipeBase::operator=(std::move(pipe_posix));
150     m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
151     m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
152     return *this;
153 }
154 
~PipePosix()155 PipePosix::~PipePosix()
156 {
157     Close();
158 }
159 
160 Error
CreateNew(bool child_processes_inherit)161 PipePosix::CreateNew(bool child_processes_inherit)
162 {
163     if (CanRead() || CanWrite())
164         return Error(EINVAL, eErrorTypePOSIX);
165 
166     Error error;
167 #if PIPE2_SUPPORTED
168     if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
169         return error;
170 #else
171     if (::pipe(m_fds) == 0)
172     {
173 #ifdef FD_CLOEXEC
174         if (!child_processes_inherit)
175         {
176             if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1]))
177             {
178                 error.SetErrorToErrno();
179                 Close();
180                 return error;
181             }
182         }
183 #endif
184         return error;
185     }
186 #endif
187 
188     error.SetErrorToErrno();
189     m_fds[READ] = PipePosix::kInvalidDescriptor;
190     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
191     return error;
192 }
193 
194 Error
CreateNew(llvm::StringRef name,bool child_process_inherit)195 PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit)
196 {
197     if (CanRead() || CanWrite())
198         return Error("Pipe is already opened");
199 
200     Error error;
201     if (::mkfifo(name.data(), 0660) != 0)
202         error.SetErrorToErrno();
203 
204     return error;
205 }
206 
207 Error
CreateWithUniqueName(llvm::StringRef prefix,bool child_process_inherit,llvm::SmallVectorImpl<char> & name)208 PipePosix::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name)
209 {
210     llvm::SmallString<PATH_MAX> named_pipe_path;
211     llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str());
212     FileSpec tmpdir_file_spec;
213     tmpdir_file_spec.Clear();
214     if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
215     {
216         tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
217     }
218     else
219     {
220         tmpdir_file_spec.AppendPathComponent("/tmp");
221         tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
222     }
223 
224     // It's possible that another process creates the target path after we've
225     // verified it's available but before we create it, in which case we
226     // should try again.
227     Error error;
228     do {
229         llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), named_pipe_path);
230         error = CreateNew(named_pipe_path, child_process_inherit);
231     } while (error.GetError() == EEXIST);
232 
233     if (error.Success())
234         name = named_pipe_path;
235     return error;
236 }
237 
238 Error
OpenAsReader(llvm::StringRef name,bool child_process_inherit)239 PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit)
240 {
241     if (CanRead() || CanWrite())
242         return Error("Pipe is already opened");
243 
244     int flags = O_RDONLY | O_NONBLOCK;
245     if (!child_process_inherit)
246         flags |= O_CLOEXEC;
247 
248     Error error;
249     int fd = ::open(name.data(), flags);
250     if (fd != -1)
251         m_fds[READ] = fd;
252     else
253         error.SetErrorToErrno();
254 
255     return error;
256 }
257 
258 Error
OpenAsWriterWithTimeout(llvm::StringRef name,bool child_process_inherit,const std::chrono::microseconds & timeout)259 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout)
260 {
261     if (CanRead() || CanWrite())
262         return Error("Pipe is already opened");
263 
264     int flags = O_WRONLY | O_NONBLOCK;
265     if (!child_process_inherit)
266         flags |= O_CLOEXEC;
267 
268     using namespace std::chrono;
269     const auto finish_time = Now() + timeout;
270 
271     while (!CanWrite())
272     {
273         if (timeout != microseconds::zero())
274         {
275             const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
276             if (dur <= 0)
277                 return Error("timeout exceeded - reader hasn't opened so far");
278         }
279 
280         errno = 0;
281         int fd = ::open(name.data(), flags);
282         if (fd == -1)
283         {
284             const auto errno_copy = errno;
285             // We may get ENXIO if a reader side of the pipe hasn't opened yet.
286             if (errno_copy != ENXIO)
287                 return Error(errno_copy, eErrorTypePOSIX);
288 
289             std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
290         }
291         else
292         {
293             m_fds[WRITE] = fd;
294         }
295     }
296 
297     return Error();
298 }
299 
300 int
GetReadFileDescriptor() const301 PipePosix::GetReadFileDescriptor() const
302 {
303     return m_fds[READ];
304 }
305 
306 int
GetWriteFileDescriptor() const307 PipePosix::GetWriteFileDescriptor() const
308 {
309     return m_fds[WRITE];
310 }
311 
312 int
ReleaseReadFileDescriptor()313 PipePosix::ReleaseReadFileDescriptor()
314 {
315     const int fd = m_fds[READ];
316     m_fds[READ] = PipePosix::kInvalidDescriptor;
317     return fd;
318 }
319 
320 int
ReleaseWriteFileDescriptor()321 PipePosix::ReleaseWriteFileDescriptor()
322 {
323     const int fd = m_fds[WRITE];
324     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
325     return fd;
326 }
327 
328 void
Close()329 PipePosix::Close()
330 {
331     CloseReadFileDescriptor();
332     CloseWriteFileDescriptor();
333 }
334 
335 Error
Delete(llvm::StringRef name)336 PipePosix::Delete(llvm::StringRef name)
337 {
338     return FileSystem::Unlink(FileSpec{name.data(), true});
339 }
340 
341 bool
CanRead() const342 PipePosix::CanRead() const
343 {
344     return m_fds[READ] != PipePosix::kInvalidDescriptor;
345 }
346 
347 bool
CanWrite() const348 PipePosix::CanWrite() const
349 {
350     return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
351 }
352 
353 void
CloseReadFileDescriptor()354 PipePosix::CloseReadFileDescriptor()
355 {
356     if (CanRead())
357     {
358         close(m_fds[READ]);
359         m_fds[READ] = PipePosix::kInvalidDescriptor;
360     }
361 }
362 
363 void
CloseWriteFileDescriptor()364 PipePosix::CloseWriteFileDescriptor()
365 {
366     if (CanWrite())
367     {
368         close(m_fds[WRITE]);
369         m_fds[WRITE] = PipePosix::kInvalidDescriptor;
370     }
371 }
372 
373 Error
ReadWithTimeout(void * buf,size_t size,const std::chrono::microseconds & timeout,size_t & bytes_read)374 PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read)
375 {
376     bytes_read = 0;
377     if (!CanRead())
378         return Error(EINVAL, eErrorTypePOSIX);
379 
380     auto handle = GetReadFileDescriptor();
381     return SelectIO(handle,
382                     true,
383                     [=, &bytes_read](bool &done)
384                     {
385                       Error error;
386                       auto result = ::read(handle,
387                                            reinterpret_cast<char*>(buf) + bytes_read,
388                                            size - bytes_read);
389                       if (result != -1)
390                       {
391                           bytes_read += result;
392                           if (bytes_read == size || result == 0)
393                               done = true;
394                       }
395                       else
396                           error.SetErrorToErrno();
397 
398                       return error;
399                   },
400                   timeout);
401 }
402 
403 Error
Write(const void * buf,size_t size,size_t & bytes_written)404 PipePosix::Write(const void *buf, size_t size, size_t &bytes_written)
405 {
406     bytes_written = 0;
407     if (!CanWrite())
408         return Error(EINVAL, eErrorTypePOSIX);
409 
410     auto handle = GetWriteFileDescriptor();
411     return SelectIO(handle,
412                     false,
413                     [=, &bytes_written](bool &done)
414                     {
415                         Error error;
416                         auto result = ::write(handle,
417                                               reinterpret_cast<const char*>(buf) + bytes_written,
418                                               size - bytes_written);
419                         if (result != -1)
420                         {
421                             bytes_written += result;
422                             if (bytes_written == size)
423                                 done = true;
424                         }
425                         else
426                             error.SetErrorToErrno();
427 
428                         return error;
429                     },
430                     std::chrono::microseconds::zero());
431 }
432