Doc update.
[polipo.git] / log.c
blobcd3f11e92b11b2b107eb81697cc30553681a02ea
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 static void
308 accumulateSyslogV(int type, const char *f, va_list args)
310 int rc;
312 again:
313 rc = vsnprintf(syslogBuf + syslogBufLength,
314 syslogBufSize - syslogBufLength,
315 f, args);
317 if(rc < 0 || rc >= syslogBufSize - syslogBufLength) {
318 rc = expandSyslog(rc);
319 if(rc < 0)
320 return;
321 goto again;
324 syslogBufLength += rc;
326 maybeFlushSyslog(type);
329 static void
330 accumulateSyslogN(int type, const char *s, int len)
332 while(syslogBufSize - syslogBufLength <= len)
333 expandSyslog(len);
335 memcpy(syslogBuf + syslogBufLength, s, len);
336 syslogBufLength += len;
337 syslogBuf[syslogBufLength] = '\0';
339 maybeFlushSyslog(type);
342 #else
343 static void
344 initSyslog()
346 return;
348 #endif
350 /* Flush any messages waiting to be logged. */
351 void flushLog()
353 if(logF)
354 fflush(logF);
356 #ifdef HAVE_SYSLOG
357 /* There shouldn't really be anything here, but let's be paranoid.
358 We can't pick a good value for `type', so just invent one. */
359 if(logSyslog && syslogBuf[0] != '\0') {
360 accumulateSyslogN(L_INFO, "\n", 1);
363 assert(syslogBufLength == 0);
364 #endif
367 void
368 reopenLog()
370 if(logFile) {
371 FILE *f;
372 f = openLogFile();
373 if(f == NULL) {
374 do_log_error(L_ERROR, errno, "Couldn't reopen log file %s",
375 logFile->string);
376 exit(1);
378 fclose(logF);
379 logF = f;
382 if(logSyslog)
383 initSyslog();
386 void
387 really_do_log(int type, const char *f, ...)
389 va_list args;
391 va_start(args, f);
392 if(type & LOGGING_MAX & logLevel)
393 really_do_log_v(type, f, args);
394 va_end(args);
397 void
398 really_do_log_v(int type, const char *f, va_list args)
400 if(type & LOGGING_MAX & logLevel) {
401 if(logF)
402 vfprintf(logF, f, args);
403 #ifdef HAVE_SYSLOG
404 if(logSyslog)
405 accumulateSyslogV(type, f, args);
406 #endif
410 void
411 really_do_log_error(int type, int e, const char *f, ...)
413 va_list args;
414 va_start(args, f);
415 if(type & LOGGING_MAX & logLevel)
416 really_do_log_error_v(type, e, f, args);
417 va_end(args);
420 void
421 really_do_log_error_v(int type, int e, const char *f, va_list args)
423 if((type & LOGGING_MAX & logLevel) != 0) {
424 char *es = pstrerror(e);
425 if(es == NULL)
426 es = "Unknown error";
428 if(logF) {
429 vfprintf(logF, f, args);
430 fprintf(logF, ": %s\n", es);
432 #ifdef HAVE_SYSLOG
433 if(logSyslog) {
434 char msg[256];
435 int n = 0;
437 n = snnvprintf(msg, n, 256, f, args);
438 n = snnprintf(msg, n, 256, ": ");
439 n = snnprint_n(msg, n, 256, es, strlen (es));
440 n = snnprintf(msg, n, 256, "\n");
441 /* Overflow? Vanishingly unlikely; truncate at 255. */
442 if(n < 0 || n > 256) {
443 n = 256;
444 msg[255] = '\0';
446 else
447 msg[n] = '\0';
449 accumulateSyslogN(type, msg, n);
451 #endif
455 void
456 really_do_log_n(int type, const char *s, int n)
458 if((type & LOGGING_MAX & logLevel) != 0) {
459 if(logF) {
460 fwrite(s, n, 1, logF);
462 #ifdef HAVE_SYSLOG
463 if(logSyslog)
464 accumulateSyslogN(type, s, n);
465 #endif
469 const char *
470 scrub(const char *message)
472 if(scrubLogs)
473 return "(scrubbed)";
474 else
475 return message;