int SCConfLogOpenRedis(ConfNode *redis_node, LogFileCtx *log_ctx) { const char *redis_server = NULL; const char *redis_port = NULL; const char *redis_mode = NULL; const char *redis_key = NULL; if (redis_node) { redis_server = ConfNodeLookupChildValue(redis_node, "server"); redis_port = ConfNodeLookupChildValue(redis_node, "port"); redis_mode = ConfNodeLookupChildValue(redis_node, "mode"); redis_key = ConfNodeLookupChildValue(redis_node, "key"); } if (!redis_server) { redis_server = "127.0.0.1"; SCLogInfo("Using default redis server (127.0.0.1)"); } if (!redis_port) redis_port = "6379"; if (!redis_mode) redis_mode = "list"; if (!redis_key) redis_key = "suricata"; log_ctx->redis_setup.key = SCStrdup(redis_key); if (!log_ctx->redis_setup.key) { SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key name"); exit(EXIT_FAILURE); } log_ctx->redis_setup.batch_size = 0; ConfNode *pipelining = ConfNodeLookupChild(redis_node, "pipelining"); if (pipelining) { int enabled = 0; int ret; intmax_t val; ret = ConfGetChildValueBool(pipelining, "enabled", &enabled); if (ret && enabled) { ret = ConfGetChildValueInt(pipelining, "batch-size", &val); if (ret) { log_ctx->redis_setup.batch_size = val; } else { log_ctx->redis_setup.batch_size = 10; } } } if (!strcmp(redis_mode, "list")) { log_ctx->redis_setup.command = redis_push_cmd; if (!log_ctx->redis_setup.command) { SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command"); exit(EXIT_FAILURE); } } else { log_ctx->redis_setup.command = redis_publish_cmd; if (!log_ctx->redis_setup.command) { SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate redis key command"); exit(EXIT_FAILURE); } } redisContext *c = redisConnect(redis_server, atoi(redis_port)); if (c != NULL && c->err) { SCLogError(SC_ERR_SOCKET, "Error connecting to redis server: %s", c->errstr); exit(EXIT_FAILURE); } /* store server params for reconnection */ log_ctx->redis_setup.server = SCStrdup(redis_server); if (!log_ctx->redis_setup.server) { SCLogError(SC_ERR_MEM_ALLOC, "Error allocating redis server string"); exit(EXIT_FAILURE); } log_ctx->redis_setup.port = atoi(redis_port); log_ctx->redis_setup.tried = 0; log_ctx->redis = c; log_ctx->Close = SCLogFileCloseRedis; return 0; }
/** \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 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; }
/** * \brief Create a new LogFileCtx for "fast" output style. * \param conf The configuration node for this output. * \return A LogFileCtx pointer on success, NULL on failure. */ OutputCtx *OutputJsonInitCtx(ConfNode *conf) { OutputJsonCtx *json_ctx = SCCalloc(1, sizeof(OutputJsonCtx));; const char *sensor_name = ConfNodeLookupChildValue(conf, "sensor-name"); if (unlikely(json_ctx == NULL)) { SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx"); return NULL; } json_ctx->file_ctx = LogFileNewCtx(); if (unlikely(json_ctx->file_ctx == NULL)) { SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx"); SCFree(json_ctx); return NULL; } if (sensor_name) { json_ctx->file_ctx->sensor_name = SCStrdup(sensor_name); if (json_ctx->file_ctx->sensor_name == NULL) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); return NULL; } } else { json_ctx->file_ctx->sensor_name = NULL; } OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); return NULL; } output_ctx->data = json_ctx; output_ctx->DeInit = OutputJsonDeInitCtx; if (conf) { const char *output_s = ConfNodeLookupChildValue(conf, "filetype"); // Backwards compatibility if (output_s == NULL) { output_s = ConfNodeLookupChildValue(conf, "type"); } if (output_s != NULL) { if (strcmp(output_s, "file") == 0 || strcmp(output_s, "regular") == 0) { json_ctx->json_out = LOGFILE_TYPE_FILE; } else if (strcmp(output_s, "syslog") == 0) { json_ctx->json_out = LOGFILE_TYPE_SYSLOG; } else if (strcmp(output_s, "unix_dgram") == 0) { json_ctx->json_out = LOGFILE_TYPE_UNIX_DGRAM; } else if (strcmp(output_s, "unix_stream") == 0) { json_ctx->json_out = LOGFILE_TYPE_UNIX_STREAM; } else if (strcmp(output_s, "redis") == 0) { #ifdef HAVE_LIBHIREDIS json_ctx->json_out = LOGFILE_TYPE_REDIS; #else SCLogError(SC_ERR_INVALID_ARGUMENT, "redis JSON output option is not compiled"); exit(EXIT_FAILURE); #endif } else { SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid JSON output option: %s", output_s); exit(EXIT_FAILURE); } } const char *prefix = ConfNodeLookupChildValue(conf, "prefix"); if (prefix != NULL) { SCLogInfo("Using prefix '%s' for JSON messages", prefix); json_ctx->file_ctx->prefix = SCStrdup(prefix); if (json_ctx->file_ctx->prefix == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for eve-log.prefix setting."); exit(EXIT_FAILURE); } json_ctx->file_ctx->prefix_len = strlen(prefix); } if (json_ctx->json_out == LOGFILE_TYPE_FILE || json_ctx->json_out == LOGFILE_TYPE_UNIX_DGRAM || json_ctx->json_out == LOGFILE_TYPE_UNIX_STREAM) { if (SCConfLogOpenGeneric(conf, json_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return NULL; } OutputRegisterFileRotationFlag(&json_ctx->file_ctx->rotation_flag); const char *format_s = ConfNodeLookupChildValue(conf, "format"); if (format_s != NULL) { if (strcmp(format_s, "indent") == 0) { json_ctx->format = INDENT; } else if (strcmp(format_s, "compact") == 0) { json_ctx->format = COMPACT; } else { SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid JSON format option: %s", format_s); exit(EXIT_FAILURE); } } } else if (json_ctx->json_out == LOGFILE_TYPE_SYSLOG) { const char *facility_s = ConfNodeLookupChildValue(conf, "facility"); if (facility_s == NULL) { facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR; } int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap()); if (facility == -1) { SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\"," " now using \"%s\" as syslog facility", facility_s, DEFAULT_ALERT_SYSLOG_FACILITY_STR); facility = DEFAULT_ALERT_SYSLOG_FACILITY; } const char *level_s = ConfNodeLookupChildValue(conf, "level"); if (level_s != NULL) { int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap()); if (level != -1) { json_ctx->file_ctx->syslog_setup.alert_syslog_level = level; } } const char *ident = ConfNodeLookupChildValue(conf, "identity"); /* if null we just pass that to openlog, which will then * figure it out by itself. */ openlog(ident, LOG_PID|LOG_NDELAY, facility); } #ifdef HAVE_LIBHIREDIS else if (json_ctx->json_out == LOGFILE_TYPE_REDIS) { ConfNode *redis_node = ConfNodeLookupChild(conf, "redis"); if (!json_ctx->file_ctx->sensor_name) { char hostname[1024]; gethostname(hostname, 1023); json_ctx->file_ctx->sensor_name = SCStrdup(hostname); } if (json_ctx->file_ctx->sensor_name == NULL) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return NULL; } if (SCConfLogOpenRedis(redis_node, json_ctx->file_ctx) < 0) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return NULL; } } #endif const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id"); if (sensor_id_s != NULL) { if (ByteExtractStringUint64((uint64_t *)&sensor_id, 10, 0, sensor_id_s) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to initialize JSON output, " "invalid sensor-is: %s", sensor_id_s); exit(EXIT_FAILURE); } } json_ctx->file_ctx->type = json_ctx->json_out; } SCLogDebug("returning output_ctx %p", output_ctx); return output_ctx; }
/** \brief configure and initializes redis output logging * \param conf ConfNode structure for the output section in question * \param log_ctx Log file context allocated by caller * \retval 0 on success */ int SCConfLogOpenRedis(ConfNode *redis_node, void *lf_ctx) { LogFileCtx *log_ctx = lf_ctx; const char *redis_port = NULL; const char *redis_mode = NULL; int is_async = 0; if (redis_node) { log_ctx->redis_setup.server = ConfNodeLookupChildValue(redis_node, "server"); log_ctx->redis_setup.key = ConfNodeLookupChildValue(redis_node, "key"); redis_port = ConfNodeLookupChildValue(redis_node, "port"); redis_mode = ConfNodeLookupChildValue(redis_node, "mode"); (void)ConfGetChildValueBool(redis_node, "async", &is_async); } if (!log_ctx->redis_setup.server) { log_ctx->redis_setup.server = redis_default_server; SCLogInfo("Using default redis server (127.0.0.1)"); } if (!redis_port) redis_port = "6379"; if (!redis_mode) redis_mode = "list"; if (!log_ctx->redis_setup.key) { log_ctx->redis_setup.key = redis_default_key; } #ifndef HAVE_LIBEVENT if (is_async) { SCLogWarning(SC_ERR_NO_REDIS_ASYNC, "async option not available."); } is_async = 0; #endif //ifndef HAVE_LIBEVENT log_ctx->redis_setup.is_async = is_async; log_ctx->redis_setup.batch_size = 0; if (redis_node) { ConfNode *pipelining = ConfNodeLookupChild(redis_node, "pipelining"); if (pipelining) { int enabled = 0; int ret; intmax_t val; ret = ConfGetChildValueBool(pipelining, "enabled", &enabled); if (ret && enabled) { ret = ConfGetChildValueInt(pipelining, "batch-size", &val); if (ret) { log_ctx->redis_setup.batch_size = val; } else { log_ctx->redis_setup.batch_size = 10; } } } } else { log_ctx->redis_setup.batch_size = 0; } if (!strcmp(redis_mode, "list") || !strcmp(redis_mode,"lpush")) { log_ctx->redis_setup.command = redis_lpush_cmd; } else if(!strcmp(redis_mode, "rpush")){ log_ctx->redis_setup.command = redis_rpush_cmd; } else if(!strcmp(redis_mode,"channel") || !strcmp(redis_mode,"publish")) { log_ctx->redis_setup.command = redis_publish_cmd; } else { SCLogError(SC_ERR_REDIS_CONFIG,"Invalid redis mode"); exit(EXIT_FAILURE); } /* store server params for reconnection */ if (!log_ctx->redis_setup.server) { SCLogError(SC_ERR_MEM_ALLOC, "Error allocating redis server string"); exit(EXIT_FAILURE); } log_ctx->redis_setup.port = atoi(redis_port); log_ctx->Close = SCLogFileCloseRedis; #ifdef HAVE_LIBEVENT if (is_async) { log_ctx->redis = SCLogRedisContextAsyncAlloc(); } #endif /*HAVE_LIBEVENT*/ if (! is_async) { log_ctx->redis = SCLogRedisContextAlloc(); SCConfLogReopenSyncRedis(log_ctx); } return 0; }
/** * \brief Create a new LogFileCtx for "fast" output style. * \param conf The configuration node for this output. * \return A LogFileCtx pointer on success, NULL on failure. */ OutputInitResult OutputJsonInitCtx(ConfNode *conf) { OutputInitResult result = { NULL, false }; OutputJsonCtx *json_ctx = SCCalloc(1, sizeof(OutputJsonCtx)); if (unlikely(json_ctx == NULL)) { SCLogDebug("could not create new OutputJsonCtx"); return result; } /* First lookup a sensor-name value in this outputs configuration * node (deprecated). If that fails, lookup the global one. */ const char *sensor_name = ConfNodeLookupChildValue(conf, "sensor-name"); if (sensor_name != NULL) { SCLogWarning(SC_ERR_DEPRECATED_CONF, "Found deprecated eve-log setting \"sensor-name\". " "Please set sensor-name globally."); } else { (void)ConfGet("sensor-name", &sensor_name); } json_ctx->file_ctx = LogFileNewCtx(); if (unlikely(json_ctx->file_ctx == NULL)) { SCLogDebug("AlertJsonInitCtx: Could not create new LogFileCtx"); SCFree(json_ctx); return result; } if (sensor_name) { json_ctx->file_ctx->sensor_name = SCStrdup(sensor_name); if (json_ctx->file_ctx->sensor_name == NULL) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); return result; } } else { json_ctx->file_ctx->sensor_name = NULL; } OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); return result; } output_ctx->data = json_ctx; output_ctx->DeInit = OutputJsonDeInitCtx; if (conf) { const char *output_s = ConfNodeLookupChildValue(conf, "filetype"); // Backwards compatibility if (output_s == NULL) { output_s = ConfNodeLookupChildValue(conf, "type"); } if (output_s != NULL) { if (strcmp(output_s, "file") == 0 || strcmp(output_s, "regular") == 0) { json_ctx->json_out = LOGFILE_TYPE_FILE; } else if (strcmp(output_s, "syslog") == 0) { json_ctx->json_out = LOGFILE_TYPE_SYSLOG; } else if (strcmp(output_s, "unix_dgram") == 0) { json_ctx->json_out = LOGFILE_TYPE_UNIX_DGRAM; } else if (strcmp(output_s, "unix_stream") == 0) { json_ctx->json_out = LOGFILE_TYPE_UNIX_STREAM; } else if (strcmp(output_s, "redis") == 0) { #ifdef HAVE_LIBHIREDIS SCLogRedisInit(); json_ctx->json_out = LOGFILE_TYPE_REDIS; #else SCLogError(SC_ERR_INVALID_ARGUMENT, "redis JSON output option is not compiled"); exit(EXIT_FAILURE); #endif } else { SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid JSON output option: %s", output_s); exit(EXIT_FAILURE); } } const char *prefix = ConfNodeLookupChildValue(conf, "prefix"); if (prefix != NULL) { SCLogInfo("Using prefix '%s' for JSON messages", prefix); json_ctx->file_ctx->prefix = SCStrdup(prefix); if (json_ctx->file_ctx->prefix == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate memory for eve-log.prefix setting."); exit(EXIT_FAILURE); } json_ctx->file_ctx->prefix_len = strlen(prefix); } if (json_ctx->json_out == LOGFILE_TYPE_FILE || json_ctx->json_out == LOGFILE_TYPE_UNIX_DGRAM || json_ctx->json_out == LOGFILE_TYPE_UNIX_STREAM) { if (SCConfLogOpenGeneric(conf, json_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return result; } OutputRegisterFileRotationFlag(&json_ctx->file_ctx->rotation_flag); } #ifndef OS_WIN32 else if (json_ctx->json_out == LOGFILE_TYPE_SYSLOG) { const char *facility_s = ConfNodeLookupChildValue(conf, "facility"); if (facility_s == NULL) { facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR; } int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap()); if (facility == -1) { SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog facility: \"%s\"," " now using \"%s\" as syslog facility", facility_s, DEFAULT_ALERT_SYSLOG_FACILITY_STR); facility = DEFAULT_ALERT_SYSLOG_FACILITY; } const char *level_s = ConfNodeLookupChildValue(conf, "level"); if (level_s != NULL) { int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap()); if (level != -1) { json_ctx->file_ctx->syslog_setup.alert_syslog_level = level; } } const char *ident = ConfNodeLookupChildValue(conf, "identity"); /* if null we just pass that to openlog, which will then * figure it out by itself. */ openlog(ident, LOG_PID|LOG_NDELAY, facility); } #endif #ifdef HAVE_LIBHIREDIS else if (json_ctx->json_out == LOGFILE_TYPE_REDIS) { ConfNode *redis_node = ConfNodeLookupChild(conf, "redis"); if (!json_ctx->file_ctx->sensor_name) { char hostname[1024]; gethostname(hostname, 1023); json_ctx->file_ctx->sensor_name = SCStrdup(hostname); } if (json_ctx->file_ctx->sensor_name == NULL) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return result; } if (SCConfLogOpenRedis(redis_node, json_ctx->file_ctx) < 0) { LogFileFreeCtx(json_ctx->file_ctx); SCFree(json_ctx); SCFree(output_ctx); return result; } } #endif const char *sensor_id_s = ConfNodeLookupChildValue(conf, "sensor-id"); if (sensor_id_s != NULL) { if (ByteExtractStringUint64((uint64_t *)&sensor_id, 10, 0, sensor_id_s) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to initialize JSON output, " "invalid sensor-id: %s", sensor_id_s); exit(EXIT_FAILURE); } } /* Check if top-level metadata should be logged. */ const ConfNode *metadata = ConfNodeLookupChild(conf, "metadata"); if (metadata && metadata->val && ConfValIsFalse(metadata->val)) { SCLogConfig("Disabling eve metadata logging."); json_ctx->cfg.include_metadata = false; } else { json_ctx->cfg.include_metadata = true; } /* See if we want to enable the community id */ const ConfNode *community_id = ConfNodeLookupChild(conf, "community-id"); if (community_id && community_id->val && ConfValIsTrue(community_id->val)) { SCLogConfig("Enabling eve community_id logging."); json_ctx->cfg.include_community_id = true; } else { json_ctx->cfg.include_community_id = false; } const char *cid_seed = ConfNodeLookupChildValue(conf, "community-id-seed"); if (cid_seed != NULL) { if (ByteExtractStringUint16(&json_ctx->cfg.community_id_seed, 10, 0, cid_seed) == -1) { SCLogError(SC_ERR_INVALID_ARGUMENT, "Failed to initialize JSON output, " "invalid community-id-seed: %s", cid_seed); exit(EXIT_FAILURE); } } /* Do we have a global eve xff configuration? */ const ConfNode *xff = ConfNodeLookupChild(conf, "xff"); if (xff != NULL) { json_ctx->xff_cfg = SCCalloc(1, sizeof(HttpXFFCfg)); if (likely(json_ctx->xff_cfg != NULL)) { HttpXFFGetCfg(conf, json_ctx->xff_cfg); } } const char *pcapfile_s = ConfNodeLookupChildValue(conf, "pcap-file"); if (pcapfile_s != NULL && ConfValIsTrue(pcapfile_s)) { json_ctx->file_ctx->is_pcap_offline = (RunmodeGetCurrent() == RUNMODE_PCAP_FILE); } json_ctx->file_ctx->type = json_ctx->json_out; } SCLogDebug("returning output_ctx %p", output_ctx); result.ctx = output_ctx; result.ok = true; return result; }