Home | History | Annotate | Download | only in liblog
      1 /*
      2 ** Copyright 2014, 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 <ctype.h>
     18 #include <pthread.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
     22 #include <sys/_system_properties.h>
     23 
     24 #include <android/log.h>
     25 
     26 #include "log_portability.h"
     27 
     28 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
     29 
     30 static int lock()
     31 {
     32     /*
     33      * If we trigger a signal handler in the middle of locked activity and the
     34      * signal handler logs a message, we could get into a deadlock state.
     35      */
     36     /*
     37      *  Any contention, and we can turn around and use the non-cached method
     38      * in less time than the system call associated with a mutex to deal with
     39      * the contention.
     40      */
     41     return pthread_mutex_trylock(&lock_loggable);
     42 }
     43 
     44 static void unlock()
     45 {
     46     pthread_mutex_unlock(&lock_loggable);
     47 }
     48 
     49 struct cache {
     50     const prop_info *pinfo;
     51     uint32_t serial;
     52     unsigned char c;
     53 };
     54 
     55 static int check_cache(struct cache *cache)
     56 {
     57     return cache->pinfo
     58         && __system_property_serial(cache->pinfo) != cache->serial;
     59 }
     60 
     61 #define BOOLEAN_TRUE 0xFF
     62 #define BOOLEAN_FALSE 0xFE
     63 
     64 static void refresh_cache(struct cache *cache, const char *key)
     65 {
     66     char buf[PROP_VALUE_MAX];
     67 
     68     if (!cache->pinfo) {
     69         cache->pinfo = __system_property_find(key);
     70         if (!cache->pinfo) {
     71             return;
     72         }
     73     }
     74     cache->serial = __system_property_serial(cache->pinfo);
     75     __system_property_read(cache->pinfo, 0, buf);
     76     switch(buf[0]) {
     77     case 't': case 'T':
     78         cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE;
     79         break;
     80     case 'f': case 'F':
     81         cache->c = strcasecmp(buf + 1, "alse") ? buf[0] : BOOLEAN_FALSE;
     82         break;
     83     default:
     84         cache->c = buf[0];
     85     }
     86 }
     87 
     88 static int __android_log_level(const char *tag, int default_prio)
     89 {
     90     /* sizeof() is used on this array below */
     91     static const char log_namespace[] = "persist.log.tag.";
     92     static const size_t base_offset = 8; /* skip "persist." */
     93     /* calculate the size of our key temporary buffer */
     94     const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
     95     /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
     96     char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */
     97     char *kp;
     98     size_t i;
     99     char c = 0;
    100     /*
    101      * Single layer cache of four properties. Priorities are:
    102      *    log.tag.<tag>
    103      *    persist.log.tag.<tag>
    104      *    log.tag
    105      *    persist.log.tag
    106      * Where the missing tag matches all tags and becomes the
    107      * system global default. We do not support ro.log.tag* .
    108      */
    109     static char last_tag[PROP_NAME_MAX];
    110     static uint32_t global_serial;
    111     /* some compilers erroneously see uninitialized use. !not_locked */
    112     uint32_t current_global_serial = 0;
    113     static struct cache tag_cache[2];
    114     static struct cache global_cache[2];
    115     int change_detected;
    116     int global_change_detected;
    117     int not_locked;
    118 
    119     strcpy(key, log_namespace);
    120 
    121     global_change_detected = change_detected = not_locked = lock();
    122 
    123     if (!not_locked) {
    124         /*
    125          *  check all known serial numbers to changes.
    126          */
    127         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
    128             if (check_cache(&tag_cache[i])) {
    129                 change_detected = 1;
    130             }
    131         }
    132         for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
    133             if (check_cache(&global_cache[i])) {
    134                 global_change_detected = 1;
    135             }
    136         }
    137 
    138         current_global_serial = __system_property_area_serial();
    139         if (current_global_serial != global_serial) {
    140             change_detected = 1;
    141             global_change_detected = 1;
    142         }
    143     }
    144 
    145     if (taglen) {
    146         int local_change_detected = change_detected;
    147         if (!not_locked) {
    148             if (!last_tag[0]
    149                     || (last_tag[0] != tag[0])
    150                     || strncmp(last_tag + 1, tag + 1, sizeof(last_tag) - 1)) {
    151                 /* invalidate log.tag.<tag> cache */
    152                 for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
    153                     tag_cache[i].pinfo = NULL;
    154                     tag_cache[i].c = '\0';
    155                 }
    156                 last_tag[0] = '\0';
    157                 local_change_detected = 1;
    158             }
    159             if (!last_tag[0]) {
    160                 strncpy(last_tag, tag, sizeof(last_tag));
    161             }
    162         }
    163         strcpy(key + sizeof(log_namespace) - 1, tag);
    164 
    165         kp = key;
    166         for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
    167             struct cache *cache = &tag_cache[i];
    168             struct cache temp_cache;
    169 
    170             if (not_locked) {
    171                 temp_cache.pinfo = NULL;
    172                 temp_cache.c = '\0';
    173                 cache = &temp_cache;
    174             }
    175             if (local_change_detected) {
    176                 refresh_cache(cache, kp);
    177             }
    178 
    179             if (cache->c) {
    180                 c = cache->c;
    181                 break;
    182             }
    183 
    184             kp = key + base_offset;
    185         }
    186     }
    187 
    188     switch (toupper(c)) { /* if invalid, resort to global */
    189     case 'V':
    190     case 'D':
    191     case 'I':
    192     case 'W':
    193     case 'E':
    194     case 'F': /* Not officially supported */
    195     case 'A':
    196     case 'S':
    197     case BOOLEAN_FALSE: /* Not officially supported */
    198         break;
    199     default:
    200         /* clear '.' after log.tag */
    201         key[sizeof(log_namespace) - 2] = '\0';
    202 
    203         kp = key;
    204         for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
    205             struct cache *cache = &global_cache[i];
    206             struct cache temp_cache;
    207 
    208             if (not_locked) {
    209                 temp_cache = *cache;
    210                 if (temp_cache.pinfo != cache->pinfo) { /* check atomic */
    211                     temp_cache.pinfo = NULL;
    212                     temp_cache.c = '\0';
    213                 }
    214                 cache = &temp_cache;
    215             }
    216             if (global_change_detected) {
    217                 refresh_cache(cache, kp);
    218             }
    219 
    220             if (cache->c) {
    221                 c = cache->c;
    222                 break;
    223             }
    224 
    225             kp = key + base_offset;
    226         }
    227         break;
    228     }
    229 
    230     if (!not_locked) {
    231         global_serial = current_global_serial;
    232         unlock();
    233     }
    234 
    235     switch (toupper(c)) {
    236     case 'V': return ANDROID_LOG_VERBOSE;
    237     case 'D': return ANDROID_LOG_DEBUG;
    238     case 'I': return ANDROID_LOG_INFO;
    239     case 'W': return ANDROID_LOG_WARN;
    240     case 'E': return ANDROID_LOG_ERROR;
    241     case 'F': /* FALLTHRU */ /* Not officially supported */
    242     case 'A': return ANDROID_LOG_FATAL;
    243     case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
    244     case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
    245     }
    246     return default_prio;
    247 }
    248 
    249 LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char *tag,
    250                                                 int default_prio)
    251 {
    252     int logLevel = __android_log_level(tag, default_prio);
    253     return logLevel >= 0 && prio >= logLevel;
    254 }
    255 
    256 LIBLOG_HIDDEN int __android_log_is_debuggable()
    257 {
    258     static uint32_t serial;
    259     static struct cache tag_cache;
    260     static const char key[] = "ro.debuggable";
    261     int ret;
    262 
    263     if (tag_cache.c) { /* ro property does not change after set */
    264         ret = tag_cache.c == '1';
    265     } else if (lock()) {
    266         struct cache temp_cache = { NULL, -1, '\0' };
    267         refresh_cache(&temp_cache, key);
    268         ret = temp_cache.c == '1';
    269     } else {
    270         int change_detected = check_cache(&tag_cache);
    271         uint32_t current_serial = __system_property_area_serial();
    272         if (current_serial != serial) {
    273             change_detected = 1;
    274         }
    275         if (change_detected) {
    276             refresh_cache(&tag_cache, key);
    277             serial = current_serial;
    278         }
    279         ret = tag_cache.c == '1';
    280 
    281         unlock();
    282     }
    283 
    284     return ret;
    285 }
    286 
    287 /*
    288  * For properties that are read often, but generally remain constant.
    289  * Since a change is rare, we will accept a trylock failure gracefully.
    290  * Use a separate lock from is_loggable to keep contention down b/25563384.
    291  */
    292 struct cache2 {
    293     pthread_mutex_t lock;
    294     uint32_t serial;
    295     const char *key_persist;
    296     struct cache cache_persist;
    297     const char *key_ro;
    298     struct cache cache_ro;
    299     unsigned char (*const evaluate)(const struct cache2 *self);
    300 };
    301 
    302 static inline unsigned char do_cache2(struct cache2 *self)
    303 {
    304     uint32_t current_serial;
    305     int change_detected;
    306     unsigned char c;
    307 
    308     if (pthread_mutex_trylock(&self->lock)) {
    309         /* We are willing to accept some race in this context */
    310         return self->evaluate(self);
    311     }
    312 
    313     change_detected = check_cache(&self->cache_persist)
    314                    || check_cache(&self->cache_ro);
    315     current_serial = __system_property_area_serial();
    316     if (current_serial != self->serial) {
    317         change_detected = 1;
    318     }
    319     if (change_detected) {
    320         refresh_cache(&self->cache_persist, self->key_persist);
    321         refresh_cache(&self->cache_ro, self->key_ro);
    322         self->serial = current_serial;
    323     }
    324     c = self->evaluate(self);
    325 
    326     pthread_mutex_unlock(&self->lock);
    327 
    328     return c;
    329 }
    330 
    331 static unsigned char evaluate_persist_ro(const struct cache2 *self)
    332 {
    333     unsigned char c = self->cache_persist.c;
    334 
    335     if (c) {
    336         return c;
    337     }
    338 
    339     return self->cache_ro.c;
    340 }
    341 
    342 /*
    343  * Timestamp state generally remains constant, but can change at any time
    344  * to handle developer requirements.
    345  */
    346 LIBLOG_ABI_PUBLIC clockid_t android_log_clockid()
    347 {
    348     static struct cache2 clockid = {
    349         PTHREAD_MUTEX_INITIALIZER,
    350         0,
    351         "persist.logd.timestamp",
    352         { NULL, -1, '\0' },
    353         "ro.logd.timestamp",
    354         { NULL, -1, '\0' },
    355         evaluate_persist_ro
    356     };
    357 
    358     return (tolower(do_cache2(&clockid)) == 'm')
    359         ? CLOCK_MONOTONIC
    360         : CLOCK_REALTIME;
    361 }
    362 
    363 /*
    364  * Security state generally remains constant, but the DO must be able
    365  * to turn off logging should it become spammy after an attack is detected.
    366  */
    367 static unsigned char evaluate_security(const struct cache2 *self)
    368 {
    369     unsigned char c = self->cache_ro.c;
    370 
    371     return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
    372 }
    373 
    374 LIBLOG_ABI_PUBLIC int __android_log_security()
    375 {
    376     static struct cache2 security = {
    377         PTHREAD_MUTEX_INITIALIZER,
    378         0,
    379         "persist.logd.security",
    380         { NULL, -1, BOOLEAN_FALSE },
    381         "ro.device_owner",
    382         { NULL, -1, BOOLEAN_FALSE },
    383         evaluate_security
    384     };
    385 
    386     return do_cache2(&security);
    387 }
    388