Update CHANGES; fix a bogus entry, and mention Jake's work.
[polipo.git] / log.c
blobec5c545615857d29b87825706513da3bef2baac3
1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include "polipo.h"
25 #ifdef HAVE_SYSLOG
26 #include <syslog.h>
27 #endif
29 static int logLevel = LOGGING_DEFAULT;
30 static int logSyslog = 0;
31 static AtomPtr logFile = NULL;
32 static FILE *logF;
33 static int logFilePermissions = 0640;
34 int scrubLogs = 0;
36 #ifdef HAVE_SYSLOG
37 static AtomPtr logFacility = NULL;
38 static int facility;
39 #endif
41 #define STR(x) XSTR(x)
42 #define XSTR(x) #x
44 static void initSyslog(void);
46 #ifdef HAVE_SYSLOG
47 static char *syslogBuf;
48 static int syslogBufSize;
49 static int syslogBufLength;
51 static int translateFacility(AtomPtr facility);
52 static int translatePriority(int type);
53 static void accumulateSyslogV(int type, const char *f, va_list args);
54 static void accumulateSyslogN(int type, const char *s, int len);
55 #endif
57 void
58 preinitLog()
60 CONFIG_VARIABLE_SETTABLE(logLevel, CONFIG_HEX, configIntSetter,
61 "Logging level (max = " STR(LOGGING_MAX) ").");
62 CONFIG_VARIABLE(logFile, CONFIG_ATOM, "Log file (stderr if empty and logSyslog is unset, /var/log/polipo if empty and daemonise is true).");
63 CONFIG_VARIABLE(logFilePermissions, CONFIG_OCTAL,
64 "Access rights of the logFile.");
65 CONFIG_VARIABLE_SETTABLE(scrubLogs, CONFIG_BOOLEAN, configIntSetter,
66 "If true, don't include URLs in logs.");
68 #ifdef HAVE_SYSLOG
69 CONFIG_VARIABLE(logSyslog, CONFIG_BOOLEAN, "Log to syslog.");
70 CONFIG_VARIABLE(logFacility, CONFIG_ATOM, "Syslog facility to use.");
71 logFacility = internAtom("user");
72 #endif
74 logF = stderr;
77 int
78 loggingToStderr(void) {
79 return(logF == stderr);
82 static FILE *
83 openLogFile(void)
85 int fd;
86 FILE *f;
88 fd = open(logFile->string, O_WRONLY | O_CREAT | O_APPEND,
89 logFilePermissions);
90 if(fd < 0)
91 return NULL;
93 f = fdopen(fd, "a");
94 if(f == NULL) {
95 int saved_errno = errno;
96 close(fd);
97 errno = saved_errno;
98 return NULL;
101 setvbuf(f, NULL, _IOLBF, 0);
102 return f;
105 void
106 initLog(void)
108 if(daemonise && logFile == NULL && !logSyslog)
109 logFile = internAtom("/var/log/polipo");
111 if(logFile != NULL && logFile->length > 0) {
112 FILE *f;
113 f = openLogFile();
114 if(f == NULL) {
115 do_log_error(L_ERROR, errno, "Couldn't open log file %s",
116 logFile->string);
117 exit(1);
119 logF = f;
122 if(logSyslog) {
123 initSyslog();
125 if(logFile == NULL) {
126 logF = NULL;
131 #ifdef HAVE_SYSLOG
132 static void
133 initSyslog()
135 if(logSyslog) {
136 facility = translateFacility(logFacility);
137 closelog();
138 openlog("polipo", LOG_PID, facility);
140 if(!syslogBuf) {
141 syslogBuf = strdup("");
142 syslogBufSize = 1;
147 /* Map a user-provided name to a syslog facility.
149 This is rendered quite ugly because POSIX hardly defines any, but we
150 should allow any the local system knows about. */
152 static int
153 translateFacility(AtomPtr facility)
155 typedef struct
157 const char *name;
158 int facility;
159 } FacilitiesRec;
161 /* List of all known valid syslog facilities.
163 This list is terminated by a NULL facility name. */
165 FacilitiesRec facilities[] = {
166 /* These are all the facilities found in glibc 2.5. */
167 #ifdef LOG_AUTH
168 { "auth", LOG_AUTH },
169 #endif
170 #ifdef LOG_AUTHPRIV
171 { "authpriv", LOG_AUTHPRIV },
172 #endif
173 #ifdef LOG_CRON
174 { "cron", LOG_CRON },
175 #endif
176 #ifdef LOG_DAEMON
177 { "daemon", LOG_DAEMON },
178 #endif
179 #ifdef LOG_FTP
180 { "ftp", LOG_FTP },
181 #endif
182 #ifdef LOG_KERN
183 { "kern", LOG_KERN },
184 #endif
185 #ifdef LOG_LPR
186 { "lpr", LOG_LPR },
187 #endif
188 #ifdef LOG_MAIL
189 { "mail", LOG_MAIL },
190 #endif
191 #ifdef LOG_NEWS
192 { "news", LOG_NEWS },
193 #endif
194 #ifdef LOG_SYSLOG
195 { "syslog", LOG_SYSLOG },
196 #endif
197 #ifdef LOG_uucp
198 { "uucp", LOG_UUCP },
199 #endif
200 /* These are required by POSIX. */
201 { "user", LOG_USER },
202 { "local0", LOG_LOCAL0 },
203 { "local1", LOG_LOCAL1 },
204 { "local2", LOG_LOCAL2 },
205 { "local3", LOG_LOCAL3 },
206 { "local4", LOG_LOCAL4 },
207 { "local5", LOG_LOCAL5 },
208 { "local6", LOG_LOCAL6 },
209 { "local7", LOG_LOCAL7 },
210 { NULL, 0 }};
212 FacilitiesRec *current;
214 /* It would be more fitting to return LOG_DAEMON, but POSIX does not
215 guarantee the existence of that facility. */
217 if(!facility) {
218 return LOG_USER;
221 current = facilities;
222 while(current->name) {
223 if(!strcmp(current->name, atomString(facility))) {
224 return current->facility;
226 current++;
229 /* This will go to stderr because syslog is not yet initialized. */
230 do_log(L_ERROR, "Specified logFacility %s nonexistent on this system.",
231 atomString(facility));
233 return LOG_USER;
236 /* Translate a Polipo error type into a syslog priority. */
238 static int
239 translatePriority(int type)
241 typedef struct
243 int type;
244 int priority;
245 } PrioritiesRec;
247 /* The list is terminated with a type of zero. */
249 PrioritiesRec priorities[] = {{ L_ERROR, LOG_ERR },
250 { L_WARN, LOG_WARNING },
251 { L_INFO, LOG_NOTICE },
252 { L_FORBIDDEN, LOG_DEBUG },
253 { L_UNCACHEABLE, LOG_DEBUG },
254 { L_SUPERSEDED, LOG_DEBUG },
255 { L_VARY, LOG_DEBUG },
256 { 0, 0 }};
257 PrioritiesRec *current;
259 current = priorities;
260 while(current->type) {
261 if(current->type == type) {
262 return current->priority;
264 current++;
267 return LOG_DEBUG;
270 static int
271 expandSyslog(int len)
273 int newsize;
274 char *newbuf;
276 if(len < 0)
277 newsize = syslogBufSize * 2;
278 else
279 newsize = syslogBufLength + len + 1;
281 newbuf = realloc(syslogBuf, newsize);
282 if(!newbuf)
283 return -1;
285 syslogBuf = newbuf;
286 syslogBufSize = newsize;
287 return 1;
290 static void
291 maybeFlushSyslog(int type)
293 char *linefeed;
294 while(1) {
295 linefeed = memchr(syslogBuf, '\n', syslogBufLength);
296 if(linefeed == NULL)
297 break;
298 *linefeed = '\0';
299 syslog(translatePriority(type), "%s", syslogBuf);
300 linefeed++;
301 syslogBufLength -= (linefeed - syslogBuf);
302 if(syslogBufLength > 0)
303 memmove(syslogBuf, linefeed, syslogBufLength);
307 #ifndef va_copy
308 #define va_copy(a, b) do { a = b; } while(0)
309 #endif
311 static void
312 accumulateSyslogV(int type, const char *f, va_list args)
314 int rc;
315 va_list args_copy;
317 again:
318 va_copy(args_copy, args);
319 rc = vsnprintf(syslogBuf + syslogBufLength,
320 syslogBufSize - syslogBufLength,
321 f, args_copy);
322 va_end(args_copy);
324 if(rc < 0 || rc >= syslogBufSize - syslogBufLength) {
325 rc = expandSyslog(rc);
326 if(rc < 0)
327 return;
328 goto again;
331 syslogBufLength += rc;
333 maybeFlushSyslog(type);
336 static void
337 accumulateSyslogN(int type, const char *s, int len)
339 while(syslogBufSize - syslogBufLength <= len)
340 expandSyslog(len);
342 memcpy(syslogBuf + syslogBufLength, s, len);
343 syslogBufLength += len;
344 syslogBuf[syslogBufLength] = '\0';
346 maybeFlushSyslog(type);
349 #else
350 static void
351 initSyslog()
353 return;
355 #endif
357 /* Flush any messages waiting to be logged. */
358 void flushLog()
360 if(logF)
361 fflush(logF);
363 #ifdef HAVE_SYSLOG
364 /* There shouldn't really be anything here, but let's be paranoid.
365 We can't pick a good value for `type', so just invent one. */
366 if(logSyslog && syslogBuf[0] != '\0') {
367 accumulateSyslogN(L_INFO, "\n", 1);
370 assert(syslogBufLength == 0);
371 #endif
374 void
375 reopenLog()
377 if(logFile && logFile->length > 0) {
378 FILE *f;
379 f = openLogFile();
380 if(f == NULL) {
381 do_log_error(L_ERROR, errno, "Couldn't reopen log file %s",
382 logFile->string);
383 exit(1);
385 fclose(logF);
386 logF = f;
389 if(logSyslog)
390 initSyslog();
393 void
394 really_do_log(int type, const char *f, ...)
396 va_list args;
398 va_start(args, f);
399 if(type & LOGGING_MAX & logLevel)
400 really_do_log_v(type, f, args);
401 va_end(args);
404 void
405 really_do_log_v(int type, const char *f, va_list args)
407 if(type & LOGGING_MAX & logLevel) {
408 if(logF)
410 va_list args_copy;
411 va_copy(args_copy, args);
412 vfprintf(logF, f, args_copy);
413 va_end(args_copy);
415 #ifdef HAVE_SYSLOG
416 if(logSyslog)
417 accumulateSyslogV(type, f, args);
418 #endif
422 void
423 really_do_log_error(int type, int e, const char *f, ...)
425 va_list args;
426 va_start(args, f);
427 if(type & LOGGING_MAX & logLevel)
428 really_do_log_error_v(type, e, f, args);
429 va_end(args);
432 void
433 really_do_log_error_v(int type, int e, const char *f, va_list args)
435 if((type & LOGGING_MAX & logLevel) != 0) {
436 char *es = pstrerror(e);
437 if(es == NULL)
438 es = "Unknown error";
440 if(logF) {
441 va_list args_copy;
442 va_copy(args_copy, args);
443 vfprintf(logF, f, args_copy);
444 fprintf(logF, ": %s\n", es);
445 va_end(args_copy);
447 #ifdef HAVE_SYSLOG
448 if(logSyslog) {
449 char msg[256];
450 int n = 0;
452 n = snnvprintf(msg, n, 256, f, args);
453 n = snnprintf(msg, n, 256, ": ");
454 n = snnprint_n(msg, n, 256, es, strlen (es));
455 n = snnprintf(msg, n, 256, "\n");
456 /* Overflow? Vanishingly unlikely; truncate at 255. */
457 if(n < 0 || n > 256) {
458 n = 256;
459 msg[255] = '\0';
461 else
462 msg[n] = '\0';
464 accumulateSyslogN(type, msg, n);
466 #endif
470 void
471 really_do_log_n(int type, const char *s, int n)
473 if((type & LOGGING_MAX & logLevel) != 0) {
474 if(logF) {
475 fwrite(s, n, 1, logF);
477 #ifdef HAVE_SYSLOG
478 if(logSyslog)
479 accumulateSyslogN(type, s, n);
480 #endif
484 const char *
485 scrub(const char *message)
487 if(scrubLogs)
488 return "(scrubbed)";
489 else
490 return message;