普通文本  |  129行  |  4.26 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "host/commands/ivserver/hald_client.h"

#include <string>

#include <glog/logging.h>

namespace ivserver {
namespace {
// The protocol between host-clients and the ivserver could change.
// Clients should verify what version they are talking to during the handshake.
const uint32_t kHaldClientProtocolVersion = 0;
}  // anonymous namespace

std::unique_ptr<HaldClient> HaldClient::New(const VSoCSharedMemory& shmem,
                                            const cvd::SharedFD& clientfd) {
  std::unique_ptr<HaldClient> res;

  if (!clientfd->IsOpen()) {
    LOG(WARNING) << "Invalid socket passed to HaldClient: "
                 << clientfd->StrError();
    return res;
  }

  res.reset(new HaldClient(clientfd));
  if (!res->PerformHandshake(shmem)) {
    LOG(ERROR) << "HalD handshake failed. Dropping connection.";
    res.reset();
  }

  return res;
}

HaldClient::HaldClient(const cvd::SharedFD& client_socket)
    : client_socket_(client_socket) {}

bool HaldClient::PerformHandshake(const VSoCSharedMemory& shared_mem) {
  int rval =
      client_socket_->Send(&kHaldClientProtocolVersion,
                           sizeof(kHaldClientProtocolVersion), MSG_NOSIGNAL);
  if (rval != sizeof(kHaldClientProtocolVersion)) {
    LOG(ERROR) << "failed to send protocol version: "
               << client_socket_->StrError();
    return false;
  }

  int16_t region_name_len;
  if (client_socket_->Recv(&region_name_len, sizeof(region_name_len),
                           MSG_NOSIGNAL) != sizeof(region_name_len)) {
    LOG(ERROR) << "Error receiving region name length: "
               << client_socket_->StrError();
    return false;
  }

  if (region_name_len <= 0 ||
      region_name_len > VSOC_DEVICE_NAME_SZ) {
    LOG(ERROR) << "Invalid region length received: " << region_name_len;
    return false;
  }

  std::vector<char> region_name_data(region_name_len);
  rval = client_socket_->Recv(region_name_data.data(), region_name_len,
                              MSG_NOSIGNAL);
  if (rval != region_name_len) {
    LOG(ERROR) << "Incomplete region name length received. Want: "
               << region_name_len << ", got: " << rval;
    return false;
  }

  std::string region_name(region_name_data.begin(), region_name_data.end());
  LOG(INFO) << "New HALD requesting region: " << region_name;

  // Send Host, Guest and SharedMemory FDs associated with this region.
  cvd::SharedFD guest_to_host_efd;
  cvd::SharedFD host_to_guest_efd;

  if (!shared_mem.GetEventFdPairForRegion(region_name, &guest_to_host_efd,
                                          &host_to_guest_efd)) {
    LOG(ERROR) << "Region " << region_name << " was not found.";
    return false;
  }

  if (!guest_to_host_efd->IsOpen()) {
    LOG(ERROR) << "Host channel is not open; last known error: "
               << guest_to_host_efd->StrError();
    return false;
  }

  if (!host_to_guest_efd->IsOpen()) {
    LOG(ERROR) << "Guest channel is not open; last known error: "
               << host_to_guest_efd->StrError();
    return false;
  }

  // TODO(ender): delete this once no longer necessary. Currently, absence of
  // payload makes RecvMsgAndFDs hang forever.
  uint64_t control_data = 0;
  struct iovec vec {
    &control_data, sizeof(control_data)
  };
  cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
  cvd::SharedFD fds[3] = {guest_to_host_efd, host_to_guest_efd,
                          shared_mem.SharedMemFD()};
  rval = client_socket_->SendMsgAndFDs<3>(hdr, MSG_NOSIGNAL, fds);
  if (rval == -1) {
    LOG(ERROR) << "failed to send Host FD: " << client_socket_->StrError();
    return false;
  }

  LOG(INFO) << "HALD managing region: " << region_name << " connected.";
  return true;
}

}  // namespace ivserver