1 //===-- PipePosix.cpp -------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
16 #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
17 #ifndef _GLIBCXX_USE_NANOSLEEP
18 #define _GLIBCXX_USE_NANOSLEEP
29 #include <sys/types.h>
33 using namespace lldb_private;
35 int PipePosix::kInvalidDescriptor = -1;
37 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
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) || \
43 #define PIPE2_SUPPORTED 1
45 #define PIPE2_SUPPORTED 0
50 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
52 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
53 bool SetCloexecFlag(int fd) {
54 int flags = ::fcntl(fd, F_GETFD);
57 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
61 std::chrono::time_point<std::chrono::steady_clock> Now() {
62 return std::chrono::steady_clock::now();
66 PipePosix::PipePosix()
67 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
69 PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
70 : m_fds{read, write} {}
72 PipePosix::PipePosix(PipePosix &&pipe_posix)
73 : PipeBase{std::move(pipe_posix)},
74 m_fds{pipe_posix.ReleaseReadFileDescriptor(),
75 pipe_posix.ReleaseWriteFileDescriptor()} {}
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();
84 PipePosix::~PipePosix() { Close(); }
86 Status PipePosix::CreateNew(bool child_processes_inherit) {
87 if (CanRead() || CanWrite())
88 return Status(EINVAL, eErrorTypePOSIX);
92 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
95 if (::pipe(m_fds) == 0) {
97 if (!child_processes_inherit) {
98 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
99 error.SetErrorToErrno();
109 error.SetErrorToErrno();
110 m_fds[READ] = PipePosix::kInvalidDescriptor;
111 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
115 Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
116 if (CanRead() || CanWrite())
117 return Status("Pipe is already opened");
120 if (::mkfifo(name.data(), 0660) != 0)
121 error.SetErrorToErrno();
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);
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
141 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
143 error = CreateNew(named_pipe_path, child_process_inherit);
144 } while (error.GetError() == EEXIST);
147 name = named_pipe_path;
151 Status PipePosix::OpenAsReader(llvm::StringRef name,
152 bool child_process_inherit) {
153 if (CanRead() || CanWrite())
154 return Status("Pipe is already opened");
156 int flags = O_RDONLY | O_NONBLOCK;
157 if (!child_process_inherit)
161 int fd = llvm::sys::RetryAfterSignal(-1, ::open, name.data(), flags);
165 error.SetErrorToErrno();
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");
177 int flags = O_WRONLY | O_NONBLOCK;
178 if (!child_process_inherit)
181 using namespace std::chrono;
182 const auto finish_time = Now() + timeout;
184 while (!CanWrite()) {
185 if (timeout != microseconds::zero()) {
186 const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
188 return Status("timeout exceeded - reader hasn't opened so far");
192 int fd = ::open(name.data(), flags);
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);
199 std::this_thread::sleep_for(
200 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
209 int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
211 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
213 int PipePosix::ReleaseReadFileDescriptor() {
214 const int fd = m_fds[READ];
215 m_fds[READ] = PipePosix::kInvalidDescriptor;
219 int PipePosix::ReleaseWriteFileDescriptor() {
220 const int fd = m_fds[WRITE];
221 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
225 void PipePosix::Close() {
226 CloseReadFileDescriptor();
227 CloseWriteFileDescriptor();
230 Status PipePosix::Delete(llvm::StringRef name) {
231 return llvm::sys::fs::remove(name);
234 bool PipePosix::CanRead() const {
235 return m_fds[READ] != PipePosix::kInvalidDescriptor;
238 bool PipePosix::CanWrite() const {
239 return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
242 void PipePosix::CloseReadFileDescriptor() {
245 m_fds[READ] = PipePosix::kInvalidDescriptor;
249 void PipePosix::CloseWriteFileDescriptor() {
252 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
256 Status PipePosix::ReadWithTimeout(void *buf, size_t size,
257 const std::chrono::microseconds &timeout,
258 size_t &bytes_read) {
261 return Status(EINVAL, eErrorTypePOSIX);
263 const int fd = GetReadFileDescriptor();
265 SelectHelper select_helper;
266 select_helper.SetTimeout(timeout);
267 select_helper.FDSetRead(fd);
270 while (error.Success()) {
271 error = select_helper.Select();
272 if (error.Success()) {
273 auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read,
276 bytes_read += result;
277 if (bytes_read == size || result == 0)
279 } else if (errno == EINTR) {
282 error.SetErrorToErrno();
290 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
293 return Status(EINVAL, eErrorTypePOSIX);
295 const int fd = GetWriteFileDescriptor();
296 SelectHelper select_helper;
297 select_helper.SetTimeout(std::chrono::seconds(0));
298 select_helper.FDSetWrite(fd);
301 while (error.Success()) {
302 error = select_helper.Select();
303 if (error.Success()) {
305 ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written,
306 size - bytes_written);
308 bytes_written += result;
309 if (bytes_written == size)
311 } else if (errno == EINTR) {
314 error.SetErrorToErrno();