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