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