diff options
Diffstat (limited to '')
-rw-r--r-- | libnetdata/buffer/buffer.c | 61 | ||||
-rw-r--r-- | libnetdata/buffer/buffer.h | 55 | ||||
-rw-r--r-- | libnetdata/buffered_reader/Makefile.am | 8 | ||||
-rw-r--r-- | libnetdata/buffered_reader/README.md | 0 | ||||
-rw-r--r-- | libnetdata/buffered_reader/buffered_reader.c | 3 | ||||
-rw-r--r-- | libnetdata/buffered_reader/buffered_reader.h | 146 |
6 files changed, 235 insertions, 38 deletions
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c index feee72fcf..64f9cce47 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... @@ -301,12 +288,15 @@ void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value strncpyz(wb->json.key_quote, key_quote, BUFFER_QUOTE_MAX_SIZE); strncpyz(wb->json.value_quote, value_quote, BUFFER_QUOTE_MAX_SIZE); - wb->json.options = options; wb->json.depth = (int8_t)(depth - 1); _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT); if(add_anonymous_object) buffer_fast_strcat(wb, "{", 1); + else + options |= BUFFER_JSON_OPTIONS_NON_ANONYMOUS; + + wb->json.options = options; wb->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(wb); @@ -316,9 +306,14 @@ void buffer_json_finalize(BUFFER *wb) { while(wb->json.depth >= 0) { switch(wb->json.stack[wb->json.depth].type) { case BUFFER_JSON_OBJECT: - buffer_json_object_close(wb); + if (wb->json.depth == 0) + if (!(wb->json.options & BUFFER_JSON_OPTIONS_NON_ANONYMOUS)) + buffer_json_object_close(wb); + else + _buffer_json_depth_pop(wb); + else + buffer_json_object_close(wb); break; - case BUFFER_JSON_ARRAY: buffer_json_array_close(wb); break; diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h index 26efe0070..88d3f0282 100644 --- a/libnetdata/buffer/buffer.h +++ b/libnetdata/buffer/buffer.h @@ -72,6 +72,7 @@ typedef enum __attribute__ ((__packed__)) { BUFFER_JSON_OPTIONS_DEFAULT = 0, BUFFER_JSON_OPTIONS_MINIFY = (1 << 0), BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS = (1 << 1), + BUFFER_JSON_OPTIONS_NON_ANONYMOUS = (1 << 2), } BUFFER_JSON_OPTIONS; typedef struct web_buffer { @@ -93,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) @@ -134,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); @@ -209,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; @@ -282,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; @@ -809,8 +836,13 @@ static inline void buffer_json_member_add_boolean(BUFFER *wb, const char *key, b static inline void buffer_json_member_add_array(BUFFER *wb, const char *key) { buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":[", 2); + if (key) { + buffer_print_json_key(wb, key); + buffer_fast_strcat(wb, ":[", 2); + } + else + buffer_fast_strcat(wb, "[", 1); + wb->json.stack[wb->json.depth].count++; _buffer_json_depth_push(wb, BUFFER_JSON_ARRAY); @@ -860,6 +892,13 @@ static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t value) wb->json.stack[wb->json.depth].count++; } +static inline void buffer_json_add_array_item_boolean(BUFFER *wb, bool value) { + buffer_print_json_comma_newline_spacing(wb); + + buffer_strcat(wb, value ? "true" : "false"); + wb->json.stack[wb->json.depth].count++; +} + static inline void buffer_json_add_array_item_time_t(BUFFER *wb, time_t value) { buffer_print_json_comma_newline_spacing(wb); @@ -959,12 +998,14 @@ typedef enum __attribute__((packed)) { RRDF_FIELD_OPTS_STICKY = (1 << 2), // the field should be sticky RRDF_FIELD_OPTS_FULL_WIDTH = (1 << 3), // the field should get full width RRDF_FIELD_OPTS_WRAP = (1 << 4), // the field should wrap - RRDR_FIELD_OPTS_DUMMY = (1 << 5), // not a presentable field + RRDF_FIELD_OPTS_DUMMY = (1 << 5), // not a presentable field + RRDF_FIELD_OPTS_EXPANDED_FILTER = (1 << 6), // show the filter expanded } RRDF_FIELD_OPTIONS; typedef enum __attribute__((packed)) { RRDF_FIELD_TYPE_NONE, RRDF_FIELD_TYPE_INTEGER, + RRDF_FIELD_TYPE_BOOLEAN, RRDF_FIELD_TYPE_STRING, RRDF_FIELD_TYPE_DETAIL_STRING, RRDF_FIELD_TYPE_BAR_WITH_INTEGER, @@ -982,6 +1023,9 @@ static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) { case RRDF_FIELD_TYPE_INTEGER: return "integer"; + case RRDF_FIELD_TYPE_BOOLEAN: + return "boolean"; + case RRDF_FIELD_TYPE_STRING: return "string"; @@ -1112,7 +1156,7 @@ static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summar } typedef enum __attribute__((packed)) { - RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_FILTER_NONE = 0, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_FILTER_MULTISELECT, RRDF_FIELD_FILTER_FACET, @@ -1173,8 +1217,9 @@ buffer_rrdf_table_add_field(BUFFER *wb, size_t field_id, const char *key, const buffer_json_member_add_boolean(wb, "full_width", options & RRDF_FIELD_OPTS_FULL_WIDTH); buffer_json_member_add_boolean(wb, "wrap", options & RRDF_FIELD_OPTS_WRAP); + buffer_json_member_add_boolean(wb, "default_expanded_filter", options & RRDF_FIELD_OPTS_EXPANDED_FILTER); - if(options & RRDR_FIELD_OPTS_DUMMY) + if(options & RRDF_FIELD_OPTS_DUMMY) buffer_json_member_add_boolean(wb, "dummy", true); } buffer_json_object_close(wb); diff --git a/libnetdata/buffered_reader/Makefile.am b/libnetdata/buffered_reader/Makefile.am new file mode 100644 index 000000000..161784b8f --- /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 000000000..e69de29bb --- /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 000000000..7cd17abfe --- /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 000000000..4db57cd29 --- /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 |