/ src / common / linux / scoped_pipe.cc
scoped_pipe.cc
  1  // Copyright 2022 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  #ifdef HAVE_CONFIG_H
 30  #include <config.h>  // Must come first
 31  #endif
 32  
 33  #include "common/linux/scoped_pipe.h"
 34  
 35  #include <unistd.h>
 36  
 37  #include "common/linux/eintr_wrapper.h"
 38  
 39  namespace google_breakpad {
 40  
 41  ScopedPipe::ScopedPipe() {
 42    fds_[0] = -1;
 43    fds_[1] = -1;
 44  }
 45  
 46  ScopedPipe::~ScopedPipe() {
 47    CloseReadFd();
 48    CloseWriteFd();
 49  }
 50  
 51  bool ScopedPipe::Init() {
 52    return pipe(fds_) == 0;
 53  }
 54  
 55  void ScopedPipe::CloseReadFd() {
 56    if (fds_[0] != -1) {
 57      close(fds_[0]);
 58      fds_[0] = -1;
 59    }
 60  }
 61  
 62  void ScopedPipe::CloseWriteFd() {
 63    if (fds_[1] != -1) {
 64      close(fds_[1]);
 65      fds_[1] = -1;
 66    }
 67  }
 68  
 69  bool ScopedPipe::ReadLine(std::string& line) {
 70    // Simple buffered file read. `read_buffer_` stores previously read bytes, and
 71    // we either return a line from this buffer, or we append blocks of read bytes
 72    // to the buffer until we have a complete line.
 73    size_t eol_index = read_buffer_.find('\n');
 74  
 75    // While we don't have a full line, and read pipe is valid.
 76    while (eol_index == std::string::npos && GetReadFd() != -1) {
 77      // Read a block of 128 bytes from the read pipe.
 78      char read_buf[128];
 79      ssize_t read_len = HANDLE_EINTR(
 80        read(GetReadFd(), read_buf, sizeof(read_buf)));
 81      if (read_len <= 0) {
 82        // Pipe error, or pipe has been closed.
 83        CloseReadFd();
 84        break;
 85      }
 86  
 87      // Append the block, and check if we have a full line now.
 88      read_buffer_.append(read_buf, read_len);
 89      eol_index = read_buffer_.find('\n');
 90    }
 91  
 92    if (eol_index != std::string::npos) {
 93      // We have a full line to output.
 94      line = read_buffer_.substr(0, eol_index);
 95      if (eol_index < read_buffer_.size()) {
 96        read_buffer_ = read_buffer_.substr(eol_index + 1);
 97      } else {
 98        read_buffer_ = "";
 99      }
100  
101      return true;
102    }
103  
104    if (read_buffer_.size()) {
105      // We don't have a full line to output, but we can only reach here if the
106      // pipe has closed and there are some bytes left at the end, so we should
107      // return those bytes.
108      line = std::move(read_buffer_);
109      read_buffer_ = "";
110  
111      return true;
112    }
113  
114    // We don't have any buffered data left, and the pipe has closed.
115    return false;
116  }
117  
118  int ScopedPipe::Dup2WriteFd(int new_fd) const {
119    return dup2(fds_[1], new_fd);
120  }
121  
122  bool ScopedPipe::WriteForTesting(const void* bytes, size_t bytes_len) {
123    ssize_t r = HANDLE_EINTR(write(GetWriteFd(), bytes, bytes_len));
124    if (r != static_cast<ssize_t>(bytes_len)) {
125      CloseWriteFd();
126      return false;
127    }
128  
129    return true;
130  }
131  
132  }  // namespace google_breakpad