@cryptotaxi247 / netdata / commits / 430cd3b67

Avoid static initialization (#20722)

* Initialize and cleanup mutexes and rwlocks dynamically across the codebase * Initialize pthread rwlock and condition variables dynamically in stress test / benchmark * Remove outdated and unused web API test files (valid_urls.c and web_api.c) Fix compilation errors

Stelios Fragkakis committed Jul 24, 2025 at 07:37 UTC 430cd3b67b7a5de664c446b4f58f1debfd26c151
20 files changed +158 -1295
src/collectors/apps.plugin/apps_plugin.c
+9 -1
@@ -654,7 +654,15 @@ static inline int check_capabilities() {
654 #endif
655 #endif
656
657 -netdata_mutex_t apps_and_stdout_mutex = NETDATA_MUTEX_INITIALIZER;
657 +netdata_mutex_t apps_and_stdout_mutex;
658 +
659 +static void __attribute__((constructor)) init_mutex(void) {
660 + netdata_mutex_init(&apps_and_stdout_mutex);
661 +}
662 +
663 +static void __attribute__((destructor)) destroy_mutex(void) {
664 + netdata_mutex_destroy(&apps_and_stdout_mutex);
665 +}
666
667 static bool apps_plugin_exit = false;
668
src/collectors/debugfs.plugin/debugfs_plugin.c
+10 -1
@@ -7,7 +7,16 @@ static char *user_config_dir = CONFIG_DIR;
7 static char *stock_config_dir = LIBCONFIG_DIR;
8
9 static int update_every = 1;
10 -netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
10 +
11 +netdata_mutex_t stdout_mutex;
12 +
13 +static void __attribute__((constructor)) init_mutex(void) {
14 + netdata_mutex_init(&stdout_mutex);
15 +}
16 +
17 +static void __attribute__((destructor)) destroy_mutex(void) {
18 + netdata_mutex_destroy(&stdout_mutex);
19 +}
20
21 static struct debugfs_module {
22 const char *name;
src/collectors/freeipmi.plugin/freeipmi_plugin.c
+10 -1
@@ -84,7 +84,16 @@ static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stt,
84
85 /* Communication Configuration - Initialize accordingly */
86
87 -static netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
87 +static netdata_mutex_t stdout_mutex;
88 +
89 +static void __attribute__((constructor)) init_mutex(void) {
90 + netdata_mutex_init(&stdout_mutex);
91 +}
92 +
93 +static void __attribute__((destructor)) destroy_mutex(void) {
94 + netdata_mutex_destroy(&stdout_mutex);
95 +}
96 +
97 static bool function_plugin_should_exit = false;
98
99 int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency
src/collectors/network-viewer.plugin/network-viewer.c
+9 -1
@@ -33,7 +33,15 @@ static SPAWN_SERVER *spawn_srv = NULL;
33 #define SIMPLE_HASHTABLE_NAME _AGGREGATED_SOCKETS
34 #include "libnetdata/simple_hashtable/simple_hashtable.h"
35
36 -netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
36 +netdata_mutex_t stdout_mutex;
37 +
38 +static void __attribute__((constructor)) init_mutex(void) {
39 + netdata_mutex_init(&stdout_mutex);
40 +}
41 +
42 +static void __attribute__((destructor)) destroy_mutex(void) {
43 + netdata_mutex_destroy(&stdout_mutex);
44 +}
45 static bool plugin_should_exit = false;
46 static SERVICENAMES_CACHE *sc;
47
src/collectors/proc.plugin/proc_diskstats.c
+9 -1
@@ -22,7 +22,15 @@
22 // always 512 on Linux (https://github.com/torvalds/linux/blob/daa121128a2d2ac6006159e2c47676e4fcd21eab/include/linux/blk_types.h#L25-L34)
23 #define SECTOR_SIZE 512
24
25 -static netdata_mutex_t diskstats_dev_mutex = NETDATA_MUTEX_INITIALIZER;
25 +static netdata_mutex_t diskstats_dev_mutex;
26 +
27 +static void __attribute__((constructor)) init_mutex(void) {
28 + netdata_mutex_init(&diskstats_dev_mutex);
29 +}
30 +
31 +static void __attribute__((destructor)) destroy_mutex(void) {
32 + netdata_mutex_destroy(&diskstats_dev_mutex);
33 +}
34
35 static struct disk {
36 char *disk; // the name of the disk (sda, sdb, etc, after being looked up)
src/collectors/proc.plugin/proc_net_dev.c
+9 -1
@@ -379,7 +379,15 @@ static void netdev_free(struct netdev *d) {
379 freez((void *)d);
380 }
381
382 -static netdata_mutex_t netdev_mutex = NETDATA_MUTEX_INITIALIZER;
382 +static netdata_mutex_t netdev_mutex;
383 +
384 +static void __attribute__((constructor)) init_mutex(void) {
385 + netdata_mutex_init(&netdev_mutex);
386 +}
387 +
388 +static void __attribute__((destructor)) destroy_mutex(void) {
389 + netdata_mutex_destroy(&netdev_mutex);
390 +}
391
392 // ----------------------------------------------------------------------------
393
src/collectors/systemd-journal.plugin/systemd-main.c
+10 -1
@@ -5,7 +5,16 @@
5
6 #define ND_SD_JOURNAL_WORKER_THREADS 5
7
8 -netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
8 +netdata_mutex_t stdout_mutex;
9 +
10 +static void __attribute__((constructor)) init_mutex(void) {
11 + netdata_mutex_init(&stdout_mutex);
12 +}
13 +
14 +static void __attribute__((destructor)) destroy_mutex(void) {
15 + netdata_mutex_destroy(&stdout_mutex);
16 +}
17 +
18 static bool plugin_should_exit = false;
19
20 static bool journal_data_directories_exist()
src/collectors/systemd-units.plugin/plugin_systemd_units.c
+9 -1
@@ -16,7 +16,15 @@
16 #define ND_SD_UNITS_MAX_PARAMS 10
17 #define ND_SD_UNITS_DBUS_TYPES "(ssssssouso)"
18
19 -netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
19 +netdata_mutex_t stdout_mutex;
20 +
21 +static void __attribute__((constructor)) init_mutex(void) {
22 + netdata_mutex_init(&stdout_mutex);
23 +}
24 +
25 +static void __attribute__((destructor)) destroy_mutex(void) {
26 + netdata_mutex_destroy(&stdout_mutex);
27 +}
28
29 // ----------------------------------------------------------------------------
30 // copied from systemd: string-table.h
src/collectors/windows-events.plugin/windows-events.c
+10 -1
@@ -5,7 +5,16 @@
5
6 #include "windows-events.h"
7
8 -netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
8 +netdata_mutex_t stdout_mutex;
9 +
10 +static void __attribute__((constructor)) init_mutex(void) {
11 + netdata_mutex_init(&stdout_mutex);
12 +}
13 +
14 +static void __attribute__((destructor)) destroy_mutex(void) {
15 + netdata_mutex_destroy(&stdout_mutex);
16 +}
17 +
18 static bool plugin_should_exit = false;
19
20 #define WEVT_ALWAYS_VISIBLE_KEYS NULL
src/database/engine/rrdengine.c
+10 -1
@@ -773,6 +773,15 @@ struct rrdengine_datafile *get_last_ctx_datafile(struct rrdengine_instance *ctx,
773 return get_ctx_datafile_first_or_last(ctx, false, with_lock);
774 }
775
776 +static netdata_mutex_t mutex;
777 +
778 +static void __attribute__((constructor)) init_mutex(void) {
779 + netdata_mutex_init(&mutex);
780 +}
781 +
782 +static void __attribute__((destructor)) destroy_mutex(void) {
783 + netdata_mutex_destroy(&mutex);
784 +}
785
786 static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_instance *ctx) {
787 struct rrdengine_datafile *datafile;
@@ -792,7 +801,7 @@ static struct rrdengine_datafile *get_datafile_to_write_extent(struct rrdengine_
801 struct rrdengine_datafile *old_datafile = datafile;
802
803 // only 1 datafile creation at a time
795 - static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER;
804 +
805 netdata_mutex_lock(&mutex);
806
807 // take the latest datafile again - without this, multiple threads may create multiple files
src/database/ram/rrddim_mem.c
+9 -1
@@ -4,7 +4,15 @@
4 #include "Judy.h"
5
6 static Pvoid_t rrddim_Judy_array = NULL;
7 -static netdata_rwlock_t rrddim_Judy_rwlock = NETDATA_RWLOCK_INITIALIZER;
7 +static netdata_rwlock_t rrddim_Judy_rwlock;
8 +
9 +static void __attribute__((constructor)) init_lock(void) {
10 + netdata_rwlock_init(&rrddim_Judy_rwlock);
11 +}
12 +
13 +static void __attribute__((destructor)) destroy_lock(void) {
14 + netdata_rwlock_destroy(&rrddim_Judy_rwlock);
15 +}
16
17 // ----------------------------------------------------------------------------
18 // metrics groups
src/database/rrdhost.c
+9 -1
@@ -7,7 +7,15 @@
7 #endif
8
9 RRDHOST *localhost = NULL;
10 -netdata_rwlock_t rrd_rwlock = NETDATA_RWLOCK_INITIALIZER;
10 +netdata_rwlock_t rrd_rwlock;
11 +
12 +static void __attribute__((constructor)) init_lock(void) {
13 + netdata_rwlock_init(&rrd_rwlock);
14 +}
15 +
16 +static void __attribute__((destructor)) destroy_lock(void) {
17 + netdata_rwlock_destroy(&rrd_rwlock);
18 +}
19
20 RRDHOST *rrdhost_find_by_node_id(const char *node_id) {
21
src/exporting/prometheus/prometheus.c
+8 -1
@@ -88,8 +88,15 @@ static struct prometheus_server {
88 struct prometheus_server *next;
89 } *prometheus_server_root = NULL;
90
91 -static netdata_mutex_t prometheus_server_root_mutex = NETDATA_MUTEX_INITIALIZER;
91 +static netdata_mutex_t prometheus_server_root_mutex;
92
93 +static void __attribute__((constructor)) init_mutex(void) {
94 + netdata_mutex_init(&prometheus_server_root_mutex);
95 +}
96 +
97 +static void __attribute__((destructor)) destroy_mutex(void) {
98 + netdata_mutex_destroy(&prometheus_server_root_mutex);
99 +}
100 /**
101 * Clean server root local structure
102 */
src/libnetdata/dictionary/dictionary.c
+9 -1
@@ -311,9 +311,17 @@ static bool dictionary_free_all_resources(DICTIONARY *dict, size_t *mem, bool fo
311 return true;
312 }
313
314 -netdata_mutex_t dictionaries_waiting_to_be_destroyed_mutex = NETDATA_MUTEX_INITIALIZER;
314 +netdata_mutex_t dictionaries_waiting_to_be_destroyed_mutex;
315 static DICTIONARY *dictionaries_waiting_to_be_destroyed = NULL;
316
317 +static void __attribute__((constructor)) init_mutex(void) {
318 + netdata_mutex_init(&dictionaries_waiting_to_be_destroyed_mutex);
319 +}
320 +
321 +static void __attribute__((destructor)) destroy_mutex(void) {
322 + netdata_mutex_destroy(&dictionaries_waiting_to_be_destroyed_mutex);
323 +}
324 +
325 #ifdef FSANITIZE_ADDRESS
326 DEFINE_JUDYL_TYPED(STACKTRACE, size_t);
327 #endif
src/libnetdata/locks/benchmark-rw.c
+8 -5
@@ -291,7 +291,9 @@ static void run_test(const char *name, int readers, int writers,
291 }
292
293 int rwlocks_stress_test(void) {
294 - pthread_rwlock_t pthread_rwlock = PTHREAD_RWLOCK_INITIALIZER;
294 + pthread_rwlock_t pthread_rwlock;
295 + pthread_rwlock_init(&pthread_rwlock, NULL);
296 +
297 RW_SPINLOCK rw_spinlock = RW_SPINLOCK_INITIALIZER;
298 summary_stats_t summary = {0};
299
@@ -301,12 +303,13 @@ int rwlocks_stress_test(void) {
303
304 // Initialize per-thread controls for both locks
305 for(int i = 0; i < MAX_THREADS; i++) {
304 - pthread_control.thread_controls[i].cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
305 - pthread_control.thread_controls[i].cond_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
306 + pthread_cond_init(&pthread_control.thread_controls[i].cond, NULL);
307 + pthread_mutex_init(&pthread_control.thread_controls[i].cond_mutex,NULL);
308 pthread_control.thread_controls[i].run_flag = 0;
309
308 - spinlock_control.thread_controls[i].cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
309 - spinlock_control.thread_controls[i].cond_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
310 + pthread_cond_init(&spinlock_control.thread_controls[i].cond, NULL);
311 + pthread_mutex_init(&spinlock_control.thread_controls[i].cond_mutex,NULL);
312 +
313 spinlock_control.thread_controls[i].run_flag = 0;
314 }
315
src/libnetdata/locks/benchmark.c
+11 -8
@@ -20,7 +20,7 @@ typedef struct {
20
21 typedef struct {
22 pthread_cond_t cond; // Individual condition for each thread
23 - pthread_mutex_t cond_mutex; // Individual mutex for each thread
23 + netdata_mutex_t cond_mutex; // Individual mutex for each thread
24 uint64_t run_flag; // Individual run flag for each thread
25 } thread_control_t;
26
@@ -321,8 +321,11 @@ int locks_stress_test(void) {
321 summary_stats_t summary = {0};
322
323 // Initialize actual locks
324 - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
325 - pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
324 + netdata_mutex_t mutex;
325 + netdata_rwlock_t rwlock;
326 + netdata_mutex_init(&mutex);
327 + netdata_rwlock_init(&rwlock);
328 +
329 SPINLOCK spinlock = SPINLOCK_INITIALIZER;
330 RW_SPINLOCK rw_spinlock = RW_SPINLOCK_INITIALIZER;
331 WAITQ waitq = WAITQ_INITIALIZER;
@@ -340,8 +343,8 @@ int locks_stress_test(void) {
343 for(int i = 0; i < NUM_LOCK_TYPES; i++) {
344 // Initialize per-thread condition variables and mutexes
345 for(int j = 0; j < MAX_THREADS; j++) {
343 - controls[i].thread_controls[j].cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
344 - controls[i].thread_controls[j].cond_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
346 + pthread_cond_init(&controls[i].thread_controls[j].cond, NULL);
347 + pthread_mutex_init(&controls[i].thread_controls[j].cond_mutex, NULL);
348 controls[i].thread_controls[j].run_flag = 0;
349 }
350 }
@@ -406,10 +409,10 @@ int locks_stress_test(void) {
409 for(int type = 0; type < NUM_LOCK_TYPES; type++) {
410 for(int i = 0; i < MAX_THREADS; i++) {
411 thread_control_t *thread_control = &controls[type].thread_controls[i];
409 - pthread_mutex_lock(&thread_control->cond_mutex);
412 + netdata_mutex_lock(&thread_control->cond_mutex);
413 thread_control->run_flag = STOP_SIGNAL;
414 pthread_cond_signal(&thread_control->cond);
412 - pthread_mutex_unlock(&thread_control->cond_mutex);
415 + netdata_mutex_unlock(&thread_control->cond_mutex);
416 }
417 }
418
@@ -425,7 +428,7 @@ int locks_stress_test(void) {
428 for(int type = 0; type < NUM_LOCK_TYPES; type++) {
429 for(int i = 0; i < MAX_THREADS; i++) {
430 pthread_cond_destroy(&controls[type].thread_controls[i].cond);
428 - pthread_mutex_destroy(&controls[type].thread_controls[i].cond_mutex);
431 + netdata_mutex_destroy(&controls[type].thread_controls[i].cond_mutex);
432 }
433 free(threads[type]);
434 }
src/libnetdata/locks/locks.h
-5
@@ -11,7 +11,6 @@
11 // #endif
12
13 typedef pthread_mutex_t netdata_mutex_t;
14 -#define NETDATA_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
14
15 #ifdef NETDATA_TRACE_RWLOCKS
16
@@ -58,10 +57,6 @@ typedef struct netdata_rwlock_t {
57 pthread_rwlock_t rwlock_t;
58 } netdata_rwlock_t;
59
61 -#define NETDATA_RWLOCK_INITIALIZER { \
62 - .rwlock_t = PTHREAD_RWLOCK_INITIALIZER \
63 - }
64 -
60 #endif // NETDATA_TRACE_RWLOCKS
61
62 int __netdata_mutex_init(netdata_mutex_t *mutex);
src/ml/ml.cc
+9 -1
@@ -18,7 +18,15 @@
18 #define WORKER_TRAIN_FLUSH_MODELS 7
19
20 sqlite3 *ml_db = NULL;
21 -static netdata_mutex_t db_mutex = NETDATA_MUTEX_INITIALIZER;
21 +static netdata_mutex_t db_mutex;
22 +
23 +static void __attribute__((constructor)) init_mutex(void) {
24 + netdata_mutex_init(&db_mutex);
25 +}
26 +
27 +static void __attribute__((destructor)) destroy_mutex(void) {
28 + netdata_mutex_destroy(&db_mutex);
29 +}
30
31 typedef struct {
32 // First/last entry of the dimension in DB when generating the response
src/web/api/tests/valid_urls.c deleted
-789
@@ -1,789 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "libnetdata/libnetdata.h"
4 -#include "libnetdata/required_dummies.h"
5 -#include "database/rrd.h"
6 -#include "web/server/web_client.h"
7 -#include <setjmp.h>
8 -#include <cmocka.h>
9 -#include <stdbool.h>
10 -
11 -void free_temporary_host(RRDHOST *host)
12 -{
13 - (void) host;
14 -}
15 -
16 -void *__wrap_free_temporary_host(RRDHOST *host)
17 -{
18 - (void) host;
19 - return NULL;
20 -}
21 -
22 -void repr(char *result, int result_size, char const *buf, int size)
23 -{
24 - int n;
25 - char *end = result + result_size - 1;
26 - unsigned char const *ubuf = (unsigned char const *)buf;
27 - while (size && result_size > 0) {
28 - if (*ubuf <= 0x20 || *ubuf >= 0x80) {
29 - n = snprintf(result, result_size, "\\%02X", *ubuf);
30 - } else {
31 - *result = *ubuf;
32 - n = 1;
33 - }
34 - result += n;
35 - result_size -= n;
36 - ubuf++;
37 - size--;
38 - }
39 - if (result_size > 0)
40 - *(result++) = 0;
41 - else
42 - *end = 0;
43 -}
44 -
45 -// ---------------------------------- Mocking accesses from web_client ------------------------------------------------
46 -
47 -ssize_t send(int sockfd, const void *buf, size_t len, int flags)
48 -{
49 - netdata_log_info("Mocking send: %zu bytes\n", len);
50 - (void)sockfd;
51 - (void)buf;
52 - (void)flags;
53 - return len;
54 -}
55 -
56 -RRDHOST *__wrap_rrdhost_find_by_hostname(const char *hostname, uint32_t hash)
57 -{
58 - (void)hostname;
59 - (void)hash;
60 - return NULL;
61 -}
62 -
63 -/* Note: we've got some intricate code inside the global statistics module, might be useful to pull it inside the
64 - test set instead of mocking it. */
65 -void __wrap_finished_web_request_statistics(
66 - uint64_t dt, uint64_t bytes_received, uint64_t bytes_sent, uint64_t content_size, uint64_t compressed_content_size)
67 -{
68 - (void)dt;
69 - (void)bytes_received;
70 - (void)bytes_sent;
71 - (void)content_size;
72 - (void)compressed_content_size;
73 -}
74 -
75 -char *__wrap_inicfg_get(&netdata_config, struct config *root, const char *section, const char *name, const char *default_value)
76 -{
77 - (void)root;
78 - (void)section;
79 - (void)name;
80 - (void)default_value;
81 - return "UNKNOWN FIX ME";
82 -}
83 -
84 -int __wrap_web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url)
85 -{
86 - char url_repr[160];
87 - repr(url_repr, sizeof(url_repr), url, strlen(url));
88 - printf("web_client_api_request_v1(url=\"%s\")\n", url_repr);
89 - check_expected_ptr(host);
90 - check_expected_ptr(w);
91 - check_expected_ptr(url_repr);
92 - return HTTP_RESP_OK;
93 -}
94 -
95 -int __wrap_mysendfile(struct web_client *w, char *filename)
96 -{
97 - (void)w;
98 - printf("mysendfile(filename=\"%s\"\n", filename);
99 - check_expected_ptr(filename);
100 - return HTTP_RESP_OK;
101 -}
102 -
103 -int __wrap_rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url)
104 -{
105 - (void)host;
106 - (void)w;
107 - (void)url;
108 - return 0;
109 -}
110 -
111 -RRDHOST *__wrap_rrdhost_find_by_guid(const char *guid, uint32_t hash)
112 -{
113 - (void)guid;
114 - (void)hash;
115 - printf("FIXME: rrdset_find_guid\n");
116 - return NULL;
117 -}
118 -
119 -RRDSET *__wrap_rrdset_find_byname(RRDHOST *host, const char *name)
120 -{
121 - (void)host;
122 - (void)name;
123 - printf("FIXME: rrdset_find_byname\n");
124 - return NULL;
125 -}
126 -
127 -RRDSET *__wrap_rrdset_find(RRDHOST *host, const char *id)
128 -{
129 - (void)host;
130 - (void)id;
131 - printf("FIXME: rrdset_find\n");
132 - return NULL;
133 -}
134 -
135 -// -------------------------------- Mocking the log - dump straight through --------------------------------------------
136 -
137 -void __wrap_debug_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
138 -{
139 - (void)file;
140 - (void)function;
141 - (void)line;
142 - va_list args;
143 - va_start(args, fmt);
144 - printf(" DEBUG: ");
145 - printf(fmt, args);
146 - printf("\n");
147 - va_end(args);
148 -}
149 -
150 -void __wrap_info_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
151 -{
152 - (void)file;
153 - (void)function;
154 - (void)line;
155 - va_list args;
156 - va_start(args, fmt);
157 - printf(" INFO: ");
158 - printf(fmt, args);
159 - printf("\n");
160 - va_end(args);
161 -}
162 -
163 -void __wrap_error_int(
164 - const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ...)
165 -{
166 - (void)prefix;
167 - (void)file;
168 - (void)function;
169 - (void)line;
170 - va_list args;
171 - va_start(args, fmt);
172 - printf(" ERROR: ");
173 - printf(fmt, args);
174 - printf("\n");
175 - va_end(args);
176 -}
177 -
178 -void __wrap_fatal_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
179 -{
180 - (void)file;
181 - (void)function;
182 - (void)line;
183 - va_list args;
184 - va_start(args, fmt);
185 - printf("FATAL: ");
186 - printf(fmt, args);
187 - printf("\n");
188 - va_end(args);
189 - fail();
190 -}
191 -
192 -WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_STATIC_THREADED;
193 -char *netdata_configured_web_dir = "UNKNOWN FIXME";
194 -RRDHOST *localhost = NULL;
195 -
196 -struct config netdata_config = { .first_section = NULL,
197 - .last_section = NULL,
198 - .mutex = NETDATA_MUTEX_INITIALIZER,
199 - .index = { .avl_tree = { .root = NULL, .compar = inicfg_section_compare },
200 - .rwlock = AVL_LOCK_INITIALIZER } };
201 -
202 -/* Note: this is not a CMocka group_test_setup/teardown pair. This is performed per-test.
203 -*/
204 -static struct web_client *setup_fresh_web_client()
205 -{
206 - struct web_client *w = (struct web_client *)malloc(sizeof(struct web_client));
207 - memset(w, 0, sizeof(struct web_client));
208 - w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
209 - w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
210 - w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
211 - strcpy(w->origin, "*"); // Simulate web_client_create_on_fd()
212 - w->cookie1[0] = 0; // Simulate web_client_create_on_fd()
213 - w->cookie2[0] = 0; // Simulate web_client_create_on_fd()
214 - w->acl = 0x1f; // Everything on
215 - return w;
216 -}
217 -
218 -static void destroy_web_client(struct web_client *w)
219 -{
220 - buffer_free(w->response.data);
221 - buffer_free(w->response.header);
222 - buffer_free(w->response.header_output);
223 - free(w);
224 -}
225 -
226 -//////////////////////////// Test cases ///////////////////////////////////////////////////////////////////////////////
227 -
228 -static void only_root(void **state)
229 -{
230 - (void)state;
231 -
232 - if (localhost != NULL)
233 - free(localhost);
234 - localhost = malloc(sizeof(RRDHOST));
235 -
236 - struct web_client *w = setup_fresh_web_client();
237 - buffer_strcat(w->response.data, "GET / HTTP/1.1\r\n\r\n");
238 -
239 - char debug[4096];
240 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
241 - printf("-> \"%s\"\n", debug);
242 -
243 - //char expected_url_repr[4096];
244 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
245 -
246 - expect_string(__wrap_mysendfile, filename, "/");
247 -
248 - web_client_process_request(w);
249 -
250 - //assert_string_equal(w->decoded_query_string, def->query_out);
251 - destroy_web_client(w);
252 - free(localhost);
253 - localhost = NULL;
254 -}
255 -
256 -static void two_slashes(void **state)
257 -{
258 - (void)state;
259 -
260 - if (localhost != NULL)
261 - free(localhost);
262 - localhost = malloc(sizeof(RRDHOST));
263 -
264 - struct web_client *w = setup_fresh_web_client();
265 - buffer_strcat(w->response.data, "GET // HTTP/1.1\r\n\r\n");
266 -
267 - char debug[4096];
268 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
269 - printf("-> \"%s\"\n", debug);
270 -
271 - //char expected_url_repr[4096];
272 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
273 -
274 - expect_string(__wrap_mysendfile, filename, "//");
275 -
276 - web_client_process_request(w);
277 -
278 - //assert_string_equal(w->decoded_query_string, def->query_out);
279 - destroy_web_client(w);
280 - free(localhost);
281 - localhost = NULL;
282 -}
283 -
284 -static void absolute_url(void **state)
285 -{
286 - (void)state;
287 -
288 - if (localhost != NULL)
289 - free(localhost);
290 - localhost = malloc(sizeof(RRDHOST));
291 -
292 - struct web_client *w = setup_fresh_web_client();
293 - buffer_strcat(w->response.data, "GET http://localhost:19999/api/v1/info HTTP/1.1\r\n\r\n");
294 -
295 - char debug[4096];
296 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
297 - printf("-> \"%s\"\n", debug);
298 -
299 - //char expected_url_repr[4096];
300 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
301 -
302 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
303 - expect_value(__wrap_web_client_api_request_v1, w, w);
304 - expect_string(__wrap_web_client_api_request_v1, url_repr, "info");
305 -
306 - web_client_process_request(w);
307 -
308 - assert_string_equal(w->decoded_query_string, "?blah");
309 - destroy_web_client(w);
310 - free(localhost);
311 - localhost = NULL;
312 -}
313 -
314 -static void valid_url(void **state)
315 -{
316 - (void)state;
317 -
318 - if (localhost != NULL)
319 - free(localhost);
320 - localhost = malloc(sizeof(RRDHOST));
321 -
322 - struct web_client *w = setup_fresh_web_client();
323 - buffer_strcat(w->response.data, "GET /api/v1/info?blah HTTP/1.1\r\n\r\n");
324 -
325 - char debug[4096];
326 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
327 - printf("-> \"%s\"\n", debug);
328 -
329 - //char expected_url_repr[4096];
330 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
331 -
332 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
333 - expect_value(__wrap_web_client_api_request_v1, w, w);
334 - expect_string(__wrap_web_client_api_request_v1, url_repr, "info");
335 -
336 - web_client_process_request(w);
337 -
338 - assert_string_equal(w->decoded_query_string, "?blah");
339 - destroy_web_client(w);
340 - free(localhost);
341 - localhost = NULL;
342 -}
343 -
344 -/* RFC2616, section 4.1:
345 -
346 - In the interest of robustness, servers SHOULD ignore any empty
347 - line(s) received where a Request-Line is expected. In other words, if
348 - the server is reading the protocol stream at the beginning of a
349 - message and receives a CRLF first, it should ignore the CRLF.
350 -*/
351 -static void leading_blanks(void **state)
352 -{
353 - (void)state;
354 -
355 - if (localhost != NULL)
356 - free(localhost);
357 - localhost = malloc(sizeof(RRDHOST));
358 -
359 - struct web_client *w = setup_fresh_web_client();
360 - buffer_strcat(w->response.data, "\r\n\r\nGET /api/v1/info?blah HTTP/1.1\r\n\r\n");
361 -
362 - char debug[4096];
363 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
364 - printf("-> \"%s\"\n", debug);
365 -
366 - //char expected_url_repr[4096];
367 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
368 -
369 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
370 - expect_value(__wrap_web_client_api_request_v1, w, w);
371 - expect_string(__wrap_web_client_api_request_v1, url_repr, "info");
372 -
373 - web_client_process_request(w);
374 -
375 - assert_string_equal(w->decoded_query_string, "?blah");
376 - destroy_web_client(w);
377 - free(localhost);
378 - localhost = NULL;
379 -}
380 -
381 -static void empty_url(void **state)
382 -{
383 - (void)state;
384 -
385 - if (localhost != NULL)
386 - free(localhost);
387 - localhost = malloc(sizeof(RRDHOST));
388 -
389 - struct web_client *w = setup_fresh_web_client();
390 - buffer_strcat(w->response.data, "GET HTTP/1.1\r\n\r\n");
391 -
392 - char debug[4096];
393 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
394 - printf("-> \"%s\"\n", debug);
395 -
396 - //char expected_url_repr[4096];
397 - //repr(expected_url_repr, sizeof(expected_url_repr), def->url_out_repr, strlen(def->url_out_repr));
398 -
399 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
400 - expect_value(__wrap_web_client_api_request_v1, w, w);
401 - expect_string(__wrap_web_client_api_request_v1, url_repr, "info");
402 -
403 - web_client_process_request(w);
404 -
405 - assert_string_equal(w->decoded_query_string, "?blah");
406 - destroy_web_client(w);
407 - free(localhost);
408 - localhost = NULL;
409 -}
410 -
411 -/* If the %-escape is being performed at the correct time then the url should not be treated as a query, but instead
412 - as a path "/api/v1/info?blah?" which should dispatch into the API with the given values.
413 -*/
414 -static void not_a_query(void **state)
415 -{
416 - (void)state;
417 - localhost = malloc(sizeof(RRDHOST));
418 -
419 - struct web_client *w = setup_fresh_web_client();
420 - buffer_strcat(w->response.data, "GET /api/v1/info%3fblah%3f HTTP/1.1\r\n\r\n");
421 -
422 - char debug[160];
423 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
424 - printf("->%s\n", debug);
425 -
426 - char expected_url_repr[160];
427 - repr(expected_url_repr, sizeof(expected_url_repr), "info?blah?", 10);
428 -
429 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
430 - expect_value(__wrap_web_client_api_request_v1, w, w);
431 - expect_string(__wrap_web_client_api_request_v1, url_repr, expected_url_repr);
432 -
433 - web_client_process_request(w);
434 -
435 - assert_string_equal(w->decoded_query_string, "");
436 - destroy_web_client(w);
437 - free(localhost);
438 -}
439 -
440 -static void cr_in_url(void **state)
441 -{
442 - (void)state;
443 - localhost = malloc(sizeof(RRDHOST));
444 -
445 - struct web_client *w = setup_fresh_web_client();
446 - buffer_strcat(w->response.data, "GET /api/v1/inf\ro\t?blah HTTP/1.1\r\n\r\n");
447 -
448 - char debug[160];
449 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
450 - printf("->%s\n", debug);
451 -
452 - char expected_url_repr[160];
453 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
454 -
455 - web_client_process_request(w);
456 -
457 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
458 -
459 - destroy_web_client(w);
460 - free(localhost);
461 -}
462 -static void newline_in_url(void **state)
463 -{
464 - (void)state;
465 - localhost = malloc(sizeof(RRDHOST));
466 -
467 - struct web_client *w = setup_fresh_web_client();
468 - buffer_strcat(w->response.data, "GET /api/v1/inf\no\t?blah HTTP/1.1\r\n\r\n");
469 -
470 - char debug[160];
471 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
472 - printf("->%s\n", debug);
473 -
474 - char expected_url_repr[160];
475 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
476 -
477 - web_client_process_request(w);
478 -
479 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
480 -
481 - destroy_web_client(w);
482 - free(localhost);
483 -}
484 -
485 -static void bad_version(void **state)
486 -{
487 - (void)state;
488 - localhost = malloc(sizeof(RRDHOST));
489 -
490 - struct web_client *w = setup_fresh_web_client();
491 - buffer_strcat(w->response.data, "GET /api/v1/info?blah HTTP/1.2\r\n\r\n");
492 -
493 - char debug[160];
494 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
495 - printf("->%s\n", debug);
496 -
497 - char expected_url_repr[160];
498 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
499 -
500 - web_client_process_request(w);
501 -
502 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
503 -
504 - destroy_web_client(w);
505 - free(localhost);
506 -}
507 -
508 -static void pathless_query(void **state)
509 -{
510 - (void)state;
511 - localhost = malloc(sizeof(RRDHOST));
512 -
513 - struct web_client *w = setup_fresh_web_client();
514 - buffer_strcat(w->response.data, "GET ?blah HTTP/1.1\r\n\r\n");
515 -
516 - char debug[160];
517 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
518 - printf("->%s\n", debug);
519 -
520 - char expected_url_repr[160];
521 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
522 -
523 - web_client_process_request(w);
524 -
525 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
526 -
527 - destroy_web_client(w);
528 - free(localhost);
529 -}
530 -
531 -static void pathless_fragment(void **state)
532 -{
533 - (void)state;
534 - localhost = malloc(sizeof(RRDHOST));
535 -
536 - struct web_client *w = setup_fresh_web_client();
537 - buffer_strcat(w->response.data, "GET #blah HTTP/1.1\r\n\r\n");
538 -
539 - char debug[160];
540 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
541 - printf("->%s\n", debug);
542 -
543 - char expected_url_repr[160];
544 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
545 -
546 - web_client_process_request(w);
547 -
548 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
549 -
550 - destroy_web_client(w);
551 - free(localhost);
552 -}
553 -
554 -static void short_percent(void **state)
555 -{
556 - (void)state;
557 - localhost = malloc(sizeof(RRDHOST));
558 -
559 - struct web_client *w = setup_fresh_web_client();
560 - buffer_strcat(w->response.data, "GET % HTTP/1.1\r\n\r\n");
561 -
562 - char debug[160];
563 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
564 - printf("->%s\n", debug);
565 -
566 - char expected_url_repr[160];
567 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
568 -
569 - web_client_process_request(w);
570 -
571 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
572 -
573 - destroy_web_client(w);
574 - free(localhost);
575 -}
576 -
577 -static void short_percent2(void **state)
578 -{
579 - (void)state;
580 - localhost = malloc(sizeof(RRDHOST));
581 -
582 - struct web_client *w = setup_fresh_web_client();
583 - buffer_strcat(w->response.data, "GET %0 HTTP/1.1\r\n\r\n");
584 -
585 - char debug[160];
586 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
587 - printf("->%s\n", debug);
588 -
589 - char expected_url_repr[160];
590 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
591 -
592 - web_client_process_request(w);
593 -
594 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
595 -
596 - destroy_web_client(w);
597 - free(localhost);
598 -}
599 -
600 -static void short_percent3(void **state)
601 -{
602 - (void)state;
603 - localhost = malloc(sizeof(RRDHOST));
604 -
605 - struct web_client *w = setup_fresh_web_client();
606 - buffer_strcat(w->response.data, "GET %");
607 -
608 - char debug[160];
609 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
610 - printf("->%s\n", debug);
611 -
612 - char expected_url_repr[160];
613 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
614 -
615 - web_client_process_request(w);
616 -
617 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
618 -
619 - destroy_web_client(w);
620 - free(localhost);
621 -}
622 -
623 -static void percent_nulls(void **state)
624 -{
625 - (void)state;
626 - localhost = malloc(sizeof(RRDHOST));
627 -
628 - struct web_client *w = setup_fresh_web_client();
629 - buffer_strcat(w->response.data, "GET %00%00%00%00%00%00 HTTP/1.1\r\n");
630 -
631 - char debug[160];
632 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
633 - printf("->%s\n", debug);
634 -
635 - char expected_url_repr[160];
636 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
637 -
638 - web_client_process_request(w);
639 -
640 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
641 -
642 - destroy_web_client(w);
643 - free(localhost);
644 -}
645 -
646 -static void percent_invalid(void **state)
647 -{
648 - (void)state;
649 - localhost = malloc(sizeof(RRDHOST));
650 -
651 - struct web_client *w = setup_fresh_web_client();
652 - buffer_strcat(w->response.data, "GET /%x%x%x%x%x%x HTTP/1.1\r\n");
653 -
654 - char debug[160];
655 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
656 - printf("->%s\n", debug);
657 -
658 - char expected_url_repr[160];
659 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
660 -
661 - web_client_process_request(w);
662 -
663 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
664 -
665 - destroy_web_client(w);
666 - free(localhost);
667 -}
668 -
669 -static void space_in_url(void **state)
670 -{
671 - (void)state;
672 - localhost = malloc(sizeof(RRDHOST));
673 -
674 - struct web_client *w = setup_fresh_web_client();
675 - buffer_strcat(w->response.data, "GET / / HTTP/1.1\r\n\r\n");
676 -
677 - char debug[160];
678 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
679 - printf("->%s\n", debug);
680 -
681 - char expected_url_repr[160];
682 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
683 -
684 - web_client_process_request(w);
685 -
686 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
687 -
688 - destroy_web_client(w);
689 - free(localhost);
690 -}
691 -
692 -static void random_sploit1(void **state)
693 -{
694 - (void)state;
695 - localhost = malloc(sizeof(RRDHOST));
696 -
697 - struct web_client *w = setup_fresh_web_client();
698 - // FIXME: Encoding probably needs to go through printf
699 - buffer_need_bytes(w->response.data, 55);
700 - memcpy(
701 - w->response.data->buffer,
702 - "GET \x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr HTTP/1.1\r\n\r\n", 54);
703 - w->response.data->len = 54;
704 -
705 - char debug[160];
706 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
707 - printf("->%s\n", debug);
708 -
709 - char expected_url_repr[160];
710 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
711 -
712 - web_client_process_request(w);
713 -
714 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
715 -
716 - destroy_web_client(w);
717 - free(localhost);
718 -}
719 -
720 -static void null_in_url(void **state)
721 -{
722 - (void)state;
723 - localhost = malloc(sizeof(RRDHOST));
724 -
725 - struct web_client *w = setup_fresh_web_client();
726 - buffer_strcat(w->response.data, "GET / / HTTP/1.1\r\n\r\n");
727 - w->response.data->buffer[5] = 0;
728 -
729 - char debug[160];
730 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
731 - printf("->%s\n", debug);
732 -
733 - char expected_url_repr[160];
734 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
735 -
736 - web_client_process_request(w);
737 -
738 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
739 -
740 - destroy_web_client(w);
741 - free(localhost);
742 -}
743 -static void many_ands(void **state)
744 -{
745 - (void)state;
746 - localhost = malloc(sizeof(RRDHOST));
747 -
748 - struct web_client *w = setup_fresh_web_client();
749 - buffer_strcat(w->response.data, "GET foo?");
750 - for (size_t i = 0; i < 600; i++)
751 - buffer_strcat(w->response.data, "&");
752 - buffer_strcat(w->response.data, " HTTP/1.1\r\n\r\n");
753 -
754 - char debug[2048];
755 - repr(debug, sizeof(debug), w->response.data->buffer, w->response.data->len);
756 - printf("->%s\n", debug);
757 -
758 - char expected_url_repr[160];
759 - repr(expected_url_repr, sizeof(expected_url_repr), "inf\no\t", 6);
760 -
761 - web_client_process_request(w);
762 -
763 - assert_int_equal(w->response.code, HTTP_RESP_BAD_REQUEST);
764 -
765 - destroy_web_client(w);
766 - free(localhost);
767 -}
768 -
769 -int main(void)
770 -{
771 - debug_flags = 0xffffffffffff;
772 - int fails = 0;
773 -
774 - struct CMUnitTest static_tests[] = {
775 - cmocka_unit_test(only_root), cmocka_unit_test(two_slashes), cmocka_unit_test(valid_url),
776 - cmocka_unit_test(leading_blanks), cmocka_unit_test(empty_url), cmocka_unit_test(newline_in_url),
777 - cmocka_unit_test(not_a_query), cmocka_unit_test(cr_in_url), cmocka_unit_test(pathless_query),
778 - cmocka_unit_test(pathless_fragment), cmocka_unit_test(short_percent), cmocka_unit_test(short_percent2),
779 - cmocka_unit_test(short_percent3), cmocka_unit_test(percent_nulls), cmocka_unit_test(percent_invalid),
780 - cmocka_unit_test(space_in_url), cmocka_unit_test(random_sploit1), cmocka_unit_test(null_in_url),
781 - cmocka_unit_test(absolute_url),
782 - // cmocka_unit_test(many_ands), CMocka cannot recover after this crash
783 - cmocka_unit_test(bad_version)
784 - };
785 - (void)many_ands;
786 -
787 - fails += cmocka_run_group_tests_name("static_tests", static_tests, NULL, NULL);
788 - return fails;
789 -}
src/web/api/tests/web_api.c deleted
-473
@@ -1,473 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "libnetdata/libnetdata.h"
4 -#include "libnetdata/required_dummies.h"
5 -#include "database/rrd.h"
6 -#include "web/server/web_client.h"
7 -#include <setjmp.h>
8 -#include <cmocka.h>
9 -#include <stdbool.h>
10 -
11 -void free_temporary_host(RRDHOST *host)
12 -{
13 - (void) host;
14 -}
15 -
16 -void *__wrap_free_temporary_host(RRDHOST *host)
17 -{
18 - (void) host;
19 - return NULL;
20 -}
21 -
22 -void repr(char *result, int result_size, char const *buf, int size)
23 -{
24 - int n;
25 - char *end = result + result_size - 1;
26 - unsigned char const *ubuf = (unsigned char const *)buf;
27 - while (size && result_size > 0) {
28 - if (*ubuf <= 0x20 || *ubuf >= 0x80) {
29 - n = snprintf(result, result_size, "\\%02X", *ubuf);
30 - } else {
31 - *result = *ubuf;
32 - n = 1;
33 - }
34 - result += n;
35 - result_size -= n;
36 - ubuf++;
37 - size--;
38 - }
39 - if (result_size > 0)
40 - *(result++) = 0;
41 - else
42 - *end = 0;
43 -}
44 -
45 -// ---------------------------------- Mocking accesses from web_client ------------------------------------------------
46 -
47 -ssize_t send(int sockfd, const void *buf, size_t len, int flags)
48 -{
49 - netdata_log_info("Mocking send: %zu bytes\n", len);
50 - (void)sockfd;
51 - (void)buf;
52 - (void)flags;
53 - return len;
54 -}
55 -
56 -RRDHOST *__wrap_rrdhost_find_by_hostname(const char *hostname, uint32_t hash)
57 -{
58 - (void)hostname;
59 - (void)hash;
60 - return NULL;
61 -}
62 -
63 -/* Note: we've got some intricate code inside the global statistics module, might be useful to pull it inside the
64 - test set instead of mocking it. */
65 -void __wrap_finished_web_request_statistics(
66 - uint64_t dt, uint64_t bytes_received, uint64_t bytes_sent, uint64_t content_size, uint64_t compressed_content_size)
67 -{
68 - (void)dt;
69 - (void)bytes_received;
70 - (void)bytes_sent;
71 - (void)content_size;
72 - (void)compressed_content_size;
73 -}
74 -
75 -char *__wrap_inicfg_get(&netdata_config, struct config *root, const char *section, const char *name, const char *default_value)
76 -{
77 - (void)root;
78 - (void)section;
79 - (void)name;
80 - (void)default_value;
81 - return "UNKNOWN FIX ME";
82 -}
83 -
84 -int __wrap_web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url)
85 -{
86 - char url_repr[160];
87 - repr(url_repr, sizeof(url_repr), url, strlen(url));
88 - netdata_log_info("web_client_api_request_v1(url=\"%s\")\n", url_repr);
89 - check_expected_ptr(host);
90 - check_expected_ptr(w);
91 - check_expected_ptr(url_repr);
92 - return HTTP_RESP_OK;
93 -}
94 -
95 -int __wrap_rrdpush_receiver_thread_spawn(RRDHOST *host, struct web_client *w, char *url)
96 -{
97 - (void)host;
98 - (void)w;
99 - (void)url;
100 - return 0;
101 -}
102 -
103 -RRDHOST *__wrap_rrdhost_find_by_guid(const char *guid, uint32_t hash)
104 -{
105 - (void)guid;
106 - (void)hash;
107 - printf("FIXME: rrdset_find_guid\n");
108 - return NULL;
109 -}
110 -
111 -RRDSET *__wrap_rrdset_find_byname(RRDHOST *host, const char *name)
112 -{
113 - (void)host;
114 - (void)name;
115 - printf("FIXME: rrdset_find_byname\n");
116 - return NULL;
117 -}
118 -
119 -RRDSET *__wrap_rrdset_find(RRDHOST *host, const char *id)
120 -{
121 - (void)host;
122 - (void)id;
123 - printf("FIXME: rrdset_find\n");
124 - return NULL;
125 -}
126 -
127 -// -------------------------------- Mocking the log - capture per-test ------------------------------------------------
128 -
129 -char log_buffer[10240] = { 0 };
130 -void __wrap_debug_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
131 -{
132 - (void)file;
133 - (void)function;
134 - (void)line;
135 - va_list args;
136 - va_start(args, fmt);
137 - size_t cur = strlen(log_buffer);
138 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, " DEBUG: ");
139 - cur = strlen(log_buffer);
140 - vsnprintf(log_buffer + cur, sizeof(log_buffer) - cur, fmt, args);
141 - cur = strlen(log_buffer);
142 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, "\n");
143 - va_end(args);
144 -}
145 -
146 -void __wrap_info_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
147 -{
148 - (void)file;
149 - (void)function;
150 - (void)line;
151 - va_list args;
152 - va_start(args, fmt);
153 - size_t cur = strlen(log_buffer);
154 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, " INFO: ");
155 - cur = strlen(log_buffer);
156 - vsnprintf(log_buffer + cur, sizeof(log_buffer) - cur, fmt, args);
157 - cur = strlen(log_buffer);
158 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, "\n");
159 - va_end(args);
160 -}
161 -
162 -void __wrap_error_int(
163 - const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ...)
164 -{
165 - (void)prefix;
166 - (void)file;
167 - (void)function;
168 - (void)line;
169 - va_list args;
170 - va_start(args, fmt);
171 - size_t cur = strlen(log_buffer);
172 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, " ERROR: ");
173 - cur = strlen(log_buffer);
174 - vsnprintf(log_buffer + cur, sizeof(log_buffer) - cur, fmt, args);
175 - cur = strlen(log_buffer);
176 - snprintf(log_buffer + cur, sizeof(log_buffer) - cur, "\n");
177 - va_end(args);
178 -}
179 -
180 -void __wrap_fatal_int(const char *file, const char *function, const unsigned long line, const char *fmt, ...)
181 -{
182 - (void)file;
183 - (void)function;
184 - (void)line;
185 - va_list args;
186 - va_start(args, fmt);
187 - printf("FATAL: ");
188 - vprintf(fmt, args);
189 - printf("\n");
190 - va_end(args);
191 - fail();
192 -}
193 -
194 -WEB_SERVER_MODE web_server_mode = WEB_SERVER_MODE_STATIC_THREADED;
195 -char *netdata_configured_web_dir = "UNKNOWN FIXME";
196 -RRDHOST *localhost = NULL;
197 -
198 -struct config netdata_config = { .first_section = NULL,
199 - .last_section = NULL,
200 - .mutex = NETDATA_MUTEX_INITIALIZER,
201 - .index = { .avl_tree = { .root = NULL, .compar = inicfg_section_compare },
202 - .rwlock = AVL_LOCK_INITIALIZER } };
203 -
204 -const char *http_headers[] = { "Host: 254.254.0.1",
205 - "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_" // No ,
206 - "0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36",
207 - "Connection: keep-alive",
208 - "X-Forwarded-For: 1.254.1.251",
209 - "Cookie: _ga=GA1.1.1227576758.1571113676; _gid=GA1.2.1222321739.1573628979",
210 - "X-Requested-With: XMLHttpRequest",
211 - "Accept-Encoding: gzip, deflate",
212 - "Cache-Control: no-cache, no-store" };
213 -#define MAX_HEADERS (sizeof(http_headers) / (sizeof(const char *)))
214 -
215 -static void build_request(struct web_buffer *wb, const char *url, bool use_cr, size_t num_headers)
216 -{
217 - buffer_reset(wb);
218 - buffer_strcat(wb, "GET ");
219 - buffer_strcat(wb, url);
220 - buffer_strcat(wb, " HTTP/1.1");
221 - if (use_cr)
222 - buffer_strcat(wb, "\r");
223 - buffer_strcat(wb, "\n");
224 - for (size_t i = 0; i < num_headers && i < MAX_HEADERS; i++) {
225 - buffer_strcat(wb, http_headers[i]);
226 - if (use_cr)
227 - buffer_strcat(wb, "\r");
228 - buffer_strcat(wb, "\n");
229 - }
230 - if (use_cr)
231 - buffer_strcat(wb, "\r");
232 - buffer_strcat(wb, "\n");
233 -}
234 -
235 -/* Note: this is not a CMocka group_test_setup/teardown pair. This is performed per-test.
236 -*/
237 -static struct web_client *setup_fresh_web_client()
238 -{
239 - struct web_client *w = (struct web_client *)malloc(sizeof(struct web_client));
240 - memset(w, 0, sizeof(struct web_client));
241 - w->response.data = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
242 - w->response.data->date = 0; // Valgrind uninitialised value
243 - w->response.data->expires = 0; // Valgrind uninitialised value
244 - w->response.data->options = 0; // Valgrind uninitialised value
245 - w->response.header = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
246 - w->response.header_output = buffer_create(NETDATA_WEB_RESPONSE_HEADER_SIZE);
247 - strcpy(w->origin, "*"); // Simulate web_client_create_on_fd()
248 - w->cookie1[0] = 0; // Simulate web_client_create_on_fd()
249 - w->cookie2[0] = 0; // Simulate web_client_create_on_fd()
250 - w->acl = 0x1f; // Everything on
251 - return w;
252 -}
253 -
254 -static void destroy_web_client(struct web_client *w)
255 -{
256 - buffer_free(w->response.data);
257 - buffer_free(w->response.header);
258 - buffer_free(w->response.header_output);
259 - free(w);
260 -}
261 -
262 -// ---------------------------------- Parameterized test-families -----------------------------------------------------
263 -// There is no way to pass a parameter block into the setup fixture, we would have to patch CMocka and maintain it
264 -// locally. (The void **current_state in _run_group_tests would be set from a parameter). This is unfortunate as a
265 -// parametric unit-tester needs to be to pass parameters to the fixtures. We are faking this by calculating the
266 -// space of tests in the launcher, passing an array of identical unit-tests to CMocka and then counting through the
267 -// parameters in the shared state passed between tests. To initialise this counter structure we use this global to
268 -// pass from the launcher (test-builder) to the setup-fixture.
269 -
270 -void *shared_test_state = NULL;
271 -
272 -// -------------------------------- Test family for /api/v1/info ------------------------------------------------------
273 -
274 -struct test_def {
275 - size_t num_headers; // Index coordinate
276 - size_t prefix_len; // Index coordinate
277 - char name[80];
278 - size_t full_len;
279 - struct web_client *instance; // Used within this single test
280 - bool completed, use_cr;
281 - struct test_def *next, *prev;
282 -};
283 -
284 -static void api_info(void **state)
285 -{
286 - (void)state;
287 - struct test_def *def = (struct test_def *)shared_test_state;
288 - shared_test_state = def->next;
289 -
290 - if (def->prev != NULL && !def->prev->completed && strlen(log_buffer) > 0) {
291 - printf("Log of failing case %s:\n", def->prev->name);
292 - puts(log_buffer);
293 - }
294 - log_buffer[0] = 0;
295 - if (localhost != NULL)
296 - free(localhost);
297 - localhost = calloc(1,sizeof(RRDHOST));
298 -
299 - def->instance = setup_fresh_web_client();
300 - build_request(def->instance->response.data, "/api/v1/info", def->use_cr, def->num_headers);
301 - def->instance->response.data->len = def->prefix_len;
302 -
303 - char buffer_repr[1024];
304 - repr(buffer_repr, sizeof(buffer_repr), def->instance->response.data->buffer,def->prefix_len);
305 - netdata_log_info("Buffer contains: %s [first %zu]", buffer_repr,def->prefix_len);
306 - if (def->prefix_len == def->full_len) {
307 - expect_value(__wrap_web_client_api_request_v1, host, localhost);
308 - expect_value(__wrap_web_client_api_request_v1, w, def->instance);
309 - expect_string(__wrap_web_client_api_request_v1, url_repr, "info");
310 - }
311 -
312 - web_client_process_request(def->instance);
313 -
314 - if (def->prefix_len == def->full_len)
315 - assert_int_equal(def->instance->flags & WEB_CLIENT_FLAG_WAIT_RECEIVE, 0);
316 - else
317 - assert_int_equal(def->instance->flags & WEB_CLIENT_FLAG_WAIT_RECEIVE, WEB_CLIENT_FLAG_WAIT_RECEIVE);
318 - assert_int_equal(def->instance->mode, WEB_CLIENT_MODE_NORMAL);
319 - def->completed = true;
320 - log_buffer[0] = 0;
321 -}
322 -
323 -static int api_info_launcher()
324 -{
325 - size_t num_tests = 0;
326 - struct web_client *template = setup_fresh_web_client();
327 - struct test_def *current, *head = NULL;
328 - struct test_def *prev = NULL;
329 -
330 - for (size_t i = 0; i < MAX_HEADERS; i++) {
331 - build_request(template->response.data, "/api/v1/info", true, i);
332 - for (size_t j = 0; j <= template->response.data->len; j++) {
333 - if (j == 0 && i > 0)
334 - continue; // All zero-length prefixes are identical, skip after first time
335 - current = malloc(sizeof(struct test_def));
336 - if (prev != NULL)
337 - prev->next = current;
338 - else
339 - head = current;
340 - current->prev = prev;
341 - prev = current;
342 -
343 - current->num_headers = i;
344 - current->prefix_len = j;
345 - current->full_len = template->response.data->len;
346 - current->instance = NULL;
347 - current->next = NULL;
348 - current->use_cr = true;
349 - current->completed = false;
350 - sprintf(
351 - current->name, "/api/v1/info@%zu,%zu/%zu+%d", current->num_headers, current->prefix_len,
352 - current->full_len,true);
353 - num_tests++;
354 - }
355 - }
356 - for (size_t i = 0; i < MAX_HEADERS; i++) {
357 - build_request(template->response.data, "/api/v1/info", false, i);
358 - for (size_t j = 0; j <= template->response.data->len; j++) {
359 - if (j == 0 && i > 0)
360 - continue; // All zero-length prefixes are identical, skip after first time
361 - current = malloc(sizeof(struct test_def));
362 - if (prev != NULL)
363 - prev->next = current;
364 - else
365 - head = current;
366 - current->prev = prev;
367 - prev = current;
368 -
369 - current->num_headers = i;
370 - current->prefix_len = j;
371 - current->full_len = template->response.data->len;
372 - current->instance = NULL;
373 - current->next = NULL;
374 - current->use_cr = false;
375 - current->completed = false;
376 - sprintf(
377 - current->name, "/api/v1/info@%zu,%zu/%zu+%d", current->num_headers, current->prefix_len,
378 - current->full_len,false);
379 - num_tests++;
380 - }
381 - }
382 -
383 - struct CMUnitTest *tests = calloc(num_tests, sizeof(struct CMUnitTest));
384 - current = head;
385 - for (size_t i = 0; i < num_tests; i++) {
386 - tests[i].name = current->name;
387 - tests[i].test_func = api_info;
388 - tests[i].setup_func = NULL;
389 - tests[i].teardown_func = NULL;
390 - tests[i].initial_state = NULL;
391 - current = current->next;
392 - }
393 -
394 - printf("Setup %zu tests in %p\n", num_tests, head);
395 - shared_test_state = head;
396 - int fails = _cmocka_run_group_tests("web_api", tests, num_tests, NULL, NULL);
397 - free(tests);
398 - destroy_web_client(template);
399 - current = head;
400 - while (current != NULL) {
401 - struct test_def *c = current;
402 - current = current->next;
403 - if (c->instance != NULL) // Clean up resources from tests that failed
404 - destroy_web_client(c->instance);
405 - free(c);
406 - }
407 - if (localhost!=NULL)
408 - free(localhost);
409 - return fails;
410 -}
411 -
412 -/* Raw notes for the cases that we did not use in the unit testing suite.
413 - Leaving them here instead of deleting them in-case we expand the suite during the
414 - work on the URL parser.
415 -
416 - Any ' ' in the URI -> invalid response (Description in 5.1 of RFC2616)
417 - Characters that can't be in paths #;?
418 - "GET /apb/../api/v1/info" HTTP/1.1\r\n"
419 -
420 - https://github.com/uriparser/uriparser/blob/uriparser-0.9.3/test/FourSuite.cpp
421 - Not clear why some of these are illegal -> reserved chars?
422 -
423 - ASSERT_TRUE(testBadUri("beepbeep\x07\x07", 8));
424 - ASSERT_TRUE(testBadUri("\n", 0));
425 - ASSERT_TRUE(testBadUri("::", 0)); // not OK, per Roy Fielding on the W3C uri list on 2004-04-01
426 -
427 - // the following test cases are from a Perl script by David A. Wheeler
428 - // at http://www.dwheeler.com/secure-programs/url.pl
429 - ASSERT_TRUE(testBadUri("http://www yahoo.com", 10));
430 - ASSERT_TRUE(testBadUri("http://www.yahoo.com/hello world/", 26));
431 - ASSERT_TRUE(testBadUri("http://www.yahoo.com/yelp.html#\"", 31));
432 -
433 - // the following test cases are from a Haskell program by Graham Klyne
434 - // at http://www.ninebynine.org/Software/HaskellUtils/Network/URITest.hs
435 - ASSERT_TRUE(testBadUri("[2010:836B:4179::836B:4179]", 0));
436 - ASSERT_TRUE(testBadUri(" ", 0));
437 - ASSERT_TRUE(testBadUri("%", 1));
438 - ASSERT_TRUE(testBadUri("A%Z", 2));
439 - ASSERT_TRUE(testBadUri("%ZZ", 1));
440 - ASSERT_TRUE(testBadUri("%AZ", 2));
441 - ASSERT_TRUE(testBadUri("A C", 1));
442 - ASSERT_TRUE(testBadUri("A\\'C", 1)); // r"A\'C"
443 - ASSERT_TRUE(testBadUri("A`C", 1));
444 - ASSERT_TRUE(testBadUri("A<C", 1));
445 - ASSERT_TRUE(testBadUri("A>C", 1));
446 - ASSERT_TRUE(testBadUri("A^C", 1));
447 - ASSERT_TRUE(testBadUri("A\\\\C", 1)); // r'A\\C'
448 - ASSERT_TRUE(testBadUri("A{C", 1));
449 - ASSERT_TRUE(testBadUri("A|C", 1));
450 - ASSERT_TRUE(testBadUri("A}C", 1));
451 - ASSERT_TRUE(testBadUri("A[C", 1));
452 - ASSERT_TRUE(testBadUri("A]C", 1));
453 - ASSERT_TRUE(testBadUri("A[**]C", 1));
454 - ASSERT_TRUE(testBadUri("http://[xyz]/", 8));
455 - ASSERT_TRUE(testBadUri("http://]/", 7));
456 - ASSERT_TRUE(testBadUri("http://example.org/[2010:836B:4179::836B:4179]", 19));
457 - ASSERT_TRUE(testBadUri("http://example.org/abc#[2010:836B:4179::836B:4179]", 23));
458 - ASSERT_TRUE(testBadUri("http://example.org/xxx/[qwerty]#a[b]", 23));
459 -
460 - // from a post to the W3C uri list on 2004-02-17
461 - // breaks at 22 instead of 17 because everything up to that point is a valid userinfo
462 - ASSERT_TRUE(testBadUri("http://w3c.org:80path1/path2", 22));
463 -
464 -*/
465 -
466 -int main(void)
467 -{
468 - debug_flags = 0xffffffffffff;
469 - int fails = 0;
470 - fails += api_info_launcher();
471 -
472 - return fails;
473 -}