1 //===-- PipePosix.cpp -------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Host/posix/PipePosix.h" 11 #include "lldb/Host/FileSystem.h" 12 #include "lldb/Host/HostInfo.h" 13 #include "llvm/ADT/SmallString.h" 14 #include "llvm/Support/FileSystem.h" 15 16 #include <functional> 17 #include <thread> 18 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <limits.h> 22 #include <unistd.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 int PipePosix::kInvalidDescriptor = -1; 30 31 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE 32 33 // pipe2 is supported by Linux, FreeBSD v10 and higher. 34 // TODO: Add more platforms that support pipe2. 35 #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) 36 #define PIPE2_SUPPORTED 1 37 #else 38 #define PIPE2_SUPPORTED 0 39 #endif 40 41 namespace 42 { 43 44 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; 45 46 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED 47 bool SetCloexecFlag(int fd) 48 { 49 int flags = ::fcntl(fd, F_GETFD); 50 if (flags == -1) 51 return false; 52 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); 53 } 54 #endif 55 56 std::chrono::time_point<std::chrono::steady_clock> 57 Now() 58 { 59 return std::chrono::steady_clock::now(); 60 } 61 62 Error 63 SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout) 64 { 65 Error error; 66 fd_set fds; 67 bool done = false; 68 69 using namespace std::chrono; 70 71 const auto finish_time = Now() + timeout; 72 73 while (!done) 74 { 75 struct timeval tv = {0, 0}; 76 if (timeout != microseconds::zero()) 77 { 78 const auto remaining_dur = duration_cast<microseconds>(finish_time - Now()); 79 if (remaining_dur.count() <= 0) 80 { 81 error.SetErrorString("timeout exceeded"); 82 break; 83 } 84 const auto dur_secs = duration_cast<seconds>(remaining_dur); 85 const auto dur_usecs = remaining_dur % seconds(1); 86 87 tv.tv_sec = dur_secs.count(); 88 tv.tv_usec = dur_usecs.count(); 89 } 90 else 91 tv.tv_sec = 1; 92 93 FD_ZERO(&fds); 94 FD_SET(handle, &fds); 95 96 const auto retval = ::select(handle + 1, 97 (is_read) ? &fds : nullptr, 98 (is_read) ? nullptr : &fds, 99 nullptr, &tv); 100 if (retval == -1) 101 { 102 if (errno == EINTR) 103 continue; 104 error.SetErrorToErrno(); 105 break; 106 } 107 if (retval == 0) 108 { 109 error.SetErrorString("timeout exceeded"); 110 break; 111 } 112 if (!FD_ISSET(handle, &fds)) 113 { 114 error.SetErrorString("invalid state"); 115 break; 116 } 117 118 error = io_handler(done); 119 if (error.Fail()) 120 { 121 if (error.GetError() == EINTR) 122 continue; 123 break; 124 } 125 } 126 return error; 127 } 128 129 } 130 131 PipePosix::PipePosix() 132 : m_fds{ 133 PipePosix::kInvalidDescriptor, 134 PipePosix::kInvalidDescriptor 135 } {} 136 137 PipePosix::PipePosix(int read_fd, int write_fd) 138 : m_fds{read_fd, write_fd} {} 139 140 PipePosix::PipePosix(PipePosix &&pipe_posix) 141 : PipeBase{std::move(pipe_posix)}, 142 m_fds{ 143 pipe_posix.ReleaseReadFileDescriptor(), 144 pipe_posix.ReleaseWriteFileDescriptor() 145 } {} 146 147 PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) 148 { 149 PipeBase::operator=(std::move(pipe_posix)); 150 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor(); 151 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor(); 152 return *this; 153 } 154 155 PipePosix::~PipePosix() 156 { 157 Close(); 158 } 159 160 Error 161 PipePosix::CreateNew(bool child_processes_inherit) 162 { 163 if (CanRead() || CanWrite()) 164 return Error(EINVAL, eErrorTypePOSIX); 165 166 Error error; 167 #if PIPE2_SUPPORTED 168 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) 169 return error; 170 #else 171 if (::pipe(m_fds) == 0) 172 { 173 #ifdef FD_CLOEXEC 174 if (!child_processes_inherit) 175 { 176 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) 177 { 178 error.SetErrorToErrno(); 179 Close(); 180 return error; 181 } 182 } 183 #endif 184 return error; 185 } 186 #endif 187 188 error.SetErrorToErrno(); 189 m_fds[READ] = PipePosix::kInvalidDescriptor; 190 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 191 return error; 192 } 193 194 Error 195 PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) 196 { 197 if (CanRead() || CanWrite()) 198 return Error("Pipe is already opened"); 199 200 Error error; 201 if (::mkfifo(name.data(), 0660) != 0) 202 error.SetErrorToErrno(); 203 204 return error; 205 } 206 207 Error 208 PipePosix::CreateWithUniqueName(llvm::StringRef prefix, bool child_process_inherit, llvm::SmallVectorImpl<char>& name) 209 { 210 llvm::SmallString<PATH_MAX> named_pipe_path; 211 llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str()); 212 FileSpec tmpdir_file_spec; 213 tmpdir_file_spec.Clear(); 214 if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) 215 { 216 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); 217 } 218 else 219 { 220 tmpdir_file_spec.AppendPathComponent("/tmp"); 221 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); 222 } 223 224 // It's possible that another process creates the target path after we've 225 // verified it's available but before we create it, in which case we 226 // should try again. 227 Error error; 228 do { 229 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), named_pipe_path); 230 error = CreateNew(named_pipe_path, child_process_inherit); 231 } while (error.GetError() == EEXIST); 232 233 if (error.Success()) 234 name = named_pipe_path; 235 return error; 236 } 237 238 Error 239 PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit) 240 { 241 if (CanRead() || CanWrite()) 242 return Error("Pipe is already opened"); 243 244 int flags = O_RDONLY | O_NONBLOCK; 245 if (!child_process_inherit) 246 flags |= O_CLOEXEC; 247 248 Error error; 249 int fd = ::open(name.data(), flags); 250 if (fd != -1) 251 m_fds[READ] = fd; 252 else 253 error.SetErrorToErrno(); 254 255 return error; 256 } 257 258 Error 259 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) 260 { 261 if (CanRead() || CanWrite()) 262 return Error("Pipe is already opened"); 263 264 int flags = O_WRONLY | O_NONBLOCK; 265 if (!child_process_inherit) 266 flags |= O_CLOEXEC; 267 268 using namespace std::chrono; 269 const auto finish_time = Now() + timeout; 270 271 while (!CanWrite()) 272 { 273 if (timeout != microseconds::zero()) 274 { 275 const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); 276 if (dur <= 0) 277 return Error("timeout exceeded - reader hasn't opened so far"); 278 } 279 280 errno = 0; 281 int fd = ::open(name.data(), flags); 282 if (fd == -1) 283 { 284 const auto errno_copy = errno; 285 // We may get ENXIO if a reader side of the pipe hasn't opened yet. 286 if (errno_copy != ENXIO) 287 return Error(errno_copy, eErrorTypePOSIX); 288 289 std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); 290 } 291 else 292 { 293 m_fds[WRITE] = fd; 294 } 295 } 296 297 return Error(); 298 } 299 300 int 301 PipePosix::GetReadFileDescriptor() const 302 { 303 return m_fds[READ]; 304 } 305 306 int 307 PipePosix::GetWriteFileDescriptor() const 308 { 309 return m_fds[WRITE]; 310 } 311 312 int 313 PipePosix::ReleaseReadFileDescriptor() 314 { 315 const int fd = m_fds[READ]; 316 m_fds[READ] = PipePosix::kInvalidDescriptor; 317 return fd; 318 } 319 320 int 321 PipePosix::ReleaseWriteFileDescriptor() 322 { 323 const int fd = m_fds[WRITE]; 324 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 325 return fd; 326 } 327 328 void 329 PipePosix::Close() 330 { 331 CloseReadFileDescriptor(); 332 CloseWriteFileDescriptor(); 333 } 334 335 Error 336 PipePosix::Delete(llvm::StringRef name) 337 { 338 return FileSystem::Unlink(name.data()); 339 } 340 341 bool 342 PipePosix::CanRead() const 343 { 344 return m_fds[READ] != PipePosix::kInvalidDescriptor; 345 } 346 347 bool 348 PipePosix::CanWrite() const 349 { 350 return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 351 } 352 353 void 354 PipePosix::CloseReadFileDescriptor() 355 { 356 if (CanRead()) 357 { 358 close(m_fds[READ]); 359 m_fds[READ] = PipePosix::kInvalidDescriptor; 360 } 361 } 362 363 void 364 PipePosix::CloseWriteFileDescriptor() 365 { 366 if (CanWrite()) 367 { 368 close(m_fds[WRITE]); 369 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 370 } 371 } 372 373 Error 374 PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) 375 { 376 bytes_read = 0; 377 if (!CanRead()) 378 return Error(EINVAL, eErrorTypePOSIX); 379 380 auto handle = GetReadFileDescriptor(); 381 return SelectIO(handle, 382 true, 383 [=, &bytes_read](bool &done) 384 { 385 Error error; 386 auto result = ::read(handle, 387 reinterpret_cast<char*>(buf) + bytes_read, 388 size - bytes_read); 389 if (result != -1) 390 { 391 bytes_read += result; 392 if (bytes_read == size || result == 0) 393 done = true; 394 } 395 else 396 error.SetErrorToErrno(); 397 398 return error; 399 }, 400 timeout); 401 } 402 403 Error 404 PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) 405 { 406 bytes_written = 0; 407 if (!CanWrite()) 408 return Error(EINVAL, eErrorTypePOSIX); 409 410 auto handle = GetWriteFileDescriptor(); 411 return SelectIO(handle, 412 false, 413 [=, &bytes_written](bool &done) 414 { 415 Error error; 416 auto result = ::write(handle, 417 reinterpret_cast<const char*>(buf) + bytes_written, 418 size - bytes_written); 419 if (result != -1) 420 { 421 bytes_written += result; 422 if (bytes_written == size) 423 done = true; 424 } 425 else 426 error.SetErrorToErrno(); 427 428 return error; 429 }, 430 std::chrono::microseconds::zero()); 431 } 432