xref: /spdk/lib/conf/conf.c (revision 307b8c112ffd90a26d53dd15fad67bd9038ef526)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
3  *   Copyright (c) Intel Corporation.
4  *   All rights reserved.
5  */
6 
7 #include "spdk/stdinc.h"
8 
9 #include "spdk/conf.h"
10 #include "spdk/string.h"
11 #include "spdk/log.h"
12 
13 struct spdk_conf_value {
14 	struct spdk_conf_value *next;
15 	char *value;
16 };
17 
18 struct spdk_conf_item {
19 	struct spdk_conf_item *next;
20 	char *key;
21 	struct spdk_conf_value *val;
22 };
23 
24 struct spdk_conf_section {
25 	struct spdk_conf_section *next;
26 	char *name;
27 	int num;
28 	struct spdk_conf_item *item;
29 };
30 
31 struct spdk_conf {
32 	char *file;
33 	struct spdk_conf_section *current_section;
34 	struct spdk_conf_section *section;
35 	bool merge_sections;
36 };
37 
38 #define CF_DELIM " \t"
39 #define CF_DELIM_KEY " \t="
40 
41 #define LIB_MAX_TMPBUF 1024
42 
43 static struct spdk_conf *default_config = NULL;
44 
45 struct spdk_conf *
46 spdk_conf_allocate(void)
47 {
48 	struct spdk_conf *ret = calloc(1, sizeof(struct spdk_conf));
49 
50 	if (ret) {
51 		ret->merge_sections = true;
52 	}
53 
54 	return ret;
55 }
56 
57 static void
58 free_conf_value(struct spdk_conf_value *vp)
59 {
60 	if (vp == NULL) {
61 		return;
62 	}
63 
64 	if (vp->value) {
65 		free(vp->value);
66 	}
67 
68 	free(vp);
69 }
70 
71 static void
72 free_all_conf_value(struct spdk_conf_value *vp)
73 {
74 	struct spdk_conf_value *next;
75 
76 	if (vp == NULL) {
77 		return;
78 	}
79 
80 	while (vp != NULL) {
81 		next = vp->next;
82 		free_conf_value(vp);
83 		vp = next;
84 	}
85 }
86 
87 static void
88 free_conf_item(struct spdk_conf_item *ip)
89 {
90 	if (ip == NULL) {
91 		return;
92 	}
93 
94 	if (ip->val != NULL) {
95 		free_all_conf_value(ip->val);
96 	}
97 
98 	if (ip->key != NULL) {
99 		free(ip->key);
100 	}
101 
102 	free(ip);
103 }
104 
105 static void
106 free_all_conf_item(struct spdk_conf_item *ip)
107 {
108 	struct spdk_conf_item *next;
109 
110 	if (ip == NULL) {
111 		return;
112 	}
113 
114 	while (ip != NULL) {
115 		next = ip->next;
116 		free_conf_item(ip);
117 		ip = next;
118 	}
119 }
120 
121 static void
122 free_conf_section(struct spdk_conf_section *sp)
123 {
124 	if (sp == NULL) {
125 		return;
126 	}
127 
128 	if (sp->item) {
129 		free_all_conf_item(sp->item);
130 	}
131 
132 	if (sp->name) {
133 		free(sp->name);
134 	}
135 
136 	free(sp);
137 }
138 
139 static void
140 free_all_conf_section(struct spdk_conf_section *sp)
141 {
142 	struct spdk_conf_section *next;
143 
144 	if (sp == NULL) {
145 		return;
146 	}
147 
148 	while (sp != NULL) {
149 		next = sp->next;
150 		free_conf_section(sp);
151 		sp = next;
152 	}
153 }
154 
155 void
156 spdk_conf_free(struct spdk_conf *cp)
157 {
158 	if (cp == NULL) {
159 		return;
160 	}
161 
162 	if (cp->section != NULL) {
163 		free_all_conf_section(cp->section);
164 	}
165 
166 	if (cp->file != NULL) {
167 		free(cp->file);
168 	}
169 
170 	free(cp);
171 }
172 
173 static struct spdk_conf_section *
174 allocate_cf_section(void)
175 {
176 	return calloc(1, sizeof(struct spdk_conf_section));
177 }
178 
179 static struct spdk_conf_item *
180 allocate_cf_item(void)
181 {
182 	return calloc(1, sizeof(struct spdk_conf_item));
183 }
184 
185 static struct spdk_conf_value *
186 allocate_cf_value(void)
187 {
188 	return calloc(1, sizeof(struct spdk_conf_value));
189 }
190 
191 
192 #define CHECK_CP_OR_USE_DEFAULT(cp) (((cp) == NULL) && (default_config != NULL)) ? default_config : (cp)
193 
194 struct spdk_conf_section *
195 spdk_conf_find_section(struct spdk_conf *cp, const char *name)
196 {
197 	struct spdk_conf_section *sp;
198 
199 	if (name == NULL || name[0] == '\0') {
200 		return NULL;
201 	}
202 
203 	cp = CHECK_CP_OR_USE_DEFAULT(cp);
204 	if (cp == NULL) {
205 		return NULL;
206 	}
207 
208 	for (sp = cp->section; sp != NULL; sp = sp->next) {
209 		if (sp->name != NULL && sp->name[0] == name[0]
210 		    && strcasecmp(sp->name, name) == 0) {
211 			return sp;
212 		}
213 	}
214 
215 	return NULL;
216 }
217 
218 struct spdk_conf_section *
219 spdk_conf_first_section(struct spdk_conf *cp)
220 {
221 	cp = CHECK_CP_OR_USE_DEFAULT(cp);
222 	if (cp == NULL) {
223 		return NULL;
224 	}
225 
226 	return cp->section;
227 }
228 
229 struct spdk_conf_section *
230 spdk_conf_next_section(struct spdk_conf_section *sp)
231 {
232 	if (sp == NULL) {
233 		return NULL;
234 	}
235 
236 	return sp->next;
237 }
238 
239 static void
240 append_cf_section(struct spdk_conf *cp, struct spdk_conf_section *sp)
241 {
242 	struct spdk_conf_section *last;
243 
244 	cp = CHECK_CP_OR_USE_DEFAULT(cp);
245 	if (cp == NULL) {
246 		SPDK_ERRLOG("cp == NULL\n");
247 		return;
248 	}
249 
250 	if (cp->section == NULL) {
251 		cp->section = sp;
252 		return;
253 	}
254 
255 	for (last = cp->section; last->next != NULL; last = last->next)
256 		;
257 	last->next = sp;
258 }
259 
260 static struct spdk_conf_item *
261 find_cf_nitem(struct spdk_conf_section *sp, const char *key, int idx)
262 {
263 	struct spdk_conf_item *ip;
264 	int i;
265 
266 	if (key == NULL || key[0] == '\0') {
267 		return NULL;
268 	}
269 
270 	i = 0;
271 	for (ip = sp->item; ip != NULL; ip = ip->next) {
272 		if (ip->key != NULL && ip->key[0] == key[0]
273 		    && strcasecmp(ip->key, key) == 0) {
274 			if (i == idx) {
275 				return ip;
276 			}
277 			i++;
278 		}
279 	}
280 
281 	return NULL;
282 }
283 
284 static void
285 append_cf_item(struct spdk_conf_section *sp, struct spdk_conf_item *ip)
286 {
287 	struct spdk_conf_item *last;
288 
289 	if (sp == NULL) {
290 		return;
291 	}
292 
293 	if (sp->item == NULL) {
294 		sp->item = ip;
295 		return;
296 	}
297 
298 	for (last = sp->item; last->next != NULL; last = last->next)
299 		;
300 	last->next = ip;
301 }
302 
303 static void
304 append_cf_value(struct spdk_conf_item *ip, struct spdk_conf_value *vp)
305 {
306 	struct spdk_conf_value *last;
307 
308 	if (ip == NULL) {
309 		return;
310 	}
311 
312 	if (ip->val == NULL) {
313 		ip->val = vp;
314 		return;
315 	}
316 
317 	for (last = ip->val; last->next != NULL; last = last->next)
318 		;
319 	last->next = vp;
320 }
321 
322 bool
323 spdk_conf_section_match_prefix(const struct spdk_conf_section *sp, const char *name_prefix)
324 {
325 	return strncasecmp(sp->name, name_prefix, strlen(name_prefix)) == 0;
326 }
327 
328 const char *
329 spdk_conf_section_get_name(const struct spdk_conf_section *sp)
330 {
331 	return sp->name;
332 }
333 
334 int
335 spdk_conf_section_get_num(const struct spdk_conf_section *sp)
336 {
337 	return sp->num;
338 }
339 
340 char *
341 spdk_conf_section_get_nmval(struct spdk_conf_section *sp, const char *key, int idx1, int idx2)
342 {
343 	struct spdk_conf_item *ip;
344 	struct spdk_conf_value *vp;
345 	int i;
346 
347 	ip = find_cf_nitem(sp, key, idx1);
348 	if (ip == NULL) {
349 		return NULL;
350 	}
351 
352 	vp = ip->val;
353 	if (vp == NULL) {
354 		return NULL;
355 	}
356 
357 	for (i = 0; vp != NULL; vp = vp->next, i++) {
358 		if (i == idx2) {
359 			return vp->value;
360 		}
361 	}
362 
363 	return NULL;
364 }
365 
366 char *
367 spdk_conf_section_get_nval(struct spdk_conf_section *sp, const char *key, int idx)
368 {
369 	struct spdk_conf_item *ip;
370 	struct spdk_conf_value *vp;
371 
372 	ip = find_cf_nitem(sp, key, idx);
373 	if (ip == NULL) {
374 		return NULL;
375 	}
376 
377 	vp = ip->val;
378 	if (vp == NULL) {
379 		return NULL;
380 	}
381 
382 	return vp->value;
383 }
384 
385 char *
386 spdk_conf_section_get_val(struct spdk_conf_section *sp, const char *key)
387 {
388 	return spdk_conf_section_get_nval(sp, key, 0);
389 }
390 
391 int
392 spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key)
393 {
394 	const char *v;
395 	int value;
396 
397 	v = spdk_conf_section_get_nval(sp, key, 0);
398 	if (v == NULL) {
399 		return -1;
400 	}
401 
402 	value = (int)spdk_strtol(v, 10);
403 	return value;
404 }
405 
406 bool
407 spdk_conf_section_get_boolval(struct spdk_conf_section *sp, const char *key, bool default_val)
408 {
409 	const char *v;
410 
411 	v = spdk_conf_section_get_nval(sp, key, 0);
412 	if (v == NULL) {
413 		return default_val;
414 	}
415 
416 	if (!strcasecmp(v, "Yes") || !strcasecmp(v, "Y") || !strcasecmp(v, "True")) {
417 		return true;
418 	}
419 
420 	if (!strcasecmp(v, "No") || !strcasecmp(v, "N") || !strcasecmp(v, "False")) {
421 		return false;
422 	}
423 
424 	return default_val;
425 }
426 
427 static int
428 parse_line(struct spdk_conf *cp, char *lp)
429 {
430 	struct spdk_conf_section *sp;
431 	struct spdk_conf_item *ip;
432 	struct spdk_conf_value *vp;
433 	char *arg;
434 	char *key;
435 	char *val;
436 	char *p;
437 	int num;
438 
439 	arg = spdk_str_trim(lp);
440 	if (arg == NULL) {
441 		SPDK_ERRLOG("no section\n");
442 		return -1;
443 	}
444 
445 	if (arg[0] == '[') {
446 		/* section */
447 		arg++;
448 		key = spdk_strsepq(&arg, "]");
449 		if (key == NULL || arg != NULL) {
450 			SPDK_ERRLOG("broken section\n");
451 			return -1;
452 		}
453 		/* determine section number */
454 		for (p = key; *p != '\0' && !isdigit((int) *p); p++)
455 			;
456 		if (*p != '\0') {
457 			num = (int)spdk_strtol(p, 10);
458 		} else {
459 			num = 0;
460 		}
461 
462 		if (cp->merge_sections) {
463 			sp = spdk_conf_find_section(cp, key);
464 		} else {
465 			sp = NULL;
466 		}
467 
468 		if (sp == NULL) {
469 			sp = allocate_cf_section();
470 			if (sp == NULL) {
471 				SPDK_ERRLOG("cannot allocate cf section\n");
472 				return -1;
473 			}
474 			append_cf_section(cp, sp);
475 
476 			sp->name = strdup(key);
477 			if (sp->name == NULL) {
478 				SPDK_ERRLOG("cannot duplicate %s to sp->name\n", key);
479 				return -1;
480 			}
481 		}
482 		cp->current_section = sp;
483 
484 
485 		sp->num = num;
486 	} else {
487 		/* parameters */
488 		sp = cp->current_section;
489 		if (sp == NULL) {
490 			SPDK_ERRLOG("unknown section\n");
491 			return -1;
492 		}
493 		key = spdk_strsepq(&arg, CF_DELIM_KEY);
494 		if (key == NULL) {
495 			SPDK_ERRLOG("broken key\n");
496 			return -1;
497 		}
498 
499 		ip = allocate_cf_item();
500 		if (ip == NULL) {
501 			SPDK_ERRLOG("cannot allocate cf item\n");
502 			return -1;
503 		}
504 		append_cf_item(sp, ip);
505 		ip->key = strdup(key);
506 		if (ip->key == NULL) {
507 			SPDK_ERRLOG("cannot make duplicate of %s\n", key);
508 			return -1;
509 		}
510 		ip->val = NULL;
511 		if (arg != NULL) {
512 			/* key has value(s) */
513 			while (arg != NULL) {
514 				val = spdk_strsepq(&arg, CF_DELIM);
515 				vp = allocate_cf_value();
516 				if (vp == NULL) {
517 					SPDK_ERRLOG("cannot allocate cf value\n");
518 					return -1;
519 				}
520 				append_cf_value(ip, vp);
521 				vp->value = strdup(val);
522 				if (vp->value == NULL) {
523 					SPDK_ERRLOG("cannot duplicate %s to vp->value\n", val);
524 					return -1;
525 				}
526 			}
527 		}
528 	}
529 
530 	return 0;
531 }
532 
533 static char *
534 fgets_line(FILE *fp)
535 {
536 	char *dst, *dst2, *p;
537 	size_t total, len;
538 
539 	dst = p = malloc(LIB_MAX_TMPBUF);
540 	if (!dst) {
541 		return NULL;
542 	}
543 
544 	dst[0] = '\0';
545 	total = 0;
546 
547 	while (fgets(p, LIB_MAX_TMPBUF, fp) != NULL) {
548 		len = strlen(p);
549 		total += len;
550 		if (len + 1 < LIB_MAX_TMPBUF || dst[total - 1] == '\n') {
551 			dst2 = realloc(dst, total + 1);
552 			if (!dst2) {
553 				free(dst);
554 				return NULL;
555 			} else {
556 				return dst2;
557 			}
558 		}
559 
560 		dst2 = realloc(dst, total + LIB_MAX_TMPBUF);
561 		if (!dst2) {
562 			free(dst);
563 			return NULL;
564 		} else {
565 			dst = dst2;
566 		}
567 
568 		p = dst + total;
569 	}
570 
571 	if (feof(fp) && total != 0) {
572 		dst2 = realloc(dst, total + 2);
573 		if (!dst2) {
574 			free(dst);
575 			return NULL;
576 		} else {
577 			dst = dst2;
578 		}
579 
580 		dst[total] = '\n';
581 		dst[total + 1] = '\0';
582 		return dst;
583 	}
584 
585 	free(dst);
586 
587 	return NULL;
588 }
589 
590 int
591 spdk_conf_read(struct spdk_conf *cp, const char *file)
592 {
593 	FILE *fp;
594 	char *lp, *p;
595 	char *lp2, *q;
596 	int line;
597 	int n, n2;
598 
599 	if (file == NULL || file[0] == '\0') {
600 		return -1;
601 	}
602 
603 	fp = fopen(file, "r");
604 	if (fp == NULL) {
605 		SPDK_ERRLOG("open error: %s\n", file);
606 		return -1;
607 	}
608 
609 	cp->file = strdup(file);
610 	if (cp->file == NULL) {
611 		SPDK_ERRLOG("cannot duplicate %s to cp->file\n", file);
612 		fclose(fp);
613 		return -1;
614 	}
615 
616 	line = 1;
617 	while ((lp = fgets_line(fp)) != NULL) {
618 		/* skip spaces */
619 		for (p = lp; *p != '\0' && isspace((int) *p); p++)
620 			;
621 		/* skip comment, empty line */
622 		if (p[0] == '#' || p[0] == '\0') {
623 			goto next_line;
624 		}
625 
626 		/* concatenate line end with '\' */
627 		n = strlen(p);
628 		while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') {
629 			n -= 2;
630 			lp2 = fgets_line(fp);
631 			if (lp2 == NULL) {
632 				break;
633 			}
634 
635 			line++;
636 			n2 = strlen(lp2);
637 
638 			q = malloc(n + n2 + 1);
639 			if (!q) {
640 				free(lp2);
641 				free(lp);
642 				SPDK_ERRLOG("malloc failed at line %d of %s\n", line, cp->file);
643 				fclose(fp);
644 				return -1;
645 			}
646 
647 			memcpy(q, p, n);
648 			memcpy(q + n, lp2, n2);
649 			q[n + n2] = '\0';
650 			free(lp2);
651 			free(lp);
652 			p = lp = q;
653 			n += n2;
654 		}
655 
656 		/* parse one line */
657 		if (parse_line(cp, p) < 0) {
658 			SPDK_ERRLOG("parse error at line %d of %s\n", line, cp->file);
659 		}
660 next_line:
661 		line++;
662 		free(lp);
663 	}
664 
665 	fclose(fp);
666 	return 0;
667 }
668 
669 void
670 spdk_conf_set_as_default(struct spdk_conf *cp)
671 {
672 	default_config = cp;
673 }
674 
675 void
676 spdk_conf_disable_sections_merge(struct spdk_conf *cp)
677 {
678 	cp->merge_sections = false;
679 }
680