blob: 5048de326b1a805f2ac091bca30df20bc986808f [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "garnet/bin/netconnector/netconnector_impl.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/fit/function.h>
#include <lib/zx/time.h>
#include <iostream>
#include "garnet/bin/netconnector/device_service_provider.h"
#include "garnet/bin/netconnector/host_name.h"
#include "garnet/bin/netconnector/netconnector_params.h"
#include "src/lib/fxl/logging.h"
namespace netconnector {
// static
const inet::IpPort NetConnectorImpl::kPort = inet::IpPort::From_uint16_t(7777);
// static
const std::string NetConnectorImpl::kFuchsiaServiceName = "_fuchsia._tcp.";
// static
const std::string NetConnectorImpl::kLocalDeviceName = "local";
NetConnectorImpl::NetConnectorImpl(NetConnectorParams* params,
fit::closure quit_callback)
: params_(params),
quit_callback_(std::move(quit_callback)),
component_context_(sys::ComponentContext::Create()),
// TODO(dalesat): Create a new RespondingServiceHost per user.
// Requestors should provide user credentials allowing a ServiceAgent
// to obtain a user environment. A RespondingServiceHost should be
// created with that environment so that responding services are
// launched in the correct environment.
responding_service_host_(
component_context_->svc()->Connect<fuchsia::sys::Environment>()),
mdns_subscriber_binding_(this) {
FXL_DCHECK(quit_callback_);
if (!params->listen()) {
// Start the listener.
fuchsia::netconnector::NetConnectorSyncPtr net_connector;
component_context_->svc()->Connect(net_connector.NewRequest());
if (params_->show_devices()) {
uint64_t version;
std::vector<std::string> device_names;
net_connector->GetKnownDeviceNames(
fuchsia::netconnector::kInitialKnownDeviceNames, &version,
&device_names);
if (device_names.size() == 0) {
std::cout << "No remote devices found\n";
} else {
for (auto& device_name : device_names) {
std::cout << device_name << "\n";
}
}
}
quit_callback_();
return;
}
// Running as listener.
component_context_->outgoing()->AddPublicService(bindings_.GetHandler(this));
device_names_publisher_.SetCallbackRunner(
[this](const GetKnownDeviceNamesCallback& callback, uint64_t version) {
fidl::VectorPtr<std::string> device_names =
fidl::VectorPtr<std::string>::New(0);
for (auto& pair : params_->devices()) {
device_names.push_back(pair.first);
}
callback(version, std::move(device_names));
});
// Register services.
for (auto& pair : params->MoveServices()) {
responding_service_host_.RegisterSingleton(pair.first,
std::move(pair.second));
}
StartListener();
}
NetConnectorImpl::~NetConnectorImpl() {}
void NetConnectorImpl::StartListener() {
if (!NetworkIsReady()) {
async::PostDelayedTask(
async_get_default_dispatcher(), [this]() { StartListener(); },
zx::sec(5));
return;
}
listener_.Start(kPort, [this](fxl::UniqueFD fd) {
AddServiceAgent(ServiceAgent::Create(std::move(fd), this));
});
mdns_subscriber_ =
component_context_->svc()->Connect<fuchsia::net::mdns::Subscriber>();
host_name_ = GetHostName();
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriber>
subscriber_handle;
mdns_subscriber_binding_.Bind(subscriber_handle.NewRequest());
mdns_subscriber_binding_.set_error_handler([this](zx_status_t status) {
mdns_subscriber_binding_.set_error_handler(nullptr);
mdns_subscriber_binding_.Unbind();
});
mdns_subscriber_->SubscribeToService(kFuchsiaServiceName,
std::move(subscriber_handle));
}
void NetConnectorImpl::ReleaseDeviceServiceProvider(
DeviceServiceProvider* device_service_provider) {
size_t removed = device_service_providers_.erase(device_service_provider);
FXL_DCHECK(removed == 1);
}
void NetConnectorImpl::ReleaseRequestorAgent(RequestorAgent* requestor_agent) {
size_t removed = requestor_agents_.erase(requestor_agent);
FXL_DCHECK(removed == 1);
}
void NetConnectorImpl::ReleaseServiceAgent(ServiceAgent* service_agent) {
size_t removed = service_agents_.erase(service_agent);
FXL_DCHECK(removed == 1);
}
void NetConnectorImpl::GetDeviceServiceProvider(
std::string device_name,
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> request) {
if (device_name == host_name_ || device_name == kLocalDeviceName) {
responding_service_host_.AddBinding(std::move(request));
return;
}
auto iter = params_->devices().find(device_name);
if (iter == params_->devices().end()) {
FXL_LOG(ERROR) << "Unrecognized device name " << device_name;
return;
}
AddDeviceServiceProvider(DeviceServiceProvider::Create(
device_name, inet::SocketAddress(iter->second, kPort), std::move(request),
this));
}
void NetConnectorImpl::GetKnownDeviceNames(
uint64_t version_last_seen, GetKnownDeviceNamesCallback callback) {
device_names_publisher_.Get(version_last_seen, std::move(callback));
}
void NetConnectorImpl::RegisterServiceProvider(
std::string name,
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> handle) {
FXL_LOG(INFO) << "Service '" << name << "' provider registered.";
responding_service_host_.RegisterProvider(name, std::move(handle));
}
void NetConnectorImpl::AddDeviceServiceProvider(
std::unique_ptr<DeviceServiceProvider> device_service_provider) {
DeviceServiceProvider* raw_ptr = device_service_provider.get();
device_service_providers_.emplace(raw_ptr,
std::move(device_service_provider));
}
void NetConnectorImpl::AddRequestorAgent(
std::unique_ptr<RequestorAgent> requestor_agent) {
RequestorAgent* raw_ptr = requestor_agent.get();
requestor_agents_.emplace(raw_ptr, std::move(requestor_agent));
}
void NetConnectorImpl::AddServiceAgent(
std::unique_ptr<ServiceAgent> service_agent) {
ServiceAgent* raw_ptr = service_agent.get();
service_agents_.emplace(raw_ptr, std::move(service_agent));
}
void NetConnectorImpl::OnInstanceDiscovered(
fuchsia::net::mdns::ServiceInstance instance,
OnInstanceDiscoveredCallback callback) {
FXL_DCHECK(!instance.endpoints.empty());
std::cerr << "netconnector: Device '" << instance.instance
<< "' discovered at address "
<< inet::IpAddress(&instance.endpoints[0].addr) << "\n";
params_->RegisterDevice(instance.instance,
inet::IpAddress(&instance.endpoints[0].addr));
device_names_publisher_.SendUpdates();
callback();
}
void NetConnectorImpl::OnInstanceChanged(
fuchsia::net::mdns::ServiceInstance instance,
OnInstanceChangedCallback callback) {
callback();
}
void NetConnectorImpl::OnInstanceLost(std::string service, std::string instance,
OnInstanceLostCallback callback) {
std::cerr << "netconnector: Device '" << instance << "' lost\n";
params_->UnregisterDevice(instance);
device_names_publisher_.SendUpdates();
callback();
}
} // namespace netconnector