diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Makefile.am | 8 | ||||
-rw-r--r-- | cli/README.md | 34 | ||||
-rw-r--r-- | cli/cli.c | 201 | ||||
-rw-r--r-- | cli/cli.h | 8 |
4 files changed, 251 insertions, 0 deletions
diff --git a/cli/Makefile.am b/cli/Makefile.am new file mode 100644 index 000000000..161784b8f --- /dev/null +++ b/cli/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/cli/README.md b/cli/README.md new file mode 100644 index 000000000..93812372a --- /dev/null +++ b/cli/README.md @@ -0,0 +1,34 @@ +<!-- +title: "Netdata CLI" +description: "The Netdata Agent includes a command-line experience for reloading health configuration, reopening log files, halting the daemon, and more." +custom_edit_url: https://github.com/netdata/netdata/edit/master/cli/README.md +--> + +# Netdata CLI + +You can see the commands `netdatacli` supports by executing it with `netdatacli` and entering `help` in +standard input. All commands are given as standard input to `netdatacli`. + +The commands that a running netdata agent can execute are the following: + +```sh +The commands are (arguments are in brackets): +help + Show this help menu. +reload-health + Reload health configuration. +save-database + Save internal DB to disk for for memory mode save. +reopen-logs + Close and reopen log files. +shutdown-agent + Cleanup and exit the netdata agent. +fatal-agent + Log the state and halt the netdata agent. +reload-claiming-state + Reload agent claiming state from disk. +``` + +Those commands are the same that can be sent to netdata via [signals](/daemon/README.md#command-line-options). + +[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Fcli%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)](<>) diff --git a/cli/cli.c b/cli/cli.c new file mode 100644 index 000000000..4df020178 --- /dev/null +++ b/cli/cli.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "cli.h" +#include "../libnetdata/required_dummies.h" + +static uv_pipe_t client_pipe; +static uv_write_t write_req; +static uv_shutdown_t shutdown_req; + +static char command_string[MAX_COMMAND_LENGTH]; +static unsigned command_string_size; + +static char response_string[MAX_COMMAND_LENGTH]; +static unsigned response_string_size; + +static int exit_status; + +struct command_context { + uv_work_t work; + uv_stream_t *client; + cmd_t idx; + char *args; + char *message; + cmd_status_t status; +}; + +static void parse_command_reply(void) +{ + FILE *stream = NULL; + char *pos; + int syntax_error = 0; + + for (pos = response_string ; + pos < response_string + response_string_size && !syntax_error ; + ++pos) { + /* Skip white-space characters */ + for ( ; isspace(*pos) && ('\0' != *pos); ++pos) {;} + + if ('\0' == *pos) + continue; + + switch (*pos) { + case CMD_PREFIX_EXIT_CODE: + exit_status = atoi(++pos); + break; + case CMD_PREFIX_INFO: + stream = stdout; + break; + case CMD_PREFIX_ERROR: + stream = stderr; + break; + default: + syntax_error = 1; + fprintf(stderr, "Syntax error, failed to parse command response.\n"); + break; + } + if (stream) { + fprintf(stream, "%s\n", ++pos); + pos += strlen(pos); + stream = NULL; + } + } +} + +static void pipe_read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) +{ + if (0 == nread) { + fprintf(stderr, "%s: Zero bytes read by command pipe.\n", __func__); + } else if (UV_EOF == nread) { +// fprintf(stderr, "EOF found in command pipe.\n"); + parse_command_reply(); + } else if (nread < 0) { + fprintf(stderr, "%s: %s\n", __func__, uv_strerror(nread)); + } + + if (nread < 0) { /* stop stream due to EOF or error */ + (void)uv_read_stop((uv_stream_t *)client); + } else if (nread) { + size_t to_copy; + + to_copy = MIN(nread, MAX_COMMAND_LENGTH - 1 - response_string_size); + memcpy(response_string + response_string_size, buf->base, to_copy); + response_string_size += to_copy; + response_string[response_string_size] = '\0'; + } + if (buf && buf->len) { + free(buf->base); + } +} + +static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) +{ + (void)handle; + + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +static void shutdown_cb(uv_shutdown_t* req, int status) +{ + int ret; + + (void)req; + (void)status; + + /* receive reply */ + response_string_size = 0; + response_string[0] = '\0'; + + ret = uv_read_start((uv_stream_t *)&client_pipe, alloc_cb, pipe_read_cb); + if (ret) { + fprintf(stderr, "uv_read_start(): %s\n", uv_strerror(ret)); + uv_close((uv_handle_t *)&client_pipe, NULL); + return; + } + +} + +static void pipe_write_cb(uv_write_t* req, int status) +{ + int ret; + + (void)req; + (void)status; + + ret = uv_shutdown(&shutdown_req, (uv_stream_t *)&client_pipe, shutdown_cb); + if (ret) { + fprintf(stderr, "uv_shutdown(): %s\n", uv_strerror(ret)); + uv_close((uv_handle_t *)&client_pipe, NULL); + return; + } +} + +static void connect_cb(uv_connect_t* req, int status) +{ + int ret; + uv_buf_t write_buf; + char *s; + + (void)req; + if (status) { + fprintf(stderr, "uv_pipe_connect(): %s\n", uv_strerror(status)); + fprintf(stderr, "Make sure the netdata service is running.\n"); + exit(-1); + } + if (0 == command_string_size) { + s = fgets(command_string, MAX_COMMAND_LENGTH, stdin); + } + (void)s; /* We don't need input to communicate with the server */ + command_string_size = strlen(command_string); + + write_req.data = &client_pipe; + write_buf.base = command_string; + write_buf.len = command_string_size; + ret = uv_write(&write_req, (uv_stream_t *)&client_pipe, &write_buf, 1, pipe_write_cb); + if (ret) { + fprintf(stderr, "uv_write(): %s\n", uv_strerror(ret)); + } +// fprintf(stderr, "COMMAND: Sending command: \"%s\"\n", command_string); +} + +int main(int argc, char **argv) +{ + int ret, i; + static uv_loop_t* loop; + uv_connect_t req; + + exit_status = -1; /* default status for when there is no command response from server */ + + loop = uv_default_loop(); + + ret = uv_pipe_init(loop, &client_pipe, 1); + if (ret) { + fprintf(stderr, "uv_pipe_init(): %s\n", uv_strerror(ret)); + return exit_status; + } + + command_string_size = 0; + command_string[0] = '\0'; + for (i = 1 ; i < argc ; ++i) { + size_t to_copy; + + to_copy = MIN(strlen(argv[i]), MAX_COMMAND_LENGTH - 1 - command_string_size); + strncpyz(command_string + command_string_size, argv[i], to_copy); + command_string_size += to_copy; + + if (command_string_size < MAX_COMMAND_LENGTH - 1) { + command_string[command_string_size++] = ' '; + } else { + break; + } + } + + uv_pipe_connect(&req, &client_pipe, PIPENAME, connect_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + uv_close((uv_handle_t *)&client_pipe, NULL); + + return exit_status; +}
\ No newline at end of file diff --git a/cli/cli.h b/cli/cli.h new file mode 100644 index 000000000..9e730a301 --- /dev/null +++ b/cli/cli.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_CLI_H +#define NETDATA_CLI_H 1 + +#include "../daemon/common.h" + +#endif //NETDATA_CLI_H |