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