xref: /spdk/lib/conf/conf.c (revision 04c48172b9879a8824de83c842087d871c433d12)
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/conf.h"
36 
37 #include <ctype.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "spdk/string.h"
44 
45 #define CF_DELIM " \t"
46 
47 #define LIB_MAX_TMPBUF 1024
48 
49 static struct spdk_conf *default_config = NULL;
50 
51 struct spdk_conf *
52 spdk_conf_allocate(void)
53 {
54 	return calloc(1, sizeof(struct spdk_conf));
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 		fprintf(stderr, "%s: cp == NULL\n", __func__);
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)strtol(v, NULL, 10);
403 	return value;
404 }
405 
406 static int
407 parse_line(struct spdk_conf *cp, char *lp)
408 {
409 	struct spdk_conf_section *sp;
410 	struct spdk_conf_item *ip;
411 	struct spdk_conf_value *vp;
412 	char *arg;
413 	char *key;
414 	char *val;
415 	char *p;
416 	int num;
417 
418 	arg = spdk_str_trim(lp);
419 	if (arg == NULL) {
420 		fprintf(stderr, "no section\n");
421 		return -1;
422 	}
423 
424 	if (arg[0] == '[') {
425 		/* section */
426 		arg++;
427 		key = spdk_strsepq(&arg, "]");
428 		if (key == NULL || arg != NULL) {
429 			fprintf(stderr, "broken section\n");
430 			return -1;
431 		}
432 		/* determine section number */
433 		for (p = key; *p != '\0' && !isdigit((int) *p); p++)
434 			;
435 		if (*p != '\0') {
436 			num = (int)strtol(p, NULL, 10);
437 		} else {
438 			num = 0;
439 		}
440 
441 		sp = spdk_conf_find_section(cp, key);
442 		if (sp == NULL) {
443 			sp = allocate_cf_section();
444 			append_cf_section(cp, sp);
445 		}
446 		cp->current_section = sp;
447 		sp->name = strdup(key);
448 		if (sp->name == NULL) {
449 			perror("strdup sp->name");
450 			return -1;
451 		}
452 
453 		sp->num = num;
454 	} else {
455 		/* parameters */
456 		sp = cp->current_section;
457 		if (sp == NULL) {
458 			fprintf(stderr, "unknown section\n");
459 			return -1;
460 		}
461 		key = spdk_strsepq(&arg, CF_DELIM);
462 		if (key == NULL) {
463 			fprintf(stderr, "broken key\n");
464 			return -1;
465 		}
466 
467 		ip = allocate_cf_item();
468 		if (ip == NULL) {
469 			fprintf(stderr, "cannot allocate cf item\n");
470 			return -1;
471 		}
472 		append_cf_item(sp, ip);
473 		ip->key = strdup(key);
474 		if (ip->key == NULL) {
475 			perror("strdup ip->key");
476 			return -1;
477 		}
478 		ip->val = NULL;
479 		if (arg != NULL) {
480 			/* key has value(s) */
481 			while (arg != NULL) {
482 				val = spdk_strsepq(&arg, CF_DELIM);
483 				vp = allocate_cf_value();
484 				if (vp == NULL) {
485 					fprintf(stderr,
486 						"cannot allocate cf value\n");
487 					return -1;
488 				}
489 				append_cf_value(ip, vp);
490 				vp->value = strdup(val);
491 				if (vp->value == NULL) {
492 					perror("strdup vp->value");
493 					return -1;
494 				}
495 			}
496 		}
497 	}
498 
499 	return 0;
500 }
501 
502 static char *
503 fgets_line(FILE *fp)
504 {
505 	char *dst, *dst2, *p;
506 	size_t total, len;
507 
508 	dst = p = malloc(LIB_MAX_TMPBUF);
509 	if (!dst) {
510 		return NULL;
511 	}
512 
513 	dst[0] = '\0';
514 	total = 0;
515 
516 	while (fgets(p, LIB_MAX_TMPBUF, fp) != NULL) {
517 		len = strlen(p);
518 		total += len;
519 		if (len + 1 < LIB_MAX_TMPBUF || dst[total - 1] == '\n') {
520 			dst2 = realloc(dst, total + 1);
521 			if (!dst2) {
522 				free(dst);
523 				return NULL;
524 			} else {
525 				return dst2;
526 			}
527 		}
528 
529 		dst2 = realloc(dst, total + LIB_MAX_TMPBUF);
530 		if (!dst2) {
531 			free(dst);
532 			return NULL;
533 		} else {
534 			dst = dst2;
535 		}
536 
537 		p = dst + total;
538 	}
539 
540 	if (feof(fp) && total != 0) {
541 		dst2 = realloc(dst, total + 2);
542 		if (!dst2) {
543 			free(dst);
544 			return NULL;
545 		} else {
546 			dst = dst2;
547 		}
548 
549 		dst[total] = '\n';
550 		dst[total + 1] = '\0';
551 		return dst;
552 	}
553 
554 	free(dst);
555 
556 	return NULL;
557 }
558 
559 int
560 spdk_conf_read(struct spdk_conf *cp, const char *file)
561 {
562 	FILE *fp;
563 	char *lp, *p;
564 	char *lp2, *q;
565 	int line;
566 	int n, n2;
567 
568 	if (file == NULL || file[0] == '\0') {
569 		return -1;
570 	}
571 
572 	fp = fopen(file, "r");
573 	if (fp == NULL) {
574 		fprintf(stderr, "open error: %s\n", file);
575 		return -1;
576 	}
577 
578 	cp->file = strdup(file);
579 	if (cp->file == NULL) {
580 		perror("strdup cp->file");
581 		fclose(fp);
582 		return -1;
583 	}
584 
585 	line = 1;
586 	while ((lp = fgets_line(fp)) != NULL) {
587 		/* skip spaces */
588 		for (p = lp; *p != '\0' && isspace((int) *p); p++)
589 			;
590 		/* skip comment, empty line */
591 		if (p[0] == '#' || p[0] == '\0') {
592 			goto next_line;
593 		}
594 
595 		/* concatenate line end with '\' */
596 		n = strlen(p);
597 		while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') {
598 			n -= 2;
599 			lp2 = fgets_line(fp);
600 			if (lp2 == NULL) {
601 				break;
602 			}
603 
604 			line++;
605 			n2 = strlen(lp2);
606 
607 			q = malloc(n + n2 + 1);
608 			if (!q) {
609 				free(lp2);
610 				free(lp);
611 				fprintf(stderr, "malloc failed at line %d of %s\n", line, cp->file);
612 				fclose(fp);
613 				return -1;
614 			}
615 
616 			memcpy(q, p, n);
617 			memcpy(q + n, lp2, n2);
618 			q[n + n2] = '\0';
619 			free(lp2);
620 			free(lp);
621 			p = lp = q;
622 			n += n2;
623 		}
624 
625 		/* parse one line */
626 		if (parse_line(cp, p) < 0) {
627 			fprintf(stderr, "parse error at line %d of %s\n", line, cp->file);
628 		}
629 next_line:
630 		line++;
631 		free(lp);
632 	}
633 
634 	fclose(fp);
635 	return 0;
636 }
637 
638 void
639 spdk_conf_set_as_default(struct spdk_conf *cp)
640 {
641 	default_config = cp;
642 }
643