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