blob: b6cad144dfcca568a197c8c86beaa14586ccb786 [file] [log] [blame]
// Copyright 2018 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/developer/feedback/crashpad_agent/config.h"
#include <zircon/errors.h>
#include "src/developer/feedback/crashpad_agent/constants.h"
#include "src/lib/files/file.h"
#include "src/lib/syslog/cpp/logger.h"
#include "third_party/rapidjson/include/rapidjson/document.h"
#include "third_party/rapidjson/include/rapidjson/error/en.h"
#include "third_party/rapidjson/include/rapidjson/schema.h"
#include "third_party/rapidjson/include/rapidjson/stringbuffer.h"
namespace feedback {
namespace {
const char kSchema[] = R"({
"type": "object",
"properties": {
"crashpad_database": {
"type": "object",
"properties": {
"max_size_in_kb": {
"type": "integer"
}
},
"required": [
"max_size_in_kb"
],
"additionalProperties": false
},
"crash_server": {
"type": "object",
"properties": {
"upload_policy": {
"type": "string",
"enum": [
"disabled",
"enabled",
"read_from_privacy_settings"
]
},
"url": {
"type": "string"
}
},
"required": [
"upload_policy"
],
"additionalProperties": false
}
},
"required": [
"crashpad_database",
"crash_server"
],
"additionalProperties": false
})";
bool CheckAgainstSchema(rapidjson::Document& doc) {
// Check that the schema is actually valid.
rapidjson::Document sd;
rapidjson::ParseResult ok = sd.Parse(kSchema);
if (!ok) {
FX_LOGS(ERROR) << "invalid JSON schema for config at offset " << ok.Offset() << " "
<< rapidjson::GetParseError_En(ok.Code());
return false;
}
// Check the document against the schema.
rapidjson::SchemaDocument schema(sd);
rapidjson::SchemaValidator validator(schema);
if (!doc.Accept(validator)) {
rapidjson::StringBuffer sb;
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
FX_LOGS(ERROR) << "config does not match schema, violating '"
<< validator.GetInvalidSchemaKeyword() << "' rule";
return false;
}
return true;
}
template <typename JsonObject>
CrashpadDatabaseConfig ParseCrashpadDatabaseConfig(const JsonObject& obj) {
CrashpadDatabaseConfig config;
config.max_size_in_kb = obj[kCrashpadDatabaseMaxSizeInKbKey].GetUint();
return config;
}
template <typename JsonObject>
bool ParseCrashServerConfig(const JsonObject& obj, CrashServerConfig* config) {
CrashServerConfig local_config;
bool should_expect_url = true;
const std::string upload_policy = obj[kCrashServerUploadPolicyKey].GetString();
if (upload_policy == "disabled") {
local_config.upload_policy = CrashServerConfig::UploadPolicy::DISABLED;
should_expect_url = false;
} else if (upload_policy == "enabled") {
local_config.upload_policy = CrashServerConfig::UploadPolicy::ENABLED;
} else if (upload_policy == "read_from_privacy_settings") {
local_config.upload_policy = CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS;
} else {
// This should not be possible as we have checked the config against the schema.
FX_LOGS(ERROR) << "unknown upload policy " << upload_policy;
return false;
}
if (should_expect_url) {
if (!obj.HasMember(kCrashServerUrlKey)) {
FX_LOGS(ERROR) << "missing crash server URL in config with upload not disabled";
return false;
}
local_config.url = std::make_unique<std::string>(obj[kCrashServerUrlKey].GetString());
} else if (obj.HasMember(kCrashServerUrlKey)) {
FX_LOGS(WARNING) << "crash server URL set in config with upload disabled, "
"ignoring value";
}
*config = std::move(local_config);
return true;
}
} // namespace
zx_status_t ParseConfig(const std::string& filepath, Config* config) {
std::string json;
if (!files::ReadFileToString(filepath, &json)) {
FX_LOGS(ERROR) << "error reading config file at " << filepath;
return ZX_ERR_IO;
}
rapidjson::Document doc;
rapidjson::ParseResult ok = doc.Parse(json.c_str());
if (!ok) {
FX_LOGS(ERROR) << "error parsing config as JSON at offset " << ok.Offset() << " "
<< rapidjson::GetParseError_En(ok.Code());
return ZX_ERR_INTERNAL;
}
if (!CheckAgainstSchema(doc)) {
return ZX_ERR_INTERNAL;
}
// We use a local config to only set the out argument after all the checks.
Config local_config;
// It is safe to directly access the fields for which the keys are marked as required as we have
// checked the config against the schema.
local_config.crashpad_database =
ParseCrashpadDatabaseConfig(doc[kCrashpadDatabaseKey].GetObject());
if (!ParseCrashServerConfig(doc[kCrashServerKey].GetObject(), &local_config.crash_server)) {
return ZX_ERR_INTERNAL;
}
*config = std::move(local_config);
return ZX_OK;
}
std::string ToString(const CrashServerConfig::UploadPolicy upload_policy) {
switch (upload_policy) {
case CrashServerConfig::UploadPolicy::DISABLED:
return "DISABLED";
case CrashServerConfig::UploadPolicy::ENABLED:
return "ENABLED";
case CrashServerConfig::UploadPolicy::READ_FROM_PRIVACY_SETTINGS:
return "READ_FROM_PRIVACY_SETTINGS";
}
}
} // namespace feedback