Coverage Report

Created: 2026-01-16 07:00

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