mirror of
https://github.com/netdata/netdata.git
synced 2025-04-24 13:14:11 +00:00

Currently, we add the repository's top-level dir in the compiler's header search path. This means that code in every top-level directory within the repo can include headers sibling top-level directories. This patch makes header inclusion consistent when it comes to files that are included from sibling top-level directories within the repo.
201 lines
5.2 KiB
C
201 lines
5.2 KiB
C
// 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;
|
|
}
|