diff --git a/src/collectors/cgroups.plugin/cgroup-charts.c b/src/collectors/cgroups.plugin/cgroup-charts.c index 52639e3c15..032099b5b2 100644 --- a/src/collectors/cgroups.plugin/cgroup-charts.c +++ b/src/collectors/cgroups.plugin/cgroup-charts.c @@ -449,7 +449,7 @@ void update_mem_pgfaults_chart(struct cgroup *cg) { } void update_mem_usage_limit_chart(struct cgroup *cg, unsigned long long memory_limit) { - if (is_cgroup_systemd_service(cg)) + if (is_cgroup_systemd_service(cg) || !memory_limit) return; RRDSET *chart = cg->st_mem_usage_limit; @@ -488,7 +488,7 @@ void update_mem_usage_limit_chart(struct cgroup *cg, unsigned long long memory_l } void update_mem_utilization_chart(struct cgroup *cg, unsigned long long memory_limit) { - if (is_cgroup_systemd_service(cg)) + if (is_cgroup_systemd_service(cg) || !memory_limit) return; RRDSET *chart = cg->st_mem_utilization; diff --git a/src/health/health_variable.c b/src/health/health_variable.c index 69637de64d..5d4f5d5883 100644 --- a/src/health/health_variable.c +++ b/src/health/health_variable.c @@ -36,7 +36,7 @@ struct variable_lookup_job { }; static void variable_lookup_add_result_with_score(struct variable_lookup_job *vbd, NETDATA_DOUBLE n, RRDSET *st, const char *source __maybe_unused) { - if(vbd->score.last_rrdset != st) { + if(vbd->score.last_rrdset != st && vbd->rc->rrdset) { vbd->score.last_rrdset = st; vbd->score.last_score = rrdlabels_common_count(vbd->rc->rrdset->rrdlabels, st->rrdlabels); } diff --git a/src/libnetdata/datetime/rfc3339.c b/src/libnetdata/datetime/rfc3339.c index ab2743e5a2..980d717235 100644 --- a/src/libnetdata/datetime/rfc3339.c +++ b/src/libnetdata/datetime/rfc3339.c @@ -4,8 +4,71 @@ #include "rfc3339.h" +// Helper functions for safe printing of date/time components +// These functions don't add a null terminator and return only the exact count of bytes written + +static inline size_t print_4digit_year(char *buffer, size_t size, int year) { + if (!buffer || size < 4) return 0; // Need at least 4 digits + + // Ensure year is in valid range (0-9999) + year = year < 0 ? 0 : (year > 9999 ? 9999 : year); + + buffer[0] = '0' + (year / 1000) % 10; + buffer[1] = '0' + (year / 100) % 10; + buffer[2] = '0' + (year / 10) % 10; + buffer[3] = '0' + year % 10; + + return 4; +} + +static inline size_t print_2digit(char *buffer, size_t size, int value) { + if (!buffer || size < 2) return 0; // Need at least 2 digits + + // Ensure value is in valid range (0-99) + value = value < 0 ? 0 : (value > 99 ? 99 : value); + + buffer[0] = '0' + (value / 10) % 10; + buffer[1] = '0' + value % 10; + + return 2; +} + +static inline size_t print_fraction(char *buffer, size_t size, usec_t fraction, size_t digits) { + if (!buffer || size < digits) return 0; + + // Validate and cap the number of digits + digits = digits < 1 ? 1 : (digits > 9 ? 9 : digits); + + // Calculate divisor to get correct precision + usec_t divisor = 1; + for (size_t i = 0; i < 6 - digits; i++) + divisor *= 10; + + // Calculate the fraction to print + fraction = fraction / divisor; + + // Ensure fraction won't exceed the requested number of digits + usec_t max_value = 1; + for (size_t i = 0; i < digits; i++) + max_value *= 10; + max_value--; + + fraction = fraction > max_value ? max_value : fraction; + + // Print the fraction with leading zeros + usec_t remaining = fraction; + + // Setup working backwards from least significant digit + for (int i = digits - 1; i >= 0; i--) { + buffer[i] = '0' + (remaining % 10); + remaining /= 10; + } + + return digits; +} + size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fractional_digits, bool utc) { - if (!buffer || len == 0) + if (!buffer || len < 20) // Minimum size for YYYY-MM-DDThh:mm:ssZ return 0; time_t t = (time_t)(now_ut / USEC_PER_SEC); @@ -21,43 +84,108 @@ size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fract return 0; } - size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp); - if (used_length == 0) { - buffer[0] = '\0'; - return 0; - } + size_t pos = 0; - if (fractional_digits >= 1 && fractional_digits <= 9) { - int fractional_part = (int)(now_ut % USEC_PER_SEC); - if (fractional_part && len - used_length > fractional_digits + 1) { - char format[] = ".%01d"; - format[3] = (char)('0' + fractional_digits); + // Year (4 digits) + if (len - pos < 4) goto finish; + pos += print_4digit_year(&buffer[pos], len - pos, tmp->tm_year + 1900); - // Adjust fractional part - fractional_part /= (int)pow(10, 6 - fractional_digits); + // Month separator + if (len - pos < 1) goto finish; + buffer[pos++] = '-'; - used_length += snprintf(buffer + used_length, len - used_length, - format, fractional_part); + // Month (2 digits) + if (len - pos < 2) goto finish; + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_mon + 1); + + // Day separator + if (len - pos < 1) goto finish; + buffer[pos++] = '-'; + + // Day (2 digits) + if (len - pos < 2) goto finish; + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_mday); + + // T separator + if (len - pos < 1) goto finish; + buffer[pos++] = 'T'; + + // Hour (2 digits) + if (len - pos < 2) goto finish; + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_hour); + + // Minute separator + if (len - pos < 1) goto finish; + buffer[pos++] = ':'; + + // Minute (2 digits) + if (len - pos < 2) goto finish; + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_min); + + // Second separator + if (len - pos < 1) goto finish; + buffer[pos++] = ':'; + + // Second (2 digits) + if (len - pos < 2) goto finish; + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_sec); + + // Add fractional part if requested + if (fractional_digits > 9) fractional_digits = 9; + if (fractional_digits) { + usec_t fractional_part = now_ut % USEC_PER_SEC; + + if (fractional_part > 0) { + // Need space for decimal point and digits + if (len - pos < fractional_digits + 1) goto finish; + + buffer[pos++] = '.'; + pos += print_fraction(&buffer[pos], len - pos, fractional_part, fractional_digits); } } + // Add timezone information if (utc) { - if (used_length + 1 < len) { - buffer[used_length++] = 'Z'; - buffer[used_length] = '\0'; - } + if (len - pos < 1) goto finish; + buffer[pos++] = 'Z'; } else { long offset = tmbuf.tm_gmtoff; int hours = (int)(offset / 3600); int minutes = abs((int)((offset % 3600) / 60)); - if (used_length + 7 < len) { // Space for "+HH:MM\0" - used_length += snprintf(buffer + used_length, len - used_length, "%+03d:%02d", hours, minutes); + // Check if timezone is UTC + if (hours == 0 && minutes == 0) { + if (len - pos < 1) goto finish; + buffer[pos++] = 'Z'; + } + else { + // Need space for sign, hours, colon, minutes (6 chars total) + if (len - pos < 6) goto finish; + + // Add timezone offset + buffer[pos++] = (hours >= 0) ? '+' : '-'; + hours = abs(hours); + + // Hours with leading zero + pos += print_2digit(&buffer[pos], len - pos, hours); + + // Colon + buffer[pos++] = ':'; + + // Minutes with leading zero + pos += print_2digit(&buffer[pos], len - pos, minutes); } } - return used_length; +finish: + // Ensure null termination + if (pos < len) + buffer[pos] = '\0'; + else + buffer[len - 1] = '\0'; + + return pos; } usec_t rfc3339_parse_ut(const char *rfc3339, char **endptr) {