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 <errno.h>
18 #include <stdatomic.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/time.h>
22 
23 #ifdef __BIONIC__
24 #include <android/set_abort_message.h>
25 #endif
26 
27 #include <log/event_tag_map.h>
28 #include <log/log_transport.h>
29 #include <private/android_filesystem_config.h>
30 #include <private/android_logger.h>
31 
32 #include "config_read.h" /* __android_log_config_read_close() definition */
33 #include "config_write.h"
34 #include "log_portability.h"
35 #include "logger.h"
36 
37 #define LOG_BUF_SIZE 1024
38 
39 static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
40 static int (*write_to_log)(log_id_t, struct iovec* vec,
41                            size_t nr) = __write_to_log_init;
42 
43 /*
44  * This is used by the C++ code to decide if it should write logs through
45  * the C code.  Basically, if /dev/socket/logd is available, we're running in
46  * the simulator rather than a desktop tool and want to use the device.
47  */
48 static enum {
49   kLogUninitialized,
50   kLogNotAvailable,
51   kLogAvailable
52 } g_log_status = kLogUninitialized;
53 
check_log_uid_permissions()54 static int check_log_uid_permissions() {
55 #if defined(__ANDROID__)
56   uid_t uid = __android_log_uid();
57 
58   /* Matches clientHasLogCredentials() in logd */
59   if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
60     uid = geteuid();
61     if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
62       gid_t gid = getgid();
63       if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
64         gid = getegid();
65         if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
66           int num_groups;
67           gid_t* groups;
68 
69           num_groups = getgroups(0, NULL);
70           if (num_groups <= 0) {
71             return -EPERM;
72           }
73           groups = calloc(num_groups, sizeof(gid_t));
74           if (!groups) {
75             return -ENOMEM;
76           }
77           num_groups = getgroups(num_groups, groups);
78           while (num_groups > 0) {
79             if (groups[num_groups - 1] == AID_LOG) {
80               break;
81             }
82             --num_groups;
83           }
84           free(groups);
85           if (num_groups <= 0) {
86             return -EPERM;
87           }
88         }
89       }
90     }
91   }
92 #endif
93   return 0;
94 }
95 
__android_log_cache_available(struct android_log_transport_write * node)96 static void __android_log_cache_available(
97     struct android_log_transport_write* node) {
98   size_t i;
99 
100   if (node->logMask) {
101     return;
102   }
103 
104   for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
105     if (node->write && (i != LOG_ID_KERNEL) &&
106         ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
107         (!node->available || ((*node->available)(i) >= 0))) {
108       node->logMask |= 1 << i;
109     }
110   }
111 }
112 
__android_log_dev_available()113 LIBLOG_ABI_PUBLIC int __android_log_dev_available() {
114   struct android_log_transport_write* node;
115 
116   if (list_empty(&__android_log_transport_write)) {
117     return kLogUninitialized;
118   }
119 
120   write_transport_for_each(node, &__android_log_transport_write) {
121     __android_log_cache_available(node);
122     if (node->logMask) {
123       return kLogAvailable;
124     }
125   }
126   return kLogNotAvailable;
127 }
128 
129 #if defined(__ANDROID__)
130 static atomic_uintptr_t tagMap;
131 #endif
132 
133 /*
134  * Release any logger resources. A new log write will immediately re-acquire.
135  */
__android_log_close()136 LIBLOG_ABI_PUBLIC void __android_log_close() {
137   struct android_log_transport_write* transport;
138 #if defined(__ANDROID__)
139   EventTagMap* m;
140 #endif
141 
142   __android_log_lock();
143 
144   write_to_log = __write_to_log_init;
145 
146   /*
147    * Threads that are actively writing at this point are not held back
148    * by a lock and are at risk of dropping the messages with a return code
149    * -EBADF. Prefer to return error code than add the overhead of a lock to
150    * each log writing call to guarantee delivery. In addition, anyone
151    * calling this is doing so to release the logging resources and shut down,
152    * for them to do so with outstanding log requests in other threads is a
153    * disengenuous use of this function.
154    */
155 
156   write_transport_for_each(transport, &__android_log_persist_write) {
157     if (transport->close) {
158       (*transport->close)();
159     }
160   }
161 
162   write_transport_for_each(transport, &__android_log_transport_write) {
163     if (transport->close) {
164       (*transport->close)();
165     }
166   }
167 
168   __android_log_config_write_close();
169 
170 #if defined(__ANDROID__)
171   /*
172    * Additional risk here somewhat mitigated by immediately unlock flushing
173    * the processor cache. The multi-threaded race that we choose to accept,
174    * to minimize locking, is an atomic_load in a writer picking up a value
175    * just prior to entering this routine. There will be an use after free.
176    *
177    * Again, anyone calling this is doing so to release the logging resources
178    * is most probably going to quiesce then shut down; or to restart after
179    * a fork so the risk should be non-existent. For this reason we
180    * choose a mitigation stance for efficiency instead of incuring the cost
181    * of a lock for every log write.
182    */
183   m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0);
184 #endif
185 
186   __android_log_unlock();
187 
188 #if defined(__ANDROID__)
189   if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m);
190 #endif
191 }
192 
193 /* log_init_lock assumed */
__write_to_log_initialize()194 static int __write_to_log_initialize() {
195   struct android_log_transport_write* transport;
196   struct listnode* n;
197   int i = 0, ret = 0;
198 
199   __android_log_config_write();
200   write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
201     __android_log_cache_available(transport);
202     if (!transport->logMask) {
203       list_remove(&transport->node);
204       continue;
205     }
206     if (!transport->open || ((*transport->open)() < 0)) {
207       if (transport->close) {
208         (*transport->close)();
209       }
210       list_remove(&transport->node);
211       continue;
212     }
213     ++ret;
214   }
215   write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
216     __android_log_cache_available(transport);
217     if (!transport->logMask) {
218       list_remove(&transport->node);
219       continue;
220     }
221     if (!transport->open || ((*transport->open)() < 0)) {
222       if (transport->close) {
223         (*transport->close)();
224       }
225       list_remove(&transport->node);
226       continue;
227     }
228     ++i;
229   }
230   if (!ret && !i) {
231     return -ENODEV;
232   }
233 
234   return ret;
235 }
236 
237 /*
238  * Extract a 4-byte value from a byte stream. le32toh open coded
239  */
get4LE(const uint8_t * src)240 static inline uint32_t get4LE(const uint8_t* src) {
241   return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
242 }
243 
__write_to_log_daemon(log_id_t log_id,struct iovec * vec,size_t nr)244 static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
245   struct android_log_transport_write* node;
246   int ret, save_errno;
247   struct timespec ts;
248   size_t len, i;
249 
250   for (len = i = 0; i < nr; ++i) {
251     len += vec[i].iov_len;
252   }
253   if (!len) {
254     return -EINVAL;
255   }
256 
257   save_errno = errno;
258 #if defined(__ANDROID__)
259   clock_gettime(android_log_clockid(), &ts);
260 
261   if (log_id == LOG_ID_SECURITY) {
262     if (vec[0].iov_len < 4) {
263       errno = save_errno;
264       return -EINVAL;
265     }
266 
267     ret = check_log_uid_permissions();
268     if (ret < 0) {
269       errno = save_errno;
270       return ret;
271     }
272     if (!__android_log_security()) {
273       /* If only we could reset downstream logd counter */
274       errno = save_errno;
275       return -EPERM;
276     }
277   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
278     const char* tag;
279     size_t len;
280     EventTagMap *m, *f;
281 
282     if (vec[0].iov_len < 4) {
283       errno = save_errno;
284       return -EINVAL;
285     }
286 
287     tag = NULL;
288     len = 0;
289     f = NULL;
290     m = (EventTagMap*)atomic_load(&tagMap);
291 
292     if (!m) {
293       ret = __android_log_trylock();
294       m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
295       if (!m) {
296         m = android_openEventTagMap(NULL);
297         if (ret) { /* trylock failed, use local copy, mark for close */
298           f = m;
299         } else {
300           if (!m) { /* One chance to open map file */
301             m = (EventTagMap*)(uintptr_t)-1LL;
302           }
303           atomic_store(&tagMap, (uintptr_t)m);
304         }
305       }
306       if (!ret) { /* trylock succeeded, unlock */
307         __android_log_unlock();
308       }
309     }
310     if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
311       tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
312     }
313     ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
314                                         ANDROID_LOG_VERBOSE);
315     if (f) { /* local copy marked for close */
316       android_closeEventTagMap(f);
317     }
318     if (!ret) {
319       errno = save_errno;
320       return -EPERM;
321     }
322   } else {
323     /* Validate the incoming tag, tag content can not split across iovec */
324     char prio = ANDROID_LOG_VERBOSE;
325     const char* tag = vec[0].iov_base;
326     size_t len = vec[0].iov_len;
327     if (!tag) {
328       len = 0;
329     }
330     if (len > 0) {
331       prio = *tag;
332       if (len > 1) {
333         --len;
334         ++tag;
335       } else {
336         len = vec[1].iov_len;
337         tag = ((const char*)vec[1].iov_base);
338         if (!tag) {
339           len = 0;
340         }
341       }
342     }
343     /* tag must be nul terminated */
344     if (tag && strnlen(tag, len) >= len) {
345       tag = NULL;
346     }
347 
348     if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
349       errno = save_errno;
350       return -EPERM;
351     }
352   }
353 #else
354   /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
355   {
356     struct timeval tv;
357     gettimeofday(&tv, NULL);
358     ts.tv_sec = tv.tv_sec;
359     ts.tv_nsec = tv.tv_usec * 1000;
360   }
361 #endif
362 
363   ret = 0;
364   i = 1 << log_id;
365   write_transport_for_each(node, &__android_log_transport_write) {
366     if (node->logMask & i) {
367       ssize_t retval;
368       retval = (*node->write)(log_id, &ts, vec, nr);
369       if (ret >= 0) {
370         ret = retval;
371       }
372     }
373   }
374 
375   write_transport_for_each(node, &__android_log_persist_write) {
376     if (node->logMask & i) {
377       (void)(*node->write)(log_id, &ts, vec, nr);
378     }
379   }
380 
381   errno = save_errno;
382   return ret;
383 }
384 
__write_to_log_init(log_id_t log_id,struct iovec * vec,size_t nr)385 static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
386   int ret, save_errno = errno;
387 
388   __android_log_lock();
389 
390   if (write_to_log == __write_to_log_init) {
391     ret = __write_to_log_initialize();
392     if (ret < 0) {
393       __android_log_unlock();
394       if (!list_empty(&__android_log_persist_write)) {
395         __write_to_log_daemon(log_id, vec, nr);
396       }
397       errno = save_errno;
398       return ret;
399     }
400 
401     write_to_log = __write_to_log_daemon;
402   }
403 
404   __android_log_unlock();
405 
406   ret = write_to_log(log_id, vec, nr);
407   errno = save_errno;
408   return ret;
409 }
410 
__android_log_write(int prio,const char * tag,const char * msg)411 LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
412                                           const char* msg) {
413   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
414 }
415 
__android_log_buf_write(int bufID,int prio,const char * tag,const char * msg)416 LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
417                                               const char* tag, const char* msg) {
418   struct iovec vec[3];
419   char tmp_tag[32];
420 
421   if (!tag) tag = "";
422 
423   /* XXX: This needs to go! */
424   if (bufID != LOG_ID_RADIO) {
425     switch (tag[0]) {
426       case 'H':
427         if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
428         goto inform;
429       case 'R':
430         /* Any log tag with "RIL" as the prefix */
431         if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
432         goto inform;
433       case 'Q':
434         /* Any log tag with "QC_RIL" as the prefix */
435         if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
436         goto inform;
437       case 'I':
438         /* Any log tag with "IMS" as the prefix */
439         if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
440         goto inform;
441       case 'A':
442         if (strcmp(tag + 1, "AT" + 1)) break;
443         goto inform;
444       case 'G':
445         if (strcmp(tag + 1, "GSM" + 1)) break;
446         goto inform;
447       case 'S':
448         if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
449         goto inform;
450       case 'C':
451         if (strcmp(tag + 1, "CDMA" + 1)) break;
452         goto inform;
453       case 'P':
454         if (strcmp(tag + 1, "PHONE" + 1)) break;
455       /* FALLTHRU */
456       inform:
457         bufID = LOG_ID_RADIO;
458         snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
459         tag = tmp_tag;
460       /* FALLTHRU */
461       default:
462         break;
463     }
464   }
465 
466 #if __BIONIC__
467   if (prio == ANDROID_LOG_FATAL) {
468     android_set_abort_message(msg);
469   }
470 #endif
471 
472   vec[0].iov_base = (unsigned char*)&prio;
473   vec[0].iov_len = 1;
474   vec[1].iov_base = (void*)tag;
475   vec[1].iov_len = strlen(tag) + 1;
476   vec[2].iov_base = (void*)msg;
477   vec[2].iov_len = strlen(msg) + 1;
478 
479   return write_to_log(bufID, vec, 3);
480 }
481 
__android_log_vprint(int prio,const char * tag,const char * fmt,va_list ap)482 LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag,
483                                            const char* fmt, va_list ap) {
484   char buf[LOG_BUF_SIZE];
485 
486   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
487 
488   return __android_log_write(prio, tag, buf);
489 }
490 
__android_log_print(int prio,const char * tag,const char * fmt,...)491 LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag,
492                                           const char* fmt, ...) {
493   va_list ap;
494   char buf[LOG_BUF_SIZE];
495 
496   va_start(ap, fmt);
497   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
498   va_end(ap);
499 
500   return __android_log_write(prio, tag, buf);
501 }
502 
__android_log_buf_print(int bufID,int prio,const char * tag,const char * fmt,...)503 LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
504                                               const char* tag, const char* fmt,
505                                               ...) {
506   va_list ap;
507   char buf[LOG_BUF_SIZE];
508 
509   va_start(ap, fmt);
510   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
511   va_end(ap);
512 
513   return __android_log_buf_write(bufID, prio, tag, buf);
514 }
515 
__android_log_assert(const char * cond,const char * tag,const char * fmt,...)516 LIBLOG_ABI_PUBLIC void __android_log_assert(const char* cond, const char* tag,
517                                             const char* fmt, ...) {
518   char buf[LOG_BUF_SIZE];
519 
520   if (fmt) {
521     va_list ap;
522     va_start(ap, fmt);
523     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
524     va_end(ap);
525   } else {
526     /* Msg not provided, log condition.  N.B. Do not use cond directly as
527      * format string as it could contain spurious '%' syntax (e.g.
528      * "%d" in "blocks%devs == 0").
529      */
530     if (cond)
531       snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
532     else
533       strcpy(buf, "Unspecified assertion failed");
534   }
535 
536   // Log assertion failures to stderr for the benefit of "adb shell" users
537   // and gtests (http://b/23675822).
538   struct iovec iov[2] = {
539     { buf, strlen(buf) }, { (char*)"\n", 1 },
540   };
541   TEMP_FAILURE_RETRY(writev(2, iov, 2));
542 
543   __android_log_write(ANDROID_LOG_FATAL, tag, buf);
544   abort(); /* abort so we have a chance to debug the situation */
545            /* NOTREACHED */
546 }
547 
__android_log_bwrite(int32_t tag,const void * payload,size_t len)548 LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag, const void* payload,
549                                            size_t len) {
550   struct iovec vec[2];
551 
552   vec[0].iov_base = &tag;
553   vec[0].iov_len = sizeof(tag);
554   vec[1].iov_base = (void*)payload;
555   vec[1].iov_len = len;
556 
557   return write_to_log(LOG_ID_EVENTS, vec, 2);
558 }
559 
__android_log_stats_bwrite(int32_t tag,const void * payload,size_t len)560 LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
561                                                  const void* payload,
562                                                  size_t len) {
563   struct iovec vec[2];
564 
565   vec[0].iov_base = &tag;
566   vec[0].iov_len = sizeof(tag);
567   vec[1].iov_base = (void*)payload;
568   vec[1].iov_len = len;
569 
570   return write_to_log(LOG_ID_STATS, vec, 2);
571 }
572 
__android_log_security_bwrite(int32_t tag,const void * payload,size_t len)573 LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
574                                                     const void* payload,
575                                                     size_t len) {
576   struct iovec vec[2];
577 
578   vec[0].iov_base = &tag;
579   vec[0].iov_len = sizeof(tag);
580   vec[1].iov_base = (void*)payload;
581   vec[1].iov_len = len;
582 
583   return write_to_log(LOG_ID_SECURITY, vec, 2);
584 }
585 
586 /*
587  * Like __android_log_bwrite, but takes the type as well.  Doesn't work
588  * for the general case where we're generating lists of stuff, but very
589  * handy if we just want to dump an integer into the log.
590  */
__android_log_btwrite(int32_t tag,char type,const void * payload,size_t len)591 LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
592                                             const void* payload, size_t len) {
593   struct iovec vec[3];
594 
595   vec[0].iov_base = &tag;
596   vec[0].iov_len = sizeof(tag);
597   vec[1].iov_base = &type;
598   vec[1].iov_len = sizeof(type);
599   vec[2].iov_base = (void*)payload;
600   vec[2].iov_len = len;
601 
602   return write_to_log(LOG_ID_EVENTS, vec, 3);
603 }
604 
605 /*
606  * Like __android_log_bwrite, but used for writing strings to the
607  * event log.
608  */
__android_log_bswrite(int32_t tag,const char * payload)609 LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char* payload) {
610   struct iovec vec[4];
611   char type = EVENT_TYPE_STRING;
612   uint32_t len = strlen(payload);
613 
614   vec[0].iov_base = &tag;
615   vec[0].iov_len = sizeof(tag);
616   vec[1].iov_base = &type;
617   vec[1].iov_len = sizeof(type);
618   vec[2].iov_base = &len;
619   vec[2].iov_len = sizeof(len);
620   vec[3].iov_base = (void*)payload;
621   vec[3].iov_len = len;
622 
623   return write_to_log(LOG_ID_EVENTS, vec, 4);
624 }
625 
626 /*
627  * Like __android_log_security_bwrite, but used for writing strings to the
628  * security log.
629  */
__android_log_security_bswrite(int32_t tag,const char * payload)630 LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
631                                                      const char* payload) {
632   struct iovec vec[4];
633   char type = EVENT_TYPE_STRING;
634   uint32_t len = strlen(payload);
635 
636   vec[0].iov_base = &tag;
637   vec[0].iov_len = sizeof(tag);
638   vec[1].iov_base = &type;
639   vec[1].iov_len = sizeof(type);
640   vec[2].iov_base = &len;
641   vec[2].iov_len = sizeof(len);
642   vec[3].iov_base = (void*)payload;
643   vec[3].iov_len = len;
644 
645   return write_to_log(LOG_ID_SECURITY, vec, 4);
646 }
647 
__write_to_log_null(log_id_t log_id,struct iovec * vec,size_t nr)648 static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
649   size_t len, i;
650 
651   if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
652     return -EINVAL;
653   }
654 
655   for (len = i = 0; i < nr; ++i) {
656     len += vec[i].iov_len;
657   }
658   if (!len) {
659     return -EINVAL;
660   }
661   return len;
662 }
663 
664 /* Following functions need access to our internal write_to_log status */
665 
666 LIBLOG_HIDDEN int __android_log_transport;
667 
android_set_log_transport(int transport_flag)668 LIBLOG_ABI_PUBLIC int android_set_log_transport(int transport_flag) {
669   int retval;
670 
671   if (transport_flag < 0) {
672     return -EINVAL;
673   }
674 
675   retval = LOGGER_NULL;
676 
677   __android_log_lock();
678 
679   if (transport_flag & LOGGER_NULL) {
680     write_to_log = __write_to_log_null;
681 
682     __android_log_unlock();
683 
684     return retval;
685   }
686 
687   __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
688 
689   transport_flag &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
690 
691   if (__android_log_transport != transport_flag) {
692     __android_log_transport = transport_flag;
693     __android_log_config_write_close();
694     __android_log_config_read_close();
695 
696     write_to_log = __write_to_log_init;
697     /* generically we only expect these two values for write_to_log */
698   } else if ((write_to_log != __write_to_log_init) &&
699              (write_to_log != __write_to_log_daemon)) {
700     write_to_log = __write_to_log_init;
701   }
702 
703   retval = __android_log_transport;
704 
705   __android_log_unlock();
706 
707   return retval;
708 }
709 
android_get_log_transport()710 LIBLOG_ABI_PUBLIC int android_get_log_transport() {
711   int ret = LOGGER_DEFAULT;
712 
713   __android_log_lock();
714   if (write_to_log == __write_to_log_null) {
715     ret = LOGGER_NULL;
716   } else {
717     __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
718     ret = __android_log_transport;
719     if ((write_to_log != __write_to_log_init) &&
720         (write_to_log != __write_to_log_daemon)) {
721       ret = -EINVAL;
722     }
723   }
724   __android_log_unlock();
725 
726   return ret;
727 }
728