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