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
isValidChar(char c)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
trim(const std::string & s,const std::string & charset)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
toLowerStr(const std::string & s)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
trimDefault(const std::string & s)47 static inline std::string trimDefault(const std::string& s) {
48 return trim(s, DEFAULT_WHITESPACE);
49 }
50
trimHeader(const std::string & s)51 static inline std::string trimHeader(const std::string& s) {
52 return toLowerStr(trimDefault(s));
53 }
54
isNumber(const std::string & s)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
split(const std::string & line,std::vector<std::string> & words,const trans_func & func,const std::string & delimiters)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
parseHeader(const std::string & line,const std::string & delimiters)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
parseRecord(const std::string & line,const std::string & delimiters)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
getColumnIndices(std::vector<int> & indices,const char ** headerNames,const std::string & line)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
parseRecordByColumns(const std::string & line,const std::vector<int> & indices,const std::string & delimiters)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
printRecord(const record_t & record)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
stripPrefix(std::string * line,const char * key,bool endAtDelimiter)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
stripSuffix(std::string * line,const char * key,bool endAtDelimiter)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
behead(std::string * line,const char cut)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
toInt(const std::string & s)224 int toInt(const std::string& s) {
225 return atoi(s.c_str());
226 }
227
toLongLong(const std::string & s)228 long long toLongLong(const std::string& s) {
229 return atoll(s.c_str());
230 }
231
toDouble(const std::string & s)232 double toDouble(const std::string& s) {
233 return atof(s.c_str());
234 }
235
236 // ==============================================================================
Reader(const int fd)237 Reader::Reader(const int fd)
238 {
239 mFile = fdopen(fd, "r");
240 mBuffer = new char[1024];
241 mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
242 }
243
~Reader()244 Reader::~Reader()
245 {
246 if (mFile != nullptr) fclose(mFile);
247 delete[] mBuffer;
248 }
249
readLine(std::string * line)250 bool Reader::readLine(std::string* line) {
251 if (mFile == nullptr) return false;
252
253 size_t len = 0;
254 ssize_t read = getline(&mBuffer, &len, mFile);
255 if (read != -1) {
256 std::string s(mBuffer);
257 line->assign(trim(s, DEFAULT_NEWLINE));
258 return true;
259 }
260 if (!feof(mFile)) {
261 mStatus = "Error reading file. Ferror: " + std::to_string(ferror(mFile));
262 }
263 return false;
264 }
265
ok(std::string * error)266 bool Reader::ok(std::string* error) {
267 if (mStatus.empty()) {
268 return true;
269 }
270 error->assign(mStatus);
271 return false;
272 }
273
274 // ==============================================================================
Table(const char * names[],const uint64_t ids[],const int count)275 Table::Table(const char* names[], const uint64_t ids[], const int count)
276 :mEnums(),
277 mEnumValuesByName()
278 {
279 std::map<std::string, uint64_t> fields;
280 for (int i = 0; i < count; i++) {
281 fields[names[i]] = ids[i];
282 }
283 mFields = fields;
284 }
285
~Table()286 Table::~Table()
287 {
288 }
289
290 void
addEnumTypeMap(const char * field,const char * enumNames[],const int enumValues[],const int enumSize)291 Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
292 {
293 if (mFields.find(field) == mFields.end()) {
294 fprintf(stderr, "Field '%s' not found", field);
295 return;
296 }
297
298 std::map<std::string, int> enu;
299 for (int i = 0; i < enumSize; i++) {
300 enu[enumNames[i]] = enumValues[i];
301 }
302 mEnums[field] = enu;
303 }
304
305 void
addEnumNameToValue(const char * enumName,const int enumValue)306 Table::addEnumNameToValue(const char* enumName, const int enumValue)
307 {
308 mEnumValuesByName[enumName] = enumValue;
309 }
310
311 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)312 Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
313 {
314 if (mFields.find(name) == mFields.end()) return false;
315
316 uint64_t found = mFields[name];
317 record_t repeats; // used for repeated fields
318 switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
319 case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
320 case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
321 proto->write(found, toDouble(value));
322 break;
323 case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
324 case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
325 proto->write(found, value);
326 break;
327 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
328 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
329 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
330 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
331 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
332 proto->write(found, toLongLong(value));
333 break;
334 case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
335 if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
336 proto->write(found, true);
337 break;
338 }
339 if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
340 proto->write(found, false);
341 break;
342 }
343 return false;
344 case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
345 // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
346 if (mEnums.find(name) != mEnums.end()) {
347 if (mEnums[name].find(value) != mEnums[name].end()) {
348 proto->write(found, mEnums[name][value]);
349 } else {
350 proto->write(found, 0); // TODO: should get the default enum value (Unknown)
351 }
352 } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
353 proto->write(found, mEnumValuesByName[value]);
354 } else if (isNumber(value)) {
355 proto->write(found, toInt(value));
356 } else {
357 return false;
358 }
359 break;
360 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
361 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
362 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
363 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
364 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
365 proto->write(found, toInt(value));
366 break;
367 // REPEATED TYPE below:
368 case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
369 repeats = parseRecord(value, COMMA_DELIMITER);
370 for (size_t i=0; i<repeats.size(); i++) {
371 proto->write(found, toInt(repeats[i]));
372 }
373 break;
374 case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
375 repeats = parseRecord(value, COMMA_DELIMITER);
376 for (size_t i=0; i<repeats.size(); i++) {
377 proto->write(found, repeats[i]);
378 }
379 break;
380 default:
381 return false;
382 }
383 return true;
384 }
385
386 // ================================================================================
Message(Table * table)387 Message::Message(Table* table)
388 :mTable(table),
389 mPreviousField(""),
390 mTokens(),
391 mSubMessages()
392 {
393 }
394
~Message()395 Message::~Message()
396 {
397 }
398
399 void
addSubMessage(uint64_t fieldId,Message * fieldMsg)400 Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
401 {
402 for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
403 if (iter->second == fieldId) {
404 mSubMessages[iter->first] = fieldMsg;
405 return;
406 }
407 }
408 }
409
410 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)411 Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
412 {
413 // If the field name can be found, it means the name is a primitive field.
414 if (mTable->mFields.find(name) != mTable->mFields.end()) {
415 endSession(proto);
416 // The only edge case is for example ro.hardware itself is a message, so a field called "value"
417 // would be defined in proto Ro::Hardware and it must be the first field.
418 if (mSubMessages.find(name) != mSubMessages.end()) {
419 startSession(proto, name);
420 return mSubMessages[name]->insertField(proto, "value", value);
421 } else {
422 return mTable->insertField(proto, name, value);
423 }
424 }
425
426 // Try to find the message field which is the prefix of name, so the value would be inserted
427 // recursively into the submessage.
428 std::string mutableName = name;
429 for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
430 std::string fieldName = iter->first;
431 std::string prefix = fieldName + "_"; // underscore is the delimiter in the name
432 if (stripPrefix(&mutableName, prefix.c_str())) {
433 if (mPreviousField != fieldName) {
434 endSession(proto);
435 startSession(proto, fieldName);
436 }
437 return mSubMessages[fieldName]->insertField(proto, mutableName, value);
438 }
439 }
440 // Can't find the name in proto definition, handle it separately.
441 return false;
442 }
443
444 void
startSession(ProtoOutputStream * proto,const std::string & name)445 Message::startSession(ProtoOutputStream* proto, const std::string& name)
446 {
447 uint64_t fieldId = mTable->mFields[name];
448 uint64_t token = proto->start(fieldId);
449 mPreviousField = name;
450 mTokens.push(token);
451 }
452
453 void
endSession(ProtoOutputStream * proto)454 Message::endSession(ProtoOutputStream* proto)
455 {
456 if (mPreviousField == "") return;
457 if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
458 mSubMessages[mPreviousField]->endSession(proto);
459 }
460 proto->end(mTokens.top());
461 mTokens.pop();
462 mPreviousField = "";
463 }
464