1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/system/name_value_pairs_parser.h"
#include <stddef.h>
#include <unistd.h>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
namespace ash::system {
namespace {
// Runs a tool and capture its standard output into |output|. Returns false
// if the tool cannot be run.
bool GetToolOutput(const base::CommandLine& command, std::string* output) {
if (!base::PathExists(command.GetProgram())) {
LOG(WARNING) << "Tool for statistics not found: " << command.GetProgram();
return false;
}
if (!base::GetAppOutput(command, output)) {
LOG(WARNING) << "Error executing " << command.GetProgram();
return false;
}
return true;
}
// Assigns a non quoted version of |input| to |unquoted|, and returns
// whether |input| was actually quoted or not.
bool GetUnquotedString(const std::string& input, std::string* unquoted) {
const size_t input_size = input.size();
if (input_size >= 2 && input[0] == '"' && input[input_size - 1] == '"')
unquoted->assign(input, 1, input_size - 2);
else
unquoted->assign(input);
return unquoted->size() != input_size;
}
// Assigns a non commented version of |input| to |uncommented|. Whitespace
// before the comment is trimmed.
void GetUncommentedString(const std::string& input, std::string* uncommented) {
const size_t comment_pos = input.find('#');
if (comment_pos == std::string::npos) {
uncommented->assign(input);
} else {
uncommented->assign(input, 0, comment_pos);
base::TrimWhitespaceASCII(*uncommented, base::TRIM_TRAILING, uncommented);
}
}
// Parse a name from |input|, validating that it is in |format|, and assign it
// to |name|.
bool ParseName(const std::string& input,
NameValuePairsFormat format,
std::string* name) {
bool parsed_ok = false;
switch (format) {
case NameValuePairsFormat::kVpdDump:
// The name must be quoted, and we unquote it.
parsed_ok = GetUnquotedString(input, name);
break;
case NameValuePairsFormat::kMachineInfo:
// The name may be quoted, and we unquote it.
GetUnquotedString(input, name);
parsed_ok = true;
break;
case NameValuePairsFormat::kCrossystem:
// We trim all ASCII whitespace and the name then must not be quoted.
base::TrimWhitespaceASCII(input, base::TRIM_ALL, name);
parsed_ok = !GetUnquotedString(*name, name);
break;
}
// Names must not be empty in addition to having parsed successfully.
return parsed_ok && !name->empty();
}
// Parse a value from |input|, validating that it is in |format|, and assign it
// to |name|.
bool ParseValue(const std::string& input,
NameValuePairsFormat format,
std::string* value) {
if (format == NameValuePairsFormat::kCrossystem) {
// The crossystem format allows for comments, remove them.
GetUncommentedString(input, value);
// We trim all ASCII whitespace and preserve the rest as is.
base::TrimWhitespaceASCII(*value, base::TRIM_ALL, value);
return true;
} else {
// The value must be quoted, and we unquote it.
return GetUnquotedString(input, value);
}
}
// Return a string for logging a value.
std::string GetLoggingStringForValue(const std::string& value) {
return "value: " + value;
}
const char* GetNameValuePairsFormatName(NameValuePairsFormat format) {
switch (format) {
case NameValuePairsFormat::kVpdDump:
return "VPD dump";
case NameValuePairsFormat::kMachineInfo:
return "machine info";
case NameValuePairsFormat::kCrossystem:
return "crossystem";
}
return "unknown";
}
} // namespace
NameValuePairsParser::NameValuePairsParser(NameValueMap* map)
: map_(map) {
}
NameValuePairsParser::~NameValuePairsParser() = default;
bool NameValuePairsParser::ParseNameValuePairsFromFile(
const base::FilePath& file_path,
NameValuePairsFormat format) {
std::string file_contents;
if (base::ReadFileToString(file_path, &file_contents)) {
return ParseNameValuePairs(file_contents, format);
} else {
if (base::SysInfo::IsRunningOnChromeOS())
VLOG(1) << "Statistics file not present: " << file_path.value();
return false;
}
}
bool NameValuePairsParser::ParseNameValuePairsFromTool(
const base::CommandLine& command,
NameValuePairsFormat format) {
std::string output_string;
if (!GetToolOutput(command, &output_string))
return false;
return ParseNameValuePairs(output_string, format);
}
bool NameValuePairsParser::ParseNameValuePairsFromString(
const std::string& input,
NameValuePairsFormat format) {
return ParseNameValuePairs(input, format);
}
void NameValuePairsParser::DeletePairsWithValue(const std::string& value) {
auto it = map_->begin();
while (it != map_->end()) {
if (it->second == value) {
it = map_->erase(it);
} else {
it++;
}
}
}
void NameValuePairsParser::AddNameValuePair(const std::string& name,
const std::string& value) {
const auto it = map_->find(name);
if (it == map_->end()) {
(*map_)[name] = value;
VLOG(1) << "Name: " << name << ", " << GetLoggingStringForValue(value);
} else if (it->second != value) {
LOG(WARNING) << "Name: " << name << " already has "
<< GetLoggingStringForValue(it->second) << ", ignoring new "
<< GetLoggingStringForValue(value);
}
}
bool NameValuePairsParser::ParseNameValuePairs(const std::string& input,
NameValuePairsFormat format) {
bool all_valid = true;
// We use StringPairs to parse pairs since this is the class that is also
// used to parse machine-info for server backed state keys in Chromium OS.
base::StringPairs pairs;
// This gives us somewhat more lenient parsing than strictly respecting the
// formats we want. For example, whitespace will be removed around the equal
// sign regardless of format. That's okay as we care more about consistency
// with the server backed state keys parser than the strictness of the format,
// and we still preserve our ability to handle the quoted values as we wish.
base::SplitStringIntoKeyValuePairs(input, '=', '\n', &pairs);
for (const auto& pair : pairs) {
std::string name;
if (!ParseName(pair.first, format, &name)) {
LOG(WARNING) << "Could not parse " << GetNameValuePairsFormatName(format)
<< " name-value name from: " << pair.first;
all_valid = false;
continue;
}
std::string value;
if (!ParseValue(pair.second, format, &value)) {
LOG(WARNING) << "Could not parse " << GetNameValuePairsFormatName(format)
<< " name-value value from: " << pair.second;
all_valid = false;
continue;
}
AddNameValuePair(name, value);
}
return all_valid;
}
} // namespace ash::system
|