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<128> named_pipe_path; 129 llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str()); 130 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir(); 131 if (!tmpdir_file_spec) 132 tmpdir_file_spec.AppendPathComponent("/tmp"); 133 tmpdir_file_spec.AppendPathComponent(pipe_spec); 134 135 // It's possible that another process creates the target path after we've 136 // verified it's available but before we create it, in which case we should 137 // try again. 138 Status error; 139 do { 140 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), 141 named_pipe_path); 142 error = CreateNew(named_pipe_path, child_process_inherit); 143 } while (error.GetError() == EEXIST); 144 145 if (error.Success()) 146 name = named_pipe_path; 147 return error; 148 } 149 150 Status PipePosix::OpenAsReader(llvm::StringRef name, 151 bool child_process_inherit) { 152 if (CanRead() || CanWrite()) 153 return Status("Pipe is already opened"); 154 155 int flags = O_RDONLY | O_NONBLOCK; 156 if (!child_process_inherit) 157 flags |= O_CLOEXEC; 158 159 Status error; 160 int fd = ::open(name.data(), flags); 161 if (fd != -1) 162 m_fds[READ] = fd; 163 else 164 error.SetErrorToErrno(); 165 166 return error; 167 } 168 169 Status 170 PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, 171 bool child_process_inherit, 172 const std::chrono::microseconds &timeout) { 173 if (CanRead() || CanWrite()) 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 (!CanWrite()) { 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.data(), 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) 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 { return m_fds[READ]; } 209 210 int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; } 211 212 int PipePosix::ReleaseReadFileDescriptor() { 213 const int fd = m_fds[READ]; 214 m_fds[READ] = PipePosix::kInvalidDescriptor; 215 return fd; 216 } 217 218 int PipePosix::ReleaseWriteFileDescriptor() { 219 const int fd = m_fds[WRITE]; 220 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 221 return fd; 222 } 223 224 void PipePosix::Close() { 225 CloseReadFileDescriptor(); 226 CloseWriteFileDescriptor(); 227 } 228 229 Status PipePosix::Delete(llvm::StringRef name) { 230 return llvm::sys::fs::remove(name); 231 } 232 233 bool PipePosix::CanRead() const { 234 return m_fds[READ] != PipePosix::kInvalidDescriptor; 235 } 236 237 bool PipePosix::CanWrite() const { 238 return m_fds[WRITE] != PipePosix::kInvalidDescriptor; 239 } 240 241 void PipePosix::CloseReadFileDescriptor() { 242 if (CanRead()) { 243 close(m_fds[READ]); 244 m_fds[READ] = PipePosix::kInvalidDescriptor; 245 } 246 } 247 248 void PipePosix::CloseWriteFileDescriptor() { 249 if (CanWrite()) { 250 close(m_fds[WRITE]); 251 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 252 } 253 } 254 255 Status PipePosix::ReadWithTimeout(void *buf, size_t size, 256 const std::chrono::microseconds &timeout, 257 size_t &bytes_read) { 258 bytes_read = 0; 259 if (!CanRead()) 260 return Status(EINVAL, eErrorTypePOSIX); 261 262 const int fd = GetReadFileDescriptor(); 263 264 SelectHelper select_helper; 265 select_helper.SetTimeout(timeout); 266 select_helper.FDSetRead(fd); 267 268 Status error; 269 while (error.Success()) { 270 error = select_helper.Select(); 271 if (error.Success()) { 272 auto result = ::read(fd, reinterpret_cast<char *>(buf) + bytes_read, 273 size - bytes_read); 274 if (result != -1) { 275 bytes_read += result; 276 if (bytes_read == size || result == 0) 277 break; 278 } else { 279 error.SetErrorToErrno(); 280 break; 281 } 282 } 283 } 284 return error; 285 } 286 287 Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { 288 bytes_written = 0; 289 if (!CanWrite()) 290 return Status(EINVAL, eErrorTypePOSIX); 291 292 const int fd = GetWriteFileDescriptor(); 293 SelectHelper select_helper; 294 select_helper.SetTimeout(std::chrono::seconds(0)); 295 select_helper.FDSetWrite(fd); 296 297 Status error; 298 while (error.Success()) { 299 error = select_helper.Select(); 300 if (error.Success()) { 301 auto result = 302 ::write(fd, reinterpret_cast<const char *>(buf) + bytes_written, 303 size - bytes_written); 304 if (result != -1) { 305 bytes_written += result; 306 if (bytes_written == size) 307 break; 308 } else { 309 error.SetErrorToErrno(); 310 } 311 } 312 } 313 return error; 314 } 315