Path: blob/main/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp
39608 views
//===-- PipePosix.cpp -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/posix/PipePosix.h"9#include "lldb/Host/FileSystem.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 <functional>15#include <thread>1617#include <cerrno>18#include <climits>19#include <fcntl.h>20#include <sys/stat.h>21#include <sys/types.h>22#include <unistd.h>2324using namespace lldb;25using namespace lldb_private;2627int PipePosix::kInvalidDescriptor = -1;2829enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE3031// pipe2 is supported by a limited set of platforms32// TODO: Add more platforms that support pipe2.33#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \34defined(__OpenBSD__)35#define PIPE2_SUPPORTED 136#else37#define PIPE2_SUPPORTED 038#endif3940static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;4142#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED43static bool SetCloexecFlag(int fd) {44int flags = ::fcntl(fd, F_GETFD);45if (flags == -1)46return false;47return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);48}49#endif5051static std::chrono::time_point<std::chrono::steady_clock> Now() {52return std::chrono::steady_clock::now();53}5455PipePosix::PipePosix()56: m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}5758PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)59: m_fds{read, write} {}6061PipePosix::PipePosix(PipePosix &&pipe_posix)62: PipeBase{std::move(pipe_posix)},63m_fds{pipe_posix.ReleaseReadFileDescriptor(),64pipe_posix.ReleaseWriteFileDescriptor()} {}6566PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {67std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(68m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,69pipe_posix.m_write_mutex);7071PipeBase::operator=(std::move(pipe_posix));72m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();73m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();74return *this;75}7677PipePosix::~PipePosix() { Close(); }7879Status PipePosix::CreateNew(bool child_processes_inherit) {80std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);81if (CanReadUnlocked() || CanWriteUnlocked())82return Status(EINVAL, eErrorTypePOSIX);8384Status error;85#if PIPE2_SUPPORTED86if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)87return error;88#else89if (::pipe(m_fds) == 0) {90#ifdef FD_CLOEXEC91if (!child_processes_inherit) {92if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {93error.SetErrorToErrno();94CloseUnlocked();95return error;96}97}98#endif99return error;100}101#endif102103error.SetErrorToErrno();104m_fds[READ] = PipePosix::kInvalidDescriptor;105m_fds[WRITE] = PipePosix::kInvalidDescriptor;106return error;107}108109Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {110std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);111if (CanReadUnlocked() || CanWriteUnlocked())112return Status("Pipe is already opened");113114Status error;115if (::mkfifo(name.str().c_str(), 0660) != 0)116error.SetErrorToErrno();117118return error;119}120121Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,122bool child_process_inherit,123llvm::SmallVectorImpl<char> &name) {124llvm::SmallString<128> named_pipe_path;125llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());126FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();127if (!tmpdir_file_spec)128tmpdir_file_spec.AppendPathComponent("/tmp");129tmpdir_file_spec.AppendPathComponent(pipe_spec);130131// It's possible that another process creates the target path after we've132// verified it's available but before we create it, in which case we should133// try again.134Status error;135do {136llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,137/*MakeAbsolute=*/false);138error = CreateNew(named_pipe_path, child_process_inherit);139} while (error.GetError() == EEXIST);140141if (error.Success())142name = named_pipe_path;143return error;144}145146Status PipePosix::OpenAsReader(llvm::StringRef name,147bool child_process_inherit) {148std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);149150if (CanReadUnlocked() || CanWriteUnlocked())151return Status("Pipe is already opened");152153int flags = O_RDONLY | O_NONBLOCK;154if (!child_process_inherit)155flags |= O_CLOEXEC;156157Status error;158int fd = FileSystem::Instance().Open(name.str().c_str(), flags);159if (fd != -1)160m_fds[READ] = fd;161else162error.SetErrorToErrno();163164return error;165}166167Status168PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,169bool child_process_inherit,170const std::chrono::microseconds &timeout) {171std::lock_guard<std::mutex> guard(m_write_mutex);172if (CanReadUnlocked() || CanWriteUnlocked())173return Status("Pipe is already opened");174175int flags = O_WRONLY | O_NONBLOCK;176if (!child_process_inherit)177flags |= O_CLOEXEC;178179using namespace std::chrono;180const auto finish_time = Now() + timeout;181182while (!CanWriteUnlocked()) {183if (timeout != microseconds::zero()) {184const auto dur = duration_cast<microseconds>(finish_time - Now()).count();185if (dur <= 0)186return Status("timeout exceeded - reader hasn't opened so far");187}188189errno = 0;190int fd = ::open(name.str().c_str(), flags);191if (fd == -1) {192const auto errno_copy = errno;193// We may get ENXIO if a reader side of the pipe hasn't opened yet.194if (errno_copy != ENXIO && errno_copy != EINTR)195return Status(errno_copy, eErrorTypePOSIX);196197std::this_thread::sleep_for(198milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));199} else {200m_fds[WRITE] = fd;201}202}203204return Status();205}206207int PipePosix::GetReadFileDescriptor() const {208std::lock_guard<std::mutex> guard(m_read_mutex);209return GetReadFileDescriptorUnlocked();210}211212int PipePosix::GetReadFileDescriptorUnlocked() const {213return m_fds[READ];214}215216int PipePosix::GetWriteFileDescriptor() const {217std::lock_guard<std::mutex> guard(m_write_mutex);218return GetWriteFileDescriptorUnlocked();219}220221int PipePosix::GetWriteFileDescriptorUnlocked() const {222return m_fds[WRITE];223}224225int PipePosix::ReleaseReadFileDescriptor() {226std::lock_guard<std::mutex> guard(m_read_mutex);227return ReleaseReadFileDescriptorUnlocked();228}229230int PipePosix::ReleaseReadFileDescriptorUnlocked() {231const int fd = m_fds[READ];232m_fds[READ] = PipePosix::kInvalidDescriptor;233return fd;234}235236int PipePosix::ReleaseWriteFileDescriptor() {237std::lock_guard<std::mutex> guard(m_write_mutex);238return ReleaseWriteFileDescriptorUnlocked();239}240241int PipePosix::ReleaseWriteFileDescriptorUnlocked() {242const int fd = m_fds[WRITE];243m_fds[WRITE] = PipePosix::kInvalidDescriptor;244return fd;245}246247void PipePosix::Close() {248std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);249CloseUnlocked();250}251252void PipePosix::CloseUnlocked() {253CloseReadFileDescriptorUnlocked();254CloseWriteFileDescriptorUnlocked();255}256257Status PipePosix::Delete(llvm::StringRef name) {258return llvm::sys::fs::remove(name);259}260261bool PipePosix::CanRead() const {262std::lock_guard<std::mutex> guard(m_read_mutex);263return CanReadUnlocked();264}265266bool PipePosix::CanReadUnlocked() const {267return m_fds[READ] != PipePosix::kInvalidDescriptor;268}269270bool PipePosix::CanWrite() const {271std::lock_guard<std::mutex> guard(m_write_mutex);272return CanWriteUnlocked();273}274275bool PipePosix::CanWriteUnlocked() const {276return m_fds[WRITE] != PipePosix::kInvalidDescriptor;277}278279void PipePosix::CloseReadFileDescriptor() {280std::lock_guard<std::mutex> guard(m_read_mutex);281CloseReadFileDescriptorUnlocked();282}283void PipePosix::CloseReadFileDescriptorUnlocked() {284if (CanReadUnlocked()) {285close(m_fds[READ]);286m_fds[READ] = PipePosix::kInvalidDescriptor;287}288}289290void PipePosix::CloseWriteFileDescriptor() {291std::lock_guard<std::mutex> guard(m_write_mutex);292CloseWriteFileDescriptorUnlocked();293}294295void PipePosix::CloseWriteFileDescriptorUnlocked() {296if (CanWriteUnlocked()) {297close(m_fds[WRITE]);298m_fds[WRITE] = PipePosix::kInvalidDescriptor;299}300}301302Status PipePosix::ReadWithTimeout(void *buf, size_t size,303const std::chrono::microseconds &timeout,304size_t &bytes_read) {305std::lock_guard<std::mutex> guard(m_read_mutex);306bytes_read = 0;307if (!CanReadUnlocked())308return Status(EINVAL, eErrorTypePOSIX);309310const int fd = GetReadFileDescriptorUnlocked();311312SelectHelper select_helper;313select_helper.SetTimeout(timeout);314select_helper.FDSetRead(fd);315316Status error;317while (error.Success()) {318error = select_helper.Select();319if (error.Success()) {320auto result =321::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);322if (result != -1) {323bytes_read += result;324if (bytes_read == size || result == 0)325break;326} else if (errno == EINTR) {327continue;328} else {329error.SetErrorToErrno();330break;331}332}333}334return error;335}336337Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {338std::lock_guard<std::mutex> guard(m_write_mutex);339bytes_written = 0;340if (!CanWriteUnlocked())341return Status(EINVAL, eErrorTypePOSIX);342343const int fd = GetWriteFileDescriptorUnlocked();344SelectHelper select_helper;345select_helper.SetTimeout(std::chrono::seconds(0));346select_helper.FDSetWrite(fd);347348Status error;349while (error.Success()) {350error = select_helper.Select();351if (error.Success()) {352auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,353size - bytes_written);354if (result != -1) {355bytes_written += result;356if (bytes_written == size)357break;358} else if (errno == EINTR) {359continue;360} else {361error.SetErrorToErrno();362}363}364}365return error;366}367368369