0aa139af07b7cd4b4ca31407e3e5355bba766b86
[privoxy.git] / errlog.c
1 const char errlog_rcs[] = "$Id: errlog.c,v 1.4 2001/05/21 19:32:54 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/errlog.c,v $
5  *
6  * Purpose     :  Log errors to a designated destination in an elegant,
7  *                printf-like fashion.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                IJBSWA team.  http://ijbswa.sourceforge.net
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: errlog.c,v $
36  *    Revision 1.4  2001/05/21 19:32:54  jongfoster
37  *    Added another #ifdef _WIN_CONSOLE
38  *
39  *    Revision 1.3  2001/05/20 01:11:40  jongfoster
40  *    Added support for LOG_LEVEL_FATAL
41  *    Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE,
42  *    and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER
43  *
44  *    Revision 1.2  2001/05/17 22:42:01  oes
45  *     - Cleaned CRLF's from the sources and related files
46  *     - Repaired logging for REF and FRC
47  *
48  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
49  *    Initial import of version 2.9.3 source tree
50  *
51  *
52  *********************************************************************/
53 \f
54
55 #include "config.h"
56
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <stdarg.h>
60 #include <string.h>
61
62 #ifndef _WIN32
63 #include <unistd.h>
64 #endif /* ndef _WIN32 */
65
66 #include <errno.h>
67 /* #include <pthread.h> */
68
69 #ifdef _WIN32
70 #include <windows.h>
71 #ifndef _WIN_CONSOLE
72 #include "w32log.h"
73 #endif /* ndef _WIN_CONSOLE */
74 #endif /* def _WIN32 */
75
76 #include "errlog.h"
77 #include "project.h"
78
79 const char errlog_h_rcs[] = ERRLOG_H_VERSION;
80
81
82 /*
83  * LOG_LEVEL_FATAL, LOG_LEVEL_ERROR and LOG_LEVEL_INFO
84  * cannot be turned off.  (There are some exceptional situations
85  * where we need to get a message to the user).
86  *
87  * FIXME: Do we need LOG_LEVEL_INFO here?
88  */
89 #define LOG_LEVEL_MINIMUM  (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO)
90
91 /* where to log (default: stderr) */
92 static FILE *logfp = NULL;
93
94 /* where to log (NULL == stderr) */
95 static char * logfilename = NULL;
96
97 /* logging detail level.  */
98 static int debug = LOG_LEVEL_MINIMUM;  
99
100 static void fatal_error(const char * error_message);
101
102
103 /*********************************************************************
104  *
105  * Function    :  fatal_error
106  *
107  * Description :  Displays a fatal error to standard error (or, on 
108  *                a WIN32 GUI, to a dialog box), and exits
109  *                JunkBuster with status code 1.
110  *
111  * Parameters  :
112  *          1  :  error_message = The error message to display.
113  *
114  * Returns     :  Does not return.
115  *
116  *********************************************************************/
117 static void fatal_error(const char * error_message)
118 {
119 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
120    MessageBox(NULL, error_message, "Internet JunkBuster Error", 
121       MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);  
122 #else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */
123    fputs(error_message, stderr);
124 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
125
126    exit(1);
127 }
128
129
130 /*********************************************************************
131  *
132  * Function    :  init_errlog
133  *
134  * Description :  Initializes the logging module.  Must call before
135  *                calling log_error.
136  *
137  * Parameters  :
138  *          1  :  prog_name  = The program name.
139  *          2  :  logfname   = The logfile name, or NULL for stderr.
140  *          3  :  debuglevel = The debugging level.
141  *
142  * Returns     :  N/A
143  *
144  *********************************************************************/
145 void init_error_log(const char *prog_name, const char *logfname, int debuglevel)
146 {
147    FILE *fp;
148
149    /* FIXME RACE HAZARD: should start critical section error_log_use here */
150
151    /* set the logging detail level */
152    debug = debuglevel | LOG_LEVEL_MINIMUM;
153
154    if ((logfp != NULL) && (logfp != stderr))
155    {
156       fclose(logfp);
157    }
158    logfp = stderr;
159
160    /* set the designated log file */
161    if( logfname )
162    {
163       if( !(fp = fopen(logfname, "a")) )
164       {
165          log_error(LOG_LEVEL_FATAL, "init_errlog(): can't open logfile: %s", logfname);
166       }
167
168       /* set logging to be completely unbuffered */
169       setbuf(fp, NULL);
170
171       logfp = fp;
172    }
173
174    log_error(LOG_LEVEL_INFO, "Internet JunkBuster version " VERSION);
175    if (prog_name != NULL)
176    {
177       log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name);
178    }
179
180    /* FIXME RACE HAZARD: should end critical section error_log_use here */
181
182 } /* init_error_log */
183
184
185 /*********************************************************************
186  *
187  * Function    :  log_error
188  *
189  * Description :  This is the error-reporting and logging function.
190  *
191  * Parameters  :
192  *          1  :  loglevel  = the type of message to be logged
193  *          2  :  fmt       = the main string we want logged, printf-like
194  *          3  :  ...       = arguments to be inserted in fmt (printf-like).
195  *
196  * Returns     :  N/A
197  *
198  *********************************************************************/
199 void log_error(int loglevel, char *fmt, ...)
200 {
201    va_list ap;
202    char outbuf[BUFSIZ];
203    char * src = fmt;
204    int outc = 0;
205    long this_thread = 1;  /* was: pthread_t this_thread;*/
206
207    /* verify if loglevel applies to current settings and bail out if negative */
208    if(!(loglevel & debug))
209    {
210                 return;
211    }
212
213    /* FIXME get current thread id */
214    /* this_thread = (long)pthread_self(); */
215
216    switch (loglevel)
217    {
218       case LOG_LEVEL_ERROR:
219          outc = sprintf(outbuf, "IJB(%d) Error: ", this_thread);
220          break;
221       case LOG_LEVEL_FATAL:
222          outc = sprintf(outbuf, "IJB(%d) Fatal error: ", this_thread);
223          break;
224       case LOG_LEVEL_GPC:
225          outc = sprintf(outbuf, "IJB(%d) Request: ", this_thread);
226          break;
227       case LOG_LEVEL_CONNECT:
228          outc = sprintf(outbuf, "IJB(%d) Connect: ", this_thread);
229          break;
230       case LOG_LEVEL_HEADER:
231          outc = sprintf(outbuf, "IJB(%d) Header: ", this_thread);
232          break;
233       case LOG_LEVEL_INFO:
234          outc = sprintf(outbuf, "IJB(%d) Info: ", this_thread);
235          break;
236 #ifdef PCRS
237       case LOG_LEVEL_RE_FILTER:
238          outc = sprintf(outbuf, "IJB(%d) Re-Filter: ", this_thread);
239          break;
240 #endif /* def PCRS */
241 #ifdef FORCE_LOAD
242       case LOG_LEVEL_FORCE:
243          outc = sprintf(outbuf, "IJB(%d) Force: ", this_thread);
244          break;
245 #endif /* def FORCE_LOAD */
246 #ifdef FAST_REDIRECTS
247       case LOG_LEVEL_REDIRECTS:
248          outc = sprintf(outbuf, "IJB(%d) Redirect: ", this_thread);
249          break;
250 #endif /* def FAST_REDIRECTS */
251       default:
252          outc = sprintf(outbuf, "IJB(%d) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel);
253          break;
254    }
255    
256    /* get ready to scan var. args. */
257    va_start( ap, fmt );
258
259    /* build formatted message from fmt and var-args */
260    while ((*src) && (outc < BUFSIZ-2))
261    {
262       char tempbuf[BUFSIZ];
263       char *sval;
264       int ival;
265       unsigned uval;
266       long lval;
267       unsigned long ulval;
268       int oldoutc;
269       char ch;
270       
271       ch = *src++;
272       if( ch != '%' )
273       {
274          outbuf[outc++] = ch;
275          continue;
276       }
277
278       ch = *src++;
279       switch (ch) {
280          case '%':
281             outbuf[outc++] = '%';
282             break;
283          case 'd':
284             ival = va_arg( ap, int );
285             oldoutc = outc;
286             outc += sprintf(tempbuf, "%d", ival);
287             if (outc < BUFSIZ-1) 
288             {
289                strcpy(outbuf + oldoutc, tempbuf);
290             }
291             else
292             {
293                outbuf[oldoutc] = '\0';
294             }
295             break;
296          case 'u':
297             uval = va_arg( ap, unsigned );
298             oldoutc = outc;
299             outc += sprintf(tempbuf, "%u", uval);
300             if (outc < BUFSIZ-1) 
301             {
302                strcpy(outbuf + oldoutc, tempbuf);
303             }
304             else
305             {
306                outbuf[oldoutc] = '\0';
307             }
308             break;
309          case 'l':
310             /* this is a modifier that must be followed by u or d */
311             ch = *src++;
312             if (ch == 'd')
313             {
314                lval = va_arg( ap, long );
315                oldoutc = outc;
316                outc += sprintf(tempbuf, "%ld", lval);
317             }
318             else if (ch == 'u')
319             {
320                ulval = va_arg( ap, unsigned long );
321                oldoutc = outc;
322                outc += sprintf(tempbuf, "%lu", ulval);
323             }
324             else
325             {
326                /* Error */
327                sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
328                                "Format = \"%s\"\n"
329                                "Exiting.", this_thread, fmt);
330                /* FIXME RACE HAZARD: should start critical section error_log_use here */
331                if( !logfp )
332                {
333                   logfp = stderr;
334                }
335                fputs(outbuf, logfp);
336                /* FIXME RACE HAZARD: should end critical section error_log_use here */
337                fatal_error(outbuf);
338                /* Never get here */
339                break;
340             }
341             if (outc < BUFSIZ-1) 
342             {
343                strcpy(outbuf + oldoutc, tempbuf);
344             }
345             else
346             {
347                outbuf[oldoutc] = '\0';
348             }
349             break;
350          case 'c':
351             /*
352              * Note that char paramaters are converted to int, so we need to
353              * pass "int" to va_arg.  (See K&R, 2nd ed, section A7.3.2, page 202)
354              */
355             outbuf[outc++] = (char) va_arg( ap, int );
356             break;
357          case 's':
358             sval = va_arg( ap, char * );
359             oldoutc = outc;
360             outc += strlen(sval);
361             if (outc < BUFSIZ-1) 
362             {
363                strcpy(outbuf + oldoutc, sval);
364             }
365             else
366             {
367                outbuf[oldoutc] = '\0';
368             }
369             break;
370          case 'E':
371             /* Non-standard: Print error code from errno */
372             ival = errno;
373 #ifndef NOSTRERROR
374             sval = strerror(ival);
375 #else /* def NOSTRERROR */
376             sval = NULL
377 #endif /* def NOSTRERROR */
378             if (sval == NULL)
379             {
380                sprintf(tempbuf, "(errno = %d)", ival);
381                sval = tempbuf;
382             }
383             oldoutc = outc;
384             outc += strlen(sval);
385             if (outc < BUFSIZ-1) 
386             {
387                strcpy(outbuf + oldoutc, sval);
388             }
389             else
390             {
391                outbuf[oldoutc] = '\0';
392             }
393             break;
394          default:
395             sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
396                             "Format = \"%s\"\n"
397                             "Exiting.", this_thread, fmt);
398             /* FIXME RACE HAZARD: should start critical section error_log_use here */
399             if( !logfp )
400             {
401                logfp = stderr;
402             }
403             fputs(outbuf, logfp);
404             /* FIXME RACE HAZARD: should end critical section error_log_use here */
405             fatal_error(outbuf);
406             /* Never get here */
407             break;
408
409       } /* switch( p ) */
410
411    } /* for( p ... ) */
412    
413    /* done with var. args */
414    va_end( ap );
415    
416    if (outc >= BUFSIZ-2)
417    {
418       /* insufficient room for newline and trailing null. */
419
420       static const char warning[] = "... [too long, truncated]\n";
421
422       if (outc < BUFSIZ)
423       {
424          /* Need to add terminating null in this case. */
425          outbuf[outc] = '\0';
426       }
427
428       /* Truncate output */
429       outbuf[BUFSIZ - sizeof(warning)] = '\0';
430
431       /* Append warning */
432       strcat(outbuf, warning);
433    }
434    else
435    {
436       /* Add terminating newline and null */
437       outbuf[outc++] = '\n';
438       outbuf[outc] = '\0';
439    }
440
441    /* FIXME RACE HAZARD: should start critical section error_log_use here */
442
443    /* deal with glibc stupidity - it won't let you initialize logfp */
444    if( !logfp )
445    {
446       logfp = stderr;
447    }
448
449    fputs(outbuf, logfp);
450
451    if (loglevel == LOG_LEVEL_FATAL)
452    {
453       fatal_error(outbuf);
454       /* Never get here */
455    }
456
457    /* FIXME RACE HAZARD: should end critical section error_log_use here */
458
459 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
460    /* Write to display */
461    LogPutString(outbuf);
462    LogShowActivity();
463 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
464
465 }
466
467
468 /*
469   Local Variables:
470   tab-width: 3
471   end:
472 */
473