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