• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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