1 // 2 // Copyright (C) 2015 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "shill/json_store.h" 18 19 #include <fcntl.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #include <cinttypes> 25 #include <map> 26 #include <memory> 27 #include <typeinfo> 28 #include <vector> 29 30 #include <base/files/important_file_writer.h> 31 #include <base/files/file_util.h> 32 #include <base/json/json_string_value_serializer.h> 33 #include <base/memory/scoped_ptr.h> 34 #include <base/strings/string_number_conversions.h> 35 #include <base/strings/string_util.h> 36 #include <base/strings/stringprintf.h> 37 #include <base/values.h> 38 39 #include "shill/crypto_rot47.h" 40 #include "shill/logging.h" 41 #include "shill/scoped_umask.h" 42 43 using std::map; 44 using std::set; 45 using std::string; 46 using std::unique_ptr; 47 using std::vector; 48 49 namespace shill { 50 51 namespace Logging { 52 53 static auto kModuleLogScope = ScopeLogger::kStorage; 54 static string ObjectID(const JsonStore* j) { 55 return "(unknown)"; 56 } 57 58 } // namespace Logging 59 60 namespace { 61 62 static const char kCorruptSuffix[] = ".corrupted"; 63 static const char kCoercedValuePropertyEncodedValue[] = "_encoded_value"; 64 static const char kCoercedValuePropertyNativeType[] = "_native_type"; 65 static const char kNativeTypeNonAsciiString[] = "non_ascii_string"; 66 static const char kNativeTypeUint64[] = "uint64"; 67 static const char kRootPropertyDescription[] = "description"; 68 static const char kRootPropertySettings[] = "settings"; 69 70 bool DoesGroupContainProperties( 71 const brillo::VariantDictionary& group, 72 const brillo::VariantDictionary& required_properties) { 73 for (const auto& required_property_name_and_value : required_properties) { 74 const auto& required_key = required_property_name_and_value.first; 75 const auto& required_value = required_property_name_and_value.second; 76 const auto& group_it = group.find(required_key); 77 if (group_it == group.end() || group_it->second != required_value) { 78 return false; 79 } 80 } 81 return true; 82 } 83 84 // Deserialization helpers. 85 86 // A coerced value is used to represent values that base::Value does 87 // not directly support. A coerced value has the form 88 // {'_native_type': <type-as-string>, '_encoded_value': <value-as-string>} 89 bool IsCoercedValue(const base::DictionaryValue& value) { 90 return value.HasKey(kCoercedValuePropertyNativeType) && 91 value.HasKey(kCoercedValuePropertyEncodedValue); 92 } 93 94 unique_ptr<brillo::Any> DecodeCoercedValue( 95 const base::DictionaryValue& coerced_value) { 96 string native_type; 97 if (!coerced_value.GetStringWithoutPathExpansion( 98 kCoercedValuePropertyNativeType, &native_type)) { 99 LOG(ERROR) << "Property |" << kCoercedValuePropertyNativeType 100 << "| is not a string."; 101 return nullptr; 102 } 103 104 string encoded_value; 105 if (!coerced_value.GetStringWithoutPathExpansion( 106 kCoercedValuePropertyEncodedValue, &encoded_value)) { 107 LOG(ERROR) << "Property |" << kCoercedValuePropertyEncodedValue 108 << "| is not a string."; 109 return nullptr; 110 } 111 112 if (native_type == kNativeTypeNonAsciiString) { 113 vector<uint8_t> native_value; 114 if (base::HexStringToBytes(encoded_value, &native_value)) { 115 return unique_ptr<brillo::Any>( 116 new brillo::Any(string(native_value.begin(), native_value.end()))); 117 } else { 118 LOG(ERROR) << "Failed to decode hex data from |" << encoded_value << "|."; 119 return nullptr; 120 } 121 } else if (native_type == kNativeTypeUint64) { 122 uint64_t native_value; 123 if (base::StringToUint64(encoded_value, &native_value)) { 124 return unique_ptr<brillo::Any>(new brillo::Any(native_value)); 125 } else { 126 LOG(ERROR) << "Failed to parse uint64 from |" << encoded_value << "|."; 127 return nullptr; 128 } 129 } else { 130 LOG(ERROR) << "Unsupported native type |" << native_type << "|."; 131 return nullptr; 132 } 133 } 134 135 unique_ptr<string> MakeStringFromValue(const base::Value& value) { 136 const auto value_type = value.GetType(); 137 138 if (value_type == base::Value::TYPE_STRING) { 139 unique_ptr<string> unwrapped_string(new string()); 140 value.GetAsString(unwrapped_string.get()); 141 return unwrapped_string; 142 } else if (value_type == base::Value::TYPE_DICTIONARY) { 143 const base::DictionaryValue* dictionary_value; 144 value.GetAsDictionary(&dictionary_value); 145 unique_ptr<brillo::Any> decoded_value( 146 DecodeCoercedValue(*dictionary_value)); 147 if (!decoded_value) { 148 LOG(ERROR) << "Failed to decode coerced value."; 149 return nullptr; 150 } 151 152 if (!decoded_value->IsTypeCompatible<string>()) { 153 LOG(ERROR) << "Can not read |" << brillo::GetUndecoratedTypeName<string>() 154 << "| from |" << decoded_value->GetUndecoratedTypeName() 155 << "."; 156 return nullptr; 157 } 158 return unique_ptr<string>(new string(decoded_value->Get<string>())); 159 } else { 160 LOG(ERROR) << "Got unexpected type |" << value_type << "|."; 161 return nullptr; 162 } 163 } 164 165 unique_ptr<vector<string>> ConvertListValueToStringVector( 166 const base::ListValue& list_value) { 167 const size_t list_len = list_value.GetSize(); 168 for (size_t i = 0; i < list_len; ++i) { 169 const base::Value* list_item; 170 list_value.Get(i, &list_item); 171 const auto item_type = list_item->GetType(); 172 if (item_type != base::Value::TYPE_STRING && 173 item_type != base::Value::TYPE_DICTIONARY) { 174 LOG(ERROR) << "Element " << i << " has type " << item_type << ", " 175 << "instead of expected types " 176 << base::Value::TYPE_STRING << " or " 177 << base::Value::TYPE_DICTIONARY << "."; 178 return nullptr; 179 } 180 } 181 182 unique_ptr<vector<string>> result(new vector<string>); 183 for (size_t i = 0; i < list_len; ++i) { 184 const base::Value* list_item; 185 unique_ptr<string> native_string; 186 list_value.Get(i, &list_item); 187 native_string = MakeStringFromValue(*list_item); 188 if (!native_string) { 189 LOG(ERROR) << "Failed to parse string from element " << i << "."; 190 return nullptr; 191 } 192 result->push_back(*native_string); 193 } 194 return result; 195 } 196 197 unique_ptr<brillo::VariantDictionary> 198 ConvertDictionaryValueToVariantDictionary( 199 const base::DictionaryValue& dictionary_value) { 200 base::DictionaryValue::Iterator it(dictionary_value); 201 unique_ptr<brillo::VariantDictionary> variant_dictionary( 202 new brillo::VariantDictionary()); 203 while (!it.IsAtEnd()) { 204 const string& key = it.key(); 205 const base::Value& value = it.value(); 206 switch (value.GetType()) { 207 case base::Value::TYPE_NULL: 208 LOG(ERROR) << "Key |" << key << "| has unsupported TYPE_NULL."; 209 return nullptr; 210 case base::Value::TYPE_BOOLEAN: { 211 bool native_bool; 212 value.GetAsBoolean(&native_bool); 213 (*variant_dictionary)[key] = native_bool; 214 break; 215 } 216 case base::Value::TYPE_INTEGER: { 217 int native_int; 218 value.GetAsInteger(&native_int); 219 (*variant_dictionary)[key] = native_int; 220 break; 221 } 222 case base::Value::TYPE_DOUBLE: 223 LOG(ERROR) << "Key |" << key << "| has unsupported TYPE_DOUBLE."; 224 return nullptr; 225 case base::Value::TYPE_STRING: { 226 string native_string; 227 value.GetAsString(&native_string); 228 (*variant_dictionary)[key] = native_string; 229 break; 230 } 231 case base::Value::TYPE_BINARY: 232 /* The JSON parser should never create Values of this type. */ 233 LOG(ERROR) << "Key |" << key << "| has unexpected TYPE_BINARY."; 234 return nullptr; 235 case base::Value::TYPE_DICTIONARY: { 236 const base::DictionaryValue* dictionary_value; 237 value.GetAsDictionary(&dictionary_value); 238 if (!IsCoercedValue(*dictionary_value)) { 239 LOG(ERROR) << "Key |" << key << "| has unsupported TYPE_DICTIONARY."; 240 return nullptr; 241 } 242 unique_ptr<brillo::Any> decoded_coerced_value( 243 DecodeCoercedValue(*dictionary_value)); 244 if (!decoded_coerced_value) { 245 LOG(ERROR) << "Key |" << key << "| could not be decoded."; 246 return nullptr; 247 } 248 (*variant_dictionary)[key] = *decoded_coerced_value; 249 break; 250 } 251 case base::Value::TYPE_LIST: { // Only string lists, for now. 252 const base::ListValue* list_value; 253 value.GetAsList(&list_value); 254 255 unique_ptr<vector<string>> string_list( 256 ConvertListValueToStringVector(*list_value)); 257 if (!string_list) { 258 LOG(ERROR) << "Key |" << key << "| could not be decoded."; 259 return nullptr; 260 } 261 (*variant_dictionary)[key] = *string_list; 262 break; 263 } 264 } 265 it.Advance(); 266 } 267 return variant_dictionary; 268 } 269 270 // Serialization helpers. 271 272 scoped_ptr<base::DictionaryValue> MakeCoercedValue( 273 const string& native_type, const string& encoded_value) { 274 auto coerced_value(make_scoped_ptr(new base::DictionaryValue())); 275 coerced_value->SetStringWithoutPathExpansion( 276 kCoercedValuePropertyNativeType, native_type); 277 coerced_value->SetStringWithoutPathExpansion( 278 kCoercedValuePropertyEncodedValue, encoded_value); 279 return coerced_value; 280 } 281 282 scoped_ptr<base::Value> MakeValueForString(const string& native_string) { 283 // Strictly speaking, we don't need to escape non-ASCII text, if 284 // that text is UTF-8. Practically speaking, however, it'll be 285 // easier to inspect config files if all non-ASCII strings are 286 // presented as byte sequences. (Unicode has many code points with 287 // similar-looking glyphs.) 288 if (base::IsStringASCII(native_string) && 289 native_string.find('\0') == string::npos) { 290 return make_scoped_ptr(new base::StringValue(native_string)); 291 } else { 292 const string hex_encoded_string( 293 base::HexEncode(native_string.data(), native_string.size())); 294 return MakeCoercedValue(kNativeTypeNonAsciiString, hex_encoded_string); 295 } 296 } 297 298 scoped_ptr<base::DictionaryValue> ConvertVariantDictionaryToDictionaryValue( 299 const brillo::VariantDictionary& variant_dictionary) { 300 auto dictionary_value(make_scoped_ptr(new base::DictionaryValue())); 301 for (const auto& key_and_value : variant_dictionary) { 302 const auto& key = key_and_value.first; 303 const auto& value = key_and_value.second; 304 if (value.IsTypeCompatible<bool>()) { 305 dictionary_value->SetBooleanWithoutPathExpansion(key, value.Get<bool>()); 306 } else if (value.IsTypeCompatible<int32_t>()) { 307 dictionary_value->SetIntegerWithoutPathExpansion(key, value.Get<int>()); 308 } else if (value.IsTypeCompatible<string>()) { 309 dictionary_value->SetWithoutPathExpansion( 310 key, MakeValueForString(value.Get<string>())); 311 } else if (value.IsTypeCompatible<uint64_t>()) { 312 const string encoded_value( 313 base::StringPrintf("%" PRIu64, value.Get<uint64_t>())); 314 dictionary_value->SetWithoutPathExpansion( 315 key, MakeCoercedValue(kNativeTypeUint64, encoded_value)); 316 } else if (value.IsTypeCompatible<vector<string>>()) { 317 auto list_value(make_scoped_ptr(new base::ListValue())); 318 for (const auto& string_list_item : value.Get<vector<string>>()) { 319 list_value->Append(MakeValueForString(string_list_item)); 320 } 321 dictionary_value->SetWithoutPathExpansion(key, std::move(list_value)); 322 } else { 323 LOG(ERROR) << "Failed to convert element with key |" << key << "|."; 324 return nullptr; 325 } 326 } 327 return dictionary_value; 328 } 329 330 } // namespace 331 332 JsonStore::JsonStore(const base::FilePath& path) 333 : path_(path) { 334 CHECK(!path_.empty()); 335 } 336 337 bool JsonStore::IsNonEmpty() const { 338 int64_t file_size = 0; 339 return base::GetFileSize(path_, &file_size) && file_size != 0; 340 } 341 342 bool JsonStore::Open() { 343 if (!IsNonEmpty()) { 344 LOG(INFO) << "Creating a new key file at |" << path_.value() << "|."; 345 return true; 346 } 347 348 string json_string; 349 if (!base::ReadFileToString(path_, &json_string)) { 350 LOG(ERROR) << "Failed to read data from |" << path_.value() << "|."; 351 return false; 352 } 353 354 JSONStringValueDeserializer json_deserializer(json_string); 355 unique_ptr<base::Value> json_value; 356 string json_error; 357 json_deserializer.set_allow_trailing_comma(true); 358 json_value.reset( 359 json_deserializer.Deserialize(nullptr, &json_error).release()); 360 if (!json_value) { 361 LOG(ERROR) << "Failed to parse JSON data from |" << path_.value() <<"|."; 362 SLOG(this, 5) << json_error; 363 return false; 364 } 365 366 const base::DictionaryValue* root_dictionary; 367 if (!json_value->GetAsDictionary(&root_dictionary)) { 368 LOG(ERROR) << "JSON value is not a dictionary."; 369 return false; 370 } 371 372 CHECK(root_dictionary); 373 if (root_dictionary->HasKey(kRootPropertyDescription) && 374 !root_dictionary->GetStringWithoutPathExpansion( 375 kRootPropertyDescription, &file_description_)) { 376 LOG(WARNING) << "Property |" << kRootPropertyDescription 377 << "| is not a string."; 378 // Description is non-critical, so continue processing. 379 } 380 381 if (!root_dictionary->HasKey(kRootPropertySettings)) { 382 LOG(ERROR) << "Property |" << kRootPropertySettings << "| is missing."; 383 return false; 384 } 385 386 const base::DictionaryValue* settings_dictionary; 387 if (!root_dictionary->GetDictionaryWithoutPathExpansion( 388 kRootPropertySettings, &settings_dictionary)) { 389 LOG(ERROR) << "Property |" << kRootPropertySettings 390 << "| is not a dictionary."; 391 return false; 392 } 393 394 if (!group_name_to_settings_.empty()) { 395 LOG(INFO) << "Clearing existing settings on open."; 396 group_name_to_settings_.clear(); 397 } 398 399 base::DictionaryValue::Iterator it(*settings_dictionary); 400 while (!it.IsAtEnd()) { 401 const string& group_name = it.key(); 402 const base::DictionaryValue* group_settings_as_values; 403 if (!it.value().GetAsDictionary(&group_settings_as_values)) { 404 LOG(ERROR) << "Group |" << group_name << "| is not a dictionary."; 405 return false; 406 } 407 408 unique_ptr<brillo::VariantDictionary> group_settings_as_variants = 409 ConvertDictionaryValueToVariantDictionary(*group_settings_as_values); 410 if (!group_settings_as_variants) { 411 LOG(ERROR) << "Failed to convert group |" << group_name 412 << "| to variants."; 413 return false; 414 } 415 416 group_name_to_settings_[group_name] = *group_settings_as_variants; 417 it.Advance(); 418 } 419 420 return true; 421 } 422 423 bool JsonStore::Close() { 424 return Flush(); 425 } 426 427 bool JsonStore::Flush() { 428 auto groups(make_scoped_ptr(new base::DictionaryValue())); 429 for (const auto& group_name_and_settings : group_name_to_settings_) { 430 const auto& group_name = group_name_and_settings.first; 431 scoped_ptr<base::DictionaryValue> group_settings( 432 ConvertVariantDictionaryToDictionaryValue( 433 group_name_and_settings.second)); 434 if (!group_settings) { 435 // This class maintains the invariant that anything placed in 436 // |group_settings| is convertible. So abort if conversion fails. 437 LOG(FATAL) << "Failed to convert group |" << group_name << "|."; 438 return false; 439 } 440 groups->SetWithoutPathExpansion(group_name, std::move(group_settings)); 441 } 442 443 base::DictionaryValue root; 444 root.SetStringWithoutPathExpansion( 445 kRootPropertyDescription, file_description_); 446 root.SetWithoutPathExpansion(kRootPropertySettings, std::move(groups)); 447 448 string json_string; 449 JSONStringValueSerializer json_serializer(&json_string); 450 json_serializer.set_pretty_print(true); 451 if (!json_serializer.Serialize(root)) { 452 LOG(ERROR) << "Failed to serialize to JSON."; 453 return false; 454 } 455 456 ScopedUmask owner_only_umask(~(S_IRUSR | S_IWUSR) & 0777); 457 if (!base::ImportantFileWriter::WriteFileAtomically(path_, json_string)) { 458 LOG(ERROR) << "Failed to write JSON file: |" << path_.value() << "|."; 459 return false; 460 } 461 462 return true; 463 } 464 465 bool JsonStore::MarkAsCorrupted() { 466 LOG(INFO) << "In " << __func__ << " for " << path_.value(); 467 string corrupted_path = path_.value() + kCorruptSuffix; 468 int ret = rename(path_.value().c_str(), corrupted_path.c_str()); 469 if (ret != 0) { 470 PLOG(ERROR) << "File rename failed."; 471 return false; 472 } 473 return true; 474 } 475 476 set<string> JsonStore::GetGroups() const { 477 set<string> matching_groups; 478 for (const auto& group_name_and_settings : group_name_to_settings_) { 479 matching_groups.insert(group_name_and_settings.first); 480 } 481 return matching_groups; 482 } 483 484 // Returns a set so that caller can easily test whether a particular group 485 // is contained within this collection. 486 set<string> JsonStore::GetGroupsWithKey(const string& key) const { 487 set<string> matching_groups; 488 // iterate over groups, find ones with matching key 489 for (const auto& group_name_and_settings : group_name_to_settings_) { 490 const auto& group_name = group_name_and_settings.first; 491 const auto& group_settings = group_name_and_settings.second; 492 if (group_settings.find(key) != group_settings.end()) { 493 matching_groups.insert(group_name); 494 } 495 } 496 return matching_groups; 497 } 498 499 set<string> JsonStore::GetGroupsWithProperties(const KeyValueStore& properties) 500 const { 501 set<string> matching_groups; 502 const brillo::VariantDictionary& properties_dict(properties.properties()); 503 for (const auto& group_name_and_settings : group_name_to_settings_) { 504 const auto& group_name = group_name_and_settings.first; 505 const auto& group_settings = group_name_and_settings.second; 506 if (DoesGroupContainProperties(group_settings, properties_dict)) { 507 matching_groups.insert(group_name); 508 } 509 } 510 return matching_groups; 511 } 512 513 bool JsonStore::ContainsGroup(const string& group) const { 514 const auto& it = group_name_to_settings_.find(group); 515 return it != group_name_to_settings_.end(); 516 } 517 518 bool JsonStore::DeleteKey(const string& group, const string& key) { 519 const auto& group_name_and_settings = group_name_to_settings_.find(group); 520 if (group_name_and_settings == group_name_to_settings_.end()) { 521 LOG(ERROR) << "Could not find group |" << group << "|."; 522 return false; 523 } 524 525 auto& group_settings = group_name_and_settings->second; 526 auto property_it = group_settings.find(key); 527 if (property_it != group_settings.end()) { 528 group_settings.erase(property_it); 529 } 530 531 return true; 532 } 533 534 bool JsonStore::DeleteGroup(const string& group) { 535 auto group_name_and_settings = group_name_to_settings_.find(group); 536 if (group_name_and_settings != group_name_to_settings_.end()) { 537 group_name_to_settings_.erase(group_name_and_settings); 538 } 539 return true; 540 } 541 542 bool JsonStore::SetHeader(const string& header) { 543 file_description_ = header; 544 return true; 545 } 546 547 bool JsonStore::GetString(const string& group, 548 const string& key, 549 string* value) const { 550 return ReadSetting(group, key, value); 551 } 552 553 bool JsonStore::SetString( 554 const string& group, const string& key, const string& value) { 555 return WriteSetting(group, key, value); 556 } 557 558 bool JsonStore::GetBool(const string& group, const string& key, bool* value) 559 const { 560 return ReadSetting(group, key, value); 561 } 562 563 bool JsonStore::SetBool(const string& group, const string& key, bool value) { 564 return WriteSetting(group, key, value); 565 } 566 567 bool JsonStore::GetInt( 568 const string& group, const string& key, int* value) const { 569 return ReadSetting(group, key, value); 570 } 571 572 bool JsonStore::SetInt(const string& group, const string& key, int value) { 573 return WriteSetting(group, key, value); 574 } 575 576 bool JsonStore::GetUint64( 577 const string& group, const string& key, uint64_t* value) const { 578 return ReadSetting(group, key, value); 579 } 580 581 bool JsonStore::SetUint64( 582 const string& group, const string& key, uint64_t value) { 583 return WriteSetting(group, key, value); 584 } 585 586 bool JsonStore::GetStringList( 587 const string& group, const string& key, vector<string>* value) const { 588 return ReadSetting(group, key, value); 589 } 590 591 bool JsonStore::SetStringList( 592 const string& group, const string& key, const vector<string>& value) { 593 return WriteSetting(group, key, value); 594 } 595 596 bool JsonStore::GetCryptedString( 597 const string& group, const string& key, string* value) { 598 string encrypted_value; 599 if (!GetString(group, key, &encrypted_value)) { 600 return false; 601 } 602 603 // TODO(quiche): Once we've removed the glib dependency in 604 // CryptoProvider, move to using CryptoProvider, instead of 605 // CryptoROT47 directly. This change should be done before using 606 // JsonStore in production, as the on-disk format of crypted strings 607 // will change. 608 CryptoROT47 rot47; 609 string decrypted_value; 610 if (!rot47.Decrypt(encrypted_value, &decrypted_value)) { 611 LOG(ERROR) << "Failed to decrypt value for |" << group << "|" 612 << ":|" << key << "|."; 613 return false; 614 } 615 616 if (value) { 617 *value = decrypted_value; 618 } 619 return true; 620 } 621 622 bool JsonStore::SetCryptedString( 623 const string& group, const string& key, const string& value) { 624 CryptoROT47 rot47; 625 string encrypted_value; 626 if (!rot47.Encrypt(value, &encrypted_value)) { 627 LOG(ERROR) << "Failed to encrypt value for |" << group << "|" 628 << ":|" << key << "|."; 629 return false; 630 } 631 632 return SetString(group, key, encrypted_value); 633 } 634 635 // Private methods. 636 template<typename T> 637 bool JsonStore::ReadSetting( 638 const string& group, const string& key, T* out) const { 639 const auto& group_name_and_settings = group_name_to_settings_.find(group); 640 if (group_name_and_settings == group_name_to_settings_.end()) { 641 SLOG(this, 10) << "Could not find group |" << group << "|."; 642 return false; 643 } 644 645 const auto& group_settings = group_name_and_settings->second; 646 const auto& property_name_and_value = group_settings.find(key); 647 if (property_name_and_value == group_settings.end()) { 648 SLOG(this, 10) << "Could not find property |" << key << "|."; 649 return false; 650 } 651 652 if (!property_name_and_value->second.IsTypeCompatible<T>()) { 653 // We assume that the reader and the writer agree on the exact 654 // type. So we do not allow implicit conversion. 655 LOG(ERROR) << "Can not read |" << brillo::GetUndecoratedTypeName<T>() 656 << "| from |" 657 << property_name_and_value->second.GetUndecoratedTypeName() 658 << "|."; 659 return false; 660 } 661 662 if (out) { 663 return property_name_and_value->second.GetValue(out); 664 } else { 665 return true; 666 } 667 } 668 669 template<typename T> 670 bool JsonStore::WriteSetting( 671 const string& group, const string& key, const T& new_value) { 672 auto group_name_and_settings = group_name_to_settings_.find(group); 673 if (group_name_and_settings == group_name_to_settings_.end()) { 674 group_name_to_settings_[group][key] = new_value; 675 return true; 676 } 677 678 auto& group_settings = group_name_and_settings->second; 679 auto property_name_and_value = group_settings.find(key); 680 if (property_name_and_value == group_settings.end()) { 681 group_settings[key] = new_value; 682 return true; 683 } 684 685 if (!property_name_and_value->second.IsTypeCompatible<T>()) { 686 SLOG(this, 10) << "New type |" << brillo::GetUndecoratedTypeName<T>() 687 << "| differs from current type |" 688 << property_name_and_value->second.GetUndecoratedTypeName() 689 << "|."; 690 return false; 691 } else { 692 property_name_and_value->second = new_value; 693 return true; 694 } 695 } 696 697 } // namespace shill 698