xref: /freebsd-src/contrib/llvm-project/lldb/source/Host/posix/PipePosix.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- PipePosix.cpp -----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Host/posix/PipePosix.h"
1006c3fb27SDimitry Andric #include "lldb/Host/FileSystem.h"
110b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h"
120b57cec5SDimitry Andric #include "lldb/Utility/SelectHelper.h"
130b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
140b57cec5SDimitry Andric #include "llvm/Support/Errno.h"
150b57cec5SDimitry Andric #include <functional>
160b57cec5SDimitry Andric #include <thread>
170b57cec5SDimitry Andric 
18fe6060f1SDimitry Andric #include <cerrno>
19fe6060f1SDimitry Andric #include <climits>
200b57cec5SDimitry Andric #include <fcntl.h>
210b57cec5SDimitry Andric #include <sys/stat.h>
220b57cec5SDimitry Andric #include <sys/types.h>
230b57cec5SDimitry Andric #include <unistd.h>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace lldb;
260b57cec5SDimitry Andric using namespace lldb_private;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric int PipePosix::kInvalidDescriptor = -1;
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric // pipe2 is supported by a limited set of platforms
330b57cec5SDimitry Andric // TODO: Add more platforms that support pipe2.
345f757f3fSDimitry Andric #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
355f757f3fSDimitry Andric     defined(__OpenBSD__)
360b57cec5SDimitry Andric #define PIPE2_SUPPORTED 1
370b57cec5SDimitry Andric #else
380b57cec5SDimitry Andric #define PIPE2_SUPPORTED 0
390b57cec5SDimitry Andric #endif
400b57cec5SDimitry Andric 
41349cc55cSDimitry Andric static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric #if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44349cc55cSDimitry Andric static bool SetCloexecFlag(int fd) {
450b57cec5SDimitry Andric   int flags = ::fcntl(fd, F_GETFD);
460b57cec5SDimitry Andric   if (flags == -1)
470b57cec5SDimitry Andric     return false;
480b57cec5SDimitry Andric   return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
490b57cec5SDimitry Andric }
500b57cec5SDimitry Andric #endif
510b57cec5SDimitry Andric 
52349cc55cSDimitry Andric static std::chrono::time_point<std::chrono::steady_clock> Now() {
530b57cec5SDimitry Andric   return std::chrono::steady_clock::now();
540b57cec5SDimitry Andric }
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric PipePosix::PipePosix()
570b57cec5SDimitry Andric     : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
600b57cec5SDimitry Andric     : m_fds{read, write} {}
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric PipePosix::PipePosix(PipePosix &&pipe_posix)
630b57cec5SDimitry Andric     : PipeBase{std::move(pipe_posix)},
640b57cec5SDimitry Andric       m_fds{pipe_posix.ReleaseReadFileDescriptor(),
650b57cec5SDimitry Andric             pipe_posix.ReleaseWriteFileDescriptor()} {}
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
685f757f3fSDimitry Andric   std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(
695f757f3fSDimitry Andric       m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,
705f757f3fSDimitry Andric       pipe_posix.m_write_mutex);
715f757f3fSDimitry Andric 
720b57cec5SDimitry Andric   PipeBase::operator=(std::move(pipe_posix));
735f757f3fSDimitry Andric   m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();
745f757f3fSDimitry Andric   m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();
750b57cec5SDimitry Andric   return *this;
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric PipePosix::~PipePosix() { Close(); }
790b57cec5SDimitry Andric 
800b57cec5SDimitry Andric Status PipePosix::CreateNew(bool child_processes_inherit) {
815f757f3fSDimitry Andric   std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
825f757f3fSDimitry Andric   if (CanReadUnlocked() || CanWriteUnlocked())
830b57cec5SDimitry Andric     return Status(EINVAL, eErrorTypePOSIX);
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   Status error;
860b57cec5SDimitry Andric #if PIPE2_SUPPORTED
870b57cec5SDimitry Andric   if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
880b57cec5SDimitry Andric     return error;
890b57cec5SDimitry Andric #else
900b57cec5SDimitry Andric   if (::pipe(m_fds) == 0) {
910b57cec5SDimitry Andric #ifdef FD_CLOEXEC
920b57cec5SDimitry Andric     if (!child_processes_inherit) {
930b57cec5SDimitry Andric       if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
940b57cec5SDimitry Andric         error.SetErrorToErrno();
955f757f3fSDimitry Andric         CloseUnlocked();
960b57cec5SDimitry Andric         return error;
970b57cec5SDimitry Andric       }
980b57cec5SDimitry Andric     }
990b57cec5SDimitry Andric #endif
1000b57cec5SDimitry Andric     return error;
1010b57cec5SDimitry Andric   }
1020b57cec5SDimitry Andric #endif
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   error.SetErrorToErrno();
1050b57cec5SDimitry Andric   m_fds[READ] = PipePosix::kInvalidDescriptor;
1060b57cec5SDimitry Andric   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
1070b57cec5SDimitry Andric   return error;
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
111*0fca6ea1SDimitry Andric   std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
1125f757f3fSDimitry Andric   if (CanReadUnlocked() || CanWriteUnlocked())
1130b57cec5SDimitry Andric     return Status("Pipe is already opened");
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   Status error;
116e8d8bef9SDimitry Andric   if (::mkfifo(name.str().c_str(), 0660) != 0)
1170b57cec5SDimitry Andric     error.SetErrorToErrno();
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   return error;
1200b57cec5SDimitry Andric }
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
1230b57cec5SDimitry Andric                                        bool child_process_inherit,
1240b57cec5SDimitry Andric                                        llvm::SmallVectorImpl<char> &name) {
1250b57cec5SDimitry Andric   llvm::SmallString<128> named_pipe_path;
1260b57cec5SDimitry Andric   llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
1270b57cec5SDimitry Andric   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
1280b57cec5SDimitry Andric   if (!tmpdir_file_spec)
1290b57cec5SDimitry Andric     tmpdir_file_spec.AppendPathComponent("/tmp");
1300b57cec5SDimitry Andric   tmpdir_file_spec.AppendPathComponent(pipe_spec);
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   // It's possible that another process creates the target path after we've
1330b57cec5SDimitry Andric   // verified it's available but before we create it, in which case we should
1340b57cec5SDimitry Andric   // try again.
1350b57cec5SDimitry Andric   Status error;
1360b57cec5SDimitry Andric   do {
137e8d8bef9SDimitry Andric     llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
138e8d8bef9SDimitry Andric                                     /*MakeAbsolute=*/false);
1390b57cec5SDimitry Andric     error = CreateNew(named_pipe_path, child_process_inherit);
1400b57cec5SDimitry Andric   } while (error.GetError() == EEXIST);
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   if (error.Success())
1430b57cec5SDimitry Andric     name = named_pipe_path;
1440b57cec5SDimitry Andric   return error;
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric Status PipePosix::OpenAsReader(llvm::StringRef name,
1480b57cec5SDimitry Andric                                bool child_process_inherit) {
149*0fca6ea1SDimitry Andric   std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
1505f757f3fSDimitry Andric 
1515f757f3fSDimitry Andric   if (CanReadUnlocked() || CanWriteUnlocked())
1520b57cec5SDimitry Andric     return Status("Pipe is already opened");
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   int flags = O_RDONLY | O_NONBLOCK;
1550b57cec5SDimitry Andric   if (!child_process_inherit)
1560b57cec5SDimitry Andric     flags |= O_CLOEXEC;
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   Status error;
15906c3fb27SDimitry Andric   int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
1600b57cec5SDimitry Andric   if (fd != -1)
1610b57cec5SDimitry Andric     m_fds[READ] = fd;
1620b57cec5SDimitry Andric   else
1630b57cec5SDimitry Andric     error.SetErrorToErrno();
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   return error;
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric Status
1690b57cec5SDimitry Andric PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
1700b57cec5SDimitry Andric                                    bool child_process_inherit,
1710b57cec5SDimitry Andric                                    const std::chrono::microseconds &timeout) {
1725f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
1735f757f3fSDimitry Andric   if (CanReadUnlocked() || CanWriteUnlocked())
1740b57cec5SDimitry Andric     return Status("Pipe is already opened");
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric   int flags = O_WRONLY | O_NONBLOCK;
1770b57cec5SDimitry Andric   if (!child_process_inherit)
1780b57cec5SDimitry Andric     flags |= O_CLOEXEC;
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric   using namespace std::chrono;
1810b57cec5SDimitry Andric   const auto finish_time = Now() + timeout;
1820b57cec5SDimitry Andric 
1835f757f3fSDimitry Andric   while (!CanWriteUnlocked()) {
1840b57cec5SDimitry Andric     if (timeout != microseconds::zero()) {
1850b57cec5SDimitry Andric       const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
1860b57cec5SDimitry Andric       if (dur <= 0)
1870b57cec5SDimitry Andric         return Status("timeout exceeded - reader hasn't opened so far");
1880b57cec5SDimitry Andric     }
1890b57cec5SDimitry Andric 
1900b57cec5SDimitry Andric     errno = 0;
191e8d8bef9SDimitry Andric     int fd = ::open(name.str().c_str(), flags);
1920b57cec5SDimitry Andric     if (fd == -1) {
1930b57cec5SDimitry Andric       const auto errno_copy = errno;
1940b57cec5SDimitry Andric       // We may get ENXIO if a reader side of the pipe hasn't opened yet.
1950b57cec5SDimitry Andric       if (errno_copy != ENXIO && errno_copy != EINTR)
1960b57cec5SDimitry Andric         return Status(errno_copy, eErrorTypePOSIX);
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric       std::this_thread::sleep_for(
1990b57cec5SDimitry Andric           milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
2000b57cec5SDimitry Andric     } else {
2010b57cec5SDimitry Andric       m_fds[WRITE] = fd;
2020b57cec5SDimitry Andric     }
2030b57cec5SDimitry Andric   }
2040b57cec5SDimitry Andric 
2050b57cec5SDimitry Andric   return Status();
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric 
2085f757f3fSDimitry Andric int PipePosix::GetReadFileDescriptor() const {
2095f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_read_mutex);
2105f757f3fSDimitry Andric   return GetReadFileDescriptorUnlocked();
2115f757f3fSDimitry Andric }
2120b57cec5SDimitry Andric 
2135f757f3fSDimitry Andric int PipePosix::GetReadFileDescriptorUnlocked() const {
2145f757f3fSDimitry Andric   return m_fds[READ];
2155f757f3fSDimitry Andric }
2165f757f3fSDimitry Andric 
2175f757f3fSDimitry Andric int PipePosix::GetWriteFileDescriptor() const {
2185f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
2195f757f3fSDimitry Andric   return GetWriteFileDescriptorUnlocked();
2205f757f3fSDimitry Andric }
2215f757f3fSDimitry Andric 
2225f757f3fSDimitry Andric int PipePosix::GetWriteFileDescriptorUnlocked() const {
2235f757f3fSDimitry Andric   return m_fds[WRITE];
2245f757f3fSDimitry Andric }
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric int PipePosix::ReleaseReadFileDescriptor() {
2275f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_read_mutex);
2285f757f3fSDimitry Andric   return ReleaseReadFileDescriptorUnlocked();
2295f757f3fSDimitry Andric }
2305f757f3fSDimitry Andric 
2315f757f3fSDimitry Andric int PipePosix::ReleaseReadFileDescriptorUnlocked() {
2320b57cec5SDimitry Andric   const int fd = m_fds[READ];
2330b57cec5SDimitry Andric   m_fds[READ] = PipePosix::kInvalidDescriptor;
2340b57cec5SDimitry Andric   return fd;
2350b57cec5SDimitry Andric }
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric int PipePosix::ReleaseWriteFileDescriptor() {
2385f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
2395f757f3fSDimitry Andric   return ReleaseWriteFileDescriptorUnlocked();
2405f757f3fSDimitry Andric }
2415f757f3fSDimitry Andric 
2425f757f3fSDimitry Andric int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
2430b57cec5SDimitry Andric   const int fd = m_fds[WRITE];
2440b57cec5SDimitry Andric   m_fds[WRITE] = PipePosix::kInvalidDescriptor;
2450b57cec5SDimitry Andric   return fd;
2460b57cec5SDimitry Andric }
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric void PipePosix::Close() {
2495f757f3fSDimitry Andric   std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
2505f757f3fSDimitry Andric   CloseUnlocked();
2515f757f3fSDimitry Andric }
2525f757f3fSDimitry Andric 
2535f757f3fSDimitry Andric void PipePosix::CloseUnlocked() {
2545f757f3fSDimitry Andric   CloseReadFileDescriptorUnlocked();
2555f757f3fSDimitry Andric   CloseWriteFileDescriptorUnlocked();
2560b57cec5SDimitry Andric }
2570b57cec5SDimitry Andric 
2580b57cec5SDimitry Andric Status PipePosix::Delete(llvm::StringRef name) {
2590b57cec5SDimitry Andric   return llvm::sys::fs::remove(name);
2600b57cec5SDimitry Andric }
2610b57cec5SDimitry Andric 
2620b57cec5SDimitry Andric bool PipePosix::CanRead() const {
2635f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_read_mutex);
2645f757f3fSDimitry Andric   return CanReadUnlocked();
2655f757f3fSDimitry Andric }
2665f757f3fSDimitry Andric 
2675f757f3fSDimitry Andric bool PipePosix::CanReadUnlocked() const {
2680b57cec5SDimitry Andric   return m_fds[READ] != PipePosix::kInvalidDescriptor;
2690b57cec5SDimitry Andric }
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric bool PipePosix::CanWrite() const {
2725f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
2735f757f3fSDimitry Andric   return CanWriteUnlocked();
2745f757f3fSDimitry Andric }
2755f757f3fSDimitry Andric 
2765f757f3fSDimitry Andric bool PipePosix::CanWriteUnlocked() const {
2770b57cec5SDimitry Andric   return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
2780b57cec5SDimitry Andric }
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric void PipePosix::CloseReadFileDescriptor() {
2815f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_read_mutex);
2825f757f3fSDimitry Andric   CloseReadFileDescriptorUnlocked();
2835f757f3fSDimitry Andric }
2845f757f3fSDimitry Andric void PipePosix::CloseReadFileDescriptorUnlocked() {
2855f757f3fSDimitry Andric   if (CanReadUnlocked()) {
2860b57cec5SDimitry Andric     close(m_fds[READ]);
2870b57cec5SDimitry Andric     m_fds[READ] = PipePosix::kInvalidDescriptor;
2880b57cec5SDimitry Andric   }
2890b57cec5SDimitry Andric }
2900b57cec5SDimitry Andric 
2910b57cec5SDimitry Andric void PipePosix::CloseWriteFileDescriptor() {
2925f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
2935f757f3fSDimitry Andric   CloseWriteFileDescriptorUnlocked();
2945f757f3fSDimitry Andric }
2955f757f3fSDimitry Andric 
2965f757f3fSDimitry Andric void PipePosix::CloseWriteFileDescriptorUnlocked() {
2975f757f3fSDimitry Andric   if (CanWriteUnlocked()) {
2980b57cec5SDimitry Andric     close(m_fds[WRITE]);
2990b57cec5SDimitry Andric     m_fds[WRITE] = PipePosix::kInvalidDescriptor;
3000b57cec5SDimitry Andric   }
3010b57cec5SDimitry Andric }
3020b57cec5SDimitry Andric 
3030b57cec5SDimitry Andric Status PipePosix::ReadWithTimeout(void *buf, size_t size,
3040b57cec5SDimitry Andric                                   const std::chrono::microseconds &timeout,
3050b57cec5SDimitry Andric                                   size_t &bytes_read) {
3065f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_read_mutex);
3070b57cec5SDimitry Andric   bytes_read = 0;
3085f757f3fSDimitry Andric   if (!CanReadUnlocked())
3090b57cec5SDimitry Andric     return Status(EINVAL, eErrorTypePOSIX);
3100b57cec5SDimitry Andric 
3115f757f3fSDimitry Andric   const int fd = GetReadFileDescriptorUnlocked();
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric   SelectHelper select_helper;
3140b57cec5SDimitry Andric   select_helper.SetTimeout(timeout);
3150b57cec5SDimitry Andric   select_helper.FDSetRead(fd);
3160b57cec5SDimitry Andric 
3170b57cec5SDimitry Andric   Status error;
3180b57cec5SDimitry Andric   while (error.Success()) {
3190b57cec5SDimitry Andric     error = select_helper.Select();
3200b57cec5SDimitry Andric     if (error.Success()) {
321480093f4SDimitry Andric       auto result =
322480093f4SDimitry Andric           ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
3230b57cec5SDimitry Andric       if (result != -1) {
3240b57cec5SDimitry Andric         bytes_read += result;
3250b57cec5SDimitry Andric         if (bytes_read == size || result == 0)
3260b57cec5SDimitry Andric           break;
3270b57cec5SDimitry Andric       } else if (errno == EINTR) {
3280b57cec5SDimitry Andric         continue;
3290b57cec5SDimitry Andric       } else {
3300b57cec5SDimitry Andric         error.SetErrorToErrno();
3310b57cec5SDimitry Andric         break;
3320b57cec5SDimitry Andric       }
3330b57cec5SDimitry Andric     }
3340b57cec5SDimitry Andric   }
3350b57cec5SDimitry Andric   return error;
3360b57cec5SDimitry Andric }
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
3395f757f3fSDimitry Andric   std::lock_guard<std::mutex> guard(m_write_mutex);
3400b57cec5SDimitry Andric   bytes_written = 0;
3415f757f3fSDimitry Andric   if (!CanWriteUnlocked())
3420b57cec5SDimitry Andric     return Status(EINVAL, eErrorTypePOSIX);
3430b57cec5SDimitry Andric 
3445f757f3fSDimitry Andric   const int fd = GetWriteFileDescriptorUnlocked();
3450b57cec5SDimitry Andric   SelectHelper select_helper;
3460b57cec5SDimitry Andric   select_helper.SetTimeout(std::chrono::seconds(0));
3470b57cec5SDimitry Andric   select_helper.FDSetWrite(fd);
3480b57cec5SDimitry Andric 
3490b57cec5SDimitry Andric   Status error;
3500b57cec5SDimitry Andric   while (error.Success()) {
3510b57cec5SDimitry Andric     error = select_helper.Select();
3520b57cec5SDimitry Andric     if (error.Success()) {
353480093f4SDimitry Andric       auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
3540b57cec5SDimitry Andric                             size - bytes_written);
3550b57cec5SDimitry Andric       if (result != -1) {
3560b57cec5SDimitry Andric         bytes_written += result;
3570b57cec5SDimitry Andric         if (bytes_written == size)
3580b57cec5SDimitry Andric           break;
3590b57cec5SDimitry Andric       } else if (errno == EINTR) {
3600b57cec5SDimitry Andric         continue;
3610b57cec5SDimitry Andric       } else {
3620b57cec5SDimitry Andric         error.SetErrorToErrno();
3630b57cec5SDimitry Andric       }
3640b57cec5SDimitry Andric     }
3650b57cec5SDimitry Andric   }
3660b57cec5SDimitry Andric   return error;
3670b57cec5SDimitry Andric }
368