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