39 #if defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SYS_TYPES_H)
40 #define BUILD_WITH_UNIXSOCKET
41 #include <sys/types.h>
42 #include <sys/socket.h>
46 #ifdef HAVE_LIBHIREDIS
50 #define LOGFILE_NAME_MAX 255
52 static bool LogFileNewThreadedCtx(
LogFileCtx *parent_ctx,
const char *log_path,
const char *append,
62 #ifdef BUILD_WITH_UNIXSOCKET
70 SCLogOpenUnixSocketFp(
const char *path,
int sock_type,
int log_err)
72 struct sockaddr_un saun;
76 memset(&saun, 0x00,
sizeof(saun));
78 s = socket(PF_UNIX, sock_type, 0);
81 saun.sun_family = AF_UNIX;
82 strlcpy(saun.sun_path, path,
sizeof(saun.sun_path));
84 if (connect(s, (
const struct sockaddr *)&saun,
sizeof(saun)) < 0)
96 "Error connecting to socket \"%s\": %s (will keep trying)", path, strerror(errno));
108 static int SCLogUnixSocketReconnect(
LogFileCtx *log_ctx)
110 int disconnected = 0;
122 gettimeofday(&
tv, NULL);
123 now = (uint64_t)
tv.tv_sec * 1000;
124 now +=
tv.tv_usec / 1000;
136 }
else if (disconnected) {
137 SCLogWarning(
"Reconnect failed: %s (will keep trying)", strerror(errno));
140 return log_ctx->
fp ? 1 : 0;
143 static int SCLogFileWriteSocket(
const char *buffer,
int buffer_len,
149 if (
ctx->fp == NULL &&
ctx->is_sock) {
150 SCLogUnixSocketReconnect(
ctx);
156 if (
ctx->fp != NULL) {
157 int fd = fileno(
ctx->fp);
158 ssize_t size = send(fd, buffer, buffer_len,
ctx->send_flags);
162 if (errno == EAGAIN || errno == EWOULDBLOCK) {
163 SCLogDebug(
"Socket would block, dropping event.");
164 }
else if (errno == EINTR) {
166 SCLogDebug(
"Interrupted system call, trying again.");
169 SCLogDebug(
"Too many interrupted system calls, "
173 SCLogDebug(
"Send failed: %s", strerror(errno));
179 if (reopen && tries++ == 0) {
180 if (SCLogUnixSocketReconnect(
ctx)) {
192 static inline void OutputWriteLock(pthread_mutex_t *
m)
201 static void SCLogFileFlushNoLock(
LogFileCtx *log_ctx)
207 static void SCLogFileFlush(
LogFileCtx *log_ctx)
209 OutputWriteLock(&log_ctx->
fp_mutex);
210 SCLogFileFlushNoLock(log_ctx);
224 const int64_t day = (int64_t)tm->tm_year * 366 + tm->tm_yday;
225 const int64_t hour = day * 24 + tm->tm_hour;
226 const int64_t minute = hour * 60 + tm->tm_min;
243 static void LogFileRotateArm(
LogFileCtx *log_ctx, time_t now)
267 static bool LogFileRotateUpdate(
LogFileCtx *log_ctx, time_t now)
269 if (now < log_ctx->rotate_time) {
279 LogFileRotateArm(log_ctx, now);
288 static bool HandleLogRotation(
LogFileCtx *log_ctx)
293 LogFileRotateArm(log_ctx, time(NULL));
298 if (LogFileRotateUpdate(log_ctx, time(NULL))) {
312 static int SCLogFileWriteNoLock(
const char *buffer,
int buffer_len,
LogFileCtx *log_ctx)
318 HandleLogRotation(log_ctx);
338 SCLogFileFlushNoLock(log_ctx);
352 static void SCLogFileRotate(
LogFileCtx *log_ctx)
354 OutputWriteLock(&log_ctx->
fp_mutex);
355 HandleLogRotation(log_ctx);
364 static int SCLogFileWrite(
const char *buffer,
int buffer_len,
LogFileCtx *log_ctx)
366 OutputWriteLock(&log_ctx->
fp_mutex);
369 #ifdef BUILD_WITH_UNIXSOCKET
371 ret = SCLogFileWriteSocket(buffer, buffer_len, log_ctx);
375 ret = SCLogFileWriteNoLock(buffer, buffer_len, log_ctx);
388 static char *SCLogFilenameFromPattern(
const char *pattern)
390 char *filename =
SCMalloc(PATH_MAX);
391 if (filename == NULL) {
404 static void SCLogFileCloseNoLock(
LogFileCtx *log_ctx)
419 static void SCLogFileClose(
LogFileCtx *log_ctx)
422 SCLogFileCloseNoLock(log_ctx);
426 static char ThreadLogFileHashCompareFunc(
427 void *data1, uint16_t datalen1,
void *data2, uint16_t datalen2)
432 if (p1 == NULL || p2 == NULL)
437 static uint32_t ThreadLogFileHashFunc(
HashTable *ht,
void *data, uint16_t datalen)
444 static void ThreadLogFileHashFreeFunc(
void *data)
466 SCLogError(
"Unable to allocate threads container");
471 ThreadLogFileHashCompareFunc, ThreadLogFileHashFreeFunc);
473 FatalError(
"Unable to initialize thread/entry hash table");
478 SCLogError(
"Unable to allocate threads append setting");
505 static FILE *SCLogOpenFileFp(
506 const char *path,
const char *append_setting, uint32_t mode,
const uint32_t buffer_size)
510 char *filename = SCLogFilenameFromPattern(path);
511 if (filename == NULL) {
522 ret = fopen(filename,
"a");
524 ret = fopen(filename,
"w");
528 SCLogError(
"Error opening file: \"%s\": %s", filename, strerror(errno));
533 int r = _chmod(filename, (mode_t)mode);
535 int r = fchmod(fileno(ret), (mode_t)mode);
538 SCLogWarning(
"Could not chmod %s to %o: %s", filename, mode, strerror(errno));
544 if (buffer_size == 0) {
546 SCLogConfig(
"Setting output to %s non-buffered", filename);
548 if (setvbuf(ret, NULL, _IOFBF, buffer_size) < 0)
549 FatalError(
"unable to set %s to buffered: %d", filename, buffer_size);
550 SCLogConfig(
"Setting output to %s buffered [limit %d bytes]", filename, buffer_size);
570 char log_path[PATH_MAX];
572 const char *filename, *filetype;
575 if (conf == NULL || log_ctx == NULL || default_filename == NULL) {
576 SCLogError(
"SCConfLogOpenGeneric(conf %p, ctx %p, default %p) "
577 "missing an argument",
578 conf, log_ctx, default_filename);
581 if (log_ctx->
fp != NULL) {
582 SCLogError(
"SCConfLogOpenGeneric: previously initialized Log CTX "
589 if (filename == NULL)
590 filename = default_filename;
595 snprintf(log_path, PATH_MAX,
"%s", filename);
597 snprintf(log_path, PATH_MAX,
"%s/%s", log_dir, filename);
608 if (rotate_int != NULL) {
610 if (strcmp(rotate_int,
"minute") == 0) {
612 }
else if (strcmp(rotate_int,
"hour") == 0) {
614 }
else if (strcmp(rotate_int,
"day") == 0) {
626 LogFileRotateArm(log_ctx, time(NULL));
630 if (filetype == NULL)
639 if (buffer_size_value != NULL) {
643 "buffer-size - %s. Killing engine",
649 SCLogDebug(
"buffering: %s -> %d", buffer_size_value, buffer_size);
652 if (filemode != NULL &&
StringParseUint32(&mode, 8, (uint16_t)strlen(filemode), filemode) > 0) {
661 log_ctx->
json_flags = JSON_PRESERVE_ORDER|JSON_COMPACT|
666 if (json_flags != 0) {
669 log_ctx->
json_flags &= ~(JSON_PRESERVE_ORDER);
684 #ifdef BUILD_WITH_UNIXSOCKET
686 if (strcasecmp(filetype,
"unix_stream") == 0 || strcasecmp(filetype,
"unix_dgram") == 0) {
687 FatalError(
"Socket file types do not support threaded output");
692 SCLogConfig(
"buffering setting ignored for %s output types", filetype);
696 if (strcasecmp(filetype,
"unix_stream") == 0) {
697 #ifdef BUILD_WITH_UNIXSOCKET
701 log_ctx->
fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1);
705 }
else if (strcasecmp(filetype,
"unix_dgram") == 0) {
706 #ifdef BUILD_WITH_UNIXSOCKET
710 log_ctx->
fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1);
715 strcasecmp(filetype,
"file") == 0) {
721 if (log_ctx->
fp == NULL)
737 "%s.filetype. Expected \"regular\" (default), \"unix_stream\", "
743 SCLogError(
"Failed to allocate memory for filename");
747 #ifdef BUILD_WITH_UNIXSOCKET
750 SCLogInfo(
"Setting logging socket of non-blocking in live mode.");
754 SCLogInfo(
"%s output device (%s) initialized: %s", conf->
name, filetype,
775 SCLogWarning(
"Can't re-open LogFileCtx without a filename.");
779 if (log_ctx->
fp != NULL) {
788 if (log_ctx->
fp == NULL) {
806 lf_ctx->
Write = SCLogFileWrite;
807 lf_ctx->
Close = SCLogFileClose;
808 lf_ctx->
Flush = SCLogFileFlush;
809 lf_ctx->
Rotate = SCLogFileRotate;
833 FatalError(
"Unable to allocate thread/hash-entry entry");
840 FatalError(
"Unable to add thread/hash-entry mapping");
860 SCLogDebug(
"%s: Adding reference for thread %" PRIi64
861 " (local thread id %d) to file %s [ctx %p]",
868 thread_id, parent_ctx->
filename, parent_ctx);
869 if (LogFileNewThreadedCtx(
872 ret_ctx = entry->
ctx;
879 ret_ctx = entry->
ctx;
885 SCLogDebug(
"Existing file for thread/entry %p reference to file %s [ctx %p]", entry,
896 static bool LogFileThreadedName(
897 const char *original_name,
char *threaded_name,
size_t len, uint32_t unique_id)
901 if (strcmp(
"/dev/null", original_name) == 0) {
908 FatalError(
"Invalid filename for threaded mode \"%s\"; "
909 "no basename found.",
914 const char *dot = strrchr(base,
'.');
916 char *tname =
SCStrdup(original_name);
925 dot = strrchr(original_name,
'.');
926 ptrdiff_t dotpos = dot - original_name;
927 tname[dotpos] =
'\0';
928 char *ext = tname + dotpos + 1;
929 if (strlen(tname) && strlen(ext)) {
930 snprintf(threaded_name,
len,
"%s.%u.%s", tname, unique_id, ext);
932 FatalError(
"Invalid filename for threaded mode \"%s\"; "
933 "filenames must include an extension, e.g: \"name.ext\"",
938 snprintf(threaded_name,
len,
"%s.%u", original_name, unique_id);
949 static bool LogFileNewThreadedCtx(
LogFileCtx *parent_ctx,
const char *log_path,
const char *append,
954 SCLogError(
"Unable to allocate thread file context entry %p", entry);
958 *thread = *parent_ctx;
962 if (!LogFileThreadedName(log_path, fname,
sizeof(fname), entry->
slot_number)) {
963 SCLogError(
"Unable to create threaded filename for log");
966 SCLogDebug(
"%s: thread open -- using name %s [replaces %s] - thread %d [slot %d]",
969 if (thread->
fp == NULL) {
974 SCLogError(
"Unable to duplicate filename for context entry %p", entry);
978 thread->
Write = SCLogFileWrite;
979 thread->
Close = SCLogFileClose;
980 thread->
Rotate = SCLogFileRotate;
991 thread->
parent = parent_ctx;
992 thread->
entry = entry;
1001 thread->
Close(thread);
1017 if (lf_ctx == NULL) {
1042 if (lf_ctx->
fp != NULL) {
1043 lf_ctx->
Close(lf_ctx);
1049 if (lf_ctx->
parent == NULL) {
1050 if (lf_ctx->
prefix != NULL) {
1074 #ifdef HAVE_LIBHIREDIS
1076 if (lf_ctx->redis_setup.stream_format != NULL) {
1077 SCFree(lf_ctx->redis_setup.stream_format);
1082 memset(lf_ctx, 0,
sizeof(*lf_ctx));
1091 file_ctx->
Flush(file_ctx);
1109 if (entry == NULL) {
1110 SCLogError(
"Unable to allocate memory for log file entry");
1156 if (entry->
ctx != NULL) {
1174 if (entry->
ctx != NULL && entry->
ctx->
Rotate != NULL) {
1194 #ifdef HAVE_LIBHIREDIS
1207 #if defined(UNITTESTS) && !defined(OS_WIN32)
1219 static int LogFileRotateTestAroundSwitch(
1222 const time_t start = dst_switch - 3600;
1223 const time_t end = dst_switch + 26 * 3600;
1226 memset(&
ctx, 0,
sizeof(
ctx));
1227 ctx.rotate_unit = unit;
1228 LogFileRotateArm(&
ctx, start);
1234 for (time_t quarter = start + 900; quarter < end; quarter += 900) {
1235 for (time_t now = quarter - 5; now < quarter + 5; now++) {
1236 char expected_name[32];
1238 const bool name_changed = strcmp(expected_name, file_name) != 0;
1240 const bool rotated = LogFileRotateUpdate(&
ctx, now);
1241 FAIL_IF(rotated != name_changed);
1244 strlcpy(file_name, expected_name,
sizeof(file_name));
1255 static void LogFileRotateTestSetTimezone(
const char *tz)
1267 static int LogFileRotateTestTimezone(
const char *tz, time_t dst_start, time_t dst_end)
1271 const char *pattern;
1278 LogFileRotateTestSetTimezone(tz);
1279 for (
size_t u = 0; u <
ARRAY_SIZE(units); u++) {
1280 FAIL_IF_NOT(LogFileRotateTestAroundSwitch(units[u].unit, units[u].pattern, dst_start) == 1);
1281 FAIL_IF_NOT(LogFileRotateTestAroundSwitch(units[u].unit, units[u].pattern, dst_end) == 1);
1288 static int LogFileRotateTest01(
void)
1297 {
"EST5EDT,M3.2.0,M11.1.0", 1772953200, 1793512800 },
1299 {
"CST5CDT,M3.2.0/0,M11.1.0/1", 1772946000, 1793509200 },
1301 {
"CET-1CEST,M3.5.0,M10.5.0/3", 1774746000, 1792890000 },
1303 {
"AEST-10AEDT,M10.1.0,M4.1.0/3", 1791043200, 1775318400 },
1305 {
"<+1030>-10:30<+11>-11,M10.1.0,M4.1.0", 1791041400, 1775314800 },
1307 {
"<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45", 1790431200, 1775311200 },
1310 const char *env_tz = getenv(
"TZ");
1311 char *orig_tz = env_tz != NULL ?
SCStrdup(env_tz) : NULL;
1312 FAIL_IF(env_tz != NULL && orig_tz == NULL);
1315 for (
size_t i = 0; i <
ARRAY_SIZE(timezones); i++) {
1316 if (LogFileRotateTestTimezone(
1317 timezones[i].tz, timezones[i].dst_start, timezones[i].dst_end) != 1) {
1324 LogFileRotateTestSetTimezone(orig_tz);
1333 #if defined(UNITTESTS) && !defined(OS_WIN32)