1 /*
2 * Copyright (C) 2007-2016 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 #include <assert.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mman.h>
27
28 #include <functional>
29 #include <string>
30 #include <string_view>
31 #include <unordered_map>
32
33 #include <log/event_tag_map.h>
34 #include <private/android_logger.h>
35 #include <utils/FastStrcmp.h>
36 #include <utils/RWLock.h>
37
38 #define OUT_TAG "EventTagMap"
39
40 typedef std::pair<std::string_view, std::string_view> TagFmt;
41
42 // Map
43 struct EventTagMap {
44 #define NUM_MAPS 2
45 // memory-mapped source file; we get strings from here
46 void* mapAddr[NUM_MAPS];
47 size_t mapLen[NUM_MAPS];
48
49 private:
50 std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
51 std::unordered_map<std::string_view, uint32_t> Tag2Idx;
52 // protect unordered sets
53 android::RWLock rwlock;
54
55 public:
EventTagMapEventTagMap56 EventTagMap() {
57 memset(mapAddr, 0, sizeof(mapAddr));
58 memset(mapLen, 0, sizeof(mapLen));
59 }
60
~EventTagMapEventTagMap61 ~EventTagMap() {
62 Idx2TagFmt.clear();
63 Tag2Idx.clear();
64 for (size_t which = 0; which < NUM_MAPS; ++which) {
65 if (mapAddr[which]) {
66 munmap(mapAddr[which], mapLen[which]);
67 mapAddr[which] = 0;
68 }
69 }
70 }
71
72 bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
73 const TagFmt* find(uint32_t tag) const;
74 int find(std::string_view tag) const;
75 };
76
emplaceUnique(uint32_t tag,const TagFmt & tagfmt,bool verbose)77 bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
78 bool verbose) {
79 bool ret = true;
80 static const char errorFormat[] =
81 OUT_TAG ": duplicate tag entries %" PRIu32 ":%.*s:%.*s and %" PRIu32
82 ":%.*s:%.*s)\n";
83 android::RWLock::AutoWLock writeLock(rwlock);
84 {
85 auto it = Idx2TagFmt.find(tag);
86 if (it != Idx2TagFmt.end()) {
87 if (verbose) {
88 fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
89 it->second.first.data(), (int)it->second.second.length(),
90 it->second.second.data(), tag, (int)tagfmt.first.length(),
91 tagfmt.first.data(), (int)tagfmt.second.length(),
92 tagfmt.second.data());
93 }
94 ret = false;
95 } else {
96 Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
97 }
98 }
99
100 {
101 auto it = Tag2Idx.find(tagfmt.first);
102 if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
103 Tag2Idx.erase(it);
104 it = Tag2Idx.end();
105 }
106 if (it == Tag2Idx.end()) {
107 Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
108 }
109 }
110
111 return ret;
112 }
113
find(uint32_t tag) const114 const TagFmt* EventTagMap::find(uint32_t tag) const {
115 android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
116 auto it = Idx2TagFmt.find(tag);
117 if (it == Idx2TagFmt.end()) return NULL;
118 return &(it->second);
119 }
120
find(std::string_view tag) const121 int EventTagMap::find(std::string_view tag) const {
122 android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
123 auto it = Tag2Idx.find(std::move(tag));
124 if (it == Tag2Idx.end()) return -1;
125 return it->second;
126 }
127
128 // The position after the end of a valid section of the tag string,
129 // caller makes sure delimited appropriately.
endOfTag(const char * cp)130 static const char* endOfTag(const char* cp) {
131 while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
132 return cp;
133 }
134
135 // Scan one tag line.
136 //
137 // "pData" should be pointing to the first digit in the tag number. On
138 // successful return, it will be pointing to the last character in the
139 // tag line (i.e. the character before the start of the next line).
140 //
141 // Returns 0 on success, nonzero on failure.
scanTagLine(EventTagMap * map,const char * & pData,int line_num)142 static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
143 char* ep;
144 unsigned long val = strtoul(pData, &ep, 10);
145 const char* cp = ep;
146 if (cp == pData) {
147 fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
148 errno = EINVAL;
149 return -1;
150 }
151
152 uint32_t tagIndex = val;
153 if (tagIndex != val) {
154 fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
155 errno = ERANGE;
156 return -1;
157 }
158
159 while ((*++cp != '\n') && isspace(*cp)) {
160 }
161
162 if (*cp == '\n') {
163 fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
164 errno = EINVAL;
165 return -1;
166 }
167
168 const char* tag = cp;
169 cp = endOfTag(cp);
170 size_t tagLen = cp - tag;
171
172 if (!isspace(*cp)) {
173 fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
174 errno = EINVAL;
175 return -1;
176 }
177
178 while (isspace(*cp) && (*cp != '\n')) ++cp;
179 const char* fmt = NULL;
180 size_t fmtLen = 0;
181 if (*cp && (*cp != '#')) {
182 fmt = cp;
183 while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
184 while ((cp > fmt) && isspace(*(cp - 1))) --cp;
185 fmtLen = cp - fmt;
186 }
187
188 // KISS Only report identicals if they are global
189 // Ideally we want to check if there are identicals
190 // recorded for the same uid, but recording that
191 // unused detail in our database is too burdensome.
192 bool verbose = true;
193 while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
194 if (*cp == '#') {
195 do {
196 ++cp;
197 } while (isspace(*cp) && (*cp != '\n'));
198 verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
199 }
200
201 while (*cp && (*cp != '\n')) ++cp;
202 #ifdef DEBUG
203 fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
204 #endif
205 pData = cp;
206
207 if (map->emplaceUnique(
208 tagIndex,
209 TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
210 verbose)) {
211 return 0;
212 }
213 errno = EMLINK;
214 return -1;
215 }
216
217 static const char* eventTagFiles[NUM_MAPS] = {
218 EVENT_TAG_MAP_FILE, "/dev/event-log-tags",
219 };
220
221 // Parse the tags out of the file.
parseMapLines(EventTagMap * map,size_t which)222 static int parseMapLines(EventTagMap* map, size_t which) {
223 const char* cp = static_cast<char*>(map->mapAddr[which]);
224 size_t len = map->mapLen[which];
225 const char* endp = cp + len;
226
227 // insist on EOL at EOF; simplifies parsing and null-termination
228 if (!len || (*(endp - 1) != '\n')) {
229 #ifdef DEBUG
230 fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
231 which, len);
232 #endif
233 if (which) { // do not propagate errors for other files
234 return 0;
235 }
236 errno = EINVAL;
237 return -1;
238 }
239
240 bool lineStart = true;
241 int lineNum = 1;
242 while (cp < endp) {
243 if (*cp == '\n') {
244 lineStart = true;
245 lineNum++;
246 } else if (lineStart) {
247 if (*cp == '#') {
248 // comment; just scan to end
249 lineStart = false;
250 } else if (isdigit(*cp)) {
251 // looks like a tag; scan it out
252 if (scanTagLine(map, cp, lineNum) != 0) {
253 if (!which || (errno != EMLINK)) {
254 return -1;
255 }
256 }
257 lineNum++; // we eat the '\n'
258 // leave lineStart==true
259 } else if (isspace(*cp)) {
260 // looks like leading whitespace; keep scanning
261 } else {
262 fprintf(stderr,
263 OUT_TAG
264 ": unexpected chars (0x%02x) in tag number on line %d\n",
265 *cp, lineNum);
266 errno = EINVAL;
267 return -1;
268 }
269 } else {
270 // this is a blank or comment line
271 }
272 cp++;
273 }
274
275 return 0;
276 }
277
278 // Open the map file and allocate a structure to manage it.
279 //
280 // We create a private mapping because we want to terminate the log tag
281 // strings with '\0'.
android_openEventTagMap(const char * fileName)282 EventTagMap* android_openEventTagMap(const char* fileName) {
283 EventTagMap* newTagMap;
284 off_t end[NUM_MAPS];
285 int save_errno, fd[NUM_MAPS];
286 size_t which;
287
288 memset(fd, -1, sizeof(fd));
289 memset(end, 0, sizeof(end));
290
291 for (which = 0; which < NUM_MAPS; ++which) {
292 const char* tagfile = fileName ? fileName : eventTagFiles[which];
293
294 fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
295 if (fd[which] < 0) {
296 if (!which) {
297 save_errno = errno;
298 fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n", tagfile,
299 strerror(save_errno));
300 goto fail_errno;
301 }
302 continue;
303 }
304 end[which] = lseek(fd[which], 0L, SEEK_END);
305 save_errno = errno;
306 (void)lseek(fd[which], 0L, SEEK_SET);
307 if (!which && (end[0] < 0)) {
308 fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n", tagfile,
309 strerror(save_errno));
310 goto fail_close;
311 }
312 if (fileName) break; // Only allow one as specified
313 }
314
315 newTagMap = new EventTagMap;
316 if (newTagMap == NULL) {
317 save_errno = errno;
318 goto fail_close;
319 }
320
321 for (which = 0; which < NUM_MAPS; ++which) {
322 if (fd[which] >= 0) {
323 newTagMap->mapAddr[which] =
324 mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
325 which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
326 save_errno = errno;
327 close(fd[which]); /* fd DONE */
328 fd[which] = -1;
329 if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
330 (newTagMap->mapAddr[which] != NULL)) {
331 newTagMap->mapLen[which] = end[which];
332 } else if (!which) {
333 const char* tagfile = fileName ? fileName : eventTagFiles[which];
334
335 fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n", tagfile,
336 strerror(save_errno));
337 goto fail_unmap;
338 }
339 }
340 }
341
342 for (which = 0; which < NUM_MAPS; ++which) {
343 if (parseMapLines(newTagMap, which) != 0) {
344 delete newTagMap;
345 return NULL;
346 }
347 /* See 'fd DONE' comments above and below, no need to clean up here */
348 }
349
350 return newTagMap;
351
352 fail_unmap:
353 save_errno = EINVAL;
354 delete newTagMap;
355 fail_close:
356 for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
357 fail_errno:
358 errno = save_errno;
359 return NULL;
360 }
361
362 // Close the map.
android_closeEventTagMap(EventTagMap * map)363 void android_closeEventTagMap(EventTagMap* map) {
364 if (map) delete map;
365 }
366
367 // Look up an entry in the map.
android_lookupEventTag_len(const EventTagMap * map,size_t * len,unsigned int tag)368 const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
369 if (len) *len = 0;
370 const TagFmt* str = map->find(tag);
371 if (!str) return NULL;
372 if (len) *len = str->first.length();
373 return str->first.data();
374 }
375
376 // Look up an entry in the map.
android_lookupEventFormat_len(const EventTagMap * map,size_t * len,unsigned int tag)377 const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
378 if (len) *len = 0;
379 const TagFmt* str = map->find(tag);
380 if (!str) return NULL;
381 if (len) *len = str->second.length();
382 return str->second.data();
383 }
384
385