1 /* 2 * Copyright (C) 2017 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 #define LOG_TAG "incident_helper" 18 19 #include "ih_util.h" 20 21 #include <algorithm> 22 #include <sstream> 23 #include <unistd.h> 24 25 bool isValidChar(char c) { 26 uint8_t v = (uint8_t)c; 27 return (v >= (uint8_t)'a' && v <= (uint8_t)'z') 28 || (v >= (uint8_t)'A' && v <= (uint8_t)'Z') 29 || (v >= (uint8_t)'0' && v <= (uint8_t)'9') 30 || (v == (uint8_t)'_'); 31 } 32 33 std::string trim(const std::string& s, const std::string& charset) { 34 const auto head = s.find_first_not_of(charset); 35 if (head == std::string::npos) return ""; 36 37 const auto tail = s.find_last_not_of(charset); 38 return s.substr(head, tail - head + 1); 39 } 40 41 static inline std::string toLowerStr(const std::string& s) { 42 std::string res(s); 43 std::transform(res.begin(), res.end(), res.begin(), ::tolower); 44 return res; 45 } 46 47 static inline std::string trimDefault(const std::string& s) { 48 return trim(s, DEFAULT_WHITESPACE); 49 } 50 51 static inline std::string trimHeader(const std::string& s) { 52 return toLowerStr(trimDefault(s)); 53 } 54 55 static inline bool isNumber(const std::string& s) { 56 std::string::const_iterator it = s.begin(); 57 while (it != s.end() && std::isdigit(*it)) ++it; 58 return !s.empty() && it == s.end(); 59 } 60 61 // This is similiar to Split in android-base/file.h, but it won't add empty string 62 static void split(const std::string& line, std::vector<std::string>& words, 63 const trans_func& func, const std::string& delimiters) { 64 words.clear(); // clear the buffer before split 65 66 size_t base = 0; 67 size_t found; 68 while (true) { 69 found = line.find_first_of(delimiters, base); 70 if (found != base) { 71 std::string word = (*func) (line.substr(base, found - base)); 72 if (!word.empty()) { 73 words.push_back(word); 74 } 75 } 76 if (found == line.npos) break; 77 base = found + 1; 78 } 79 } 80 81 header_t parseHeader(const std::string& line, const std::string& delimiters) { 82 header_t header; 83 trans_func f = &trimHeader; 84 split(line, header, f, delimiters); 85 return header; 86 } 87 88 record_t parseRecord(const std::string& line, const std::string& delimiters) { 89 record_t record; 90 trans_func f = &trimDefault; 91 split(line, record, f, delimiters); 92 return record; 93 } 94 95 bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) { 96 indices.clear(); 97 98 size_t lastIndex = 0; 99 int i = 0; 100 while (headerNames[i] != nullptr) { 101 std::string s = headerNames[i]; 102 lastIndex = line.find(s, lastIndex); 103 if (lastIndex == std::string::npos) { 104 fprintf(stderr, "Bad Task Header: %s\n", line.c_str()); 105 return false; 106 } 107 lastIndex += s.length(); 108 indices.push_back(lastIndex); 109 i++; 110 } 111 112 return true; 113 } 114 115 record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) { 116 record_t record; 117 int lastIndex = 0; 118 int lastBeginning = 0; 119 int lineSize = (int)line.size(); 120 for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) { 121 int idx = *it; 122 if (idx <= lastIndex) { 123 // We saved up until lastIndex last time, so we should start at 124 // lastIndex + 1 this time. 125 idx = lastIndex + 1; 126 } 127 if (idx > lineSize) { 128 if (lastIndex < idx && lastIndex < lineSize) { 129 // There's a little bit more for us to save, which we'll do 130 // outside of the loop. 131 break; 132 } 133 // If we're past the end of the line AND we've already saved everything up to the end. 134 fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize); 135 record.clear(); // The indices are wrong, return empty. 136 return record; 137 } 138 while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos); 139 record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex))); 140 lastBeginning = lastIndex; 141 lastIndex = idx; 142 } 143 if (lineSize - lastIndex > 0) { 144 int beginning = lastIndex; 145 if (record.size() == indices.size() && !record.empty()) { 146 // We've already encountered all of the columns...put whatever is 147 // left in the last column. 148 record.pop_back(); 149 beginning = lastBeginning; 150 } 151 record.push_back(trimDefault(line.substr(beginning, lineSize - beginning))); 152 } 153 return record; 154 } 155 156 void printRecord(const record_t& record) { 157 fprintf(stderr, "Record: { "); 158 if (record.size() == 0) { 159 fprintf(stderr, "}\n"); 160 return; 161 } 162 for(size_t i = 0; i < record.size(); ++i) { 163 if(i != 0) fprintf(stderr, "\", "); 164 fprintf(stderr, "\"%s", record[i].c_str()); 165 } 166 fprintf(stderr, "\" }\n"); 167 } 168 169 bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) { 170 const auto head = line->find_first_not_of(DEFAULT_WHITESPACE); 171 if (head == std::string::npos) return false; 172 int len = (int)line->length(); 173 int i = 0; 174 int j = head; 175 while (key[i] != '\0') { 176 if (j >= len || key[i++] != line->at(j++)) { 177 return false; 178 } 179 } 180 181 if (endAtDelimiter) { 182 // this means if the line only have prefix or no delimiter, we still return false. 183 if (j == len || isValidChar(line->at(j))) return false; 184 } 185 186 line->assign(trimDefault(line->substr(j))); 187 return true; 188 } 189 190 bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) { 191 const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE); 192 if (tail == std::string::npos) return false; 193 int i = 0; 194 while (key[++i] != '\0'); // compute the size of the key 195 int j = tail; 196 while (i > 0) { 197 if (j < 0 || key[--i] != line->at(j--)) { 198 return false; 199 } 200 } 201 202 if (endAtDelimiter) { 203 // this means if the line only have suffix or no delimiter, we still return false. 204 if (j < 0 || isValidChar(line->at(j))) return false; 205 } 206 207 line->assign(trimDefault(line->substr(0, j+1))); 208 return true; 209 } 210 211 std::string behead(std::string* line, const char cut) { 212 auto found = line->find_first_of(cut); 213 if (found == std::string::npos) { 214 std::string head = line->substr(0); 215 line->assign(""); 216 return head; 217 } 218 std::string head = line->substr(0, found); 219 while(line->at(found) == cut) found++; // trim more cut of the rest 220 line->assign(line->substr(found)); 221 return head; 222 } 223 224 int toInt(const std::string& s) { 225 return atoi(s.c_str()); 226 } 227 228 long long toLongLong(const std::string& s) { 229 return atoll(s.c_str()); 230 } 231 232 double toDouble(const std::string& s) { 233 return atof(s.c_str()); 234 } 235 236 // ============================================================================== 237 Reader::Reader(const int fd) 238 { 239 mFile = fdopen(fd, "r"); 240 mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : ""; 241 } 242 243 Reader::~Reader() 244 { 245 if (mFile != nullptr) fclose(mFile); 246 } 247 248 bool Reader::readLine(std::string* line) { 249 if (mFile == nullptr) return false; 250 251 char* buf = nullptr; 252 size_t len = 0; 253 ssize_t read = getline(&buf, &len, mFile); 254 if (read != -1) { 255 std::string s(buf); 256 line->assign(trim(s, DEFAULT_NEWLINE)); 257 } else if (errno == EINVAL) { 258 mStatus = "Bad Argument"; 259 } 260 free(buf); 261 return read != -1; 262 } 263 264 bool Reader::ok(std::string* error) { 265 error->assign(mStatus); 266 return mStatus.empty(); 267 } 268 269 // ============================================================================== 270 Table::Table(const char* names[], const uint64_t ids[], const int count) 271 :mEnums(), 272 mEnumValuesByName() 273 { 274 std::map<std::string, uint64_t> fields; 275 for (int i = 0; i < count; i++) { 276 fields[names[i]] = ids[i]; 277 } 278 mFields = fields; 279 } 280 281 Table::~Table() 282 { 283 } 284 285 void 286 Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize) 287 { 288 if (mFields.find(field) == mFields.end()) { 289 fprintf(stderr, "Field '%s' not found", field); 290 return; 291 } 292 293 std::map<std::string, int> enu; 294 for (int i = 0; i < enumSize; i++) { 295 enu[enumNames[i]] = enumValues[i]; 296 } 297 mEnums[field] = enu; 298 } 299 300 void 301 Table::addEnumNameToValue(const char* enumName, const int enumValue) 302 { 303 mEnumValuesByName[enumName] = enumValue; 304 } 305 306 bool 307 Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value) 308 { 309 if (mFields.find(name) == mFields.end()) return false; 310 311 uint64_t found = mFields[name]; 312 record_t repeats; // used for repeated fields 313 switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) { 314 case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE: 315 case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT: 316 proto->write(found, toDouble(value)); 317 break; 318 case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING: 319 case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES: 320 proto->write(found, value); 321 break; 322 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64: 323 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64: 324 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64: 325 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64: 326 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64: 327 proto->write(found, toLongLong(value)); 328 break; 329 case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL: 330 if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) { 331 proto->write(found, true); 332 break; 333 } 334 if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) { 335 proto->write(found, false); 336 break; 337 } 338 return false; 339 case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM: 340 // if the field has its own enum mapping, use this, otherwise use general name to value mapping. 341 if (mEnums.find(name) != mEnums.end()) { 342 if (mEnums[name].find(value) != mEnums[name].end()) { 343 proto->write(found, mEnums[name][value]); 344 } else { 345 proto->write(found, 0); // TODO: should get the default enum value (Unknown) 346 } 347 } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) { 348 proto->write(found, mEnumValuesByName[value]); 349 } else if (isNumber(value)) { 350 proto->write(found, toInt(value)); 351 } else { 352 return false; 353 } 354 break; 355 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32: 356 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32: 357 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32: 358 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32: 359 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32: 360 proto->write(found, toInt(value)); 361 break; 362 // REPEATED TYPE below: 363 case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32: 364 repeats = parseRecord(value, COMMA_DELIMITER); 365 for (size_t i=0; i<repeats.size(); i++) { 366 proto->write(found, toInt(repeats[i])); 367 } 368 break; 369 case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING: 370 repeats = parseRecord(value, COMMA_DELIMITER); 371 for (size_t i=0; i<repeats.size(); i++) { 372 proto->write(found, repeats[i]); 373 } 374 break; 375 default: 376 return false; 377 } 378 return true; 379 } 380 381 // ================================================================================ 382 Message::Message(Table* table) 383 :mTable(table), 384 mPreviousField(""), 385 mTokens(), 386 mSubMessages() 387 { 388 } 389 390 Message::~Message() 391 { 392 } 393 394 void 395 Message::addSubMessage(uint64_t fieldId, Message* fieldMsg) 396 { 397 for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) { 398 if (iter->second == fieldId) { 399 mSubMessages[iter->first] = fieldMsg; 400 return; 401 } 402 } 403 } 404 405 bool 406 Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value) 407 { 408 // If the field name can be found, it means the name is a primitive field. 409 if (mTable->mFields.find(name) != mTable->mFields.end()) { 410 endSession(proto); 411 // The only edge case is for example ro.hardware itself is a message, so a field called "value" 412 // would be defined in proto Ro::Hardware and it must be the first field. 413 if (mSubMessages.find(name) != mSubMessages.end()) { 414 startSession(proto, name); 415 return mSubMessages[name]->insertField(proto, "value", value); 416 } else { 417 return mTable->insertField(proto, name, value); 418 } 419 } 420 421 // Try to find the message field which is the prefix of name, so the value would be inserted 422 // recursively into the submessage. 423 std::string mutableName = name; 424 for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) { 425 std::string fieldName = iter->first; 426 std::string prefix = fieldName + "_"; // underscore is the delimiter in the name 427 if (stripPrefix(&mutableName, prefix.c_str())) { 428 if (mPreviousField != fieldName) { 429 endSession(proto); 430 startSession(proto, fieldName); 431 } 432 return mSubMessages[fieldName]->insertField(proto, mutableName, value); 433 } 434 } 435 // Can't find the name in proto definition, handle it separately. 436 return false; 437 } 438 439 void 440 Message::startSession(ProtoOutputStream* proto, const std::string& name) 441 { 442 uint64_t fieldId = mTable->mFields[name]; 443 uint64_t token = proto->start(fieldId); 444 mPreviousField = name; 445 mTokens.push(token); 446 } 447 448 void 449 Message::endSession(ProtoOutputStream* proto) 450 { 451 if (mPreviousField == "") return; 452 if (mSubMessages.find(mPreviousField) != mSubMessages.end()) { 453 mSubMessages[mPreviousField]->endSession(proto); 454 } 455 proto->end(mTokens.top()); 456 mTokens.pop(); 457 mPreviousField = ""; 458 } 459