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 25 using namespace lldb; 26 using namespace lldb_private; 27 28 int PipePosix::kInvalidDescriptor = -1; 29 30 enum 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 41 static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; 42 43 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED 44 static 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 52 static std::chrono::time_point<std::chrono::steady_clock> Now() { 53 return std::chrono::steady_clock::now(); 54 } 55 56 PipePosix::PipePosix() 57 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {} 58 59 PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write) 60 : m_fds{read, write} {} 61 62 PipePosix::PipePosix(PipePosix &&pipe_posix) 63 : PipeBase{std::move(pipe_posix)}, 64 m_fds{pipe_posix.ReleaseReadFileDescriptor(), 65 pipe_posix.ReleaseWriteFileDescriptor()} {} 66 67 PipePosix &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 78 PipePosix::~PipePosix() { Close(); } 79 80 Status 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 = Status::FromErrno(); 95 CloseUnlocked(); 96 return error; 97 } 98 } 99 #endif 100 return error; 101 } 102 #endif 103 104 error = Status::FromErrno(); 105 m_fds[READ] = PipePosix::kInvalidDescriptor; 106 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 107 return error; 108 } 109 110 Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) { 111 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 112 if (CanReadUnlocked() || CanWriteUnlocked()) 113 return Status::FromErrorString("Pipe is already opened"); 114 115 Status error; 116 if (::mkfifo(name.str().c_str(), 0660) != 0) 117 error = Status::FromErrno(); 118 return error; 119 } 120 121 Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix, 122 bool child_process_inherit, 123 llvm::SmallVectorImpl<char> &name) { 124 llvm::SmallString<128> named_pipe_path; 125 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str()); 126 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 127 if (!tmpdir_file_spec) 128 tmpdir_file_spec.AppendPathComponent("/tmp"); 129 tmpdir_file_spec.AppendPathComponent(pipe_spec); 130 131 // It's possible that another process creates the target path after we've 132 // verified it's available but before we create it, in which case we should 133 // try again. 134 Status error; 135 do { 136 llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path, 137 /*MakeAbsolute=*/false); 138 error = CreateNew(named_pipe_path, child_process_inherit); 139 } while (error.GetError() == EEXIST); 140 141 if (error.Success()) 142 name = named_pipe_path; 143 return error; 144 } 145 146 Status PipePosix::OpenAsReader(llvm::StringRef name, 147 bool child_process_inherit) { 148 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 149 150 if (CanReadUnlocked() || CanWriteUnlocked()) 151 return Status::FromErrorString("Pipe is already opened"); 152 153 int flags = O_RDONLY | O_NONBLOCK; 154 if (!child_process_inherit) 155 flags |= O_CLOEXEC; 156 157 Status error; 158 int fd = FileSystem::Instance().Open(name.str().c_str(), flags); 159 if (fd != -1) 160 m_fds[READ] = fd; 161 else 162 error = Status::FromErrno(); 163 164 return error; 165 } 166 167 Status 168 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, 169 bool child_process_inherit, 170 const std::chrono::microseconds &timeout) { 171 std::lock_guard<std::mutex> guard(m_write_mutex); 172 if (CanReadUnlocked() || CanWriteUnlocked()) 173 return Status::FromErrorString("Pipe is already opened"); 174 175 int flags = O_WRONLY | O_NONBLOCK; 176 if (!child_process_inherit) 177 flags |= O_CLOEXEC; 178 179 using namespace std::chrono; 180 const auto finish_time = Now() + timeout; 181 182 while (!CanWriteUnlocked()) { 183 if (timeout != microseconds::zero()) { 184 const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); 185 if (dur <= 0) 186 return Status::FromErrorString( 187 "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 208 int PipePosix::GetReadFileDescriptor() const { 209 std::lock_guard<std::mutex> guard(m_read_mutex); 210 return GetReadFileDescriptorUnlocked(); 211 } 212 213 int PipePosix::GetReadFileDescriptorUnlocked() const { 214 return m_fds[READ]; 215 } 216 217 int PipePosix::GetWriteFileDescriptor() const { 218 std::lock_guard<std::mutex> guard(m_write_mutex); 219 return GetWriteFileDescriptorUnlocked(); 220 } 221 222 int PipePosix::GetWriteFileDescriptorUnlocked() const { 223 return m_fds[WRITE]; 224 } 225 226 int PipePosix::ReleaseReadFileDescriptor() { 227 std::lock_guard<std::mutex> guard(m_read_mutex); 228 return ReleaseReadFileDescriptorUnlocked(); 229 } 230 231 int PipePosix::ReleaseReadFileDescriptorUnlocked() { 232 const int fd = m_fds[READ]; 233 m_fds[READ] = PipePosix::kInvalidDescriptor; 234 return fd; 235 } 236 237 int PipePosix::ReleaseWriteFileDescriptor() { 238 std::lock_guard<std::mutex> guard(m_write_mutex); 239 return ReleaseWriteFileDescriptorUnlocked(); 240 } 241 242 int PipePosix::ReleaseWriteFileDescriptorUnlocked() { 243 const int fd = m_fds[WRITE]; 244 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 245 return fd; 246 } 247 248 void PipePosix::Close() { 249 std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex); 250 CloseUnlocked(); 251 } 252 253 void PipePosix::CloseUnlocked() { 254 CloseReadFileDescriptorUnlocked(); 255 CloseWriteFileDescriptorUnlocked(); 256 } 257 258 Status PipePosix::Delete(llvm::StringRef name) { 259 return llvm::sys::fs::remove(name); 260 } 261 262 bool PipePosix::CanRead() const { 263 std::lock_guard<std::mutex> guard(m_read_mutex); 264 return CanReadUnlocked(); 265 } 266 267 bool PipePosix::CanReadUnlocked() const { 268 return m_fds[READ] != PipePosix::kInvalidDescriptor; 269 } 270 271 bool PipePosix::CanWrite() const { 272 std::lock_guard<std::mutex> guard(m_write_mutex); 273 return CanWriteUnlocked(); 274 } 275 276 bool PipePosix::CanWriteUnlocked() const { 277 return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 278 } 279 280 void PipePosix::CloseReadFileDescriptor() { 281 std::lock_guard<std::mutex> guard(m_read_mutex); 282 CloseReadFileDescriptorUnlocked(); 283 } 284 void PipePosix::CloseReadFileDescriptorUnlocked() { 285 if (CanReadUnlocked()) { 286 close(m_fds[READ]); 287 m_fds[READ] = PipePosix::kInvalidDescriptor; 288 } 289 } 290 291 void PipePosix::CloseWriteFileDescriptor() { 292 std::lock_guard<std::mutex> guard(m_write_mutex); 293 CloseWriteFileDescriptorUnlocked(); 294 } 295 296 void PipePosix::CloseWriteFileDescriptorUnlocked() { 297 if (CanWriteUnlocked()) { 298 close(m_fds[WRITE]); 299 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 300 } 301 } 302 303 Status 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 = Status::FromErrno(); 331 break; 332 } 333 } 334 } 335 return error; 336 } 337 338 Status PipePosix::WriteWithTimeout(const void *buf, size_t size, 339 const std::chrono::microseconds &timeout, 340 size_t &bytes_written) { 341 std::lock_guard<std::mutex> guard(m_write_mutex); 342 bytes_written = 0; 343 if (!CanWriteUnlocked()) 344 return Status(EINVAL, eErrorTypePOSIX); 345 346 const int fd = GetWriteFileDescriptorUnlocked(); 347 SelectHelper select_helper; 348 select_helper.SetTimeout(timeout); 349 select_helper.FDSetWrite(fd); 350 351 Status error; 352 while (error.Success()) { 353 error = select_helper.Select(); 354 if (error.Success()) { 355 auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, 356 size - bytes_written); 357 if (result != -1) { 358 bytes_written += result; 359 if (bytes_written == size) 360 break; 361 } else if (errno == EINTR) { 362 continue; 363 } else { 364 error = Status::FromErrno(); 365 } 366 } 367 } 368 return error; 369 } 370