blob: 8fdf97f2718191a673489c0d9fd63f51dc75093d [file] [log] [blame]
Zachary Turnerb2df30d2014-10-08 20:38:41 +00001//===-- PipePosix.cpp -------------------------------------------*- C++ -*-===//
Greg Clayton100eb932014-07-02 21:10:39 +00002//
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
Zachary Turnerb2df30d2014-10-08 20:38:41 +000010#include "lldb/Host/posix/PipePosix.h"
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000011#include "lldb/Host/FileSystem.h"
Oleksiy Vyalov4536c452015-02-05 16:29:12 +000012#include "lldb/Host/HostInfo.h"
13#include "llvm/ADT/SmallString.h"
14#include "llvm/Support/FileSystem.h"
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000015
Omair Javaid20405482015-08-09 19:04:41 +000016#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
17#define _GLIBCXX_USE_NANOSLEEP
18#endif
19
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000020#include <functional>
21#include <thread>
Greg Clayton100eb932014-07-02 21:10:39 +000022
Zachary Turner0b9d3ee2014-12-17 18:02:19 +000023#include <errno.h>
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +000024#include <fcntl.h>
Oleksiy Vyalov4536c452015-02-05 16:29:12 +000025#include <limits.h>
Zachary Turner0b9d3ee2014-12-17 18:02:19 +000026#include <unistd.h>
27#include <sys/types.h>
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000028#include <sys/stat.h>
Greg Clayton100eb932014-07-02 21:10:39 +000029
Zachary Turner0b9d3ee2014-12-17 18:02:19 +000030using namespace lldb;
Greg Clayton100eb932014-07-02 21:10:39 +000031using namespace lldb_private;
32
Zachary Turner0b9d3ee2014-12-17 18:02:19 +000033int PipePosix::kInvalidDescriptor = -1;
Greg Clayton100eb932014-07-02 21:10:39 +000034
35enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
36
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +000037// pipe2 is supported by Linux, FreeBSD v10 and higher.
38// TODO: Add more platforms that support pipe2.
Zachary Turner0b9d3ee2014-12-17 18:02:19 +000039#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10)
40#define PIPE2_SUPPORTED 1
41#else
42#define PIPE2_SUPPORTED 0
43#endif
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +000044
45namespace
46{
47
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000048constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
49
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +000050#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
51bool SetCloexecFlag(int fd)
52{
53 int flags = ::fcntl(fd, F_GETFD);
54 if (flags == -1)
55 return false;
56 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
57}
58#endif
59
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000060std::chrono::time_point<std::chrono::steady_clock>
61Now()
62{
63 return std::chrono::steady_clock::now();
64}
65
66Error
67SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout)
68{
69 Error error;
70 fd_set fds;
71 bool done = false;
72
73 using namespace std::chrono;
74
75 const auto finish_time = Now() + timeout;
76
77 while (!done)
78 {
79 struct timeval tv = {0, 0};
Pavel Labath7c52f852015-07-21 11:04:52 +000080 if (timeout != microseconds::zero())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000081 {
82 const auto remaining_dur = duration_cast<microseconds>(finish_time - Now());
83 if (remaining_dur.count() <= 0)
84 {
85 error.SetErrorString("timeout exceeded");
86 break;
87 }
88 const auto dur_secs = duration_cast<seconds>(remaining_dur);
89 const auto dur_usecs = remaining_dur % seconds(1);
90
91 tv.tv_sec = dur_secs.count();
92 tv.tv_usec = dur_usecs.count();
93 }
94 else
Pavel Labath7c52f852015-07-21 11:04:52 +000095 tv.tv_sec = 1;
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +000096
97 FD_ZERO(&fds);
98 FD_SET(handle, &fds);
99
100 const auto retval = ::select(handle + 1,
101 (is_read) ? &fds : nullptr,
102 (is_read) ? nullptr : &fds,
Pavel Labath7c52f852015-07-21 11:04:52 +0000103 nullptr, &tv);
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000104 if (retval == -1)
105 {
106 if (errno == EINTR)
107 continue;
108 error.SetErrorToErrno();
109 break;
110 }
111 if (retval == 0)
112 {
113 error.SetErrorString("timeout exceeded");
114 break;
115 }
116 if (!FD_ISSET(handle, &fds))
117 {
118 error.SetErrorString("invalid state");
119 break;
120 }
121
122 error = io_handler(done);
123 if (error.Fail())
124 {
125 if (error.GetError() == EINTR)
126 continue;
127 break;
128 }
129 }
130 return error;
131}
132
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000133}
134
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000135PipePosix::PipePosix()
Chaoren Linec534822015-05-01 16:49:23 +0000136 : m_fds{
137 PipePosix::kInvalidDescriptor,
138 PipePosix::kInvalidDescriptor
139 } {}
Chaoren Lina52f4842015-04-29 17:36:58 +0000140
141PipePosix::PipePosix(int read_fd, int write_fd)
142 : m_fds{read_fd, write_fd} {}
Greg Clayton100eb932014-07-02 21:10:39 +0000143
Chaoren Linec534822015-05-01 16:49:23 +0000144PipePosix::PipePosix(PipePosix &&pipe_posix)
145 : PipeBase{std::move(pipe_posix)},
146 m_fds{
147 pipe_posix.ReleaseReadFileDescriptor(),
148 pipe_posix.ReleaseWriteFileDescriptor()
149 } {}
150
151PipePosix &PipePosix::operator=(PipePosix &&pipe_posix)
152{
153 PipeBase::operator=(std::move(pipe_posix));
154 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
155 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
156 return *this;
157}
158
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000159PipePosix::~PipePosix()
Greg Clayton100eb932014-07-02 21:10:39 +0000160{
161 Close();
162}
163
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000164Error
165PipePosix::CreateNew(bool child_processes_inherit)
Greg Clayton100eb932014-07-02 21:10:39 +0000166{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000167 if (CanRead() || CanWrite())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000168 return Error(EINVAL, eErrorTypePOSIX);
Greg Clayton100eb932014-07-02 21:10:39 +0000169
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000170 Error error;
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000171#if PIPE2_SUPPORTED
172 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000173 return error;
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000174#else
175 if (::pipe(m_fds) == 0)
176 {
177#ifdef FD_CLOEXEC
178 if (!child_processes_inherit)
179 {
180 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1]))
181 {
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000182 error.SetErrorToErrno();
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000183 Close();
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000184 return error;
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000185 }
186 }
187#endif
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000188 return error;
Oleksiy Vyalovff9a0722014-11-21 16:18:57 +0000189 }
190#endif
Zachary Turnerb2df30d2014-10-08 20:38:41 +0000191
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000192 error.SetErrorToErrno();
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000193 m_fds[READ] = PipePosix::kInvalidDescriptor;
194 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000195 return error;
196}
197
198Error
199PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit)
200{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000201 if (CanRead() || CanWrite())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000202 return Error("Pipe is already opened");
203
204 Error error;
205 if (::mkfifo(name.data(), 0660) != 0)
206 error.SetErrorToErrno();
207
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000208 return error;
209}
210
211Error
Oleksiy Vyalov4536c452015-02-05 16:29:12 +0000212PipePosix::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name)
213{
214 llvm::SmallString<PATH_MAX> named_pipe_path;
215 llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str());
216 FileSpec tmpdir_file_spec;
217 tmpdir_file_spec.Clear();
218 if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
219 {
220 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
221 }
222 else
223 {
224 tmpdir_file_spec.AppendPathComponent("/tmp");
225 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str());
226 }
227
228 // It's possible that another process creates the target path after we've
229 // verified it's available but before we create it, in which case we
230 // should try again.
231 Error error;
232 do {
233 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), named_pipe_path);
234 error = CreateNew(named_pipe_path, child_process_inherit);
235 } while (error.GetError() == EEXIST);
236
237 if (error.Success())
238 name = named_pipe_path;
239 return error;
240}
241
242Error
Oleksiy Vyalov47718292015-01-14 01:31:27 +0000243PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit)
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000244{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000245 if (CanRead() || CanWrite())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000246 return Error("Pipe is already opened");
247
248 int flags = O_RDONLY | O_NONBLOCK;
249 if (!child_process_inherit)
250 flags |= O_CLOEXEC;
251
252 Error error;
253 int fd = ::open(name.data(), flags);
254 if (fd != -1)
255 m_fds[READ] = fd;
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000256 else
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000257 error.SetErrorToErrno();
258
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000259 return error;
260}
261
262Error
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000263PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout)
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000264{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000265 if (CanRead() || CanWrite())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000266 return Error("Pipe is already opened");
267
268 int flags = O_WRONLY | O_NONBLOCK;
269 if (!child_process_inherit)
270 flags |= O_CLOEXEC;
271
272 using namespace std::chrono;
273 const auto finish_time = Now() + timeout;
274
275 while (!CanWrite())
276 {
Pavel Labath7c52f852015-07-21 11:04:52 +0000277 if (timeout != microseconds::zero())
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000278 {
279 const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
280 if (dur <= 0)
281 return Error("timeout exceeded - reader hasn't opened so far");
282 }
283
284 errno = 0;
285 int fd = ::open(name.data(), flags);
286 if (fd == -1)
287 {
288 const auto errno_copy = errno;
289 // We may get ENXIO if a reader side of the pipe hasn't opened yet.
290 if (errno_copy != ENXIO)
291 return Error(errno_copy, eErrorTypePOSIX);
292
293 std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
294 }
295 else
296 {
297 m_fds[WRITE] = fd;
298 }
299 }
300
301 return Error();
Greg Clayton100eb932014-07-02 21:10:39 +0000302}
303
304int
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000305PipePosix::GetReadFileDescriptor() const
Greg Clayton100eb932014-07-02 21:10:39 +0000306{
307 return m_fds[READ];
308}
309
310int
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000311PipePosix::GetWriteFileDescriptor() const
Greg Clayton100eb932014-07-02 21:10:39 +0000312{
313 return m_fds[WRITE];
314}
315
316int
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000317PipePosix::ReleaseReadFileDescriptor()
Greg Clayton100eb932014-07-02 21:10:39 +0000318{
319 const int fd = m_fds[READ];
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000320 m_fds[READ] = PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000321 return fd;
322}
323
324int
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000325PipePosix::ReleaseWriteFileDescriptor()
Greg Clayton100eb932014-07-02 21:10:39 +0000326{
327 const int fd = m_fds[WRITE];
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000328 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000329 return fd;
330}
331
332void
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000333PipePosix::Close()
Greg Clayton100eb932014-07-02 21:10:39 +0000334{
335 CloseReadFileDescriptor();
336 CloseWriteFileDescriptor();
337}
338
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000339Error
340PipePosix::Delete(llvm::StringRef name)
341{
Chaoren Lind3173f32015-05-29 19:52:29 +0000342 return FileSystem::Unlink(FileSpec{name.data(), true});
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000343}
344
Greg Clayton100eb932014-07-02 21:10:39 +0000345bool
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000346PipePosix::CanRead() const
Greg Clayton100eb932014-07-02 21:10:39 +0000347{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000348 return m_fds[READ] != PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000349}
350
351bool
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000352PipePosix::CanWrite() const
Greg Clayton100eb932014-07-02 21:10:39 +0000353{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000354 return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000355}
356
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000357void
358PipePosix::CloseReadFileDescriptor()
Greg Clayton100eb932014-07-02 21:10:39 +0000359{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000360 if (CanRead())
Greg Clayton100eb932014-07-02 21:10:39 +0000361 {
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000362 close(m_fds[READ]);
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000363 m_fds[READ] = PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000364 }
Greg Clayton100eb932014-07-02 21:10:39 +0000365}
366
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000367void
368PipePosix::CloseWriteFileDescriptor()
Greg Clayton100eb932014-07-02 21:10:39 +0000369{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000370 if (CanWrite())
Greg Clayton100eb932014-07-02 21:10:39 +0000371 {
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000372 close(m_fds[WRITE]);
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000373 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
Greg Clayton100eb932014-07-02 21:10:39 +0000374 }
Greg Clayton100eb932014-07-02 21:10:39 +0000375}
376
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000377Error
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000378PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read)
Greg Clayton100eb932014-07-02 21:10:39 +0000379{
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000380 bytes_read = 0;
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000381 if (!CanRead())
382 return Error(EINVAL, eErrorTypePOSIX);
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000383
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000384 auto handle = GetReadFileDescriptor();
385 return SelectIO(handle,
386 true,
387 [=, &bytes_read](bool &done)
388 {
389 Error error;
390 auto result = ::read(handle,
391 reinterpret_cast<char*>(buf) + bytes_read,
392 size - bytes_read);
393 if (result != -1)
394 {
395 bytes_read += result;
396 if (bytes_read == size || result == 0)
397 done = true;
398 }
399 else
400 error.SetErrorToErrno();
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000401
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000402 return error;
403 },
404 timeout);
Greg Clayton100eb932014-07-02 21:10:39 +0000405}
406
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000407Error
Pavel Labath7c52f852015-07-21 11:04:52 +0000408PipePosix::Write(const void *buf, size_t size, size_t &bytes_written)
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000409{
410 bytes_written = 0;
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000411 if (!CanWrite())
412 return Error(EINVAL, eErrorTypePOSIX);
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000413
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000414 auto handle = GetWriteFileDescriptor();
415 return SelectIO(handle,
416 false,
417 [=, &bytes_written](bool &done)
418 {
419 Error error;
420 auto result = ::write(handle,
421 reinterpret_cast<const char*>(buf) + bytes_written,
422 size - bytes_written);
423 if (result != -1)
424 {
425 bytes_written += result;
426 if (bytes_written == size)
427 done = true;
428 }
429 else
430 error.SetErrorToErrno();
Zachary Turner0b9d3ee2014-12-17 18:02:19 +0000431
Oleksiy Vyalovd5f8b6a2015-01-13 23:19:40 +0000432 return error;
433 },
Pavel Labath7c52f852015-07-21 11:04:52 +0000434 std::chrono::microseconds::zero());
Greg Clayton100eb932014-07-02 21:10:39 +0000435}