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.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 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("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 122 Status 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 147 Status PipePosix::OpenAsReader(llvm::StringRef name, 148 bool child_process_inherit) { 149 std::scoped_lock<std::mutex, std::mutex> guard(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 168 Status 169 PipePosix::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 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.SetErrorToErrno(); 331 break; 332 } 333 } 334 } 335 return error; 336 } 337 338 Status 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