1 /*
2  * Copyright (c) 2009 NLnet Labs. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
27 /**
28  * Logging.
29  *
30  */
31 
32 #include "config.h"
33 #include "duration.h"
34 #include "file.h"
35 #include "log.h"
36 #include "util.h"
37 
38 #ifdef HAVE_SYSLOG_H
39 static int logging_to_syslog = 0;
40 #endif /* !HAVE_SYSLOG_H */
41 
42 #include <stdarg.h> /* va_start(), va_end()  */
43 #include <stdio.h> /* fflush, fprintf(), vsnprintf() */
44 #include <stdlib.h> /* exit() */
45 #include <string.h> /* strlen() */
46 #include <pthread.h>
47 
48 #define LOG_DEEEBUG 8 /* ods_log_deeebug */
49 
50 static FILE* logfile = NULL;
51 static int log_level = LOG_CRIT;
52 
53 #define CTIME_LENGTH 26
54 
55 /**
56  * Use _r() functions on platforms that have. They are thread safe versions of
57  * the normal syslog functions. Platforms without _r() usually have thread safe
58  * normal functions.
59  */
60 #if defined(HAVE_SYSLOG_R) && defined(HAVE_OPENLOG_R) && defined(HAVE_CLOSELOG_R)
61 struct syslog_data sdata = SYSLOG_DATA_INIT;
62 #else
63 #undef HAVE_SYSLOG_R
64 #undef HAVE_OPENLOG_R
65 #undef HAVE_CLOSELOG_R
66 #endif
67 
68 /* TODO:
69    - prepend ods_ in common library ?
70    - log_init should have program_name variable
71    - wrap special case logging onto generic one
72    - check if xml-specific logging functions are still neeeded (enforcer)
73    -
74 */
75 
76 static const char* log_str = "log";
77 static char* log_ident = NULL;
78 
79 /**
80  * Initialize logging.
81  */
82 void
ods_log_init(const char * programname,int use_syslog,const char * targetname,int verbosity)83 ods_log_init(const char *programname, int use_syslog, const char *targetname, int verbosity)
84 {
85 #ifdef HAVE_SYSLOG_H
86     int facility;
87     int error = 0;
88 #endif /* HAVE_SYSLOG_H */
89     if(logfile && logfile != stderr && logfile != stdout) {
90             ods_fclose(logfile);
91     }
92     if(log_ident) {
93         free(log_ident);
94         log_ident = NULL;
95     }
96     log_level = verbosity + 2;
97 
98 #ifdef HAVE_SYSLOG_H
99     if(logging_to_syslog) {
100 #ifdef HAVE_CLOSELOG_R
101     	closelog_r(&sdata);
102 #else
103         closelog();
104 #endif
105         logging_to_syslog = 0;
106     }
107     if(use_syslog) {
108        facility = ods_log_get_facility(targetname, &error);
109 #ifdef HAVE_OPENLOG_R
110        openlog_r(programname, LOG_NDELAY, facility, &sdata);
111 #else
112        openlog(programname, LOG_NDELAY, facility);
113 #endif
114        logging_to_syslog = 1;
115        if (error == 1) {
116         ods_log_warning("[%s] syslog facility %s not supported, logging to "
117                    "log_daemon", log_str, targetname);
118        }
119        ods_log_verbose("[%s] switching log to syslog verbosity %i (log level %i)",
120           log_str, verbosity, verbosity+2);
121        return;
122     }
123 #endif /* HAVE_SYSLOG_H */
124 
125     log_ident = strdup(programname);
126     if(targetname && targetname[0]) {
127         logfile = ods_fopen(targetname, NULL, "a");
128         if (logfile) {
129             ods_log_debug("[%s] new logfile %s", log_str, targetname);
130             return;
131         }
132         logfile = stderr;
133         ods_log_warning("[%s] cannot open %s for appending, logging to "
134             "stderr", log_str, targetname);
135     } else {
136         logfile = stderr;
137         targetname = "stderr";
138     }
139     ods_log_verbose("[%s] switching log to %s verbosity %i (log level %i)",
140           log_str, targetname, verbosity, verbosity+2);
141 
142 }
143 
144 int
ods_log_verbosity(void)145 ods_log_verbosity(void)
146 {
147 	return log_level-2;
148 }
149 
150 void
ods_log_setverbosity(int verbosity)151 ods_log_setverbosity(int verbosity)
152 {
153     log_level = verbosity + 2;
154 }
155 
156 /**
157  * Close logging.
158  *
159  */
160 void
ods_log_close(void)161 ods_log_close(void)
162 {
163     ods_log_debug("[%s] close log", log_str);
164     ods_log_init("", 0, NULL, 0);
165 }
166 
167 
168 /**
169  * Get facility by string.
170  * ods_log_get_user
171  * ods_log_get_facility
172  * return error, LOG_*** as a parameter
173  *
174  */
175 #ifdef HAVE_SYSLOG_H
176 int
ods_log_get_facility(const char * facility,int * error)177 ods_log_get_facility(const char* facility, int* error)
178 {
179     int length;
180 
181     if (!facility) {
182         return LOG_DAEMON;
183     }
184     length = strlen(facility);
185 
186     if (length == 4 && strncasecmp(facility, "KERN", 4) == 0)
187         return LOG_KERN;
188     else if (length == 4 && strncasecmp(facility, "USER", 4) == 0)
189         return LOG_USER;
190     else if (length == 4 && strncasecmp(facility, "MAIL", 4) == 0)
191         return LOG_MAIL;
192     else if (length == 6 && strncasecmp(facility, "DAEMON", 6) == 0)
193         return LOG_DAEMON;
194     else if (length == 4 && strncasecmp(facility, "AUTH", 4) == 0)
195         return LOG_AUTH;
196     else if (length == 3 && strncasecmp(facility, "LPR", 3) == 0)
197         return LOG_LPR;
198     else if (length == 4 && strncasecmp(facility, "NEWS", 4) == 0)
199         return LOG_NEWS;
200     else if (length == 4 && strncasecmp(facility, "UUCP", 4) == 0)
201         return LOG_UUCP;
202     else if (length == 4 && strncasecmp(facility, "CRON", 4) == 0)
203         return LOG_CRON;
204     else if (length == 6 && strncasecmp(facility, "LOCAL0", 6) == 0)
205         return LOG_LOCAL0;
206     else if (length == 6 && strncasecmp(facility, "LOCAL1", 6) == 0)
207         return LOG_LOCAL1;
208     else if (length == 6 && strncasecmp(facility, "LOCAL2", 6) == 0)
209         return LOG_LOCAL2;
210     else if (length == 6 && strncasecmp(facility, "LOCAL3", 6) == 0)
211         return LOG_LOCAL3;
212     else if (length == 6 && strncasecmp(facility, "LOCAL4", 6) == 0)
213         return LOG_LOCAL4;
214     else if (length == 6 && strncasecmp(facility, "LOCAL5", 6) == 0)
215         return LOG_LOCAL5;
216     else if (length == 6 && strncasecmp(facility, "LOCAL6", 6) == 0)
217         return LOG_LOCAL6;
218     else if (length == 6 && strncasecmp(facility, "LOCAL7", 6) == 0)
219         return LOG_LOCAL7;
220     *error = 1;
221     return LOG_DAEMON;
222 
223 }
224 #endif /* HAVE_SYSLOG_H */
225 
226 /**
227  * Get the log level.
228  *
229  */
230 int
ods_log_get_level()231 ods_log_get_level()
232 {
233     return log_level;
234 }
235 
236 /**
237  * Log message wrapper.
238  *
239  */
240 static void
ods_log_vmsg(int priority,const char * t,const char * s,va_list args)241 ods_log_vmsg(int priority, const char* t, const char* s, va_list args)
242 {
243     char message[ODS_SE_MAXLINE];
244     static char nowstr[CTIME_LENGTH];
245     time_t now = time_now();
246 
247     vsnprintf(message, sizeof(message), s, args);
248 
249 #ifdef HAVE_SYSLOG_H
250     if (logging_to_syslog) {
251 #ifdef HAVE_SYSLOG_R
252         syslog_r(priority, &sdata, "%s", message);
253 #else
254         syslog(priority, "%s", message);
255 #endif
256         return;
257     }
258 #endif /* HAVE_SYSLOG_H */
259 
260     if (!logfile) {
261         fprintf(stdout, "%s\n", message);
262         return;
263     }
264 
265     (void) ctime_r(&now, nowstr);
266     nowstr[CTIME_LENGTH-2] = '\0'; /* remove trailing linefeed */
267 
268     fprintf(logfile, "[%s] %s[%i] %s: %s\n", nowstr,
269         log_ident, priority, t, message);
270     fflush(logfile);
271 }
272 
273 
274 /**
275  * Heavy debug logging.
276  *
277  */
278 void
ods_log_deeebug(const char * format,...)279 ods_log_deeebug(const char *format, ...)
280 {
281     va_list args;
282     va_start(args, format);
283     if (log_level >= LOG_DEEEBUG) {
284         ods_log_vmsg(LOG_DEBUG, "debug  ", format, args);
285     }
286     va_end(args);
287 }
288 
289 
290 /**
291  * Log debug.
292  *
293  */
294 void
ods_log_debug(const char * format,...)295 ods_log_debug(const char *format, ...)
296 {
297     va_list args;
298     va_start(args, format);
299     if (log_level >= LOG_DEBUG) {
300         ods_log_vmsg(LOG_DEBUG, "debug  ", format, args);
301     }
302     va_end(args);
303 }
304 
305 
306 /**
307  * Log verbose.
308  *
309  */
310 void
ods_log_verbose(const char * format,...)311 ods_log_verbose(const char *format, ...)
312 {
313     va_list args;
314     va_start(args, format);
315     if (log_level >= LOG_INFO) {
316         ods_log_vmsg(LOG_INFO, "verbose", format, args);
317     }
318     va_end(args);
319 }
320 
321 
322 /**
323  * Log info.
324  *
325  */
326 void
ods_log_info(const char * format,...)327 ods_log_info(const char *format, ...)
328 {
329     va_list args;
330     va_start(args, format);
331     if (log_level >= LOG_NOTICE) {
332         ods_log_vmsg(LOG_NOTICE, "msg    ", format, args);
333     }
334     va_end(args);
335 }
336 
337 
338 /**
339  * Log warning.
340  *
341  */
342 void
ods_log_warning(const char * format,...)343 ods_log_warning(const char *format, ...)
344 {
345     va_list args;
346     va_start(args, format);
347     if (log_level >= LOG_WARNING) {
348         ods_log_vmsg(LOG_WARNING, "warning", format, args);
349     }
350     va_end(args);
351 }
352 
353 
354 /**
355  * Log error.
356  *
357  */
358 void
ods_log_error(const char * format,...)359 ods_log_error(const char *format, ...)
360 {
361     va_list args;
362     va_start(args, format);
363     if (log_level >= LOG_ERR) {
364         ods_log_vmsg(LOG_ERR, "error  ", format, args);
365     }
366     va_end(args);
367 }
368 
369 /**
370  * Log error.
371  *
372  */
373 void
ods_log_verror(const char * format,va_list args)374 ods_log_verror(const char *format, va_list args)
375 {
376     if (log_level >= LOG_ERR) {
377         ods_log_vmsg(LOG_ERR, "error  ", format, args);
378     }
379 }
380 
381 /**
382  * Log critical.
383  *
384  */
385 void
ods_log_crit(const char * format,...)386 ods_log_crit(const char *format, ...)
387 {
388     va_list args;
389     va_start(args, format);
390     if (log_level >= LOG_CRIT) {
391         ods_log_vmsg(LOG_CRIT, "crit   ", format, args);
392     }
393     va_end(args);
394 }
395 
396 
397 /**
398  * Log alert.
399  *
400  */
401 void
ods_log_alert(const char * format,...)402 ods_log_alert(const char *format, ...)
403 {
404     va_list args;
405     va_start(args, format);
406     if (log_level >= LOG_ALERT) {
407         ods_log_vmsg(LOG_ALERT, "alert  ", format, args);
408     }
409     va_end(args);
410 }
411 
412 
413 /**
414  * Log emergency and exit.
415  *
416  */
417 void
ods_fatal_exit(const char * format,...)418 ods_fatal_exit(const char *format, ...)
419 {
420     va_list args;
421     va_start(args, format);
422     if (log_level >= LOG_CRIT) {
423         ods_log_vmsg(LOG_CRIT, "fatal  ", format, args);
424     }
425     va_end(args);
426     abort();
427 }
428