blob: 18b0ca549a4ea2ce24ef3b58c58702801e9252fc [file] [log] [blame]
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07001// Copyright 2016 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/shill_client.h"
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07006
Garrick Evans49879532018-12-03 13:15:36 +09007#include <vector>
8
Kevin Cernekee95d4ae92016-06-19 10:26:29 -07009#include <base/bind.h>
10#include <base/logging.h>
Garrick Evanse87bb4f2020-02-18 10:27:37 +090011#include <base/strings/string_util.h>
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070012#include <chromeos/dbus/service_constants.h>
13
Garrick Evans3388a032020-03-24 11:25:55 +090014namespace patchpanel {
Hugo Benichid17aa592019-04-26 15:15:01 +090015
Garrick Evans08843932019-09-17 14:41:08 +090016ShillClient::ShillClient(const scoped_refptr<dbus::Bus>& bus) : bus_(bus) {
Hidehiko Abe3a7e5132018-02-15 13:07:50 +090017 manager_proxy_.reset(new org::chromium::flimflam::ManagerProxy(bus_));
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070018 manager_proxy_->RegisterPropertyChangedSignalHandler(
19 base::Bind(&ShillClient::OnManagerPropertyChange,
20 weak_factory_.GetWeakPtr()),
21 base::Bind(&ShillClient::OnManagerPropertyChangeRegistration,
22 weak_factory_.GetWeakPtr()));
23}
24
Garrick Evans1b1f67c2020-02-04 16:21:25 +090025const std::string& ShillClient::default_interface() const {
26 return default_interface_;
27}
28
Hugo Benichicc6850f2020-01-17 13:26:06 +090029const std::set<std::string> ShillClient::get_devices() const {
30 return devices_;
31}
32
33bool ShillClient::has_device(const std::string& ifname) const {
34 return devices_.find(ifname) != devices_.end();
35}
36
Garrick Evans139708f2020-02-06 14:38:59 +090037void ShillClient::ScanDevices(const DevicesChangeHandler& handler) {
Garrick Evans49879532018-12-03 13:15:36 +090038 brillo::VariantDictionary props;
39 if (!manager_proxy_->GetProperties(&props, nullptr)) {
40 LOG(ERROR) << "Unable to get manager properties";
41 return;
42 }
43 const auto it = props.find(shill::kDevicesProperty);
44 if (it == props.end()) {
45 LOG(WARNING) << "Manager properties is missing devices";
46 return;
47 }
Garrick Evans139708f2020-02-06 14:38:59 +090048 UpdateDevices(it->second);
Garrick Evans49879532018-12-03 13:15:36 +090049}
50
Hugo Benichiddee2812019-05-10 16:03:43 +090051std::string ShillClient::GetDefaultInterface() {
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070052 brillo::VariantDictionary manager_props;
53
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070054 if (!manager_proxy_->GetProperties(&manager_props, nullptr)) {
55 LOG(ERROR) << "Unable to get manager properties";
Hugo Benichiddee2812019-05-10 16:03:43 +090056 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070057 }
58
59 auto it = manager_props.find(shill::kDefaultServiceProperty);
60 if (it == manager_props.end()) {
61 LOG(WARNING) << "Manager properties is missing default service";
Hugo Benichiddee2812019-05-10 16:03:43 +090062 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070063 }
64
65 dbus::ObjectPath service_path = it->second.TryGet<dbus::ObjectPath>();
66 if (!service_path.IsValid() || service_path.value() == "/") {
Hugo Benichiddee2812019-05-10 16:03:43 +090067 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070068 }
69
70 org::chromium::flimflam::ServiceProxy service_proxy(bus_, service_path);
71 brillo::VariantDictionary service_props;
72 if (!service_proxy.GetProperties(&service_props, nullptr)) {
73 LOG(ERROR) << "Can't retrieve properties for service";
Hugo Benichiddee2812019-05-10 16:03:43 +090074 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070075 }
76
Alex Khouderchah05a8b5b2019-11-20 12:18:51 -080077 it = service_props.find(shill::kIsConnectedProperty);
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070078 if (it == service_props.end()) {
Alex Khouderchah05a8b5b2019-11-20 12:18:51 -080079 LOG(WARNING) << "Service properties is missing \"IsConnected\"";
Hugo Benichiddee2812019-05-10 16:03:43 +090080 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070081 }
Alex Khouderchah05a8b5b2019-11-20 12:18:51 -080082 if (!it->second.TryGet<bool>()) {
83 LOG(INFO) << "Ignoring non-connected service";
Hugo Benichiddee2812019-05-10 16:03:43 +090084 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070085 }
86
87 it = service_props.find(shill::kDeviceProperty);
88 if (it == service_props.end()) {
89 LOG(WARNING) << "Service properties is missing device path";
Hugo Benichiddee2812019-05-10 16:03:43 +090090 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070091 }
92
93 dbus::ObjectPath device_path = it->second.TryGet<dbus::ObjectPath>();
94 if (!device_path.IsValid()) {
95 LOG(WARNING) << "Invalid device path";
Hugo Benichiddee2812019-05-10 16:03:43 +090096 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -070097 }
98
99 org::chromium::flimflam::DeviceProxy device_proxy(bus_, device_path);
100 brillo::VariantDictionary device_props;
101 if (!device_proxy.GetProperties(&device_props, nullptr)) {
102 LOG(ERROR) << "Can't retrieve properties for device";
Hugo Benichiddee2812019-05-10 16:03:43 +0900103 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700104 }
105
106 it = device_props.find(shill::kInterfaceProperty);
107 if (it == device_props.end()) {
108 LOG(WARNING) << "Device properties is missing interface name";
Hugo Benichiddee2812019-05-10 16:03:43 +0900109 return "";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700110 }
111
112 std::string interface = it->second.TryGet<std::string>();
113 if (interface.empty()) {
114 LOG(WARNING) << "Device interface name is empty";
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700115 }
116
Hugo Benichiddee2812019-05-10 16:03:43 +0900117 return interface;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700118}
119
120void ShillClient::OnManagerPropertyChangeRegistration(
121 const std::string& interface,
122 const std::string& signal_name,
123 bool success) {
124 if (!success)
125 LOG(FATAL) << "Unable to register for interface change events";
126}
127
128void ShillClient::OnManagerPropertyChange(const std::string& property_name,
129 const brillo::Any& property_value) {
Hugo Benichiddee2812019-05-10 16:03:43 +0900130 if (property_name == shill::kDevicesProperty) {
Garrick Evans139708f2020-02-06 14:38:59 +0900131 UpdateDevices(property_value);
Hugo Benichiddee2812019-05-10 16:03:43 +0900132
133 // Choose a fallback interface when any network device exist. Update the
134 // fallback interface if it that device does not exist anymore.
Garrick Evansd4742832019-05-20 11:51:40 +0900135 if (!devices_.empty() &&
136 devices_.find(fallback_default_interface_) == devices_.end()) {
Hugo Benichiddee2812019-05-10 16:03:43 +0900137 fallback_default_interface_ = *devices_.begin();
138 // When the system appears to have no default interface, use the fallback
139 // interface instead.
Garrick Evansd4742832019-05-20 11:51:40 +0900140 if (default_interface_.empty() ||
141 default_interface_ != fallback_default_interface_)
Hugo Benichiddee2812019-05-10 16:03:43 +0900142 SetDefaultInterface(fallback_default_interface_);
143 }
144
Hugo Benichiddee2812019-05-10 16:03:43 +0900145 // Remove the fallback interface when no network device is managed by shill.
146 if (!fallback_default_interface_.empty() && devices_.empty()) {
147 fallback_default_interface_ = "";
148 SetDefaultInterface("");
149 }
150
Garrick Evans49879532018-12-03 13:15:36 +0900151 return;
152 }
153
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700154 if (property_name != shill::kDefaultServiceProperty &&
155 property_name != shill::kConnectionStateProperty)
156 return;
157
Hugo Benichiddee2812019-05-10 16:03:43 +0900158 SetDefaultInterface(GetDefaultInterface());
159}
160
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900161std::string ShillClient::SetDefaultInterface(std::string new_default) {
Hugo Benichiddee2812019-05-10 16:03:43 +0900162 // When the system default is lost, use the fallback interface instead.
163 if (new_default.empty())
164 new_default = fallback_default_interface_;
165
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700166 if (default_interface_ == new_default)
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900167 return default_interface_;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700168
Garrick Evans139708f2020-02-06 14:38:59 +0900169 LOG(INFO) << "Default interface changed from [" << default_interface_
170 << "] to [" << new_default << "]";
171
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900172 const std::string prev_default = default_interface_;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700173 default_interface_ = new_default;
Garrick Evans139708f2020-02-06 14:38:59 +0900174 for (const auto& h : default_interface_handlers_) {
175 if (!h.is_null())
176 h.Run(default_interface_, prev_default);
Garrick Evansc7fea0a2020-02-04 10:46:42 +0900177 }
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900178 return prev_default;
Kevin Cernekee95d4ae92016-06-19 10:26:29 -0700179}
180
181void ShillClient::RegisterDefaultInterfaceChangedHandler(
Garrick Evans139708f2020-02-06 14:38:59 +0900182 const DefaultInterfaceChangeHandler& handler) {
183 default_interface_handlers_.emplace_back(handler);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900184 const auto prev_default = SetDefaultInterface(GetDefaultInterface());
Garrick Evans139708f2020-02-06 14:38:59 +0900185 handler.Run(default_interface_, prev_default);
Garrick Evans49879532018-12-03 13:15:36 +0900186}
187
188void ShillClient::RegisterDevicesChangedHandler(
Garrick Evans139708f2020-02-06 14:38:59 +0900189 const DevicesChangeHandler& handler) {
190 device_handlers_.emplace_back(handler);
Garrick Evans49879532018-12-03 13:15:36 +0900191}
192
Garrick Evans139708f2020-02-06 14:38:59 +0900193void ShillClient::UpdateDevices(const brillo::Any& property_value) {
194 std::set<std::string> new_devices, added, removed;
195 for (const auto& path :
196 property_value.TryGet<std::vector<dbus::ObjectPath>>()) {
197 std::string device = path.value();
198 // Strip "/device/" prefix.
199 device = device.substr(device.find_last_of('/') + 1);
Garrick Evanse87bb4f2020-02-18 10:27:37 +0900200
Garrick Evans139708f2020-02-06 14:38:59 +0900201 new_devices.emplace(device);
202 if (devices_.find(device) == devices_.end())
203 added.insert(device);
204 }
205
206 for (const auto& d : devices_) {
207 if (new_devices.find(d) == new_devices.end())
208 removed.insert(d);
209 }
210
211 devices_ = new_devices;
212
213 for (const auto& h : device_handlers_)
214 h.Run(added, removed);
Garrick Evans49879532018-12-03 13:15:36 +0900215}
216
Garrick Evans3388a032020-03-24 11:25:55 +0900217} // namespace patchpanel