master
c 402 lines 15.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "database/rrd.h"
4 #include "registry_internals.h"
5
6 int registry_db_should_be_saved(void) {
7 netdata_log_debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
8 return registry.log_count > registry.save_registry_every_entries;
9 }
10
11 // ----------------------------------------------------------------------------
12 // INTERNAL FUNCTIONS FOR SAVING REGISTRY OBJECTS
13
14 static int registry_machine_save_url(REGISTRY_MACHINE_URL *mu, FILE *fp) {
15 netdata_log_debug(D_REGISTRY, "REGISTRY: registry_machine_save_url('%s')", string2str(mu->url));
16
17 int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
18 mu->first_t,
19 mu->last_t,
20 mu->usages,
21 mu->flags,
22 string2str(mu->url)
23 );
24
25 // error handling is done at registry_db_save()
26
27 return ret;
28 }
29
30 static int registry_machine_save(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) {
31
32 REGISTRY_MACHINE *m = entry;
33 FILE *fp = file;
34
35 netdata_log_debug(D_REGISTRY, "REGISTRY: registry_machine_save('%s')", m->guid);
36
37 int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
38 m->first_t,
39 m->last_t,
40 m->usages,
41 m->guid
42 );
43
44 if(ret >= 0) {
45 for(REGISTRY_MACHINE_URL *mu = m->machine_urls; mu ; mu = mu->next) {
46 int rc = registry_machine_save_url(mu, fp);
47 if(rc < 0)
48 return -1; // Error saving URL
49 }
50 return 1; // Successfully saved 1 machine
51 }
52
53 // error handling is done at registry_db_save()
54
55 return -1; // Error writing machine record
56 }
57
58 static inline int registry_person_save_url(REGISTRY_PERSON_URL *pu, FILE *fp) {
59 netdata_log_debug(D_REGISTRY, "REGISTRY: registry_person_save_url('%s')", string2str(pu->url));
60
61 int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
62 pu->first_t,
63 pu->last_t,
64 pu->usages,
65 pu->flags,
66 pu->machine->guid,
67 string2str(pu->machine_name),
68 string2str(pu->url)
69 );
70
71 // error handling is done at registry_db_save()
72
73 return ret;
74 }
75
76 static inline int registry_person_save(const DICTIONARY_ITEM *item __maybe_unused, void *entry, void *file) {
77 REGISTRY_PERSON *p = entry;
78 FILE *fp = file;
79
80 netdata_log_debug(D_REGISTRY, "REGISTRY: registry_person_save('%s')", p->guid);
81
82 int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
83 p->first_t,
84 p->last_t,
85 p->usages,
86 p->guid
87 );
88
89 if(ret >= 0) {
90 for(REGISTRY_PERSON_URL *pu = p->person_urls; pu ;pu = pu->next) {
91 int rc = registry_person_save_url(pu, fp);
92 if(rc < 0)
93 return -1; // Error saving URL
94 }
95 return 1; // Successfully saved 1 person
96 }
97
98 // error handling is done at registry_db_save()
99
100 return -1; // Error writing person record
101 }
102
103 // ----------------------------------------------------------------------------
104 // SAVE THE REGISTRY DATABASE
105
106 int registry_db_save(void) {
107 if(unlikely(!registry.enabled))
108 return -1;
109
110 if(unlikely(!registry_db_should_be_saved()))
111 return -2;
112
113 // Implement exponential backoff for save failures
114 if(registry.consecutive_save_failures > 0) {
115 time_t now = now_realtime_sec();
116 time_t backoff_seconds = 60 * (1 << (registry.consecutive_save_failures - 1)); // 60s, 120s, 240s, etc.
117 if(backoff_seconds > 3600) backoff_seconds = 3600; // Cap at 1 hour
118
119 if((now - registry.last_save_failure) < backoff_seconds) {
120 netdata_log_debug(D_REGISTRY, "REGISTRY: skipping save due to backoff (failed %d times, waiting %ld seconds)",
121 registry.consecutive_save_failures, backoff_seconds);
122 return -3;
123 }
124 }
125
126 nd_log_limits_unlimited();
127
128 char tmp_filename[FILENAME_MAX + 1];
129 char old_filename[FILENAME_MAX + 1];
130
131 snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
132 snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
133
134 netdata_log_debug(D_REGISTRY, "REGISTRY: Creating file '%s'", tmp_filename);
135 FILE *fp = fopen(tmp_filename, "w");
136 if(!fp) {
137 netdata_log_error("REGISTRY: Cannot create file: %s", tmp_filename);
138 nd_log_limits_reset();
139 registry.consecutive_save_failures++;
140 registry.last_save_failure = now_realtime_sec();
141 return -1;
142 }
143
144 // dictionary_walkthrough_read() has its own locking, so this is safe to do
145
146 netdata_log_debug(D_REGISTRY, "REGISTRY: saving all machines");
147 int machines_saved = dictionary_walkthrough_read(registry.machines, registry_machine_save, fp);
148 if(machines_saved < 0) {
149 netdata_log_error("REGISTRY: Cannot save registry machines - return value %d", machines_saved);
150 fclose(fp);
151 nd_log_limits_reset();
152 registry.consecutive_save_failures++;
153 registry.last_save_failure = now_realtime_sec();
154 return machines_saved;
155 }
156 netdata_log_debug(D_REGISTRY, "REGISTRY: saved %d machines", machines_saved);
157
158 netdata_log_debug(D_REGISTRY, "Saving all persons");
159 int persons_saved = dictionary_walkthrough_read(registry.persons, registry_person_save, fp);
160 if(persons_saved < 0) {
161 netdata_log_error("REGISTRY: Cannot save registry persons - return value %d", persons_saved);
162 fclose(fp);
163 nd_log_limits_reset();
164 registry.consecutive_save_failures++;
165 registry.last_save_failure = now_realtime_sec();
166 return persons_saved;
167 }
168 netdata_log_debug(D_REGISTRY, "REGISTRY: saved %d persons", persons_saved);
169
170 // save the totals
171 fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
172 registry.persons_count,
173 registry.machines_count,
174 registry.usages_count + 1, // this is required - it is lost on db rotation
175 0LLU, //registry.urls_count,
176 registry.persons_urls_count,
177 registry.machines_urls_count
178 );
179
180 fclose(fp);
181
182 errno_clear();
183
184 // remove the .old db
185 netdata_log_debug(D_REGISTRY, "REGISTRY: Removing old db '%s'", old_filename);
186 if(unlink(old_filename) == -1 && errno != ENOENT)
187 netdata_log_error("REGISTRY: cannot remove old registry file '%s'", old_filename);
188
189 // rename the db to .old
190 netdata_log_debug(D_REGISTRY, "REGISTRY: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
191 if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
192 netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Saving registry DB failed!", registry.db_filename, old_filename);
193
194 else {
195 // remove the database (it is saved in .old)
196 netdata_log_debug(D_REGISTRY, "REGISTRY: removing db '%s'", registry.db_filename);
197 if (unlink(registry.db_filename) == -1 && errno != ENOENT)
198 netdata_log_error("REGISTRY: cannot remove old registry file '%s'", registry.db_filename);
199
200 // move the .tmp to make it active
201 netdata_log_debug(D_REGISTRY, "REGISTRY: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
202 if (link(tmp_filename, registry.db_filename) == -1) {
203 netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
204 registry.db_filename);
205
206 // move the .old back
207 netdata_log_debug(D_REGISTRY, "REGISTRY: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
208 if(link(old_filename, registry.db_filename) == -1)
209 netdata_log_error("REGISTRY: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
210 }
211 else {
212 netdata_log_debug(D_REGISTRY, "REGISTRY: removing tmp db '%s'", tmp_filename);
213 if(unlink(tmp_filename) == -1)
214 netdata_log_error("REGISTRY: cannot remove tmp registry file '%s'", tmp_filename);
215
216 // it has been moved successfully
217 // discard the current registry log
218 registry_log_recreate();
219 registry.log_count = 0;
220
221 // Reset failure tracking on success
222 registry.consecutive_save_failures = 0;
223 registry.last_save_failure = 0;
224 }
225 }
226
227 // continue operations
228 nd_log_limits_reset();
229
230 return 0; // Success
231 }
232
233 // ----------------------------------------------------------------------------
234 // LOAD THE REGISTRY DATABASE
235
236 size_t registry_db_load(void) {
237 char *s, buf[4096 + 1];
238 REGISTRY_PERSON *p = NULL;
239 REGISTRY_MACHINE *m = NULL;
240 STRING *u = NULL;
241 size_t line = 0;
242
243 netdata_log_debug(D_REGISTRY, "REGISTRY: loading active db from: '%s'", registry.db_filename);
244 FILE *fp = fopen(registry.db_filename, "r");
245 if(!fp) {
246 if (errno != ENOENT)
247 netdata_log_error("REGISTRY: cannot open registry file: '%s'", registry.db_filename);
248 return 0;
249 }
250
251 REGISTRY_MACHINE_URL *mu;
252 size_t len = 0;
253 buf[4096] = '\0';
254 while((s = fgets_trim_len(buf, 4096, fp, &len))) {
255 line++;
256
257 netdata_log_debug(D_REGISTRY, "REGISTRY: read line %zu to length %zu: %s", line, len, s);
258 switch(*s) {
259 case 'U': // person URL
260 if(unlikely(!p)) {
261 netdata_log_error("REGISTRY: ignoring line %zu, no person loaded: %s", line, s);
262 continue;
263 }
264
265 // verify it is valid
266 if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
267 netdata_log_error("REGISTRY: person URL line %zu is wrong (len = %zu).", line, len);
268 continue;
269 }
270
271 s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
272
273 // skip the name to find the url
274 char *url = &s[69];
275 while(*url && *url != '\t') url++;
276 if(!*url) {
277 netdata_log_error("REGISTRY: person URL line %zu does not have a url.", line);
278 continue;
279 }
280 *url++ = '\0';
281
282 if(*url != 'h' && *url != '*') {
283 netdata_log_error("REGISTRY: person URL line %zu does not have a valid url: %s", line, url);
284 continue;
285 }
286
287 u = string_strdupz(url);
288
289 time_t first_t = (time_t)strtoul(&s[2], NULL, 16);
290
291 m = registry_machine_find(&s[32]);
292 if(!m) m = registry_machine_allocate(&s[32], first_t);
293
294 mu = registry_machine_url_find(m, u);
295 if(!mu) {
296 netdata_log_error("REGISTRY: person URL line %zu was not linked to the machine it refers to", line);
297 mu = registry_machine_url_allocate(m, u, first_t);
298 }
299
300 REGISTRY_PERSON_URL *pu = registry_person_url_index_find(p, u);
301 if(!pu)
302 pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
303 else
304 netdata_log_error("REGISTRY: person URL line %zu is duplicate, reusing the old one.", line);
305
306 pu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
307 pu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
308 pu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
309 netdata_log_debug(D_REGISTRY, "REGISTRY: loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x",
310 string2str(u), string2str(pu->machine_name), m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
311
312 string_freez(u);
313 break;
314
315 case 'P': // person
316 m = NULL;
317 // verify it is valid
318 if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
319 netdata_log_error("REGISTRY: person line %zu is wrong (len = %zu).", line, len);
320 continue;
321 }
322
323 s[1] = s[10] = s[19] = s[28] = '\0';
324 p = registry_person_allocate(&s[29], (time_t)strtoul(&s[2], NULL, 16));
325 p->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
326 p->usages = (uint32_t)strtoul(&s[20], NULL, 16);
327 netdata_log_debug(D_REGISTRY, "REGISTRY: loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
328 break;
329
330 case 'V': // machine URL
331 if(unlikely(!m)) {
332 netdata_log_error("REGISTRY: ignoring line %zu, no machine loaded: %s", line, s);
333 continue;
334 }
335
336 // verify it is valid
337 if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
338 netdata_log_error("REGISTRY: person URL line %zu is wrong (len = %zu).", line, len);
339 continue;
340 }
341
342 s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
343
344 url = &s[32];
345 if(*url != 'h' && *url != '*') {
346 netdata_log_error("REGISTRY: machine URL line %zu does not have a valid url: %s", line, url);
347 continue;
348 }
349
350 u = string_strdupz(url);
351
352 mu = registry_machine_url_find(m, u);
353 if(!mu)
354 mu = registry_machine_url_allocate(m, u, (time_t)strtoul(&s[2], NULL, 16));
355 else
356 netdata_log_error("REGISTRY: machine URL line %zu is duplicate, reusing the old one.", line);
357
358 mu->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
359 mu->usages = (uint32_t)strtoul(&s[20], NULL, 16);
360 mu->flags = (uint8_t)strtoul(&s[29], NULL, 16);
361 netdata_log_debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x",
362 string2str(u), m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
363
364 string_freez(u);
365 break;
366
367 case 'M': // machine
368 p = NULL;
369 // verify it is valid
370 if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
371 netdata_log_error("REGISTRY: person line %zu is wrong (len = %zu).", line, len);
372 continue;
373 }
374
375 s[1] = s[10] = s[19] = s[28] = '\0';
376 m = registry_machine_allocate(&s[29], (time_t)strtoul(&s[2], NULL, 16));
377 m->last_t = (uint32_t)strtoul(&s[11], NULL, 16);
378 m->usages = (uint32_t)strtoul(&s[20], NULL, 16);
379 netdata_log_debug(D_REGISTRY, "REGISTRY: loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
380 break;
381
382 case 'T': // totals
383 if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
384 netdata_log_error("REGISTRY: totals line %zu is wrong (len = %zu).", line, len);
385 continue;
386 }
387 registry.persons_count = strtoull(&s[2], NULL, 16);
388 registry.machines_count = strtoull(&s[19], NULL, 16);
389 registry.usages_count = strtoull(&s[36], NULL, 16);
390 registry.persons_urls_count = strtoull(&s[70], NULL, 16);
391 registry.machines_urls_count = strtoull(&s[87], NULL, 16);
392 break;
393
394 default:
395 netdata_log_error("REGISTRY: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
396 break;
397 }
398 }
399 fclose(fp);
400
401 return line;
402 }