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