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) {
525 SCLogError(
"Error opening file: \"%s\": %s", filename, strerror(errno));
530 int r = _chmod(filename, (mode_t)mode);
532 int r = fchmod(fileno(ret), (mode_t)mode);
535 SCLogWarning(
"Could not chmod %s to %o: %s", filename, mode, strerror(errno));
541 if (buffer_size == 0) {
543 SCLogConfig(
"Setting output to %s non-buffered", filename);
545 if (setvbuf(ret, NULL, _IOFBF, buffer_size) < 0)
546 FatalError(
"unable to set %s to buffered: %d", filename, buffer_size);
547 SCLogConfig(
"Setting output to %s buffered [limit %d bytes]", filename, buffer_size);
567 char log_path[PATH_MAX];
569 const char *filename, *filetype;
572 if (conf == NULL || log_ctx == NULL || default_filename == NULL) {
573 SCLogError(
"SCConfLogOpenGeneric(conf %p, ctx %p, default %p) "
574 "missing an argument",
575 conf, log_ctx, default_filename);
578 if (log_ctx->
fp != NULL) {
579 SCLogError(
"SCConfLogOpenGeneric: previously initialized Log CTX "
586 if (filename == NULL)
587 filename = default_filename;
592 snprintf(log_path, PATH_MAX,
"%s", filename);
594 snprintf(log_path, PATH_MAX,
"%s/%s", log_dir, filename);
605 if (rotate_int != NULL) {
607 if (strcmp(rotate_int,
"minute") == 0) {
609 }
else if (strcmp(rotate_int,
"hour") == 0) {
611 }
else if (strcmp(rotate_int,
"day") == 0) {
623 LogFileRotateArm(log_ctx, time(NULL));
627 if (filetype == NULL)
636 if (buffer_size_value != NULL) {
640 "buffer-size - %s. Killing engine",
646 SCLogDebug(
"buffering: %s -> %d", buffer_size_value, buffer_size);
649 if (filemode != NULL &&
StringParseUint32(&mode, 8, (uint16_t)strlen(filemode), filemode) > 0) {
658 log_ctx->
json_flags = JSON_PRESERVE_ORDER|JSON_COMPACT|
663 if (json_flags != 0) {
666 log_ctx->
json_flags &= ~(JSON_PRESERVE_ORDER);
681 #ifdef BUILD_WITH_UNIXSOCKET
683 if (strcasecmp(filetype,
"unix_stream") == 0 || strcasecmp(filetype,
"unix_dgram") == 0) {
684 FatalError(
"Socket file types do not support threaded output");
689 SCLogConfig(
"buffering setting ignored for %s output types", filetype);
693 if (strcasecmp(filetype,
"unix_stream") == 0) {
694 #ifdef BUILD_WITH_UNIXSOCKET
698 log_ctx->
fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1);
702 }
else if (strcasecmp(filetype,
"unix_dgram") == 0) {
703 #ifdef BUILD_WITH_UNIXSOCKET
707 log_ctx->
fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1);
712 strcasecmp(filetype,
"file") == 0) {
718 if (log_ctx->
fp == NULL)
734 "%s.filetype. Expected \"regular\" (default), \"unix_stream\", "
740 SCLogError(
"Failed to allocate memory for filename");
744 #ifdef BUILD_WITH_UNIXSOCKET
747 SCLogInfo(
"Setting logging socket of non-blocking in live mode.");
751 SCLogInfo(
"%s output device (%s) initialized: %s", conf->
name, filetype,
772 SCLogWarning(
"Can't re-open LogFileCtx without a filename.");
776 if (log_ctx->
fp != NULL) {
785 if (log_ctx->
fp == NULL) {
803 lf_ctx->
Write = SCLogFileWrite;
804 lf_ctx->
Close = SCLogFileClose;
805 lf_ctx->
Flush = SCLogFileFlush;
806 lf_ctx->
Rotate = SCLogFileRotate;
830 FatalError(
"Unable to allocate thread/hash-entry entry");
837 FatalError(
"Unable to add thread/hash-entry mapping");
857 SCLogDebug(
"%s: Adding reference for thread %" PRIi64
858 " (local thread id %d) to file %s [ctx %p]",
865 thread_id, parent_ctx->
filename, parent_ctx);
866 if (LogFileNewThreadedCtx(
869 ret_ctx = entry->
ctx;
876 ret_ctx = entry->
ctx;
882 SCLogDebug(
"Existing file for thread/entry %p reference to file %s [ctx %p]", entry,
893 static bool LogFileThreadedName(
894 const char *original_name,
char *threaded_name,
size_t len, uint32_t unique_id)
898 if (strcmp(
"/dev/null", original_name) == 0) {
905 FatalError(
"Invalid filename for threaded mode \"%s\"; "
906 "no basename found.",
911 const char *dot = strrchr(base,
'.');
913 char *tname =
SCStrdup(original_name);
922 dot = strrchr(original_name,
'.');
923 ptrdiff_t dotpos = dot - original_name;
924 tname[dotpos] =
'\0';
925 char *ext = tname + dotpos + 1;
926 if (strlen(tname) && strlen(ext)) {
927 snprintf(threaded_name,
len,
"%s.%u.%s", tname, unique_id, ext);
929 FatalError(
"Invalid filename for threaded mode \"%s\"; "
930 "filenames must include an extension, e.g: \"name.ext\"",
935 snprintf(threaded_name,
len,
"%s.%u", original_name, unique_id);
946 static bool LogFileNewThreadedCtx(
LogFileCtx *parent_ctx,
const char *log_path,
const char *append,
951 SCLogError(
"Unable to allocate thread file context entry %p", entry);
955 *thread = *parent_ctx;
959 if (!LogFileThreadedName(log_path, fname,
sizeof(fname), entry->
slot_number)) {
960 SCLogError(
"Unable to create threaded filename for log");
963 SCLogDebug(
"%s: thread open -- using name %s [replaces %s] - thread %d [slot %d]",
966 if (thread->
fp == NULL) {
971 SCLogError(
"Unable to duplicate filename for context entry %p", entry);
975 thread->
Write = SCLogFileWrite;
976 thread->
Close = SCLogFileClose;
977 thread->
Rotate = SCLogFileRotate;
988 thread->
parent = parent_ctx;
989 thread->
entry = entry;
998 thread->
Close(thread);
1014 if (lf_ctx == NULL) {
1039 if (lf_ctx->
fp != NULL) {
1040 lf_ctx->
Close(lf_ctx);
1046 if (lf_ctx->
parent == NULL) {
1047 if (lf_ctx->
prefix != NULL) {
1071 #ifdef HAVE_LIBHIREDIS
1073 if (lf_ctx->redis_setup.stream_format != NULL) {
1074 SCFree(lf_ctx->redis_setup.stream_format);
1079 memset(lf_ctx, 0,
sizeof(*lf_ctx));
1088 file_ctx->
Flush(file_ctx);
1106 if (entry == NULL) {
1107 SCLogError(
"Unable to allocate memory for log file entry");
1153 if (entry->
ctx != NULL) {
1171 if (entry->
ctx != NULL && entry->
ctx->
Rotate != NULL) {
1191 #ifdef HAVE_LIBHIREDIS
1204 #if defined(UNITTESTS) && !defined(OS_WIN32)
1216 static int LogFileRotateTestAroundSwitch(
1219 const time_t start = dst_switch - 3600;
1220 const time_t end = dst_switch + 26 * 3600;
1223 memset(&
ctx, 0,
sizeof(
ctx));
1224 ctx.rotate_unit = unit;
1225 LogFileRotateArm(&
ctx, start);
1231 for (time_t quarter = start + 900; quarter < end; quarter += 900) {
1232 for (time_t now = quarter - 5; now < quarter + 5; now++) {
1233 char expected_name[32];
1235 const bool name_changed = strcmp(expected_name, file_name) != 0;
1237 const bool rotated = LogFileRotateUpdate(&
ctx, now);
1238 FAIL_IF(rotated != name_changed);
1241 strlcpy(file_name, expected_name,
sizeof(file_name));
1252 static void LogFileRotateTestSetTimezone(
const char *tz)
1264 static int LogFileRotateTestTimezone(
const char *tz, time_t dst_start, time_t dst_end)
1268 const char *pattern;
1275 LogFileRotateTestSetTimezone(tz);
1276 for (
size_t u = 0; u <
ARRAY_SIZE(units); u++) {
1277 FAIL_IF_NOT(LogFileRotateTestAroundSwitch(units[u].unit, units[u].pattern, dst_start) == 1);
1278 FAIL_IF_NOT(LogFileRotateTestAroundSwitch(units[u].unit, units[u].pattern, dst_end) == 1);
1285 static int LogFileRotateTest01(
void)
1294 {
"EST5EDT,M3.2.0,M11.1.0", 1772953200, 1793512800 },
1296 {
"CST5CDT,M3.2.0/0,M11.1.0/1", 1772946000, 1793509200 },
1298 {
"CET-1CEST,M3.5.0,M10.5.0/3", 1774746000, 1792890000 },
1300 {
"AEST-10AEDT,M10.1.0,M4.1.0/3", 1791043200, 1775318400 },
1302 {
"<+1030>-10:30<+11>-11,M10.1.0,M4.1.0", 1791041400, 1775314800 },
1304 {
"<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45", 1790431200, 1775311200 },
1307 const char *env_tz = getenv(
"TZ");
1308 char *orig_tz = env_tz != NULL ?
SCStrdup(env_tz) : NULL;
1309 FAIL_IF(env_tz != NULL && orig_tz == NULL);
1312 for (
size_t i = 0; i <
ARRAY_SIZE(timezones); i++) {
1313 if (LogFileRotateTestTimezone(
1314 timezones[i].tz, timezones[i].dst_start, timezones[i].dst_end) != 1) {
1321 LogFileRotateTestSetTimezone(orig_tz);
1330 #if defined(UNITTESTS) && !defined(OS_WIN32)