summaryrefslogtreecommitdiffstats
path: root/libnetdata
diff options
context:
space:
mode:
Diffstat (limited to 'libnetdata')
-rw-r--r--libnetdata/Makefile.am4
-rw-r--r--libnetdata/buffer/buffer.c47
-rw-r--r--libnetdata/buffer/buffer.h26
-rw-r--r--libnetdata/buffered_reader/Makefile.am8
-rw-r--r--libnetdata/buffered_reader/README.md0
-rw-r--r--libnetdata/buffered_reader/buffered_reader.c3
-rw-r--r--libnetdata/buffered_reader/buffered_reader.h146
-rw-r--r--libnetdata/clocks/clocks.c20
-rw-r--r--libnetdata/datetime/Makefile.am8
-rw-r--r--libnetdata/datetime/README.md11
-rw-r--r--libnetdata/datetime/iso8601.c81
-rw-r--r--libnetdata/datetime/iso8601.h18
-rw-r--r--libnetdata/datetime/rfc7231.c29
-rw-r--r--libnetdata/datetime/rfc7231.h12
-rw-r--r--libnetdata/functions_evloop/functions_evloop.c6
-rw-r--r--libnetdata/inlined.h36
-rw-r--r--libnetdata/libnetdata.c51
-rw-r--r--libnetdata/libnetdata.h106
-rw-r--r--libnetdata/line_splitter/Makefile.am8
-rw-r--r--libnetdata/line_splitter/README.md14
-rw-r--r--libnetdata/line_splitter/line_splitter.c69
-rw-r--r--libnetdata/line_splitter/line_splitter.h120
-rw-r--r--libnetdata/log/Makefile.am1
-rw-r--r--libnetdata/log/README.md194
-rw-r--r--libnetdata/log/journal.c138
-rw-r--r--libnetdata/log/journal.h18
-rw-r--r--libnetdata/log/log.c3006
-rw-r--r--libnetdata/log/log.h300
-rw-r--r--libnetdata/log/log2journal.c1015
-rw-r--r--libnetdata/log/log2journal.md518
-rw-r--r--libnetdata/log/systemd-cat-native.c781
-rw-r--r--libnetdata/log/systemd-cat-native.h8
-rw-r--r--libnetdata/socket/security.c41
-rw-r--r--libnetdata/socket/socket.c524
-rw-r--r--libnetdata/threads/threads.c36
-rw-r--r--libnetdata/uuid/Makefile.am8
-rw-r--r--libnetdata/uuid/README.md13
-rw-r--r--libnetdata/uuid/uuid.c179
-rw-r--r--libnetdata/uuid/uuid.h29
39 files changed, 6194 insertions, 1438 deletions
diff --git a/libnetdata/Makefile.am b/libnetdata/Makefile.am
index f01a1d34f0..0a854cd443 100644
--- a/libnetdata/Makefile.am
+++ b/libnetdata/Makefile.am
@@ -8,9 +8,11 @@ SUBDIRS = \
aral \
avl \
buffer \
+ buffered_reader \
clocks \
completion \
config \
+ datetime \
dictionary \
ebpf \
eval \
@@ -19,6 +21,7 @@ SUBDIRS = \
json \
july \
health \
+ line_splitter \
locks \
log \
onewayalloc \
@@ -31,6 +34,7 @@ SUBDIRS = \
string \
threads \
url \
+ uuid \
worker_utilization \
tests \
$(NULL)
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c
index 488e7dd24f..64f9cce47c 100644
--- a/libnetdata/buffer/buffer.c
+++ b/libnetdata/buffer/buffer.c
@@ -81,6 +81,7 @@ void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
va_list args;
va_start(args, fmt);
+ // vsnprintfz() returns the number of bytes actually written - after possible truncation
wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
va_end(args);
@@ -89,53 +90,39 @@ void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
// the buffer is \0 terminated by vsnprintfz
}
-void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
-{
+inline void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args) {
if(unlikely(!fmt || !*fmt)) return;
- size_t wrote = 0, need = 2, space_remaining = 0;
+ size_t full_size_bytes = 0, need = 2, space_remaining = 0;
do {
- need += space_remaining * 2;
+ need += full_size_bytes + 2;
- netdata_log_debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
buffer_need_bytes(wb, need);
space_remaining = wb->size - wb->len - 1;
- wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
+ // Use the copy of va_list for vsnprintf
+ va_list args_copy;
+ va_copy(args_copy, args);
+ // vsnprintf() returns the number of bytes required, even if bigger than the buffer provided
+ full_size_bytes = (size_t) vsnprintf(&wb->buffer[wb->len], space_remaining, fmt, args_copy);
+ va_end(args_copy);
- } while(wrote >= space_remaining);
+ } while(full_size_bytes >= space_remaining);
- wb->len += wrote;
+ wb->len += full_size_bytes;
- // the buffer is \0 terminated by vsnprintf
+ wb->buffer[wb->len] = '\0';
+ buffer_overflow_check(wb);
}
void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
{
- if(unlikely(!fmt || !*fmt)) return;
-
va_list args;
- size_t wrote = 0, need = 2, space_remaining = 0;
-
- do {
- need += space_remaining * 2;
-
- netdata_log_debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
- buffer_need_bytes(wb, need);
-
- space_remaining = wb->size - wb->len - 1;
-
- va_start(args, fmt);
- wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
- va_end(args);
-
- } while(wrote >= space_remaining);
-
- wb->len += wrote;
-
- // the buffer is \0 terminated by vsnprintf
+ va_start(args, fmt);
+ buffer_vsprintf(wb, fmt, args);
+ va_end(args);
}
// generate a javascript date, the fastest possible way...
diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h
index e5dd9992d1..88d3f0282a 100644
--- a/libnetdata/buffer/buffer.h
+++ b/libnetdata/buffer/buffer.h
@@ -94,6 +94,8 @@ typedef struct web_buffer {
} json;
} BUFFER;
+#define CLEAN_BUFFER _cleanup_(buffer_freep) BUFFER
+
#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0)
@@ -135,6 +137,10 @@ BUFFER *buffer_create(size_t size, size_t *statistics);
void buffer_free(BUFFER *b);
void buffer_increase(BUFFER *b, size_t free_size_required);
+static inline void buffer_freep(BUFFER **bp) {
+ if(bp) buffer_free(*bp);
+}
+
void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4);
void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args);
void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3);
@@ -210,6 +216,13 @@ static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
buffer_overflow_check(wb);
}
+static inline void buffer_putc(BUFFER *wb, char c) {
+ buffer_need_bytes(wb, 2);
+ wb->buffer[wb->len++] = c;
+ wb->buffer[wb->len] = '\0';
+ buffer_overflow_check(wb);
+}
+
static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
if(unlikely(!txt || !*txt || !len)) return;
@@ -283,6 +296,19 @@ static inline void buffer_strncat(BUFFER *wb, const char *txt, size_t len) {
buffer_overflow_check(wb);
}
+static inline void buffer_memcat(BUFFER *wb, const void *mem, size_t bytes) {
+ if(unlikely(!mem)) return;
+
+ buffer_need_bytes(wb, bytes + 1);
+
+ memcpy(&wb->buffer[wb->len], mem, bytes);
+
+ wb->len += bytes;
+ wb->buffer[wb->len] = '\0';
+
+ buffer_overflow_check(wb);
+}
+
static inline void buffer_json_strcat(BUFFER *wb, const char *txt) {
if(unlikely(!txt || !*txt)) return;
diff --git a/libnetdata/buffered_reader/Makefile.am b/libnetdata/buffered_reader/Makefile.am
new file mode 100644
index 0000000000..161784b8f6
--- /dev/null
+++ b/libnetdata/buffered_reader/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/libnetdata/buffered_reader/README.md b/libnetdata/buffered_reader/README.md
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libnetdata/buffered_reader/README.md
diff --git a/libnetdata/buffered_reader/buffered_reader.c b/libnetdata/buffered_reader/buffered_reader.c
new file mode 100644
index 0000000000..7cd17abfe7
--- /dev/null
+++ b/libnetdata/buffered_reader/buffered_reader.c
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
diff --git a/libnetdata/buffered_reader/buffered_reader.h b/libnetdata/buffered_reader/buffered_reader.h
new file mode 100644
index 0000000000..4db57cd29e
--- /dev/null
+++ b/libnetdata/buffered_reader/buffered_reader.h
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+#ifndef NETDATA_BUFFERED_READER_H
+#define NETDATA_BUFFERED_READER_H
+
+struct buffered_reader {
+ ssize_t read_len;
+ ssize_t pos;
+ char read_buffer[PLUGINSD_LINE_MAX + 1];
+};
+
+static inline void buffered_reader_init(struct buffered_reader *reader) {
+ reader->read_buffer[0] = '\0';
+ reader->read_len = 0;
+ reader->pos = 0;
+}
+
+typedef enum {
+ BUFFERED_READER_READ_OK = 0,
+ BUFFERED_READER_READ_FAILED = -1,
+ BUFFERED_READER_READ_BUFFER_FULL = -2,
+ BUFFERED_READER_READ_POLLERR = -3,
+ BUFFERED_READER_READ_POLLHUP = -4,
+ BUFFERED_READER_READ_POLLNVAL = -5,
+ BUFFERED_READER_READ_POLL_UNKNOWN = -6,
+ BUFFERED_READER_READ_POLL_TIMEOUT = -7,
+ BUFFERED_READER_READ_POLL_FAILED = -8,
+} buffered_reader_ret_t;
+
+
+static inline buffered_reader_ret_t buffered_reader_read(struct buffered_reader *reader, int fd) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(reader->read_buffer[reader->read_len] != '\0')
+ fatal("read_buffer does not start with zero");
+#endif
+
+ char *read_at = reader->read_buffer + reader->read_len;
+ ssize_t remaining = sizeof(reader->read_buffer) - reader->read_len - 1;
+
+ if(unlikely(remaining <= 0))
+ return BUFFERED_READER_READ_BUFFER_FULL;
+
+ ssize_t bytes_read = read(fd, read_at, remaining);
+ if(unlikely(bytes_read <= 0))
+ return BUFFERED_READER_READ_FAILED;
+
+ reader->read_len += bytes_read;
+ reader->read_buffer[reader->read_len] = '\0';
+
+ return BUFFERED_READER_READ_OK;
+}
+
+static inline buffered_reader_ret_t buffered_reader_read_timeout(struct buffered_reader *reader, int fd, int timeout_ms, bool log_error) {
+ errno = 0;
+ struct pollfd fds[1];
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+
+ int ret = poll(fds, 1, timeout_ms);
+
+ if (ret > 0) {
+ /* There is data to read */
+ if (fds[0].revents & POLLIN)
+ return buffered_reader_read(reader, fd);
+
+ else if(fds[0].revents & POLLERR) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLERR.");
+ return BUFFERED_READER_READ_POLLERR;
+ }
+ else if(fds[0].revents & POLLHUP) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLHUP.");
+ return BUFFERED_READER_READ_POLLHUP;
+ }
+ else if(fds[0].revents & POLLNVAL) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLNVAL.");
+ return BUFFERED_READER_READ_POLLNVAL;
+ }
+
+ if(log_error)
+ netdata_log_error("PARSER: poll() returned positive number, but POLLIN|POLLERR|POLLHUP|POLLNVAL are not set.");
+ return BUFFERED_READER_READ_POLL_UNKNOWN;
+ }
+ else if (ret == 0) {
+ if(log_error)
+ netdata_log_error("PARSER: timeout while waiting for data.");
+ return BUFFERED_READER_READ_POLL_TIMEOUT;
+ }
+
+ if(log_error)
+ netdata_log_error("PARSER: poll() failed with code %d.", ret);
+ return BUFFERED_READER_READ_POLL_FAILED;
+}
+
+/* Produce a full line if one exists, statefully return where we start next time.
+ * When we hit the end of the buffer with a partial line move it to the beginning for the next fill.
+ */
+static inline bool buffered_reader_next_line(struct buffered_reader *reader, BUFFER *dst) {
+ buffer_need_bytes(dst, reader->read_len - reader->pos + 2);
+
+ size_t start = reader->pos;
+
+ char *ss = &reader->read_buffer[start];
+ char *se = &reader->read_buffer[reader->read_len];
+ char *ds = &dst->buffer[dst->len];
+ char *de = &ds[dst->size - dst->len - 2];
+
+ if(ss >= se) {
+ *ds = '\0';
+ reader->pos = 0;
+ reader->read_len = 0;
+ reader->read_buffer[reader->read_len] = '\0';
+ return false;
+ }
+
+ // copy all bytes to buffer
+ while(ss < se && ds < de && *ss != '\n') {
+ *ds++ = *ss++;
+ dst->len++;
+ }
+
+ // if we have a newline, return the buffer
+ if(ss < se && ds < de && *ss == '\n') {
+ // newline found in the r->read_buffer
+
+ *ds++ = *ss++; // copy the newline too
+ dst->len++;
+
+ *ds = '\0';
+
+ reader->pos = ss - reader->read_buffer;
+ return true;
+ }
+
+ reader->pos = 0;
+ reader->read_len = 0;
+ reader->read_buffer[reader->read_len] = '\0';
+ return false;
+}
+
+#endif //NETDATA_BUFFERED_READER_H
diff --git a/libnetdata/clocks/clocks.c b/libnetdata/clocks/clocks.c
index 489e968553..c77fbb2387 100644
--- a/libnetdata/clocks/clocks.c
+++ b/libnetdata/clocks/clocks.c
@@ -298,8 +298,10 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) {
// TODO: The heartbeat tick should be specified at the heartbeat_init() function
usec_t tmp = (now_realtime_usec() * clock_realtime_resolution) % (tick / 2);
- error_limit_static_global_var(erl, 10, 0);
- error_limit(&erl, "heartbeat randomness of %"PRIu64" is too big for a tick of %"PRIu64" - setting it to %"PRIu64"", hb->randomness, tick, tmp);
+ nd_log_limit_static_global_var(erl, 10, 0);
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
+ "heartbeat randomness of %"PRIu64" is too big for a tick of %"PRIu64" - setting it to %"PRIu64"",
+ hb->randomness, tick, tmp);
hb->randomness = tmp;
}
@@ -325,13 +327,19 @@ usec_t heartbeat_next(heartbeat_t *hb, usec_t tick) {
if(unlikely(now < next)) {
errno = 0;
- error_limit_static_global_var(erl, 10, 0);
- error_limit(&erl, "heartbeat clock: woke up %"PRIu64" microseconds earlier than expected (can be due to the CLOCK_REALTIME set to the past).", next - now);
+ nd_log_limit_static_global_var(erl, 10, 0);
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
+ "heartbeat clock: woke up %"PRIu64" microseconds earlier than expected "
+ "(can be due to the CLOCK_REALTIME set to the past).",
+ next - now);
}
else if(unlikely(now - next > tick / 2)) {
errno = 0;
- error_limit_static_global_var(erl, 10, 0);
- error_limit(&erl, "heartbeat clock: woke up %"PRIu64" microseconds later than expected (can be due to system load or the CLOCK_REALTIME set to the future).", now - next);
+ nd_log_limit_static_global_var(erl, 10, 0);
+ nd_log_limit(&erl, NDLS_DAEMON, NDLP_NOTICE,
+ "heartbeat clock: woke up %"PRIu64" microseconds later than expected "
+ "(can be due to system load or the CLOCK_REALTIME set to the future).",
+ now - next);
}
if(unlikely(!hb->realtime)) {
diff --git a/libnetdata/datetime/Makefile.am b/libnetdata/datetime/Makefile.am
new file mode 100644
index 0000000000..161784b8f6
--- /dev/null
+++ b/libnetdata/datetime/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/libnetdata/datetime/README.md b/libnetdata/datetime/README.md
new file mode 100644
index 0000000000..ba23663162
--- /dev/null
+++ b/libnetdata/datetime/README.md
@@ -0,0 +1,11 @@
+<!--
+title: "Datetime"
+custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/datetime/README.md
+sidebar_label: "Datetime"
+learn_topic_type: "Tasks"
+learn_rel_path: "Developers/libnetdata"
+-->
+
+# Datetime
+
+Formatting dates and timestamps.
diff --git a/libnetdata/datetime/iso8601.c b/libnetdata/datetime/iso8601.c
new file mode 100644
index 0000000000..8e3f4e0276
--- /dev/null
+++ b/libnetdata/datetime/iso8601.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+size_t iso8601_datetime_ut(char *buffer, size_t len, usec_t now_ut, ISO8601_OPTIONS options) {
+ if(unlikely(!buffer || len == 0))
+ return 0;
+
+ time_t t = (time_t)(now_ut / USEC_PER_SEC);
+ struct tm *tmp, tmbuf;
+
+ if(options & ISO8601_UTC)
+ // Use gmtime_r for UTC time conversion.
+ tmp = gmtime_r(&t, &tmbuf);
+ else
+ // Use localtime_r for local time conversion.
+ tmp = localtime_r(&t, &tmbuf);
+
+ if (unlikely(!tmp)) {
+ buffer[0] = '\0';
+ return 0;
+ }
+
+ // Format the date and time according to the ISO 8601 format.
+ size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp);
+ if (unlikely(used_length == 0)) {
+ buffer[0] = '\0';
+ return 0;
+ }
+
+ if(options & ISO8601_MILLISECONDS) {
+ // Calculate the remaining microseconds
+ int milliseconds = (int) ((now_ut % USEC_PER_SEC) / USEC_PER_MS);
+ if(milliseconds && len - used_length > 4)
+ used_length += snprintfz(buffer + used_length, len - used_length, ".%03d", milliseconds);
+ }
+ else if(options & ISO8601_MICROSECONDS) {
+ // Calculate the remaining microseconds
+ int microseconds = (int) (now_ut % USEC_PER_SEC);
+ if(microseconds && len - used_length > 7)
+ used_length += snprintfz(buffer + used_length, len - used_length, ".%06d", microseconds);
+ }
+
+ if(options & ISO8601_UTC) {
+ if(used_length + 1 < len) {
+ buffer[used_length++] = 'Z';
+ buffer[used_length] = '\0'; // null-terminate the string.
+ }
+ }
+ else {
+ // Calculate the timezone offset in hours and minutes from UTC.
+ long offset = tmbuf.tm_gmtoff;
+ int hours = (int) (offset / 3600); // Convert offset seconds to hours.
+ int minutes = (int) ((offset % 3600) / 60); // Convert remainder to minutes (keep the sign for minutes).
+
+ // Check if timezone is UTC.
+ if(hours == 0 && minutes == 0) {
+ // For UTC, append 'Z' to the timestamp.
+ if(used_length + 1 < len) {
+ buffer[used_length++] = 'Z';
+ buffer[used_length] = '\0'; // null-terminate the string.
+ }
+ }
+ else {
+ // For non-UTC, format the timezone offset. Omit minutes if they are zero.
+ if(minutes == 0) {
+ // Check enough space is available for the timezone offset string.
+ if(used_length + 3 < len) // "+hh\0"
+ used_length += snprintfz(buffer + used_length, len - used_length, "%+03d", hours);
+ }
+ else {
+ // Check enough space is available for the timezone offset string.
+ if(used_length + 6 < len) // "+hh:mm\0"
+ used_length += snprintfz(buffer + used_length, len - used_length,
+ "%+03d:%02d", hours, abs(minutes));
+ }
+ }
+ }
+
+ return used_length;
+}
diff --git a/libnetdata/datetime/iso8601.h b/libnetdata/datetime/iso8601.h
new file mode 100644
index 0000000000..ce4800963a
--- /dev/null
+++ b/libnetdata/datetime/iso8601.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+#ifndef NETDATA_ISO8601_H
+#define NETDATA_ISO8601_H
+
+typedef enum __attribute__((__packed__)) {
+ ISO8601_UTC = (1 << 0),
+ ISO8601_LOCAL_TIMEZONE = (1 << 1),
+ ISO8601_MILLISECONDS = (1 << 2),
+ ISO8601_MICROSECONDS = (1 << 3),
+} ISO8601_OPTIONS;
+
+#define ISO8601_MAX_LENGTH 64
+size_t iso8601_datetime_ut(char *buffer, size_t len, usec_t now_ut, ISO8601_OPTIONS options);
+
+#endif //NETDATA_ISO8601_H
diff --git a/libnetdata/datetime/rfc7231.c b/libnetdata/datetime/rfc7231.c
new file mode 100644
index 0000000000..4925ed2c95
--- /dev/null
+++ b/libnetdata/datetime/rfc7231.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+inline size_t rfc7231_datetime(char *buffer, size_t len, time_t now_t) {
+ if (unlikely(!buffer || !len))
+ return 0;
+
+ struct tm *tmp, tmbuf;
+
+ // Use gmtime_r for UTC time conversion.
+ tmp = gmtime_r(&now_t, &tmbuf);
+
+ if (unlikely(!tmp)) {
+ buffer[0] = '\0';
+ return 0;
+ }
+
+ // Format the date and time according to the RFC 7231 format.
+ size_t ret = strftime(buffer, len, "%a, %d %b %Y %H:%M:%S GMT", tmp);
+ if (unlikely(ret == 0))
+ buffer[0] = '\0';
+
+ return ret;
+}
+
+size_t rfc7231_datetime_ut(char *buffer, size_t len, usec_t now_ut) {
+ return rfc7231_datetime(buffer, len, (time_t) (now_ut / USEC_PER_SEC));
+}
diff --git a/libnetdata/datetime/rfc7231.h b/libnetdata/datetime/rfc7231.h
new file mode 100644
index 0000000000..5ba93053fb
--- /dev/null
+++ b/libnetdata/datetime/rfc7231.h
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+#ifndef NETDATA_RFC7231_H
+#define NETDATA_RFC7231_H
+
+#define RFC7231_MAX_LENGTH 30
+size_t rfc7231_datetime(char *buffer, size_t len, time_t now_t);
+size_t rfc7231_datetime_ut(char *buffer, size_t len, usec_t now_ut);
+
+#endif //NETDATA_RFC7231_H
diff --git a/libnetdata/functions_evloop/functions_evloop.c b/libnetdata/functions_evloop/functions_evloop.c
index 3fcd70aa1f..44f080a9d9 100644
--- a/libnetdata/functions_evloop/functions_evloop.c
+++ b/libnetdata/functions_evloop/functions_evloop.c
@@ -64,6 +64,12 @@ static void *rrd_functions_worker_globals_worker_main(void *arg) {
pthread_mutex_unlock(&wg->worker_mutex);
if(acquired) {
+ ND_LOG_STACK lgs[] = {
+ ND_LOG_FIELD_TXT(NDF_REQUEST, j->cmd),
+ ND_LOG_FIELD_END(),
+ };
+ ND_LOG_STACK_PUSH(lgs);
+
last_acquired = true;
j = dictionary_acquired_item_value(acquired);
j->cb(j->transaction, j->cmd, j->timeout, &j->cancelled);
diff --git a/libnetdata/inlined.h b/libnetdata/inlined.h
index 9c07d97b6d..535b791e3f 100644
--- a/libnetdata/inlined.h
+++ b/libnetdata/inlined.h
@@ -426,13 +426,24 @@ static inline void sanitize_json_string(char *dst, const char *src, size_t dst_s
}
static inline bool sanitize_command_argument_string(char *dst, const char *src, size_t dst_size) {
+ if(dst_size)
+ *dst = '\0';
+
// skip leading dashes
- while (src[0] == '-')
+ while (*src == '-')
src++;
- // escape single quotes
- while (src[0] != '\0') {
- if (src[0] == '\'') {
+ while (*src != '\0') {
+ if (dst_size < 1)
+ return false;
+
+ if (iscntrl(*src) || *src == '$') {
+ // remove control characters and characters that are expanded by bash
+ *dst++ = '_';
+ dst_size--;
+ }
+ else if (*src == '\'' || *src == '`') {
+ // escape single quotes
if (dst_size < 4)
return false;
@@ -440,14 +451,10 @@ static inline bool sanitize_command_argument_string(char *dst, const char *src,
dst += 4;
dst_size -= 4;
- } else {
- if (dst_size < 1)
- return false;
-
- dst[0] = src[0];
-
- dst += 1;
- dst_size -= 1;
+ }
+ else {
+ *dst++ = *src;
+ dst_size--;
}
src++;
@@ -456,6 +463,7 @@ static inline bool sanitize_command_argument_string(char *dst, const char *src,
// make sure we have space to terminate the string
if (dst_size == 0)
return false;
+
*dst = '\0';
return true;
@@ -531,10 +539,6 @@ static inline int read_single_base64_or_hex_number_file(const char *filename, un
}
}
-static inline int uuid_memcmp(const uuid_t *uu1, const uuid_t *uu2) {
- return memcmp(uu1, uu2, sizeof(uuid_t));
-}
-
static inline char *strsep_skip_consecutive_separators(char **ptr,