@cryptotaxi247 / netdata-1 / commits / 1fa603733

Remove netdatacli response size limitation (#14906)

* Dump config * Add charcat and rawcat * Build incoming response in an buffer * Allocate a buffer to hold the command response so that we dont have a 4K char limit * Add a dumpconfig command to output the current netdata.conf * Remove -W dumpconfig for now * Fix typo * Improve help message

Stelios Fragkakis committed Apr 21, 2023 at 16:50 UTC 1fa60373300bbcd796201ee576ea11d3711f69e7
4 files changed +77 -40
cli/cli.c
+22 -27
@@ -10,9 +10,6 @@ static uv_shutdown_t shutdown_req;
10 static char command_string[MAX_COMMAND_LENGTH];
11 static unsigned command_string_size;
12
13 -static char response_string[MAX_COMMAND_LENGTH];
14 -static unsigned response_string_size;
15 -
13 static int exit_status;
14
15 struct command_context {
@@ -24,8 +21,10 @@ struct command_context {
21 cmd_status_t status;
22 };
23
27 -static void parse_command_reply(void)
24 +static void parse_command_reply(BUFFER *buf)
25 {
26 + char *response_string = (char *) buffer_tostring(buf);
27 + unsigned response_string_size = buffer_strlen(buf);
28 FILE *stream = NULL;
29 char *pos;
30 int syntax_error = 0;
@@ -64,28 +63,21 @@ static void parse_command_reply(void)
63
64 static void pipe_read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
65 {
67 - if (0 == nread) {
66 + BUFFER *response = client->data;
67 +
68 + if (0 == nread)
69 fprintf(stderr, "%s: Zero bytes read by command pipe.\n", __func__);
69 - } else if (UV_EOF == nread) {
70 -// fprintf(stderr, "EOF found in command pipe.\n");
71 - parse_command_reply();
72 - } else if (nread < 0) {
73 - fprintf(stderr, "%s: %s\n", __func__, uv_strerror(nread));
70 + else if (UV_EOF == nread)
71 + parse_command_reply(response);
72 + else if (nread < 0) {
73 + fprintf(stderr, "%s: %s\n", __func__, uv_strerror(nread));
74 + (void)uv_read_stop((uv_stream_t *)client);
75 }
76 + else
77 + buffer_fast_rawcat(response, buf->base, nread);
78
76 - if (nread < 0) { /* stop stream due to EOF or error */
77 - (void)uv_read_stop((uv_stream_t *)client);
78 - } else if (nread) {
79 - size_t to_copy;
80 -
81 - to_copy = MIN((unsigned int) nread, MAX_COMMAND_LENGTH - 1 - response_string_size);
82 - memcpy(response_string + response_string_size, buf->base, to_copy);
83 - response_string_size += to_copy;
84 - response_string[response_string_size] = '\0';
85 - }
86 - if (buf && buf->len) {
79 + if (buf && buf->len)
80 free(buf->base);
88 - }
81 }
82
83 static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
@@ -104,8 +96,7 @@ static void shutdown_cb(uv_shutdown_t* req, int status)
96 (void)status;
97
98 /* receive reply */
107 - response_string_size = 0;
108 - response_string[0] = '\0';
99 + client_pipe.data = req->data;
100
101 ret = uv_read_start((uv_stream_t *)&client_pipe, alloc_cb, pipe_read_cb);
102 if (ret) {
@@ -113,16 +104,17 @@ static void shutdown_cb(uv_shutdown_t* req, int status)
104 uv_close((uv_handle_t *)&client_pipe, NULL);
105 return;
106 }
116 -
107 }
108
109 static void pipe_write_cb(uv_write_t* req, int status)
110 {
111 int ret;
112
123 - (void)req;
113 (void)status;
114
115 + uv_pipe_t *clientp = req->data;
116 + shutdown_req.data = clientp->data;
117 +
118 ret = uv_shutdown(&shutdown_req, (uv_stream_t *)&client_pipe, shutdown_cb);
119 if (ret) {
120 fprintf(stderr, "uv_shutdown(): %s\n", uv_strerror(ret));
@@ -144,10 +136,11 @@ static void connect_cb(uv_connect_t* req, int status)
136 exit(-1);
137 }
138 if (0 == command_string_size) {
147 - s = fgets(command_string, MAX_COMMAND_LENGTH, stdin);
139 + s = fgets(command_string, MAX_COMMAND_LENGTH - 1, stdin);
140 }
141 (void)s; /* We don't need input to communicate with the server */
142 command_string_size = strlen(command_string);
143 + client_pipe.data = req->data;
144
145 write_req.data = &client_pipe;
146 write_buf.base = command_string;
@@ -191,11 +184,13 @@ int main(int argc, char **argv)
184 }
185 }
186
187 + req.data = buffer_create(128, NULL);
188 uv_pipe_connect(&req, &client_pipe, PIPENAME, connect_cb);
189
190 uv_run(loop, UV_RUN_DEFAULT);
191
192 uv_close((uv_handle_t *)&client_pipe, NULL);
193 + buffer_free(client_pipe.data);
194
195 return exit_status;
196 }
daemon/commands.c
+25 -13
@@ -47,6 +47,7 @@ static cmd_status_t cmd_write_config_execute(char *args, char **message);
47 static cmd_status_t cmd_ping_execute(char *args, char **message);
48 static cmd_status_t cmd_aclk_state(char *args, char **message);
49 static cmd_status_t cmd_version(char *args, char **message);
50 +static cmd_status_t cmd_dumpconfig(char *args, char **message);
51
52 static command_info_t command_info_array[] = {
53 {"help", cmd_help_execute, CMD_TYPE_HIGH_PRIORITY}, // show help menu
@@ -61,7 +62,8 @@ static command_info_t command_info_array[] = {
62 {"write-config", cmd_write_config_execute, CMD_TYPE_ORTHOGONAL},
63 {"ping", cmd_ping_execute, CMD_TYPE_ORTHOGONAL},
64 {"aclk-state", cmd_aclk_state, CMD_TYPE_ORTHOGONAL},
64 - {"version", cmd_version, CMD_TYPE_ORTHOGONAL}
65 + {"version", cmd_version, CMD_TYPE_ORTHOGONAL},
66 + {"dumpconfig", cmd_dumpconfig, CMD_TYPE_ORTHOGONAL}
67 };
68
69 /* Mutexes for commands of type CMD_TYPE_ORTHOGONAL */
@@ -127,6 +129,8 @@ static cmd_status_t cmd_help_execute(char *args, char **message)
129 " Return with 'pong' if agent is alive.\n"
130 "aclk-state [json]\n"
131 " Returns current state of ACLK and Cloud connection. (optionally in json).\n"
132 + "dumpconfig\n"
133 + " Returns the current netdata.conf on stdout.\n"
134 "version\n"
135 " Returns the netdata version.\n",
136 MAX_COMMAND_LENGTH - 1);
@@ -330,6 +334,17 @@ static cmd_status_t cmd_version(char *args, char **message)
334 return CMD_STATUS_SUCCESS;
335 }
336
337 +static cmd_status_t cmd_dumpconfig(char *args, char **message)
338 +{
339 + (void)args;
340 +
341 + BUFFER *wb = buffer_create(1024, NULL);
342 + config_generate(wb, 0);
343 + *message = strdupz(buffer_tostring(wb));
344 + buffer_free(wb);
345 + return CMD_STATUS_SUCCESS;
346 +}
347 +
348 static void cmd_lock_exclusive(unsigned index)
349 {
350 (void)index;
@@ -393,32 +408,30 @@ static void pipe_write_cb(uv_write_t* req, int status)
408
409 uv_close((uv_handle_t *)client, pipe_close_cb);
410 --clients;
396 - freez(client->data);
411 + buffer_free(client->data);
412 info("Command Clients = %u\n", clients);
413 }
414
400 -static inline void add_char_to_command_reply(char *reply_string, unsigned *reply_string_size, char character)
415 +static inline void add_char_to_command_reply(BUFFER *reply_string, unsigned *reply_string_size, char character)
416 {
402 - reply_string[(*reply_string_size)++] = character;
417 + buffer_fast_charcat(reply_string, character);
418 + *reply_string_size +=1;
419 }
420
405 -static inline void add_string_to_command_reply(char *reply_string, unsigned *reply_string_size, char *str)
421 +static inline void add_string_to_command_reply(BUFFER *reply_string, unsigned *reply_string_size, char *str)
422 {
423 unsigned len;
424
425 len = strlen(str);
410 -
411 - if (MAX_COMMAND_LENGTH - 1 < len + *reply_string_size)
412 - len = MAX_COMMAND_LENGTH - *reply_string_size - 1;
413 -
414 - strncpyz(reply_string + *reply_string_size, str, len);
426 + buffer_fast_strcat(reply_string, str, len);
427 *reply_string_size += len;
428 }
429
430 static void send_command_reply(struct command_context *cmd_ctx, cmd_status_t status, char *message)
431 {
432 int ret;
421 - char *reply_string = mallocz(MAX_COMMAND_LENGTH);
433 + BUFFER *reply_string = buffer_create(128, NULL);
434 +
435 char exit_status_string[MAX_EXIT_STATUS_LENGTH + 1] = {'\0', };
436 unsigned reply_string_size = 0;
437 uv_buf_t write_buf;
@@ -436,13 +449,12 @@ static void send_command_reply(struct command_context *cmd_ctx, cmd_status_t sta
449
450 cmd_ctx->write_req.data = client;
451 client->data = reply_string;
439 - write_buf.base = reply_string;
452 + write_buf.base = reply_string->buffer;
453 write_buf.len = reply_string_size;
454 ret = uv_write(&cmd_ctx->write_req, (uv_stream_t *)client, &write_buf, 1, pipe_write_cb);
455 if (ret) {
456 error("uv_write(): %s", uv_strerror(ret));
457 }
445 - info("COMMAND: Sending reply: \"%s\"", reply_string);
458 }
459
460 cmd_status_t execute_command(cmd_t idx, char *args, char **message)
daemon/commands.h
+1
@@ -26,6 +26,7 @@ typedef enum cmd {
26 CMD_PING,
27 CMD_ACLK_STATE,
28 CMD_VERSION,
29 + CMD_DUMPCONFIG,
30 CMD_TOTAL_COMMANDS
31 } cmd_t;
32
libnetdata/buffer/buffer.h
+29
@@ -152,6 +152,35 @@ static inline void _buffer_json_depth_pop(BUFFER *wb) {
152 wb->json.depth--;
153 }
154
155 +static inline void buffer_fast_charcat(BUFFER *wb, const char c) {
156 +
157 + buffer_need_bytes(wb, 2);
158 + *(&wb->buffer[wb->len]) = c;
159 + wb->len += 1;
160 + wb->buffer[wb->len] = '\0';
161 +
162 + buffer_overflow_check(wb);
163 +}
164 +
165 +static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
166 + if(unlikely(!txt || !*txt || !len)) return;
167 +
168 + buffer_need_bytes(wb, len + 1);
169 +
170 + const char *t = txt;
171 + const char *e = &txt[len];
172 +
173 + char *d = &wb->buffer[wb->len];
174 +
175 + while(t != e)
176 + *d++ = *t++;
177 +
178 + wb->len += len;
179 + wb->buffer[wb->len] = '\0';
180 +
181 + buffer_overflow_check(wb);
182 +}
183 +
184 static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
185 if(unlikely(!txt || !*txt || !len)) return;
186