Back to index

lightning-sunbird  0.9+nobinonly
prlog.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Netscape Portable Runtime (NSPR).
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /*
00040  * Contributors:
00041  *
00042  * This Original Code has been modified by IBM Corporation.
00043  * Modifications made by IBM described herein are
00044  * Copyright (c) International Business Machines Corporation, 2000.
00045  * Modifications to Mozilla code or documentation identified per
00046  * MPL Section 3.3
00047  *
00048  * Date         Modified by     Description of modification
00049  * 04/10/2000   IBM Corp.       Added DebugBreak() definitions for OS/2
00050  */
00051 
00052 #include "primpl.h"
00053 #include "prenv.h"
00054 #include "prprf.h"
00055 #include <string.h>
00056 
00057 /*
00058  * Lock used to lock the log.
00059  *
00060  * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
00061  * contain assertions.  We have to avoid assertions in _PR_LOCK_LOG
00062  * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
00063  * This can lead to infinite recursion.
00064  */
00065 static PRLock *_pr_logLock;
00066 #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
00067 #define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
00068 #define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
00069 #elif defined(_PR_GLOBAL_THREADS_ONLY)
00070 #define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
00071 #define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
00072 #else
00073 
00074 #define _PR_LOCK_LOG() \
00075 { \
00076     PRIntn _is; \
00077     PRThread *_me = _PR_MD_CURRENT_THREAD(); \
00078     if (!_PR_IS_NATIVE_THREAD(_me)) \
00079         _PR_INTSOFF(_is); \
00080     _PR_LOCK_LOCK(_pr_logLock)
00081 
00082 #define _PR_UNLOCK_LOG() \
00083     _PR_LOCK_UNLOCK(_pr_logLock); \
00084     PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
00085     if (!_PR_IS_NATIVE_THREAD(_me)) \
00086         _PR_INTSON(_is); \
00087 }
00088 
00089 #endif
00090 
00091 #if defined(XP_PC)
00092 #define strcasecmp stricmp
00093 #define strncasecmp strnicmp
00094 #endif
00095 
00096 /*
00097  * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
00098  * because every asynchronous file io operation leads to a fiber context
00099  * switch.  So we define _PUT_LOG as fputs (from stdio.h).  A side
00100  * benefit is that fputs handles the LF->CRLF translation.  This
00101  * code can also be used on other platforms with file stream io.
00102  */
00103 #if defined(WIN32) || defined(XP_OS2)
00104 #define _PR_USE_STDIO_FOR_LOGGING
00105 #endif
00106 
00107 /*
00108 ** Coerce Win32 log output to use OutputDebugString() when
00109 ** NSPR_LOG_FILE is set to "WinDebug".
00110 */
00111 #if defined(XP_PC)
00112 #define WIN32_DEBUG_FILE (FILE*)-2
00113 #endif
00114 
00115 /* Macros used to reduce #ifdef pollution */
00116 
00117 #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
00118 #define _PUT_LOG(fd, buf, nb) \
00119     PR_BEGIN_MACRO \
00120     if (logFile == WIN32_DEBUG_FILE) { \
00121         char savebyte = buf[nb]; \
00122         buf[nb] = '\0'; \
00123         OutputDebugString(buf); \
00124         buf[nb] = savebyte; \
00125     } else { \
00126         fwrite(buf, 1, nb, fd); \
00127         fflush(fd); \
00128     } \
00129     PR_END_MACRO
00130 #elif defined(_PR_USE_STDIO_FOR_LOGGING)
00131 #define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);}
00132 #elif defined(_PR_PTHREADS)
00133 #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
00134 #elif defined(XP_MAC)
00135 #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE_SYNC(fd, buf, nb)
00136 #else
00137 #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
00138 #endif
00139 
00140 /************************************************************************/
00141 
00142 static PRLogModuleInfo *logModules;
00143 
00144 static char *logBuf = NULL;
00145 static char *logp;
00146 static char *logEndp;
00147 #ifdef _PR_USE_STDIO_FOR_LOGGING
00148 static FILE *logFile = NULL;
00149 #else
00150 static PRFileDesc *logFile = 0;
00151 #endif
00152 
00153 #define LINE_BUF_SIZE           512
00154 #define DEFAULT_BUF_SIZE        16384
00155 
00156 #ifdef _PR_NEED_STRCASECMP
00157 
00158 /*
00159  * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
00160  * such as NCR and Unixware.  Linking with both libc and libucb
00161  * may cause some problem, so I just provide our own implementation
00162  * of strcasecmp here.
00163  */
00164 
00165 static const unsigned char uc[] =
00166 {
00167     '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
00168     '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
00169     '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
00170     '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
00171     ' ',    '!',    '"',    '#',    '$',    '%',    '&',    '\'',
00172     '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
00173     '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
00174     '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
00175     '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
00176     'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
00177     'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
00178     'X',    'Y',    'Z',    '[',    '\\',   ']',    '^',    '_',
00179     '`',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
00180     'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
00181     'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
00182     'X',    'Y',    'Z',    '{',    '|',    '}',    '~',    '\177'
00183 };
00184 
00185 PRIntn strcasecmp(const char *a, const char *b)
00186 {
00187     const unsigned char *ua = (const unsigned char *)a;
00188     const unsigned char *ub = (const unsigned char *)b;
00189 
00190     if( ((const char *)0 == a) || (const char *)0 == b ) 
00191         return (PRIntn)(a-b);
00192 
00193     while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
00194     {
00195         a++;
00196         ua++;
00197         ub++;
00198     }
00199 
00200     return (PRIntn)(uc[*ua] - uc[*ub]);
00201 }
00202 
00203 #endif /* _PR_NEED_STRCASECMP */
00204 
00205 void _PR_InitLog(void)
00206 {
00207     char *ev;
00208 
00209     _pr_logLock = PR_NewLock();
00210 
00211     ev = PR_GetEnv("NSPR_LOG_MODULES");
00212     if (ev && ev[0]) {
00213         char module[64];  /* Security-Critical: If you change this
00214                            * size, you must also change the sscanf
00215                            * format string to be size-1.
00216                            */
00217         PRBool isSync = PR_FALSE;
00218         PRIntn evlen = strlen(ev), pos = 0;
00219         PRInt32 bufSize = DEFAULT_BUF_SIZE;
00220         while (pos < evlen) {
00221             PRIntn level = 1, count = 0, delta = 0;
00222             count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
00223                            module, &delta, &level, &delta);
00224             pos += delta;
00225             if (count == 0) break;
00226 
00227             /*
00228             ** If count == 2, then we got module and level. If count
00229             ** == 1, then level defaults to 1 (module enabled).
00230             */
00231             if (strcasecmp(module, "sync") == 0) {
00232                 isSync = PR_TRUE;
00233             } else if (strcasecmp(module, "bufsize") == 0) {
00234                 if (level >= LINE_BUF_SIZE) {
00235                     bufSize = level;
00236                 }
00237             } else {
00238                 PRLogModuleInfo *lm = logModules;
00239                 PRBool skip_modcheck =
00240                     (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
00241 
00242                 while (lm != NULL) {
00243                     if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
00244                     else if (strcasecmp(module, lm->name) == 0) {
00245                         lm->level = (PRLogModuleLevel)level;
00246                         break;
00247                     }
00248                     lm = lm->next;
00249                 }
00250             }
00251             /*found:*/
00252             count = sscanf(&ev[pos], " , %n", &delta);
00253             pos += delta;
00254             if (count == EOF) break;
00255         }
00256         PR_SetLogBuffering(isSync ? bufSize : 0);
00257 
00258 #ifdef XP_UNIX
00259         if ((getuid() != geteuid()) || (getgid() != getegid())) {
00260             return;
00261         }
00262 #endif /* XP_UNIX */
00263 
00264         ev = PR_GetEnv("NSPR_LOG_FILE");
00265         if (ev && ev[0]) {
00266             if (!PR_SetLogFile(ev)) {
00267 #ifdef XP_PC
00268                 char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
00269                 if (str) {
00270                     OutputDebugString(str);
00271                     PR_smprintf_free(str);
00272                 }
00273 #else
00274                 fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
00275 #endif
00276             }
00277         } else {
00278 #ifdef _PR_USE_STDIO_FOR_LOGGING
00279             logFile = stderr;
00280 #else
00281             logFile = _pr_stderr;
00282 #endif
00283         }
00284     }
00285 }
00286 
00287 void _PR_LogCleanup(void)
00288 {
00289     PRLogModuleInfo *lm = logModules;
00290 
00291     PR_LogFlush();
00292 
00293 #ifdef _PR_USE_STDIO_FOR_LOGGING
00294     if (logFile
00295         && logFile != stdout
00296         && logFile != stderr
00297 #ifdef XP_PC
00298         && logFile != WIN32_DEBUG_FILE
00299 #endif
00300         ) {
00301         fclose(logFile);
00302         logFile = NULL;
00303     }
00304 #else
00305     if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
00306         PR_Close(logFile);
00307         logFile = NULL;
00308     }
00309 #endif
00310 
00311     while (lm != NULL) {
00312         PRLogModuleInfo *next = lm->next;
00313         free((/*const*/ char *)lm->name);
00314         PR_Free(lm);
00315         lm = next;
00316     }
00317     logModules = NULL;
00318 
00319     if (_pr_logLock) {
00320         PR_DestroyLock(_pr_logLock);
00321         _pr_logLock = NULL;
00322     }
00323 }
00324 
00325 static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
00326 {
00327     char *ev;
00328 
00329     ev = PR_GetEnv("NSPR_LOG_MODULES");
00330     if (ev && ev[0]) {
00331         char module[64];  /* Security-Critical: If you change this
00332                            * size, you must also change the sscanf
00333                            * format string to be size-1.
00334                            */
00335         PRIntn evlen = strlen(ev), pos = 0;
00336         while (pos < evlen) {
00337             PRIntn level = 1, count = 0, delta = 0;
00338 
00339             count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
00340                            module, &delta, &level, &delta);
00341             pos += delta;
00342             if (count == 0) break;
00343 
00344             /*
00345             ** If count == 2, then we got module and level. If count
00346             ** == 1, then level defaults to 1 (module enabled).
00347             */
00348             if (lm != NULL)
00349             {
00350                 if ((strcasecmp(module, "all") == 0)
00351                     || (strcasecmp(module, lm->name) == 0))
00352                 {
00353                     lm->level = (PRLogModuleLevel)level;
00354                 }
00355             }
00356             count = sscanf(&ev[pos], " , %n", &delta);
00357             pos += delta;
00358             if (count == EOF) break;
00359         }
00360     }
00361 } /* end _PR_SetLogModuleLevel() */
00362 
00363 PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
00364 {
00365     PRLogModuleInfo *lm;
00366 
00367         if (!_pr_initialized) _PR_ImplicitInitialization();
00368 
00369     lm = PR_NEWZAP(PRLogModuleInfo);
00370     if (lm) {
00371         lm->name = strdup(name);
00372         lm->level = PR_LOG_NONE;
00373         lm->next = logModules;
00374         logModules = lm;
00375         _PR_SetLogModuleLevel(lm);
00376     }
00377     return lm;
00378 }
00379 
00380 PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
00381 {
00382 #ifdef _PR_USE_STDIO_FOR_LOGGING
00383     FILE *newLogFile;
00384 
00385 #ifdef XP_PC
00386     if ( strcmp( file, "WinDebug") == 0)
00387     {
00388         newLogFile = WIN32_DEBUG_FILE;
00389     }
00390     else
00391 #endif
00392     {
00393         newLogFile = fopen(file, "w");
00394         if (!newLogFile)
00395             return PR_FALSE;
00396 
00397         /* We do buffering ourselves. */
00398         setvbuf(newLogFile, NULL, _IONBF, 0);
00399     }
00400     if (logFile
00401         && logFile != stdout
00402         && logFile != stderr
00403 #ifdef XP_PC
00404         && logFile != WIN32_DEBUG_FILE
00405 #endif
00406         ) {
00407         fclose(logFile);
00408     }
00409     logFile = newLogFile;
00410     return PR_TRUE;
00411 #else
00412     PRFileDesc *newLogFile;
00413 
00414     newLogFile = PR_Open(file, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0666);
00415     if (newLogFile) {
00416         if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
00417             PR_Close(logFile);
00418         }
00419         logFile = newLogFile;
00420 #if defined(XP_MAC)
00421         SetLogFileTypeCreator(file);
00422 #endif
00423     }
00424     return (PRBool) (newLogFile != 0);
00425 #endif /* _PR_USE_STDIO_FOR_LOGGING */
00426 }
00427 
00428 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
00429 {
00430     PR_LogFlush();
00431 
00432     if (logBuf)
00433         PR_DELETE(logBuf);
00434 
00435     if (buffer_size >= LINE_BUF_SIZE) {
00436         logp = logBuf = (char*) PR_MALLOC(buffer_size);
00437         logEndp = logp + buffer_size;
00438     }
00439 }
00440 
00441 PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
00442 {
00443     va_list ap;
00444     char line[LINE_BUF_SIZE];
00445     PRUint32 nb;
00446     PRThread *me;
00447 
00448     if (!_pr_initialized) _PR_ImplicitInitialization();
00449 
00450     if (!logFile) {
00451         return;
00452     }
00453 
00454     va_start(ap, fmt);
00455     me = PR_GetCurrentThread();
00456     nb = PR_snprintf(line, sizeof(line)-1, "%ld[%p]: ",
00457 #if defined(_PR_DCETHREADS)
00458              /* The problem is that for _PR_DCETHREADS, pthread_t is not a 
00459               * pointer, but a structure; so you can't easily print it...
00460               */
00461                      me ? &(me->id): 0L, me);
00462 #elif defined(_PR_BTHREADS)
00463                    me, me);
00464 #else
00465                      me ? me->id : 0L, me);
00466 #endif
00467 
00468     nb += PR_vsnprintf(line+nb, sizeof(line)-nb-1, fmt, ap);
00469     if (nb && (line[nb-1] != '\n')) {
00470 #ifndef XP_MAC
00471         line[nb++] = '\n';
00472 #else
00473         line[nb++] = '\015';
00474 #endif 
00475         line[nb] = '\0';
00476     } else {
00477 #ifdef XP_MAC
00478         line[nb-1] = '\015';
00479 #endif
00480     }
00481     va_end(ap);
00482 
00483     _PR_LOCK_LOG();
00484     if (logBuf == 0) {
00485         _PUT_LOG(logFile, line, nb);
00486     } else {
00487         if (logp + nb > logEndp) {
00488             _PUT_LOG(logFile, logBuf, logp - logBuf);
00489             logp = logBuf;
00490         }
00491         memcpy(logp, line, nb);
00492         logp += nb;
00493     }
00494     _PR_UNLOCK_LOG();
00495     PR_LogFlush();
00496 }
00497 
00498 PR_IMPLEMENT(void) PR_LogFlush(void)
00499 {
00500     if (logBuf && logFile) {
00501         _PR_LOCK_LOG();
00502             if (logp > logBuf) {
00503                 _PUT_LOG(logFile, logBuf, logp - logBuf);
00504                 logp = logBuf;
00505             }
00506         _PR_UNLOCK_LOG();
00507     }
00508 }
00509 
00510 PR_IMPLEMENT(void) PR_Abort(void)
00511 {
00512     PR_LogPrint("Aborting");
00513     abort();
00514 }
00515 
00516 #if defined(XP_OS2)
00517 /*
00518  * Added definitions for DebugBreak() for 2 different OS/2 compilers.
00519  * Doing the int3 on purpose for Visual Age so that a developer can
00520  * step over the instruction if so desired.  Not always possible if
00521  * trapping due to exception handling IBM-AKR
00522  */
00523 #if defined(XP_OS2_VACPP)
00524 #include <builtin.h>
00525 static void DebugBreak(void) { _interrupt(3); }
00526 #elif defined(XP_OS2_EMX)
00527 static void DebugBreak(void) { asm("int $3"); }
00528 #else
00529 static void DebugBreak(void) { }
00530 #endif
00531 #endif /* XP_OS2 */
00532 
00533 PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
00534 {
00535     PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
00536 #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS)
00537     fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
00538 #endif
00539 #ifdef XP_MAC
00540     dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln);
00541 #endif
00542 #if defined(WIN32) || defined(XP_OS2)
00543     DebugBreak();
00544 #endif
00545 #ifndef XP_MAC
00546     abort();
00547 #endif
00548 }
00549 
00550 #ifdef XP_MAC
00551 PR_IMPLEMENT(void) PR_Init_Log(void)
00552 {
00553        _PR_InitLog();
00554 }
00555 #endif