1 // Copyright (c) 2012 The Chromium 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 5 #include "chromeos/network/network_configuration_handler.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/format_macros.h" 12 #include "base/json/json_writer.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/stl_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/values.h" 19 #include "chromeos/dbus/dbus_thread_manager.h" 20 #include "chromeos/dbus/shill_manager_client.h" 21 #include "chromeos/dbus/shill_profile_client.h" 22 #include "chromeos/dbus/shill_service_client.h" 23 #include "chromeos/network/network_event_log.h" 24 #include "chromeos/network/network_state.h" 25 #include "chromeos/network/network_state_handler.h" 26 #include "chromeos/network/shill_property_util.h" 27 #include "dbus/object_path.h" 28 #include "third_party/cros_system_api/dbus/service_constants.h" 29 30 namespace chromeos { 31 32 namespace { 33 34 // Strip surrounding "" from keys (if present). 35 std::string StripQuotations(const std::string& in_str) { 36 size_t len = in_str.length(); 37 if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"') 38 return in_str.substr(1, len-2); 39 return in_str; 40 } 41 42 void InvokeErrorCallback(const std::string& service_path, 43 const network_handler::ErrorCallback& error_callback, 44 const std::string& error_name) { 45 std::string error_msg = "Config Error: " + error_name; 46 NET_LOG_ERROR(error_msg, service_path); 47 network_handler::RunErrorCallback( 48 error_callback, service_path, error_name, error_msg); 49 } 50 51 void GetPropertiesCallback( 52 const network_handler::DictionaryResultCallback& callback, 53 const network_handler::ErrorCallback& error_callback, 54 const std::string& service_path, 55 DBusMethodCallStatus call_status, 56 const base::DictionaryValue& properties) { 57 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 58 // Because network services are added and removed frequently, we will see 59 // failures regularly, so don't log these. 60 network_handler::RunErrorCallback(error_callback, 61 service_path, 62 network_handler::kDBusFailedError, 63 network_handler::kDBusFailedErrorMessage); 64 return; 65 } 66 if (callback.is_null()) 67 return; 68 69 // Get the correct name from WifiHex if necessary. 70 scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy()); 71 std::string name = 72 shill_property_util::GetNameFromProperties(service_path, properties); 73 if (!name.empty()) 74 properties_copy->SetStringWithoutPathExpansion(shill::kNameProperty, name); 75 callback.Run(service_path, *properties_copy.get()); 76 } 77 78 void SetNetworkProfileErrorCallback( 79 const std::string& service_path, 80 const std::string& profile_path, 81 const network_handler::ErrorCallback& error_callback, 82 const std::string& dbus_error_name, 83 const std::string& dbus_error_message) { 84 network_handler::ShillErrorCallbackFunction( 85 "Config.SetNetworkProfile Failed: " + profile_path, 86 service_path, error_callback, 87 dbus_error_name, dbus_error_message); 88 } 89 90 void LogConfigProperties(const std::string& desc, 91 const std::string& path, 92 const base::DictionaryValue& properties) { 93 for (base::DictionaryValue::Iterator iter(properties); 94 !iter.IsAtEnd(); iter.Advance()) { 95 std::string v = "******"; 96 if (!shill_property_util::IsPassphraseKey(iter.key())) 97 base::JSONWriter::Write(&iter.value(), &v); 98 NET_LOG_DEBUG(desc, path + "." + iter.key() + "=" + v); 99 } 100 } 101 102 } // namespace 103 104 // Helper class to request from Shill the profile entries associated with a 105 // Service and delete the service from each profile. Triggers either 106 // |callback| on success or |error_callback| on failure, and calls 107 // |handler|->ProfileEntryDeleterCompleted() on completion to delete itself. 108 class NetworkConfigurationHandler::ProfileEntryDeleter 109 : public base::SupportsWeakPtr<ProfileEntryDeleter> { 110 public: 111 ProfileEntryDeleter(NetworkConfigurationHandler* handler, 112 const std::string& service_path, 113 const base::Closure& callback, 114 const network_handler::ErrorCallback& error_callback) 115 : owner_(handler), 116 service_path_(service_path), 117 callback_(callback), 118 error_callback_(error_callback) { 119 } 120 121 void Run() { 122 DBusThreadManager::Get()->GetShillServiceClient()-> 123 GetLoadableProfileEntries( 124 dbus::ObjectPath(service_path_), 125 base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback, 126 AsWeakPtr())); 127 } 128 129 private: 130 void GetProfileEntriesToDeleteCallback( 131 DBusMethodCallStatus call_status, 132 const base::DictionaryValue& profile_entries) { 133 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 134 InvokeErrorCallback( 135 service_path_, error_callback_, "GetLoadableProfileEntriesFailed"); 136 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 137 return; 138 } 139 140 for (base::DictionaryValue::Iterator iter(profile_entries); 141 !iter.IsAtEnd(); iter.Advance()) { 142 std::string profile_path = StripQuotations(iter.key()); 143 std::string entry_path; 144 iter.value().GetAsString(&entry_path); 145 if (profile_path.empty() || entry_path.empty()) { 146 NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf( 147 "%s: %s", profile_path.c_str(), entry_path.c_str())); 148 continue; 149 } 150 if (profile_delete_entries_.count(profile_path) != 0) { 151 NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf( 152 "%s: %s", profile_path.c_str(), entry_path.c_str())); 153 continue; 154 } 155 NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf( 156 "%s: %s", profile_path.c_str(), entry_path.c_str())); 157 profile_delete_entries_[profile_path] = entry_path; 158 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( 159 dbus::ObjectPath(profile_path), 160 entry_path, 161 base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback, 162 AsWeakPtr(), profile_path, entry_path), 163 base::Bind(&ProfileEntryDeleter::ShillErrorCallback, 164 AsWeakPtr(), profile_path, entry_path)); 165 } 166 } 167 168 void ProfileEntryDeletedCallback(const std::string& profile_path, 169 const std::string& entry) { 170 NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf( 171 "%s: %s", profile_path.c_str(), entry.c_str())); 172 profile_delete_entries_.erase(profile_path); 173 if (!profile_delete_entries_.empty()) 174 return; 175 // Run the callback if this is the last pending deletion. 176 if (!callback_.is_null()) 177 callback_.Run(); 178 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 179 } 180 181 void ShillErrorCallback(const std::string& profile_path, 182 const std::string& entry, 183 const std::string& dbus_error_name, 184 const std::string& dbus_error_message) { 185 // Any Shill Error triggers a failure / error. 186 network_handler::ShillErrorCallbackFunction( 187 "GetLoadableProfileEntries Failed", profile_path, error_callback_, 188 dbus_error_name, dbus_error_message); 189 // Delete this even if there are pending deletions; any callbacks will 190 // safely become no-ops (by invalidating the WeakPtrs). 191 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 192 } 193 194 NetworkConfigurationHandler* owner_; // Unowned 195 std::string service_path_; 196 base::Closure callback_; 197 network_handler::ErrorCallback error_callback_; 198 199 // Map of pending profile entry deletions, indexed by profile path. 200 std::map<std::string, std::string> profile_delete_entries_; 201 202 DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter); 203 }; 204 205 // NetworkConfigurationHandler 206 207 void NetworkConfigurationHandler::GetProperties( 208 const std::string& service_path, 209 const network_handler::DictionaryResultCallback& callback, 210 const network_handler::ErrorCallback& error_callback) const { 211 NET_LOG_USER("GetProperties", service_path); 212 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 213 dbus::ObjectPath(service_path), 214 base::Bind(&GetPropertiesCallback, 215 callback, error_callback, service_path)); 216 } 217 218 void NetworkConfigurationHandler::SetProperties( 219 const std::string& service_path, 220 const base::DictionaryValue& properties, 221 const base::Closure& callback, 222 const network_handler::ErrorCallback& error_callback) { 223 if (properties.empty()) { 224 if (!callback.is_null()) 225 callback.Run(); 226 return; 227 } 228 NET_LOG_USER("SetProperties", service_path); 229 LogConfigProperties("SetProperty", service_path, properties); 230 231 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( 232 dbus::ObjectPath(service_path), 233 properties, 234 base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback, 235 AsWeakPtr(), service_path, callback), 236 base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback, 237 AsWeakPtr(), service_path, error_callback)); 238 } 239 240 void NetworkConfigurationHandler::ClearProperties( 241 const std::string& service_path, 242 const std::vector<std::string>& names, 243 const base::Closure& callback, 244 const network_handler::ErrorCallback& error_callback) { 245 if (names.empty()) { 246 if (!callback.is_null()) 247 callback.Run(); 248 return; 249 } 250 NET_LOG_USER("ClearProperties", service_path); 251 for (std::vector<std::string>::const_iterator iter = names.begin(); 252 iter != names.end(); ++iter) { 253 NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter); 254 } 255 DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties( 256 dbus::ObjectPath(service_path), 257 names, 258 base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback, 259 AsWeakPtr(), service_path, names, callback), 260 base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback, 261 AsWeakPtr(), service_path, error_callback)); 262 } 263 264 void NetworkConfigurationHandler::CreateConfiguration( 265 const base::DictionaryValue& properties, 266 const network_handler::StringResultCallback& callback, 267 const network_handler::ErrorCallback& error_callback) { 268 ShillManagerClient* manager = 269 DBusThreadManager::Get()->GetShillManagerClient(); 270 std::string type; 271 properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); 272 DCHECK(!type.empty()); 273 if (NetworkTypePattern::Ethernet().MatchesType(type)) { 274 InvokeErrorCallback( 275 shill_property_util::GetNetworkIdFromProperties(properties), 276 error_callback, 277 "ConfigureServiceForProfile: Invalid type: " + type); 278 return; 279 } 280 281 NET_LOG_USER("CreateConfiguration: " + type, 282 shill_property_util::GetNetworkIdFromProperties(properties)); 283 LogConfigProperties("Configure", type, properties); 284 285 std::string profile; 286 properties.GetStringWithoutPathExpansion(shill::kProfileProperty, 287 &profile); 288 DCHECK(!profile.empty()); 289 manager->ConfigureServiceForProfile( 290 dbus::ObjectPath(profile), 291 properties, 292 base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback, 293 AsWeakPtr(), 294 callback), 295 base::Bind(&network_handler::ShillErrorCallbackFunction, 296 "Config.CreateConfiguration Failed", 297 "", 298 error_callback)); 299 } 300 301 void NetworkConfigurationHandler::RemoveConfiguration( 302 const std::string& service_path, 303 const base::Closure& callback, 304 const network_handler::ErrorCallback& error_callback) { 305 // Service.Remove is not reliable. Instead, request the profile entries 306 // for the service and remove each entry. 307 if (ContainsKey(profile_entry_deleters_, service_path)) { 308 InvokeErrorCallback( 309 service_path, error_callback, "RemoveConfigurationInProgress"); 310 return; 311 } 312 313 NET_LOG_USER("Remove Configuration", service_path); 314 ProfileEntryDeleter* deleter = 315 new ProfileEntryDeleter(this, service_path, callback, error_callback); 316 profile_entry_deleters_[service_path] = deleter; 317 deleter->Run(); 318 } 319 320 void NetworkConfigurationHandler::SetNetworkProfile( 321 const std::string& service_path, 322 const std::string& profile_path, 323 const base::Closure& callback, 324 const network_handler::ErrorCallback& error_callback) { 325 NET_LOG_USER("SetNetworkProfile", service_path + ": " + profile_path); 326 base::StringValue profile_path_value(profile_path); 327 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 328 dbus::ObjectPath(service_path), 329 shill::kProfileProperty, 330 profile_path_value, 331 callback, 332 base::Bind(&SetNetworkProfileErrorCallback, 333 service_path, profile_path, error_callback)); 334 } 335 336 // NetworkConfigurationHandler Private methods 337 338 NetworkConfigurationHandler::NetworkConfigurationHandler() 339 : network_state_handler_(NULL) { 340 } 341 342 NetworkConfigurationHandler::~NetworkConfigurationHandler() { 343 STLDeleteContainerPairSecondPointers( 344 profile_entry_deleters_.begin(), profile_entry_deleters_.end()); 345 } 346 347 void NetworkConfigurationHandler::Init( 348 NetworkStateHandler* network_state_handler) { 349 network_state_handler_ = network_state_handler; 350 } 351 352 void NetworkConfigurationHandler::RunCreateNetworkCallback( 353 const network_handler::StringResultCallback& callback, 354 const dbus::ObjectPath& service_path) { 355 if (!callback.is_null()) 356 callback.Run(service_path.value()); 357 // This may also get called when CreateConfiguration is used to update an 358 // existing configuration, so request a service update just in case. 359 // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger 360 // this on an update. 361 network_state_handler_->RequestUpdateForNetwork(service_path.value()); 362 } 363 364 void NetworkConfigurationHandler::ProfileEntryDeleterCompleted( 365 const std::string& service_path) { 366 std::map<std::string, ProfileEntryDeleter*>::iterator iter = 367 profile_entry_deleters_.find(service_path); 368 DCHECK(iter != profile_entry_deleters_.end()); 369 delete iter->second; 370 profile_entry_deleters_.erase(iter); 371 } 372 373 void NetworkConfigurationHandler::SetPropertiesSuccessCallback( 374 const std::string& service_path, 375 const base::Closure& callback) { 376 if (!callback.is_null()) 377 callback.Run(); 378 network_state_handler_->RequestUpdateForNetwork(service_path); 379 } 380 381 void NetworkConfigurationHandler::SetPropertiesErrorCallback( 382 const std::string& service_path, 383 const network_handler::ErrorCallback& error_callback, 384 const std::string& dbus_error_name, 385 const std::string& dbus_error_message) { 386 network_handler::ShillErrorCallbackFunction( 387 "Config.SetProperties Failed", 388 service_path, error_callback, 389 dbus_error_name, dbus_error_message); 390 // Some properties may have changed so request an update regardless. 391 network_state_handler_->RequestUpdateForNetwork(service_path); 392 } 393 394 void NetworkConfigurationHandler::ClearPropertiesSuccessCallback( 395 const std::string& service_path, 396 const std::vector<std::string>& names, 397 const base::Closure& callback, 398 const base::ListValue& result) { 399 const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed"); 400 DCHECK(names.size() == result.GetSize()) 401 << "Incorrect result size from ClearProperties."; 402 403 for (size_t i = 0; i < result.GetSize(); ++i) { 404 bool success = false; 405 result.GetBoolean(i, &success); 406 if (!success) { 407 // If a property was cleared that has never been set, the clear will fail. 408 // We do not track which properties have been set, so just log the error. 409 NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path); 410 } 411 } 412 413 if (!callback.is_null()) 414 callback.Run(); 415 network_state_handler_->RequestUpdateForNetwork(service_path); 416 } 417 418 void NetworkConfigurationHandler::ClearPropertiesErrorCallback( 419 const std::string& service_path, 420 const network_handler::ErrorCallback& error_callback, 421 const std::string& dbus_error_name, 422 const std::string& dbus_error_message) { 423 network_handler::ShillErrorCallbackFunction( 424 "Config.ClearProperties Failed", 425 service_path, error_callback, 426 dbus_error_name, dbus_error_message); 427 // Some properties may have changed so request an update regardless. 428 network_state_handler_->RequestUpdateForNetwork(service_path); 429 } 430 431 // static 432 NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest( 433 NetworkStateHandler* network_state_handler) { 434 NetworkConfigurationHandler* handler = new NetworkConfigurationHandler(); 435 handler->Init(network_state_handler); 436 return handler; 437 } 438 439 } // namespace chromeos 440