Reformat CHANGES.
[polipo.git] / log.c
blobbf159582c4884bc58cd570a954c928ce1bd6fbb3
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 #ifdef HAVE_SYSLOG
34 static AtomPtr logFacility = NULL;
35 static int facility;
36 #endif
38 #define STR(x) XSTR(x)
39 #define XSTR(x) #x
41 static void initSyslog(void);
43 #ifdef HAVE_SYSLOG
44 static char *syslogBuf;
45 static int syslogBufSize;
46 static int syslogBufLength;
48 static int translateFacility(AtomPtr facility);
49 static int translatePriority(int type);
50 static void accumulateSyslogV(int type, const char *f, va_list args);
51 static void accumulateSyslogN(int type, const char *s, int len);
52 #endif
54 void
55 preinitLog()
57 CONFIG_VARIABLE_SETTABLE(logLevel, CONFIG_HEX, configIntSetter,
58 "Logging level (max = " STR(LOGGING_MAX) ").");
59 CONFIG_VARIABLE(logFile, CONFIG_ATOM, "Log file (stderr if empty and logSyslog is unset).");
61 #ifdef HAVE_SYSLOG
62 CONFIG_VARIABLE(logSyslog, CONFIG_BOOLEAN, "Log to syslog.");
63 CONFIG_VARIABLE(logFacility, CONFIG_ATOM, "Syslog facility to use.");
64 logFacility = internAtom("user");
65 #endif
67 logF = stderr;
70 int
71 loggingToStderr(void) {
72 return(logF == stderr);
75 void
76 initLog(void)
78 if(daemonise && logFile == NULL && !logSyslog)
79 logFile = internAtom("/var/log/polipo");
81 if(logFile != NULL && logFile->length > 0) {
82 FILE *f;
83 f = fopen(logFile->string, "a");
84 if(f == NULL) {
85 do_log_error(L_ERROR, errno, "Couldn't open log file %s",
86 logFile->string);
87 exit(1);
89 setvbuf(f, NULL, _IOLBF, 0);
90 logF = f;
93 if(logSyslog) {
94 initSyslog();
96 if(logFile == NULL) {
97 logF = NULL;
102 #ifdef HAVE_SYSLOG
103 static void
104 initSyslog()
106 if(logSyslog) {
107 facility = translateFacility(logFacility);
108 closelog();
109 openlog("polipo", LOG_PID, facility);
111 if(!syslogBuf) {
112 syslogBuf = strdup("");
113 syslogBufSize = 1;
118 /* Map a user-provided name to a syslog facility.
120 This is rendered quite ugly because POSIX hardly defines any, but we
121 should allow any the local system knows about. */
123 static int
124 translateFacility(AtomPtr facility)
126 typedef struct
128 const char *name;
129 int facility;
130 } FacilitiesRec;
132 /* List of all known valid syslog facilities.
134 This list is terminated by a NULL facility name. */
136 FacilitiesRec facilities[] = {
137 /* These are all the facilities found in glibc 2.5. */
138 #ifdef LOG_AUTH
139 { "auth", LOG_AUTH },
140 #endif
141 #ifdef LOG_AUTHPRIV
142 { "authpriv", LOG_AUTHPRIV },
143 #endif
144 #ifdef LOG_CRON
145 { "cron", LOG_CRON },
146 #endif
147 #ifdef LOG_DAEMON
148 { "daemon", LOG_DAEMON },
149 #endif
150 #ifdef LOG_FTP
151 { "ftp", LOG_FTP },
152 #endif
153 #ifdef LOG_KERN
154 { "kern", LOG_KERN },
155 #endif
156 #ifdef LOG_LPR
157 { "lpr", LOG_LPR },
158 #endif
159 #ifdef LOG_MAIL
160 { "mail", LOG_MAIL },
161 #endif
162 #ifdef LOG_NEWS
163 { "news", LOG_NEWS },
164 #endif
165 #ifdef LOG_SYSLOG
166 { "syslog", LOG_SYSLOG },
167 #endif
168 #ifdef LOG_uucp
169 { "uucp", LOG_UUCP },
170 #endif
171 /* These are required by POSIX. */
172 { "user", LOG_USER },
173 { "local0", LOG_LOCAL0 },
174 { "local1", LOG_LOCAL1 },
175 { "local2", LOG_LOCAL2 },
176 { "local3", LOG_LOCAL3 },
177 { "local4", LOG_LOCAL4 },
178 { "local5", LOG_LOCAL5 },
179 { "local6", LOG_LOCAL6 },
180 { "local7", LOG_LOCAL7 },
181 { NULL, 0 }};
183 FacilitiesRec *current;
185 /* It would be more fitting to return LOG_DAEMON, but POSIX does not
186 guarantee the existence of that facility. */
188 if(!facility) {
189 return LOG_USER;
192 current = facilities;
193 while(current->name) {
194 if(!strcmp(current->name, atomString(facility))) {
195 return current->facility;
197 current++;
200 /* This will go to stderr because syslog is not yet initialized. */
201 do_log(L_ERROR, "Specified logFacility %s nonexistent on this system.",
202 atomString(facility));
204 return LOG_USER;
207 /* Translate a Polipo error type into a syslog priority. */
209 static int
210 translatePriority(int type)
212 typedef struct
214 int type;
215 int priority;
216 } PrioritiesRec;
218 /* The list is terminated with a type of zero. */
220 PrioritiesRec priorities[] = {{ L_ERROR, LOG_ERR },
221 { L_WARN, LOG_WARNING },
222 { L_INFO, LOG_NOTICE },
223 { L_FORBIDDEN, LOG_DEBUG },
224 { L_UNCACHEABLE, LOG_DEBUG },
225 { L_SUPERSEDED, LOG_DEBUG },
226 { L_VARY, LOG_DEBUG },
227 { 0, 0 }};
228 PrioritiesRec *current;
230 current = priorities;
231 while(current->type) {
232 if(current->type == type) {
233 return current->priority;
235 current++;
238 return LOG_DEBUG;
241 static int
242 expandSyslog(int len)
244 int newsize;
245 char *newbuf;
247 if(len < 0)
248 newsize = syslogBufSize * 2;
249 else
250 newsize = syslogBufLength + len + 1;
252 newbuf = realloc(syslogBuf, newsize);
253 if(!newbuf)
254 return -1;
256 syslogBuf = newbuf;
257 syslogBufSize = newsize;
258 return 1;
261 static void
262 maybeFlushSyslog(int type)
264 char *linefeed;
265 while(1) {
266 linefeed = memchr(syslogBuf, '\n', syslogBufLength);
267 if(linefeed == NULL)
268 break;
269 *linefeed = '\0';
270 syslog(translatePriority(type), "%s", syslogBuf);
271 linefeed++;
272 syslogBufLength -= (linefeed - syslogBuf);
273 if(syslogBufLength > 0)
274 memmove(syslogBuf, linefeed, syslogBufLength);
278 static void
279 accumulateSyslogV(int type, const char *f, va_list args)
281 int rc;
283 again:
284 rc = vsnprintf(syslogBuf + syslogBufLength,
285 syslogBufSize - syslogBufLength,
286 f, args);
288 if(rc < 0 || rc >= syslogBufSize - syslogBufLength) {
289 rc = expandSyslog(rc);
290 if(rc < 0)
291 return;
292 goto again;
295 syslogBufLength += rc;
297 maybeFlushSyslog(type);
300 static void
301 accumulateSyslogN(int type, const char *s, int len)
303 while(syslogBufSize - syslogBufLength <= len)
304 expandSyslog(len);
306 memcpy(syslogBuf + syslogBufLength, s, len);
307 syslogBufLength += len;
308 syslogBuf[syslogBufLength] = '\0';
310 maybeFlushSyslog(type);
313 #else
314 static void
315 initSyslog()
317 return;
319 #endif
321 /* Flush any messages waiting to be logged. */
322 void flushLog()
324 if(logF)
325 fflush(logF);
327 #ifdef HAVE_SYSLOG
328 /* There shouldn't really be anything here, but let's be paranoid.
329 We can't pick a good value for `type', so just invent one. */
330 if(logSyslog && syslogBuf[0] != '\0') {
331 accumulateSyslogN(L_INFO, "\n", 1);
334 assert(syslogBufLength == 0);
335 #endif
338 void
339 reopenLog()
341 if(logFile) {
342 FILE *f;
343 f = fopen(logFile->string, "a");
344 if(f == NULL) {
345 do_log_error(L_ERROR, errno, "Couldn't reopen log file %s",
346 logFile->string);
347 exit(1);
349 setvbuf(f, NULL, _IOLBF, 0);
350 fclose(logF);
351 logF = f;
354 if(logSyslog) {
355 initSyslog();
359 void
360 really_do_log(int type, const char *f, ...)
362 va_list args;
364 va_start(args, f);
365 if(type & LOGGING_MAX & logLevel)
366 really_do_log_v(type, f, args);
367 va_end(args);
370 void
371 really_do_log_v(int type, const char *f, va_list args)
373 if(type & LOGGING_MAX & logLevel) {
374 if(logF)
375 vfprintf(logF, f, args);
376 #ifdef HAVE_SYSLOG
377 if(logSyslog)
378 accumulateSyslogV(type, f, args);
379 #endif
383 void
384 really_do_log_error(int type, int e, const char *f, ...)
386 va_list args;
387 va_start(args, f);
388 if(type & LOGGING_MAX & logLevel)
389 really_do_log_error_v(type, e, f, args);
390 va_end(args);
393 void
394 really_do_log_error_v(int type, int e, const char *f, va_list args)
396 if((type & LOGGING_MAX & logLevel) != 0) {
397 char *es = pstrerror(e);
398 if(es == NULL)
399 es = "Unknown error";
401 if(logF) {
402 vfprintf(logF, f, args);
403 fprintf(logF, ": %s\n", es);
405 #ifdef HAVE_SYSLOG
406 if(logSyslog) {
407 char msg[256];
408 size_t n = 0;
410 n = snnvprintf(msg, n, 256, f, args);
411 n = snnprintf(msg, n, 256, ": ");
412 n = snnprint_n(msg, n, 256, es, strlen (es));
413 n = snnprintf(msg, n, 256, "\n");
414 /* Overflow? Vanishingly unlikely; truncate at 255. */
415 if(n < 0 || n > 256) {
416 n = 256;
417 msg[255] = '\0';
419 else
420 msg[n] = '\0';
422 accumulateSyslogN(type, msg, n);
424 #endif
428 void
429 really_do_log_n(int type, const char *s, int n)
431 if((type & LOGGING_MAX & logLevel) != 0) {
432 if(logF) {
433 fwrite(s, n, 1, logF);
435 #ifdef HAVE_SYSLOG
436 if(logSyslog)
437 accumulateSyslogN(type, s, n);
438 #endif