diff --git a/include/fluent-bit/flb_parser.h b/include/fluent-bit/flb_parser.h index 62770a97109..7a2a16d2764 100644 --- a/include/fluent-bit/flb_parser.h +++ b/include/fluent-bit/flb_parser.h @@ -49,6 +49,7 @@ struct flb_parser { char *time_key; /* field name that contains the time */ int time_offset; /* fixed UTC offset */ int time_system_timezone; /* use the system timezone as a fallback */ + char *time_zone; /* IANA timezone for naive timestamps (e.g. America/New_York) */ int time_keep; /* keep time field */ int time_strict; /* parse time field strictly */ int logfmt_no_bare_keys; /* in logfmt parsers, require all keys to have values */ @@ -75,21 +76,10 @@ enum { FLB_PARSER_TYPE_HEX, }; -static inline time_t flb_parser_tm2time(const struct flb_tm *src, - int use_system_timezone) -{ - struct tm tmp; - time_t res; +time_t flb_parser_tm2time_simple(const struct flb_tm *src, + int use_system_timezone); - tmp = src->tm; - if (use_system_timezone) { - tmp.tm_isdst = -1; - res = mktime(&tmp); - } else { - res = timegm(&tmp) - flb_tm_gmtoff(src); - } - return res; -} +time_t flb_parser_tm2time(const struct flb_tm *src, struct flb_parser *parser); struct flb_parser *flb_parser_create(const char *name, const char *format, @@ -100,6 +90,7 @@ struct flb_parser *flb_parser_create(const char *name, const char *format, int time_keep, int time_strict, int time_system_timezone, + const char *time_zone, int logfmt_no_bare_keys, struct flb_parser_types *types, int types_len, diff --git a/plugins/in_kubernetes_events/kubernetes_events.c b/plugins/in_kubernetes_events/kubernetes_events.c index 313d7e6a09b..a7a1f50b2f0 100644 --- a/plugins/in_kubernetes_events/kubernetes_events.c +++ b/plugins/in_kubernetes_events/kubernetes_events.c @@ -227,7 +227,7 @@ static int record_get_field_time(msgpack_object *obj, const char *fieldname, str return -2; } - val->tm.tv_sec = flb_parser_tm2time(&tm, FLB_FALSE); + val->tm.tv_sec = flb_parser_tm2time_simple(&tm, FLB_FALSE); val->tm.tv_nsec = 0; return 0; diff --git a/src/flb_parser.c b/src/flb_parser.c index f13243f580f..ba4c3958fa6 100644 --- a/src/flb_parser.c +++ b/src/flb_parser.c @@ -42,8 +42,71 @@ #include #include #include +#include #include +#include + +static pthread_mutex_t flb_time_zone_mutex = PTHREAD_MUTEX_INITIALIZER; + +time_t flb_parser_tm2time_simple(const struct flb_tm *src, int use_system_timezone) +{ + struct tm tmp; + time_t res; + + tmp = src->tm; + if (use_system_timezone) { + tmp.tm_isdst = -1; + res = mktime(&tmp); + } + else { + res = timegm(&tmp) - flb_tm_gmtoff(src); + } + return res; +} + +time_t flb_parser_tm2time(const struct flb_tm *src, struct flb_parser *parser) +{ +#ifndef FLB_SYSTEM_WINDOWS + if (parser->time_zone) { + char *old_tz = NULL; + const char *prev; + struct tm tmp; + time_t res; + + pthread_mutex_lock(&flb_time_zone_mutex); + prev = getenv("TZ"); + if (prev) { + old_tz = flb_strdup(prev); + if (!old_tz) { + pthread_mutex_unlock(&flb_time_zone_mutex); + return (time_t) -1; + } + } + if (setenv("TZ", parser->time_zone, 1) != 0) { + flb_free(old_tz); + pthread_mutex_unlock(&flb_time_zone_mutex); + return (time_t) -1; + } + tzset(); + tmp = src->tm; + tmp.tm_isdst = -1; + res = mktime(&tmp); + if (old_tz) { + setenv("TZ", old_tz, 1); + flb_free(old_tz); + } + else { + unsetenv("TZ"); + } + tzset(); + pthread_mutex_unlock(&flb_time_zone_mutex); + return res; + } +#endif + return flb_parser_tm2time_simple(src, parser->time_system_timezone); +} + static inline uint32_t digits10(uint64_t v) { if (v < 10) return 1; if (v < 100) return 2; @@ -140,6 +203,9 @@ static void flb_interim_parser_destroy(struct flb_parser *parser) if (parser->time_key) { flb_free(parser->time_key); } + if (parser->time_zone) { + flb_free(parser->time_zone); + } mk_list_del(&parser->_head); flb_free(parser); @@ -153,6 +219,7 @@ struct flb_parser *flb_parser_create(const char *name, const char *format, int time_keep, int time_strict, int time_system_timezone, + const char *time_zone, int logfmt_no_bare_keys, struct flb_parser_types *types, int types_len, @@ -231,6 +298,24 @@ struct flb_parser *flb_parser_create(const char *name, const char *format, p->name = flb_strdup(name); +#if defined(FLB_SYSTEM_WINDOWS) + if (time_zone && time_zone[0]) { + flb_error("[parser:%s] time_zone is not supported on Windows", name); + mk_list_del(&p->_head); + flb_free(p->name); + flb_free(p); + return NULL; + } +#endif + + if (time_zone && time_zone[0] && !time_fmt) { + flb_error("[parser:%s] time_zone requires time_format", name); + mk_list_del(&p->_head); + flb_free(p->name); + flb_free(p); + return NULL; + } + if (time_fmt) { p->time_fmt_full = flb_strdup(time_fmt); if (!p->time_fmt_full) { @@ -319,11 +404,33 @@ struct flb_parser *flb_parser_create(const char *name, const char *format, */ p->time_system_timezone = time_system_timezone; + if (time_zone && time_zone[0]) { + if (time_system_timezone) { + flb_error("[parser:%s] time_zone cannot be combined with " + "time_system_timezone", + name); + flb_interim_parser_destroy(p); + return NULL; + } + if (time_offset && time_offset[0]) { + flb_error("[parser:%s] time_zone cannot be combined with " + "time_offset", + name); + flb_interim_parser_destroy(p); + return NULL; + } + p->time_zone = flb_strdup(time_zone); + if (!p->time_zone) { + flb_interim_parser_destroy(p); + return NULL; + } + } + /* * Optional fixed timezone offset, only applied if - * not falling back to system timezone. + * not falling back to system timezone or IANA time_zone. */ - if (!p->time_system_timezone && time_offset) { + if (!p->time_system_timezone && !p->time_zone && time_offset) { diff = 0; len = strlen(time_offset); ret = flb_parser_tzone_offset(time_offset, len, &diff); @@ -367,6 +474,9 @@ void flb_parser_destroy(struct flb_parser *parser) if (parser->time_key) { flb_free(parser->time_key); } + if (parser->time_zone) { + flb_free(parser->time_zone); + } if (parser->types_len != 0) { for (i=0; itypes_len; i++){ flb_free(parser->types[i].key); @@ -492,6 +602,7 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, flb_sds_t time_fmt; flb_sds_t time_key; flb_sds_t time_offset; + flb_sds_t time_zone; flb_sds_t types_str; flb_sds_t tmp_str; int skip_empty; @@ -513,6 +624,7 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, time_fmt = NULL; time_key = NULL; time_offset = NULL; + time_zone = NULL; types_str = NULL; tmp_str = NULL; @@ -582,6 +694,9 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, /* time_offset (UTC offset) */ time_offset = get_parser_key(config, cf, s, "time_offset"); + /* time_zone (IANA region, DST-aware; Unix only) */ + time_zone = get_parser_key(config, cf, s, "time_zone"); + /* logfmt_no_bare_keys */ logfmt_no_bare_keys = FLB_FALSE; tmp_str = get_parser_key(config, cf, s, "logfmt_no_bare_keys"); @@ -605,7 +720,8 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, /* Create the parser context */ if (!flb_parser_create(name, format, regex, skip_empty, time_fmt, time_key, time_offset, time_keep, time_strict, - time_system_timezone, logfmt_no_bare_keys, types, types_len, + time_system_timezone, time_zone, logfmt_no_bare_keys, + types, types_len, decoders, config)) { goto fconf_error; } @@ -627,6 +743,9 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, if (time_offset) { flb_sds_destroy(time_offset); } + if (time_zone) { + flb_sds_destroy(time_zone); + } if (types_str) { flb_sds_destroy(types_str); } @@ -635,8 +754,8 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, return 0; - /* Use early exit before call to flb_parser_create */ - fconf_early_error: + /* Use early exit before call to flb_parser_create */ + fconf_early_error: if (name) { flb_sds_destroy(name); } @@ -663,6 +782,9 @@ int flb_parser_load_parser_definitions(const char *cfg, struct flb_cf *cf, if (time_offset) { flb_sds_destroy(time_offset); } + if (time_zone) { + flb_sds_destroy(time_zone); + } if (types_str) { flb_sds_destroy(types_str); } @@ -1271,7 +1393,12 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, } if (parser->time_with_tz == FLB_FALSE) { - flb_tm_gmtoff(tm) = parser->time_offset; + if (parser->time_zone) { + flb_tm_gmtoff(tm) = 0; + } + else { + flb_tm_gmtoff(tm) = parser->time_offset; + } } return 0; diff --git a/src/flb_parser_json.c b/src/flb_parser_json.c index 203793a7e9e..51976359248 100644 --- a/src/flb_parser_json.c +++ b/src/flb_parser_json.c @@ -211,7 +211,7 @@ int flb_parser_json_do(struct flb_parser *parser, skip = map_size; } else { - time_lookup = flb_parser_tm2time(&tm, parser->time_system_timezone); + time_lookup = flb_parser_tm2time(&tm, parser); } /* Compose a new map without the time_key field */ diff --git a/src/flb_parser_logfmt.c b/src/flb_parser_logfmt.c index ad372a07121..371b412f2cc 100644 --- a/src/flb_parser_logfmt.c +++ b/src/flb_parser_logfmt.c @@ -166,7 +166,7 @@ static int logfmt_parser(struct flb_parser *parser, parser->name, parser->time_fmt_full); return -1; } - *time_lookup = flb_parser_tm2time(&tm, parser->time_system_timezone); + *time_lookup = flb_parser_tm2time(&tm, parser); } time_found = FLB_TRUE; } diff --git a/src/flb_parser_ltsv.c b/src/flb_parser_ltsv.c index ea099d83bf6..ab6bc2ca976 100644 --- a/src/flb_parser_ltsv.c +++ b/src/flb_parser_ltsv.c @@ -139,7 +139,7 @@ static int ltsv_parser(struct flb_parser *parser, parser->name, parser->time_fmt_full); return -1; } - *time_lookup = flb_parser_tm2time(&tm, parser->time_system_timezone); + *time_lookup = flb_parser_tm2time(&tm, parser); } time_found = FLB_TRUE; } diff --git a/src/flb_parser_regex.c b/src/flb_parser_regex.c index b1baee2d693..a6540ad21c0 100644 --- a/src/flb_parser_regex.c +++ b/src/flb_parser_regex.c @@ -87,7 +87,7 @@ static void cb_results(const char *name, const char *value, } pcb->time_frac = frac; - pcb->time_lookup = flb_parser_tm2time(&tm, parser->time_system_timezone); + pcb->time_lookup = flb_parser_tm2time(&tm, parser); if (parser->time_keep == FLB_FALSE) { pcb->num_skipped++; diff --git a/src/multiline/flb_ml_parser_cri.c b/src/multiline/flb_ml_parser_cri.c index 3c8d585c120..6531a5c794d 100644 --- a/src/multiline/flb_ml_parser_cri.c +++ b/src/multiline/flb_ml_parser_cri.c @@ -41,6 +41,7 @@ static struct flb_parser *cri_parser_create(struct flb_config *config) FLB_TRUE, /* time keep */ FLB_FALSE, /* time strict */ FLB_FALSE, /* time system timezone */ + NULL, /* time zone (IANA) */ FLB_FALSE, /* no bare keys */ NULL, /* parser types */ 0, /* types len */ diff --git a/src/multiline/flb_ml_parser_docker.c b/src/multiline/flb_ml_parser_docker.c index 0f1c4f2d5d9..558abdb9427 100644 --- a/src/multiline/flb_ml_parser_docker.c +++ b/src/multiline/flb_ml_parser_docker.c @@ -36,6 +36,7 @@ static struct flb_parser *docker_parser_create(struct flb_config *config) FLB_TRUE, /* time keep */ FLB_FALSE, /* time strict */ FLB_FALSE, /* time system timezone */ + NULL, /* time zone (IANA) */ FLB_FALSE, /* no bare keys */ NULL, /* parser types */ 0, /* types len */ diff --git a/tests/internal/fuzzers/engine_fuzzer.c b/tests/internal/fuzzers/engine_fuzzer.c index 5d7e9ab802b..f91ad170705 100644 --- a/tests/internal/fuzzers/engine_fuzzer.c +++ b/tests/internal/fuzzers/engine_fuzzer.c @@ -143,7 +143,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv) { parser = flb_parser_create("timestamp", "regex", "^(?