blob: f7662de2b7ccf93dc6e11fcd7b23dcd76c4ddfe5 [file] [log] [blame]
// Copyright 2014 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 "peerd/manager.h"
#include <base/format_macros.h>
#include <base/guid.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <chromeos/dbus/async_event_sequencer.h>
#include <chromeos/dbus/exported_object_manager.h>
#include <dbus/object_path.h>
#include "peerd/constants.h"
#include "peerd/dbus_constants.h"
#include "peerd/ip_addr.h"
#include "peerd/peer_manager_impl.h"
#include "peerd/published_peer.h"
#include "peerd/service.h"
#include "peerd/technologies.h"
using chromeos::Error;
using chromeos::ErrorPtr;
using chromeos::dbus_utils::AsyncEventSequencer;
using chromeos::dbus_utils::DBusObject;
using chromeos::dbus_utils::ExportedObjectManager;
using dbus::ObjectPath;
using peerd::constants::kSerbusServiceId;
using peerd::dbus_constants::kManagerExposeService;
using peerd::dbus_constants::kManagerInterface;
using peerd::dbus_constants::kManagerPing;
using peerd::dbus_constants::kManagerRemoveExposedService;
using peerd::dbus_constants::kManagerServicePath;
using peerd::dbus_constants::kManagerStartMonitoring;
using peerd::dbus_constants::kManagerStopMonitoring;
using peerd::dbus_constants::kPingResponse;
using peerd::dbus_constants::kSelfPath;
using std::map;
using std::set;
using std::string;
using std::unique_ptr;
using std::vector;
namespace peerd {
namespace errors {
namespace manager {
const char kInvalidServiceToken[] = "manager.service_token";
const char kInvalidMonitoringTechnology[] = "manager.monitoring_technology";
const char kInvalidMonitoringToken[] = "manager.monitoring_token";
} // namespace manager
} // namespace errors
Manager::Manager(ExportedObjectManager* object_manager,
const string& initial_mdns_prefix)
: Manager(unique_ptr<DBusObject>{
new DBusObject{object_manager, object_manager->GetBus(),
ObjectPath{kManagerServicePath}}},
unique_ptr<PublishedPeer>{},
unique_ptr<PeerManagerInterface>{},
unique_ptr<AvahiClient>{},
initial_mdns_prefix) {
}
Manager::Manager(unique_ptr<DBusObject> dbus_object,
unique_ptr<PublishedPeer> self,
unique_ptr<PeerManagerInterface> peer_manager,
unique_ptr<AvahiClient> avahi_client,
const string& initial_mdns_prefix)
: dbus_object_{std::move(dbus_object)},
self_{std::move(self)},
peer_manager_{std::move(peer_manager)},
avahi_client_{std::move(avahi_client)} {
// If we haven't gotten mocks for these objects, make real ones.
if (!self_) {
self_.reset(
new PublishedPeer{dbus_object_->GetObjectManager()->GetBus(),
dbus_object_->GetObjectManager().get(),
ObjectPath{kSelfPath}});
}
if (!peer_manager_) {
peer_manager_.reset(
new PeerManagerImpl{dbus_object_->GetObjectManager()->GetBus(),
dbus_object_->GetObjectManager().get()});
}
if (!avahi_client_) {
avahi_client_.reset(
new AvahiClient{dbus_object_->GetObjectManager()->GetBus(),
peer_manager_.get()});
avahi_client_->AttemptToUseMDnsPrefix(initial_mdns_prefix);
}
}
void Manager::RegisterAsync(const CompletionAction& completion_callback) {
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
chromeos::dbus_utils::DBusInterface* itf =
dbus_object_->AddOrGetInterface(kManagerInterface);
itf->AddSimpleMethodHandlerWithError(kManagerStartMonitoring,
base::Unretained(this),
&Manager::StartMonitoring);
itf->AddSimpleMethodHandlerWithError(kManagerStopMonitoring,
base::Unretained(this),
&Manager::StopMonitoring);
itf->AddSimpleMethodHandlerWithError(kManagerExposeService,
base::Unretained(this),
&Manager::ExposeService);
itf->AddSimpleMethodHandlerWithError(kManagerRemoveExposedService,
base::Unretained(this),
&Manager::RemoveExposedService);
itf->AddSimpleMethodHandler(kManagerPing,
base::Unretained(this),
&Manager::Ping);
chromeos::ErrorPtr error;
const bool self_success = self_->RegisterAsync(
&error,
base::GenerateGUID(), // Every boot is a new GUID for now.
base::Time::UnixEpoch(),
sequencer->GetHandler("Failed exporting Self.", true));
CHECK(self_success) << "Failed to RegisterAsync Self.";
dbus_object_->RegisterAsync(
sequencer->GetHandler("Failed exporting Manager.", true));
avahi_client_->RegisterOnAvahiRestartCallback(
base::Bind(&Manager::ShouldRefreshAvahiPublisher,
base::Unretained(this)));
avahi_client_->RegisterAsync(
sequencer->GetHandler("Failed AvahiClient.RegisterAsync().", true));
sequencer->OnAllTasksCompletedCall({completion_callback});
}
bool Manager::StartMonitoring(
chromeos::ErrorPtr* error,
const vector<technologies::tech_t>& requested_technologies,
std::string* monitoring_token) {
if (requested_technologies.empty()) {
Error::AddTo(error,
kPeerdErrorDomain,
errors::manager::kInvalidMonitoringTechnology,
"Expected at least one monitoring technology.");
return false;
}
technologies::tech_t combined = 0;
for (technologies::tech_t tech : requested_technologies) {
if (tech != technologies::kAll &&
tech != technologies::kMDNS) {
Error::AddToPrintf(error,
kPeerdErrorDomain,
errors::manager::kInvalidMonitoringTechnology,
"Invalid monitoring technology: %d.", tech);
return false;
}
combined |= tech;
}
*monitoring_token = "monitoring_" +
std::to_string(++monitoring_tokens_issued_);
monitoring_requests_[*monitoring_token] = combined;
if (((technologies::kAll | technologies::kMDNS) & combined) != 0) {
// Let the AvahiClient worry about if we're already monitoring.
avahi_client_->StartMonitoring();
}
// TODO(wiley): Monitor DBus identifier for disconnect.
return true;
}
bool Manager::StopMonitoring(chromeos::ErrorPtr* error,
const string& monitoring_token) {
auto it = monitoring_requests_.find(monitoring_token);
if (it == monitoring_requests_.end()) {
Error::AddToPrintf(error,
kPeerdErrorDomain,
errors::manager::kInvalidMonitoringToken,
"Unknown monitoring token: %s.",
monitoring_token.c_str());
return false;
}
monitoring_requests_.erase(it);
technologies::tech_t combined = 0;
for (const auto& request : monitoring_requests_) {
combined |= request.second;
}
if (combined & technologies::kAll) {
// Carry on, everything we had going on should continue.
return true;
}
if (!(combined & technologies::kMDNS)) {
avahi_client_->StopMonitoring();
}
return true;
}
bool Manager::ExposeService(chromeos::ErrorPtr* error,
const string& service_id,
const map<string, string>& service_info,
std::string* service_token) {
VLOG(1) << "Exposing service '" << service_id << "'.";
if (service_id == kSerbusServiceId) {
Error::AddToPrintf(error,
kPeerdErrorDomain,
errors::service::kInvalidServiceId,
"Cannot expose a service named %s",
kSerbusServiceId);
return false;
}
if (!self_->AddService(error, service_id, {}, service_info)) {
return false;
}
*service_token = "service_token_" + std::to_string(++services_added_);
service_token_to_id_.emplace(*service_token, service_id);
// TODO(wiley) Maybe trigger an advertisement run since we have updated
// information.
return true;
}
bool Manager::RemoveExposedService(chromeos::ErrorPtr* error,
const string& service_token) {
auto it = service_token_to_id_.find(service_token);
if (it == service_token_to_id_.end()) {
Error::AddTo(error,
kPeerdErrorDomain,
errors::manager::kInvalidServiceToken,
"Invalid service token given to RemoveExposedService.");
return false;
}
bool success = self_->RemoveService(error, it->second);
// Maybe RemoveService returned with an error, but either way, we should
// forget this service token.
service_token_to_id_.erase(it);
// TODO(wiley) Maybe trigger an advertisement run since we have updated
// information.
return success;
}
string Manager::Ping() {
return kPingResponse;
}
void Manager::ShouldRefreshAvahiPublisher() {
LOG(INFO) << "Publishing services to mDNS";
// The old publisher has been invalidated, and the records pulled. We should
// re-register the records we care about.
self_->RegisterServicePublisher(
avahi_client_->GetPublisher(self_->GetUUID()));
}
} // namespace peerd