summaryrefslogtreecommitdiffstats
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/Makefile.am8
-rw-r--r--cli/README.md38
-rw-r--r--cli/cli.c295
-rw-r--r--cli/cli.h8
4 files changed, 349 insertions, 0 deletions
diff --git a/cli/Makefile.am b/cli/Makefile.am
new file mode 100644
index 00000000..161784b8
--- /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 00000000..8dd9cce8
--- /dev/null
+++ b/cli/README.md
@@ -0,0 +1,38 @@
+# Netdata Agent CLI
+
+The `netdatacli` executable provides a simple way to control the Netdata agent's operation.
+
+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.
+reload-labels
+ Reload all labels.
+save-database
+ Save internal DB to disk for database 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.
+ping
+ Return with 'pong' if agent is alive.
+aclk-state [json]
+ Returns current state of ACLK and Cloud connection. (optionally in json)
+dumpconfig
+ Returns the current netdata.conf on stdout.
+```
+
+See also the Netdata daemon [command line options](https://github.com/netdata/netdata/blob/master/daemon/README.md#command-line-options).
+
+
diff --git a/cli/cli.c b/cli/cli.c
new file mode 100644
index 00000000..2efa518e
--- /dev/null
+++ b/cli/cli.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "cli.h"
+#include "daemon/pipename.h"
+
+void netdata_logger(ND_LOG_SOURCES source, ND_LOG_FIELD_PRIORITY priority, const char *file, const char *function, unsigned long line, const char *fmt, ... ) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args );
+ va_end(args);
+}
+
+#ifdef NETDATA_INTERNAL_CHECKS
+
+uint64_t debug_flags;
+
+void netdata_logger_fatal( const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt __maybe_unused, ... )
+{
+ abort();
+};
+#endif
+
+#ifdef NETDATA_TRACE_ALLOCATIONS
+void *callocz_int(size_t nmemb, size_t size, const char *file __maybe_unused, const char *function __maybe_unused, size_t line __maybe_unused)
+{
+ void *p = calloc(nmemb, size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", nmemb * size);
+ exit(1);
+ }
+ return p;
+}
+
+void *mallocz_int(size_t size, const char *file __maybe_unused, const char *function __maybe_unused, size_t line __maybe_unused)
+{
+ void *p = malloc(size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", size);
+ exit(1);
+ }
+ return p;
+}
+
+void *reallocz_int(void *ptr, size_t size, const char *file __maybe_unused, const char *function __maybe_unused, size_t line __maybe_unused)
+{
+ void *p = realloc(ptr, size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", size);
+ exit(1);
+ }
+ return p;
+}
+
+void freez_int(void *ptr, const char *file __maybe_unused, const char *function __maybe_unused, size_t line __maybe_unused)
+{
+ free(ptr);
+}
+#else
+void freez(void *ptr) {
+ free(ptr);
+}
+
+void *mallocz(size_t size) {
+ void *p = malloc(size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", size);
+ exit(1);
+ }
+ return p;
+}
+
+void *callocz(size_t nmemb, size_t size) {
+ void *p = calloc(nmemb, size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", nmemb * size);
+ exit(1);
+ }
+ return p;
+}
+
+void *reallocz(void *ptr, size_t size) {
+ void *p = realloc(ptr, size);
+ if (unlikely(!p)) {
+ netdata_log_error("Cannot allocate %zu bytes of memory.", size);
+ exit(1);
+ }
+ return p;
+}
+#endif
+
+int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) {
+ if(unlikely(!n)) return 0;
+
+ int size = vsnprintf(dst, n, fmt, args);
+ dst[n - 1] = '\0';
+
+ if (unlikely((size_t) size > n)) size = (int)n;
+
+ return size;
+}
+
+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 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(BUFFER *buf)
+{
+ char *response_string = (char *) buffer_tostring(buf);
+ unsigned response_string_size = buffer_strlen(buf);
+ 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)
+{
+ BUFFER *response = client->data;
+
+ if (0 == nread)
+ fprintf(stderr, "%s: Zero bytes read by command pipe.\n", __func__);
+ else if (UV_EOF == nread)
+ parse_command_reply(response);
+ else if (nread < 0) {
+ fprintf(stderr, "%s: %s\n", __func__, uv_strerror(nread));
+ (void)uv_read_stop((uv_stream_t *)client);
+ }
+ else
+ buffer_fast_rawcat(response, buf->base, nread);
+
+ 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 */
+ client_pipe.data = req->data;
+
+ 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)status;
+
+ uv_pipe_t *clientp = req->data;
+ shutdown_req.data = clientp->data;
+
+ 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 - 1, stdin);
+ }
+ (void)s; /* We don't need input to communicate with the server */
+ command_string_size = strlen(command_string);
+ client_pipe.data = req->data;
+
+ 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);
+ strncpy(command_string + command_string_size, argv[i], to_copy);
+ command_string_size += to_copy;
+ command_string[command_string_size] = '\0';
+
+ if (command_string_size < MAX_COMMAND_LENGTH - 1) {
+ command_string[command_string_size++] = ' ';
+ } else {
+ break;
+ }
+ }
+
+ req.data = buffer_create(128, NULL);
+
+ const char *pipename = daemon_pipename();
+ uv_pipe_connect(&req, &client_pipe, pipename, connect_cb);
+
+ uv_run(loop, UV_RUN_DEFAULT);
+
+ uv_close((uv_handle_t *)&client_pipe, NULL);
+ buffer_free(client_pipe.data);
+
+ return exit_status;
+}
diff --git a/cli/cli.h b/cli/cli.h
new file mode 100644
index 00000000..056e9986
--- /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