1 //===-- PipePosix.cpp -------------------------------------------*- C++ -*-===// 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 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" 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 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 61 std::chrono::time_point<std::chrono::steady_clock> Now() { 62 return std::chrono::steady_clock::now(); 63 } 64 } // namespace 65 66 PipePosix::PipePosix() 67 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {} 68 69 PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write) 70 : m_fds{read, write} {} 71 72 PipePosix::PipePosix(PipePosix &&pipe_posix) 73 : PipeBase{std::move(pipe_posix)}, 74 m_fds{pipe_posix.ReleaseReadFileDescriptor(), 75 pipe_posix.ReleaseWriteFileDescriptor()} {} 76 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 84 PipePosix::~PipePosix() { Close(); } 85 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 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 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 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 = llvm::sys::RetryAfterSignal(-1, ::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 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 && errno_copy != EINTR) 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 209 int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; } 210 211 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; } 212 213 int PipePosix::ReleaseReadFileDescriptor() { 214 const int fd = m_fds[READ]; 215 m_fds[READ] = PipePosix::kInvalidDescriptor; 216 return fd; 217 } 218 219 int PipePosix::ReleaseWriteFileDescriptor() { 220 const int fd = m_fds[WRITE]; 221 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 222 return fd; 223 } 224 225 void PipePosix::Close() { 226 CloseReadFileDescriptor(); 227 CloseWriteFileDescriptor(); 228 } 229 230 Status PipePosix::Delete(llvm::StringRef name) { 231 return llvm::sys::fs::remove(name); 232 } 233 234 bool PipePosix::CanRead() const { 235 return m_fds[READ] != PipePosix::kInvalidDescriptor; 236 } 237 238 bool PipePosix::CanWrite() const { 239 return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 240 } 241 242 void PipePosix::CloseReadFileDescriptor() { 243 if (CanRead()) { 244 close(m_fds[READ]); 245 m_fds[READ] = PipePosix::kInvalidDescriptor; 246 } 247 } 248 249 void PipePosix::CloseWriteFileDescriptor() { 250 if (CanWrite()) { 251 close(m_fds[WRITE]); 252 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 253 } 254 } 255 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 = 274 ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read); 275 if (result != -1) { 276 bytes_read += result; 277 if (bytes_read == size || result == 0) 278 break; 279 } else if (errno == EINTR) { 280 continue; 281 } else { 282 error.SetErrorToErrno(); 283 break; 284 } 285 } 286 } 287 return error; 288 } 289 290 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { 291 bytes_written = 0; 292 if (!CanWrite()) 293 return Status(EINVAL, eErrorTypePOSIX); 294 295 const int fd = GetWriteFileDescriptor(); 296 SelectHelper select_helper; 297 select_helper.SetTimeout(std::chrono::seconds(0)); 298 select_helper.FDSetWrite(fd); 299 300 Status error; 301 while (error.Success()) { 302 error = select_helper.Select(); 303 if (error.Success()) { 304 auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, 305 size - bytes_written); 306 if (result != -1) { 307 bytes_written += result; 308 if (bytes_written == size) 309 break; 310 } else if (errno == EINTR) { 311 continue; 312 } else { 313 error.SetErrorToErrno(); 314 } 315 } 316 } 317 return error; 318 } 319