xref: /spdk/lib/conf/conf.c (revision 7192849ed24874f3e9cc31e8a33a9b32c49b9506)
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 			append_cf_section(cp, sp);
499 
500 			sp->name = strdup(key);
501 			if (sp->name == NULL) {
502 				SPDK_ERRLOG("cannot duplicate %s to sp->name\n", key);
503 				return -1;
504 			}
505 		}
506 		cp->current_section = sp;
507 
508 
509 		sp->num = num;
510 	} else {
511 		/* parameters */
512 		sp = cp->current_section;
513 		if (sp == NULL) {
514 			SPDK_ERRLOG("unknown section\n");
515 			return -1;
516 		}
517 		key = spdk_strsepq(&arg, CF_DELIM_KEY);
518 		if (key == NULL) {
519 			SPDK_ERRLOG("broken key\n");
520 			return -1;
521 		}
522 
523 		ip = allocate_cf_item();
524 		if (ip == NULL) {
525 			SPDK_ERRLOG("cannot allocate cf item\n");
526 			return -1;
527 		}
528 		append_cf_item(sp, ip);
529 		ip->key = strdup(key);
530 		if (ip->key == NULL) {
531 			SPDK_ERRLOG("cannot make duplicate of %s\n", key);
532 			return -1;
533 		}
534 		ip->val = NULL;
535 		if (arg != NULL) {
536 			/* key has value(s) */
537 			while (arg != NULL) {
538 				val = spdk_strsepq(&arg, CF_DELIM);
539 				vp = allocate_cf_value();
540 				if (vp == NULL) {
541 					SPDK_ERRLOG("cannot allocate cf value\n");
542 					return -1;
543 				}
544 				append_cf_value(ip, vp);
545 				vp->value = strdup(val);
546 				if (vp->value == NULL) {
547 					SPDK_ERRLOG("cannot duplicate %s to vp->value\n", val);
548 					return -1;
549 				}
550 			}
551 		}
552 	}
553 
554 	return 0;
555 }
556 
557 static char *
558 fgets_line(FILE *fp)
559 {
560 	char *dst, *dst2, *p;
561 	size_t total, len;
562 
563 	dst = p = malloc(LIB_MAX_TMPBUF);
564 	if (!dst) {
565 		return NULL;
566 	}
567 
568 	dst[0] = '\0';
569 	total = 0;
570 
571 	while (fgets(p, LIB_MAX_TMPBUF, fp) != NULL) {
572 		len = strlen(p);
573 		total += len;
574 		if (len + 1 < LIB_MAX_TMPBUF || dst[total - 1] == '\n') {
575 			dst2 = realloc(dst, total + 1);
576 			if (!dst2) {
577 				free(dst);
578 				return NULL;
579 			} else {
580 				return dst2;
581 			}
582 		}
583 
584 		dst2 = realloc(dst, total + LIB_MAX_TMPBUF);
585 		if (!dst2) {
586 			free(dst);
587 			return NULL;
588 		} else {
589 			dst = dst2;
590 		}
591 
592 		p = dst + total;
593 	}
594 
595 	if (feof(fp) && total != 0) {
596 		dst2 = realloc(dst, total + 2);
597 		if (!dst2) {
598 			free(dst);
599 			return NULL;
600 		} else {
601 			dst = dst2;
602 		}
603 
604 		dst[total] = '\n';
605 		dst[total + 1] = '\0';
606 		return dst;
607 	}
608 
609 	free(dst);
610 
611 	return NULL;
612 }
613 
614 int
615 spdk_conf_read(struct spdk_conf *cp, const char *file)
616 {
617 	FILE *fp;
618 	char *lp, *p;
619 	char *lp2, *q;
620 	int line;
621 	int n, n2;
622 
623 	if (file == NULL || file[0] == '\0') {
624 		return -1;
625 	}
626 	SPDK_ERRLOG("INI configuration has been deprecated and will be removed in a future release. Please switch to JSON-RPC.\n");
627 
628 	fp = fopen(file, "r");
629 	if (fp == NULL) {
630 		SPDK_ERRLOG("open error: %s\n", file);
631 		return -1;
632 	}
633 
634 	cp->file = strdup(file);
635 	if (cp->file == NULL) {
636 		SPDK_ERRLOG("cannot duplicate %s to cp->file\n", file);
637 		fclose(fp);
638 		return -1;
639 	}
640 
641 	line = 1;
642 	while ((lp = fgets_line(fp)) != NULL) {
643 		/* skip spaces */
644 		for (p = lp; *p != '\0' && isspace((int) *p); p++)
645 			;
646 		/* skip comment, empty line */
647 		if (p[0] == '#' || p[0] == '\0') {
648 			goto next_line;
649 		}
650 
651 		/* concatenate line end with '\' */
652 		n = strlen(p);
653 		while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') {
654 			n -= 2;
655 			lp2 = fgets_line(fp);
656 			if (lp2 == NULL) {
657 				break;
658 			}
659 
660 			line++;
661 			n2 = strlen(lp2);
662 
663 			q = malloc(n + n2 + 1);
664 			if (!q) {
665 				free(lp2);
666 				free(lp);
667 				SPDK_ERRLOG("malloc failed at line %d of %s\n", line, cp->file);
668 				fclose(fp);
669 				return -1;
670 			}
671 
672 			memcpy(q, p, n);
673 			memcpy(q + n, lp2, n2);
674 			q[n + n2] = '\0';
675 			free(lp2);
676 			free(lp);
677 			p = lp = q;
678 			n += n2;
679 		}
680 
681 		/* parse one line */
682 		if (parse_line(cp, p) < 0) {
683 			SPDK_ERRLOG("parse error at line %d of %s\n", line, cp->file);
684 		}
685 next_line:
686 		line++;
687 		free(lp);
688 	}
689 
690 	fclose(fp);
691 	return 0;
692 }
693 
694 void
695 spdk_conf_set_as_default(struct spdk_conf *cp)
696 {
697 	default_config = cp;
698 }
699 
700 void
701 spdk_conf_disable_sections_merge(struct spdk_conf *cp)
702 {
703 	cp->merge_sections = false;
704 }
705