1 /*
2    Configuration file handling on top of tini
3 
4    Copyright (C) Amitay Isaacs  2017
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "replace.h"
21 
22 #include <assert.h>
23 
24 #include "common/conf.c"
25 
26 static void test1(void)
27 {
28 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
29 	struct conf_context *conf;
30 	int ret;
31 	bool status;
32 
test1_read_handler(uint8_t * buf,size_t buflen,void * private_data)33 	ret = conf_init(mem_ctx, &conf);
34 	assert(ret == 0);
35 	assert(conf != NULL);
36 
37 	conf_define_section(conf, "section1", NULL);
38 	status = conf_valid(conf);
39 	assert(status == true);
40 
test1_dead_handler(void * private_data)41 	conf_define_section(conf, NULL, NULL);
42 	status = conf_valid(conf);
43 	assert(status == false);
44 
45 	talloc_free(mem_ctx);
46 }
47 
test1(void)48 static void test2(void)
49 {
50 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
51 	struct conf_context *conf;
52 	int ret;
53 	bool status;
54 
55 	ret = conf_init(mem_ctx, &conf);
56 	assert(ret == 0);
57 	assert(conf != NULL);
58 
59 	conf_define_string(conf, "section1", "key1", "default", NULL);
60 	status = conf_valid(conf);
61 	assert(status == false);
62 
63 	talloc_free(mem_ctx);
64 }
65 
66 static void test3(void)
67 {
68 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
69 	struct conf_context *conf;
70 	int ret;
71 	bool status;
72 
73 	ret = conf_init(mem_ctx, &conf);
74 	assert(ret == 0);
75 	assert(conf != NULL);
76 
77 	conf_define_section(conf, "section1", NULL);
78 	status = conf_valid(conf);
79 	assert(status == true);
80 
81 	conf_define_string(conf, "section1", "key1", NULL, NULL);
82 	status = conf_valid(conf);
83 	assert(status == true);
84 
85 	conf_define_string(conf, "section1", "key1", "value1", NULL);
86 	status = conf_valid(conf);
87 	assert(status == false);
88 
89 	talloc_free(mem_ctx);
90 }
91 
92 static void test4(void)
93 {
94 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
95 	struct conf_context *conf;
96 	int ret;
97 	bool status;
98 
99 	ret = conf_init(mem_ctx, &conf);
100 	assert(ret == 0);
101 	assert(conf != NULL);
102 
103 	conf_define_section(conf, "section1", NULL);
104 	status = conf_valid(conf);
test2_read_handler(uint8_t * buf,size_t buflen,void * private_data)105 	assert(status == true);
106 
107 	conf_define_string(conf, "section1", "key1", NULL, NULL);
108 	status = conf_valid(conf);
109 	assert(status == true);
110 
111 	conf_define_integer(conf, "section1", "key1", 10, NULL);
112 	status = conf_valid(conf);
test2_dead_handler(void * private_data)113 	assert(status == false);
114 
115 	talloc_free(mem_ctx);
116 }
117 
118 static void test5(void)
119 {
120 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
121 	struct conf_context *conf;
122 	enum conf_type type;
123 	int ret;
test2_write_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct comm_context * comm,uint8_t * buf,size_t buflen)124 	bool status;
125 	const char *s_val;
126 	int i_val;
127 	bool b_val;
128 
129 	ret = conf_init(mem_ctx, &conf);
130 	assert(ret == 0);
131 	assert(conf != NULL);
132 
133 	conf_define_section(conf, "section1", NULL);
134 	status = conf_valid(conf);
135 	assert(status == true);
136 
137 	conf_define_string(conf, "section1", "key1", "value1", NULL);
138 	conf_define_integer(conf, "section1", "key2", 10, NULL);
139 	conf_define_boolean(conf, "section1", "key3", true, NULL);
140 
141 	conf_assign_string_pointer(conf, "section1", "key1", &s_val);
142 	conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
143 	conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
144 
145 	status = conf_valid(conf);
146 	assert(status == true);
147 
148 	status = conf_query(conf, "section1", "key1", &type);
149 	assert(status == true);
150 	assert(type == CONF_STRING);
test2_write_done(struct tevent_req * subreq)151 
152 	status = conf_query(conf, "section1", "key2", &type);
153 	assert(status == true);
154 	assert(type == CONF_INTEGER);
155 
156 	status = conf_query(conf, "section1", "key3", &type);
157 	assert(status == true);
158 	assert(type == CONF_BOOLEAN);
159 
160 	assert(strcmp(s_val, "value1") == 0);
161 	assert(i_val == 10);
162 	assert(b_val == true);
163 
164 	conf_set_defaults(conf);
165 
166 	assert(strcmp(s_val, "value1") == 0);
167 	assert(i_val == 10);
168 	assert(b_val == true);
169 
170 	talloc_free(mem_ctx);
171 }
172 
173 static void test6(void)
test2_timer_handler(struct tevent_context * ev,struct tevent_timer * te,struct timeval cur_time,void * private_data)174 {
175 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
176 	struct conf_context *conf;
177 	int ret;
178 	bool status;
179 	const char *s_val, *s2_val;
180 	int i_val, i2_val;
181 	bool b_val, b2_val, is_default;
182 
183 	ret = conf_init(mem_ctx, &conf);
184 	assert(ret == 0);
185 	assert(conf != NULL);
186 
187 	conf_define_section(conf, "section1", NULL);
188 	status = conf_valid(conf);
189 	assert(status == true);
190 
191 	conf_define_string(conf, "section1", "key1", "default", NULL);
192 	conf_define_integer(conf, "section1", "key2", 10, NULL);
193 	conf_define_boolean(conf, "section1", "key3", true, NULL);
194 
195 	conf_assign_string_pointer(conf, "section1", "key1", &s_val);
196 	conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
197 	conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
198 
199 	status = conf_valid(conf);
200 	assert(status == true);
201 
202 	is_default = false;
203 	ret = conf_get_string(conf, "section1", "key1", &s2_val, &is_default);
204 	assert(ret == 0);
205 	assert(strcmp(s2_val, "default") == 0);
206 	assert(is_default == true);
207 
208 	is_default = false;
209 	ret = conf_get_integer(conf, "section1", "key2", &i2_val, &is_default);
210 	assert(ret == 0);
211 	assert(i2_val == 10);
212 	assert(is_default == true);
213 
214 	is_default = false;
215 	ret = conf_get_boolean(conf, "section1", "key3", &b2_val, &is_default);
216 	assert(ret == 0);
217 	assert(b2_val == true);
218 	assert(is_default == true);
219 
220 	ret = conf_set_string(conf, "section1", "key1", "foobar");
221 	assert(ret == 0);
222 
223 	ret = conf_set_integer(conf, "section1", "key2", 20);
224 	assert(ret == 0);
225 
226 	ret = conf_set_boolean(conf, "section1", "key3", false);
227 	assert(ret == 0);
228 
229 	assert(strcmp(s_val, "foobar") == 0);
230 	assert(i_val == 20);
231 	assert(b_val == false);
232 
233 	is_default = true;
234 	ret = conf_get_string(conf, "section1", "key1", &s2_val, &is_default);
235 	assert(ret == 0);
236 	assert(strcmp(s2_val, "foobar") == 0);
237 	assert(is_default == false);
238 
239 	is_default = true;
240 	ret = conf_get_integer(conf, "section1", "key2", &i2_val, &is_default);
241 	assert(ret == 0);
242 	assert(i2_val == 20);
243 	assert(is_default == false);
244 
245 	is_default = true;
246 	ret = conf_get_boolean(conf, "section1", "key3", &b2_val, &is_default);
247 	assert(ret == 0);
248 	assert(b2_val == false);
249 	assert(is_default == false);
250 
251 	conf_dump(conf, stdout);
252 
253 	conf_set_defaults(conf);
254 
255 	assert(strcmp(s_val, "default") == 0);
256 	assert(i_val == 10);
257 	assert(b_val == true);
258 
259 	talloc_free(mem_ctx);
260 }
261 
262 static bool test7_validate_string(const char *key,
263 				  const char *old_value, const char *new_value,
264 				  enum conf_update_mode mode)
test3_writer_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct comm_context * comm,size_t * pkt_size,size_t count)265 {
266 	return false;
267 }
268 
269 static bool test7_validate_integer(const char *key,
270 				   int old_value, int new_value,
271 				   enum conf_update_mode mode)
272 {
273 	return false;
274 }
275 
276 static bool test7_validate_boolean(const char *key,
277 				   bool old_value, bool new_value,
278 				   enum conf_update_mode mode)
279 {
280 	return false;
281 }
282 
283 static void test7(void)
284 {
285 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
286 	struct conf_context *conf;
287 	int ret;
288 	bool status;
289 	const char *s_val, *s2_val;
290 	int i_val, i2_val;
291 	bool b_val, b2_val;
292 
293 	ret = conf_init(mem_ctx, &conf);
294 	assert(ret == 0);
295 	assert(conf != NULL);
296 
297 	conf_define_section(conf, "section1", NULL);
298 	status = conf_valid(conf);
299 	assert(status == true);
300 
301 	conf_define_string(conf, "section1", "key1", "default",
302 			   test7_validate_string);
303 	conf_define_integer(conf, "section1", "key2", 10,
304 			    test7_validate_integer);
305 	conf_define_boolean(conf, "section1", "key3", true,
306 			    test7_validate_boolean);
307 
308 	conf_assign_string_pointer(conf, "section1", "key1", &s_val);
309 	conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
310 	conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
311 
312 	status = conf_valid(conf);
test3_writer_next(struct tevent_req * subreq)313 	assert(status == true);
314 
315 	ret = conf_set_string(conf, "section1", "key1", "default");
316 	assert(ret == 0);
317 
318 	ret = conf_set_string(conf, "section1", "key1", "foobar");
319 	assert(ret == EINVAL);
320 
321 	ret = conf_set_integer(conf, "section1", "key2", 10);
322 	assert(ret == 0);
323 
324 	ret = conf_set_integer(conf, "section1", "key2", 20);
325 	assert(ret == EINVAL);
326 
327 	ret = conf_set_boolean(conf, "section1", "key3", true);
328 	assert(ret == 0);
329 
330 	ret = conf_set_boolean(conf, "section1", "key3", false);
331 	assert(ret == EINVAL);
332 
333 	assert(strcmp(s_val, "default") == 0);
334 	assert(i_val == 10);
335 	assert(b_val == true);
336 
337 	ret = conf_get_string(conf, "section1", "key2", &s2_val, NULL);
338 	assert(ret == EINVAL);
339 
340 	ret = conf_get_integer(conf, "section1", "key3", &i2_val, NULL);
341 	assert(ret == EINVAL);
342 
343 	ret = conf_get_boolean(conf, "section1", "key1", &b2_val, NULL);
344 	assert(ret == EINVAL);
345 
346 	talloc_free(mem_ctx);
test3_writer_recv(struct tevent_req * req,int * perr)347 }
348 
349 static bool test8_validate(struct conf_context *conf,
350 			   const char *section,
351 			   enum conf_update_mode mode)
352 {
353 	return false;
354 }
test3_writer(int fd,size_t * pkt_size,size_t count)355 
356 static void test8(const char *filename)
357 {
358 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
359 	struct conf_context *conf;
360 	int ret;
361 	bool status;
362 
363 	ret = conf_init(mem_ctx, &conf);
364 	assert(ret == 0);
365 	assert(conf != NULL);
366 
367 	conf_define_section(conf, "section1", test8_validate);
368 	status = conf_valid(conf);
369 	assert(status == true);
370 
371 	conf_define_string(conf, "section1", "key1", "default", NULL);
372 
373 	status = conf_valid(conf);
374 	assert(status == true);
375 
376 	ret = conf_load(conf, filename, true);
377 	conf_dump(conf, stdout);
378 
379 	talloc_free(mem_ctx);
380 	exit(ret);
381 }
382 
383 static void test9(const char *filename, bool ignore_unknown)
384 {
385 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
386 	struct conf_context *conf;
387 	int ret;
388 	bool status;
389 
390 	ret = conf_init(mem_ctx, &conf);
391 	assert(ret == 0);
test3_reader_handler(uint8_t * buf,size_t buflen,void * private_data)392 	assert(conf != NULL);
393 
394 	conf_define_section(conf, "section1", NULL);
395 
396 	conf_define_string(conf, "section1", "key1", "value1", NULL);
397 	conf_define_integer(conf, "section1", "key2", 10, NULL);
398 	conf_define_boolean(conf, "section1", "key3", true, NULL);
399 
400 	status = conf_valid(conf);
401 	assert(status == true);
402 
403 	conf_set_boolean(conf, "section1", "key3", false);
404 
405 	ret = conf_load(conf, filename, ignore_unknown);
406 	conf_dump(conf, stdout);
407 
test3_reader(int fd,size_t * pkt_size,int count)408 	talloc_free(mem_ctx);
409 	exit(ret);
410 }
411 
412 static void test11(const char *filename)
413 {
414 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
415 	char reload[PATH_MAX];
416 	struct conf_context *conf;
417 	int ret;
418 	bool status;
419 
420 	ret = snprintf(reload, sizeof(reload), "%s.reload", filename);
421 	assert((size_t)ret < sizeof(reload));
422 
423 	ret = conf_init(mem_ctx, &conf);
424 	assert(ret == 0);
425 	assert(conf != NULL);
426 
427 	conf_define_section(conf, "section1", NULL);
428 
429 	conf_define_string(conf, "section1", "key1", "value1", NULL);
430 	conf_define_integer(conf, "section1", "key2", 10, NULL);
431 	conf_define_boolean(conf, "section1", "key3", true, NULL);
432 
433 	status = conf_valid(conf);
434 	assert(status == true);
435 
436 	ret = conf_load(conf, filename, false);
437 	assert(ret == 0);
438 
439 	ret = rename(reload, filename);
440 	assert(ret == 0);
441 
442 	ret = conf_reload(conf);
test3(void)443 	assert(ret == 0);
444 
445 	conf_dump(conf, stdout);
446 
447 	talloc_free(mem_ctx);
448 	exit(ret);
449 }
450 
451 int main(int argc, const char **argv)
452 {
453 	int num;
454 
455 	if (argc < 2) {
456 		fprintf(stderr, "Usage: %s <testnum> [<config>]\n", argv[0]);
457 		exit(1);
458 	}
459 
460 	num = atoi(argv[1]);
461 	if (num > 7 && argc != 3) {
462 		fprintf(stderr, "Usage: %s <testnum> [<config>]\n", argv[0]);
463 		exit(1);
464 	}
465 
466 	switch (num) {
467 	case 1:
468 		test1();
469 		break;
470 
471 	case 2:
main(int argc,const char ** argv)472 		test2();
473 		break;
474 
475 	case 3:
476 		test3();
477 		break;
478 
479 	case 4:
480 		test4();
481 		break;
482 
483 	case 5:
484 		test5();
485 		break;
486 
487 	case 6:
488 		test6();
489 		break;
490 
491 	case 7:
492 		test7();
493 		break;
494 
495 	case 8:
496 		test8(argv[2]);
497 		break;
498 
499 	case 9:
500 		test9(argv[2], true);
501 		break;
502 
503 	case 10:
504 		test9(argv[2], false);
505 		break;
506 
507 	case 11:
508 		test11(argv[2]);
509 		break;
510 	}
511 
512 	return 0;
513 }
514