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