1 /* Copyright (C) 2007-2020 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18 /**
19 * \file
20 *
21 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
22 *
23 * Debug utility functions
24 */
25
26 #include "suricata-common.h"
27 #include "threads.h"
28 #include "util-debug.h"
29 #include "util-error.h"
30 #include "util-enum.h"
31 #include "util-debug-filters.h"
32
33 #include "decode.h"
34 #include "detect.h"
35 #include "packet-queue.h"
36 #include "threadvars.h"
37 #include "output.h"
38
39 #include "tm-queuehandlers.h"
40 #include "tm-queues.h"
41 #include "tm-threads.h"
42
43 #include "util-unittest.h"
44 #include "util-syslog.h"
45 #include "rust.h"
46
47
48 #include "conf.h"
49
50 /* holds the string-enum mapping for the enums held in the table SCLogLevel */
51 SCEnumCharMap sc_log_level_map[ ] = {
52 { "Not set", SC_LOG_NOTSET},
53 { "None", SC_LOG_NONE },
54 { "Emergency", SC_LOG_EMERGENCY },
55 { "Alert", SC_LOG_ALERT },
56 { "Critical", SC_LOG_CRITICAL },
57 { "Error", SC_LOG_ERROR },
58 { "Warning", SC_LOG_WARNING },
59 { "Notice", SC_LOG_NOTICE },
60 { "Info", SC_LOG_INFO },
61 { "Perf", SC_LOG_PERF },
62 { "Config", SC_LOG_CONFIG },
63 { "Debug", SC_LOG_DEBUG },
64 { NULL, -1 }
65 };
66
67 /* holds the string-enum mapping for the enums held in the table SCLogOPIface */
68 SCEnumCharMap sc_log_op_iface_map[ ] = {
69 { "Console", SC_LOG_OP_IFACE_CONSOLE },
70 { "File", SC_LOG_OP_IFACE_FILE },
71 { "Syslog", SC_LOG_OP_IFACE_SYSLOG },
72 { NULL, -1 }
73 };
74
75 #if defined (OS_WIN32)
76 /**
77 * \brief Used for synchronous output on WIN32
78 */
79 static SCMutex sc_log_stream_lock;
80 #endif /* OS_WIN32 */
81
82 /**
83 * \brief Holds the config state for the logging module
84 */
85 static SCLogConfig *sc_log_config = NULL;
86
87 /**
88 * \brief Returns the full path given a file and configured log dir
89 */
90 static char *SCLogGetLogFilename(const char *);
91
92 /**
93 * \brief Holds the global log level. Is the same as sc_log_config->log_level
94 */
95 SCLogLevel sc_log_global_log_level;
96
97 /**
98 * \brief Used to indicate whether the logging module has been init or not
99 */
100 int sc_log_module_initialized = 0;
101
102 /**
103 * \brief Used to indicate whether the logging module has been cleaned or not
104 */
105 int sc_log_module_cleaned = 0;
106
107 /**
108 * \brief Maps the SC logging level to the syslog logging level
109 *
110 * \param The SC logging level that has to be mapped to the syslog_log_level
111 *
112 * \retval syslog_log_level The mapped syslog_api_log_level, for the logging
113 * module api's internal log_level
114 */
SCLogMapLogLevelToSyslogLevel(int log_level)115 static inline int SCLogMapLogLevelToSyslogLevel(int log_level)
116 {
117 int syslog_log_level = 0;
118
119 switch (log_level) {
120 case SC_LOG_EMERGENCY:
121 syslog_log_level = LOG_EMERG;
122 break;
123 case SC_LOG_ALERT:
124 syslog_log_level = LOG_ALERT;
125 break;
126 case SC_LOG_CRITICAL:
127 syslog_log_level = LOG_CRIT;
128 break;
129 case SC_LOG_ERROR:
130 syslog_log_level = LOG_ERR;
131 break;
132 case SC_LOG_WARNING:
133 syslog_log_level = LOG_WARNING;
134 break;
135 case SC_LOG_NOTICE:
136 syslog_log_level = LOG_NOTICE;
137 break;
138 case SC_LOG_INFO:
139 syslog_log_level = LOG_INFO;
140 break;
141 case SC_LOG_CONFIG:
142 case SC_LOG_DEBUG:
143 case SC_LOG_PERF:
144 syslog_log_level = LOG_DEBUG;
145 break;
146 default:
147 syslog_log_level = LOG_EMERG;
148 break;
149 }
150
151 return syslog_log_level;
152 }
153
154 /**
155 * \brief Output function that logs a character string out to a file descriptor
156 *
157 * \param fd Pointer to the file descriptor
158 * \param msg Pointer to the character string that should be logged
159 */
SCLogPrintToStream(FILE * fd,char * msg)160 static inline void SCLogPrintToStream(FILE *fd, char *msg)
161 {
162 /* Would only happen if the log file failed to re-open during rotation. */
163 if (fd == NULL) {
164 return;
165 }
166
167 #if defined (OS_WIN32)
168 SCMutexLock(&sc_log_stream_lock);
169 #endif /* OS_WIN32 */
170
171 if (fprintf(fd, "%s\n", msg) < 0)
172 printf("Error writing to stream using fprintf\n");
173
174 fflush(fd);
175
176 #if defined (OS_WIN32)
177 SCMutexUnlock(&sc_log_stream_lock);
178 #endif /* OS_WIN32 */
179
180 return;
181 }
182
183 /**
184 * \brief Output function that logs a character string throught the syslog iface
185 *
186 * \param syslog_log_level Holds the syslog_log_level that the message should be
187 * logged as
188 * \param msg Pointer to the char string, that should be logged
189 *
190 * \todo syslog is thread-safe according to POSIX manual and glibc code, but we
191 * we will have to look into non POSIX compliant boxes like freeBSD
192 */
SCLogPrintToSyslog(int syslog_log_level,const char * msg)193 static inline void SCLogPrintToSyslog(int syslog_log_level, const char *msg)
194 {
195 //static struct syslog_data data = SYSLOG_DATA_INIT;
196 //syslog_r(syslog_log_level, NULL, "%s", msg);
197
198 syslog(syslog_log_level, "%s", msg);
199
200 return;
201 }
202
203 /**
204 */
SCLogMessageJSON(struct timeval * tval,char * buffer,size_t buffer_size,SCLogLevel log_level,const char * file,unsigned line,const char * function,SCError error_code,const char * message)205 static int SCLogMessageJSON(struct timeval *tval, char *buffer, size_t buffer_size,
206 SCLogLevel log_level, const char *file,
207 unsigned line, const char *function, SCError error_code,
208 const char *message)
209 {
210 json_t *js = json_object();
211 if (unlikely(js == NULL))
212 goto error;
213 json_t *ejs = json_object();
214 if (unlikely(ejs == NULL))
215 goto error;
216
217 char timebuf[64];
218 CreateIsoTimeString(tval, timebuf, sizeof(timebuf));
219 json_object_set_new(js, "timestamp", json_string(timebuf));
220
221 const char *s = SCMapEnumValueToName(log_level, sc_log_level_map);
222 if (s != NULL) {
223 json_object_set_new(js, "log_level", json_string(s));
224 } else {
225 json_object_set_new(js, "log_level", json_string("INVALID"));
226 }
227
228 json_object_set_new(js, "event_type", json_string("engine"));
229
230 if (error_code > 0) {
231 json_object_set_new(ejs, "error_code", json_integer(error_code));
232 json_object_set_new(ejs, "error", json_string(SCErrorToString(error_code)));
233 }
234
235 if (message)
236 json_object_set_new(ejs, "message", json_string(message));
237
238 if (log_level >= SC_LOG_DEBUG) {
239 if (function)
240 json_object_set_new(ejs, "function", json_string(function));
241
242 if (file)
243 json_object_set_new(ejs, "file", json_string(file));
244
245 if (line > 0)
246 json_object_set_new(ejs, "line", json_integer(line));
247 }
248
249 json_object_set_new(js, "engine", ejs);
250
251 char *js_s = json_dumps(js,
252 JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
253 JSON_ESCAPE_SLASH);
254 snprintf(buffer, buffer_size, "%s", js_s);
255 free(js_s);
256
257 json_object_del(js, "engine");
258 json_object_clear(js);
259 json_decref(js);
260
261 return 0;
262 error:
263 return -1;
264 }
265
266 /**
267 * \brief Adds the global log_format to the outgoing buffer
268 *
269 * \param log_level log_level of the message that has to be logged
270 * \param msg Buffer containing the outgoing message
271 * \param file File_name from where the message originated
272 * \param function Function_name from where the message originated
273 * \param line Line_no from where the messaged originated
274 *
275 * \retval SC_OK on success; else an error code
276 */
SCLogMessageGetBuffer(struct timeval * tval,int color,SCLogOPType type,char * buffer,size_t buffer_size,const char * log_format,const SCLogLevel log_level,const char * file,const unsigned int line,const char * function,const SCError error_code,const char * message)277 static SCError SCLogMessageGetBuffer(
278 struct timeval *tval, int color, SCLogOPType type,
279 char *buffer, size_t buffer_size,
280 const char *log_format,
281
282 const SCLogLevel log_level, const char *file,
283 const unsigned int line, const char *function,
284 const SCError error_code, const char *message)
285 {
286 if (type == SC_LOG_OP_TYPE_JSON)
287 return SCLogMessageJSON(tval, buffer, buffer_size, log_level, file, line, function, error_code, message);
288
289 char *temp = buffer;
290 const char *s = NULL;
291 struct tm *tms = NULL;
292
293 const char *redb = "";
294 const char *red = "";
295 const char *yellowb = "";
296 const char *yellow = "";
297 const char *green = "";
298 const char *blue = "";
299 const char *reset = "";
300 if (color) {
301 redb = "\x1b[1;31m";
302 red = "\x1b[31m";
303 yellowb = "\x1b[1;33m";
304 yellow = "\x1b[33m";
305 green = "\x1b[32m";
306 blue = "\x1b[34m";
307 reset = "\x1b[0m";
308 }
309 /* no of characters_written(cw) by snprintf */
310 int cw = 0;
311
312 BUG_ON(sc_log_module_initialized != 1);
313
314 /* make a copy of the format string as it will be modified below */
315 char local_format[strlen(log_format) + 1];
316 strlcpy(local_format, log_format, sizeof(local_format));
317 char *temp_fmt = local_format;
318 char *substr = temp_fmt;
319
320 while ( (temp_fmt = strchr(temp_fmt, SC_LOG_FMT_PREFIX)) ) {
321 if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
322 return SC_OK;
323 }
324 switch(temp_fmt[1]) {
325 case SC_LOG_FMT_TIME:
326 temp_fmt[0] = '\0';
327
328 struct tm local_tm;
329 tms = SCLocalTime(tval->tv_sec, &local_tm);
330
331 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
332 "%s%s%d/%d/%04d -- %02d:%02d:%02d%s",
333 substr, green, tms->tm_mday, tms->tm_mon + 1,
334 tms->tm_year + 1900, tms->tm_hour, tms->tm_min,
335 tms->tm_sec, reset);
336 if (cw < 0)
337 return SC_ERR_SPRINTF;
338 temp += cw;
339 temp_fmt++;
340 substr = temp_fmt;
341 substr++;
342 break;
343
344 case SC_LOG_FMT_PID:
345 temp_fmt[0] = '\0';
346 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
347 "%s%s%u%s", substr, yellow, getpid(), reset);
348 if (cw < 0)
349 return SC_ERR_SPRINTF;
350 temp += cw;
351 temp_fmt++;
352 substr = temp_fmt;
353 substr++;
354 break;
355
356 case SC_LOG_FMT_TID:
357 temp_fmt[0] = '\0';
358 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
359 "%s%s%lu%s", substr, yellow, SCGetThreadIdLong(), reset);
360 if (cw < 0)
361 return SC_ERR_SPRINTF;
362 temp += cw;
363 temp_fmt++;
364 substr = temp_fmt;
365 substr++;
366 break;
367
368 case SC_LOG_FMT_TM:
369 temp_fmt[0] = '\0';
370 /* disabled to prevent dead lock:
371 * log or alloc (which calls log on error) can call TmThreadsGetCallingThread
372 * which will lock tv_root_lock. This can happen while we already hold this
373 * lock. */
374 #if 0
375 ThreadVars *tv = TmThreadsGetCallingThread();
376 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - *msg),
377 "%s%s", substr, ((tv != NULL)? tv->name: "UNKNOWN TM"));
378 #endif
379 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
380 "%s%s", substr, "N/A");
381 if (cw < 0)
382 return SC_ERR_SPRINTF;
383 temp += cw;
384 temp_fmt++;
385 substr = temp_fmt;
386 substr++;
387 break;
388
389 case SC_LOG_FMT_LOG_LEVEL:
390 temp_fmt[0] = '\0';
391 s = SCMapEnumValueToName(log_level, sc_log_level_map);
392 if (s != NULL) {
393 if (log_level <= SC_LOG_ERROR)
394 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
395 "%s%s%s%s", substr, redb, s, reset);
396 else if (log_level == SC_LOG_WARNING)
397 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
398 "%s%s%s%s", substr, red, s, reset);
399 else if (log_level == SC_LOG_NOTICE)
400 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
401 "%s%s%s%s", substr, yellowb, s, reset);
402 else
403 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
404 "%s%s%s%s", substr, yellow, s, reset);
405 } else {
406 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
407 "%s%s", substr, "INVALID");
408 }
409 if (cw < 0)
410 return SC_ERR_SPRINTF;
411 temp += cw;
412 temp_fmt++;
413 substr = temp_fmt;
414 substr++;
415 break;
416
417 case SC_LOG_FMT_FILE_NAME:
418 temp_fmt[0] = '\0';
419 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
420 "%s%s%s%s", substr, blue, file, reset);
421 if (cw < 0)
422 return SC_ERR_SPRINTF;
423 temp += cw;
424 temp_fmt++;
425 substr = temp_fmt;
426 substr++;
427 break;
428
429 case SC_LOG_FMT_LINE:
430 temp_fmt[0] = '\0';
431 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
432 "%s%s%u%s", substr, green, line, reset);
433 if (cw < 0)
434 return SC_ERR_SPRINTF;
435 temp += cw;
436 temp_fmt++;
437 substr = temp_fmt;
438 substr++;
439 break;
440
441 case SC_LOG_FMT_FUNCTION:
442 temp_fmt[0] = '\0';
443 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
444 "%s%s%s%s", substr, green, function, reset);
445 if (cw < 0)
446 return SC_ERR_SPRINTF;
447 temp += cw;
448 temp_fmt++;
449 substr = temp_fmt;
450 substr++;
451 break;
452
453 }
454 temp_fmt++;
455 }
456 if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
457 return SC_OK;
458 }
459 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s", substr);
460 if (cw < 0) {
461 return SC_ERR_SPRINTF;
462 }
463 temp += cw;
464 if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
465 return SC_OK;
466 }
467
468 if (error_code != SC_OK) {
469 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
470 "[%sERRCODE%s: %s%s%s(%s%d%s)] - ", yellow, reset, red, SCErrorToString(error_code), reset, yellow, error_code, reset);
471 if (cw < 0) {
472 return SC_ERR_SPRINTF;
473 }
474 temp += cw;
475 if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
476 return SC_OK;
477 }
478 }
479
480 const char *hi = "";
481 if (error_code > SC_OK)
482 hi = red;
483 else if (log_level <= SC_LOG_NOTICE)
484 hi = yellow;
485 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s%s%s", hi, message, reset);
486 if (cw < 0) {
487 return SC_ERR_SPRINTF;
488 }
489 temp += cw;
490 if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
491 return SC_OK;
492 }
493
494 if (sc_log_config->op_filter_regex != NULL) {
495 #define MAX_SUBSTRINGS 30
496 int ov[MAX_SUBSTRINGS];
497
498 if (pcre_exec(sc_log_config->op_filter_regex,
499 sc_log_config->op_filter_regex_study,
500 buffer, strlen(buffer), 0, 0, ov, MAX_SUBSTRINGS) < 0)
501 {
502 return SC_ERR_LOG_FG_FILTER_MATCH; // bit hacky, but just return !0
503 }
504 #undef MAX_SUBSTRINGS
505 }
506
507 return SC_OK;
508 }
509
510 /** \internal
511 * \brief try to reopen file
512 * \note no error reporting here, as we're called by SCLogMessage
513 * \retval status 0 ok, -1 error */
SCLogReopen(SCLogOPIfaceCtx * op_iface_ctx)514 static int SCLogReopen(SCLogOPIfaceCtx *op_iface_ctx)
515 {
516 if (op_iface_ctx->file == NULL) {
517 return 0;
518 }
519
520 if (op_iface_ctx->file_d != NULL) {
521 fclose(op_iface_ctx->file_d);
522 }
523 op_iface_ctx->file_d = fopen(op_iface_ctx->file, "a");
524 if (op_iface_ctx->file_d == NULL) {
525 return -1;
526 }
527 return 0;
528 }
529
530 /**
531 * \brief Adds the global log_format to the outgoing buffer
532 *
533 * \param log_level log_level of the message that has to be logged
534 * \param msg Buffer containing the outgoing message
535 * \param file File_name from where the message originated
536 * \param function Function_name from where the message originated
537 * \param line Line_no from where the messaged originated
538 *
539 * \retval SC_OK on success; else an error code
540 */
SCLogMessage(const SCLogLevel log_level,const char * file,const unsigned int line,const char * function,const SCError error_code,const char * message)541 SCError SCLogMessage(const SCLogLevel log_level, const char *file,
542 const unsigned int line, const char *function,
543 const SCError error_code, const char *message)
544 {
545 char buffer[SC_LOG_MAX_LOG_MSG_LEN] = "";
546 SCLogOPIfaceCtx *op_iface_ctx = NULL;
547
548 if (sc_log_module_initialized != 1) {
549 printf("Logging module not initialized. Call SCLogInitLogModule() "
550 "first before using the debug API\n");
551 return SC_OK;
552 }
553
554 /* get ts here so we log the same ts to each output */
555 struct timeval tval;
556 gettimeofday(&tval, NULL);
557
558 op_iface_ctx = sc_log_config->op_ifaces;
559 while (op_iface_ctx != NULL) {
560 if (log_level != SC_LOG_NOTSET && log_level > op_iface_ctx->log_level) {
561 op_iface_ctx = op_iface_ctx->next;
562 continue;
563 }
564
565 switch (op_iface_ctx->iface) {
566 case SC_LOG_OP_IFACE_CONSOLE:
567 if (SCLogMessageGetBuffer(&tval, op_iface_ctx->use_color, op_iface_ctx->type,
568 buffer, sizeof(buffer),
569 op_iface_ctx->log_format ?
570 op_iface_ctx->log_format : sc_log_config->log_format,
571 log_level, file, line, function,
572 error_code, message) == 0)
573 {
574 SCLogPrintToStream((log_level == SC_LOG_ERROR)? stderr: stdout, buffer);
575 }
576 break;
577 case SC_LOG_OP_IFACE_FILE:
578 if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
579 op_iface_ctx->log_format ?
580 op_iface_ctx->log_format : sc_log_config->log_format,
581 log_level, file, line, function,
582 error_code, message) == 0)
583 {
584 int r = 0;
585 SCMutexLock(&op_iface_ctx->fp_mutex);
586 if (op_iface_ctx->rotation_flag) {
587 r = SCLogReopen(op_iface_ctx);
588 op_iface_ctx->rotation_flag = 0;
589 }
590 SCLogPrintToStream(op_iface_ctx->file_d, buffer);
591 SCMutexUnlock(&op_iface_ctx->fp_mutex);
592
593 /* report error outside of lock to avoid recursion */
594 if (r == -1) {
595 SCLogError(SC_ERR_FOPEN, "re-opening file \"%s\" failed: %s",
596 op_iface_ctx->file, strerror(errno));
597 }
598 }
599 break;
600 case SC_LOG_OP_IFACE_SYSLOG:
601 if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
602 op_iface_ctx->log_format ?
603 op_iface_ctx->log_format : sc_log_config->log_format,
604 log_level, file, line, function,
605 error_code, message) == 0)
606 {
607 SCLogPrintToSyslog(SCLogMapLogLevelToSyslogLevel(log_level), buffer);
608 }
609 break;
610 default:
611 break;
612 }
613 op_iface_ctx = op_iface_ctx->next;
614 }
615 return SC_OK;
616 }
617
SCLog(int x,const char * file,const char * func,const int line,const char * fmt,...)618 void SCLog(int x, const char *file, const char *func, const int line,
619 const char *fmt, ...)
620 {
621 if (sc_log_global_log_level >= x &&
622 (sc_log_fg_filters_present == 0 ||
623 SCLogMatchFGFilterWL(file, func, line) == 1 ||
624 SCLogMatchFGFilterBL(file, func, line) == 1) &&
625 (sc_log_fd_filters_present == 0 ||
626 SCLogMatchFDFilter(func) == 1))
627 {
628 char msg[SC_LOG_MAX_LOG_MSG_LEN];
629 va_list ap;
630 va_start(ap, fmt);
631 vsnprintf(msg, sizeof(msg), fmt, ap);
632 va_end(ap);
633 SCLogMessage(x, file, line, func, SC_OK, msg);
634 }
635 }
636
SCLogErr(int x,const char * file,const char * func,const int line,const int err,const char * fmt,...)637 void SCLogErr(int x, const char *file, const char *func, const int line,
638 const int err, const char *fmt, ...)
639 {
640 if (sc_log_global_log_level >= x &&
641 (sc_log_fg_filters_present == 0 ||
642 SCLogMatchFGFilterWL(file, func, line) == 1 ||
643 SCLogMatchFGFilterBL(file, func, line) == 1) &&
644 (sc_log_fd_filters_present == 0 ||
645 SCLogMatchFDFilter(func) == 1))
646 {
647 char msg[SC_LOG_MAX_LOG_MSG_LEN];
648 va_list ap;
649 va_start(ap, fmt);
650 vsnprintf(msg, sizeof(msg), fmt, ap);
651 va_end(ap);
652 SCLogMessage(x, file, line, func, err, msg);
653 }
654 }
655
656 /**
657 * \brief Returns whether debug messages are enabled to be logged or not
658 *
659 * \retval 1 if debug messages are enabled to be logged
660 * \retval 0 if debug messages are not enabled to be logged
661 */
SCLogDebugEnabled(void)662 int SCLogDebugEnabled(void)
663 {
664 #ifdef DEBUG
665 if (sc_log_global_log_level == SC_LOG_DEBUG)
666 return 1;
667 else
668 return 0;
669 #else
670 return 0;
671 #endif
672 }
673
674 /**
675 * \brief Allocates an output buffer for an output interface. Used when we
676 * want the op_interface log_format to override the global_log_format.
677 * Currently not used.
678 *
679 * \retval buffer Pointer to the newly created output_buffer
680 */
SCLogAllocLogOPBuffer(void)681 SCLogOPBuffer *SCLogAllocLogOPBuffer(void)
682 {
683 SCLogOPBuffer *buffer = NULL;
684 SCLogOPIfaceCtx *op_iface_ctx = NULL;
685 int i = 0;
686
687 if ( (buffer = SCMalloc(sc_log_config->op_ifaces_cnt *
688 sizeof(SCLogOPBuffer))) == NULL) {
689 FatalError(SC_ERR_FATAL,
690 "Fatal error encountered in SCLogAllocLogOPBuffer. Exiting...");
691 }
692
693 op_iface_ctx = sc_log_config->op_ifaces;
694 for (i = 0;
695 i < sc_log_config->op_ifaces_cnt;
696 i++, op_iface_ctx = op_iface_ctx->next) {
697 buffer[i].log_format = op_iface_ctx->log_format;
698 buffer[i].temp = buffer[i].msg;
699 }
700
701 return buffer;
702 }
703
704 /*----------------------The logging module initialization code--------------- */
705
706 /**
707 * \brief Returns a new output_interface_context
708 *
709 * \retval iface_ctx Pointer to a newly allocated output_interface_context
710 * \initonly
711 */
SCLogAllocLogOPIfaceCtx(void)712 static inline SCLogOPIfaceCtx *SCLogAllocLogOPIfaceCtx(void)
713 {
714 SCLogOPIfaceCtx *iface_ctx = NULL;
715
716 if ( (iface_ctx = SCMalloc(sizeof(SCLogOPIfaceCtx))) == NULL) {
717 FatalError(SC_ERR_FATAL,
718 "Fatal error encountered in SCLogallocLogOPIfaceCtx. Exiting...");
719 }
720 memset(iface_ctx, 0, sizeof(SCLogOPIfaceCtx));
721
722 return iface_ctx;
723 }
724
725 /**
726 * \brief Initializes the file output interface
727 *
728 * \param file Path to the file used for logging purposes
729 * \param log_format Pointer to the log_format for this op interface, that
730 * overrides the global_log_format
731 * \param log_level Override of the global_log_level by this interface
732 *
733 * \retval iface_ctx Pointer to the file output interface context created
734 * \initonly
735 */
SCLogInitFileOPIface(const char * file,const char * log_format,int log_level,SCLogOPType type)736 static inline SCLogOPIfaceCtx *SCLogInitFileOPIface(const char *file,
737 const char *log_format,
738 int log_level,
739 SCLogOPType type)
740 {
741 SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
742
743 if (iface_ctx == NULL) {
744 FatalError(SC_ERR_FATAL,
745 "Fatal error encountered in SCLogInitFileOPIface. Exiting...");
746 }
747
748 if (file == NULL) {
749 goto error;
750 }
751
752 iface_ctx->iface = SC_LOG_OP_IFACE_FILE;
753 iface_ctx->type = type;
754
755 if ( (iface_ctx->file_d = fopen(file, "a")) == NULL) {
756 printf("Error opening file %s\n", file);
757 goto error;
758 }
759
760 if ((iface_ctx->file = SCStrdup(file)) == NULL) {
761 goto error;
762 }
763
764 if (log_format != NULL && (iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
765 goto error;
766 }
767
768 SCMutexInit(&iface_ctx->fp_mutex, NULL);
769 OutputRegisterFileRotationFlag(&iface_ctx->rotation_flag);
770
771 iface_ctx->log_level = log_level;
772
773 return iface_ctx;
774
775 error:
776 if (iface_ctx->file != NULL) {
777 SCFree((char *)iface_ctx->file);
778 iface_ctx->file = NULL;
779 }
780 if (iface_ctx->log_format != NULL) {
781 SCFree((char *)iface_ctx->log_format);
782 iface_ctx->log_format = NULL;
783 }
784 if (iface_ctx->file_d != NULL) {
785 fclose(iface_ctx->file_d);
786 iface_ctx->file_d = NULL;
787 }
788 SCFree(iface_ctx);
789 return NULL;
790 }
791
792 /**
793 * \brief Initializes the console output interface and deals with possible
794 * env var overrides.
795 *
796 * \param log_format Pointer to the log_format for this op interface, that
797 * overrides the global_log_format
798 * \param log_level Override of the global_log_level by this interface
799 *
800 * \retval iface_ctx Pointer to the console output interface context created
801 * \initonly
802 */
SCLogInitConsoleOPIface(const char * log_format,SCLogLevel log_level,SCLogOPType type)803 static inline SCLogOPIfaceCtx *SCLogInitConsoleOPIface(const char *log_format,
804 SCLogLevel log_level, SCLogOPType type)
805 {
806 SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
807
808 if (iface_ctx == NULL) {
809 FatalError(SC_ERR_FATAL,
810 "Fatal error encountered in SCLogInitConsoleOPIface. Exiting...");
811 }
812
813 iface_ctx->iface = SC_LOG_OP_IFACE_CONSOLE;
814 iface_ctx->type = type;
815
816 /* console log format is overridden by envvars */
817 const char *tmp_log_format = log_format;
818 const char *s = getenv(SC_LOG_ENV_LOG_FORMAT);
819 if (s != NULL) {
820 #if 0
821 printf("Overriding setting for \"console.format\" because of env "
822 "var SC_LOG_FORMAT=\"%s\".\n", s);
823 #endif
824 tmp_log_format = s;
825 }
826
827 if (tmp_log_format != NULL &&
828 (iface_ctx->log_format = SCStrdup(tmp_log_format)) == NULL) {
829 printf("Error allocating memory\n");
830 exit(EXIT_FAILURE);
831 }
832
833 /* console log level is overridden by envvars */
834 SCLogLevel tmp_log_level = log_level;
835 s = getenv(SC_LOG_ENV_LOG_LEVEL);
836 if (s != NULL) {
837 SCLogLevel l = SCMapEnumNameToValue(s, sc_log_level_map);
838 if (l > SC_LOG_NOTSET && l < SC_LOG_LEVEL_MAX) {
839 #if 0
840 printf("Overriding setting for \"console.level\" because of env "
841 "var SC_LOG_LEVEL=\"%s\".\n", s);
842 #endif
843 tmp_log_level = l;
844 }
845 }
846 iface_ctx->log_level = tmp_log_level;
847
848 #ifndef OS_WIN32
849 if (isatty(fileno(stdout)) && isatty(fileno(stderr))) {
850 iface_ctx->use_color = TRUE;
851 }
852 #endif
853
854 return iface_ctx;
855 }
856
857 /**
858 * \brief Initializes the syslog output interface
859 *
860 * \param facility The facility code for syslog
861 * \param log_format Pointer to the log_format for this op interface, that
862 * overrides the global_log_format
863 * \param log_level Override of the global_log_level by this interface
864 *
865 * \retval iface_ctx Pointer to the syslog output interface context created
866 */
SCLogInitSyslogOPIface(int facility,const char * log_format,SCLogLevel log_level,SCLogOPType type)867 static inline SCLogOPIfaceCtx *SCLogInitSyslogOPIface(int facility,
868 const char *log_format,
869 SCLogLevel log_level,
870 SCLogOPType type)
871 {
872 SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
873
874 if ( iface_ctx == NULL) {
875 FatalError(SC_ERR_FATAL,
876 "Fatal error encountered in SCLogInitSyslogOPIface. Exiting...");
877 }
878
879 iface_ctx->iface = SC_LOG_OP_IFACE_SYSLOG;
880 iface_ctx->type = type;
881
882 if (facility == -1)
883 facility = SC_LOG_DEF_SYSLOG_FACILITY;
884 iface_ctx->facility = facility;
885
886 if (log_format != NULL &&
887 (iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
888 printf("Error allocating memory\n");
889 exit(EXIT_FAILURE);
890 }
891
892 iface_ctx->log_level = log_level;
893
894 openlog(NULL, LOG_NDELAY, iface_ctx->facility);
895
896 return iface_ctx;
897 }
898
899 /**
900 * \brief Frees the output_interface context supplied as an argument
901 *
902 * \param iface_ctx Pointer to the op_interface_context to be freed
903 */
SCLogFreeLogOPIfaceCtx(SCLogOPIfaceCtx * iface_ctx)904 static inline void SCLogFreeLogOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx)
905 {
906 SCLogOPIfaceCtx *temp = NULL;
907
908 while (iface_ctx != NULL) {
909 temp = iface_ctx;
910
911 if (iface_ctx->file_d != NULL) {
912 fclose(iface_ctx->file_d);
913 SCMutexDestroy(&iface_ctx->fp_mutex);
914 }
915
916 if (iface_ctx->file != NULL)
917 SCFree((void *)iface_ctx->file);
918
919 if (iface_ctx->log_format != NULL)
920 SCFree((void *)iface_ctx->log_format);
921
922 if (iface_ctx->iface == SC_LOG_OP_IFACE_SYSLOG) {
923 closelog();
924 }
925
926 iface_ctx = iface_ctx->next;
927
928 SCFree(temp);
929 }
930
931 return;
932 }
933
934 /**
935 * \brief Internal function used to set the logging module global_log_level
936 * during the initialization phase
937 *
938 * \param sc_lid The initialization data supplied.
939 * \param sc_lc The logging module context which has to be updated.
940 */
SCLogSetLogLevel(SCLogInitData * sc_lid,SCLogConfig * sc_lc)941 static inline void SCLogSetLogLevel(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
942 {
943 SCLogLevel log_level = SC_LOG_NOTSET;
944 const char *s = NULL;
945
946 /* envvar overrides config */
947 s = getenv(SC_LOG_ENV_LOG_LEVEL);
948 if (s != NULL) {
949 log_level = SCMapEnumNameToValue(s, sc_log_level_map);
950 } else if (sc_lid != NULL) {
951 log_level = sc_lid->global_log_level;
952 }
953
954 /* deal with the global_log_level to be used */
955 if (log_level > SC_LOG_NOTSET && log_level < SC_LOG_LEVEL_MAX)
956 sc_lc->log_level = log_level;
957 else {
958 sc_lc->log_level = SC_LOG_DEF_LOG_LEVEL;
959 #ifndef UNITTESTS
960 if (sc_lid != NULL) {
961 printf("Warning: Invalid/No global_log_level assigned by user. Falling "
962 "back on the default_log_level \"%s\"\n",
963 SCMapEnumValueToName(sc_lc->log_level, sc_log_level_map));
964 }
965 #endif
966 }
967
968 /* we also set it to a global var, as it is easier to access it */
969 sc_log_global_log_level = sc_lc->log_level;
970
971 return;
972 }
973
SCLogGetLogLevel(void)974 SCLogLevel SCLogGetLogLevel(void)
975 {
976 return sc_log_global_log_level;
977 }
978
SCLogGetDefaultLogFormat(void)979 static inline const char *SCLogGetDefaultLogFormat(void)
980 {
981 const char *prog_ver = GetProgramVersion();
982 if (strstr(prog_ver, "RELEASE") != NULL) {
983 return SC_LOG_DEF_LOG_FORMAT_REL;
984 }
985 return SC_LOG_DEF_LOG_FORMAT_DEV;
986 }
987
988 /**
989 * \brief Internal function used to set the logging module global_log_format
990 * during the initialization phase
991 *
992 * \param sc_lid The initialization data supplied.
993 * \param sc_lc The logging module context which has to be updated.
994 */
SCLogSetLogFormat(SCLogInitData * sc_lid,SCLogConfig * sc_lc)995 static inline void SCLogSetLogFormat(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
996 {
997 const char *format = NULL;
998
999 /* envvar overrides config */
1000 format = getenv(SC_LOG_ENV_LOG_FORMAT);
1001 if (format == NULL) {
1002 if (sc_lid != NULL) {
1003 format = sc_lid->global_log_format;
1004 }
1005 }
1006
1007 /* deal with the global log format to be used */
1008 if (format == NULL || strlen(format) > SC_LOG_MAX_LOG_FORMAT_LEN) {
1009 format = SCLogGetDefaultLogFormat();
1010 #ifndef UNITTESTS
1011 if (sc_lid != NULL) {
1012 printf("Warning: Invalid/No global_log_format supplied by user or format "
1013 "length exceeded limit of \"%d\" characters. Falling back on "
1014 "default log_format \"%s\"\n", SC_LOG_MAX_LOG_FORMAT_LEN,
1015 format);
1016 }
1017 #endif
1018 }
1019
1020 if (format != NULL && (sc_lc->log_format = SCStrdup(format)) == NULL) {
1021 printf("Error allocating memory\n");
1022 exit(EXIT_FAILURE);
1023 }
1024
1025 return;
1026 }
1027
1028 /**
1029 * \brief Internal function used to set the logging module global_op_ifaces
1030 * during the initialization phase
1031 *
1032 * \param sc_lid The initialization data supplied.
1033 * \param sc_lc The logging module context which has to be updated.
1034 */
SCLogSetOPIface(SCLogInitData * sc_lid,SCLogConfig * sc_lc)1035 static inline void SCLogSetOPIface(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
1036 {
1037 SCLogOPIfaceCtx *op_ifaces_ctx = NULL;
1038 int op_iface = 0;
1039 const char *s = NULL;
1040
1041 if (sc_lid != NULL && sc_lid->op_ifaces != NULL) {
1042 sc_lc->op_ifaces = sc_lid->op_ifaces;
1043 sc_lid->op_ifaces = NULL;
1044 sc_lc->op_ifaces_cnt = sc_lid->op_ifaces_cnt;
1045 } else {
1046 s = getenv(SC_LOG_ENV_LOG_OP_IFACE);
1047 if (s != NULL) {
1048 op_iface = SCMapEnumNameToValue(s, sc_log_op_iface_map);
1049
1050 if(op_iface < 0 || op_iface >= SC_LOG_OP_IFACE_MAX) {
1051 op_iface = SC_LOG_DEF_LOG_OP_IFACE;
1052 #ifndef UNITTESTS
1053 printf("Warning: Invalid output interface supplied by user. "
1054 "Falling back on default_output_interface \"%s\"\n",
1055 SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
1056 #endif
1057 }
1058 }
1059 else {
1060 op_iface = SC_LOG_DEF_LOG_OP_IFACE;
1061 #ifndef UNITTESTS
1062 if (sc_lid != NULL) {
1063 printf("Warning: Output_interface not supplied by user. Falling "
1064 "back on default_output_interface \"%s\"\n",
1065 SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
1066 }
1067 #endif
1068 }
1069
1070 switch (op_iface) {
1071 case SC_LOG_OP_IFACE_CONSOLE:
1072 op_ifaces_ctx = SCLogInitConsoleOPIface(NULL, SC_LOG_LEVEL_MAX,0);
1073 break;
1074 case SC_LOG_OP_IFACE_FILE:
1075 s = getenv(SC_LOG_ENV_LOG_FILE);
1076 if (s == NULL) {
1077 char *str = SCLogGetLogFilename(SC_LOG_DEF_LOG_FILE);
1078 if (str != NULL) {
1079 op_ifaces_ctx = SCLogInitFileOPIface(str, NULL, SC_LOG_LEVEL_MAX,0);
1080 SCFree(str);
1081 }
1082 } else {
1083 op_ifaces_ctx = SCLogInitFileOPIface(s, NULL, SC_LOG_LEVEL_MAX,0);
1084 }
1085 break;
1086 case SC_LOG_OP_IFACE_SYSLOG:
1087 s = getenv(SC_LOG_ENV_LOG_FACILITY);
1088 if (s == NULL)
1089 s = SC_LOG_DEF_SYSLOG_FACILITY_STR;
1090
1091 op_ifaces_ctx = SCLogInitSyslogOPIface(SCMapEnumNameToValue(s, SCSyslogGetFacilityMap()), NULL, -1,0);
1092 break;
1093 }
1094 sc_lc->op_ifaces = op_ifaces_ctx;
1095 sc_lc->op_ifaces_cnt++;
1096 }
1097 return;
1098 }
1099
1100 /**
1101 * \brief Internal function used to set the logging module op_filter
1102 * during the initialization phase
1103 *
1104 * \param sc_lid The initialization data supplied.
1105 * \param sc_lc The logging module context which has to be updated.
1106 */
SCLogSetOPFilter(SCLogInitData * sc_lid,SCLogConfig * sc_lc)1107 static inline void SCLogSetOPFilter(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
1108 {
1109 const char *filter = NULL;
1110
1111 int opts = 0;
1112 const char *ep;
1113 int eo = 0;
1114
1115 /* envvar overrides */
1116 filter = getenv(SC_LOG_ENV_LOG_OP_FILTER);
1117 if (filter == NULL) {
1118 if (sc_lid != NULL) {
1119 filter = sc_lid->op_filter;
1120 }
1121 }
1122
1123 if (filter != NULL && strcmp(filter, "") != 0) {
1124 sc_lc->op_filter = SCStrdup(filter);
1125 if (sc_lc->op_filter == NULL) {
1126 printf("pcre filter alloc failed\n");
1127 return;
1128 }
1129 sc_lc->op_filter_regex = pcre_compile(filter, opts, &ep, &eo, NULL);
1130 if (sc_lc->op_filter_regex == NULL) {
1131 SCFree(sc_lc->op_filter);
1132 printf("pcre compile of \"%s\" failed at offset %d : %s\n", filter,
1133 eo, ep);
1134 return;
1135 }
1136
1137 sc_lc->op_filter_regex_study = pcre_study(sc_lc->op_filter_regex, 0,
1138 &ep);
1139 if (ep != NULL) {
1140 printf("pcre study failed: %s\n", ep);
1141 return;
1142 }
1143 }
1144
1145 return;
1146 }
1147
1148 /**
1149 * \brief Returns a pointer to a new SCLogInitData. This is a public interface
1150 * intended to be used after the logging paramters are read from the
1151 * conf file
1152 *
1153 * \retval sc_lid Pointer to the newly created SCLogInitData
1154 * \initonly
1155 */
SCLogAllocLogInitData(void)1156 SCLogInitData *SCLogAllocLogInitData(void)
1157 {
1158 SCLogInitData *sc_lid = NULL;
1159
1160 /* not using SCMalloc here because if it fails we can't log */
1161 if ( (sc_lid = SCMalloc(sizeof(SCLogInitData))) == NULL)
1162 return NULL;
1163
1164 memset(sc_lid, 0, sizeof(SCLogInitData));
1165
1166 return sc_lid;
1167 }
1168
1169 #ifdef UNITTESTS
1170 #ifndef OS_WIN32
1171 /**
1172 * \brief Frees a SCLogInitData
1173 *
1174 * \param sc_lid Pointer to the SCLogInitData to be freed
1175 */
SCLogFreeLogInitData(SCLogInitData * sc_lid)1176 static void SCLogFreeLogInitData(SCLogInitData *sc_lid)
1177 {
1178 if (sc_lid != NULL) {
1179 SCLogFreeLogOPIfaceCtx(sc_lid->op_ifaces);
1180 SCFree(sc_lid);
1181 }
1182
1183 return;
1184 }
1185 #endif
1186 #endif
1187
1188 /**
1189 * \brief Frees the logging module context
1190 */
SCLogFreeLogConfig(SCLogConfig * sc_lc)1191 static inline void SCLogFreeLogConfig(SCLogConfig *sc_lc)
1192 {
1193 if (sc_lc != NULL) {
1194 if (sc_lc->startup_message != NULL)
1195 SCFree(sc_lc->startup_message);
1196 if (sc_lc->log_format != NULL)
1197 SCFree(sc_lc->log_format);
1198 if (sc_lc->op_filter != NULL)
1199 SCFree(sc_lc->op_filter);
1200
1201 if (sc_lc->op_filter_regex != NULL)
1202 pcre_free(sc_lc->op_filter_regex);
1203 if (sc_lc->op_filter_regex_study)
1204 pcre_free_study(sc_lc->op_filter_regex_study);
1205
1206 SCLogFreeLogOPIfaceCtx(sc_lc->op_ifaces);
1207 SCFree(sc_lc);
1208 }
1209
1210 return;
1211 }
1212
1213 /**
1214 * \brief Appends an output_interface to the output_interface list sent in head
1215 *
1216 * \param iface_ctx Pointer to the output_interface that has to be added to head
1217 * \param head Pointer to the output_interface list
1218 */
SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx * iface_ctx,SCLogInitData * sc_lid)1219 void SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx, SCLogInitData *sc_lid)
1220 {
1221 SCLogOPIfaceCtx *temp = NULL, *prev = NULL;
1222 SCLogOPIfaceCtx **head = &sc_lid->op_ifaces;
1223
1224 if (iface_ctx == NULL) {
1225 #ifdef DEBUG
1226 printf("Argument(s) to SCLogAppendOPIfaceCtx() NULL\n");
1227 #endif
1228 return;
1229 }
1230
1231 temp = *head;
1232 while (temp != NULL) {
1233 prev = temp;
1234 temp = temp->next;
1235 }
1236
1237 if (prev == NULL)
1238 *head = iface_ctx;
1239 else
1240 prev->next = iface_ctx;
1241
1242 sc_lid->op_ifaces_cnt++;
1243
1244 return;
1245 }
1246
1247
1248 /**
1249 * \brief Creates a new output interface based on the arguments sent. The kind
1250 * of output interface to be created is decided by the iface_name arg.
1251 * If iface_name is "file", the arg argument will hold the filename to be
1252 * used for logging purposes. If iface_name is "syslog", the arg
1253 * argument holds the facility code. If iface_name is "console", arg is
1254 * NULL.
1255 *
1256 * \param iface_name Interface name. Can be "console", "file" or "syslog"
1257 * \param log_format Override for the global_log_format
1258 * \param log_level Override for the global_log_level
1259 * \param log_level Parameter required by a particular interface. Explained in
1260 * the function description
1261 *
1262 * \retval iface_ctx Pointer to the newly created output interface
1263 */
SCLogInitOPIfaceCtx(const char * iface_name,const char * log_format,int log_level,const char * arg)1264 SCLogOPIfaceCtx *SCLogInitOPIfaceCtx(const char *iface_name,
1265 const char *log_format,
1266 int log_level, const char *arg)
1267 {
1268 int iface = SCMapEnumNameToValue(iface_name, sc_log_op_iface_map);
1269
1270 if (log_level < SC_LOG_NONE || log_level > SC_LOG_DEBUG) {
1271 #ifndef UNITTESTS
1272 printf("Warning: Supplied log_level_override for op_interface \"%s\" "
1273 "is invalid. Defaulting to not specifying an override\n",
1274 iface_name);
1275 #endif
1276 log_level = SC_LOG_NOTSET;
1277 }
1278
1279 switch (iface) {
1280 case SC_LOG_OP_IFACE_CONSOLE:
1281 return SCLogInitConsoleOPIface(log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1282 case SC_LOG_OP_IFACE_FILE:
1283 return SCLogInitFileOPIface(arg, log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1284 case SC_LOG_OP_IFACE_SYSLOG:
1285 return SCLogInitSyslogOPIface(SCMapEnumNameToValue(arg, SCSyslogGetFacilityMap()),
1286 log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1287 default:
1288 #ifdef DEBUG
1289 printf("Output Interface \"%s\" not supported by the logging module",
1290 iface_name);
1291 #endif
1292 return NULL;
1293 }
1294 }
1295
1296 /**
1297 * \brief Initializes the logging module.
1298 *
1299 * \param sc_lid The initialization data for the logging module. If sc_lid is
1300 * NULL, we would stick to the default configuration for the
1301 * logging subsystem.
1302 * \initonly
1303 */
SCLogInitLogModule(SCLogInitData * sc_lid)1304 void SCLogInitLogModule(SCLogInitData *sc_lid)
1305 {
1306 /* De-initialize the logging context, if it has already init by the
1307 * environment variables at the start of the engine */
1308 SCLogDeInitLogModule();
1309
1310 #if defined (OS_WIN32)
1311 if (SCMutexInit(&sc_log_stream_lock, NULL) != 0) {
1312 FatalError(SC_ERR_FATAL, "Failed to initialize log mutex.");
1313 }
1314 #endif /* OS_WIN32 */
1315
1316 /* sc_log_config is a global variable */
1317 if ( (sc_log_config = SCMalloc(sizeof(SCLogConfig))) == NULL) {
1318 FatalError(SC_ERR_FATAL,
1319 "Fatal error encountered in SCLogInitLogModule. Exiting...");
1320 }
1321 memset(sc_log_config, 0, sizeof(SCLogConfig));
1322
1323 SCLogSetLogLevel(sc_lid, sc_log_config);
1324 SCLogSetLogFormat(sc_lid, sc_log_config);
1325 SCLogSetOPIface(sc_lid, sc_log_config);
1326 SCLogSetOPFilter(sc_lid, sc_log_config);
1327
1328 sc_log_module_initialized = 1;
1329 sc_log_module_cleaned = 0;
1330
1331 //SCOutputPrint(sc_did->startup_message);
1332
1333 rs_log_set_level(sc_log_global_log_level);
1334 return;
1335 }
1336
SCLogLoadConfig(int daemon,int verbose)1337 void SCLogLoadConfig(int daemon, int verbose)
1338 {
1339 ConfNode *outputs;
1340 SCLogInitData *sc_lid;
1341 int have_logging = 0;
1342 int max_level = 0;
1343 SCLogLevel min_level = 0;
1344
1345 /* If verbose logging was requested, set the minimum as
1346 * SC_LOG_NOTICE plus the extra verbosity. */
1347 if (verbose) {
1348 min_level = SC_LOG_NOTICE + verbose;
1349 }
1350
1351 outputs = ConfGetNode("logging.outputs");
1352 if (outputs == NULL) {
1353 SCLogDebug("No logging.output configuration section found.");
1354 return;
1355 }
1356
1357 sc_lid = SCLogAllocLogInitData();
1358 if (sc_lid == NULL) {
1359 SCLogDebug("Could not allocate memory for log init data");
1360 return;
1361 }
1362
1363 /* Get default log level and format. */
1364 const char *default_log_level_s = NULL;
1365 if (ConfGet("logging.default-log-level", &default_log_level_s) == 1) {
1366 SCLogLevel default_log_level =
1367 SCMapEnumNameToValue(default_log_level_s, sc_log_level_map);
1368 if (default_log_level == -1) {
1369 SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid default log level: %s",
1370 default_log_level_s);
1371 exit(EXIT_FAILURE);
1372 }
1373 sc_lid->global_log_level = MAX(min_level, default_log_level);
1374 }
1375 else {
1376 sc_lid->global_log_level = MAX(min_level, SC_LOG_NOTICE);
1377 }
1378
1379 if (ConfGet("logging.default-log-format", &sc_lid->global_log_format) != 1)
1380 sc_lid->global_log_format = SCLogGetDefaultLogFormat();
1381
1382 (void)ConfGet("logging.default-output-filter", &sc_lid->op_filter);
1383
1384 ConfNode *seq_node, *output;
1385 TAILQ_FOREACH(seq_node, &outputs->head, next) {
1386 SCLogLevel level = sc_lid->global_log_level;
1387 SCLogOPIfaceCtx *op_iface_ctx = NULL;
1388 const char *format;
1389 const char *level_s;
1390
1391 output = ConfNodeLookupChild(seq_node, seq_node->val);
1392 if (output == NULL)
1393 continue;
1394
1395 /* By default an output is enabled. */
1396 const char *enabled = ConfNodeLookupChildValue(output, "enabled");
1397 if (enabled != NULL && ConfValIsFalse(enabled))
1398 continue;
1399
1400 SCLogOPType type = SC_LOG_OP_TYPE_REGULAR;
1401 const char *type_s = ConfNodeLookupChildValue(output, "type");
1402 if (type_s != NULL) {
1403 if (strcmp(type_s, "regular") == 0)
1404 type = SC_LOG_OP_TYPE_REGULAR;
1405 else if (strcmp(type_s, "json") == 0) {
1406 type = SC_LOG_OP_TYPE_JSON;
1407 }
1408 }
1409
1410 /* if available use the log format setting for this output,
1411 * otherwise fall back to the global setting. */
1412 format = ConfNodeLookupChildValue(output, "format");
1413 if (format == NULL)
1414 format = sc_lid->global_log_format;
1415
1416 level_s = ConfNodeLookupChildValue(output, "level");
1417 if (level_s != NULL) {
1418 level = SCMapEnumNameToValue(level_s, sc_log_level_map);
1419 if (level == -1) {
1420 SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid log level: %s",
1421 level_s);
1422 exit(EXIT_FAILURE);
1423 }
1424 max_level = MAX(max_level, level);
1425 }
1426
1427 /* Increase the level of extra verbosity was requested. */
1428 level = MAX(min_level, level);
1429
1430 if (strcmp(output->name, "console") == 0) {
1431 op_iface_ctx = SCLogInitConsoleOPIface(format, level, type);
1432 }
1433 else if (strcmp(output->name, "file") == 0) {
1434 const char *filename = ConfNodeLookupChildValue(output, "filename");
1435 if (filename == NULL) {
1436 FatalError(SC_ERR_FATAL,
1437 "Logging to file requires a filename");
1438 }
1439 char *path = NULL;
1440 if (!(PathIsAbsolute(filename))) {
1441 path = SCLogGetLogFilename(filename);
1442 } else {
1443 path = SCStrdup(filename);
1444 }
1445 if (path == NULL)
1446 FatalError(SC_ERR_FATAL, "failed to setup output to file");
1447 have_logging = 1;
1448 op_iface_ctx = SCLogInitFileOPIface(path, format, level, type);
1449 SCFree(path);
1450 }
1451 else if (strcmp(output->name, "syslog") == 0) {
1452 int facility = SC_LOG_DEF_SYSLOG_FACILITY;
1453 const char *facility_s = ConfNodeLookupChildValue(output,
1454 "facility");
1455 if (facility_s != NULL) {
1456 facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
1457 if (facility == -1) {
1458 SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog "
1459 "facility: \"%s\", now using \"%s\" as syslog "
1460 "facility", facility_s, SC_LOG_DEF_SYSLOG_FACILITY_STR);
1461 facility = SC_LOG_DEF_SYSLOG_FACILITY;
1462 }
1463 }
1464 SCLogDebug("Initializing syslog logging with format \"%s\"", format);
1465 have_logging = 1;
1466 op_iface_ctx = SCLogInitSyslogOPIface(facility, format, level, type);
1467 }
1468 else {
1469 SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid logging method: %s, "
1470 "ignoring", output->name);
1471 }
1472 if (op_iface_ctx != NULL) {
1473 SCLogAppendOPIfaceCtx(op_iface_ctx, sc_lid);
1474 }
1475 }
1476
1477 if (daemon && (have_logging == 0)) {
1478 SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
1479 "NO logging compatible with daemon mode selected,"
1480 " suricata won't be able to log. Please update "
1481 " 'logging.outputs' in the YAML.");
1482 }
1483
1484 /* Set the global log level to that of the max level used. */
1485 sc_lid->global_log_level = MAX(sc_lid->global_log_level, max_level);
1486 SCLogInitLogModule(sc_lid);
1487
1488 SCLogDebug("sc_log_global_log_level: %d", sc_log_global_log_level);
1489 SCLogDebug("sc_lc->log_format: %s", sc_log_config->log_format);
1490 SCLogDebug("SCLogSetOPFilter: filter: %s", sc_log_config->op_filter);
1491
1492 if (sc_lid != NULL)
1493 SCFree(sc_lid);
1494 }
1495
1496 /**
1497 * \brief Returns a full file path given a filename uses log dir specified in
1498 * conf or DEFAULT_LOG_DIR
1499 *
1500 * \param filearg The relative filename for which we want a full path include
1501 * log directory
1502 *
1503 * \retval log_filename The fullpath of the logfile to open
1504 */
SCLogGetLogFilename(const char * filearg)1505 static char *SCLogGetLogFilename(const char *filearg)
1506 {
1507 const char *log_dir = ConfigGetLogDirectory();
1508 char *log_filename = SCMalloc(PATH_MAX);
1509 if (unlikely(log_filename == NULL))
1510 return NULL;
1511 snprintf(log_filename, PATH_MAX, "%s/%s", log_dir, filearg);
1512 return log_filename;
1513 }
1514
1515 /**
1516 * \brief De-Initializes the logging module
1517 */
SCLogDeInitLogModule(void)1518 void SCLogDeInitLogModule(void)
1519 {
1520 SCLogFreeLogConfig(sc_log_config);
1521
1522 /* reset the global logging_module variables */
1523 sc_log_global_log_level = 0;
1524 sc_log_module_initialized = 0;
1525 sc_log_module_cleaned = 1;
1526 sc_log_config = NULL;
1527
1528 /* de-init the FD filters */
1529 SCLogReleaseFDFilters();
1530 /* de-init the FG filters */
1531 SCLogReleaseFGFilters();
1532
1533 #if defined (OS_WIN32)
1534 SCMutexDestroy(&sc_log_stream_lock);
1535 #endif /* OS_WIN32 */
1536
1537 return;
1538 }
1539
1540 //------------------------------------Unit_Tests--------------------------------
1541
1542 /* The logging engine should be tested to the maximum extent possible, since
1543 * logging code would be used throughout the codebase, and hence we can't afford
1544 * to have a single bug here(not that you can afford to have a bug
1545 * elsewhere ;) ). Please report a bug, if you get a slightest hint of a bug
1546 * from the logging module.
1547 */
1548
1549 #ifdef UNITTESTS
1550
SCLogTestInit01(void)1551 static int SCLogTestInit01(void)
1552 {
1553 #ifndef OS_WIN32
1554 /* unset any environment variables set for the logging module */
1555 unsetenv(SC_LOG_ENV_LOG_LEVEL);
1556 unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
1557 unsetenv(SC_LOG_ENV_LOG_FORMAT);
1558
1559 SCLogInitLogModule(NULL);
1560
1561 FAIL_IF_NULL(sc_log_config);
1562
1563 FAIL_IF_NOT(SC_LOG_DEF_LOG_LEVEL == sc_log_config->log_level);
1564 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1565 SC_LOG_DEF_LOG_OP_IFACE == sc_log_config->op_ifaces->iface);
1566 FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1567 strcmp(SCLogGetDefaultLogFormat(), sc_log_config->log_format) == 0);
1568
1569 SCLogDeInitLogModule();
1570
1571 setenv(SC_LOG_ENV_LOG_LEVEL, "Debug", 1);
1572 setenv(SC_LOG_ENV_LOG_OP_IFACE, "Console", 1);
1573 setenv(SC_LOG_ENV_LOG_FORMAT, "%n- %l", 1);
1574
1575 SCLogInitLogModule(NULL);
1576
1577 FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1578 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1579 SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
1580 FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1581 !strcmp("%n- %l", sc_log_config->log_format));
1582
1583 unsetenv(SC_LOG_ENV_LOG_LEVEL);
1584 unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
1585 unsetenv(SC_LOG_ENV_LOG_FORMAT);
1586
1587 SCLogDeInitLogModule();
1588 #endif
1589 PASS;
1590 }
1591
SCLogTestInit02(void)1592 static int SCLogTestInit02(void)
1593 {
1594 #ifndef OS_WIN32
1595 SCLogInitData *sc_lid = NULL;
1596 SCLogOPIfaceCtx *sc_iface_ctx = NULL;
1597 char *logfile = SCLogGetLogFilename("boo.txt");
1598 sc_lid = SCLogAllocLogInitData();
1599 FAIL_IF_NULL(sc_lid);
1600 sc_lid->startup_message = "Test02";
1601 sc_lid->global_log_level = SC_LOG_DEBUG;
1602 sc_lid->op_filter = "boo";
1603 sc_iface_ctx = SCLogInitOPIfaceCtx("file", "%m - %d", SC_LOG_ALERT,
1604 logfile);
1605 SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
1606 sc_iface_ctx = SCLogInitOPIfaceCtx("console", NULL, SC_LOG_ERROR,
1607 NULL);
1608 SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
1609
1610 SCLogInitLogModule(sc_lid);
1611
1612 FAIL_IF_NULL(sc_log_config);
1613
1614 FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1615 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1616 SC_LOG_OP_IFACE_FILE == sc_log_config->op_ifaces->iface);
1617 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1618 sc_log_config->op_ifaces->next != NULL &&
1619 SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->next->iface);
1620 FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1621 strcmp(SCLogGetDefaultLogFormat(), sc_log_config->log_format) == 0);
1622 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1623 sc_log_config->op_ifaces->log_format != NULL &&
1624 strcmp("%m - %d", sc_log_config->op_ifaces->log_format) == 0);
1625 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1626 sc_log_config->op_ifaces->next != NULL &&
1627 sc_log_config->op_ifaces->next->log_format == NULL);
1628
1629 SCLogFreeLogInitData(sc_lid);
1630 SCLogDeInitLogModule();
1631
1632 sc_lid = SCLogAllocLogInitData();
1633 FAIL_IF_NULL(sc_lid);
1634 sc_lid->startup_message = "Test02";
1635 sc_lid->global_log_level = SC_LOG_DEBUG;
1636 sc_lid->op_filter = "boo";
1637 sc_lid->global_log_format = "kaboo";
1638
1639 SCLogInitLogModule(sc_lid);
1640
1641 FAIL_IF_NULL(sc_log_config);
1642
1643 FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1644 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1645 SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
1646 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1647 sc_log_config->op_ifaces->next == NULL);
1648 FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1649 strcmp("kaboo", sc_log_config->log_format) == 0);
1650 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1651 sc_log_config->op_ifaces->log_format == NULL);
1652 FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1653 sc_log_config->op_ifaces->next == NULL);
1654
1655 SCLogFreeLogInitData(sc_lid);
1656 SCLogDeInitLogModule();
1657 SCFree(logfile);
1658 #endif
1659 PASS;
1660 }
1661
SCLogTestInit03(void)1662 static int SCLogTestInit03(void)
1663 {
1664 SCLogInitLogModule(NULL);
1665
1666 SCLogAddFGFilterBL(NULL, "bamboo", -1);
1667 SCLogAddFGFilterBL(NULL, "soo", -1);
1668 SCLogAddFGFilterBL(NULL, "dummy", -1);
1669
1670 FAIL_IF_NOT(SCLogPrintFGFilters() == 3);
1671
1672 SCLogAddFGFilterBL(NULL, "dummy1", -1);
1673 SCLogAddFGFilterBL(NULL, "dummy2", -1);
1674
1675 FAIL_IF_NOT(SCLogPrintFGFilters() == 5);
1676
1677 SCLogDeInitLogModule();
1678
1679 PASS;
1680 }
1681
SCLogTestInit04(void)1682 static int SCLogTestInit04(void)
1683 {
1684 SCLogInitLogModule(NULL);
1685
1686 SCLogAddFDFilter("bamboo");
1687 SCLogAddFDFilter("soo");
1688 SCLogAddFDFilter("foo");
1689 SCLogAddFDFilter("roo");
1690
1691 FAIL_IF_NOT(SCLogPrintFDFilters() == 4);
1692
1693 SCLogAddFDFilter("loo");
1694 SCLogAddFDFilter("soo");
1695
1696 FAIL_IF_NOT(SCLogPrintFDFilters() == 5);
1697
1698 SCLogRemoveFDFilter("bamboo");
1699 SCLogRemoveFDFilter("soo");
1700 SCLogRemoveFDFilter("foo");
1701 SCLogRemoveFDFilter("noo");
1702
1703 FAIL_IF_NOT(SCLogPrintFDFilters() == 2);
1704
1705 SCLogDeInitLogModule();
1706
1707 PASS;
1708 }
1709
SCLogTestInit05(void)1710 static int SCLogTestInit05(void)
1711 {
1712 char str[4096];
1713 memset(str, 'A', sizeof(str));
1714 SCLogInfo("%s", str);
1715
1716 PASS;
1717 }
1718
1719 #endif /* UNITTESTS */
1720
SCLogRegisterTests()1721 void SCLogRegisterTests()
1722 {
1723
1724 #ifdef UNITTESTS
1725
1726 UtRegisterTest("SCLogTestInit01", SCLogTestInit01);
1727 UtRegisterTest("SCLogTestInit02", SCLogTestInit02);
1728 UtRegisterTest("SCLogTestInit03", SCLogTestInit03);
1729 UtRegisterTest("SCLogTestInit04", SCLogTestInit04);
1730 UtRegisterTest("SCLogTestInit05", SCLogTestInit05);
1731
1732 #endif /* UNITTESTS */
1733
1734 return;
1735 }
1736