Home | History | Annotate | Download | only in liblog
      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 <experimental/string_view>
     29 #include <functional>
     30 #include <string>
     31 #include <unordered_map>
     32 
     33 #include <log/event_tag_map.h>
     34 #include <log/log_properties.h>
     35 #include <private/android_logger.h>
     36 #include <utils/FastStrcmp.h>
     37 #include <utils/RWLock.h>
     38 
     39 #include "log_portability.h"
     40 #include "logd_reader.h"
     41 
     42 #define OUT_TAG "EventTagMap"
     43 
     44 class MapString {
     45  private:
     46   const std::string* alloc;                  // HAS-AN
     47   const std::experimental::string_view str;  // HAS-A
     48 
     49  public:
     50   operator const std::experimental::string_view() const {
     51     return str;
     52   }
     53 
     54   const char* data() const {
     55     return str.data();
     56   }
     57   size_t length() const {
     58     return str.length();
     59   }
     60 
     61   bool operator==(const MapString& rval) const {
     62     if (length() != rval.length()) return false;
     63     if (length() == 0) return true;
     64     return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
     65   }
     66   bool operator!=(const MapString& rval) const {
     67     return !(*this == rval);
     68   }
     69 
     70   MapString(const char* str, size_t len) : alloc(NULL), str(str, len) {
     71   }
     72   explicit MapString(const std::string& str)
     73       : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
     74   }
     75   MapString(MapString&& rval)
     76       : alloc(rval.alloc), str(rval.data(), rval.length()) {
     77     rval.alloc = NULL;
     78   }
     79   explicit MapString(const MapString& rval)
     80       : alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
     81         str(alloc ? alloc->data() : rval.data(), rval.length()) {
     82   }
     83 
     84   ~MapString() {
     85     if (alloc) delete alloc;
     86   }
     87 };
     88 
     89 // Hash for MapString
     90 template <>
     91 struct std::hash<MapString>
     92     : public std::unary_function<const MapString&, size_t> {
     93   size_t operator()(const MapString& __t) const noexcept {
     94     if (!__t.length()) return 0;
     95     return std::hash<std::experimental::string_view>()(
     96         std::experimental::string_view(__t));
     97   }
     98 };
     99 
    100 typedef std::pair<MapString, MapString> TagFmt;
    101 
    102 template <>
    103 struct std::hash<TagFmt> : public std::unary_function<const TagFmt&, size_t> {
    104   size_t operator()(const TagFmt& __t) const noexcept {
    105     // Tag is typically unique.  Will cost us an extra 100ns for the
    106     // unordered_map lookup if we instead did a hash that combined
    107     // both of tag and fmt members, e.g.:
    108     //
    109     // return std::hash<MapString>()(__t.first) ^
    110     //        std::hash<MapString>()(__t.second);
    111     return std::hash<MapString>()(__t.first);
    112   }
    113 };
    114 
    115 // Map
    116 struct EventTagMap {
    117 #define NUM_MAPS 2
    118   // memory-mapped source file; we get strings from here
    119   void* mapAddr[NUM_MAPS];
    120   size_t mapLen[NUM_MAPS];
    121 
    122  private:
    123   std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
    124   std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
    125   std::unordered_map<MapString, uint32_t> Tag2Idx;
    126   // protect unordered sets
    127   android::RWLock rwlock;
    128 
    129  public:
    130   EventTagMap() {
    131     memset(mapAddr, 0, sizeof(mapAddr));
    132     memset(mapLen, 0, sizeof(mapLen));
    133   }
    134 
    135   ~EventTagMap() {
    136     Idx2TagFmt.clear();
    137     TagFmt2Idx.clear();
    138     Tag2Idx.clear();
    139     for (size_t which = 0; which < NUM_MAPS; ++which) {
    140       if (mapAddr[which]) {
    141         munmap(mapAddr[which], mapLen[which]);
    142         mapAddr[which] = 0;
    143       }
    144     }
    145   }
    146 
    147   bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
    148   const TagFmt* find(uint32_t tag) const;
    149   int find(TagFmt&& tagfmt) const;
    150   int find(MapString&& tag) const;
    151 };
    152 
    153 bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
    154                                 bool verbose) {
    155   bool ret = true;
    156   static const char errorFormat[] =
    157       OUT_TAG ": duplicate tag entries %" PRIu32 ":%.*s:%.*s and %" PRIu32
    158               ":%.*s:%.*s)\n";
    159   android::RWLock::AutoWLock writeLock(rwlock);
    160   {
    161     std::unordered_map<uint32_t, TagFmt>::const_iterator it;
    162     it = Idx2TagFmt.find(tag);
    163     if (it != Idx2TagFmt.end()) {
    164       if (verbose) {
    165         fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
    166                 it->second.first.data(), (int)it->second.second.length(),
    167                 it->second.second.data(), tag, (int)tagfmt.first.length(),
    168                 tagfmt.first.data(), (int)tagfmt.second.length(),
    169                 tagfmt.second.data());
    170       }
    171       ret = false;
    172     } else {
    173       Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
    174     }
    175   }
    176 
    177   {
    178     std::unordered_map<TagFmt, uint32_t>::const_iterator it;
    179     it = TagFmt2Idx.find(tagfmt);
    180     if (it != TagFmt2Idx.end()) {
    181       if (verbose) {
    182         fprintf(stderr, errorFormat, it->second, (int)it->first.first.length(),
    183                 it->first.first.data(), (int)it->first.second.length(),
    184                 it->first.second.data(), tag, (int)tagfmt.first.length(),
    185                 tagfmt.first.data(), (int)tagfmt.second.length(),
    186                 tagfmt.second.data());
    187       }
    188       ret = false;
    189     } else {
    190       TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
    191     }
    192   }
    193 
    194   {
    195     std::unordered_map<MapString, uint32_t>::const_iterator it;
    196     it = Tag2Idx.find(tagfmt.first);
    197     if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
    198       Tag2Idx.erase(it);
    199       it = Tag2Idx.end();
    200     }
    201     if (it == Tag2Idx.end()) {
    202       Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
    203     }
    204   }
    205 
    206   return ret;
    207 }
    208 
    209 const TagFmt* EventTagMap::find(uint32_t tag) const {
    210   std::unordered_map<uint32_t, TagFmt>::const_iterator it;
    211   android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
    212   it = Idx2TagFmt.find(tag);
    213   if (it == Idx2TagFmt.end()) return NULL;
    214   return &(it->second);
    215 }
    216 
    217 int EventTagMap::find(TagFmt&& tagfmt) const {
    218   std::unordered_map<TagFmt, uint32_t>::const_iterator it;
    219   android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
    220   it = TagFmt2Idx.find(std::move(tagfmt));
    221   if (it == TagFmt2Idx.end()) return -1;
    222   return it->second;
    223 }
    224 
    225 int EventTagMap::find(MapString&& tag) const {
    226   std::unordered_map<MapString, uint32_t>::const_iterator it;
    227   android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
    228   it = Tag2Idx.find(std::move(tag));
    229   if (it == Tag2Idx.end()) return -1;
    230   return it->second;
    231 }
    232 
    233 // The position after the end of a valid section of the tag string,
    234 // caller makes sure delimited appropriately.
    235 static const char* endOfTag(const char* cp) {
    236   while (*cp && (isalnum(*cp) || strchr("_.-@,", *cp))) ++cp;
    237   return cp;
    238 }
    239 
    240 // Scan one tag line.
    241 //
    242 // "pData" should be pointing to the first digit in the tag number.  On
    243 // successful return, it will be pointing to the last character in the
    244 // tag line (i.e. the character before the start of the next line).
    245 //
    246 // lineNum = 0 removes verbose comments and requires us to cache the
    247 // content rather than make direct raw references since the content
    248 // will disappear after the call. A non-zero lineNum means we own the
    249 // data and it will outlive the call.
    250 //
    251 // Returns 0 on success, nonzero on failure.
    252 static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
    253   char* ep;
    254   unsigned long val = strtoul(pData, &ep, 10);
    255   const char* cp = ep;
    256   if (cp == pData) {
    257     if (lineNum) {
    258       fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
    259     }
    260     errno = EINVAL;
    261     return -1;
    262   }
    263 
    264   uint32_t tagIndex = val;
    265   if (tagIndex != val) {
    266     if (lineNum) {
    267       fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
    268     }
    269     errno = ERANGE;
    270     return -1;
    271   }
    272 
    273   while ((*++cp != '\n') && isspace(*cp)) {
    274   }
    275 
    276   if (*cp == '\n') {
    277     if (lineNum) {
    278       fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
    279     }
    280     errno = EINVAL;
    281     return -1;
    282   }
    283 
    284   const char* tag = cp;
    285   cp = endOfTag(cp);
    286   size_t tagLen = cp - tag;
    287 
    288   if (!isspace(*cp)) {
    289     if (lineNum) {
    290       fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
    291               lineNum);
    292     }
    293     errno = EINVAL;
    294     return -1;
    295   }
    296 
    297   while (isspace(*cp) && (*cp != '\n')) ++cp;
    298   const char* fmt = NULL;
    299   size_t fmtLen = 0;
    300   if (*cp && (*cp != '#')) {
    301     fmt = cp;
    302     while (*cp && (*cp != '\n') && (*cp != '#')) ++cp;
    303     while ((cp > fmt) && isspace(*(cp - 1))) --cp;
    304     fmtLen = cp - fmt;
    305   }
    306 
    307   // KISS Only report identicals if they are global
    308   // Ideally we want to check if there are identicals
    309   // recorded for the same uid, but recording that
    310   // unused detail in our database is too burdensome.
    311   bool verbose = true;
    312   while (*cp && (*cp != '#') && (*cp != '\n')) ++cp;
    313   if (*cp == '#') {
    314     do {
    315       ++cp;
    316     } while (isspace(*cp) && (*cp != '\n'));
    317     verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
    318   }
    319 
    320   while (*cp && (*cp != '\n')) ++cp;
    321 #ifdef DEBUG
    322   fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
    323 #endif
    324   pData = cp;
    325 
    326   if (lineNum) {
    327     if (map->emplaceUnique(tagIndex,
    328                            TagFmt(std::make_pair(MapString(tag, tagLen),
    329                                                  MapString(fmt, fmtLen))),
    330                            verbose)) {
    331       return 0;
    332     }
    333   } else {
    334     // cache
    335     if (map->emplaceUnique(
    336             tagIndex,
    337             TagFmt(std::make_pair(MapString(std::string(tag, tagLen)),
    338                                   MapString(std::string(fmt, fmtLen)))))) {
    339       return 0;
    340     }
    341   }
    342   errno = EMLINK;
    343   return -1;
    344 }
    345 
    346 static const char* eventTagFiles[NUM_MAPS] = {
    347   EVENT_TAG_MAP_FILE, "/dev/event-log-tags",
    348 };
    349 
    350 // Parse the tags out of the file.
    351 static int parseMapLines(EventTagMap* map, size_t which) {
    352   const char* cp = static_cast<char*>(map->mapAddr[which]);
    353   size_t len = map->mapLen[which];
    354   const char* endp = cp + len;
    355 
    356   // insist on EOL at EOF; simplifies parsing and null-termination
    357   if (!len || (*(endp - 1) != '\n')) {
    358 #ifdef DEBUG
    359     fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
    360             which, len);
    361 #endif
    362     if (which) {  // do not propagate errors for other files
    363       return 0;
    364     }
    365     errno = EINVAL;
    366     return -1;
    367   }
    368 
    369   bool lineStart = true;
    370   int lineNum = 1;
    371   while (cp < endp) {
    372     if (*cp == '\n') {
    373       lineStart = true;
    374       lineNum++;
    375     } else if (lineStart) {
    376       if (*cp == '#') {
    377         // comment; just scan to end
    378         lineStart = false;
    379       } else if (isdigit(*cp)) {
    380         // looks like a tag; scan it out
    381         if (scanTagLine(map, cp, lineNum) != 0) {
    382           if (!which || (errno != EMLINK)) {
    383             return -1;
    384           }
    385         }
    386         lineNum++;  // we eat the '\n'
    387                     // leave lineStart==true
    388       } else if (isspace(*cp)) {
    389         // looks like leading whitespace; keep scanning
    390       } else {
    391         fprintf(stderr,
    392                 OUT_TAG
    393                 ": unexpected chars (0x%02x) in tag number on line %d\n",
    394                 *cp, lineNum);
    395         errno = EINVAL;
    396         return -1;
    397       }
    398     } else {
    399       // this is a blank or comment line
    400     }
    401     cp++;
    402   }
    403 
    404   return 0;
    405 }
    406 
    407 // Open the map file and allocate a structure to manage it.
    408 //
    409 // We create a private mapping because we want to terminate the log tag
    410 // strings with '\0'.
    411 LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
    412   EventTagMap* newTagMap;
    413   off_t end[NUM_MAPS];
    414   int save_errno, fd[NUM_MAPS];
    415   size_t which;
    416 
    417   memset(fd, -1, sizeof(fd));
    418   memset(end, 0, sizeof(end));
    419 
    420   for (which = 0; which < NUM_MAPS; ++which) {
    421     const char* tagfile = fileName ? fileName : eventTagFiles[which];
    422 
    423     fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
    424     if (fd[which] < 0) {
    425       if (!which) {
    426         save_errno = errno;
    427         fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n", tagfile,
    428                 strerror(save_errno));
    429         goto fail_errno;
    430       }
    431       continue;
    432     }
    433     end[which] = lseek(fd[which], 0L, SEEK_END);
    434     save_errno = errno;
    435     (void)lseek(fd[which], 0L, SEEK_SET);
    436     if (!which && (end[0] < 0)) {
    437       fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n", tagfile,
    438               strerror(save_errno));
    439       goto fail_close;
    440     }
    441     if (fileName) break;  // Only allow one as specified
    442   }
    443 
    444   newTagMap = new EventTagMap;
    445   if (newTagMap == NULL) {
    446     save_errno = errno;
    447     goto fail_close;
    448   }
    449 
    450   for (which = 0; which < NUM_MAPS; ++which) {
    451     if (fd[which] >= 0) {
    452       newTagMap->mapAddr[which] =
    453           mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
    454                which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
    455       save_errno = errno;
    456       close(fd[which]); /* fd DONE */
    457       fd[which] = -1;
    458       if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
    459           (newTagMap->mapAddr[which] != NULL)) {
    460         newTagMap->mapLen[which] = end[which];
    461       } else if (!which) {
    462         const char* tagfile = fileName ? fileName : eventTagFiles[which];
    463 
    464         fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n", tagfile,
    465                 strerror(save_errno));
    466         goto fail_unmap;
    467       }
    468     }
    469   }
    470 
    471   for (which = 0; which < NUM_MAPS; ++which) {
    472     if (parseMapLines(newTagMap, which) != 0) {
    473       delete newTagMap;
    474       return NULL;
    475     }
    476     /* See 'fd DONE' comments above and below, no need to clean up here */
    477   }
    478 
    479   return newTagMap;
    480 
    481 fail_unmap:
    482   save_errno = EINVAL;
    483   delete newTagMap;
    484 fail_close:
    485   for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
    486 fail_errno:
    487   errno = save_errno;
    488   return NULL;
    489 }
    490 
    491 // Close the map.
    492 LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
    493   if (map) delete map;
    494 }
    495 
    496 // Cache miss, go to logd to acquire a public reference.
    497 // Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
    498 static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
    499   // call event tag service to arrange for a new tag
    500   char* buf = NULL;
    501   // Can not use android::base::StringPrintf, asprintf + free instead.
    502   static const char command_template[] = "getEventTag id=%u";
    503   int ret = asprintf(&buf, command_template, tag);
    504   if (ret > 0) {
    505     // Add some buffer margin for an estimate of the full return content.
    506     size_t size =
    507         ret - strlen(command_template) +
    508         strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
    509     if (size > (size_t)ret) {
    510       char* np = static_cast<char*>(realloc(buf, size));
    511       if (np) {
    512         buf = np;
    513       } else {
    514         size = ret;
    515       }
    516     } else {
    517       size = ret;
    518     }
    519     // Ask event log tag service for an existing entry
    520     if (__send_log_msg(buf, size) >= 0) {
    521       buf[size - 1] = '\0';
    522       char* ep;
    523       unsigned long val = strtoul(buf, &ep, 10);  // return size
    524       const char* cp = ep;
    525       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
    526         ++cp;
    527         if (!scanTagLine(map, cp, 0)) {
    528           free(buf);
    529           return map->find(tag);
    530         }
    531       }
    532     }
    533     free(buf);
    534   }
    535   return NULL;
    536 }
    537 
    538 // Look up an entry in the map.
    539 LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
    540                                                          size_t* len,
    541                                                          unsigned int tag) {
    542   if (len) *len = 0;
    543   const TagFmt* str = map->find(tag);
    544   if (!str) {
    545     str = __getEventTag(const_cast<EventTagMap*>(map), tag);
    546   }
    547   if (!str) return NULL;
    548   if (len) *len = str->first.length();
    549   return str->first.data();
    550 }
    551 
    552 // Look up an entry in the map.
    553 LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
    554     const EventTagMap* map, size_t* len, unsigned int tag) {
    555   if (len) *len = 0;
    556   const TagFmt* str = map->find(tag);
    557   if (!str) {
    558     str = __getEventTag(const_cast<EventTagMap*>(map), tag);
    559   }
    560   if (!str) return NULL;
    561   if (len) *len = str->second.length();
    562   return str->second.data();
    563 }
    564 
    565 // This function is deprecated and replaced with android_lookupEventTag_len
    566 // since it will cause the map to change from Shared and backed by a file,
    567 // to Private Dirty and backed up by swap, albeit highly compressible. By
    568 // deprecating this function everywhere, we save 100s of MB of memory space.
    569 LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
    570                                                      unsigned int tag) {
    571   size_t len;
    572   const char* tagStr = android_lookupEventTag_len(map, &len, tag);
    573 
    574   if (!tagStr) return tagStr;
    575   char* cp = const_cast<char*>(tagStr);
    576   cp += len;
    577   if (*cp) *cp = '\0';  // Trigger copy on write :-( and why deprecated.
    578   return tagStr;
    579 }
    580 
    581 // Look up tagname, generate one if necessary, and return a tag
    582 LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
    583                                                 const char* tagname,
    584                                                 const char* format, int prio) {
    585   const char* ep = endOfTag(tagname);
    586   size_t len = ep - tagname;
    587   if (!len || *ep) {
    588     errno = EINVAL;
    589     return -1;
    590   }
    591 
    592   if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
    593       !__android_log_is_loggable_len(prio, tagname, len,
    594                                      __android_log_is_debuggable()
    595                                          ? ANDROID_LOG_VERBOSE
    596                                          : ANDROID_LOG_DEBUG)) {
    597     errno = EPERM;
    598     return -1;
    599   }
    600 
    601   if (!format) format = "";
    602   ssize_t fmtLen = strlen(format);
    603   int ret = map->find(TagFmt(
    604       std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
    605   if (ret != -1) return ret;
    606 
    607   // call event tag service to arrange for a new tag
    608   char* buf = NULL;
    609   // Can not use android::base::StringPrintf, asprintf + free instead.
    610   static const char command_template[] = "getEventTag name=%s format=\"%s\"";
    611   ret = asprintf(&buf, command_template, tagname, format);
    612   if (ret > 0) {
    613     // Add some buffer margin for an estimate of the full return content.
    614     char* cp;
    615     size_t size =
    616         ret - strlen(command_template) +
    617         strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
    618     if (size > (size_t)ret) {
    619       cp = static_cast<char*>(realloc(buf, size));
    620       if (cp) {
    621         buf = cp;
    622       } else {
    623         size = ret;
    624       }
    625     } else {
    626       size = ret;
    627     }
    628     // Ask event log tag service for an allocation
    629     if (__send_log_msg(buf, size) >= 0) {
    630       buf[size - 1] = '\0';
    631       unsigned long val = strtoul(buf, &cp, 10);        // return size
    632       if ((buf != cp) && (val > 0) && (*cp == '\n')) {  // truncation OK
    633         val = strtoul(cp + 1, &cp, 10);                 // allocated tag number
    634         if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
    635           free(buf);
    636           ret = val;
    637           // cache
    638           map->emplaceUnique(ret, TagFmt(std::make_pair(
    639                                       MapString(std::string(tagname, len)),
    640                                       MapString(std::string(format, fmtLen)))));
    641           return ret;
    642         }
    643       }
    644     }
    645     free(buf);
    646   }
    647 
    648   // Hail Mary
    649   ret = map->find(MapString(tagname, len));
    650   if (ret == -1) errno = ESRCH;
    651   return ret;
    652 }
    653