void SCProfilingKeywordsGlobalInit(void) { ConfNode *conf; conf = ConfGetNode("profiling.keywords"); if (conf != NULL) { if (ConfNodeChildValueIsTrue(conf, "enabled")) { profiling_keyword_enabled = 1; const char *filename = ConfNodeLookupChildValue(conf, "filename"); if (filename != NULL) { char *log_dir; log_dir = ConfigGetLogDirectory(); profiling_file_name = SCMalloc(PATH_MAX); if (unlikely(profiling_file_name == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name"); exit(EXIT_FAILURE); } snprintf(profiling_file_name, PATH_MAX, "%s/%s", log_dir, filename); const char *v = ConfNodeLookupChildValue(conf, "append"); if (v == NULL || ConfValIsTrue(v)) { profiling_file_mode = "a"; } else { profiling_file_mode = "w"; } profiling_keywords_output_to_file = 1; } } } }
static int LuaCallbackLogPath(lua_State *luastate) { const char *ld = ConfigGetLogDirectory(); if (ld == NULL) return LuaCallbackError(luastate, "internal error: no log dir"); return LuaPushStringBuffer(luastate, (const uint8_t *)ld, strlen(ld)); }
/** * \brief Sets up the rule analyzer according to the config * \retval 1 if rule analyzer successfully enabled * \retval 0 if not enabled */ int SetupRuleAnalyzer(void) { ConfNode *conf = ConfGetNode("engine-analysis"); int enabled = 0; if (conf != NULL) { const char *value = ConfNodeLookupChildValue(conf, "rules"); if (value && ConfValIsTrue(value)) { enabled = 1; } else if (value && strcasecmp(value, "warnings-only") == 0) { enabled = 1; rule_warnings_only = 1; } if (enabled) { char *log_dir; log_dir = ConfigGetLogDirectory(); snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, "rules_analysis.txt"); rule_engine_analysis_FD = fopen(log_path, "w"); if (rule_engine_analysis_FD == NULL) { SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path, strerror(errno)); return 0; } SCLogInfo("Engine-Analysis for rules printed to file - %s", log_path); struct timeval tval; struct tm *tms; gettimeofday(&tval, NULL); struct tm local_tm; tms = SCLocalTime(tval.tv_sec, &local_tm); fprintf(rule_engine_analysis_FD, "----------------------------------------------" "---------------------\n"); fprintf(rule_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- " "%02d:%02d:%02d\n", tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec); fprintf(rule_engine_analysis_FD, "----------------------------------------------" "---------------------\n"); /*compile regex's for rule analysis*/ if (PerCentEncodingSetup()== 0) { fprintf(rule_engine_analysis_FD, "Error compiling regex; can't check for percent encoding in normalized http content.\n"); } } } else { SCLogInfo("Conf parameter \"engine-analysis.rules\" not found. " "Defaulting to not printing the rules analysis report."); } if (!enabled) { SCLogInfo("Engine-Analysis for rules disabled in conf file."); return 0; } return 1; }
/** \brief Create a new http log LogFilestoreCtx. * \param conf Pointer to ConfNode containing this loggers configuration. * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful * */ static OutputCtx *LogFilestoreLogInitCtx(ConfNode *conf) { LogFileCtx *logfile_ctx = LogFileNewCtx(); if (logfile_ctx == NULL) { SCLogDebug("Could not create new LogFilestoreCtx"); return NULL; } OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) return NULL; output_ctx->data = NULL; output_ctx->DeInit = LogFilestoreLogDeInitCtx; char *s_default_log_dir = NULL; s_default_log_dir = ConfigGetLogDirectory(); const char *s_base_dir = NULL; s_base_dir = ConfNodeLookupChildValue(conf, "log-dir"); if (s_base_dir == NULL || strlen(s_base_dir) == 0) { strlcpy(g_logfile_base_dir, s_default_log_dir, sizeof(g_logfile_base_dir)); } else { if (PathIsAbsolute(s_base_dir)) { strlcpy(g_logfile_base_dir, s_base_dir, sizeof(g_logfile_base_dir)); } else { snprintf(g_logfile_base_dir, sizeof(g_logfile_base_dir), "%s/%s", s_default_log_dir, s_base_dir); } } const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic"); if (force_magic != NULL && ConfValIsTrue(force_magic)) { FileForceMagicEnable(); SCLogInfo("forcing magic lookup for stored files"); } const char *force_md5 = ConfNodeLookupChildValue(conf, "force-md5"); if (force_md5 != NULL && ConfValIsTrue(force_md5)) { #ifdef HAVE_NSS FileForceMd5Enable(); SCLogInfo("forcing md5 calculation for stored files"); #else SCLogInfo("md5 calculation requires linking against libnss"); #endif } SCLogInfo("storing files in %s", g_logfile_base_dir); SCReturnPtr(output_ctx, "OutputCtx"); }
/** * \brief Sets up the fast pattern analyzer according to the config. * * \retval 1 If rule analyzer successfully enabled. * \retval 0 If not enabled. */ int SetupFPAnalyzer(void) { int fp_engine_analysis_set = 0; if ((ConfGetBool("engine-analysis.rules-fast-pattern", &fp_engine_analysis_set)) == 0) { return 0; } if (fp_engine_analysis_set == 0) return 0; char *log_dir; log_dir = ConfigGetLogDirectory(); snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, "rules_fast_pattern.txt"); fp_engine_analysis_FD = fopen(log_path, "w"); if (fp_engine_analysis_FD == NULL) { SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path, strerror(errno)); return 0; } SCLogInfo("Engine-Analysis for fast_pattern printed to file - %s", log_path); struct timeval tval; struct tm *tms; gettimeofday(&tval, NULL); struct tm local_tm; tms = SCLocalTime(tval.tv_sec, &local_tm); fprintf(fp_engine_analysis_FD, "----------------------------------------------" "---------------------\n"); fprintf(fp_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- " "%02d:%02d:%02d\n", tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec); fprintf(fp_engine_analysis_FD, "----------------------------------------------" "---------------------\n"); memset(&fp_pattern_stats, 0, sizeof(fp_pattern_stats)); return 1; }
/** \brief Read the config set the file pointer, open the file * \param file_ctx pointer to a created LogFileCtx using LogFileNewCtx() * \param filename name of log file * \param mode append mode (bool) * \return -1 if failure, 0 if succesful * */ static int AlertPcapInfoOpenFileCtx(LogFileCtx *file_ctx, const char *filename, const char *mode) { char log_path[PATH_MAX]; char *log_dir; log_dir = ConfigGetLogDirectory(); snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename); if (ConfValIsTrue(mode)) { file_ctx->fp = fopen(log_path, "a"); } else { file_ctx->fp = fopen(log_path, "w"); } if (file_ctx->fp == NULL) { SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", log_path, strerror(errno)); return -1; } return 0; }
/** * \brief Initialize profiling. */ void SCProfilingInit(void) { ConfNode *conf; SC_ATOMIC_INIT(samples); intmax_t rate_v = 0; (void)ConfGetInt("profiling.sample-rate", &rate_v); if (rate_v > 0 && rate_v < INT_MAX) { rate = (int)rate_v; if (rate != 1) SCLogInfo("profiling runs for every %dth packet", rate); else SCLogInfo("profiling runs for every packet"); } conf = ConfGetNode("profiling.packets"); if (conf != NULL) { if (ConfNodeChildValueIsTrue(conf, "enabled")) { profiling_packets_enabled = 1; if (pthread_mutex_init(&packet_profile_lock, NULL) != 0) { SCLogError(SC_ERR_MUTEX, "Failed to initialize packet profiling mutex."); exit(EXIT_FAILURE); } memset(&packet_profile_data4, 0, sizeof(packet_profile_data4)); memset(&packet_profile_data6, 0, sizeof(packet_profile_data6)); memset(&packet_profile_tmm_data4, 0, sizeof(packet_profile_tmm_data4)); memset(&packet_profile_tmm_data6, 0, sizeof(packet_profile_tmm_data6)); memset(&packet_profile_app_data4, 0, sizeof(packet_profile_app_data4)); memset(&packet_profile_app_data6, 0, sizeof(packet_profile_app_data6)); memset(&packet_profile_app_pd_data4, 0, sizeof(packet_profile_app_pd_data4)); memset(&packet_profile_app_pd_data6, 0, sizeof(packet_profile_app_pd_data6)); memset(&packet_profile_detect_data4, 0, sizeof(packet_profile_detect_data4)); memset(&packet_profile_detect_data6, 0, sizeof(packet_profile_detect_data6)); memset(&packet_profile_log_data4, 0, sizeof(packet_profile_log_data4)); memset(&packet_profile_log_data6, 0, sizeof(packet_profile_log_data6)); memset(&packet_profile_flowworker_data, 0, sizeof(packet_profile_flowworker_data)); const char *filename = ConfNodeLookupChildValue(conf, "filename"); if (filename != NULL) { char *log_dir; log_dir = ConfigGetLogDirectory(); profiling_packets_file_name = SCMalloc(PATH_MAX); if (unlikely(profiling_packets_file_name == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name"); exit(EXIT_FAILURE); } snprintf(profiling_packets_file_name, PATH_MAX, "%s/%s", log_dir, filename); const char *v = ConfNodeLookupChildValue(conf, "append"); if (v == NULL || ConfValIsTrue(v)) { profiling_packets_file_mode = "a"; } else { profiling_packets_file_mode = "w"; } profiling_packets_output_to_file = 1; } } conf = ConfGetNode("profiling.packets.csv"); if (conf != NULL) { if (ConfNodeChildValueIsTrue(conf, "enabled")) { const char *filename = ConfNodeLookupChildValue(conf, "filename"); if (filename == NULL) { filename = "packet_profile.csv"; } char *log_dir; log_dir = ConfigGetLogDirectory(); profiling_csv_file_name = SCMalloc(PATH_MAX); if (unlikely(profiling_csv_file_name == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "out of memory"); exit(EXIT_FAILURE); } snprintf(profiling_csv_file_name, PATH_MAX, "%s/%s", log_dir, filename); packet_profile_csv_fp = fopen(profiling_csv_file_name, "w"); if (packet_profile_csv_fp == NULL) { return; } fprintf(packet_profile_csv_fp, "pcap_cnt,ipver,ipproto,total,"); int i; for (i = 0; i < TMM_SIZE; i++) { fprintf(packet_profile_csv_fp, "%s,", TmModuleTmmIdToString(i)); } fprintf(packet_profile_csv_fp, "threading,"); for (i = 0; i < ALPROTO_MAX; i++) { fprintf(packet_profile_csv_fp, "%s,", AppProtoToString(i)); } fprintf(packet_profile_csv_fp, "proto detect,"); for (i = 0; i < PROF_DETECT_SIZE; i++) { fprintf(packet_profile_csv_fp, "%s,", PacketProfileDetectIdToString(i)); } fprintf(packet_profile_csv_fp, "\n"); profiling_packets_csv_enabled = 1; } } } conf = ConfGetNode("profiling.locks"); if (conf != NULL) { if (ConfNodeChildValueIsTrue(conf, "enabled")) { #ifndef PROFILE_LOCKING SCLogWarning(SC_WARN_PROFILE, "lock profiling not compiled in. Add --enable-profiling-locks to configure."); #else profiling_locks_enabled = 1; LockRecordInitHash(); const char *filename = ConfNodeLookupChildValue(conf, "filename"); if (filename != NULL) { char *log_dir; log_dir = ConfigGetLogDirectory(); profiling_locks_file_name = SCMalloc(PATH_MAX); if (unlikely(profiling_locks_file_name == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name"); exit(EXIT_FAILURE); } snprintf(profiling_locks_file_name, PATH_MAX, "%s/%s", log_dir, filename); const char *v = ConfNodeLookupChildValue(conf, "append"); if (v == NULL || ConfValIsTrue(v)) { profiling_locks_file_mode = "a"; } else { profiling_locks_file_mode = "w"; } profiling_locks_output_to_file = 1; } #endif } } }
/** \brief open a generic output "log file", which may be a regular file or a socket * \param conf ConfNode structure for the output section in question * \param log_ctx Log file context allocated by caller * \param default_filename Default name of file to open, if not specified in ConfNode * \param rotate Register the file for rotation in HUP. * \retval 0 on success * \retval -1 on error */ int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate) { char log_path[PATH_MAX]; char *log_dir; const char *filename, *filetype; // Arg check if (conf == NULL || log_ctx == NULL || default_filename == NULL) { SCLogError(SC_ERR_INVALID_ARGUMENT, "SCConfLogOpenGeneric(conf %p, ctx %p, default %p) " "missing an argument", conf, log_ctx, default_filename); return -1; } if (log_ctx->fp != NULL) { SCLogError(SC_ERR_INVALID_ARGUMENT, "SCConfLogOpenGeneric: previously initialized Log CTX " "encountered"); return -1; } // Resolve the given config filename = ConfNodeLookupChildValue(conf, "filename"); if (filename == NULL) filename = default_filename; log_dir = ConfigGetLogDirectory(); if (PathIsAbsolute(filename)) { snprintf(log_path, PATH_MAX, "%s", filename); } else { snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename); } filetype = ConfNodeLookupChildValue(conf, "filetype"); if (filetype == NULL) filetype = DEFAULT_LOG_FILETYPE; const char *append = ConfNodeLookupChildValue(conf, "append"); if (append == NULL) append = DEFAULT_LOG_MODE_APPEND; // Now, what have we been asked to open? if (strcasecmp(filetype, "unix_stream") == 0) { /* Don't bail. May be able to connect later. */ log_ctx->is_sock = 1; log_ctx->sock_type = SOCK_STREAM; log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1); } else if (strcasecmp(filetype, "unix_dgram") == 0) { /* Don't bail. May be able to connect later. */ log_ctx->is_sock = 1; log_ctx->sock_type = SOCK_DGRAM; log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1); } else if (strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 || strcasecmp(filetype, "file") == 0) { log_ctx->fp = SCLogOpenFileFp(log_path, append); if (log_ctx->fp == NULL) return -1; // Error already logged by Open...Fp routine log_ctx->is_regular = 1; if (rotate) { OutputRegisterFileRotationFlag(&log_ctx->rotation_flag); } } else if (strcasecmp(filetype, "pcie") == 0) { log_ctx->pcie_fp = SCLogOpenPcieFp(log_ctx, log_path, append); if (log_ctx->pcie_fp == NULL) return -1; // Error already logged by Open...Fp routine #ifdef HAVE_LIBHIREDIS } else if (strcasecmp(filetype, "redis") == 0) { ConfNode *redis_node = ConfNodeLookupChild(conf, "redis"); if (SCConfLogOpenRedis(redis_node, log_ctx) < 0) { SCLogError(SC_ERR_REDIS, "failed to open redis output"); return -1; } log_ctx->type = LOGFILE_TYPE_REDIS; #endif } else { SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for " "%s.filetype. Expected \"regular\" (default), \"unix_stream\", " "\"pcie\" " "or \"unix_dgram\"", conf->name); } log_ctx->filename = SCStrdup(log_path); if (unlikely(log_ctx->filename == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for filename"); return -1; } SCLogInfo("%s output device (%s) initialized: %s", conf->name, filetype, filename); return 0; }
/** \brief Create a new http log LogFileCtx. * \param conf Pointer to ConfNode containing this loggers configuration. * \return NULL if failure, LogFileCtx* to the file_ctx if succesful * */ OutputCtx *LogTcpDataLogInitCtx(ConfNode *conf) { char filename[PATH_MAX] = ""; char dirname[32] = ""; strlcpy(filename, DEFAULT_LOG_FILENAME, sizeof(filename)); LogFileCtx *file_ctx = LogFileNewCtx(); if(file_ctx == NULL) { SCLogError(SC_ERR_TCPDATA_LOG_GENERIC, "couldn't create new file_ctx"); return NULL; } LogTcpDataFileCtx *tcpdatalog_ctx = SCMalloc(sizeof(LogTcpDataFileCtx)); if (unlikely(tcpdatalog_ctx == NULL)) { LogFileFreeCtx(file_ctx); return NULL; } memset(tcpdatalog_ctx, 0x00, sizeof(LogTcpDataFileCtx)); tcpdatalog_ctx->file_ctx = file_ctx; if (conf) { if (conf->name) { if (strcmp(conf->name, "tcp-data") == 0) { tcpdatalog_ctx->type = STREAMING_TCP_DATA; snprintf(filename, sizeof(filename), "%s.log", conf->name); strlcpy(dirname, "tcp", sizeof(dirname)); } else if (strcmp(conf->name, "http-body-data") == 0) { tcpdatalog_ctx->type = STREAMING_HTTP_BODIES; snprintf(filename, sizeof(filename), "%s.log", conf->name); strlcpy(dirname, "http", sizeof(dirname)); } } const char *logtype = ConfNodeLookupChildValue(conf, "type"); if (logtype == NULL) logtype = "file"; if (strcmp(logtype, "file") == 0) { tcpdatalog_ctx->file = 1; } else if (strcmp(logtype, "dir") == 0) { tcpdatalog_ctx->dir = 1; } else if (strcmp(logtype, "both") == 0) { tcpdatalog_ctx->file = 1; tcpdatalog_ctx->dir = 1; } } else { tcpdatalog_ctx->file = 1; tcpdatalog_ctx->dir = 0; } if (tcpdatalog_ctx->file == 1) { SCLogInfo("opening logfile"); if (SCConfLogOpenGeneric(conf, file_ctx, filename, 1) < 0) { LogFileFreeCtx(file_ctx); SCFree(tcpdatalog_ctx); return NULL; } } if (tcpdatalog_ctx->dir == 1) { tcpdatalog_ctx->log_dir = ConfigGetLogDirectory(); char dirfull[PATH_MAX]; /* create the filename to use */ snprintf(dirfull, PATH_MAX, "%s/%s", tcpdatalog_ctx->log_dir, dirname); SCLogInfo("using directory %s", dirfull); /* if mkdir fails file open will fail, so deal with errors there */ #ifndef OS_WIN32 (void)mkdir(dirfull, 0700); #else (void)mkdir(dirfull); #endif } OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) { goto parsererror; } output_ctx->data = tcpdatalog_ctx; output_ctx->DeInit = LogTcpDataLogDeInitCtx; SCLogDebug("Streaming log output initialized"); return output_ctx; parsererror: LogFileFreeCtx(file_ctx); SCFree(tcpdatalog_ctx); SCLogError(SC_ERR_INVALID_ARGUMENT,"Syntax error in custom http log format string."); return NULL; }
void SCProfilingRulesGlobalInit(void) { ConfNode *conf; const char *val; conf = ConfGetNode("profiling.rules"); if (conf != NULL) { if (ConfNodeChildValueIsTrue(conf, "enabled")) { profiling_rules_enabled = 1; val = ConfNodeLookupChildValue(conf, "sort"); if (val != NULL) { if (strcmp(val, "ticks") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_TICKS; } else if (strcmp(val, "avgticks") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_AVG_TICKS; } else if (strcmp(val, "avgticks_match") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH; } else if (strcmp(val, "avgticks_no_match") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH; } else if (strcmp(val, "checks") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_CHECKS; } else if (strcmp(val, "matches") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_MATCHES; } else if (strcmp(val, "maxticks") == 0) { profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_MAX_TICKS; } else { SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid profiling sort order: %s", val); exit(EXIT_FAILURE); } } val = ConfNodeLookupChildValue(conf, "limit"); if (val != NULL) { if (ByteExtractStringUint32(&profiling_rules_limit, 10, (uint16_t)strlen(val), val) <= 0) { SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid limit: %s", val); exit(EXIT_FAILURE); } } const char *filename = ConfNodeLookupChildValue(conf, "filename"); if (filename != NULL) { char *log_dir; log_dir = ConfigGetLogDirectory(); profiling_file_name = SCMalloc(PATH_MAX); if (unlikely(profiling_file_name == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "can't duplicate file name"); exit(EXIT_FAILURE); } snprintf(profiling_file_name, PATH_MAX, "%s/%s", log_dir, filename); const char *v = ConfNodeLookupChildValue(conf, "append"); if (v == NULL || ConfValIsTrue(v)) { profiling_file_mode = "a"; } else { profiling_file_mode = "w"; } profiling_output_to_file = 1; } } } }
void RulesDumpMatchArray(const DetectEngineThreadCtx *det_ctx, const Packet *p) { json_t *js = CreateJSONHeader(p, 0, "inspectedrules"); if (js == NULL) return; json_t *ir = json_object(); if (ir == NULL) return; json_object_set_new(ir, "rule_group_id", json_integer(det_ctx->sgh->id)); json_object_set_new(ir, "rule_cnt", json_integer(det_ctx->match_array_cnt)); json_t *js_array = json_array(); uint32_t x; for (x = 0; x < det_ctx->match_array_cnt; x++) { const Signature *s = det_ctx->match_array[x]; if (s == NULL) continue; json_t *js_sig = json_object(); if (unlikely(js == NULL)) continue; json_object_set_new(js_sig, "sig_id", json_integer(s->id)); json_object_set_new(js_sig, "mpm", (s->mpm_sm != NULL) ? json_true() : json_false()); if (s->mpm_sm != NULL) { char orig[256] = ""; char chop[256] = ""; DumpFp(s->mpm_sm, orig, sizeof(orig), chop, sizeof(chop)); json_object_set_new(js_sig, "mpm_buffer", json_string(DetectListToHumanString(SigMatchListSMBelongsTo(s, s->mpm_sm)))); json_object_set_new(js_sig, "mpm_pattern", json_string(orig)); if (strlen(chop) > 0) { json_object_set_new(js_sig, "mpm_pattern_chop", json_string(chop)); } } json_array_append_new(js_array, js_sig); } json_object_set_new(ir, "rules", js_array); json_object_set_new(js, "inspectedrules", ir); const char *filename = "packet_inspected_rules.json"; const char *log_dir = ConfigGetLogDirectory(); char log_path[PATH_MAX] = ""; snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename); MemBuffer *mbuf = NULL; mbuf = MemBufferCreateNew(4096); BUG_ON(mbuf == NULL); OutputJSONMemBufferWrapper wrapper = { .buffer = &mbuf, .expand_by = 4096, }; int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper, JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII| JSON_ESCAPE_SLASH); if (r != 0) { SCLogWarning(SC_ERR_SOCKET, "unable to serialize JSON object"); } else { MemBufferWriteString(mbuf, "\n"); SCMutexLock(&g_rule_dump_write_m); FILE *fp = fopen(log_path, "a"); if (fp != NULL) { MemBufferPrintToFPAsString(mbuf, fp); fclose(fp); SCMutexUnlock(&g_rule_dump_write_m); } } MemBufferFree(mbuf); json_object_clear(js); json_decref(js); }
/** \brief Create a new http log LogFilestoreCtx. * \param conf Pointer to ConfNode containing this loggers configuration. * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful * */ static OutputInitResult LogFilestoreLogInitCtx(ConfNode *conf) { OutputInitResult result = { NULL, false }; intmax_t version = 0; if (ConfGetChildValueInt(conf, "version", &version)) { if (version > 1) { result.ok = true; return result; } } if (RunModeOutputFiledataEnabled()) { SCLogWarning(SC_ERR_NOT_SUPPORTED, "A file data logger is already enabled. Filestore (v1) " "will not be enabled."); return result; } OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) return result; output_ctx->data = NULL; output_ctx->DeInit = LogFilestoreLogDeInitCtx; const char *s_default_log_dir = NULL; s_default_log_dir = ConfigGetLogDirectory(); const char *s_base_dir = NULL; s_base_dir = ConfNodeLookupChildValue(conf, "log-dir"); if (s_base_dir == NULL || strlen(s_base_dir) == 0) { strlcpy(g_logfile_base_dir, s_default_log_dir, sizeof(g_logfile_base_dir)); } else { if (PathIsAbsolute(s_base_dir)) { strlcpy(g_logfile_base_dir, s_base_dir, sizeof(g_logfile_base_dir)); } else { snprintf(g_logfile_base_dir, sizeof(g_logfile_base_dir), "%s/%s", s_default_log_dir, s_base_dir); } } const char *force_filestore = ConfNodeLookupChildValue(conf, "force-filestore"); if (force_filestore != NULL && ConfValIsTrue(force_filestore)) { FileForceFilestoreEnable(); SCLogInfo("forcing filestore of all files"); } const char *force_magic = ConfNodeLookupChildValue(conf, "force-magic"); if (force_magic != NULL && ConfValIsTrue(force_magic)) { FileForceMagicEnable(); SCLogInfo("forcing magic lookup for stored files"); } const char *write_meta = ConfNodeLookupChildValue(conf, "write-meta"); if (write_meta != NULL && !ConfValIsTrue(write_meta)) { FileWriteMetaDisable(); SCLogInfo("File-store output will not write meta files"); } FileForceHashParseCfg(conf); SCLogInfo("storing files in %s", g_logfile_base_dir); const char *stream_depth_str = ConfNodeLookupChildValue(conf, "stream-depth"); if (stream_depth_str != NULL && strcmp(stream_depth_str, "no")) { uint32_t stream_depth = 0; if (ParseSizeStringU32(stream_depth_str, &stream_depth) < 0) { SCLogError(SC_ERR_SIZE_PARSE, "Error parsing " "file-store.stream-depth " "from conf file - %s. Killing engine", stream_depth_str); exit(EXIT_FAILURE); } else { FileReassemblyDepthEnable(stream_depth); } } const char *file_count_str = ConfNodeLookupChildValue(conf, "max-open-files"); if (file_count_str != NULL) { uint32_t file_count = 0; if (ParseSizeStringU32(file_count_str, &file_count) < 0) { SCLogError(SC_ERR_SIZE_PARSE, "Error parsing " "file-store.max-open-files " "from conf file - %s. Killing engine", stream_depth_str); exit(EXIT_FAILURE); } else { if (file_count != 0) { FileSetMaxOpenFiles(file_count); SCLogInfo("file-store will keep a max of %d simultaneously" " open files", file_count); } } } const char *include_pid = ConfNodeLookupChildValue(conf, "include-pid"); if (include_pid != NULL && ConfValIsTrue(include_pid)) { FileIncludePidEnable(); SCLogInfo("enabling pid as a part of all file names"); } StatsRegisterGlobalCounter("file_store.open_files", LogFilestoreOpenFilesCounter); result.ctx = output_ctx; result.ok = true; SCReturnCT(result, "OutputInitResult"); }
/** \brief open a generic output "log file", which may be a regular file or a socket * \param conf ConfNode structure for the output section in question * \param log_ctx Log file context allocated by caller * \param default_filename Default name of file to open, if not specified in ConfNode * \param rotate Register the file for rotation in HUP. * \retval 0 on success * \retval -1 on error */ int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate) { char log_path[PATH_MAX]; const char *log_dir; const char *filename, *filetype; // Arg check if (conf == NULL || log_ctx == NULL || default_filename == NULL) { SCLogError(SC_ERR_INVALID_ARGUMENT, "SCConfLogOpenGeneric(conf %p, ctx %p, default %p) " "missing an argument", conf, log_ctx, default_filename); return -1; } if (log_ctx->fp != NULL) { SCLogError(SC_ERR_INVALID_ARGUMENT, "SCConfLogOpenGeneric: previously initialized Log CTX " "encountered"); return -1; } // Resolve the given config filename = ConfNodeLookupChildValue(conf, "filename"); if (filename == NULL) filename = default_filename; log_dir = ConfigGetLogDirectory(); if (PathIsAbsolute(filename)) { snprintf(log_path, PATH_MAX, "%s", filename); } else { snprintf(log_path, PATH_MAX, "%s/%s", log_dir, filename); } /* Rotate log file based on time */ const char *rotate_int = ConfNodeLookupChildValue(conf, "rotate-interval"); if (rotate_int != NULL) { time_t now = time(NULL); log_ctx->flags |= LOGFILE_ROTATE_INTERVAL; /* Use a specific time */ if (strcmp(rotate_int, "minute") == 0) { log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now); log_ctx->rotate_interval = 60; } else if (strcmp(rotate_int, "hour") == 0) { log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now); log_ctx->rotate_interval = 3600; } else if (strcmp(rotate_int, "day") == 0) { log_ctx->rotate_time = now + SCGetSecondsUntil(rotate_int, now); log_ctx->rotate_interval = 86400; } /* Use a timer */ else { log_ctx->rotate_interval = SCParseTimeSizeString(rotate_int); if (log_ctx->rotate_interval == 0) { SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "invalid rotate-interval value"); exit(EXIT_FAILURE); } log_ctx->rotate_time = now + log_ctx->rotate_interval; } } filetype = ConfNodeLookupChildValue(conf, "filetype"); if (filetype == NULL) filetype = DEFAULT_LOG_FILETYPE; const char *filemode = ConfNodeLookupChildValue(conf, "filemode"); uint32_t mode = 0; if (filemode != NULL && ByteExtractStringUint32(&mode, 8, strlen(filemode), filemode) > 0) { log_ctx->filemode = mode; } const char *append = ConfNodeLookupChildValue(conf, "append"); if (append == NULL) append = DEFAULT_LOG_MODE_APPEND; /* JSON flags */ #ifdef HAVE_LIBJANSSON log_ctx->json_flags = JSON_PRESERVE_ORDER|JSON_COMPACT| JSON_ENSURE_ASCII|JSON_ESCAPE_SLASH; ConfNode *json_flags = ConfNodeLookupChild(conf, "json"); if (json_flags != 0) { const char *preserve_order = ConfNodeLookupChildValue(json_flags, "preserve-order"); if (preserve_order != NULL && ConfValIsFalse(preserve_order)) log_ctx->json_flags &= ~(JSON_PRESERVE_ORDER); const char *compact = ConfNodeLookupChildValue(json_flags, "compact"); if (compact != NULL && ConfValIsFalse(compact)) log_ctx->json_flags &= ~(JSON_COMPACT); const char *ensure_ascii = ConfNodeLookupChildValue(json_flags, "ensure-ascii"); if (ensure_ascii != NULL && ConfValIsFalse(ensure_ascii)) log_ctx->json_flags &= ~(JSON_ENSURE_ASCII); const char *escape_slash = ConfNodeLookupChildValue(json_flags, "escape-slash"); if (escape_slash != NULL && ConfValIsFalse(escape_slash)) log_ctx->json_flags &= ~(JSON_ESCAPE_SLASH); } #endif /* HAVE_LIBJANSSON */ // Now, what have we been asked to open? if (strcasecmp(filetype, "unix_stream") == 0) { #ifdef BUILD_WITH_UNIXSOCKET /* Don't bail. May be able to connect later. */ log_ctx->is_sock = 1; log_ctx->sock_type = SOCK_STREAM; log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_STREAM, 1); #else return -1; #endif } else if (strcasecmp(filetype, "unix_dgram") == 0) { #ifdef BUILD_WITH_UNIXSOCKET /* Don't bail. May be able to connect later. */ log_ctx->is_sock = 1; log_ctx->sock_type = SOCK_DGRAM; log_ctx->fp = SCLogOpenUnixSocketFp(log_path, SOCK_DGRAM, 1); #else return -1; #endif } else if (strcasecmp(filetype, DEFAULT_LOG_FILETYPE) == 0 || strcasecmp(filetype, "file") == 0) { log_ctx->fp = SCLogOpenFileFp(log_path, append, log_ctx->filemode); if (log_ctx->fp == NULL) return -1; // Error already logged by Open...Fp routine log_ctx->is_regular = 1; if (rotate) { OutputRegisterFileRotationFlag(&log_ctx->rotation_flag); } } else if (strcasecmp(filetype, "pcie") == 0) { log_ctx->pcie_fp = SCLogOpenPcieFp(log_ctx, log_path, append); if (log_ctx->pcie_fp == NULL) return -1; // Error already logged by Open...Fp routine #ifdef HAVE_LIBHIREDIS } else if (strcasecmp(filetype, "redis") == 0) { ConfNode *redis_node = ConfNodeLookupChild(conf, "redis"); if (SCConfLogOpenRedis(redis_node, log_ctx) < 0) { SCLogError(SC_ERR_REDIS, "failed to open redis output"); return -1; } log_ctx->type = LOGFILE_TYPE_REDIS; #endif } else { SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry for " "%s.filetype. Expected \"regular\" (default), \"unix_stream\", " "\"pcie\" " "or \"unix_dgram\"", conf->name); } log_ctx->filename = SCStrdup(log_path); if (unlikely(log_ctx->filename == NULL)) { SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for filename"); return -1; } #ifdef BUILD_WITH_UNIXSOCKET /* If a socket and running live, do non-blocking writes. */ if (log_ctx->is_sock && run_mode_offline == 0) { SCLogInfo("Setting logging socket of non-blocking in live mode."); log_ctx->send_flags |= MSG_DONTWAIT; } #endif SCLogInfo("%s output device (%s) initialized: %s", conf->name, filetype, filename); return 0; }