1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 :
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is the Netscape Portable Runtime (NSPR).
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * IBM Corporation
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "primpl.h"
41 : #include "prenv.h"
42 : #include "prprf.h"
43 : #include <string.h>
44 : #ifdef ANDROID
45 : #include <android/log.h>
46 : #endif
47 :
48 : /*
49 : * Lock used to lock the log.
50 : *
51 : * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
52 : * contain assertions. We have to avoid assertions in _PR_LOCK_LOG
53 : * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
54 : * This can lead to infinite recursion.
55 : */
56 : static PRLock *_pr_logLock;
57 : #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
58 : #define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
59 : #define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
60 : #elif defined(_PR_GLOBAL_THREADS_ONLY)
61 : #define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
62 : #define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
63 : #else
64 :
65 : #define _PR_LOCK_LOG() \
66 : { \
67 : PRIntn _is; \
68 : PRThread *_me = _PR_MD_CURRENT_THREAD(); \
69 : if (!_PR_IS_NATIVE_THREAD(_me)) \
70 : _PR_INTSOFF(_is); \
71 : _PR_LOCK_LOCK(_pr_logLock)
72 :
73 : #define _PR_UNLOCK_LOG() \
74 : _PR_LOCK_UNLOCK(_pr_logLock); \
75 : PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
76 : if (!_PR_IS_NATIVE_THREAD(_me)) \
77 : _PR_INTSON(_is); \
78 : }
79 :
80 : #endif
81 :
82 : #if defined(XP_PC)
83 : #define strcasecmp stricmp
84 : #endif
85 :
86 : /*
87 : * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
88 : * because every asynchronous file io operation leads to a fiber context
89 : * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
90 : * benefit is that fputs handles the LF->CRLF translation. This
91 : * code can also be used on other platforms with file stream io.
92 : */
93 : #if defined(WIN32) || defined(XP_OS2)
94 : #define _PR_USE_STDIO_FOR_LOGGING
95 : #endif
96 :
97 : /*
98 : ** Coerce Win32 log output to use OutputDebugString() when
99 : ** NSPR_LOG_FILE is set to "WinDebug".
100 : */
101 : #if defined(XP_PC)
102 : #define WIN32_DEBUG_FILE (FILE*)-2
103 : #endif
104 :
105 : #ifdef WINCE
106 : static void OutputDebugStringA(const char* msg) {
107 : int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0);
108 : WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR));
109 : MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len);
110 : OutputDebugStringW(wMsg);
111 : PR_Free(wMsg);
112 : }
113 : #endif
114 :
115 : /* Macros used to reduce #ifdef pollution */
116 :
117 : #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
118 : #define _PUT_LOG(fd, buf, nb) \
119 : PR_BEGIN_MACRO \
120 : if (logFile == WIN32_DEBUG_FILE) { \
121 : char savebyte = buf[nb]; \
122 : buf[nb] = '\0'; \
123 : OutputDebugStringA(buf); \
124 : buf[nb] = savebyte; \
125 : } else { \
126 : fwrite(buf, 1, nb, fd); \
127 : fflush(fd); \
128 : } \
129 : PR_END_MACRO
130 : #elif defined(_PR_USE_STDIO_FOR_LOGGING)
131 : #define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);}
132 : #elif defined(ANDROID)
133 : #define _PUT_LOG(fd, buf, nb) \
134 : PR_BEGIN_MACRO \
135 : if (fd == _pr_stderr) { \
136 : char savebyte = buf[nb]; \
137 : buf[nb] = '\0'; \
138 : __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
139 : buf[nb] = savebyte; \
140 : } else { \
141 : PR_Write(fd, buf, nb); \
142 : } \
143 : PR_END_MACRO
144 : #elif defined(_PR_PTHREADS)
145 : #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
146 : #else
147 : #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
148 : #endif
149 :
150 : /************************************************************************/
151 :
152 : static PRLogModuleInfo *logModules;
153 :
154 : static char *logBuf = NULL;
155 : static char *logp;
156 : static char *logEndp;
157 : #ifdef _PR_USE_STDIO_FOR_LOGGING
158 : static FILE *logFile = NULL;
159 : #else
160 : static PRFileDesc *logFile = 0;
161 : #endif
162 : static PRBool outputTimeStamp = PR_FALSE;
163 : static PRBool appendToLog = PR_FALSE;
164 :
165 : #define LINE_BUF_SIZE 512
166 : #define DEFAULT_BUF_SIZE 16384
167 :
168 : #ifdef _PR_NEED_STRCASECMP
169 :
170 : /*
171 : * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
172 : * such as NCR and Unixware. Linking with both libc and libucb
173 : * may cause some problem, so I just provide our own implementation
174 : * of strcasecmp here.
175 : */
176 :
177 : static const unsigned char uc[] =
178 : {
179 : '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
180 : '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
181 : '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
182 : '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
183 : ' ', '!', '"', '#', '$', '%', '&', '\'',
184 : '(', ')', '*', '+', ',', '-', '.', '/',
185 : '0', '1', '2', '3', '4', '5', '6', '7',
186 : '8', '9', ':', ';', '<', '=', '>', '?',
187 : '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
188 : 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
189 : 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
190 : 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
191 : '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
192 : 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
193 : 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
194 : 'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
195 : };
196 :
197 : PRIntn strcasecmp(const char *a, const char *b)
198 : {
199 : const unsigned char *ua = (const unsigned char *)a;
200 : const unsigned char *ub = (const unsigned char *)b;
201 :
202 : if( ((const char *)0 == a) || (const char *)0 == b )
203 : return (PRIntn)(a-b);
204 :
205 : while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
206 : {
207 : a++;
208 : ua++;
209 : ub++;
210 : }
211 :
212 : return (PRIntn)(uc[*ua] - uc[*ub]);
213 : }
214 :
215 : #endif /* _PR_NEED_STRCASECMP */
216 :
217 20034 : void _PR_InitLog(void)
218 : {
219 : char *ev;
220 :
221 20034 : _pr_logLock = PR_NewLock();
222 :
223 20034 : ev = PR_GetEnv("NSPR_LOG_MODULES");
224 20034 : if (ev && ev[0]) {
225 : char module[64]; /* Security-Critical: If you change this
226 : * size, you must also change the sscanf
227 : * format string to be size-1.
228 : */
229 0 : PRBool isSync = PR_FALSE;
230 0 : PRIntn evlen = strlen(ev), pos = 0;
231 0 : PRInt32 bufSize = DEFAULT_BUF_SIZE;
232 0 : while (pos < evlen) {
233 0 : PRIntn level = 1, count = 0, delta = 0;
234 0 : count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
235 : module, &delta, &level, &delta);
236 0 : pos += delta;
237 0 : if (count == 0) break;
238 :
239 : /*
240 : ** If count == 2, then we got module and level. If count
241 : ** == 1, then level defaults to 1 (module enabled).
242 : */
243 0 : if (strcasecmp(module, "sync") == 0) {
244 0 : isSync = PR_TRUE;
245 0 : } else if (strcasecmp(module, "bufsize") == 0) {
246 0 : if (level >= LINE_BUF_SIZE) {
247 0 : bufSize = level;
248 : }
249 0 : } else if (strcasecmp(module, "timestamp") == 0) {
250 0 : outputTimeStamp = PR_TRUE;
251 0 : } else if (strcasecmp(module, "append") == 0) {
252 0 : appendToLog = PR_TRUE;
253 : } else {
254 0 : PRLogModuleInfo *lm = logModules;
255 0 : PRBool skip_modcheck =
256 0 : (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
257 :
258 0 : while (lm != NULL) {
259 0 : if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
260 0 : else if (strcasecmp(module, lm->name) == 0) {
261 0 : lm->level = (PRLogModuleLevel)level;
262 0 : break;
263 : }
264 0 : lm = lm->next;
265 : }
266 : }
267 : /*found:*/
268 0 : count = sscanf(&ev[pos], " , %n", &delta);
269 0 : pos += delta;
270 0 : if (count == EOF) break;
271 : }
272 0 : PR_SetLogBuffering(isSync ? 0 : bufSize);
273 :
274 : #ifdef XP_UNIX
275 0 : if ((getuid() != geteuid()) || (getgid() != getegid())) {
276 0 : return;
277 : }
278 : #endif /* XP_UNIX */
279 :
280 0 : ev = PR_GetEnv("NSPR_LOG_FILE");
281 0 : if (ev && ev[0]) {
282 0 : if (!PR_SetLogFile(ev)) {
283 : #ifdef XP_PC
284 : char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
285 : if (str) {
286 : OutputDebugStringA(str);
287 : PR_smprintf_free(str);
288 : }
289 : #else
290 0 : fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
291 : #endif
292 : }
293 : } else {
294 : #ifdef _PR_USE_STDIO_FOR_LOGGING
295 : logFile = stderr;
296 : #else
297 0 : logFile = _pr_stderr;
298 : #endif
299 : }
300 : }
301 : }
302 :
303 140 : void _PR_LogCleanup(void)
304 : {
305 140 : PRLogModuleInfo *lm = logModules;
306 :
307 140 : PR_LogFlush();
308 :
309 : #ifdef _PR_USE_STDIO_FOR_LOGGING
310 : if (logFile
311 : && logFile != stdout
312 : && logFile != stderr
313 : #ifdef XP_PC
314 : && logFile != WIN32_DEBUG_FILE
315 : #endif
316 : ) {
317 : fclose(logFile);
318 : }
319 : #else
320 140 : if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
321 0 : PR_Close(logFile);
322 : }
323 : #endif
324 140 : logFile = NULL;
325 :
326 140 : if (logBuf)
327 0 : PR_DELETE(logBuf);
328 :
329 1820 : while (lm != NULL) {
330 1540 : PRLogModuleInfo *next = lm->next;
331 1540 : free((/*const*/ char *)lm->name);
332 1540 : PR_Free(lm);
333 1540 : lm = next;
334 : }
335 140 : logModules = NULL;
336 :
337 140 : if (_pr_logLock) {
338 140 : PR_DestroyLock(_pr_logLock);
339 140 : _pr_logLock = NULL;
340 : }
341 140 : }
342 :
343 306978 : static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
344 : {
345 : char *ev;
346 :
347 306978 : ev = PR_GetEnv("NSPR_LOG_MODULES");
348 306978 : if (ev && ev[0]) {
349 : char module[64]; /* Security-Critical: If you change this
350 : * size, you must also change the sscanf
351 : * format string to be size-1.
352 : */
353 0 : PRIntn evlen = strlen(ev), pos = 0;
354 0 : while (pos < evlen) {
355 0 : PRIntn level = 1, count = 0, delta = 0;
356 :
357 0 : count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
358 : module, &delta, &level, &delta);
359 0 : pos += delta;
360 0 : if (count == 0) break;
361 :
362 : /*
363 : ** If count == 2, then we got module and level. If count
364 : ** == 1, then level defaults to 1 (module enabled).
365 : */
366 0 : if (lm != NULL)
367 : {
368 0 : if ((strcasecmp(module, "all") == 0)
369 0 : || (strcasecmp(module, lm->name) == 0))
370 : {
371 0 : lm->level = (PRLogModuleLevel)level;
372 : }
373 : }
374 0 : count = sscanf(&ev[pos], " , %n", &delta);
375 0 : pos += delta;
376 0 : if (count == EOF) break;
377 : }
378 : }
379 306978 : } /* end _PR_SetLogModuleLevel() */
380 :
381 306978 : PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
382 : {
383 : PRLogModuleInfo *lm;
384 :
385 306978 : if (!_pr_initialized) _PR_ImplicitInitialization();
386 :
387 306978 : lm = PR_NEWZAP(PRLogModuleInfo);
388 306978 : if (lm) {
389 306978 : lm->name = strdup(name);
390 306978 : lm->level = PR_LOG_NONE;
391 306978 : lm->next = logModules;
392 306978 : logModules = lm;
393 306978 : _PR_SetLogModuleLevel(lm);
394 : }
395 306978 : return lm;
396 : }
397 :
398 0 : PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
399 : {
400 : #ifdef _PR_USE_STDIO_FOR_LOGGING
401 : FILE *newLogFile;
402 :
403 : #ifdef XP_PC
404 : if ( strcmp( file, "WinDebug") == 0)
405 : {
406 : newLogFile = WIN32_DEBUG_FILE;
407 : }
408 : else
409 : #endif
410 : {
411 : const char *mode = appendToLog ? "a" : "w";
412 : newLogFile = fopen(file, mode);
413 : if (!newLogFile)
414 : return PR_FALSE;
415 :
416 : #ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
417 : /* We do buffering ourselves. */
418 : setvbuf(newLogFile, NULL, _IONBF, 0);
419 : #endif
420 : }
421 : if (logFile
422 : && logFile != stdout
423 : && logFile != stderr
424 : #ifdef XP_PC
425 : && logFile != WIN32_DEBUG_FILE
426 : #endif
427 : ) {
428 : fclose(logFile);
429 : }
430 : logFile = newLogFile;
431 : return PR_TRUE;
432 : #else
433 : PRFileDesc *newLogFile;
434 0 : PRIntn flags = PR_WRONLY|PR_CREATE_FILE;
435 0 : if (appendToLog) {
436 0 : flags |= PR_APPEND;
437 : } else {
438 0 : flags |= PR_TRUNCATE;
439 : }
440 :
441 0 : newLogFile = PR_Open(file, flags, 0666);
442 0 : if (newLogFile) {
443 0 : if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
444 0 : PR_Close(logFile);
445 : }
446 0 : logFile = newLogFile;
447 : }
448 0 : return (PRBool) (newLogFile != 0);
449 : #endif /* _PR_USE_STDIO_FOR_LOGGING */
450 : }
451 :
452 0 : PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
453 : {
454 0 : PR_LogFlush();
455 :
456 0 : if (logBuf)
457 0 : PR_DELETE(logBuf);
458 :
459 0 : if (buffer_size >= LINE_BUF_SIZE) {
460 0 : logp = logBuf = (char*) PR_MALLOC(buffer_size);
461 0 : logEndp = logp + buffer_size;
462 : }
463 0 : }
464 :
465 6012 : PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
466 : {
467 : va_list ap;
468 : char line[LINE_BUF_SIZE];
469 6012 : char *line_long = NULL;
470 6012 : PRUint32 nb_tid = 0, nb;
471 : PRThread *me;
472 : PRExplodedTime now;
473 :
474 6012 : if (!_pr_initialized) _PR_ImplicitInitialization();
475 :
476 6012 : if (!logFile) {
477 6012 : return;
478 : }
479 :
480 0 : if (outputTimeStamp) {
481 0 : PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
482 0 : nb_tid = PR_snprintf(line, sizeof(line)-1,
483 : "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
484 0 : now.tm_year, now.tm_month + 1, now.tm_mday,
485 : now.tm_hour, now.tm_min, now.tm_sec,
486 : now.tm_usec);
487 : }
488 :
489 0 : me = PR_GetCurrentThread();
490 0 : nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ",
491 : #if defined(_PR_BTHREADS)
492 : me, me);
493 : #else
494 : me ? me->id : 0L, me);
495 : #endif
496 :
497 0 : va_start(ap, fmt);
498 0 : nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap);
499 0 : va_end(ap);
500 :
501 : /*
502 : * Check if we might have run out of buffer space (in case we have a
503 : * long line), and malloc a buffer just this once.
504 : */
505 0 : if (nb == sizeof(line)-2) {
506 0 : va_start(ap, fmt);
507 0 : line_long = PR_vsmprintf(fmt, ap);
508 0 : va_end(ap);
509 : /* If this failed, we'll fall back to writing the truncated line. */
510 : }
511 :
512 0 : if (line_long) {
513 0 : nb = strlen(line_long);
514 0 : _PR_LOCK_LOG();
515 0 : if (logBuf != 0) {
516 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
517 0 : logp = logBuf;
518 : }
519 : /*
520 : * Write out the thread id (with an optional timestamp) and the
521 : * malloc'ed buffer.
522 : */
523 0 : _PUT_LOG(logFile, line, nb_tid);
524 0 : _PUT_LOG(logFile, line_long, nb);
525 : /* Ensure there is a trailing newline. */
526 0 : if (!nb || (line_long[nb-1] != '\n')) {
527 : char eol[2];
528 0 : eol[0] = '\n';
529 0 : eol[1] = '\0';
530 0 : _PUT_LOG(logFile, eol, 1);
531 : }
532 0 : _PR_UNLOCK_LOG();
533 0 : PR_smprintf_free(line_long);
534 : } else {
535 : /* Ensure there is a trailing newline. */
536 0 : if (nb && (line[nb-1] != '\n')) {
537 0 : line[nb++] = '\n';
538 0 : line[nb] = '\0';
539 : }
540 0 : _PR_LOCK_LOG();
541 0 : if (logBuf == 0) {
542 0 : _PUT_LOG(logFile, line, nb);
543 : } else {
544 : /* If nb can't fit into logBuf, write out logBuf first. */
545 0 : if (logp + nb > logEndp) {
546 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
547 0 : logp = logBuf;
548 : }
549 : /* nb is guaranteed to fit into logBuf. */
550 0 : memcpy(logp, line, nb);
551 0 : logp += nb;
552 : }
553 0 : _PR_UNLOCK_LOG();
554 : }
555 0 : PR_LogFlush();
556 : }
557 :
558 7715 : PR_IMPLEMENT(void) PR_LogFlush(void)
559 : {
560 7715 : if (logBuf && logFile) {
561 0 : _PR_LOCK_LOG();
562 0 : if (logp > logBuf) {
563 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
564 0 : logp = logBuf;
565 : }
566 0 : _PR_UNLOCK_LOG();
567 : }
568 7715 : }
569 :
570 0 : PR_IMPLEMENT(void) PR_Abort(void)
571 : {
572 0 : PR_LogPrint("Aborting");
573 0 : abort();
574 : }
575 :
576 0 : PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
577 : {
578 0 : PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
579 0 : fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
580 0 : fflush(stderr);
581 : #ifdef WIN32
582 : DebugBreak();
583 : #endif
584 : #ifdef XP_OS2
585 : asm("int $3");
586 : #endif
587 0 : abort();
588 : }
|