xref: /openbsd-src/gnu/llvm/lldb/source/Host/posix/PipePosix.cpp (revision 101d251d5caf88a9341f3045ab62e122abae1b90)
1dda28197Spatrick //===-- PipePosix.cpp -----------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Host/posix/PipePosix.h"
10061da546Spatrick #include "lldb/Host/HostInfo.h"
11061da546Spatrick #include "lldb/Utility/SelectHelper.h"
12061da546Spatrick #include "llvm/ADT/SmallString.h"
13061da546Spatrick #include "llvm/Support/Errno.h"
14061da546Spatrick #include "llvm/Support/FileSystem.h"
15061da546Spatrick #include <functional>
16061da546Spatrick #include <thread>
17061da546Spatrick 
18a0747c9fSpatrick #include <cerrno>
19a0747c9fSpatrick #include <climits>
20061da546Spatrick #include <fcntl.h>
21061da546Spatrick #include <sys/stat.h>
22061da546Spatrick #include <sys/types.h>
23061da546Spatrick #include <unistd.h>
24061da546Spatrick 
25061da546Spatrick using namespace lldb;
26061da546Spatrick using namespace lldb_private;
27061da546Spatrick 
28061da546Spatrick int PipePosix::kInvalidDescriptor = -1;
29061da546Spatrick 
30061da546Spatrick enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
31061da546Spatrick 
32061da546Spatrick // pipe2 is supported by a limited set of platforms
33061da546Spatrick // TODO: Add more platforms that support pipe2.
34061da546Spatrick #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) ||       \
35adae0cfdSpatrick     defined(__NetBSD__) || defined(__OpenBSD__)
36061da546Spatrick #define PIPE2_SUPPORTED 1
37061da546Spatrick #else
38061da546Spatrick #define PIPE2_SUPPORTED 0
39061da546Spatrick #endif
40061da546Spatrick 
41*101d251dSrobert static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
42061da546Spatrick 
43061da546Spatrick #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
SetCloexecFlag(int fd)44*101d251dSrobert static bool SetCloexecFlag(int fd) {
45061da546Spatrick   int flags = ::fcntl(fd, F_GETFD);
46061da546Spatrick   if (flags == -1)
47061da546Spatrick     return false;
48061da546Spatrick   return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
49061da546Spatrick }
50061da546Spatrick #endif
51061da546Spatrick 
Now()52*101d251dSrobert static std::chrono::time_point<std::chrono::steady_clock> Now() {
53061da546Spatrick   return std::chrono::steady_clock::now();
54061da546Spatrick }
55061da546Spatrick 
PipePosix()56061da546Spatrick PipePosix::PipePosix()
57061da546Spatrick     : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
58061da546Spatrick 
PipePosix(lldb::pipe_t read,lldb::pipe_t write)59061da546Spatrick PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
60061da546Spatrick     : m_fds{read, write} {}
61061da546Spatrick 
PipePosix(PipePosix && pipe_posix)62061da546Spatrick PipePosix::PipePosix(PipePosix &&pipe_posix)
63061da546Spatrick     : PipeBase{std::move(pipe_posix)},
64061da546Spatrick       m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65061da546Spatrick             pipe_posix.ReleaseWriteFileDescriptor()} {}
66061da546Spatrick 
operator =(PipePosix && pipe_posix)67061da546Spatrick PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
68061da546Spatrick   PipeBase::operator=(std::move(pipe_posix));
69061da546Spatrick   m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
70061da546Spatrick   m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
71061da546Spatrick   return *this;
72061da546Spatrick }
73061da546Spatrick 
~PipePosix()74061da546Spatrick PipePosix::~PipePosix() { Close(); }
75061da546Spatrick 
CreateNew(bool child_processes_inherit)76061da546Spatrick Status PipePosix::CreateNew(bool child_processes_inherit) {
77061da546Spatrick   if (CanRead() || CanWrite())
78061da546Spatrick     return Status(EINVAL, eErrorTypePOSIX);
79061da546Spatrick 
80061da546Spatrick   Status error;
81061da546Spatrick #if PIPE2_SUPPORTED
82061da546Spatrick   if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
83061da546Spatrick     return error;
84061da546Spatrick #else
85061da546Spatrick   if (::pipe(m_fds) == 0) {
86061da546Spatrick #ifdef FD_CLOEXEC
87061da546Spatrick     if (!child_processes_inherit) {
88061da546Spatrick       if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
89061da546Spatrick         error.SetErrorToErrno();
90061da546Spatrick         Close();
91061da546Spatrick         return error;
92061da546Spatrick       }
93061da546Spatrick     }
94061da546Spatrick #endif
95061da546Spatrick     return error;
96061da546Spatrick   }
97061da546Spatrick #endif
98061da546Spatrick 
99061da546Spatrick   error.SetErrorToErrno();
100061da546Spatrick   m_fds[READ] = PipePosix::kInvalidDescriptor;
101061da546Spatrick   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
102061da546Spatrick   return error;
103061da546Spatrick }
104061da546Spatrick 
CreateNew(llvm::StringRef name,bool child_process_inherit)105061da546Spatrick Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
106061da546Spatrick   if (CanRead() || CanWrite())
107061da546Spatrick     return Status("Pipe is already opened");
108061da546Spatrick 
109061da546Spatrick   Status error;
110a0747c9fSpatrick   if (::mkfifo(name.str().c_str(), 0660) != 0)
111061da546Spatrick     error.SetErrorToErrno();
112061da546Spatrick 
113061da546Spatrick   return error;
114061da546Spatrick }
115061da546Spatrick 
CreateWithUniqueName(llvm::StringRef prefix,bool child_process_inherit,llvm::SmallVectorImpl<char> & name)116061da546Spatrick Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
117061da546Spatrick                                        bool child_process_inherit,
118061da546Spatrick                                        llvm::SmallVectorImpl<char> &name) {
119061da546Spatrick   llvm::SmallString<128> named_pipe_path;
120061da546Spatrick   llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
121061da546Spatrick   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
122061da546Spatrick   if (!tmpdir_file_spec)
123061da546Spatrick     tmpdir_file_spec.AppendPathComponent("/tmp");
124061da546Spatrick   tmpdir_file_spec.AppendPathComponent(pipe_spec);
125061da546Spatrick 
126061da546Spatrick   // It's possible that another process creates the target path after we've
127061da546Spatrick   // verified it's available but before we create it, in which case we should
128061da546Spatrick   // try again.
129061da546Spatrick   Status error;
130061da546Spatrick   do {
131a0747c9fSpatrick     llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
132a0747c9fSpatrick                                     /*MakeAbsolute=*/false);
133061da546Spatrick     error = CreateNew(named_pipe_path, child_process_inherit);
134061da546Spatrick   } while (error.GetError() == EEXIST);
135061da546Spatrick 
136061da546Spatrick   if (error.Success())
137061da546Spatrick     name = named_pipe_path;
138061da546Spatrick   return error;
139061da546Spatrick }
140061da546Spatrick 
OpenAsReader(llvm::StringRef name,bool child_process_inherit)141061da546Spatrick Status PipePosix::OpenAsReader(llvm::StringRef name,
142061da546Spatrick                                bool child_process_inherit) {
143061da546Spatrick   if (CanRead() || CanWrite())
144061da546Spatrick     return Status("Pipe is already opened");
145061da546Spatrick 
146061da546Spatrick   int flags = O_RDONLY | O_NONBLOCK;
147061da546Spatrick   if (!child_process_inherit)
148061da546Spatrick     flags |= O_CLOEXEC;
149061da546Spatrick 
150061da546Spatrick   Status error;
151a0747c9fSpatrick   int fd = llvm::sys::RetryAfterSignal(-1, ::open, name.str().c_str(), flags);
152061da546Spatrick   if (fd != -1)
153061da546Spatrick     m_fds[READ] = fd;
154061da546Spatrick   else
155061da546Spatrick     error.SetErrorToErrno();
156061da546Spatrick 
157061da546Spatrick   return error;
158061da546Spatrick }
159061da546Spatrick 
160061da546Spatrick Status
OpenAsWriterWithTimeout(llvm::StringRef name,bool child_process_inherit,const std::chrono::microseconds & timeout)161061da546Spatrick PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
162061da546Spatrick                                    bool child_process_inherit,
163061da546Spatrick                                    const std::chrono::microseconds &timeout) {
164061da546Spatrick   if (CanRead() || CanWrite())
165061da546Spatrick     return Status("Pipe is already opened");
166061da546Spatrick 
167061da546Spatrick   int flags = O_WRONLY | O_NONBLOCK;
168061da546Spatrick   if (!child_process_inherit)
169061da546Spatrick     flags |= O_CLOEXEC;
170061da546Spatrick 
171061da546Spatrick   using namespace std::chrono;
172061da546Spatrick   const auto finish_time = Now() + timeout;
173061da546Spatrick 
174061da546Spatrick   while (!CanWrite()) {
175061da546Spatrick     if (timeout != microseconds::zero()) {
176061da546Spatrick       const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
177061da546Spatrick       if (dur <= 0)
178061da546Spatrick         return Status("timeout exceeded - reader hasn't opened so far");
179061da546Spatrick     }
180061da546Spatrick 
181061da546Spatrick     errno = 0;
182a0747c9fSpatrick     int fd = ::open(name.str().c_str(), flags);
183061da546Spatrick     if (fd == -1) {
184061da546Spatrick       const auto errno_copy = errno;
185061da546Spatrick       // We may get ENXIO if a reader side of the pipe hasn't opened yet.
186061da546Spatrick       if (errno_copy != ENXIO && errno_copy != EINTR)
187061da546Spatrick         return Status(errno_copy, eErrorTypePOSIX);
188061da546Spatrick 
189061da546Spatrick       std::this_thread::sleep_for(
190061da546Spatrick           milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
191061da546Spatrick     } else {
192061da546Spatrick       m_fds[WRITE] = fd;
193061da546Spatrick     }
194061da546Spatrick   }
195061da546Spatrick 
196061da546Spatrick   return Status();
197061da546Spatrick }
198061da546Spatrick 
GetReadFileDescriptor() const199061da546Spatrick int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
200061da546Spatrick 
GetWriteFileDescriptor() const201061da546Spatrick int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
202061da546Spatrick 
ReleaseReadFileDescriptor()203061da546Spatrick int PipePosix::ReleaseReadFileDescriptor() {
204061da546Spatrick   const int fd = m_fds[READ];
205061da546Spatrick   m_fds[READ] = PipePosix::kInvalidDescriptor;
206061da546Spatrick   return fd;
207061da546Spatrick }
208061da546Spatrick 
ReleaseWriteFileDescriptor()209061da546Spatrick int PipePosix::ReleaseWriteFileDescriptor() {
210061da546Spatrick   const int fd = m_fds[WRITE];
211061da546Spatrick   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
212061da546Spatrick   return fd;
213061da546Spatrick }
214061da546Spatrick 
Close()215061da546Spatrick void PipePosix::Close() {
216061da546Spatrick   CloseReadFileDescriptor();
217061da546Spatrick   CloseWriteFileDescriptor();
218061da546Spatrick }
219061da546Spatrick 
Delete(llvm::StringRef name)220061da546Spatrick Status PipePosix::Delete(llvm::StringRef name) {
221061da546Spatrick   return llvm::sys::fs::remove(name);
222061da546Spatrick }
223061da546Spatrick 
CanRead() const224061da546Spatrick bool PipePosix::CanRead() const {
225061da546Spatrick   return m_fds[READ] != PipePosix::kInvalidDescriptor;
226061da546Spatrick }
227061da546Spatrick 
CanWrite() const228061da546Spatrick bool PipePosix::CanWrite() const {
229061da546Spatrick   return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
230061da546Spatrick }
231061da546Spatrick 
CloseReadFileDescriptor()232061da546Spatrick void PipePosix::CloseReadFileDescriptor() {
233061da546Spatrick   if (CanRead()) {
234061da546Spatrick     close(m_fds[READ]);
235061da546Spatrick     m_fds[READ] = PipePosix::kInvalidDescriptor;
236061da546Spatrick   }
237061da546Spatrick }
238061da546Spatrick 
CloseWriteFileDescriptor()239061da546Spatrick void PipePosix::CloseWriteFileDescriptor() {
240061da546Spatrick   if (CanWrite()) {
241061da546Spatrick     close(m_fds[WRITE]);
242061da546Spatrick     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
243061da546Spatrick   }
244061da546Spatrick }
245061da546Spatrick 
ReadWithTimeout(void * buf,size_t size,const std::chrono::microseconds & timeout,size_t & bytes_read)246061da546Spatrick Status PipePosix::ReadWithTimeout(void *buf, size_t size,
247061da546Spatrick                                   const std::chrono::microseconds &timeout,
248061da546Spatrick                                   size_t &bytes_read) {
249061da546Spatrick   bytes_read = 0;
250061da546Spatrick   if (!CanRead())
251061da546Spatrick     return Status(EINVAL, eErrorTypePOSIX);
252061da546Spatrick 
253061da546Spatrick   const int fd = GetReadFileDescriptor();
254061da546Spatrick 
255061da546Spatrick   SelectHelper select_helper;
256061da546Spatrick   select_helper.SetTimeout(timeout);
257061da546Spatrick   select_helper.FDSetRead(fd);
258061da546Spatrick 
259061da546Spatrick   Status error;
260061da546Spatrick   while (error.Success()) {
261061da546Spatrick     error = select_helper.Select();
262061da546Spatrick     if (error.Success()) {
263061da546Spatrick       auto result =
264061da546Spatrick           ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
265061da546Spatrick       if (result != -1) {
266061da546Spatrick         bytes_read += result;
267061da546Spatrick         if (bytes_read == size || result == 0)
268061da546Spatrick           break;
269061da546Spatrick       } else if (errno == EINTR) {
270061da546Spatrick         continue;
271061da546Spatrick       } else {
272061da546Spatrick         error.SetErrorToErrno();
273061da546Spatrick         break;
274061da546Spatrick       }
275061da546Spatrick     }
276061da546Spatrick   }
277061da546Spatrick   return error;
278061da546Spatrick }
279061da546Spatrick 
Write(const void * buf,size_t size,size_t & bytes_written)280061da546Spatrick Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
281061da546Spatrick   bytes_written = 0;
282061da546Spatrick   if (!CanWrite())
283061da546Spatrick     return Status(EINVAL, eErrorTypePOSIX);
284061da546Spatrick 
285061da546Spatrick   const int fd = GetWriteFileDescriptor();
286061da546Spatrick   SelectHelper select_helper;
287061da546Spatrick   select_helper.SetTimeout(std::chrono::seconds(0));
288061da546Spatrick   select_helper.FDSetWrite(fd);
289061da546Spatrick 
290061da546Spatrick   Status error;
291061da546Spatrick   while (error.Success()) {
292061da546Spatrick     error = select_helper.Select();
293061da546Spatrick     if (error.Success()) {
294061da546Spatrick       auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
295061da546Spatrick                             size - bytes_written);
296061da546Spatrick       if (result != -1) {
297061da546Spatrick         bytes_written += result;
298061da546Spatrick         if (bytes_written == size)
299061da546Spatrick           break;
300061da546Spatrick       } else if (errno == EINTR) {
301061da546Spatrick         continue;
302061da546Spatrick       } else {
303061da546Spatrick         error.SetErrorToErrno();
304061da546Spatrick       }
305061da546Spatrick     }
306061da546Spatrick   }
307061da546Spatrick   return error;
308061da546Spatrick }
309