blob: 9ce16c127e99ae6e41078ce8f08a062849418c7c [file] [log] [blame]
// Copyright 2018 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 "vm_tools/concierge/shill_client.h"
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/logging.h>
#include <brillo/variant_dictionary.h>
#include <chromeos/dbus/service_constants.h>
#include "vm_tools/concierge/future.h"
using org::chromium::flimflam::IPConfigProxy;
using org::chromium::flimflam::ServiceProxy;
namespace vm_tools {
namespace concierge {
ShillClient::ShillClient(scoped_refptr<dbus::Bus> bus)
: bus_(bus),
manager_proxy_(new org::chromium::flimflam::ManagerProxy(bus_)),
default_service_proxy_(nullptr),
default_ipconfig_proxy_(nullptr) {
// The Manager must be watched for changes to the default Service. To be
// exact, we are interested in watching changes to the following hierarchy:
// +----------------+---------------------------+------------------------+
// | Manager | /service/42 | /ipconfig/eth0_88_dhcp |
// +----------------+---------------------------+------------------------+
// | ... | ... | ... |
// | DefaultService | IPConfig: | Nameservers: [...] |
// | /service/42 | /ipconfig/eth0_88_dhcp | SearchDomains: [...] |
// | ... | ... | ... |
// +----------------+---------------------------+------------------------+
//
// Any change to
// 1) The Manager's DefaultService,
// 2) The default Service's IPConfig, or
// 3) That IPConfig's Nameservers and SearchDomains properties
// may require updating the nameservers and search domains in the guest VMs.
manager_proxy_->RegisterPropertyChangedSignalHandler(
base::Bind(&ShillClient::OnManagerPropertyChange,
weak_factory_.GetWeakPtr()),
base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
weak_factory_.GetWeakPtr()));
auto owner_changed_cb = base::Bind(&ShillClient::OnShillServiceOwnerChange,
weak_factory_.GetWeakPtr());
bus_->GetObjectProxy(shill::kFlimflamServiceName, dbus::ObjectPath{"/"})
->SetNameOwnerChangedCallback(owner_changed_cb);
}
void ShillClient::OnShillServiceOwnerChange(const std::string& old_owner,
const std::string& new_owner) {
if (new_owner.empty()) {
// If shill disappears, clear the cached nameservers and search domains.
// We'll expect property change signals to come later.
nameservers_.clear();
search_domains_.clear();
}
}
template <typename Proxy>
std::optional<brillo::VariantDictionary> GetPropertiesHelper(dbus::Bus* bus,
Proxy* proxy) {
bus->AssertOnOriginThread();
brillo::VariantDictionary properties;
if (bus->HasDBusThread()) {
bool success =
AsyncNoReject(
bus->GetDBusTaskRunner(),
base::BindOnce(
[](Proxy* proxy, brillo::VariantDictionary* properties) {
return proxy->GetProperties(properties, nullptr);
},
proxy, &properties))
.Get()
.val;
if (success) {
return properties;
}
} else {
if (proxy->GetProperties(&properties, nullptr)) {
return properties;
}
}
return std::nullopt;
}
void ShillClient::OnManagerPropertyChangeRegistration(
const std::string& interface,
const std::string& signal_name,
bool success) {
CHECK(success) << "Unable to register for Manager change events";
auto properties = GetPropertiesHelper(bus_.get(), manager_proxy_.get());
if (!properties) {
LOG(ERROR) << "Unable to get shill Manager properties";
return;
}
auto it = properties->find(shill::kDefaultServiceProperty);
CHECK(it != properties->end())
<< "Shill should always publish a default service.";
OnManagerPropertyChange(shill::kDefaultServiceProperty, it->second);
}
void ShillClient::OnManagerPropertyChange(const std::string& property_name,
const brillo::Any& property_value) {
// Only handle changes to the default service.
if (property_name != shill::kDefaultServiceProperty) {
return;
}
dbus::ObjectPath service_path = property_value.TryGet<dbus::ObjectPath>();
if (!service_path.IsValid() || service_path.value() == "/") {
// A path of "/" indicates that there is no default service yet. Wait for a
// future update of the default service property.
nameservers_.clear();
search_domains_.clear();
if (!config_changed_callback_.is_null()) {
config_changed_callback_.Run(nameservers_, search_domains_);
}
return;
}
// The default service has changed, so update the proxy object and register
// a handler for its properties.
if (default_service_proxy_) {
default_service_proxy_->ReleaseObjectProxy(base::Bind([]() {}));
}
default_service_proxy_.reset(new ServiceProxy(bus_, service_path));
default_service_proxy_->RegisterPropertyChangedSignalHandler(
base::Bind(&ShillClient::OnServicePropertyChange,
weak_factory_.GetWeakPtr()),
base::Bind(&ShillClient::OnServicePropertyChangeRegistration,
weak_factory_.GetWeakPtr()));
if (!default_service_changed_callback_.is_null()) {
default_service_changed_callback_.Run();
}
}
void ShillClient::OnServicePropertyChangeRegistration(
const std::string& interface,
const std::string& signal_name,
bool success) {
CHECK(success) << "Unable to register for Service change events";
auto properties =
GetPropertiesHelper(bus_.get(), default_service_proxy_.get());
if (!properties) {
LOG(ERROR) << "Unable to get shill Service properties";
return;
}
auto it = properties->find(shill::kIPConfigProperty);
if (it == properties->end()) {
return;
}
OnServicePropertyChange(shill::kIPConfigProperty, it->second);
}
void ShillClient::OnServicePropertyChange(const std::string& property_name,
const brillo::Any& property_value) {
if (property_name != shill::kIPConfigProperty) {
return;
}
dbus::ObjectPath ipconfig_path = property_value.TryGet<dbus::ObjectPath>();
if (!ipconfig_path.IsValid() || ipconfig_path.value() == "/") {
// A path of "/" indicates that there is no IPConfig yet. Wait for a future
// update of the IPConfig.
nameservers_.clear();
search_domains_.clear();
if (!config_changed_callback_.is_null()) {
config_changed_callback_.Run(nameservers_, search_domains_);
}
return;
}
if (default_ipconfig_proxy_ &&
default_ipconfig_proxy_->GetObjectPath() == ipconfig_path) {
// ipconfig path didn't change. Don't need to replace the proxy
return;
}
std::unique_ptr<IPConfigProxy> ipconfig_proxy{
new IPConfigProxy(bus_, ipconfig_path)};
auto properties = GetPropertiesHelper(bus_.get(), ipconfig_proxy.get());
if (!properties) {
LOG(ERROR) << "Unable to get shill IPConfig properties";
return;
}
auto it = properties->find(shill::kMethodProperty);
if (it == properties->end()) {
return;
}
// Use it as the default IPConfig for nameservers.
if (default_ipconfig_proxy_) {
default_ipconfig_proxy_->ReleaseObjectProxy(base::Bind([]() {}));
}
default_ipconfig_proxy_ = std::move(ipconfig_proxy);
default_ipconfig_proxy_->RegisterPropertyChangedSignalHandler(
base::Bind(&ShillClient::OnIPConfigPropertyChange,
weak_factory_.GetWeakPtr()),
base::Bind(&ShillClient::OnIPConfigPropertyChangeRegistration,
weak_factory_.GetWeakPtr()));
}
void ShillClient::OnIPConfigPropertyChangeRegistration(
const std::string& interface,
const std::string& signal_name,
bool success) {
CHECK(success) << "Unable to register for IPConfig change events";
auto properties =
GetPropertiesHelper(bus_.get(), default_ipconfig_proxy_.get());
if (!properties) {
LOG(ERROR) << "Unable to get shill IPConfig properties";
return;
}
auto ns_it = properties->find(shill::kNameServersProperty);
if (ns_it != properties->end()) {
OnIPConfigPropertyChange(shill::kNameServersProperty, ns_it->second);
}
auto sd_it = properties->find(shill::kSearchDomainsProperty);
if (sd_it != properties->end()) {
OnIPConfigPropertyChange(shill::kSearchDomainsProperty, sd_it->second);
}
}
void ShillClient::OnIPConfigPropertyChange(const std::string& property_name,
const brillo::Any& property_value) {
if (property_name != shill::kNameServersProperty &&
property_name != shill::kSearchDomainsProperty) {
return;
}
if (property_name == shill::kNameServersProperty) {
std::vector<std::string> new_nameservers{
property_value.TryGet<std::vector<std::string>>()};
nameservers_ = std::move(new_nameservers);
} else {
std::vector<std::string> new_search_domains{
property_value.TryGet<std::vector<std::string>>()};
search_domains_ = std::move(new_search_domains);
}
if (!config_changed_callback_.is_null()) {
config_changed_callback_.Run(nameservers_, search_domains_);
}
}
void ShillClient::RegisterResolvConfigChangedHandler(
base::Callback<void(std::vector<std::string>, std::vector<std::string>)>
callback) {
config_changed_callback_ = std::move(callback);
CHECK(!config_changed_callback_.is_null());
config_changed_callback_.Run(nameservers_, search_domains_);
}
void ShillClient::RegisterDefaultServiceChangedHandler(
base::Callback<void()> callback) {
default_service_changed_callback_ = std::move(callback);
}
} // namespace concierge
} // namespace vm_tools