| // Copyright 2020 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/boot_notification_server/util.h" |
| |
| #include <linux/vm_sockets.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include <base/logging.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <re2/re2.h> |
| |
| constexpr size_t kChunkSize = 256; |
| |
| socklen_t GetSockLen(sa_family_t family) { |
| switch (family) { |
| case AF_VSOCK: |
| return sizeof(sockaddr_vm); |
| case AF_UNIX: |
| return sizeof(sockaddr_un); |
| default: |
| LOG(ERROR) << "Using unsupported socket type " << family; |
| return sizeof(sockaddr); |
| } |
| } |
| |
| base::ScopedFD StartListening(sockaddr* addr) { |
| LOG(INFO) << "Creating socket"; |
| base::ScopedFD fd( |
| socket(addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */)); |
| if (!fd.is_valid()) { |
| PLOG(ERROR) << "Failed to create socket"; |
| return {}; |
| } |
| |
| LOG(INFO) << "Binding socket"; |
| if (HANDLE_EINTR(bind(fd.get(), addr, GetSockLen(addr->sa_family))) == -1) { |
| PLOG(ERROR) << "Failed to bind to socket address"; |
| return {}; |
| } |
| |
| LOG(INFO) << "Listening on socket"; |
| if (HANDLE_EINTR(listen(fd.get(), 5 /* backlog */)) == -1) { |
| PLOG(ERROR) << "Failed to listen to socket"; |
| return {}; |
| } |
| |
| return fd; |
| } |
| |
| base::ScopedFD WaitForClientConnect(int fd) { |
| LOG(INFO) << "Waiting for client to connect"; |
| base::ScopedFD client_fd(HANDLE_EINTR(accept(fd, nullptr, nullptr))); |
| if (!client_fd.is_valid()) { |
| PLOG(ERROR) << "Failed to accept connection on socket"; |
| return {}; |
| } |
| |
| LOG(INFO) << "Client connected"; |
| return client_fd; |
| } |
| |
| base::Optional<std::string> ReadFD(int fd) { |
| std::string out; |
| char buf[kChunkSize]; |
| |
| while (true) { |
| ssize_t len = HANDLE_EINTR(read(fd, buf, kChunkSize)); |
| if (len == -1) { |
| PLOG(ERROR) << "Unable to read from fd " << fd; |
| return base::nullopt; |
| } |
| if (len == 0) |
| break; |
| |
| out.append(buf, len); |
| } |
| |
| if (out.empty()) |
| return base::nullopt; |
| |
| return out; |
| } |
| |
| base::Optional<std::pair<unsigned int, std::string>> ExtractCidValue( |
| const std::string& props) { |
| // Pattern to extract CID from props string. `(?s)` flag is needed to let `.` |
| // match newlines. |
| static const re2::RE2& pattern = *new re2::RE2("(?s)^CID=(\\d+)\n(.*)$"); |
| unsigned int cid; |
| std::string tail; |
| if (!re2::RE2::FullMatch(props, pattern, &cid, &tail)) { |
| LOG(ERROR) << "The input '" << props |
| << "' did not match the expected pattern"; |
| return base::nullopt; |
| } |
| return std::make_pair(cid, tail); |
| } |
| |
| base::Optional<unsigned int> GetPeerCid(int fd) { |
| sockaddr_vm addr; |
| socklen_t len = sizeof(sockaddr_vm); |
| if (getpeername(fd, reinterpret_cast<sockaddr*>(&addr), &len) == -1) { |
| PLOG(ERROR) << "Unable to get peer address from socket fd " << fd; |
| return base::nullopt; |
| } |
| return addr.svm_cid; |
| } |