| // Copyright 2016 The Fuchsia 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 "src/sys/sysmgr/config.h" | 
 |  | 
 | #include <lib/syslog/cpp/macros.h> | 
 |  | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "rapidjson/document.h" | 
 | #include "src/lib/files/file.h" | 
 | #include "src/lib/fxl/strings/string_printf.h" | 
 |  | 
 | namespace sysmgr { | 
 | namespace { | 
 | using fxl::StringPrintf; | 
 |  | 
 | constexpr char kApps[] = "apps"; | 
 | constexpr char kServices[] = "services"; | 
 | constexpr char kStartupServices[] = "startup_services"; | 
 | constexpr char kOptionalServices[] = "optional_services"; | 
 | constexpr char kUpdateDependencies[] = "update_dependencies"; | 
 | constexpr char kCriticalComponents[] = "critical_components"; | 
 | }  // namespace | 
 |  | 
 | bool Config::ParseFromDirectory(const std::string& dir) { | 
 |   auto cb = [this](rapidjson::Document document) { ParseDocument(std::move(document)); }; | 
 |   json_parser_.ParseFromDirectory(dir, cb); | 
 |   if (json_parser_.HasError()) { | 
 |     FX_LOGS(INFO) << json_parser_.error_str(); | 
 |   } | 
 |   return !json_parser_.HasError(); | 
 | } | 
 |  | 
 | void Config::ReadJsonStringArray(const rapidjson::Document& document, const char* member, | 
 |                                  std::vector<std::string>* out) { | 
 |   auto it = document.FindMember(member); | 
 |   if (it != document.MemberEnd()) { | 
 |     const auto& value = it->value; | 
 |     if (value.IsArray() && | 
 |         std::all_of(value.GetArray().begin(), value.GetArray().end(), | 
 |                     [](const rapidjson::Value& val) { return val.IsString(); })) { | 
 |       for (const auto& service : value.GetArray()) { | 
 |         out->push_back(service.GetString()); | 
 |       } | 
 |     } else { | 
 |       json_parser_.ReportError(StringPrintf("'%s' is not an array of strings.", member)); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool Config::HasError() const { return json_parser_.HasError(); } | 
 |  | 
 | std::string Config::error_str() const { return json_parser_.error_str(); } | 
 |  | 
 | void Config::ParseDocument(rapidjson::Document document) { | 
 |   if (!document.IsObject()) { | 
 |     json_parser_.ReportError("Config file is not a JSON object."); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!ParseServiceMap(document, kServices, &services_)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   auto apps_it = document.FindMember(kApps); | 
 |   if (apps_it != document.MemberEnd()) { | 
 |     const auto& value = apps_it->value; | 
 |     const auto* name = apps_it->name.GetString(); | 
 |     if (value.IsArray()) { | 
 |       for (const auto& app : value.GetArray()) { | 
 |         auto launch_info = GetLaunchInfo(app, name); | 
 |         if (launch_info) { | 
 |           apps_.push_back(std::move(launch_info)); | 
 |         } | 
 |       } | 
 |     } else { | 
 |       json_parser_.ReportError(StringPrintf("'%s' is not an array.", name)); | 
 |     } | 
 |   } | 
 |  | 
 |   ReadJsonStringArray(document, kStartupServices, &startup_services_); | 
 |   ReadJsonStringArray(document, kUpdateDependencies, &update_dependencies_); | 
 |   ReadJsonStringArray(document, kOptionalServices, &optional_services_); | 
 |   ReadJsonStringArray(document, kCriticalComponents, &critical_components_); | 
 | } | 
 |  | 
 | bool Config::ParseServiceMap(const rapidjson::Document& document, const std::string& key, | 
 |                              Config::ServiceMap* services) { | 
 |   auto it = document.FindMember(key); | 
 |   if (it != document.MemberEnd()) { | 
 |     const auto& value = it->value; | 
 |     if (!value.IsObject()) { | 
 |       json_parser_.ReportError(StringPrintf("'%s' must be an object.", key.c_str())); | 
 |       return false; | 
 |     } | 
 |     for (const auto& reg : value.GetObject()) { | 
 |       if (!reg.name.IsString()) { | 
 |         json_parser_.ReportError(StringPrintf("Keys of '%s' must be strings.", key.c_str())); | 
 |         continue; | 
 |       } | 
 |       std::string service_key = reg.name.GetString(); | 
 |       if (services->find(service_key) != services->end()) { | 
 |         json_parser_.ReportError(StringPrintf("Duplicate definition in map for '%s': %s", kServices, | 
 |                                               service_key.c_str())); | 
 |         continue; | 
 |       } | 
 |       auto launch_info = | 
 |           GetLaunchInfo(reg.value, StringPrintf("%s.%s", key.c_str(), service_key.c_str())); | 
 |       if (launch_info) { | 
 |         services->emplace(service_key, std::move(launch_info)); | 
 |       } | 
 |     } | 
 |   } | 
 |   return !json_parser_.HasError(); | 
 | } | 
 |  | 
 | fuchsia::sys::LaunchInfoPtr Config::GetLaunchInfo(const rapidjson::Document::ValueType& value, | 
 |                                                   const std::string& name) { | 
 |   auto launch_info = fuchsia::sys::LaunchInfo::New(); | 
 |   if (value.IsString()) { | 
 |     launch_info->url = value.GetString(); | 
 |     return launch_info; | 
 |   } | 
 |  | 
 |   if (value.IsArray()) { | 
 |     const auto& array = value.GetArray(); | 
 |     // If the element is an array, ensure it is non-empty and all values are | 
 |     // strings. | 
 |     if (!array.Empty() && std::all_of(array.begin(), array.end(), | 
 |                                       [](const rapidjson::Value& val) { return val.IsString(); })) { | 
 |       launch_info->url = array[0].GetString(); | 
 |       launch_info->arguments.emplace(); | 
 |       for (size_t i = 1; i < array.Size(); ++i) { | 
 |         launch_info->arguments->push_back(array[i].GetString()); | 
 |       } | 
 |       return launch_info; | 
 |     } | 
 |   } | 
 |  | 
 |   json_parser_.ReportError( | 
 |       StringPrintf("'%s' must be a string or a non-empty array of strings.", name.c_str())); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | }  // namespace sysmgr |