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