/********************************************************************* * * Function : log_error * * Description : This is the error-reporting and logging function. * * Parameters : * 1 : loglevel = the type of message to be logged * 2 : fmt = the main string we want logged, printf-like * 3 : ... = arguments to be inserted in fmt (printf-like). * * Returns : N/A * *********************************************************************/ void log_error(int loglevel, const char *fmt, ...) { va_list ap; char *outbuf = NULL; static char *outbuf_save = NULL; char tempbuf[BUFFER_SIZE]; size_t length = 0; const char * src = fmt; long thread_id; char timestamp[30]; /* * XXX: Make this a config option, * why else do we allocate instead of using * an array? */ size_t log_buffer_size = BUFFER_SIZE; #if defined(_WIN32) && !defined(_WIN_CONSOLE) /* * Irrespective of debug setting, a GET/POST/CONNECT makes * the taskbar icon animate. (There is an option to disable * this but checking that is handled inside LogShowActivity()). */ if ((loglevel == LOG_LEVEL_GPC) || (loglevel == LOG_LEVEL_CRUNCH)) { LogShowActivity(); } #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ /* * verify that the loglevel applies to current * settings and that logging is enabled. * Bail out otherwise. */ if ((0 == (loglevel & debug)) #ifndef _WIN32 || (logfp == NULL) #endif ) { if (loglevel == LOG_LEVEL_FATAL) { fatal_error("Fatal error. You're not supposed to" "see this message. Please file a bug report."); } return; } thread_id = get_thread_id(); get_log_timestamp(timestamp, sizeof(timestamp)); /* protect the whole function because of the static buffer (outbuf) */ lock_logfile(); if (NULL == outbuf_save) { outbuf_save = (char*)zalloc(log_buffer_size + 1); /* +1 for paranoia */ if (NULL == outbuf_save) { snprintf(tempbuf, sizeof(tempbuf), "%s %08lx Fatal error: Out of memory in log_error().", timestamp, thread_id); fatal_error(tempbuf); /* Exit */ return; } } outbuf = outbuf_save; /* * Memsetting the whole buffer to zero (in theory) * makes things easier later on. */ memset(outbuf, 0, log_buffer_size); /* Add prefix for everything but Common Log Format messages */ if (loglevel != LOG_LEVEL_CLF) { length = (size_t)snprintf(outbuf, log_buffer_size, "%s %08lx %s: ", timestamp, thread_id, get_log_level_string(loglevel)); } /* get ready to scan var. args. */ va_start(ap, fmt); /* build formatted message from fmt and var-args */ while ((*src) && (length < log_buffer_size-2)) { const char *sval = NULL; /* %N string */ int ival; /* %N string length or an error code */ unsigned uval; /* %u value */ long lval; /* %l value */ unsigned long ulval; /* %ul value */ char ch; const char *format_string = tempbuf; ch = *src++; if (ch != '%') { outbuf[length++] = ch; /* * XXX: Only necessary on platforms where multiple threads * can write to the buffer at the same time because we * don't support mutexes (OS/2 for example). */ outbuf[length] = '\0'; continue; } outbuf[length] = '\0'; ch = *src++; switch (ch) { case '%': tempbuf[0] = '%'; tempbuf[1] = '\0'; break; case 'd': ival = va_arg(ap, int); snprintf(tempbuf, sizeof(tempbuf), "%d", ival); break; case 'u': uval = va_arg(ap, unsigned); snprintf(tempbuf, sizeof(tempbuf), "%u", uval); break; case 'l': /* this is a modifier that must be followed by u, lu, or d */ ch = *src++; if (ch == 'd') { lval = va_arg(ap, long); snprintf(tempbuf, sizeof(tempbuf), "%ld", lval); } else if (ch == 'u') { ulval = va_arg(ap, unsigned long); snprintf(tempbuf, sizeof(tempbuf), "%lu", ulval); }
/********************************************************************* * * Function : log_error * * Description : This is the error-reporting and logging function. * * Parameters : * 1 : loglevel = the type of message to be logged * 2 : fmt = the main string we want logged, printf-like * 3 : ... = arguments to be inserted in fmt (printf-like). * * Returns : N/A * *********************************************************************/ void log_error(int loglevel, char *fmt, ...) { va_list ap; char *outbuf= NULL; static char *outbuf_save = NULL; char * src = fmt; int outc = 0; long this_thread = 1; /* was: pthread_t this_thread;*/ #ifdef __OS2__ PTIB ptib; APIRET ulrc; #endif /* __OS2__ */ #if defined(_WIN32) && !defined(_WIN_CONSOLE) /* * Irrespective of debug setting, a GET/POST/CONNECT makes * the taskbar icon animate. (There is an option to disable * this but checking that is handled inside LogShowActivity()). */ if (loglevel == LOG_LEVEL_GPC) { LogShowActivity(); } #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */ /* verify if loglevel applies to current settings and bail out if negative */ if ((loglevel & debug) == 0) { return; } /* FIXME get current thread id */ #ifdef FEATURE_PTHREAD this_thread = (long)pthread_self(); #ifdef __MACH__ /* * Mac OSX (and perhaps other Mach instances) doesn't have a debuggable * value at the first 4 bytes of pthread_self()'s return value, a pthread_t. * pthread_t is supposed to be opaque... but it's fairly random, though, so * we make it mostly presentable. */ this_thread = abs(this_thread % 1000); #endif /* def __MACH__ */ #elif defined(_WIN32) this_thread = GetCurrentThreadId(); #elif defined(__OS2__) ulrc = DosGetInfoBlocks(&ptib, NULL); if (ulrc == 0) this_thread = ptib -> tib_ptib2 -> tib2_ultid; #endif /* def FEATURE_PTHREAD */ if ( !outbuf_save ) { outbuf_save = outbuf = (char*)malloc(BUFFER_SIZE); assert(outbuf); } outbuf = outbuf_save; { /* * Write timestamp into tempbuf. * * Complex because not all OSs have tm_gmtoff or * the %z field in strftime() */ time_t now; struct tm tm_now; time (&now); #ifdef HAVE_LOCALTIME_R tm_now = *localtime_r(&now, &tm_now); #elif OSX_DARWIN pthread_mutex_lock(&localtime_mutex); tm_now = *localtime (&now); pthread_mutex_unlock(&localtime_mutex); #else tm_now = *localtime (&now); #endif strftime(outbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); outbuf += strlen( outbuf ); } switch (loglevel) { case LOG_LEVEL_ERROR: outc = sprintf(outbuf, "Privoxy(%05ld) Error: ", this_thread); break; case LOG_LEVEL_FATAL: outc = sprintf(outbuf, "Privoxy(%05ld) Fatal error: ", this_thread); break; case LOG_LEVEL_GPC: outc = sprintf(outbuf, "Privoxy(%05ld) Request: ", this_thread); break; case LOG_LEVEL_CONNECT: outc = sprintf(outbuf, "Privoxy(%05ld) Connect: ", this_thread); break; case LOG_LEVEL_LOG: outc = sprintf(outbuf, "Privoxy(%05ld) Writing: ", this_thread); break; case LOG_LEVEL_HEADER: outc = sprintf(outbuf, "Privoxy(%05ld) Header: ", this_thread); break; case LOG_LEVEL_INFO: outc = sprintf(outbuf, "Privoxy(%05ld) Info: ", this_thread); break; case LOG_LEVEL_RE_FILTER: outc = sprintf(outbuf, "Privoxy(%05ld) Re-Filter: ", this_thread); break; #ifdef FEATURE_FORCE_LOAD case LOG_LEVEL_FORCE: outc = sprintf(outbuf, "Privoxy(%05ld) Force: ", this_thread); break; #endif /* def FEATURE_FORCE_LOAD */ #ifdef FEATURE_FAST_REDIRECTS case LOG_LEVEL_REDIRECTS: outc = sprintf(outbuf, "Privoxy(%05ld) Redirect: ", this_thread); break; #endif /* def FEATURE_FAST_REDIRECTS */ case LOG_LEVEL_DEANIMATE: outc = sprintf(outbuf, "Privoxy(%05ld) Gif-Deanimate: ", this_thread); break; case LOG_LEVEL_CLF: outbuf = outbuf_save; outc = 0; outbuf[0] = '\0'; break; #ifdef FEATURE_KILL_POPUPS case LOG_LEVEL_POPUPS: outc = sprintf(outbuf, "Privoxy(%05ld) Kill-Popups: ", this_thread); break; #endif /* def FEATURE_KILL_POPUPS */ case LOG_LEVEL_CGI: outc = sprintf(outbuf, "Privoxy(%05ld) CGI: ", this_thread); break; default: outc = sprintf(outbuf, "Privoxy(%05ld) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel); break; } /* get ready to scan var. args. */ va_start( ap, fmt ); /* build formatted message from fmt and var-args */ while ((*src) && (outc < BUFFER_SIZE-2)) { char tempbuf[BUFFER_SIZE]; char *sval = NULL; int ival; unsigned uval; long lval; unsigned long ulval; int oldoutc; char ch; ch = *src++; if( ch != '%' ) { outbuf[outc++] = ch; continue; } ch = *src++; switch (ch) { case '%': outbuf[outc++] = '%'; break; case 'd': ival = va_arg( ap, int ); oldoutc = outc; outc += sprintf(tempbuf, "%d", ival); if (outc < BUFFER_SIZE-1) { strcpy(outbuf + oldoutc, tempbuf); } else { outbuf[oldoutc] = '\0'; } break; case 'u': uval = va_arg( ap, unsigned ); oldoutc = outc; outc += sprintf(tempbuf, "%u", uval); if (outc < BUFFER_SIZE-1) { strcpy(outbuf + oldoutc, tempbuf); } else { outbuf[oldoutc] = '\0'; } break; case 'l': /* this is a modifier that must be followed by u or d */ ch = *src++; if (ch == 'd') { lval = va_arg( ap, long ); oldoutc = outc; outc += sprintf(tempbuf, "%ld", lval); } else if (ch == 'u') { ulval = va_arg( ap, unsigned long ); oldoutc = outc; outc += sprintf(tempbuf, "%lu", ulval); }