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