dyncfg-files.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "dyncfg-internals.h"
  3. #include "dyncfg.h"
  4. void dyncfg_file_delete(const char *id) {
  5. CLEAN_CHAR_P *escaped_id = dyncfg_escape_id_for_filename(id);
  6. char filename[FILENAME_MAX];
  7. snprintfz(filename, sizeof(filename), "%s/%s.dyncfg", dyncfg_globals.dir, escaped_id);
  8. unlink(filename);
  9. }
  10. void dyncfg_file_save(const char *id, DYNCFG *df) {
  11. CLEAN_CHAR_P *escaped_id = dyncfg_escape_id_for_filename(id);
  12. char filename[FILENAME_MAX];
  13. snprintfz(filename, sizeof(filename), "%s/%s.dyncfg", dyncfg_globals.dir, escaped_id);
  14. FILE *fp = fopen(filename, "w");
  15. if(!fp) {
  16. nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot create file '%s'", filename);
  17. return;
  18. }
  19. df->modified_ut = now_realtime_usec();
  20. fprintf(fp, "version=%zu\n", DYNCFG_VERSION);
  21. fprintf(fp, "id=%s\n", id);
  22. if(df->template)
  23. fprintf(fp, "template=%s\n", string2str(df->template));
  24. char uuid_str[UUID_COMPACT_STR_LEN];
  25. uuid_unparse_lower_compact(df->host_uuid, uuid_str);
  26. fprintf(fp, "host=%s\n", uuid_str);
  27. fprintf(fp, "path=%s\n", string2str(df->path));
  28. fprintf(fp, "type=%s\n", dyncfg_id2type(df->type));
  29. fprintf(fp, "source_type=%s\n", dyncfg_id2source_type(df->source_type));
  30. fprintf(fp, "source=%s\n", string2str(df->source));
  31. fprintf(fp, "created=%"PRIu64"\n", df->created_ut);
  32. fprintf(fp, "modified=%"PRIu64"\n", df->modified_ut);
  33. fprintf(fp, "sync=%s\n", df->sync ? "true" : "false");
  34. fprintf(fp, "user_disabled=%s\n", df->user_disabled ? "true" : "false");
  35. fprintf(fp, "saves=%"PRIu32"\n", ++df->saves);
  36. fprintf(fp, "cmds=");
  37. dyncfg_cmds2fp(df->cmds, fp);
  38. fprintf(fp, "\n");
  39. if(df->payload && buffer_strlen(df->payload) > 0) {
  40. fprintf(fp, "content_type=%s\n", content_type_id2string(df->payload->content_type));
  41. fprintf(fp, "content_length=%zu\n", buffer_strlen(df->payload));
  42. fprintf(fp, "---\n");
  43. fwrite(buffer_tostring(df->payload), 1, buffer_strlen(df->payload), fp);
  44. }
  45. fclose(fp);
  46. }
  47. void dyncfg_file_load(const char *filename) {
  48. FILE *fp = fopen(filename, "r");
  49. if (!fp) {
  50. nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot open file '%s'", filename);
  51. return;
  52. }
  53. DYNCFG tmp = {
  54. .host = NULL,
  55. .status = DYNCFG_STATUS_ORPHAN,
  56. };
  57. char line[PLUGINSD_LINE_MAX];
  58. CLEAN_CHAR_P *id = NULL;
  59. HTTP_CONTENT_TYPE content_type = CT_NONE;
  60. size_t content_length = 0;
  61. bool read_payload = false;
  62. while (fgets(line, sizeof(line), fp)) {
  63. if(strcmp(line, "---\n") == 0) {
  64. read_payload = true;
  65. break;
  66. }
  67. char *value = strchr(line, '=');
  68. if(!value) continue;
  69. *value++ = '\0';
  70. value = trim(value);
  71. if(!value) continue;
  72. char *key = trim(line);
  73. if(!key) continue;
  74. // Parse key-value pairs
  75. if (strcmp(key, "version") == 0) {
  76. size_t version = strtoull(value, NULL, 10);
  77. if(version > DYNCFG_VERSION)
  78. nd_log(NDLS_DAEMON, NDLP_NOTICE,
  79. "DYNCFG: configuration file '%s' has version %zu, which is newer than our version %zu",
  80. filename, version, DYNCFG_VERSION);
  81. } else if (strcmp(key, "id") == 0) {
  82. freez(id);
  83. id = strdupz(value);
  84. } else if (strcmp(key, "template") == 0) {
  85. tmp.template = string_strdupz(value);
  86. } else if (strcmp(key, "host") == 0) {
  87. uuid_parse_flexi(value, tmp.host_uuid);
  88. } else if (strcmp(key, "path") == 0) {
  89. tmp.path = string_strdupz(value);
  90. } else if (strcmp(key, "type") == 0) {
  91. tmp.type = dyncfg_type2id(value);
  92. } else if (strcmp(key, "source_type") == 0) {
  93. tmp.source_type = dyncfg_source_type2id(value);
  94. } else if (strcmp(key, "source") == 0) {
  95. tmp.source = string_strdupz(value);
  96. } else if (strcmp(key, "created") == 0) {
  97. tmp.created_ut = strtoull(value, NULL, 10);
  98. } else if (strcmp(key, "modified") == 0) {
  99. tmp.modified_ut = strtoull(value, NULL, 10);
  100. } else if (strcmp(key, "sync") == 0) {
  101. tmp.sync = (strcmp(value, "true") == 0);
  102. } else if (strcmp(key, "user_disabled") == 0) {
  103. tmp.user_disabled = (strcmp(value, "true") == 0);
  104. } else if (strcmp(key, "saves") == 0) {
  105. tmp.saves = strtoull(value, NULL, 10);
  106. } else if (strcmp(key, "content_type") == 0) {
  107. content_type = content_type_string2id(value);
  108. } else if (strcmp(key, "content_length") == 0) {
  109. content_length = strtoull(value, NULL, 10);
  110. } else if (strcmp(key, "cmds") == 0) {
  111. tmp.cmds = dyncfg_cmds2id(value);
  112. }
  113. }
  114. if(read_payload && content_length) {
  115. tmp.payload = buffer_create(content_length, NULL);
  116. tmp.payload->content_type = content_type;
  117. buffer_need_bytes(tmp.payload, content_length);
  118. tmp.payload->len = fread(tmp.payload->buffer, 1, content_length, fp);
  119. }
  120. fclose(fp);
  121. if(!id) {
  122. nd_log(NDLS_DAEMON, NDLP_ERR,
  123. "DYNCFG: configuration file '%s' does not include a unique id. Ignoring it.",
  124. filename);
  125. dyncfg_cleanup(&tmp);
  126. return;
  127. }
  128. dictionary_set(dyncfg_globals.nodes, id, &tmp, sizeof(tmp));
  129. }
  130. void dyncfg_load_all(void) {
  131. DIR *dir = opendir(dyncfg_globals.dir);
  132. if (!dir) {
  133. nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot open directory '%s'", dyncfg_globals.dir);
  134. return;
  135. }
  136. struct dirent *entry;
  137. char filepath[PATH_MAX];
  138. while ((entry = readdir(dir)) != NULL) {
  139. if ((entry->d_type == DT_REG || entry->d_type == DT_LNK) && strendswith(entry->d_name, ".dyncfg")) {
  140. snprintf(filepath, sizeof(filepath), "%s/%s", dyncfg_globals.dir, entry->d_name);
  141. dyncfg_file_load(filepath);
  142. }
  143. }
  144. closedir(dir);
  145. }
  146. // ----------------------------------------------------------------------------
  147. // schemas loading
  148. static bool dyncfg_read_file_to_buffer(const char *filename, BUFFER *dst) {
  149. int fd = open(filename, O_RDONLY, 0666);
  150. if(unlikely(fd == -1))
  151. return false;
  152. struct stat st = { 0 };
  153. if(fstat(fd, &st) != 0) {
  154. close(fd);
  155. return false;
  156. }
  157. buffer_flush(dst);
  158. buffer_need_bytes(dst, st.st_size + 1); // +1 for the terminating zero
  159. ssize_t r = read(fd, (char*)dst->buffer, st.st_size);
  160. if(unlikely(r == -1)) {
  161. close(fd);
  162. return false;
  163. }
  164. dst->len = r;
  165. dst->buffer[dst->len] = '\0';
  166. close(fd);
  167. return true;
  168. }
  169. bool dyncfg_get_schema(const char *id, BUFFER *dst) {
  170. char filename[FILENAME_MAX + 1];
  171. snprintfz(filename, sizeof(filename), "%s/schema.d/%s.json", netdata_configured_user_config_dir, id);
  172. if(dyncfg_read_file_to_buffer(filename, dst))
  173. return true;
  174. snprintfz(filename, sizeof(filename), "%s/schema.d/%s.json", netdata_configured_stock_config_dir, id);
  175. if(dyncfg_read_file_to_buffer(filename, dst))
  176. return true;
  177. return false;
  178. }