@cryptotaxi247 / netdata-1 / commits / 8246d10d2

allow netdata to cleanup orphan hosts that have not pushed any metrics for some time (configurable)

Costa Tsaousis (ktsaou) committed Feb 26, 2017 at 00:21 UTC 8246d10d2749734d15f8ad9c44c9a8f81850f33c
9 files changed +146 -72
conf.d/stream.conf
+1
@@ -28,6 +28,7 @@
28 # buffer size bytes = 1048576
29 # reconnect delay seconds = 5
30 # initial clock resync iterations = 60
31 + # free orphan hosts after seconds = 3600
32
33
34 # -----------------------------------------------------------------------------
src/rrd.c
-1
@@ -136,4 +136,3 @@ char *rrdset_cache_dir(RRDHOST *host, const char *id, const char *config_section
136
137 return ret;
138 }
139 -
src/rrd.h
+84 -49
@@ -74,21 +74,6 @@ typedef enum rrd_algorithm {
74 extern RRD_ALGORITHM rrd_algorithm_id(const char *name);
75 extern const char *rrd_algorithm_name(RRD_ALGORITHM algorithm);
76
77 -// ----------------------------------------------------------------------------
78 -// flags
79 -
80 -typedef enum rrddim_flags {
81 - RRDDIM_FLAG_HIDDEN = 1 << 0, // this dimension will not be offered to callers
82 - RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = 1 << 1, // do not offer RESET or OVERFLOW info to callers
83 - RRDDIM_FLAG_UPDATED = 1 << 2, // the dimension has been updated since the last processing
84 - RRDDIM_FLAG_EXPOSED = 1 << 3 // when set what have sent this dimension to the central netdata
85 -} RRDDIM_FLAGS;
86 -
87 -#define rrddim_flag_check(rd, flag) ((rd)->flags & flag)
88 -#define rrddim_flag_set(rd, flag) (rd)->flags |= flag
89 -#define rrddim_flag_clear(rd, flag) (rd)->flags &= ~flag
90 -
91 -
77 // ----------------------------------------------------------------------------
78 // RRD FAMILY
79
@@ -105,6 +90,22 @@ struct rrdfamily {
90 typedef struct rrdfamily RRDFAMILY;
91
92
93 +// ----------------------------------------------------------------------------
94 +// flags
95 +// use this for configuration flags, not for state control
96 +// flags are set/unset in a manner that is not thread safe
97 +// and may lead to missing information.
98 +
99 +typedef enum rrddim_flags {
100 + RRDDIM_FLAG_HIDDEN = 1 << 0, // this dimension will not be offered to callers
101 + RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS = 1 << 1 // do not offer RESET or OVERFLOW info to callers
102 +} RRDDIM_FLAGS;
103 +
104 +#define rrddim_flag_check(rd, flag) ((rd)->flags & flag)
105 +#define rrddim_flag_set(rd, flag) (rd)->flags |= flag
106 +#define rrddim_flag_clear(rd, flag) (rd)->flags &= ~flag
107 +
108 +
109 // ----------------------------------------------------------------------------
110 // RRD DIMENSION - this is a metric
111
@@ -130,7 +131,7 @@ struct rrddim {
131 collected_number multiplier; // the multiplier of the collected values
132 collected_number divisor; // the divider of the collected values
133
133 - uint32_t flags; // options and status options for the dimension
134 + uint32_t flags; // configuration flags for the dimension
135
136 // ------------------------------------------------------------------------
137 // members for temporary data we need for calculations
@@ -143,7 +144,11 @@ struct rrddim {
144
145 char *cache_filename; // the filename we load/save from/to this set
146
146 - size_t counter; // the number of times we added values to this rrdim
147 + size_t collections_counter; // the number of times we added values to this rrdim
148 + size_t unused[10];
149 +
150 + int updated:1; // 1 when the dimension has been updated since the last processing
151 + int exposed:1; // 1 when set what have sent this dimension to the central netdata
152
153 struct timeval last_collected_time; // when was this dimension last updated
154 // this is actual date time we updated the last_collected_value
@@ -200,11 +205,15 @@ typedef struct rrddim RRDDIM;
205 // ----------------------------------------------------------------------------
206 // RRDSET - this is a chart
207
208 +// use this for configuration flags, not for state control
209 +// flags are set/unset in a manner that is not thread safe
210 +// and may lead to missing information.
211 +
212 typedef enum rrdset_flags {
204 - RRDSET_FLAG_ENABLED = 1 << 0, // enables or disables a chart
205 - RRDSET_FLAG_DETAIL = 1 << 1, // if set, the data set should be considered as a detail of another
206 - // (the master data set should be the one that has the same family and is not detail)
207 - RRDSET_FLAG_DEBUG = 1 << 2 // enables or disables debugging for a chart
213 + RRDSET_FLAG_ENABLED = 1 << 0, // enables or disables a chart
214 + RRDSET_FLAG_DETAIL = 1 << 1, // if set, the data set should be considered as a detail of another
215 + // (the master data set should be the one that has the same family and is not detail)
216 + RRDSET_FLAG_DEBUG = 1 << 2 // enables or disables debugging for a chart
217 } RRDSET_FLAGS;
218
219 #define rrdset_flag_check(st, flag) ((st)->flags & flag)
@@ -247,7 +256,7 @@ struct rrdset {
256 long current_entry; // the entry that is currently being updated
257 // it goes around in a round-robin fashion
258
250 - uint32_t flags;
259 + uint32_t flags; // configuration flags
260
261 int gap_when_lost_iterations_above; // after how many lost iterations a gap should be stored
262 // netdata will interpolate values for gaps lower than this
@@ -265,8 +274,9 @@ struct rrdset {
274
275 pthread_rwlock_t rrdset_rwlock; // protects dimensions linked list
276
268 - unsigned long counter; // the number of times we added values to this rrd
269 - unsigned long counter_done; // the number of times we added values to this rrd
277 + size_t counter; // the number of times we added values to this database
278 + size_t counter_done; // the number of times rrdset_done() has been called
279 + size_t unused[10];
280
281 uint32_t hash; // a simple hash on the id, to speed up searching
282 // we first compare hashes, and only if the hashes are equal we do string comparisons
@@ -332,43 +342,58 @@ typedef struct rrdset RRDSET;
342 struct rrdhost {
343 avl avl; // the index of hosts
344
345 + // ------------------------------------------------------------------------
346 + // host information
347 +
348 char *hostname; // the hostname of this host
349 uint32_t hash_hostname; // the hostname hash
350
351 char machine_guid[GUID_LEN + 1]; // the unique ID of this host
352 uint32_t hash_machine_guid; // the hash of the unique ID
353
354 + char *os; // the O/S type of the host
355 int rrd_update_every; // the update frequency of the host
356 int rrd_history_entries; // the number of history entries for the host's charts
357 + RRD_MEMORY_MODE rrd_memory_mode; // the memory more for the charts of this host
358 +
359 + char *cache_dir; // the directory to save RRD cache files
360 + char *varlib_dir; // the directory to save health log
361 +
362
344 - int rrdpush_enabled; // 1 when this host sends metrics to another netdata
363 + // ------------------------------------------------------------------------
364 + // streaming of data to remote hosts - rrdpush
365 +
366 + int rrdpush_enabled:1; // 1 when this host sends metrics to another netdata
367 char *rrdpush_destination; // where to send metrics to
368 char *rrdpush_api_key; // the api key at the receiving netdata
347 - volatile int rrdpush_connected; // 1 when the sender is ready to push metrics
348 - volatile int rrdpush_spawn; // 1 when the sender thread has been spawn
349 - volatile int rrdpush_error_shown; // 1 when we have logged a communication error
369 + volatile int rrdpush_connected:1; // 1 when the sender is ready to push metrics
370 + volatile int rrdpush_spawn:1; // 1 when the sender thread has been spawn
371 + volatile int rrdpush_error_shown:1; // 1 when we have logged a communication error
372 int rrdpush_socket; // the fd of the socket to the remote host, or -1
373 pthread_t rrdpush_thread; // the sender thread
374 pthread_mutex_t rrdpush_mutex; // exclusive access to rrdpush_buffer
375 int rrdpush_pipe[2]; // collector to sender thread communication
376 BUFFER *rrdpush_buffer; // collector fills it, sender sends them
377
356 - int health_enabled; // 1 when this host has health enabled
357 - time_t health_delay_up_to; // a timestamp to delay alarms processing up to
358 - RRD_MEMORY_MODE rrd_memory_mode; // the memory more for the charts of this host
378
360 - RRDSET *rrdset_root; // the host charts
379 + // ------------------------------------------------------------------------
380 + // streaming of data from remote hosts - rrdpush
381
362 - pthread_rwlock_t rrdhost_rwlock; // lock for this RRDHOST (protects rrdset_root linked list)
382 + volatile size_t connected_senders; // when remote hosts are streaming to this
383 + // host, this is the counter of connected clients
384
364 - avl_tree_lock rrdset_root_index; // the host's charts index (by id)
365 - avl_tree_lock rrdset_root_index_name; // the host's charts index (by name)
385 + time_t senders_disconnected_time; // the time the last sender was disconnected
386
367 - avl_tree_lock rrdfamily_root_index; // the host's chart families index
368 - avl_tree_lock variables_root_index; // the host's chart variables index
387 + // ------------------------------------------------------------------------
388 + // health monitoring options
389
370 - char *cache_dir; // the directory to save RRD cache files
371 - char *varlib_dir; // the directory to save health log
390 + int health_enabled:1; // 1 when this host has health enabled
391 + time_t health_delay_up_to; // a timestamp to delay alarms processing up to
392 + char *health_default_exec; // the full path of the alarms notifications program
393 + char *health_default_recipient; // the default recipient for all alarms
394 + char *health_log_filename; // the alarms event log filename
395 + size_t health_log_entries_written; // the number of alarm events writtern to the alarms event log
396 + FILE *health_log_fp; // the FILE pointer to the open alarms event log file
397
398 // all RRDCALCs are primarily allocated and linked here
399 // RRDCALCs may be linked to charts at any point
@@ -382,16 +407,23 @@ struct rrdhost {
407 // are created or renamed, that match them
408 RRDCALCTEMPLATE *templates;
409
385 - char *os; // the O/S type of the host
386 - volatile size_t use_counter; // when remote hosts are streaming to this
387 - // host, this is the counter of connected clients
410
389 - // health / alarm settings
390 - char *health_default_exec;
391 - char *health_default_recipient;
392 - char *health_log_filename;
393 - size_t health_log_entries_written;
394 - FILE *health_log_fp;
411 + // ------------------------------------------------------------------------
412 + // the charts of the host
413 +
414 + RRDSET *rrdset_root; // the host charts
415 +
416 +
417 + // ------------------------------------------------------------------------
418 + // locks
419 +
420 + pthread_rwlock_t rrdhost_rwlock; // lock for this RRDHOST (protects rrdset_root linked list)
421 +
422 + avl_tree_lock rrdset_root_index; // the host's charts index (by id)
423 + avl_tree_lock rrdset_root_index_name; // the host's charts index (by name)
424 +
425 + avl_tree_lock rrdfamily_root_index; // the host's chart families index
426 + avl_tree_lock variables_root_index; // the host's chart variables index
427
428 struct rrdhost *next;
429 };
@@ -422,9 +454,11 @@ extern pthread_rwlock_t rrd_rwlock;
454
455 // ----------------------------------------------------------------------------
456
457 +extern time_t rrdhost_free_orphan_time;
458 +
459 extern void rrd_init(char *hostname);
460
427 -extern RRDHOST *rrdhost_find(const char *guid, uint32_t hash);
461 +extern RRDHOST *rrdhost_find_guid(const char *guid, uint32_t hash);
462 extern RRDHOST *rrdhost_find_or_create(
463 const char *hostname
464 , const char *guid
@@ -484,6 +518,7 @@ extern RRDSET *rrdset_create(RRDHOST *host
518 extern void rrdhost_free_all(void);
519 extern void rrdhost_save_all(void);
520
521 +extern void rrdhost_cleanup_remote_stale(RRDHOST *protected);
522 extern void rrdhost_free(RRDHOST *host);
523 extern void rrdhost_save(RRDHOST *host);
524
src/rrd2json.c
+2 -2
@@ -169,7 +169,7 @@ void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb) {
169 // for each dimension
170 RRDDIM *rd;
171 rrddim_foreach_read(rd, st) {
172 - if(rd->counter) {
172 + if(rd->collections_counter) {
173 char dimension[PROMETHEUS_ELEMENT_MAX + 1];
174 prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX);
175
@@ -241,7 +241,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
241 // for each dimension
242 RRDDIM *rd;
243 rrddim_foreach_read(rd, st) {
244 - if(rd->counter) {
244 + if(rd->collections_counter) {
245 char dimension[SHELL_ELEMENT_MAX + 1];
246 shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX);
247
src/rrddim.c
+5 -3
@@ -162,7 +162,8 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, collected_numbe
162 rd->update_every = st->update_every;
163
164 // prevent incremental calculation spikes
165 - rd->counter = 0;
165 + rd->collections_counter = 0;
166 + rd->updated = 0;
167 rd->flags = 0x00000000;
168
169 rd->calculated_value = 0;
@@ -292,8 +293,9 @@ inline collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_
293
294 now_realtime_timeval(&rd->last_collected_time);
295 rd->collected_value = value;
295 - rrddim_flag_set(rd, RRDDIM_FLAG_UPDATED);
296 - rd->counter++;
296 + rd->updated = 1;
297 +
298 + rd->collections_counter++;
299
300 // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update));
301
src/rrdhost.c
+24 -2
@@ -5,6 +5,7 @@ RRDHOST *localhost = NULL;
5
6 pthread_rwlock_t rrd_rwlock = PTHREAD_RWLOCK_INITIALIZER;
7
8 +time_t rrdhost_free_orphan_time = 3600;
9
10 // ----------------------------------------------------------------------------
11 // RRDHOST index
@@ -20,7 +21,7 @@ avl_tree_lock rrdhost_root_index = {
21 .rwlock = AVL_LOCK_INITIALIZER
22 };
23
23 -RRDHOST *rrdhost_find(const char *guid, uint32_t hash) {
24 +RRDHOST *rrdhost_find_guid(const char *guid, uint32_t hash) {
25 debug(D_RRDHOST, "Searching in index for host with guid '%s'", guid);
26
27 RRDHOST tmp;
@@ -250,7 +251,7 @@ RRDHOST *rrdhost_find_or_create(
251 ) {
252 debug(D_RRDHOST, "Searching for host '%s' with guid '%s'", hostname, guid);
253
253 - RRDHOST *host = rrdhost_find(guid, 0);
254 + RRDHOST *host = rrdhost_find_guid(guid, 0);
255 if(!host) {
256 host = rrdhost_create(
257 hostname
@@ -286,9 +287,30 @@ RRDHOST *rrdhost_find_or_create(
287 error("Host '%s' has memory mode '%s', but the wanted one is '%s'.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode));
288 }
289
290 + rrdhost_cleanup_remote_stale(host);
291 +
292 return host;
293 }
294
295 +void rrdhost_cleanup_remote_stale(RRDHOST *protected) {
296 + rrd_wrlock();
297 +
298 + RRDHOST *h;
299 + rrdhost_foreach_write(h) {
300 + if(h != protected
301 + && h != localhost
302 + && !h->connected_senders
303 + && h->senders_disconnected_time + rrdhost_free_orphan_time > now_realtime_sec()) {
304 + info("Host '%s' with machine guid '%s' is obsolete - cleaning up.", h->hostname, h->machine_guid);
305 + rrdhost_save(h);
306 + rrdhost_free(h);
307 + break;
308 + }
309 + }
310 +
311 + rrd_unlock();
312 +}
313 +
314 // ----------------------------------------------------------------------------
315 // RRDHOST global / startup initialization
316
src/rrdpush.c
+10 -7
@@ -33,6 +33,7 @@ int rrdpush_init() {
33 default_rrdpush_enabled = appconfig_get_boolean(&stream_config, CONFIG_SECTION_STREAM, "enabled", default_rrdpush_enabled);
34 default_rrdpush_destination = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "destination", "");
35 default_rrdpush_api_key = appconfig_get(&stream_config, CONFIG_SECTION_STREAM, "api key", "");
36 + rrdhost_free_orphan_time = appconfig_get_number(&stream_config, CONFIG_SECTION_STREAM, "free orphan hosts after seconds", rrdhost_free_orphan_time);
37
38 if(default_rrdpush_enabled && (!default_rrdpush_destination || !*default_rrdpush_destination || !default_rrdpush_api_key || !*default_rrdpush_api_key)) {
39 error("STREAM [send]: cannot enable sending thread - information is missing.");
@@ -65,7 +66,7 @@ static unsigned int remote_clock_resync_iterations = 60;
66 static inline int need_to_send_chart_definition(RRDSET *st) {
67 RRDDIM *rd;
68 rrddim_foreach_read(rd, st)
68 - if(!rrddim_flag_check(rd, RRDDIM_FLAG_EXPOSED))
69 + if(!rd->exposed)
70 return 1;
71
72 return 0;
@@ -96,7 +97,7 @@ static inline void send_chart_definition(RRDSET *st) {
97 , rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)?"hidden":""
98 , rrddim_flag_check(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)?"noreset":""
99 );
99 - rrddim_flag_set(rd, RRDDIM_FLAG_EXPOSED);
100 + rd->exposed = 1;
101 }
102 }
103
@@ -106,7 +107,7 @@ static inline void send_chart_metrics(RRDSET *st) {
107
108 RRDDIM *rd;
109 rrddim_foreach_read(rd, st) {
109 - if(rrddim_flag_check(rd, RRDDIM_FLAG_UPDATED) && rrddim_flag_check(rd, RRDDIM_FLAG_EXPOSED))
110 + if(rd->updated && rd->exposed)
111 buffer_sprintf(st->rrdhost->rrdpush_buffer, "SET %s = " COLLECTED_NUMBER_FORMAT "\n"
112 , rd->id
113 , rd->collected_value
@@ -174,7 +175,7 @@ static void rrdpush_sender_thread_reset_all_charts(RRDHOST *host) {
175
176 RRDDIM *rd;
177 rrddim_foreach_read(rd, st)
177 - rrddim_flag_clear(rd, RRDDIM_FLAG_EXPOSED);
178 + rd->exposed = 0;
179
180 rrdset_unlock(st);
181 }
@@ -548,7 +549,7 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m
549 }
550
551 rrdhost_wrlock(host);
551 - host->use_counter++;
552 + host->connected_senders++;
553 if(health_enabled != CONFIG_BOOLEAN_NO)
554 host->health_delay_up_to = now_realtime_sec() + alarms_delay;
555 rrdhost_unlock(host);
@@ -559,11 +560,13 @@ int rrdpush_receive(int fd, const char *key, const char *hostname, const char *m
560 error("STREAM %s [receive from [%s]:%s]: disconnected (completed updates %zu).", host->hostname, client_ip, client_port, count);
561
562 rrdhost_wrlock(host);
562 - host->use_counter--;
563 - if(!host->use_counter) {
563 + host->connected_senders--;
564 + if(!host->connected_senders) {
565 if(health_enabled == CONFIG_BOOLEAN_AUTO)
566 host->health_enabled = 0;
567
568 + host->senders_disconnected_time = now_realtime_sec();
569 +
570 rrdpush_sender_thread_stop(host);
571 }
572 rrdhost_unlock(host);
src/rrdset.c
+8 -8
@@ -187,7 +187,7 @@ void rrdset_reset(RRDSET *st) {
187 rrddim_foreach_read(rd, st) {
188 rd->last_collected_time.tv_sec = 0;
189 rd->last_collected_time.tv_usec = 0;
190 - rd->counter = 0;
190 + rd->collections_counter = 0;
191 memset(rd->values, 0, rd->entries * sizeof(storage_number));
192 }
193 }
@@ -767,7 +767,7 @@ void rrdset_done(RRDSET *st) {
767 st->collected_total = 0;
768 rrddim_foreach_read(rd, st) {
769 dimensions++;
770 - if(likely(rrddim_flag_check(rd, RRDDIM_FLAG_UPDATED)))
770 + if(likely(rd->updated))
771 st->collected_total += rd->collected_value;
772 }
773
@@ -778,7 +778,7 @@ void rrdset_done(RRDSET *st) {
778 // at this stage we do not interpolate anything
779 rrddim_foreach_read(rd, st) {
780
781 - if(unlikely(!rrddim_flag_check(rd, RRDDIM_FLAG_UPDATED))) {
781 + if(unlikely(!rd->updated)) {
782 rd->calculated_value = 0;
783 continue;
784 }
@@ -840,7 +840,7 @@ void rrdset_done(RRDSET *st) {
840 break;
841
842 case RRD_ALGORITHM_INCREMENTAL:
843 - if(unlikely(rd->counter <= 1)) {
843 + if(unlikely(rd->collections_counter <= 1)) {
844 rd->calculated_value = 0;
845 continue;
846 }
@@ -880,7 +880,7 @@ void rrdset_done(RRDSET *st) {
880 break;
881
882 case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
883 - if(unlikely(rd->counter <= 1)) {
883 + if(unlikely(rd->collections_counter <= 1)) {
884 rd->calculated_value = 0;
885 continue;
886 }
@@ -1064,7 +1064,7 @@ void rrdset_done(RRDSET *st) {
1064 continue;
1065 }
1066
1067 - if(likely(rrddim_flag_check(rd, RRDDIM_FLAG_UPDATED) && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
1067 + if(likely(rd->updated && rd->collections_counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
1068 rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
1069 rd->last_stored_value = new_value;
1070
@@ -1127,7 +1127,7 @@ void rrdset_done(RRDSET *st) {
1127 st->last_collected_total = st->collected_total;
1128
1129 rrddim_foreach_read(rd, st) {
1130 - if(unlikely(!rrddim_flag_check(rd, RRDDIM_FLAG_UPDATED)))
1130 + if(unlikely(!rd->updated))
1131 continue;
1132
1133 if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
@@ -1159,7 +1159,7 @@ void rrdset_done(RRDSET *st) {
1159
1160 rd->calculated_value = 0;
1161 rd->collected_value = 0;
1162 - rrddim_flag_clear(rd, RRDDIM_FLAG_UPDATED);
1162 + rd->updated = 0;
1163
1164 if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
1165 debug(D_RRD_STATS, "%s/%s: END "
src/web_client.c
+12
@@ -302,6 +302,7 @@ int mysendfile(struct web_client *w, char *filename) {
302 for(s = filename; *s ;s++) {
303 if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
304 debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
305 + w->response.data->contenttype = CT_TEXT_HTML;
306 buffer_sprintf(w->response.data, "Filename contains invalid characters: ");
307 buffer_strcat_htmlescape(w->response.data, filename);
308 return 400;
@@ -311,6 +312,7 @@ int mysendfile(struct web_client *w, char *filename) {
312 // if the filename contains a .. refuse to serve it
313 if(strstr(filename, "..") != 0) {
314 debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
315 + w->response.data->contenttype = CT_TEXT_HTML;
316 buffer_strcat(w->response.data, "Relative filenames are not supported: ");
317 buffer_strcat_htmlescape(w->response.data, filename);
318 return 400;
@@ -324,6 +326,7 @@ int mysendfile(struct web_client *w, char *filename) {
326 struct stat stat;
327 if(lstat(webfilename, &stat) != 0) {
328 debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
329 + w->response.data->contenttype = CT_TEXT_HTML;
330 buffer_strcat(w->response.data, "File does not exist, or is not accessible: ");
331 buffer_strcat_htmlescape(w->response.data, webfilename);
332 return 404;
@@ -332,6 +335,7 @@ int mysendfile(struct web_client *w, char *filename) {
335 // check if the file is owned by expected user
336 if(stat.st_uid != web_files_uid()) {
337 error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
338 + w->response.data->contenttype = CT_TEXT_HTML;
339 buffer_strcat(w->response.data, "Access to file is not permitted: ");
340 buffer_strcat_htmlescape(w->response.data, webfilename);
341 return 403;
@@ -340,6 +344,7 @@ int mysendfile(struct web_client *w, char *filename) {
344 // check if the file is owned by expected group
345 if(stat.st_gid != web_files_gid()) {
346 error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
347 + w->response.data->contenttype = CT_TEXT_HTML;
348 buffer_strcat(w->response.data, "Access to file is not permitted: ");
349 buffer_strcat_htmlescape(w->response.data, webfilename);
350 return 403;
@@ -352,6 +357,7 @@ int mysendfile(struct web_client *w, char *filename) {
357
358 if((stat.st_mode & S_IFMT) != S_IFREG) {
359 error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
360 + w->response.data->contenttype = CT_TEXT_HTML;
361 buffer_strcat(w->response.data, "Access to file is not permitted: ");
362 buffer_strcat_htmlescape(w->response.data, webfilename);
363 return 403;
@@ -364,6 +370,7 @@ int mysendfile(struct web_client *w, char *filename) {
370
371 if(errno == EBUSY || errno == EAGAIN) {
372 error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
373 + w->response.data->contenttype = CT_TEXT_HTML;
374 buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
375 buffer_strcat(w->response.data, "File is currently busy, please try again later: ");
376 buffer_strcat_htmlescape(w->response.data, webfilename);
@@ -371,6 +378,7 @@ int mysendfile(struct web_client *w, char *filename) {
378 }
379 else {
380 error("%llu: Cannot open file '%s'.", w->id, webfilename);
381 + w->response.data->contenttype = CT_TEXT_HTML;
382 buffer_strcat(w->response.data, "Cannot open file: ");
383 buffer_strcat_htmlescape(w->response.data, webfilename);
384 return 404;
@@ -575,6 +583,7 @@ int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
583 return web_client_api_request_v1(host, w, url);
584 else {
585 buffer_flush(w->response.data);
586 + w->response.data->contenttype = CT_TEXT_HTML;
587 buffer_strcat(w->response.data, "Unsupported API version: ");
588 buffer_strcat_htmlescape(w->response.data, tok);
589 return 404;
@@ -1041,6 +1050,7 @@ static inline int web_client_switch_host(RRDHOST *host, struct web_client *w, ch
1050 }
1051
1052 buffer_flush(w->response.data);
1053 + w->response.data->contenttype = CT_TEXT_HTML;
1054 buffer_strcat(w->response.data, "This netdata does not maintain a database for host: ");
1055 buffer_strcat_htmlescape(w->response.data, tok?tok:"");
1056 return 404;
@@ -1143,6 +1153,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1153 RRDSET *st = rrdset_find_byname(host, tok);
1154 if(!st) st = rrdset_find(host, tok);
1155 if(!st) {
1156 + w->response.data->contenttype = CT_TEXT_HTML;
1157 buffer_strcat(w->response.data, "Chart is not found: ");
1158 buffer_strcat_htmlescape(w->response.data, tok);
1159 debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
@@ -1156,6 +1167,7 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1167 else
1168 rrdset_flag_set(st, RRDSET_FLAG_DEBUG);
1169
1170 + w->response.data->contenttype = CT_TEXT_HTML;
1171 buffer_sprintf(w->response.data, "Chart has now debug %s: ", rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");
1172 buffer_strcat_htmlescape(w->response.data, tok);
1173 debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?"enabled":"disabled");