blob: 03e7f18aa4155156f0ff4e439ef1ce872a4f5b13 [file] [log] [blame]
Gilad Arnold55f39b72014-01-28 12:51:45 -08001// Copyright (c) 2014 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
Gilad Arnold55f39b72014-01-28 12:51:45 -08005#include "update_engine/policy_manager/real_shill_provider.h"
6
Gilad Arnold5ef9c482014-03-03 13:51:02 -08007#include <string>
8
9#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070010#include <base/strings/stringprintf.h>
Gilad Arnold5ef9c482014-03-03 13:51:02 -080011#include <chromeos/dbus/service_constants.h>
12
13#include "update_engine/policy_manager/generic_variables.h"
14#include "update_engine/utils.h"
15
16using std::string;
17
18namespace {
19
Gilad Arnolddf3dd242014-04-09 07:15:51 -070020const char* kConnInfoNotAvailErrMsg = "Connection information not available";
21
Gilad Arnold5ef9c482014-03-03 13:51:02 -080022// Looks up a key in a hash table and returns the string inside of the returned
23// GValue.
24const char* GetStrProperty(GHashTable* hash_table, const char* key) {
25 auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
26 return (gval ? g_value_get_string(gval) : NULL);
27}
28
29}; // namespace
30
31
Gilad Arnold55f39b72014-01-28 12:51:45 -080032namespace chromeos_policy_manager {
33
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070034// ShillConnector methods.
35
Gilad Arnold5ef9c482014-03-03 13:51:02 -080036const ShillConnector::ConnStrToType ShillConnector::shill_conn_str_to_type[] = {
Gilad Arnoldaf309d52014-03-13 11:21:55 -070037 {shill::kTypeEthernet, ConnectionType::kEthernet},
38 {shill::kTypeWifi, ConnectionType::kWifi},
39 {shill::kTypeWimax, ConnectionType::kWimax},
40 {shill::kTypeBluetooth, ConnectionType::kBluetooth},
41 {shill::kTypeCellular, ConnectionType::kCellular},
Gilad Arnold5ef9c482014-03-03 13:51:02 -080042};
Gilad Arnold55f39b72014-01-28 12:51:45 -080043
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070044ShillConnector::~ShillConnector() {
45 if (!is_init_)
46 return;
47
48 // Detach signal handler, free manager proxy.
49 dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
50 G_CALLBACK(signal_handler_), signal_data_);
51 dbus_->ProxyUnref(manager_proxy_);
52}
53
Gilad Arnold5ef9c482014-03-03 13:51:02 -080054bool ShillConnector::Init() {
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070055 if (is_init_)
56 return true;
57
58 // Obtain a DBus connection.
Gilad Arnold5ef9c482014-03-03 13:51:02 -080059 GError* error = NULL;
60 connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
61 if (!connection_) {
62 LOG(ERROR) << "Failed to initialize DBus connection: "
63 << chromeos_update_engine::utils::GetAndFreeGError(&error);
64 return false;
65 }
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070066
67 // Allocate a shill manager proxy.
Gilad Arnold5ef9c482014-03-03 13:51:02 -080068 manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
69 shill::kFlimflamManagerInterface);
Gilad Arnold55f39b72014-01-28 12:51:45 -080070
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070071 // Subscribe to the manager's PropertyChanged signal.
72 dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
73 G_TYPE_STRING, G_TYPE_VALUE);
74 dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
75 G_CALLBACK(signal_handler_), signal_data_, NULL);
Gilad Arnold5ef9c482014-03-03 13:51:02 -080076
Gilad Arnoldbeb39e92014-03-11 11:34:50 -070077 return is_init_ = true;
Gilad Arnold5ef9c482014-03-03 13:51:02 -080078}
79
80bool ShillConnector::GetConnectionType(const string& service_path,
Gilad Arnoldaf309d52014-03-13 11:21:55 -070081 ConnectionType* conn_type_p) {
Gilad Arnold5ef9c482014-03-03 13:51:02 -080082 // Obtain a proxy for the service path.
83 DBusGProxy* service_proxy = GetProxy(service_path.c_str(),
84 shill::kFlimflamServiceInterface);
85
86 GHashTable* hash_table = NULL;
87 bool success = false;
88 bool is_vpn = false;
89 if (GetProperties(service_proxy, &hash_table)) {
90 const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
91 if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
92 is_vpn = true;
93 type_str = GetStrProperty(hash_table, shill::kPhysicalTechnologyProperty);
94 }
95 if (type_str) {
96 success = true;
Alex Deymoc83baf62014-04-02 17:43:35 -070097 *conn_type_p = ParseConnectionType(type_str);
Gilad Arnold5ef9c482014-03-03 13:51:02 -080098 }
99 g_hash_table_unref(hash_table);
100 }
101
102 if (!success) {
103 LOG(ERROR) << "Could not find type of "
104 << (is_vpn ? "physical connection underlying VPN " : "")
105 << "connection (" << service_path << ")";
106 }
107
108 dbus_->ProxyUnref(service_proxy);
109 return success;
110}
111
112DBusGProxy* ShillConnector::GetProxy(const char* path, const char* interface) {
113 return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
114 path, interface);
115}
116
Alex Deymoc83baf62014-04-02 17:43:35 -0700117ConnectionType ShillConnector::ParseConnectionType(const char* str) {
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800118 for (unsigned i = 0; i < arraysize(shill_conn_str_to_type); i++)
119 if (!strcmp(str, shill_conn_str_to_type[i].str))
120 return shill_conn_str_to_type[i].type;
121
Gilad Arnoldaf309d52014-03-13 11:21:55 -0700122 return ConnectionType::kUnknown;
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800123}
124
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800125bool ShillConnector::GetProperties(DBusGProxy* proxy, GHashTable** result_p) {
126 GError* error = NULL;
127 if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
128 result_p)) {
129 LOG(ERROR) << "Calling shill via DBus proxy failed: "
130 << chromeos_update_engine::utils::GetAndFreeGError(&error);
131 return false;
132 }
133 return true;
134}
135
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800136
137// A variable returning the curent connection type.
Gilad Arnoldaf309d52014-03-13 11:21:55 -0700138class ConnTypeVariable : public Variable<ConnectionType> {
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800139 public:
140 ConnTypeVariable(const string& name, ShillConnector* connector,
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700141 RealShillProvider* provider)
Gilad Arnoldaf309d52014-03-13 11:21:55 -0700142 : Variable<ConnectionType>(name, kVariableModePoll),
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700143 connector_(connector), provider_(provider) {}
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800144
145 protected:
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700146 // TODO(garnold) Shift to a non-blocking version, respect the timeout.
Gilad Arnoldaf309d52014-03-13 11:21:55 -0700147 virtual const ConnectionType* GetValue(base::TimeDelta /* timeout */,
148 string* errmsg) {
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700149 if (!(provider_->is_conn_status_init_)) {
150 if (errmsg)
151 *errmsg = kConnInfoNotAvailErrMsg;
152 return NULL;
153 }
154
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700155 if (!(provider_->is_connected_)) {
156 if (errmsg)
157 *errmsg = "No connection detected";
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800158 return NULL;
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700159 }
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800160
Gilad Arnoldecc7f792014-04-09 12:56:13 -0700161 if (!provider_->is_conn_type_valid_) {
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700162 if (!connector_->GetConnectionType(provider_->default_service_path_,
Gilad Arnoldecc7f792014-04-09 12:56:13 -0700163 &provider_->conn_type_)) {
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700164 if (errmsg)
Alex Vakulenko75039d72014-03-25 12:36:28 -0700165 *errmsg = base::StringPrintf(
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700166 "Could not retrieve type of default connection (%s)",
167 provider_->default_service_path_.c_str());
168 return NULL;
169 }
170 provider_->is_conn_type_valid_ = true;
171 }
172
Gilad Arnoldecc7f792014-04-09 12:56:13 -0700173 return new ConnectionType(provider_->conn_type_);
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800174 }
175
176 private:
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700177 // The DBus connector.
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800178 ShillConnector* connector_;
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700179
180 // The shill provider object (we need to read/update some internal members).
181 RealShillProvider* provider_;
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800182
183 DISALLOW_COPY_AND_ASSIGN(ConnTypeVariable);
184};
185
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700186
187// RealShillProvider methods.
188
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800189bool RealShillProvider::DoInit() {
190 // Initialize a DBus connection and obtain the shill manager proxy.
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700191 connector_.reset(new ShillConnector(dbus_, HandlePropertyChangedStatic,
192 this));
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800193 if (!connector_->Init())
194 return false;
195
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700196 // Attempt to read initial connection status. Even if this fails because shill
197 // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
198 // signal as soon as it comes up, so this is not a critical step.
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700199 GHashTable* hash_table = NULL;
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700200 if (connector_->GetManagerProperties(&hash_table)) {
201 GValue* value = reinterpret_cast<GValue*>(
202 g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
203 ProcessDefaultService(value);
204 g_hash_table_unref(hash_table);
205 }
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700206
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800207 // Initialize variables.
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700208 var_is_connected_.reset(
209 new CopyVariable<bool>("is_connected", kVariableModePoll, is_connected_,
210 &is_conn_status_init_, kConnInfoNotAvailErrMsg));
211 var_conn_type_.reset(
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700212 new ConnTypeVariable("conn_type", connector_.get(), this));
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700213 var_conn_last_changed_.reset(
Gilad Arnold5ef9c482014-03-03 13:51:02 -0800214 new CopyVariable<base::Time>("conn_last_changed", kVariableModePoll,
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700215 conn_last_changed_, &is_conn_status_init_,
216 kConnInfoNotAvailErrMsg));
Gilad Arnold55f39b72014-01-28 12:51:45 -0800217 return true;
218}
219
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700220bool RealShillProvider::ProcessDefaultService(GValue* value) {
221 // Decode the string from the boxed value.
222 const char* default_service_path_str = NULL;
223 if (!(value && (default_service_path_str = g_value_get_string(value))))
224 return false;
225
226 // Update the connection status.
227 is_connected_ = strcmp(default_service_path_str, "/");
228 if (default_service_path_ != default_service_path_str)
229 conn_last_changed_ = clock_->GetWallclockTime();
230 default_service_path_ = default_service_path_str;
231 is_conn_type_valid_ = false;
Gilad Arnolddf3dd242014-04-09 07:15:51 -0700232
233 // Mark the connection status as initialized.
234 is_conn_status_init_ = true;
Gilad Arnoldbeb39e92014-03-11 11:34:50 -0700235 return true;
236}
237
238void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
239 const char* name, GValue* value) {
240 if (!strcmp(name, shill::kDefaultServiceProperty))
241 ProcessDefaultService(value);
242}
243
244void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
245 const char* name,
246 GValue* value,
247 void* data) {
248 auto obj = reinterpret_cast<RealShillProvider*>(data);
249 obj->HandlePropertyChanged(proxy, name, value);
250}
251
Gilad Arnold55f39b72014-01-28 12:51:45 -0800252} // namespace chromeos_policy_manager