blob: 8894d12f8689a10cd634025873911529fcf13a1d [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "arc/vm/vsock_proxy/file_descriptor_util.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// Needs to be included after sys/socket.h
#include <linux/un.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
namespace arc {
namespace {
bool ToSockAddr(const base::FilePath& path, struct sockaddr_un* sa) {
// sun_path needs to include trailing '\0' byte.
if (path.value().size() >= sizeof(sa->sun_path)) {
LOG(ERROR) << "Path is too long: " << path.value();
return false;
}
memset(sa, 0, sizeof(*sa));
sa->sun_family = AF_UNIX;
strncpy(sa->sun_path, path.value().c_str(), sizeof(sa->sun_path) - 1);
return true;
}
} // namespace
base::Optional<std::pair<base::ScopedFD, base::ScopedFD>> CreatePipe() {
int fds[2];
if (pipe2(fds, O_CLOEXEC) == -1) {
PLOG(ERROR) << "Failed to create pipe";
return base::nullopt;
}
return base::make_optional(
std::make_pair(base::ScopedFD(fds[0]), base::ScopedFD(fds[1])));
}
base::Optional<std::pair<base::ScopedFD, base::ScopedFD>> CreateSocketPair() {
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */, fds) ==
-1) {
PLOG(ERROR) << "Failed to create socketpair";
return base::nullopt;
}
return base::make_optional(
std::make_pair(base::ScopedFD(fds[0]), base::ScopedFD(fds[1])));
}
base::ScopedFD CreateUnixDomainSocket(const base::FilePath& path) {
LOG(INFO) << "Creating " << path.value();
struct sockaddr_un sa;
if (!ToSockAddr(path, &sa))
return {};
base::ScopedFD fd(
socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to create unix domain socket";
return {};
}
// Remove stale file first. Ignore the error intentionally.
base::DeleteFile(path, false /* recursive */);
if (bind(fd.get(), reinterpret_cast<const struct sockaddr*>(&sa),
sizeof(sa)) == -1) {
PLOG(ERROR) << "Failed to bind a unix domain socket";
return {};
}
if (fchmod(fd.get(), 0666) == -1) {
PLOG(ERROR) << "Failed to set permission";
return {};
}
if (listen(fd.get(), 5 /* backlog */) == -1) {
PLOG(ERROR) << "Failed to start listening a socket";
return {};
}
LOG(INFO) << path.value() << " created.";
return fd;
}
base::ScopedFD AcceptSocket(int raw_fd) {
base::ScopedFD fd(
HANDLE_EINTR(accept4(raw_fd, nullptr, nullptr, SOCK_CLOEXEC)));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to accept() unix domain socket";
return {};
}
return fd;
}
std::pair<int, base::ScopedFD> ConnectUnixDomainSocket(
const base::FilePath& path) {
LOG(INFO) << "Connecting to " << path.value();
struct sockaddr_un sa;
if (!ToSockAddr(path, &sa))
return std::make_pair(EFAULT, base::ScopedFD());
base::ScopedFD fd(
socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */));
if (!fd.is_valid()) {
int result_errno = errno;
PLOG(ERROR) << "Failed to create unix domain socket";
return std::make_pair(result_errno, base::ScopedFD());
}
if (HANDLE_EINTR(connect(fd.get(),
reinterpret_cast<const struct sockaddr*>(&sa),
sizeof(sa))) == -1) {
int result_errno = errno;
PLOG(ERROR) << "Failed to connect.";
return std::make_pair(result_errno, base::ScopedFD());
}
LOG(INFO) << "Connected to " << path.value();
return std::make_pair(0, std::move(fd));
}
} // namespace arc