// 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/peer.h" #include #include #include #include #include #include "peerd/dbus_constants.h" #include "peerd/typedefs.h" using base::Time; using base::TimeDelta; using chromeos::Any; using chromeos::Error; using chromeos::dbus_utils::AsyncEventSequencer; using chromeos::dbus_utils::DBusInterface; using chromeos::dbus_utils::DBusObject; using chromeos::dbus_utils::ExportedObjectManager; using dbus::ObjectPath; using peerd::dbus_constants::kPeerInterface; using peerd::dbus_constants::kPeerLastSeen; using peerd::dbus_constants::kPeerUUID; using peerd::dbus_constants::kServicePathFragment; using std::map; using std::string; using std::vector; using std::unique_ptr; namespace peerd { namespace errors { namespace peer { const char kInvalidUUID[] = "peer.uuid"; const char kInvalidTime[] = "peer.time"; const char kUnknownService[] = "peer.unknown_service"; } // namespace peer } // namespace errors Peer::Peer(const scoped_refptr& bus, ExportedObjectManager* object_manager, const ObjectPath& path) : bus_(bus), dbus_object_(new DBusObject{object_manager, bus, path}), service_path_prefix_(path.value() + kServicePathFragment) { } bool Peer::RegisterAsync( chromeos::ErrorPtr* error, const std::string& uuid, const Time& last_seen, const CompletionAction& completion_callback) { if (!base::IsValidGUID(uuid)) { Error::AddTo(error, FROM_HERE, kPeerdErrorDomain, errors::peer::kInvalidUUID, "Invalid UUID for peer."); return false; } dbus_adaptor_.SetUUID(uuid); if (!SetLastSeen(error, last_seen)) { return false; } dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get()); dbus_object_->RegisterAsync(completion_callback); return true; } bool Peer::SetLastSeen(chromeos::ErrorPtr* error, const Time& last_seen) { if (!IsValidUpdateTime(error, last_seen)) { return false; } uint64_t milliseconds_since_epoch = 0; CHECK(TimeToMillisecondsSinceEpoch(last_seen, &milliseconds_since_epoch)); dbus_adaptor_.SetLastSeen(milliseconds_since_epoch); return true; } std::string Peer::GetUUID() const { return dbus_adaptor_.GetUUID(); } Time Peer::GetLastSeen() const { return TimeDelta::FromMilliseconds(dbus_adaptor_.GetLastSeen()) + Time::UnixEpoch(); } bool Peer::IsValidUpdateTime(chromeos::ErrorPtr* error, const base::Time& last_seen) const { uint64_t milliseconds_since_epoch = 0; if (!TimeToMillisecondsSinceEpoch(last_seen, &milliseconds_since_epoch)) { Error::AddTo(error, FROM_HERE, kPeerdErrorDomain, errors::peer::kInvalidTime, "Negative time update is invalid."); return false; } if (milliseconds_since_epoch < dbus_adaptor_.GetLastSeen()) { Error::AddTo(error, FROM_HERE, kPeerdErrorDomain, errors::peer::kInvalidTime, "Discarding update to last seen time as stale."); return false; } return true; } bool Peer::AddService(chromeos::ErrorPtr* error, const string& service_id, const Service::IpAddresses& addresses, const map& service_info, const map& options) { ObjectPath service_path(service_path_prefix_.value() + std::to_string(++services_added_)); // TODO(wiley): There is a potential race here. If we remove the service // too quickly, we race with DBus export completing. We'll // have to be careful in Service not to assume export has // finished. scoped_refptr sequencer(new AsyncEventSequencer()); unique_ptr new_service{new Service{ bus_, dbus_object_->GetObjectManager().get(), service_path}}; const bool success = new_service->RegisterAsync( error, service_id, addresses, service_info, options, sequencer->GetHandler("Failed exporting service.", true)); if (success) { services_.emplace(service_id, std::move(new_service)); sequencer->OnAllTasksCompletedCall({}); } return success; } bool Peer::RemoveService(chromeos::ErrorPtr* error, const string& service_id) { if (services_.erase(service_id) == 0) { Error::AddTo(error, FROM_HERE, kPeerdErrorDomain, errors::peer::kUnknownService, "Unknown service id."); return false; } return true; } bool Peer::TimeToMillisecondsSinceEpoch(const Time& time, uint64_t* ret) const { int64_t time_diff = (time - Time::UnixEpoch()).InMilliseconds(); if (time_diff < 0) { return false; } *ret = static_cast(time_diff); return true; } } // namespace peerd