cli.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "daemon/pipename.h"
  3. #include "daemon/common.h"
  4. #include "libnetdata/required_dummies.h"
  5. static uv_pipe_t client_pipe;
  6. static uv_write_t write_req;
  7. static uv_shutdown_t shutdown_req;
  8. static char command_string[MAX_COMMAND_LENGTH];
  9. static unsigned command_string_size;
  10. static int exit_status;
  11. struct command_context {
  12. uv_work_t work;
  13. uv_stream_t *client;
  14. cmd_t idx;
  15. char *args;
  16. char *message;
  17. cmd_status_t status;
  18. };
  19. static void parse_command_reply(BUFFER *buf)
  20. {
  21. char *response_string = (char *) buffer_tostring(buf);
  22. unsigned response_string_size = buffer_strlen(buf);
  23. FILE *stream = NULL;
  24. char *pos;
  25. int syntax_error = 0;
  26. for (pos = response_string ;
  27. pos < response_string + response_string_size && !syntax_error ;
  28. ++pos) {
  29. /* Skip white-space characters */
  30. for ( ; isspace(*pos) && ('\0' != *pos); ++pos) ;
  31. if ('\0' == *pos)
  32. continue;
  33. switch (*pos) {
  34. case CMD_PREFIX_EXIT_CODE:
  35. exit_status = atoi(++pos);
  36. break;
  37. case CMD_PREFIX_INFO:
  38. stream = stdout;
  39. break;
  40. case CMD_PREFIX_ERROR:
  41. stream = stderr;
  42. break;
  43. default:
  44. syntax_error = 1;
  45. fprintf(stderr, "Syntax error, failed to parse command response.\n");
  46. break;
  47. }
  48. if (stream) {
  49. fprintf(stream, "%s\n", ++pos);
  50. pos += strlen(pos);
  51. stream = NULL;
  52. }
  53. }
  54. }
  55. static void pipe_read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
  56. {
  57. BUFFER *response = client->data;
  58. if (0 == nread)
  59. fprintf(stderr, "%s: Zero bytes read by command pipe.\n", __func__);
  60. else if (UV_EOF == nread)
  61. parse_command_reply(response);
  62. else if (nread < 0) {
  63. fprintf(stderr, "%s: %s\n", __func__, uv_strerror(nread));
  64. (void)uv_read_stop((uv_stream_t *)client);
  65. }
  66. else
  67. buffer_fast_rawcat(response, buf->base, nread);
  68. if (buf && buf->len)
  69. free(buf->base);
  70. }
  71. static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
  72. {
  73. (void)handle;
  74. buf->base = malloc(suggested_size);
  75. buf->len = suggested_size;
  76. }
  77. static void shutdown_cb(uv_shutdown_t* req, int status)
  78. {
  79. int ret;
  80. (void)req;
  81. (void)status;
  82. /* receive reply */
  83. client_pipe.data = req->data;
  84. ret = uv_read_start((uv_stream_t *)&client_pipe, alloc_cb, pipe_read_cb);
  85. if (ret) {
  86. fprintf(stderr, "uv_read_start(): %s\n", uv_strerror(ret));
  87. uv_close((uv_handle_t *)&client_pipe, NULL);
  88. return;
  89. }
  90. }
  91. static void pipe_write_cb(uv_write_t* req, int status)
  92. {
  93. int ret;
  94. (void)status;
  95. uv_pipe_t *clientp = req->data;
  96. shutdown_req.data = clientp->data;
  97. ret = uv_shutdown(&shutdown_req, (uv_stream_t *)&client_pipe, shutdown_cb);
  98. if (ret) {
  99. fprintf(stderr, "uv_shutdown(): %s\n", uv_strerror(ret));
  100. uv_close((uv_handle_t *)&client_pipe, NULL);
  101. return;
  102. }
  103. }
  104. static void connect_cb(uv_connect_t* req, int status)
  105. {
  106. int ret;
  107. uv_buf_t write_buf;
  108. char *s;
  109. (void)req;
  110. if (status) {
  111. fprintf(stderr, "uv_pipe_connect(): %s\n", uv_strerror(status));
  112. fprintf(stderr, "Make sure the netdata service is running.\n");
  113. exit(-1);
  114. }
  115. if (0 == command_string_size) {
  116. s = fgets(command_string, MAX_COMMAND_LENGTH - 1, stdin);
  117. }
  118. (void)s; /* We don't need input to communicate with the server */
  119. command_string_size = strlen(command_string);
  120. client_pipe.data = req->data;
  121. write_req.data = &client_pipe;
  122. write_buf.base = command_string;
  123. write_buf.len = command_string_size;
  124. ret = uv_write(&write_req, (uv_stream_t *)&client_pipe, &write_buf, 1, pipe_write_cb);
  125. if (ret) {
  126. fprintf(stderr, "uv_write(): %s\n", uv_strerror(ret));
  127. }
  128. // fprintf(stderr, "COMMAND: Sending command: \"%s\"\n", command_string);
  129. }
  130. int main(int argc, char **argv)
  131. {
  132. clocks_init();
  133. nd_log_initialize_for_external_plugins("netdatacli");
  134. int ret, i;
  135. static uv_loop_t* loop;
  136. uv_connect_t req;
  137. exit_status = -1; /* default status for when there is no command response from server */
  138. loop = uv_default_loop();
  139. ret = uv_pipe_init(loop, &client_pipe, 1);
  140. if (ret) {
  141. fprintf(stderr, "uv_pipe_init(): %s\n", uv_strerror(ret));
  142. return exit_status;
  143. }
  144. command_string_size = 0;
  145. command_string[0] = '\0';
  146. for (i = 1 ; i < argc ; ++i) {
  147. size_t to_copy;
  148. to_copy = MIN(strlen(argv[i]), MAX_COMMAND_LENGTH - 1 - command_string_size);
  149. strncpy(command_string + command_string_size, argv[i], to_copy);
  150. command_string_size += to_copy;
  151. command_string[command_string_size] = '\0';
  152. if (command_string_size < MAX_COMMAND_LENGTH - 1) {
  153. command_string[command_string_size++] = ' ';
  154. } else {
  155. break;
  156. }
  157. }
  158. req.data = buffer_create(128, NULL);
  159. const char *pipename = daemon_pipename();
  160. uv_pipe_connect(&req, &client_pipe, pipename, connect_cb);
  161. uv_run(loop, UV_RUN_DEFAULT);
  162. uv_close((uv_handle_t *)&client_pipe, NULL);
  163. buffer_free(client_pipe.data);
  164. return exit_status;
  165. }