1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
3
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:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
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.
21 */
22
23 #include "polipo.h"
24
25 #ifdef HAVE_SYSLOG
26 #include <syslog.h>
27 #endif
28
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;
35
36 #ifdef HAVE_SYSLOG
37 static AtomPtr logFacility = NULL;
38 static int facility;
39 #endif
40
41 #define STR(x) XSTR(x)
42 #define XSTR(x) #x
43
44 static void initSyslog(void);
45
46 #ifdef HAVE_SYSLOG
47 static char *syslogBuf;
48 static int syslogBufSize;
49 static int syslogBufLength;
50
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
56
57 void
preinitLog()58 preinitLog()
59 {
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.");
67
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
73
74 logF = stderr;
75 }
76
77 int
loggingToStderr(void)78 loggingToStderr(void) {
79 return(logF == stderr);
80 }
81
82 static FILE *
openLogFile(void)83 openLogFile(void)
84 {
85 int fd;
86 FILE *f;
87
88 fd = open(logFile->string, O_WRONLY | O_CREAT | O_APPEND,
89 logFilePermissions);
90 if(fd < 0)
91 return NULL;
92
93 f = fdopen(fd, "a");
94 if(f == NULL) {
95 int saved_errno = errno;
96 close(fd);
97 errno = saved_errno;
98 return NULL;
99 }
100
101 setvbuf(f, NULL, _IOLBF, 0);
102 return f;
103 }
104
105 void
initLog(void)106 initLog(void)
107 {
108 if(daemonise && logFile == NULL && !logSyslog)
109 logFile = internAtom("/var/log/polipo");
110
111 if(logFile != NULL && logFile->length > 0) {
112 FILE *f;
113 logFile = expandTilde(logFile);
114 f = openLogFile();
115 if(f == NULL) {
116 do_log_error(L_ERROR, errno, "Couldn't open log file %s",
117 logFile->string);
118 exit(1);
119 }
120 logF = f;
121 }
122
123 if(logSyslog) {
124 initSyslog();
125
126 if(logFile == NULL) {
127 logF = NULL;
128 }
129 }
130 }
131
132 #ifdef HAVE_SYSLOG
133 static void
initSyslog()134 initSyslog()
135 {
136 if(logSyslog) {
137 facility = translateFacility(logFacility);
138 closelog();
139 openlog("polipo", LOG_PID, facility);
140
141 if(!syslogBuf) {
142 syslogBuf = strdup("");
143 syslogBufSize = 1;
144 }
145 }
146 }
147
148 /* Map a user-provided name to a syslog facility.
149
150 This is rendered quite ugly because POSIX hardly defines any, but we
151 should allow any the local system knows about. */
152
153 static int
translateFacility(AtomPtr facility)154 translateFacility(AtomPtr facility)
155 {
156 typedef struct
157 {
158 const char *name;
159 int facility;
160 } FacilitiesRec;
161
162 /* List of all known valid syslog facilities.
163
164 This list is terminated by a NULL facility name. */
165
166 FacilitiesRec facilities[] = {
167 /* These are all the facilities found in glibc 2.5. */
168 #ifdef LOG_AUTH
169 { "auth", LOG_AUTH },
170 #endif
171 #ifdef LOG_AUTHPRIV
172 { "authpriv", LOG_AUTHPRIV },
173 #endif
174 #ifdef LOG_CRON
175 { "cron", LOG_CRON },
176 #endif
177 #ifdef LOG_DAEMON
178 { "daemon", LOG_DAEMON },
179 #endif
180 #ifdef LOG_FTP
181 { "ftp", LOG_FTP },
182 #endif
183 #ifdef LOG_KERN
184 { "kern", LOG_KERN },
185 #endif
186 #ifdef LOG_LPR
187 { "lpr", LOG_LPR },
188 #endif
189 #ifdef LOG_MAIL
190 { "mail", LOG_MAIL },
191 #endif
192 #ifdef LOG_NEWS
193 { "news", LOG_NEWS },
194 #endif
195 #ifdef LOG_SYSLOG
196 { "syslog", LOG_SYSLOG },
197 #endif
198 #ifdef LOG_uucp
199 { "uucp", LOG_UUCP },
200 #endif
201 /* These are required by POSIX. */
202 { "user", LOG_USER },
203 { "local0", LOG_LOCAL0 },
204 { "local1", LOG_LOCAL1 },
205 { "local2", LOG_LOCAL2 },
206 { "local3", LOG_LOCAL3 },
207 { "local4", LOG_LOCAL4 },
208 { "local5", LOG_LOCAL5 },
209 { "local6", LOG_LOCAL6 },
210 { "local7", LOG_LOCAL7 },
211 { NULL, 0 }};
212
213 FacilitiesRec *current;
214
215 /* It would be more fitting to return LOG_DAEMON, but POSIX does not
216 guarantee the existence of that facility. */
217
218 if(!facility) {
219 return LOG_USER;
220 }
221
222 current = facilities;
223 while(current->name) {
224 if(!strcmp(current->name, atomString(facility))) {
225 return current->facility;
226 }
227 current++;
228 }
229
230 /* This will go to stderr because syslog is not yet initialized. */
231 do_log(L_ERROR, "Specified logFacility %s nonexistent on this system.",
232 atomString(facility));
233
234 return LOG_USER;
235 }
236
237 /* Translate a Polipo error type into a syslog priority. */
238
239 static int
translatePriority(int type)240 translatePriority(int type)
241 {
242 typedef struct
243 {
244 int type;
245 int priority;
246 } PrioritiesRec;
247
248 /* The list is terminated with a type of zero. */
249
250 PrioritiesRec priorities[] = {{ L_ERROR, LOG_ERR },
251 { L_WARN, LOG_WARNING },
252 { L_INFO, LOG_NOTICE },
253 { L_FORBIDDEN, LOG_DEBUG },
254 { L_UNCACHEABLE, LOG_DEBUG },
255 { L_SUPERSEDED, LOG_DEBUG },
256 { L_VARY, LOG_DEBUG },
257 { L_TUNNEL, LOG_NOTICE },
258 { 0, 0 }};
259 PrioritiesRec *current;
260
261 current = priorities;
262 while(current->type) {
263 if(current->type == type) {
264 return current->priority;
265 }
266 current++;
267 }
268
269 return LOG_DEBUG;
270 }
271
272 static int
expandSyslog(int len)273 expandSyslog(int len)
274 {
275 int newsize;
276 char *newbuf;
277
278 if(len < 0)
279 newsize = syslogBufSize * 2;
280 else
281 newsize = syslogBufLength + len + 1;
282
283 newbuf = realloc(syslogBuf, newsize);
284 if(!newbuf)
285 return -1;
286
287 syslogBuf = newbuf;
288 syslogBufSize = newsize;
289 return 1;
290 }
291
292 static void
maybeFlushSyslog(int type)293 maybeFlushSyslog(int type)
294 {
295 char *linefeed;
296 while(1) {
297 linefeed = memchr(syslogBuf, '\n', syslogBufLength);
298 if(linefeed == NULL)
299 break;
300 *linefeed = '\0';
301 syslog(translatePriority(type), "%s", syslogBuf);
302 linefeed++;
303 syslogBufLength -= (linefeed - syslogBuf);
304 if(syslogBufLength > 0)
305 memmove(syslogBuf, linefeed, syslogBufLength);
306 }
307 }
308
309 static void
accumulateSyslogV(int type,const char * f,va_list args)310 accumulateSyslogV(int type, const char *f, va_list args)
311 {
312 int rc;
313 va_list args_copy;
314
315 again:
316 va_copy(args_copy, args);
317 rc = vsnprintf(syslogBuf + syslogBufLength,
318 syslogBufSize - syslogBufLength,
319 f, args_copy);
320 va_end(args_copy);
321
322 if(rc < 0 || rc >= syslogBufSize - syslogBufLength) {
323 rc = expandSyslog(rc);
324 if(rc < 0)
325 return;
326 goto again;
327 }
328
329 syslogBufLength += rc;
330
331 maybeFlushSyslog(type);
332 }
333
334 static void
accumulateSyslogN(int type,const char * s,int len)335 accumulateSyslogN(int type, const char *s, int len)
336 {
337 while(syslogBufSize - syslogBufLength <= len)
338 expandSyslog(len);
339
340 memcpy(syslogBuf + syslogBufLength, s, len);
341 syslogBufLength += len;
342 syslogBuf[syslogBufLength] = '\0';
343
344 maybeFlushSyslog(type);
345 }
346
347 #else
348 static void
initSyslog()349 initSyslog()
350 {
351 return;
352 }
353 #endif
354
355 /* Flush any messages waiting to be logged. */
flushLog()356 void flushLog()
357 {
358 if(logF)
359 fflush(logF);
360
361 #ifdef HAVE_SYSLOG
362 /* There shouldn't really be anything here, but let's be paranoid.
363 We can't pick a good value for `type', so just invent one. */
364 if(logSyslog && syslogBuf[0] != '\0') {
365 accumulateSyslogN(L_INFO, "\n", 1);
366 }
367
368 assert(syslogBufLength == 0);
369 #endif
370 }
371
372 void
reopenLog()373 reopenLog()
374 {
375 if(logFile && logFile->length > 0) {
376 FILE *f;
377 f = openLogFile();
378 if(f == NULL) {
379 do_log_error(L_ERROR, errno, "Couldn't reopen log file %s",
380 logFile->string);
381 exit(1);
382 }
383 fclose(logF);
384 logF = f;
385 }
386
387 if(logSyslog)
388 initSyslog();
389 }
390
391 void
really_do_log(int type,const char * f,...)392 really_do_log(int type, const char *f, ...)
393 {
394 va_list args;
395
396 va_start(args, f);
397 if(type & LOGGING_MAX & logLevel)
398 really_do_log_v(type, f, args);
399 va_end(args);
400 }
401
402 void
really_do_log_v(int type,const char * f,va_list args)403 really_do_log_v(int type, const char *f, va_list args)
404 {
405 va_list args_copy;
406
407 if(type & LOGGING_MAX & logLevel) {
408 if(logF)
409 {
410 va_copy(args_copy, args);
411 vfprintf(logF, f, args_copy);
412 va_end(args_copy);
413 }
414 #ifdef HAVE_SYSLOG
415 if(logSyslog) {
416 va_copy(args_copy, args);
417 accumulateSyslogV(type, f, args_copy);
418 va_end(args_copy);
419 }
420 #endif
421 }
422 }
423
424 void
really_do_log_error(int type,int e,const char * f,...)425 really_do_log_error(int type, int e, const char *f, ...)
426 {
427 va_list args;
428 va_start(args, f);
429 if(type & LOGGING_MAX & logLevel)
430 really_do_log_error_v(type, e, f, args);
431 va_end(args);
432 }
433
434 void
really_do_log_error_v(int type,int e,const char * f,va_list args)435 really_do_log_error_v(int type, int e, const char *f, va_list args)
436 {
437 va_list args_copy;
438
439 if((type & LOGGING_MAX & logLevel) != 0) {
440 char *es = pstrerror(e);
441 if(es == NULL)
442 es = "Unknown error";
443
444 if(logF) {
445 va_copy(args_copy, args);
446 vfprintf(logF, f, args_copy);
447 fprintf(logF, ": %s\n", es);
448 va_end(args_copy);
449 }
450 #ifdef HAVE_SYSLOG
451 if(logSyslog) {
452 char msg[256];
453 int n = 0;
454
455 va_copy(args_copy, args);
456 n = snnvprintf(msg, n, 256, f, args_copy);
457 va_end(args_copy);
458 n = snnprintf(msg, n, 256, ": ");
459 n = snnprint_n(msg, n, 256, es, strlen (es));
460 n = snnprintf(msg, n, 256, "\n");
461 /* Overflow? Vanishingly unlikely; truncate at 255. */
462 if(n < 0 || n > 256) {
463 n = 256;
464 msg[255] = '\0';
465 }
466 else
467 msg[n] = '\0';
468
469 accumulateSyslogN(type, msg, n);
470 }
471 #endif
472 }
473 }
474
475 void
really_do_log_n(int type,const char * s,int n)476 really_do_log_n(int type, const char *s, int n)
477 {
478 if((type & LOGGING_MAX & logLevel) != 0) {
479 if(logF) {
480 fwrite(s, n, 1, logF);
481 }
482 #ifdef HAVE_SYSLOG
483 if(logSyslog)
484 accumulateSyslogN(type, s, n);
485 #endif
486 }
487 }
488
489 const char *
scrub(const char * message)490 scrub(const char *message)
491 {
492 if(scrubLogs)
493 return "(scrubbed)";
494 else
495 return message;
496 }
497