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
25using namespace lldb;
26using namespace lldb_private;
27
28int PipePosix::kInvalidDescriptor = -1;
29
30enum 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
41static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
42
43#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44static 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
52static std::chrono::time_point<std::chrono::steady_clock> Now() {
53 return std::chrono::steady_clock::now();
54}
55
56PipePosix::PipePosix()
57 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
58
59PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
60 : m_fds{read, write} {}
61
62PipePosix::PipePosix(PipePosix &&pipe_posix)
63 : PipeBase{std::move(pipe_posix)},
64 m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65 pipe_posix.ReleaseWriteFileDescriptor()} {}
66
67PipePosix &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
74PipePosix::~PipePosix() { Close(); }
75
76Status PipePosix::CreateNew(bool child_processes_inherit) {
77 if (CanRead() || CanWrite())
78 return Status(EINVAL, eErrorTypePOSIX);
79
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
105Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
106 if (CanRead() || CanWrite())
107 return Status("Pipe is already opened");
108
110 if (::mkfifo(name.str().c_str(), 0660) != 0)
111 error.SetErrorToErrno();
112
113 return error;
114}
115
116Status 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.
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
141Status 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
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
160Status
161PipePosix::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
199int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
200
201int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
202
203int PipePosix::ReleaseReadFileDescriptor() {
204 const int fd = m_fds[READ];
205 m_fds[READ] = PipePosix::kInvalidDescriptor;
206 return fd;
207}
208
209int PipePosix::ReleaseWriteFileDescriptor() {
210 const int fd = m_fds[WRITE];
211 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
212 return fd;
213}
214
215void PipePosix::Close() {
216 CloseReadFileDescriptor();
217 CloseWriteFileDescriptor();
218}
219
220Status PipePosix::Delete(llvm::StringRef name) {
221 return llvm::sys::fs::remove(name);
222}
223
224bool PipePosix::CanRead() const {
225 return m_fds[READ] != PipePosix::kInvalidDescriptor;
226}
227
228bool PipePosix::CanWrite() const {
229 return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
230}
231
232void PipePosix::CloseReadFileDescriptor() {
233 if (CanRead()) {
234 close(m_fds[READ]);
235 m_fds[READ] = PipePosix::kInvalidDescriptor;
236 }
237}
238
239void PipePosix::CloseWriteFileDescriptor() {
240 if (CanWrite()) {
241 close(m_fds[WRITE]);
242 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
243 }
244}
245
246Status 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
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
280Status 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
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}
static llvm::raw_ostream & error(Stream &strm)
static std::chrono::time_point< std::chrono::steady_clock > Now()
Definition: PipePosix.cpp:52
static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS
Definition: PipePosix.cpp:41
PIPES
Definition: PipePosix.cpp:30
@ WRITE
Definition: PipePosix.cpp:30
@ READ
Definition: PipePosix.cpp:30
static bool SetCloexecFlag(int fd)
lldb_private::Status Select()
void FDSetRead(lldb::socket_t fd)
void SetTimeout(const std::chrono::microseconds &timeout)
void FDSetWrite(lldb::socket_t fd)
A file utility class.
Definition: FileSpec.h:56
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:453
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:366
An error handling class.
Definition: Status.h:44
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
int pipe_t
Definition: lldb-types.h:64
@ eErrorTypePOSIX
POSIX error codes.
#define O_NONBLOCK