Line data Source code
1 : /*
2 : * Copyright (C) 2008 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 : * Intercepts log messages intended for the Android log device.
18 : * When running in the context of the simulator, the messages are
19 : * passed on to the underlying (fake) log device. When not in the
20 : * simulator, messages are printed to stderr.
21 : */
22 : #include <log/logd.h>
23 :
24 : #include <stdlib.h>
25 : #include <string.h>
26 : #include <ctype.h>
27 : #include <errno.h>
28 : #include <fcntl.h>
29 :
30 : #ifdef HAVE_PTHREADS
31 : #include <pthread.h>
32 : #endif
33 :
34 : #ifdef _MSC_VER
35 : #include <io.h>
36 : #include <process.h>
37 : #if _MSC_VER < 1900
38 : #include <nspr/prprf.h>
39 : #define snprintf PR_snprintf
40 : #endif
41 :
42 : /* We don't want to indent large blocks because it causes unnecessary merge
43 : * conflicts */
44 : #define UNINDENTED_BLOCK_START {
45 : #define UNINDENTED_BLOCK_END }
46 : #else
47 : #define UNINDENTED_BLOCK_START
48 : #define UNINDENTED_BLOCK_END
49 : #endif
50 :
51 : #ifdef _MSC_VER
52 : #include <io.h>
53 : #include <process.h>
54 :
55 : /* We don't want to indent large blocks because it causes unnecessary merge
56 : * conflicts */
57 : #define UNINDENTED_BLOCK_START {
58 : #define UNINDENTED_BLOCK_END }
59 : #else
60 : #define UNINDENTED_BLOCK_START
61 : #define UNINDENTED_BLOCK_END
62 : #endif
63 :
64 : #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
65 :
66 : #define kTagSetSize 16 /* arbitrary */
67 :
68 : #if 0
69 : #define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
70 : #else
71 : #define TRACE(...) ((void)0)
72 : #endif
73 :
74 : /* from the long-dead utils/Log.cpp */
75 : typedef enum {
76 : FORMAT_OFF = 0,
77 : FORMAT_BRIEF,
78 : FORMAT_PROCESS,
79 : FORMAT_TAG,
80 : FORMAT_THREAD,
81 : FORMAT_RAW,
82 : FORMAT_TIME,
83 : FORMAT_THREADTIME,
84 : FORMAT_LONG
85 : } LogFormat;
86 :
87 :
88 : /*
89 : * Log driver state.
90 : */
91 : typedef struct LogState {
92 : /* the fake fd that's seen by the user */
93 : int fakeFd;
94 :
95 : /* a printable name for this fake device */
96 : char *debugName;
97 :
98 : /* nonzero if this is a binary log */
99 : int isBinary;
100 :
101 : /* global minimum priority */
102 : int globalMinPriority;
103 :
104 : /* output format */
105 : LogFormat outputFormat;
106 :
107 : /* tags and priorities */
108 : struct {
109 : char tag[kMaxTagLen];
110 : int minPriority;
111 : } tagSet[kTagSetSize];
112 : } LogState;
113 :
114 :
115 : #ifdef HAVE_PTHREADS
116 : /*
117 : * Locking. Since we're emulating a device, we need to be prepared
118 : * to have multiple callers at the same time. This lock is used
119 : * to both protect the fd list and to prevent LogStates from being
120 : * freed out from under a user.
121 : */
122 : static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
123 :
124 : static void lock()
125 : {
126 : pthread_mutex_lock(&fakeLogDeviceLock);
127 : }
128 :
129 : static void unlock()
130 : {
131 : pthread_mutex_unlock(&fakeLogDeviceLock);
132 : }
133 : #else // !HAVE_PTHREADS
134 : #define lock() ((void)0)
135 : #define unlock() ((void)0)
136 : #endif // !HAVE_PTHREADS
137 :
138 :
139 : /*
140 : * File descriptor management.
141 : */
142 : #define FAKE_FD_BASE 10000
143 : #define MAX_OPEN_LOGS 16
144 : static LogState *openLogTable[MAX_OPEN_LOGS];
145 :
146 : /*
147 : * Allocate an fd and associate a new LogState with it.
148 : * The fd is available via the fakeFd field of the return value.
149 : */
150 0 : static LogState *createLogState()
151 : {
152 : size_t i;
153 :
154 0 : for (i = 0; i < sizeof(openLogTable); i++) {
155 0 : if (openLogTable[i] == NULL) {
156 0 : openLogTable[i] = calloc(1, sizeof(LogState));
157 0 : openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
158 0 : return openLogTable[i];
159 : }
160 : }
161 0 : return NULL;
162 : }
163 :
164 : /*
165 : * Translate an fd to a LogState.
166 : */
167 0 : static LogState *fdToLogState(int fd)
168 : {
169 0 : if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
170 0 : return openLogTable[fd - FAKE_FD_BASE];
171 : }
172 0 : return NULL;
173 : }
174 :
175 : /*
176 : * Unregister the fake fd and free the memory it pointed to.
177 : */
178 0 : static void deleteFakeFd(int fd)
179 : {
180 : LogState *ls;
181 :
182 : lock();
183 :
184 0 : ls = fdToLogState(fd);
185 0 : if (ls != NULL) {
186 0 : openLogTable[fd - FAKE_FD_BASE] = NULL;
187 0 : free(ls->debugName);
188 0 : free(ls);
189 : }
190 :
191 : unlock();
192 0 : }
193 :
194 : /*
195 : * Configure logging based on ANDROID_LOG_TAGS environment variable. We
196 : * need to parse a string that looks like
197 : *
198 : * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
199 : *
200 : * The tag (or '*' for the global level) comes first, followed by a colon
201 : * and a letter indicating the minimum priority level we're expected to log.
202 : * This can be used to reveal or conceal logs with specific tags.
203 : *
204 : * We also want to check ANDROID_PRINTF_LOG to determine how the output
205 : * will look.
206 : */
207 0 : static void configureInitialState(const char* pathName, LogState* logState)
208 : {
209 : static const int kDevLogLen = sizeof("/dev/log/") - 1;
210 :
211 0 : logState->debugName = strdup(pathName);
212 :
213 : /* identify binary logs */
214 0 : if (strcmp(pathName + kDevLogLen, "events") == 0) {
215 0 : logState->isBinary = 1;
216 : }
217 :
218 : /* global min priority defaults to "info" level */
219 0 : logState->globalMinPriority = ANDROID_LOG_INFO;
220 :
221 : /*
222 : * This is based on the the long-dead utils/Log.cpp code.
223 : */
224 : UNINDENTED_BLOCK_START
225 0 : const char* tags = getenv("ANDROID_LOG_TAGS");
226 : TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
227 0 : if (tags != NULL) {
228 0 : int entry = 0;
229 :
230 0 : while (*tags != '\0') {
231 : char tagName[kMaxTagLen];
232 : int i, minPrio;
233 :
234 0 : while (isspace(*tags))
235 0 : tags++;
236 :
237 0 : i = 0;
238 0 : while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
239 : i < kMaxTagLen)
240 : {
241 0 : tagName[i++] = *tags++;
242 : }
243 0 : if (i == kMaxTagLen) {
244 : TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
245 0 : return;
246 : }
247 0 : tagName[i] = '\0';
248 :
249 : /* default priority, if there's no ":" part; also zero out '*' */
250 0 : minPrio = ANDROID_LOG_VERBOSE;
251 0 : if (tagName[0] == '*' && tagName[1] == '\0') {
252 0 : minPrio = ANDROID_LOG_DEBUG;
253 0 : tagName[0] = '\0';
254 : }
255 :
256 0 : if (*tags == ':') {
257 0 : tags++;
258 0 : if (*tags >= '0' && *tags <= '9') {
259 0 : if (*tags >= ('0' + ANDROID_LOG_SILENT))
260 0 : minPrio = ANDROID_LOG_VERBOSE;
261 : else
262 0 : minPrio = *tags - '\0';
263 : } else {
264 0 : switch (*tags) {
265 0 : case 'v': minPrio = ANDROID_LOG_VERBOSE; break;
266 0 : case 'd': minPrio = ANDROID_LOG_DEBUG; break;
267 0 : case 'i': minPrio = ANDROID_LOG_INFO; break;
268 0 : case 'w': minPrio = ANDROID_LOG_WARN; break;
269 0 : case 'e': minPrio = ANDROID_LOG_ERROR; break;
270 0 : case 'f': minPrio = ANDROID_LOG_FATAL; break;
271 0 : case 's': minPrio = ANDROID_LOG_SILENT; break;
272 0 : default: minPrio = ANDROID_LOG_DEFAULT; break;
273 : }
274 : }
275 :
276 0 : tags++;
277 0 : if (*tags != '\0' && !isspace(*tags)) {
278 : TRACE("ERROR: garbage in tag env; expected whitespace\n");
279 : TRACE(" env='%s'\n", tags);
280 0 : return;
281 : }
282 : }
283 :
284 0 : if (tagName[0] == 0) {
285 0 : logState->globalMinPriority = minPrio;
286 : TRACE("+++ global min prio %d\n", logState->globalMinPriority);
287 : } else {
288 0 : logState->tagSet[entry].minPriority = minPrio;
289 0 : strcpy(logState->tagSet[entry].tag, tagName);
290 : TRACE("+++ entry %d: %s:%d\n",
291 : entry,
292 : logState->tagSet[entry].tag,
293 : logState->tagSet[entry].minPriority);
294 0 : entry++;
295 : }
296 : }
297 : }
298 : UNINDENTED_BLOCK_END
299 :
300 : /*
301 : * Taken from the long-dead utils/Log.cpp
302 : */
303 : UNINDENTED_BLOCK_START
304 0 : const char* fstr = getenv("ANDROID_PRINTF_LOG");
305 : LogFormat format;
306 0 : if (fstr == NULL) {
307 0 : format = FORMAT_BRIEF;
308 : } else {
309 0 : if (strcmp(fstr, "brief") == 0)
310 0 : format = FORMAT_BRIEF;
311 0 : else if (strcmp(fstr, "process") == 0)
312 0 : format = FORMAT_PROCESS;
313 0 : else if (strcmp(fstr, "tag") == 0)
314 0 : format = FORMAT_PROCESS;
315 0 : else if (strcmp(fstr, "thread") == 0)
316 0 : format = FORMAT_PROCESS;
317 0 : else if (strcmp(fstr, "raw") == 0)
318 0 : format = FORMAT_PROCESS;
319 0 : else if (strcmp(fstr, "time") == 0)
320 0 : format = FORMAT_PROCESS;
321 0 : else if (strcmp(fstr, "long") == 0)
322 0 : format = FORMAT_PROCESS;
323 : else
324 0 : format = (LogFormat) atoi(fstr); // really?!
325 : }
326 :
327 0 : logState->outputFormat = format;
328 : UNINDENTED_BLOCK_END
329 : }
330 :
331 : /*
332 : * Return a human-readable string for the priority level. Always returns
333 : * a valid string.
334 : */
335 0 : static const char* getPriorityString(int priority)
336 : {
337 : /* the first character of each string should be unique */
338 : static const char* priorityStrings[] = {
339 : "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
340 : };
341 : int idx;
342 :
343 0 : idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
344 0 : if (idx < 0 ||
345 : idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
346 0 : return "?unknown?";
347 0 : return priorityStrings[idx];
348 : }
349 :
350 : #ifndef HAVE_WRITEV
351 : /*
352 : * Some platforms like WIN32 do not have writev().
353 : * Make up something to replace it.
354 : */
355 0 : static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
356 0 : int result = 0;
357 0 : const struct iovec* end = iov + iovcnt;
358 0 : for (; iov < end; iov++) {
359 0 : int w = write(fd, iov->iov_base, iov->iov_len);
360 0 : if (w != iov->iov_len) {
361 0 : if (w < 0)
362 0 : return w;
363 0 : return result + w;
364 : }
365 0 : result += w;
366 : }
367 0 : return result;
368 : }
369 :
370 : #define writev fake_writev
371 : #endif
372 :
373 :
374 : /*
375 : * Write a filtered log message to stderr.
376 : *
377 : * Log format parsing taken from the long-dead utils/Log.cpp.
378 : */
379 0 : static void showLog(LogState *state,
380 : int logPrio, const char* tag, const char* msg)
381 : {
382 : #if defined(HAVE_LOCALTIME_R)
383 : struct tm tmBuf;
384 : #endif
385 : struct tm* ptm;
386 : char timeBuf[32];
387 : char prefixBuf[128], suffixBuf[128];
388 : char priChar;
389 : time_t when;
390 : #ifdef _MSC_VER
391 : int pid, tid;
392 : #else
393 : pid_t pid, tid;
394 : #endif
395 :
396 : TRACE("LOG %d: %s %s", logPrio, tag, msg);
397 :
398 0 : priChar = getPriorityString(logPrio)[0];
399 0 : when = time(NULL);
400 0 : pid = tid = getpid(); // find gettid()?
401 :
402 : /*
403 : * Get the current date/time in pretty form
404 : *
405 : * It's often useful when examining a log with "less" to jump to
406 : * a specific point in the file by searching for the date/time stamp.
407 : * For this reason it's very annoying to have regexp meta characters
408 : * in the time stamp. Don't use forward slashes, parenthesis,
409 : * brackets, asterisks, or other special chars here.
410 : */
411 : #if defined(HAVE_LOCALTIME_R)
412 0 : ptm = localtime_r(&when, &tmBuf);
413 : #else
414 : ptm = localtime(&when);
415 : #endif
416 : //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
417 0 : strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
418 :
419 : /*
420 : * Construct a buffer containing the log header and log message.
421 : */
422 : UNINDENTED_BLOCK_START
423 : size_t prefixLen, suffixLen;
424 :
425 0 : switch (state->outputFormat) {
426 : case FORMAT_TAG:
427 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
428 : "%c/%-8s: ", priChar, tag);
429 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
430 0 : break;
431 : case FORMAT_PROCESS:
432 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
433 : "%c(%5d) ", priChar, pid);
434 0 : suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
435 : " (%s)\n", tag);
436 0 : break;
437 : case FORMAT_THREAD:
438 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
439 : "%c(%5d:%5d) ", priChar, pid, tid);
440 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
441 0 : break;
442 : case FORMAT_RAW:
443 0 : prefixBuf[0] = 0; prefixLen = 0;
444 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
445 0 : break;
446 : case FORMAT_TIME:
447 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
448 : "%s %-8s\n\t", timeBuf, tag);
449 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
450 0 : break;
451 : case FORMAT_THREADTIME:
452 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
453 : "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag);
454 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
455 0 : break;
456 : case FORMAT_LONG:
457 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
458 : "[ %s %5d:%5d %c/%-8s ]\n",
459 : timeBuf, pid, tid, priChar, tag);
460 0 : strcpy(suffixBuf, "\n\n"); suffixLen = 2;
461 0 : break;
462 : default:
463 0 : prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
464 : "%c/%-8s(%5d): ", priChar, tag, pid);
465 0 : strcpy(suffixBuf, "\n"); suffixLen = 1;
466 0 : break;
467 : }
468 :
469 : /*
470 : * Figure out how many lines there will be.
471 : */
472 : UNINDENTED_BLOCK_START
473 0 : const char* end = msg + strlen(msg);
474 0 : size_t numLines = 0;
475 0 : const char* p = msg;
476 0 : while (p < end) {
477 0 : if (*p++ == '\n') numLines++;
478 : }
479 0 : if (p > msg && *(p-1) != '\n') numLines++;
480 :
481 : /*
482 : * Create an array of iovecs large enough to write all of
483 : * the lines with a prefix and a suffix.
484 : */
485 : UNINDENTED_BLOCK_START
486 : #define INLINE_VECS 6
487 0 : const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
488 : struct iovec stackVec[INLINE_VECS];
489 0 : struct iovec* vec = stackVec;
490 : size_t numVecs;
491 :
492 0 : if (numLines > MAX_LINES)
493 0 : numLines = MAX_LINES;
494 :
495 0 : numVecs = numLines*3; // 3 iovecs per line.
496 0 : if (numVecs > INLINE_VECS) {
497 0 : vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
498 0 : if (vec == NULL) {
499 0 : msg = "LOG: write failed, no memory";
500 0 : numVecs = 3;
501 0 : numLines = 1;
502 0 : vec = stackVec;
503 : }
504 : }
505 :
506 : /*
507 : * Fill in the iovec pointers.
508 : */
509 0 : p = msg;
510 : UNINDENTED_BLOCK_START
511 0 : struct iovec* v = vec;
512 0 : int totalLen = 0;
513 0 : while (numLines > 0 && p < end) {
514 0 : if (prefixLen > 0) {
515 0 : v->iov_base = prefixBuf;
516 0 : v->iov_len = prefixLen;
517 0 : totalLen += prefixLen;
518 0 : v++;
519 : }
520 : UNINDENTED_BLOCK_START
521 0 : const char* start = p;
522 0 : while (p < end && *p != '\n') p++;
523 0 : if ((p-start) > 0) {
524 0 : v->iov_base = (void*)start;
525 0 : v->iov_len = p-start;
526 0 : totalLen += p-start;
527 0 : v++;
528 : }
529 0 : if (*p == '\n') p++;
530 0 : if (suffixLen > 0) {
531 0 : v->iov_base = suffixBuf;
532 0 : v->iov_len = suffixLen;
533 0 : totalLen += suffixLen;
534 0 : v++;
535 : }
536 0 : numLines -= 1;
537 : UNINDENTED_BLOCK_END
538 : }
539 :
540 : /*
541 : * Write the entire message to the log file with a single writev() call.
542 : * We need to use this rather than a collection of printf()s on a FILE*
543 : * because of multi-threading and multi-process issues.
544 : *
545 : * If the file was not opened with O_APPEND, this will produce interleaved
546 : * output when called on the same file from multiple processes.
547 : *
548 : * If the file descriptor is actually a network socket, the writev()
549 : * call may return with a partial write. Putting the writev() call in
550 : * a loop can result in interleaved data. This can be alleviated
551 : * somewhat by wrapping the writev call in the Mutex.
552 : */
553 :
554 0 : for(;;) {
555 0 : int cc = writev(fileno(stderr), vec, v-vec);
556 :
557 0 : if (cc == totalLen) break;
558 :
559 0 : if (cc < 0) {
560 0 : if(errno == EINTR) continue;
561 :
562 : /* can't really log the failure; for now, throw out a stderr */
563 0 : fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
564 0 : break;
565 : } else {
566 : /* shouldn't happen when writing to file or tty */
567 0 : fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
568 0 : break;
569 : }
570 : }
571 :
572 : /* if we allocated storage for the iovecs, free it */
573 0 : if (vec != stackVec)
574 0 : free(vec);
575 : UNINDENTED_BLOCK_END
576 : UNINDENTED_BLOCK_END
577 : UNINDENTED_BLOCK_END
578 : UNINDENTED_BLOCK_END
579 0 : }
580 :
581 :
582 : /*
583 : * Receive a log message. We happen to know that "vector" has three parts:
584 : *
585 : * priority (1 byte)
586 : * tag (N bytes -- null-terminated ASCII string)
587 : * message (N bytes -- null-terminated ASCII string)
588 : */
589 0 : static ssize_t logWritev(int fd, const struct iovec* vector, int count)
590 : {
591 : LogState* state;
592 :
593 : /* Make sure that no-one frees the LogState while we're using it.
594 : * Also guarantees that only one thread is in showLog() at a given
595 : * time (if it matters).
596 : */
597 : lock();
598 :
599 0 : state = fdToLogState(fd);
600 0 : if (state == NULL) {
601 0 : errno = EBADF;
602 0 : goto error;
603 : }
604 :
605 0 : if (state->isBinary) {
606 : TRACE("%s: ignoring binary log\n", state->debugName);
607 0 : goto bail;
608 : }
609 :
610 0 : if (count != 3) {
611 : TRACE("%s: writevLog with count=%d not expected\n",
612 : state->debugName, count);
613 0 : goto error;
614 : }
615 :
616 : /* pull out the three fields */
617 : UNINDENTED_BLOCK_START
618 0 : int logPrio = *(const char*)vector[0].iov_base;
619 0 : const char* tag = (const char*) vector[1].iov_base;
620 0 : const char* msg = (const char*) vector[2].iov_base;
621 :
622 : /* see if this log tag is configured */
623 : int i;
624 0 : int minPrio = state->globalMinPriority;
625 0 : for (i = 0; i < kTagSetSize; i++) {
626 0 : if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
627 0 : break; /* reached end of configured values */
628 :
629 0 : if (strcmp(state->tagSet[i].tag, tag) == 0) {
630 : //TRACE("MATCH tag '%s'\n", tag);
631 0 : minPrio = state->tagSet[i].minPriority;
632 0 : break;
633 : }
634 : }
635 :
636 0 : if (logPrio >= minPrio) {
637 0 : showLog(state, logPrio, tag, msg);
638 : } else {
639 : //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
640 : }
641 : UNINDENTED_BLOCK_END
642 :
643 : bail:
644 : unlock();
645 0 : return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
646 : error:
647 : unlock();
648 0 : return -1;
649 : }
650 :
651 : /*
652 : * Free up our state and close the fake descriptor.
653 : */
654 0 : static int logClose(int fd)
655 : {
656 0 : deleteFakeFd(fd);
657 0 : return 0;
658 : }
659 :
660 : /*
661 : * Open a log output device and return a fake fd.
662 : */
663 0 : static int logOpen(const char* pathName, int flags)
664 : {
665 : LogState *logState;
666 0 : int fd = -1;
667 :
668 : lock();
669 :
670 0 : logState = createLogState();
671 0 : if (logState != NULL) {
672 0 : configureInitialState(pathName, logState);
673 0 : fd = logState->fakeFd;
674 : } else {
675 0 : errno = ENFILE;
676 : }
677 :
678 : unlock();
679 :
680 0 : return fd;
681 : }
682 :
683 :
684 : /*
685 : * Runtime redirection. If this binary is running in the simulator,
686 : * just pass log messages to the emulated device. If it's running
687 : * outside of the simulator, write the log messages to stderr.
688 : */
689 :
690 : static int (*redirectOpen)(const char *pathName, int flags) = NULL;
691 : static int (*redirectClose)(int fd) = NULL;
692 : static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count)
693 : = NULL;
694 :
695 0 : static void setRedirects()
696 : {
697 : const char *ws;
698 :
699 : /* Wrapsim sets this environment variable on children that it's
700 : * created using its LD_PRELOAD wrapper.
701 : */
702 0 : ws = getenv("ANDROID_WRAPSIM");
703 0 : if (ws != NULL && strcmp(ws, "1") == 0) {
704 : /* We're running inside wrapsim, so we can just write to the device. */
705 0 : redirectOpen = (int (*)(const char *pathName, int flags))open;
706 0 : redirectClose = close;
707 0 : redirectWritev = writev;
708 : } else {
709 : /* There's no device to delegate to; handle the logging ourselves. */
710 0 : redirectOpen = logOpen;
711 0 : redirectClose = logClose;
712 0 : redirectWritev = logWritev;
713 : }
714 0 : }
715 :
716 0 : int fakeLogOpen(const char *pathName, int flags)
717 : {
718 0 : if (redirectOpen == NULL) {
719 0 : setRedirects();
720 : }
721 0 : return redirectOpen(pathName, flags);
722 : }
723 :
724 0 : int fakeLogClose(int fd)
725 : {
726 : /* Assume that open() was called first. */
727 0 : return redirectClose(fd);
728 : }
729 :
730 0 : ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
731 : {
732 : /* Assume that open() was called first. */
733 0 : return redirectWritev(fd, vector, count);
734 : }
735 :
736 : #undef UNINDENTED_BLOCK_START
737 : #undef UNINDENTED_BLOCK_END
|