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 mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
241 }
242
~Reader()243 Reader::~Reader()
244 {
245 if (mFile != nullptr) fclose(mFile);
246 }
247
readLine(std::string * line)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
ok(std::string * error)264 bool Reader::ok(std::string* error) {
265 error->assign(mStatus);
266 return mStatus.empty();
267 }
268
269 // ==============================================================================
Table(const char * names[],const uint64_t ids[],const int count)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
~Table()281 Table::~Table()
282 {
283 }
284
285 void
addEnumTypeMap(const char * field,const char * enumNames[],const int enumValues[],const int enumSize)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
addEnumNameToValue(const char * enumName,const int enumValue)301 Table::addEnumNameToValue(const char* enumName, const int enumValue)
302 {
303 mEnumValuesByName[enumName] = enumValue;
304 }
305
306 bool
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)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 // ================================================================================
Message(Table * table)382 Message::Message(Table* table)
383 :mTable(table),
384 mPreviousField(""),
385 mTokens(),
386 mSubMessages()
387 {
388 }
389
~Message()390 Message::~Message()
391 {
392 }
393
394 void
addSubMessage(uint64_t fieldId,Message * fieldMsg)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
insertField(ProtoOutputStream * proto,const std::string & name,const std::string & value)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
startSession(ProtoOutputStream * proto,const std::string & name)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
endSession(ProtoOutputStream * proto)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