analytics.c 33 KB

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