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 9#include "lldb/Host/posix/PipePosix.h" 10#include "lldb/Host/FileSystem.h" 11#include "lldb/Host/HostInfo.h" 12#include "lldb/Utility/SelectHelper.h" 13#include "llvm/ADT/SmallString.h" 14#include "llvm/Support/Errno.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__) || defined(__NetBSD__) || \ 35 defined(__OpenBSD__) 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 std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard( 69 m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex, 70 pipe_posix.m_write_mutex); 71 72 PipeBase::operator=(std::move(pipe_posix)); 73 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked(); 74 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked(); 75 return *this; 76} 77 78PipePosix::~PipePosix() { Close(); } 79 80Status PipePosix::CreateNew(bool child_processes_inherit) { 81 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 82 if (CanReadUnlocked() || CanWriteUnlocked()) 83 return Status(EINVAL, eErrorTypePOSIX); 84 85 Status error; 86#if PIPE2_SUPPORTED 87 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) 88 return error; 89#else 90 if (::pipe(m_fds) == 0) { 91#ifdef FD_CLOEXEC 92 if (!child_processes_inherit) { 93 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) { 94 error.SetErrorToErrno(); 95 CloseUnlocked(); 96 return error; 97 } 98 } 99#endif 100 return error; 101 } 102#endif 103 104 error.SetErrorToErrno(); 105 m_fds[READ] = PipePosix::kInvalidDescriptor; 106 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 107 return error; 108} 109 110Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) { 111 std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex); 112 if (CanReadUnlocked() || CanWriteUnlocked()) 113 return Status("Pipe is already opened"); 114 115 Status error; 116 if (::mkfifo(name.str().c_str(), 0660) != 0) 117 error.SetErrorToErrno(); 118 119 return error; 120} 121 122Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix, 123 bool child_process_inherit, 124 llvm::SmallVectorImpl<char> &name) { 125 llvm::SmallString<128> named_pipe_path; 126 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str()); 127 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 128 if (!tmpdir_file_spec) 129 tmpdir_file_spec.AppendPathComponent("/tmp"); 130 tmpdir_file_spec.AppendPathComponent(pipe_spec); 131 132 // It's possible that another process creates the target path after we've 133 // verified it's available but before we create it, in which case we should 134 // try again. 135 Status error; 136 do { 137 llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path, 138 /*MakeAbsolute=*/false); 139 error = CreateNew(named_pipe_path, child_process_inherit); 140 } while (error.GetError() == EEXIST); 141 142 if (error.Success()) 143 name = named_pipe_path; 144 return error; 145} 146 147Status PipePosix::OpenAsReader(llvm::StringRef name, 148 bool child_process_inherit) { 149 std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex); 150 151 if (CanReadUnlocked() || CanWriteUnlocked()) 152 return Status("Pipe is already opened"); 153 154 int flags = O_RDONLY | O_NONBLOCK; 155 if (!child_process_inherit) 156 flags |= O_CLOEXEC; 157 158 Status error; 159 int fd = FileSystem::Instance().Open(name.str().c_str(), flags); 160 if (fd != -1) 161 m_fds[READ] = fd; 162 else 163 error.SetErrorToErrno(); 164 165 return error; 166} 167 168Status 169PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, 170 bool child_process_inherit, 171 const std::chrono::microseconds &timeout) { 172 std::lock_guard<std::mutex> guard(m_write_mutex); 173 if (CanReadUnlocked() || CanWriteUnlocked()) 174 return Status("Pipe is already opened"); 175 176 int flags = O_WRONLY | O_NONBLOCK; 177 if (!child_process_inherit) 178 flags |= O_CLOEXEC; 179 180 using namespace std::chrono; 181 const auto finish_time = Now() + timeout; 182 183 while (!CanWriteUnlocked()) { 184 if (timeout != microseconds::zero()) { 185 const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); 186 if (dur <= 0) 187 return Status("timeout exceeded - reader hasn't opened so far"); 188 } 189 190 errno = 0; 191 int fd = ::open(name.str().c_str(), flags); 192 if (fd == -1) { 193 const auto errno_copy = errno; 194 // We may get ENXIO if a reader side of the pipe hasn't opened yet. 195 if (errno_copy != ENXIO && errno_copy != EINTR) 196 return Status(errno_copy, eErrorTypePOSIX); 197 198 std::this_thread::sleep_for( 199 milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); 200 } else { 201 m_fds[WRITE] = fd; 202 } 203 } 204 205 return Status(); 206} 207 208int PipePosix::GetReadFileDescriptor() const { 209 std::lock_guard<std::mutex> guard(m_read_mutex); 210 return GetReadFileDescriptorUnlocked(); 211} 212 213int PipePosix::GetReadFileDescriptorUnlocked() const { 214 return m_fds[READ]; 215} 216 217int PipePosix::GetWriteFileDescriptor() const { 218 std::lock_guard<std::mutex> guard(m_write_mutex); 219 return GetWriteFileDescriptorUnlocked(); 220} 221 222int PipePosix::GetWriteFileDescriptorUnlocked() const { 223 return m_fds[WRITE]; 224} 225 226int PipePosix::ReleaseReadFileDescriptor() { 227 std::lock_guard<std::mutex> guard(m_read_mutex); 228 return ReleaseReadFileDescriptorUnlocked(); 229} 230 231int PipePosix::ReleaseReadFileDescriptorUnlocked() { 232 const int fd = m_fds[READ]; 233 m_fds[READ] = PipePosix::kInvalidDescriptor; 234 return fd; 235} 236 237int PipePosix::ReleaseWriteFileDescriptor() { 238 std::lock_guard<std::mutex> guard(m_write_mutex); 239 return ReleaseWriteFileDescriptorUnlocked(); 240} 241 242int PipePosix::ReleaseWriteFileDescriptorUnlocked() { 243 const int fd = m_fds[WRITE]; 244 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 245 return fd; 246} 247 248void PipePosix::Close() { 249 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 250 CloseUnlocked(); 251} 252 253void PipePosix::CloseUnlocked() { 254 CloseReadFileDescriptorUnlocked(); 255 CloseWriteFileDescriptorUnlocked(); 256} 257 258Status PipePosix::Delete(llvm::StringRef name) { 259 return llvm::sys::fs::remove(name); 260} 261 262bool PipePosix::CanRead() const { 263 std::lock_guard<std::mutex> guard(m_read_mutex); 264 return CanReadUnlocked(); 265} 266 267bool PipePosix::CanReadUnlocked() const { 268 return m_fds[READ] != PipePosix::kInvalidDescriptor; 269} 270 271bool PipePosix::CanWrite() const { 272 std::lock_guard<std::mutex> guard(m_write_mutex); 273 return CanWriteUnlocked(); 274} 275 276bool PipePosix::CanWriteUnlocked() const { 277 return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 278} 279 280void PipePosix::CloseReadFileDescriptor() { 281 std::lock_guard<std::mutex> guard(m_read_mutex); 282 CloseReadFileDescriptorUnlocked(); 283} 284void PipePosix::CloseReadFileDescriptorUnlocked() { 285 if (CanReadUnlocked()) { 286 close(m_fds[READ]); 287 m_fds[READ] = PipePosix::kInvalidDescriptor; 288 } 289} 290 291void PipePosix::CloseWriteFileDescriptor() { 292 std::lock_guard<std::mutex> guard(m_write_mutex); 293 CloseWriteFileDescriptorUnlocked(); 294} 295 296void PipePosix::CloseWriteFileDescriptorUnlocked() { 297 if (CanWriteUnlocked()) { 298 close(m_fds[WRITE]); 299 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 300 } 301} 302 303Status PipePosix::ReadWithTimeout(void *buf, size_t size, 304 const std::chrono::microseconds &timeout, 305 size_t &bytes_read) { 306 std::lock_guard<std::mutex> guard(m_read_mutex); 307 bytes_read = 0; 308 if (!CanReadUnlocked()) 309 return Status(EINVAL, eErrorTypePOSIX); 310 311 const int fd = GetReadFileDescriptorUnlocked(); 312 313 SelectHelper select_helper; 314 select_helper.SetTimeout(timeout); 315 select_helper.FDSetRead(fd); 316 317 Status error; 318 while (error.Success()) { 319 error = select_helper.Select(); 320 if (error.Success()) { 321 auto result = 322 ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read); 323 if (result != -1) { 324 bytes_read += result; 325 if (bytes_read == size || result == 0) 326 break; 327 } else if (errno == EINTR) { 328 continue; 329 } else { 330 error.SetErrorToErrno(); 331 break; 332 } 333 } 334 } 335 return error; 336} 337 338Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { 339 std::lock_guard<std::mutex> guard(m_write_mutex); 340 bytes_written = 0; 341 if (!CanWriteUnlocked()) 342 return Status(EINVAL, eErrorTypePOSIX); 343 344 const int fd = GetWriteFileDescriptorUnlocked(); 345 SelectHelper select_helper; 346 select_helper.SetTimeout(std::chrono::seconds(0)); 347 select_helper.FDSetWrite(fd); 348 349 Status error; 350 while (error.Success()) { 351 error = select_helper.Select(); 352 if (error.Success()) { 353 auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, 354 size - bytes_written); 355 if (result != -1) { 356 bytes_written += result; 357 if (bytes_written == size) 358 break; 359 } else if (errno == EINTR) { 360 continue; 361 } else { 362 error.SetErrorToErrno(); 363 } 364 } 365 } 366 return error; 367} 368