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