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
-}