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/HostInfo.h"
12 #include "lldb/Utility/SelectHelper.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/FileSystem.h"
15
16 #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
17 #ifndef _GLIBCXX_USE_NANOSLEEP
18 #define _GLIBCXX_USE_NANOSLEEP
19 #endif
20 #endif
21
22 #include <functional>
23 #include <thread>
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 using namespace lldb;
33 using namespace lldb_private;
34
35 int PipePosix::kInvalidDescriptor = -1;
36
37 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
38
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) || \
42 defined(__NetBSD__)
43 #define PIPE2_SUPPORTED 1
44 #else
45 #define PIPE2_SUPPORTED 0
46 #endif
47
48 namespace {
49
50 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
51
52 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
SetCloexecFlag(int fd)53 bool SetCloexecFlag(int fd) {
54 int flags = ::fcntl(fd, F_GETFD);
55 if (flags == -1)
56 return false;
57 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
58 }
59 #endif
60
Now()61 std::chrono::time_point<std::chrono::steady_clock> Now() {
62 return std::chrono::steady_clock::now();
63 }
64 } // namespace
65
PipePosix()66 PipePosix::PipePosix()
67 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
68
PipePosix(lldb::pipe_t read,lldb::pipe_t write)69 PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
70 : m_fds{read, write} {}
71
PipePosix(PipePosix && pipe_posix)72 PipePosix::PipePosix(PipePosix &&pipe_posix)
73 : PipeBase{std::move(pipe_posix)},
74 m_fds{pipe_posix.ReleaseReadFileDescriptor(),
75 pipe_posix.ReleaseWriteFileDescriptor()} {}
76
operator =(PipePosix && pipe_posix)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();
81 return *this;
82 }
83
~PipePosix()84 PipePosix::~PipePosix() { Close(); }
85
CreateNew(bool child_processes_inherit)86 Status PipePosix::CreateNew(bool child_processes_inherit) {
87 if (CanRead() || CanWrite())
88 return Status(EINVAL, eErrorTypePOSIX);
89
90 Status error;
91 #if PIPE2_SUPPORTED
92 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
93 return error;
94 #else
95 if (::pipe(m_fds) == 0) {
96 #ifdef FD_CLOEXEC
97 if (!child_processes_inherit) {
98 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
99 error.SetErrorToErrno();
100 Close();
101 return error;
102 }
103 }
104 #endif
105 return error;
106 }
107 #endif
108
109 error.SetErrorToErrno();
110 m_fds[READ] = PipePosix::kInvalidDescriptor;
111 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
112 return error;
113 }
114
CreateNew(llvm::StringRef name,bool child_process_inherit)115 Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
116 if (CanRead() || CanWrite())
117 return Status("Pipe is already opened");
118
119 Status error;
120 if (::mkfifo(name.data(), 0660) != 0)
121 error.SetErrorToErrno();
122
123 return error;
124 }
125
CreateWithUniqueName(llvm::StringRef prefix,bool child_process_inherit,llvm::SmallVectorImpl<char> & name)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);
135
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
138 // try again.
139 Status error;
140 do {
141 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(),
142 named_pipe_path);
143 error = CreateNew(named_pipe_path, child_process_inherit);
144 } while (error.GetError() == EEXIST);
145
146 if (error.Success())
147 name = named_pipe_path;
148 return error;
149 }
150
OpenAsReader(llvm::StringRef name,bool child_process_inherit)151 Status PipePosix::OpenAsReader(llvm::StringRef name,
152 bool child_process_inherit) {
153 if (CanRead() || CanWrite())
154 return Status("Pipe is already opened");
155
156 int flags = O_RDONLY | O_NONBLOCK;
157 if (!child_process_inherit)
158 flags |= O_CLOEXEC;
159
160 Status error;
161 int fd = ::open(name.data(), flags);
162 if (fd != -1)
163 m_fds[READ] = fd;
164 else
165 error.SetErrorToErrno();
166
167 return error;
168 }
169
170 Status
OpenAsWriterWithTimeout(llvm::StringRef name,bool child_process_inherit,const std::chrono::microseconds & timeout)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");
176
177 int flags = O_WRONLY | O_NONBLOCK;
178 if (!child_process_inherit)
179 flags |= O_CLOEXEC;
180
181 using namespace std::chrono;
182 const auto finish_time = Now() + timeout;
183
184 while (!CanWrite()) {
185 if (timeout != microseconds::zero()) {
186 const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
187 if (dur <= 0)
188 return Status("timeout exceeded - reader hasn't opened so far");
189 }
190
191 errno = 0;
192 int fd = ::open(name.data(), flags);
193 if (fd == -1) {
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)
197 return Status(errno_copy, eErrorTypePOSIX);
198
199 std::this_thread::sleep_for(
200 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
201 } else {
202 m_fds[WRITE] = fd;
203 }
204 }
205
206 return Status();
207 }
208
GetReadFileDescriptor() const209 int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
210
GetWriteFileDescriptor() const211 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
212
ReleaseReadFileDescriptor()213 int PipePosix::ReleaseReadFileDescriptor() {
214 const int fd = m_fds[READ];
215 m_fds[READ] = PipePosix::kInvalidDescriptor;
216 return fd;
217 }
218
ReleaseWriteFileDescriptor()219 int PipePosix::ReleaseWriteFileDescriptor() {
220 const int fd = m_fds[WRITE];
221 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
222 return fd;
223 }
224
Close()225 void PipePosix::Close() {
226 CloseReadFileDescriptor();
227 CloseWriteFileDescriptor();
228 }
229
Delete(llvm::StringRef name)230 Status PipePosix::Delete(llvm::StringRef name) {
231 return llvm::sys::fs::remove(name);
232 }
233
CanRead() const234 bool PipePosix::CanRead() const {
235 return m_fds[READ] != PipePosix::kInvalidDescriptor;
236 }
237
CanWrite() const238 bool PipePosix::CanWrite() const {
239 return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
240 }
241
CloseReadFileDescriptor()242 void PipePosix::CloseReadFileDescriptor() {
243 if (CanRead()) {
244 close(m_fds[READ]);
245 m_fds[READ] = PipePosix::kInvalidDescriptor;
246 }
247 }
248
CloseWriteFileDescriptor()249 void PipePosix::CloseWriteFileDescriptor() {
250 if (CanWrite()) {
251 close(m_fds[WRITE]);
252 m_fds[WRITE] = PipePosix::kInvalidDescriptor;
253 }
254 }
255
ReadWithTimeout(void * buf,size_t size,const std::chrono::microseconds & timeout,size_t & bytes_read)256 Status PipePosix::ReadWithTimeout(void *buf, size_t size,
257 const std::chrono::microseconds &timeout,
258 size_t &bytes_read) {
259 bytes_read = 0;
260 if (!CanRead())
261 return Status(EINVAL, eErrorTypePOSIX);
262
263 const int fd = GetReadFileDescriptor();
264
265 SelectHelper select_helper;
266 select_helper.SetTimeout(timeout);
267 select_helper.FDSetRead(fd);
268
269 Status error;
270 while (error.Success()) {
271 error = select_helper.Select();
272 if (error.Success()) {
273 auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read,
274 size - bytes_read);
275 if (result != -1) {
276 bytes_read += result;
277 if (bytes_read == size || result == 0)
278 break;
279 } else {
280 error.SetErrorToErrno();
281 break;
282 }
283 }
284 }
285 return error;
286 }
287
Write(const void * buf,size_t size,size_t & bytes_written)288 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
289 bytes_written = 0;
290 if (!CanWrite())
291 return Status(EINVAL, eErrorTypePOSIX);
292
293 const int fd = GetWriteFileDescriptor();
294 SelectHelper select_helper;
295 select_helper.SetTimeout(std::chrono::seconds(0));
296 select_helper.FDSetWrite(fd);
297
298 Status error;
299 while (error.Success()) {
300 error = select_helper.Select();
301 if (error.Success()) {
302 auto result =
303 ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written,
304 size - bytes_written);
305 if (result != -1) {
306 bytes_written += result;
307 if (bytes_written == size)
308 break;
309 } else {
310 error.SetErrorToErrno();
311 }
312 }
313 }
314 return error;
315 }
316