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