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 "lldb/Utility/SelectHelper.h" 14 #include "llvm/ADT/SmallString.h" 15 #include "llvm/Support/FileSystem.h" 16 17 #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) 18 #ifndef _GLIBCXX_USE_NANOSLEEP 19 #define _GLIBCXX_USE_NANOSLEEP 20 #endif 21 #endif 22 23 #include <functional> 24 #include <thread> 25 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <limits.h> 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 #include <unistd.h> 32 33 using namespace lldb; 34 using namespace lldb_private; 35 36 int PipePosix::kInvalidDescriptor = -1; 37 38 enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE 39 40 // pipe2 is supported by a limited set of platforms 41 // TODO: Add more platforms that support pipe2. 42 #if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \ 43 defined(__NetBSD__) 44 #define PIPE2_SUPPORTED 1 45 #else 46 #define PIPE2_SUPPORTED 0 47 #endif 48 49 namespace { 50 51 constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; 52 53 #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED 54 bool SetCloexecFlag(int fd) { 55 int flags = ::fcntl(fd, F_GETFD); 56 if (flags == -1) 57 return false; 58 return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); 59 } 60 #endif 61 62 std::chrono::time_point<std::chrono::steady_clock> Now() { 63 return std::chrono::steady_clock::now(); 64 } 65 } 66 67 PipePosix::PipePosix() 68 : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {} 69 70 PipePosix::PipePosix(int read_fd, int write_fd) : m_fds{read_fd, write_fd} {} 71 72 PipePosix::PipePosix(PipePosix &&pipe_posix) 73 : PipeBase{std::move(pipe_posix)}, 74 m_fds{pipe_posix.ReleaseReadFileDescriptor(), 75 pipe_posix.ReleaseWriteFileDescriptor()} {} 76 77 PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) { 78 PipeBase::operator=(std::move(pipe_posix)); 79 m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor(); 80 m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor(); 81 return *this; 82 } 83 84 PipePosix::~PipePosix() { Close(); } 85 86 Error PipePosix::CreateNew(bool child_processes_inherit) { 87 if (CanRead() || CanWrite()) 88 return Error(EINVAL, eErrorTypePOSIX); 89 90 Error error; 91 #if PIPE2_SUPPORTED 92 if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) 93 return error; 94 #else 95 if (::pipe(m_fds) == 0) { 96 #ifdef FD_CLOEXEC 97 if (!child_processes_inherit) { 98 if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) { 99 error.SetErrorToErrno(); 100 Close(); 101 return error; 102 } 103 } 104 #endif 105 return error; 106 } 107 #endif 108 109 error.SetErrorToErrno(); 110 m_fds[READ] = PipePosix::kInvalidDescriptor; 111 m_fds[WRITE] = PipePosix::kInvalidDescriptor; 112 return error; 113 } 114 115 Error PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) { 116 if (CanRead() || CanWrite()) 117 return Error("Pipe is already opened"); 118 119 Error error; 120 if (::mkfifo(name.data(), 0660) != 0) 121 error.SetErrorToErrno(); 122 123 return error; 124 } 125 126 Error PipePosix::CreateWithUniqueName(llvm::StringRef prefix, 127 bool child_process_inherit, 128 llvm::SmallVectorImpl<char> &name) { 129 llvm::SmallString<PATH_MAX> named_pipe_path; 130 llvm::SmallString<PATH_MAX> pipe_spec((prefix + ".%%%%%%").str()); 131 FileSpec tmpdir_file_spec; 132 tmpdir_file_spec.Clear(); 133 if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { 134 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); 135 } else { 136 tmpdir_file_spec.AppendPathComponent("/tmp"); 137 tmpdir_file_spec.AppendPathComponent(pipe_spec.c_str()); 138 } 139 140 // It's possible that another process creates the target path after we've 141 // verified it's available but before we create it, in which case we 142 // should try again. 143 Error error; 144 do { 145 llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), 146 named_pipe_path); 147 error = CreateNew(named_pipe_path, child_process_inherit); 148 } while (error.GetError() == EEXIST); 149 150 if (error.Success()) 151 name = named_pipe_path; 152 return error; 153 } 154 155 Error PipePosix::OpenAsReader(llvm::StringRef name, 156 bool child_process_inherit) { 157 if (CanRead() || CanWrite()) 158 return Error("Pipe is already opened"); 159 160 int flags = O_RDONLY | O_NONBLOCK; 161 if (!child_process_inherit) 162 flags |= O_CLOEXEC; 163 164 Error error; 165 int fd = ::open(name.data(), flags); 166 if (fd != -1) 167 m_fds[READ] = fd; 168 else 169 error.SetErrorToErrno(); 170 171 return error; 172 } 173 174 Error PipePosix::OpenAsWriterWithTimeout( 175 llvm::StringRef name, bool child_process_inherit, 176 const std::chrono::microseconds &timeout) { 177 if (CanRead() || CanWrite()) 178 return Error("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 Error("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 Error(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 Error(); 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 Error PipePosix::Delete(llvm::StringRef name) { 234 return FileSystem::Unlink(FileSpec{name.data(), true}); 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 Error 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 Error(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 Error 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 Error PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) { 292 bytes_written = 0; 293 if (!CanWrite()) 294 return Error(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 Error 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