analytics.c 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "common.h"
  3. #include "buildinfo.h"
  4. struct analytics_data analytics_data;
  5. extern void analytics_exporting_connectors (BUFFER *b);
  6. extern void analytics_exporting_connectors_ssl (BUFFER *b);
  7. extern void analytics_build_info (BUFFER *b);
  8. extern int aclk_connected;
  9. struct collector {
  10. const char *plugin;
  11. const char *module;
  12. };
  13. struct array_printer {
  14. int c;
  15. BUFFER *both;
  16. };
  17. /*
  18. * Debug logging
  19. */
  20. void analytics_log_data(void)
  21. {
  22. debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
  23. debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
  24. debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
  25. debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
  26. debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
  27. debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
  28. debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
  29. debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
  30. debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
  31. debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
  32. debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
  33. debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
  34. debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
  35. debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
  36. debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
  37. debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
  38. debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
  39. debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
  40. debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
  41. debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
  42. debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
  43. debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
  44. debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
  45. debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
  46. debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
  47. debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
  48. debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
  49. debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
  50. debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
  51. debug(D_ANALYTICS, "NETDATA_HOST_ACLK_PROTOCOL : [%s]", analytics_data.netdata_host_aclk_protocol);
  52. debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
  53. debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
  54. debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_ENABLED : [%s]", analytics_data.netdata_host_cloud_enabled);
  55. debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_AVAILABLE : [%s]", analytics_data.netdata_config_https_available);
  56. debug(D_ANALYTICS, "NETDATA_INSTALL_TYPE : [%s]", analytics_data.netdata_install_type);
  57. debug(D_ANALYTICS, "NETDATA_PREBUILT_DISTRO : [%s]", analytics_data.netdata_prebuilt_distro);
  58. debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PRIVATE_REGISTRY : [%s]", analytics_data.netdata_config_is_private_registry);
  59. debug(D_ANALYTICS, "NETDATA_CONFIG_USE_PRIVATE_REGISTRY: [%s]", analytics_data.netdata_config_use_private_registry);
  60. debug(D_ANALYTICS, "NETDATA_CONFIG_OOM_SCORE : [%s]", analytics_data.netdata_config_oom_score);
  61. }
  62. /*
  63. * Free data
  64. */
  65. void analytics_free_data(void)
  66. {
  67. freez(analytics_data.netdata_config_stream_enabled);
  68. freez(analytics_data.netdata_config_memory_mode);
  69. freez(analytics_data.netdata_config_exporting_enabled);
  70. freez(analytics_data.netdata_exporting_connectors);
  71. freez(analytics_data.netdata_allmetrics_prometheus_used);
  72. freez(analytics_data.netdata_allmetrics_shell_used);
  73. freez(analytics_data.netdata_allmetrics_json_used);
  74. freez(analytics_data.netdata_dashboard_used);
  75. freez(analytics_data.netdata_collectors);
  76. freez(analytics_data.netdata_collectors_count);
  77. freez(analytics_data.netdata_buildinfo);
  78. freez(analytics_data.netdata_config_page_cache_size);
  79. freez(analytics_data.netdata_config_multidb_disk_quota);
  80. freez(analytics_data.netdata_config_https_enabled);
  81. freez(analytics_data.netdata_config_web_enabled);
  82. freez(analytics_data.netdata_config_release_channel);
  83. freez(analytics_data.netdata_mirrored_host_count);
  84. freez(analytics_data.netdata_mirrored_hosts_reachable);
  85. freez(analytics_data.netdata_mirrored_hosts_unreachable);
  86. freez(analytics_data.netdata_notification_methods);
  87. freez(analytics_data.netdata_alarms_normal);
  88. freez(analytics_data.netdata_alarms_warning);
  89. freez(analytics_data.netdata_alarms_critical);
  90. freez(analytics_data.netdata_charts_count);
  91. freez(analytics_data.netdata_metrics_count);
  92. freez(analytics_data.netdata_config_is_parent);
  93. freez(analytics_data.netdata_config_hosts_available);
  94. freez(analytics_data.netdata_host_cloud_available);
  95. freez(analytics_data.netdata_host_aclk_available);
  96. freez(analytics_data.netdata_host_aclk_protocol);
  97. freez(analytics_data.netdata_host_aclk_implementation);
  98. freez(analytics_data.netdata_host_agent_claimed);
  99. freez(analytics_data.netdata_host_cloud_enabled);
  100. freez(analytics_data.netdata_config_https_available);
  101. freez(analytics_data.netdata_install_type);
  102. freez(analytics_data.netdata_config_is_private_registry);
  103. freez(analytics_data.netdata_config_use_private_registry);
  104. freez(analytics_data.netdata_config_oom_score);
  105. freez(analytics_data.netdata_prebuilt_distro);
  106. }
  107. /*
  108. * Set a numeric/boolean data with a value
  109. */
  110. void analytics_set_data(char **name, char *value)
  111. {
  112. if (*name) {
  113. analytics_data.data_length -= strlen(*name);
  114. freez(*name);
  115. }
  116. *name = strdupz(value);
  117. analytics_data.data_length += strlen(*name);
  118. }
  119. /*
  120. * Set a string data with a value
  121. */
  122. void analytics_set_data_str(char **name, char *value)
  123. {
  124. size_t value_string_len;
  125. if (*name) {
  126. analytics_data.data_length -= strlen(*name);
  127. freez(*name);
  128. }
  129. value_string_len = strlen(value) + 4;
  130. *name = mallocz(sizeof(char) * value_string_len);
  131. snprintfz(*name, value_string_len - 1, "\"%s\"", value);
  132. analytics_data.data_length += strlen(*name);
  133. }
  134. /*
  135. * Get data, used by web api v1
  136. */
  137. void analytics_get_data(char *name, BUFFER *wb)
  138. {
  139. buffer_strcat(wb, name);
  140. }
  141. /*
  142. * Log hits on the allmetrics page, with prometheus parameter
  143. */
  144. void analytics_log_prometheus(void)
  145. {
  146. if (netdata_anonymous_statistics_enabled == 1 && likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
  147. analytics_data.prometheus_hits++;
  148. char b[7];
  149. snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
  150. analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
  151. }
  152. }
  153. /*
  154. * Log hits on the allmetrics page, with shell parameter (or default)
  155. */
  156. void analytics_log_shell(void)
  157. {
  158. if (netdata_anonymous_statistics_enabled == 1 && likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
  159. analytics_data.shell_hits++;
  160. char b[7];
  161. snprintfz(b, 6, "%d", analytics_data.shell_hits);
  162. analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
  163. }
  164. }
  165. /*
  166. * Log hits on the allmetrics page, with json parameter
  167. */
  168. void analytics_log_json(void)
  169. {
  170. if (netdata_anonymous_statistics_enabled == 1 && likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
  171. analytics_data.json_hits++;
  172. char b[7];
  173. snprintfz(b, 6, "%d", analytics_data.json_hits);
  174. analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
  175. }
  176. }
  177. /*
  178. * Log hits on the dashboard, (when calling HELLO).
  179. */
  180. void analytics_log_dashboard(void)
  181. {
  182. if (netdata_anonymous_statistics_enabled == 1 && likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
  183. analytics_data.dashboard_hits++;
  184. char b[7];
  185. snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
  186. analytics_set_data(&analytics_data.netdata_dashboard_used, b);
  187. }
  188. }
  189. /*
  190. * Called when setting the oom score
  191. */
  192. void analytics_report_oom_score(long long int score){
  193. char b[7];
  194. snprintfz(b, 6, "%d", (int)score);
  195. analytics_set_data(&analytics_data.netdata_config_oom_score, b);
  196. }
  197. void analytics_mirrored_hosts(void)
  198. {
  199. RRDHOST *host;
  200. int count = 0;
  201. int reachable = 0;
  202. int unreachable = 0;
  203. char b[11];
  204. rrd_rdlock();
  205. rrdhost_foreach_read(host)
  206. {
  207. if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
  208. continue;
  209. ((host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)) ? reachable++ : unreachable++);
  210. count++;
  211. }
  212. rrd_unlock();
  213. snprintfz(b, 10, "%d", count);
  214. analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
  215. snprintfz(b, 10, "%d", reachable);
  216. analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
  217. snprintfz(b, 10, "%d", unreachable);
  218. analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
  219. }
  220. void analytics_exporters(void)
  221. {
  222. //when no exporters are available, an empty string will be sent
  223. //decide if something else is more suitable (but probably not null)
  224. BUFFER *bi = buffer_create(1000, NULL);
  225. analytics_exporting_connectors(bi);
  226. analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
  227. buffer_free(bi);
  228. }
  229. int collector_counter_callb(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *data) {
  230. struct array_printer *ap = (struct array_printer *)data;
  231. struct collector *col = (struct collector *)entry;
  232. BUFFER *bt = ap->both;
  233. if (likely(ap->c)) {
  234. buffer_strcat(bt, ",");
  235. }
  236. buffer_strcat(bt, "{");
  237. buffer_strcat(bt, " \"plugin\": \"");
  238. buffer_strcat(bt, col->plugin);
  239. buffer_strcat(bt, "\", \"module\":\"");
  240. buffer_strcat(bt, col->module);
  241. buffer_strcat(bt, "\" }");
  242. (ap->c)++;
  243. return 0;
  244. }
  245. /*
  246. * Create a JSON array of available collectors, same as in api/v1/info
  247. */
  248. void analytics_collectors(void)
  249. {
  250. RRDSET *st;
  251. DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED);
  252. char name[500];
  253. BUFFER *bt = buffer_create(1000, NULL);
  254. rrdset_foreach_read(st, localhost) {
  255. if(!rrdset_is_available_for_viewers(st))
  256. continue;
  257. struct collector col = {
  258. .plugin = rrdset_plugin_name(st),
  259. .module = rrdset_module_name(st)
  260. };
  261. snprintfz(name, 499, "%s:%s", col.plugin, col.module);
  262. dictionary_set(dict, name, &col, sizeof(struct collector));
  263. }
  264. rrdset_foreach_done(st);
  265. struct array_printer ap;
  266. ap.c = 0;
  267. ap.both = bt;
  268. dictionary_walkthrough_read(dict, collector_counter_callb, &ap);
  269. dictionary_destroy(dict);
  270. analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
  271. {
  272. char b[7];
  273. snprintfz(b, 6, "%d", ap.c);
  274. analytics_set_data(&analytics_data.netdata_collectors_count, b);
  275. }
  276. buffer_free(bt);
  277. }
  278. /*
  279. * Run alarm-notify.sh script using the dump_methods parameter
  280. * SEND_CUSTOM is always available
  281. */
  282. void analytics_alarms_notifications(void)
  283. {
  284. char *script;
  285. script = mallocz(
  286. sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
  287. sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
  288. if (unlikely(access(script, R_OK) != 0)) {
  289. info("Alarm notify script %s not found.", script);
  290. freez(script);
  291. return;
  292. }
  293. strcat(script, " dump_methods");
  294. pid_t command_pid;
  295. debug(D_ANALYTICS, "Executing %s", script);
  296. BUFFER *b = buffer_create(1000, NULL);
  297. int cnt = 0;
  298. FILE *fp_child_input;
  299. FILE *fp_child_output = netdata_popen(script, &command_pid, &fp_child_input);
  300. if (fp_child_output) {
  301. char line[200 + 1];
  302. while (fgets(line, 200, fp_child_output) != NULL) {
  303. char *end = line;
  304. while (*end && *end != '\n')
  305. end++;
  306. *end = '\0';
  307. if (likely(cnt))
  308. buffer_strcat(b, "|");
  309. buffer_strcat(b, line);
  310. cnt++;
  311. }
  312. netdata_pclose(fp_child_input, fp_child_output, command_pid);
  313. }
  314. freez(script);
  315. analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
  316. buffer_free(b);
  317. }
  318. void analytics_get_install_type(void)
  319. {
  320. if (localhost->system_info->install_type == NULL) {
  321. analytics_set_data_str(&analytics_data.netdata_install_type, "unknown");
  322. } else {
  323. analytics_set_data_str(&analytics_data.netdata_install_type, localhost->system_info->install_type);
  324. }
  325. if (localhost->system_info->prebuilt_dist != NULL) {
  326. analytics_set_data_str(&analytics_data.netdata_prebuilt_distro, localhost->system_info->prebuilt_dist);
  327. }
  328. }
  329. /*
  330. * Pick up if https is actually used
  331. */
  332. void analytics_https(void)
  333. {
  334. BUFFER *b = buffer_create(30, NULL);
  335. #ifdef ENABLE_HTTPS
  336. analytics_exporting_connectors_ssl(b);
  337. buffer_strcat(b, netdata_ssl_client_ctx && rrdhost_flag_check(localhost, RRDHOST_FLAG_RRDPUSH_SENDER_CONNECTED) && localhost->sender->ssl.flags == NETDATA_SSL_HANDSHAKE_COMPLETE ? "streaming|" : "|");
  338. buffer_strcat(b, netdata_ssl_srv_ctx ? "web" : "");
  339. #else
  340. buffer_strcat(b, "||");
  341. #endif
  342. analytics_set_data_str(&analytics_data.netdata_config_https_available, (char *)buffer_tostring(b));
  343. buffer_free(b);
  344. }
  345. void analytics_charts(void)
  346. {
  347. RRDSET *st;
  348. int c = 0;
  349. rrdset_foreach_read(st, localhost)
  350. if(rrdset_is_available_for_viewers(st)) c++;
  351. rrdset_foreach_done(st);
  352. {
  353. char b[7];
  354. snprintfz(b, 6, "%d", c);
  355. analytics_set_data(&analytics_data.netdata_charts_count, b);
  356. }
  357. }
  358. void analytics_metrics(void)
  359. {
  360. RRDSET *st;
  361. long int dimensions = 0;
  362. rrdset_foreach_read(st, localhost) {
  363. if (rrdset_is_available_for_viewers(st)) {
  364. RRDDIM *rd;
  365. rrddim_foreach_read(rd, st) {
  366. if (rrddim_option_check(rd, RRDDIM_OPTION_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
  367. continue;
  368. dimensions++;
  369. }
  370. rrddim_foreach_done(rd);
  371. }
  372. }
  373. rrdset_foreach_done(st);
  374. {
  375. char b[7];
  376. snprintfz(b, 6, "%ld", dimensions);
  377. analytics_set_data(&analytics_data.netdata_metrics_count, b);
  378. }
  379. }
  380. void analytics_alarms(void)
  381. {
  382. int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
  383. char b[10];
  384. RRDCALC *rc;
  385. foreach_rrdcalc_in_rrdhost_read(localhost, rc) {
  386. if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
  387. continue;
  388. switch (rc->status) {
  389. case RRDCALC_STATUS_WARNING:
  390. alarm_warn++;
  391. break;
  392. case RRDCALC_STATUS_CRITICAL:
  393. alarm_crit++;
  394. break;
  395. default:
  396. alarm_normal++;
  397. }
  398. }
  399. foreach_rrdcalc_in_rrdhost_done(rc);
  400. snprintfz(b, 9, "%d", alarm_normal);
  401. analytics_set_data(&analytics_data.netdata_alarms_normal, b);
  402. snprintfz(b, 9, "%d", alarm_warn);
  403. analytics_set_data(&analytics_data.netdata_alarms_warning, b);
  404. snprintfz(b, 9, "%d", alarm_crit);
  405. analytics_set_data(&analytics_data.netdata_alarms_critical, b);
  406. }
  407. /*
  408. * Misc attributes to get (run from start)
  409. */
  410. void analytics_misc(void)
  411. {
  412. #ifdef ENABLE_ACLK
  413. analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
  414. analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
  415. #else
  416. analytics_set_data(&analytics_data.netdata_host_cloud_available, "false");
  417. analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "");
  418. #endif
  419. analytics_set_data(&analytics_data.netdata_config_exporting_enabled, appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", CONFIG_BOOLEAN_NO) ? "true" : "false");
  420. analytics_set_data(&analytics_data.netdata_config_is_private_registry, "false");
  421. analytics_set_data(&analytics_data.netdata_config_use_private_registry, "false");
  422. if (strcmp(
  423. config_get(CONFIG_SECTION_REGISTRY, "registry to announce", "https://registry.my-netdata.io"),
  424. "https://registry.my-netdata.io"))
  425. analytics_set_data(&analytics_data.netdata_config_use_private_registry, "true");
  426. //do we need both registry to announce and enabled to indicate that this is a private registry ?
  427. if (config_get_boolean(CONFIG_SECTION_REGISTRY, "enabled", CONFIG_BOOLEAN_NO) &&
  428. web_server_mode != WEB_SERVER_MODE_NONE)
  429. analytics_set_data(&analytics_data.netdata_config_is_private_registry, "true");
  430. }
  431. void analytics_aclk(void)
  432. {
  433. #ifdef ENABLE_ACLK
  434. if (aclk_connected) {
  435. analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
  436. analytics_set_data_str(&analytics_data.netdata_host_aclk_protocol, "New");
  437. }
  438. else
  439. #endif
  440. analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
  441. }
  442. /*
  443. * Get the meta data, called from the thread once after the original delay
  444. * These are values that won't change during agent runtime, and therefore
  445. * don't try to read them on each META event send
  446. */
  447. void analytics_gather_immutable_meta_data(void)
  448. {
  449. analytics_misc();
  450. analytics_exporters();
  451. analytics_https();
  452. }
  453. /*
  454. * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
  455. * These are values that can change between agent restarts, and therefore
  456. * try to read them on each META event send
  457. */
  458. void analytics_gather_mutable_meta_data(void)
  459. {
  460. analytics_collectors();
  461. analytics_alarms();
  462. analytics_charts();
  463. analytics_metrics();
  464. analytics_aclk();
  465. analytics_mirrored_hosts();
  466. analytics_alarms_notifications();
  467. analytics_set_data(
  468. &analytics_data.netdata_config_is_parent, (rrdhost_hosts_available() > 1 || configured_as_parent()) ? "true" : "false");
  469. char *claim_id = get_agent_claimid();
  470. analytics_set_data(&analytics_data.netdata_host_agent_claimed, claim_id ? "true" : "false");
  471. freez(claim_id);
  472. {
  473. char b[7];
  474. snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
  475. analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
  476. snprintfz(b, 6, "%d", analytics_data.shell_hits);
  477. analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
  478. snprintfz(b, 6, "%d", analytics_data.json_hits);
  479. analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
  480. snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
  481. analytics_set_data(&analytics_data.netdata_dashboard_used, b);
  482. snprintfz(b, 6, "%zu", rrdhost_hosts_available());
  483. analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
  484. }
  485. }
  486. void analytics_main_cleanup(void *ptr)
  487. {
  488. struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
  489. static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
  490. debug(D_ANALYTICS, "Cleaning up...");
  491. analytics_free_data();
  492. static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
  493. }
  494. /*
  495. * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
  496. * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
  497. * it will send a new META event after gathering data that could be changed
  498. * while the agent is running
  499. */
  500. void *analytics_main(void *ptr)
  501. {
  502. netdata_thread_cleanup_push(analytics_main_cleanup, ptr);
  503. unsigned int sec = 0;
  504. heartbeat_t hb;
  505. heartbeat_init(&hb);
  506. usec_t step_ut = USEC_PER_SEC;
  507. debug(D_ANALYTICS, "Analytics thread starts");
  508. //first delay after agent start
  509. while (service_running(SERVICE_ANALYTICS) && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
  510. heartbeat_next(&hb, step_ut);
  511. sec++;
  512. }
  513. if (unlikely(!service_running(SERVICE_ANALYTICS)))
  514. goto cleanup;
  515. analytics_gather_immutable_meta_data();
  516. analytics_gather_mutable_meta_data();
  517. send_statistics("META_START", "-", "-");
  518. analytics_log_data();
  519. sec = 0;
  520. while (1) {
  521. heartbeat_next(&hb, step_ut * 2);
  522. sec += 2;
  523. if (unlikely(!service_running(SERVICE_ANALYTICS)))
  524. break;
  525. if (likely(sec < ANALYTICS_HEARTBEAT))
  526. continue;
  527. analytics_gather_mutable_meta_data();
  528. send_statistics("META", "-", "-");
  529. analytics_log_data();
  530. sec = 0;
  531. }
  532. cleanup:
  533. netdata_thread_cleanup_pop(1);
  534. return NULL;
  535. }
  536. static const char *verify_required_directory(const char *dir)
  537. {
  538. if (chdir(dir) == -1)
  539. fatal("Cannot change directory to '%s'", dir);
  540. DIR *d = opendir(dir);
  541. if (!d)
  542. fatal("Cannot examine the contents of directory '%s'", dir);
  543. closedir(d);
  544. return dir;
  545. }
  546. /*
  547. * This is called after the rrdinit
  548. * These values will be sent on the START event
  549. */
  550. void set_late_global_environment()
  551. {
  552. analytics_set_data(&analytics_data.netdata_config_stream_enabled, default_rrdpush_enabled ? "true" : "false");
  553. analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
  554. #ifdef DISABLE_CLOUD
  555. analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "false");
  556. #else
  557. analytics_set_data(
  558. &analytics_data.netdata_host_cloud_enabled,
  559. appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", CONFIG_BOOLEAN_YES) ? "true" : "false");
  560. #endif
  561. #ifdef ENABLE_DBENGINE
  562. {
  563. char b[16];
  564. snprintfz(b, 15, "%d", default_rrdeng_page_cache_mb);
  565. analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
  566. snprintfz(b, 15, "%d", default_multidb_disk_quota_mb);
  567. analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
  568. }
  569. #endif
  570. #ifdef ENABLE_HTTPS
  571. analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
  572. #else
  573. analytics_set_data(&analytics_data.netdata_config_https_enabled, "false");
  574. #endif
  575. if (web_server_mode == WEB_SERVER_MODE_NONE)
  576. analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
  577. else
  578. analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
  579. analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
  580. {
  581. BUFFER *bi = buffer_create(1000, NULL);
  582. analytics_build_info(bi);
  583. analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
  584. buffer_free(bi);
  585. }
  586. analytics_get_install_type();
  587. }
  588. void get_system_timezone(void)
  589. {
  590. // avoid flood calls to stat(/etc/localtime)
  591. // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
  592. const char *tz = getenv("TZ");
  593. if (!tz || !*tz)
  594. setenv("TZ", config_get(CONFIG_SECTION_ENV_VARS, "TZ", ":/etc/localtime"), 0);
  595. char buffer[FILENAME_MAX + 1] = "";
  596. const char *timezone = NULL;
  597. ssize_t ret;
  598. // use the TZ variable
  599. if (tz && *tz && *tz != ':') {
  600. timezone = tz;
  601. info("TIMEZONE: using TZ variable '%s'", timezone);
  602. }
  603. // use the contents of /etc/timezone
  604. if (!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
  605. timezone = buffer;
  606. info("TIMEZONE: using the contents of /etc/timezone");
  607. }
  608. // read the link /etc/localtime
  609. if (!timezone) {
  610. ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
  611. if (ret > 0) {
  612. buffer[ret] = '\0';
  613. char *cmp = "/usr/share/zoneinfo/";
  614. size_t cmp_len = strlen(cmp);
  615. char *s = strstr(buffer, cmp);
  616. if (s && s[cmp_len]) {
  617. timezone = &s[cmp_len];
  618. info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
  619. }
  620. } else
  621. buffer[0] = '\0';
  622. }
  623. // find the timezone from strftime()
  624. if (!timezone) {
  625. time_t t;
  626. struct tm *tmp, tmbuf;
  627. t = now_realtime_sec();
  628. tmp = localtime_r(&t, &tmbuf);
  629. if (tmp != NULL) {
  630. if (strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
  631. buffer[0] = '\0';
  632. else {
  633. buffer[FILENAME_MAX] = '\0';
  634. timezone = buffer;
  635. info("TIMEZONE: using strftime(): '%s'", timezone);
  636. }
  637. }
  638. }
  639. if (timezone && *timezone) {
  640. // make sure it does not have illegal characters
  641. // info("TIMEZONE: fixing '%s'", timezone);
  642. size_t len = strlen(timezone);
  643. char tmp[len + 1];
  644. char *d = tmp;
  645. *d = '\0';
  646. while (*timezone) {
  647. if (isalnum(*timezone) || *timezone == '_' || *timezone == '/')
  648. *d++ = *timezone++;
  649. else
  650. timezone++;
  651. }
  652. *d = '\0';
  653. strncpyz(buffer, tmp, len);
  654. timezone = buffer;
  655. info("TIMEZONE: fixed as '%s'", timezone);
  656. }
  657. if (!timezone || !*timezone)
  658. timezone = "unknown";
  659. netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
  660. //get the utc offset, and the timezone as returned by strftime
  661. //will be sent to the cloud
  662. //Note: This will need an agent restart to get new offset on time change (dst, etc).
  663. {
  664. time_t t;
  665. struct tm *tmp, tmbuf;
  666. char zone[FILENAME_MAX + 1];
  667. char sign[2], hh[3], mm[3];
  668. t = now_realtime_sec();
  669. tmp = localtime_r(&t, &tmbuf);
  670. if (tmp != NULL) {
  671. if (strftime(zone, FILENAME_MAX, "%Z", tmp) == 0) {
  672. netdata_configured_abbrev_timezone = strdupz("UTC");
  673. } else
  674. netdata_configured_abbrev_timezone = strdupz(zone);
  675. if (strftime(zone, FILENAME_MAX, "%z", tmp) == 0) {
  676. netdata_configured_utc_offset = 0;
  677. } else {
  678. sign[0] = zone[0] == '-' || zone[0] == '+' ? zone[0] : '0';
  679. sign[1] = '\0';
  680. hh[0] = isdigit(zone[1]) ? zone[1] : '0';
  681. hh[1] = isdigit(zone[2]) ? zone[2] : '0';
  682. hh[2] = '\0';
  683. mm[0] = isdigit(zone[3]) ? zone[3] : '0';
  684. mm[1] = isdigit(zone[4]) ? zone[4] : '0';
  685. mm[2] = '\0';
  686. netdata_configured_utc_offset = (str2i(hh) * 3600) + (str2i(mm) * 60);
  687. netdata_configured_utc_offset =
  688. sign[0] == '-' ? -netdata_configured_utc_offset : netdata_configured_utc_offset;
  689. }
  690. } else {
  691. netdata_configured_abbrev_timezone = strdupz("UTC");
  692. netdata_configured_utc_offset = 0;
  693. }
  694. }
  695. }
  696. void set_global_environment()
  697. {
  698. {
  699. char b[16];
  700. snprintfz(b, 15, "%d", default_rrd_update_every);
  701. setenv("NETDATA_UPDATE_EVERY", b, 1);
  702. }
  703. setenv("NETDATA_VERSION", program_version, 1);
  704. setenv("NETDATA_HOSTNAME", netdata_configured_hostname, 1);
  705. setenv("NETDATA_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
  706. setenv("NETDATA_USER_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
  707. setenv("NETDATA_STOCK_CONFIG_DIR", verify_required_directory(netdata_configured_stock_config_dir), 1);
  708. setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_primary_plugins_dir), 1);
  709. setenv("NETDATA_WEB_DIR", verify_required_directory(netdata_configured_web_dir), 1);
  710. setenv("NETDATA_CACHE_DIR", verify_required_directory(netdata_configured_cache_dir), 1);
  711. setenv("NETDATA_LIB_DIR", verify_required_directory(netdata_configured_varlib_dir), 1);
  712. setenv("NETDATA_LOCK_DIR", netdata_configured_lock_dir, 1);
  713. setenv("NETDATA_LOG_DIR", verify_required_directory(netdata_configured_log_dir), 1);
  714. setenv("HOME", verify_required_directory(netdata_configured_home_dir), 1);
  715. setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
  716. {
  717. BUFFER *user_plugins_dirs = buffer_create(FILENAME_MAX, NULL);
  718. for (size_t i = 1; i < PLUGINSD_MAX_DIRECTORIES && plugin_directories[i]; i++) {
  719. if (i > 1)
  720. buffer_strcat(user_plugins_dirs, " ");
  721. buffer_strcat(user_plugins_dirs, plugin_directories[i]);
  722. }
  723. setenv("NETDATA_USER_PLUGINS_DIRS", buffer_tostring(user_plugins_dirs), 1);
  724. buffer_free(user_plugins_dirs);
  725. }
  726. analytics_data.data_length = 0;
  727. analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
  728. analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
  729. analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
  730. analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
  731. analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
  732. analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
  733. analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
  734. analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
  735. analytics_set_data(&analytics_data.netdata_collectors, "null");
  736. analytics_set_data(&analytics_data.netdata_collectors_count, "null");
  737. analytics_set_data(&analytics_data.netdata_buildinfo, "null");
  738. analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
  739. analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
  740. analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
  741. analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
  742. analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
  743. analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
  744. analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
  745. analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
  746. analytics_set_data(&analytics_data.netdata_notification_methods, "null");
  747. analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
  748. analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
  749. analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
  750. analytics_set_data(&analytics_data.netdata_charts_count, "null");
  751. analytics_set_data(&analytics_data.netdata_metrics_count, "null");
  752. analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
  753. analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
  754. analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
  755. analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
  756. analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
  757. analytics_set_data(&analytics_data.netdata_host_aclk_protocol, "null");
  758. analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
  759. analytics_set_data(&analytics_data.netdata_host_cloud_enabled, "null");
  760. analytics_set_data(&analytics_data.netdata_config_https_available, "null");
  761. analytics_set_data(&analytics_data.netdata_install_type, "null");
  762. analytics_set_data(&analytics_data.netdata_config_is_private_registry, "null");
  763. analytics_set_data(&analytics_data.netdata_config_use_private_registry, "null");
  764. analytics_set_data(&analytics_data.netdata_config_oom_score, "null");
  765. analytics_set_data(&analytics_data.netdata_prebuilt_distro, "null");
  766. analytics_data.prometheus_hits = 0;
  767. analytics_data.shell_hits = 0;
  768. analytics_data.json_hits = 0;
  769. analytics_data.dashboard_hits = 0;
  770. char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
  771. int clean = 0;
  772. if (!default_port) {
  773. default_port = strdupz("19999");
  774. clean = 1;
  775. }
  776. setenv("NETDATA_LISTEN_PORT", default_port, 1);
  777. if (clean)
  778. freez(default_port);
  779. // set the path we need
  780. char path[1024 + 1], *p = getenv("PATH");
  781. if (!p)
  782. p = "/bin:/usr/bin";
  783. snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
  784. setenv("PATH", config_get(CONFIG_SECTION_ENV_VARS, "PATH", path), 1);
  785. // python options
  786. p = getenv("PYTHONPATH");
  787. if (!p)
  788. p = "";
  789. setenv("PYTHONPATH", config_get(CONFIG_SECTION_ENV_VARS, "PYTHONPATH", p), 1);
  790. // disable buffering for python plugins
  791. setenv("PYTHONUNBUFFERED", "1", 1);
  792. // switch to standard locale for plugins
  793. setenv("LC_ALL", "C", 1);
  794. }
  795. void send_statistics(const char *action, const char *action_result, const char *action_data)
  796. {
  797. static char *as_script;
  798. if (netdata_anonymous_statistics_enabled == -1) {
  799. char *optout_file = mallocz(
  800. sizeof(char) *
  801. (strlen(netdata_configured_user_config_dir) + strlen(".opt-out-from-anonymous-statistics") + 2));
  802. sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
  803. if (likely(access(optout_file, R_OK) != 0)) {
  804. as_script = mallocz(
  805. sizeof(char) *
  806. (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
  807. sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
  808. if (unlikely(access(as_script, R_OK) != 0)) {
  809. netdata_anonymous_statistics_enabled = 0;
  810. info("Anonymous statistics script %s not found.", as_script);
  811. freez(as_script);
  812. } else {
  813. netdata_anonymous_statistics_enabled = 1;
  814. }
  815. } else {
  816. netdata_anonymous_statistics_enabled = 0;
  817. as_script = NULL;
  818. }
  819. freez(optout_file);
  820. }
  821. if (!netdata_anonymous_statistics_enabled)
  822. return;
  823. if (!action)
  824. return;
  825. if (!action_result)
  826. action_result = "";
  827. if (!action_data)
  828. action_data = "";
  829. char *command_to_run = mallocz(
  830. sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) +
  831. analytics_data.data_length + (ANALYTICS_NO_OF_ITEMS * 3) + 15));
  832. pid_t command_pid;
  833. sprintf(
  834. command_to_run,
  835. "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' ",
  836. as_script,
  837. action,
  838. action_result,
  839. action_data,
  840. analytics_data.netdata_config_stream_enabled,
  841. analytics_data.netdata_config_memory_mode,
  842. analytics_data.netdata_config_exporting_enabled,
  843. analytics_data.netdata_exporting_connectors,
  844. analytics_data.netdata_allmetrics_prometheus_used,
  845. analytics_data.netdata_allmetrics_shell_used,
  846. analytics_data.netdata_allmetrics_json_used,
  847. analytics_data.netdata_dashboard_used,
  848. analytics_data.netdata_collectors,
  849. analytics_data.netdata_collectors_count,
  850. analytics_data.netdata_buildinfo,
  851. analytics_data.netdata_config_page_cache_size,
  852. analytics_data.netdata_config_multidb_disk_quota,
  853. analytics_data.netdata_config_https_enabled,
  854. analytics_data.netdata_config_web_enabled,
  855. analytics_data.netdata_config_release_channel,
  856. analytics_data.netdata_mirrored_host_count,
  857. analytics_data.netdata_mirrored_hosts_reachable,
  858. analytics_data.netdata_mirrored_hosts_unreachable,
  859. analytics_data.netdata_notification_methods,
  860. analytics_data.netdata_alarms_normal,
  861. analytics_data.netdata_alarms_warning,
  862. analytics_data.netdata_alarms_critical,
  863. analytics_data.netdata_charts_count,
  864. analytics_data.netdata_metrics_count,
  865. analytics_data.netdata_config_is_parent,
  866. analytics_data.netdata_config_hosts_available,
  867. analytics_data.netdata_host_cloud_available,
  868. analytics_data.netdata_host_aclk_available,
  869. analytics_data.netdata_host_aclk_protocol,
  870. analytics_data.netdata_host_aclk_implementation,
  871. analytics_data.netdata_host_agent_claimed,
  872. analytics_data.netdata_host_cloud_enabled,
  873. analytics_data.netdata_config_https_available,
  874. analytics_data.netdata_install_type,
  875. analytics_data.netdata_config_is_private_registry,
  876. analytics_data.netdata_config_use_private_registry,
  877. analytics_data.netdata_config_oom_score,
  878. analytics_data.netdata_prebuilt_distro);
  879. info("%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
  880. FILE *fp_child_input;
  881. FILE *fp_child_output = netdata_popen(command_to_run, &command_pid, &fp_child_input);
  882. if (fp_child_output) {
  883. char buffer[4 + 1];
  884. char *s = fgets(buffer, 4, fp_child_output);
  885. int exit_code = netdata_pclose(fp_child_input, fp_child_output, command_pid);
  886. if (exit_code)
  887. error("Execution of anonymous statistics script returned %d.", exit_code);
  888. if (s && strncmp(buffer, "200", 3))
  889. error("Execution of anonymous statistics script returned http code %s.", buffer);
  890. } else {
  891. error("Failed to run anonymous statistics script %s.", as_script);
  892. }
  893. freez(command_to_run);
  894. }