chromium/chrome/test/base/chromeos/crosier/helper/utils.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/base/chromeos/crosier/helper/utils.h"

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <string>
#include <string_view>

#include "base/check.h"
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/posix/eintr_wrapper.h"

namespace crosier {

namespace {

void SendBuffer(const base::ScopedFD& sock, const char* buf, int byte_size) {
  const char* p = buf;
  int remaining = byte_size;

  while (remaining > 0) {
    ssize_t bytes_sent = HANDLE_EINTR(send(sock.get(), p, remaining, 0));
    CHECK_GT(bytes_sent, 0);

    p += bytes_sent;
    remaining -= bytes_sent;
  }
}

}  // namespace

base::ScopedFD CreateSocketAndBind(const base::FilePath& path) {
  int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, /*protocol=*/0);
  PCHECK(fd >= 0);
  base::ScopedFD socket(fd);

  // `unlink` just in case there was left over from previous runs.
  unlink(path.value().c_str());

  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, path.value().c_str(), sizeof(sockaddr_un::sun_path));

  PCHECK(bind(socket.get(), reinterpret_cast<struct sockaddr*>(&addr),
              sizeof(addr)) == 0);

  return socket;
}

std::string ReadString(const base::ScopedFD& sock) {
  constexpr int buf_size = 1024;
  char buf[buf_size];

  std::string result;
  bool done = false;
  while (!done) {
    ssize_t bytes_read = HANDLE_EINTR(recv(sock.get(), buf, buf_size, 0));
    CHECK_GE(bytes_read, 0);
    if (bytes_read == 0) {
      done = true;
      break;
    }

    if (buf[bytes_read - 1] == 0) {
      done = true;
      --bytes_read;  // No need to copy the null terminator.
    }
    result.append(buf, bytes_read);
  }
  return result;
}

void ReadBuffer(const base::ScopedFD& sock, void* buf, int byte_size) {
  char* p = reinterpret_cast<char*>(buf);
  int remaining = byte_size;

  while (remaining > 0) {
    ssize_t bytes_read = HANDLE_EINTR(recv(sock.get(), p, remaining, 0));
    CHECK_GE(bytes_read, 0);
    if (bytes_read == 0) {
      // The connection is lost before finishing read. Not supported.
      CHECK(false) << "Connection lost";
      break;
    }

    p += bytes_read;
    remaining -= bytes_read;
  }
}

void SendString(const base::ScopedFD& sock, const std::string_view str) {
  SendBuffer(sock, str.data(), str.size());

  char terminator = 0;
  SendBuffer(sock, &terminator, 1);
}

}  // namespace crosier