numam-spdk/lib/conf/conf.c

683 lines
12 KiB
C
Raw Normal View History

/*-
* BSD LICENSE
*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spdk/stdinc.h"
#include "spdk/conf.h"
#include "spdk/string.h"
#include "spdk/log.h"
struct spdk_conf_value {
struct spdk_conf_value *next;
char *value;
};
struct spdk_conf_item {
struct spdk_conf_item *next;
char *key;
struct spdk_conf_value *val;
};
struct spdk_conf_section {
struct spdk_conf_section *next;
char *name;
int num;
struct spdk_conf_item *item;
};
struct spdk_conf {
char *file;
struct spdk_conf_section *current_section;
struct spdk_conf_section *section;
};
#define CF_DELIM " \t"
#define LIB_MAX_TMPBUF 1024
static struct spdk_conf *default_config = NULL;
struct spdk_conf *
spdk_conf_allocate(void)
{
return calloc(1, sizeof(struct spdk_conf));
}
static void
free_conf_value(struct spdk_conf_value *vp)
{
if (vp == NULL) {
return;
}
if (vp->value) {
free(vp->value);
}
free(vp);
}
static void
free_all_conf_value(struct spdk_conf_value *vp)
{
struct spdk_conf_value *next;
if (vp == NULL) {
return;
}
while (vp != NULL) {
next = vp->next;
free_conf_value(vp);
vp = next;
}
}
static void
free_conf_item(struct spdk_conf_item *ip)
{
if (ip == NULL) {
return;
}
if (ip->val != NULL) {
free_all_conf_value(ip->val);
}
if (ip->key != NULL) {
free(ip->key);
}
free(ip);
}
static void
free_all_conf_item(struct spdk_conf_item *ip)
{
struct spdk_conf_item *next;
if (ip == NULL) {
return;
}
while (ip != NULL) {
next = ip->next;
free_conf_item(ip);
ip = next;
}
}
static void
free_conf_section(struct spdk_conf_section *sp)
{
if (sp == NULL) {
return;
}
if (sp->item) {
free_all_conf_item(sp->item);
}
if (sp->name) {
free(sp->name);
}
free(sp);
}
static void
free_all_conf_section(struct spdk_conf_section *sp)
{
struct spdk_conf_section *next;
if (sp == NULL) {
return;
}
while (sp != NULL) {
next = sp->next;
free_conf_section(sp);
sp = next;
}
}
void
spdk_conf_free(struct spdk_conf *cp)
{
if (cp == NULL) {
return;
}
if (cp->section != NULL) {
free_all_conf_section(cp->section);
}
if (cp->file != NULL) {
free(cp->file);
}
free(cp);
}
static struct spdk_conf_section *
allocate_cf_section(void)
{
return calloc(1, sizeof(struct spdk_conf_section));
}
static struct spdk_conf_item *
allocate_cf_item(void)
{
return calloc(1, sizeof(struct spdk_conf_item));
}
static struct spdk_conf_value *
allocate_cf_value(void)
{
return calloc(1, sizeof(struct spdk_conf_value));
}
#define CHECK_CP_OR_USE_DEFAULT(cp) (((cp) == NULL) && (default_config != NULL)) ? default_config : (cp)
struct spdk_conf_section *
spdk_conf_find_section(struct spdk_conf *cp, const char *name)
{
struct spdk_conf_section *sp;
if (name == NULL || name[0] == '\0') {
return NULL;
}
cp = CHECK_CP_OR_USE_DEFAULT(cp);
if (cp == NULL) {
return NULL;
}
for (sp = cp->section; sp != NULL; sp = sp->next) {
if (sp->name != NULL && sp->name[0] == name[0]
&& strcasecmp(sp->name, name) == 0) {
return sp;
}
}
return NULL;
}
struct spdk_conf_section *
spdk_conf_first_section(struct spdk_conf *cp)
{
cp = CHECK_CP_OR_USE_DEFAULT(cp);
if (cp == NULL) {
return NULL;
}
return cp->section;
}
struct spdk_conf_section *
spdk_conf_next_section(struct spdk_conf_section *sp)
{
if (sp == NULL) {
return NULL;
}
return sp->next;
}
static void
append_cf_section(struct spdk_conf *cp, struct spdk_conf_section *sp)
{
struct spdk_conf_section *last;
cp = CHECK_CP_OR_USE_DEFAULT(cp);
if (cp == NULL) {
SPDK_ERRLOG("cp == NULL\n");
return;
}
if (cp->section == NULL) {
cp->section = sp;
return;
}
for (last = cp->section; last->next != NULL; last = last->next)
;
last->next = sp;
}
static struct spdk_conf_item *
find_cf_nitem(struct spdk_conf_section *sp, const char *key, int idx)
{
struct spdk_conf_item *ip;
int i;
if (key == NULL || key[0] == '\0') {
return NULL;
}
i = 0;
for (ip = sp->item; ip != NULL; ip = ip->next) {
if (ip->key != NULL && ip->key[0] == key[0]
&& strcasecmp(ip->key, key) == 0) {
if (i == idx) {
return ip;
}
i++;
}
}
return NULL;
}
static void
append_cf_item(struct spdk_conf_section *sp, struct spdk_conf_item *ip)
{
struct spdk_conf_item *last;
if (sp == NULL) {
return;
}
if (sp->item == NULL) {
sp->item = ip;
return;
}
for (last = sp->item; last->next != NULL; last = last->next)
;
last->next = ip;
}
static void
append_cf_value(struct spdk_conf_item *ip, struct spdk_conf_value *vp)
{
struct spdk_conf_value *last;
if (ip == NULL) {
return;
}
if (ip->val == NULL) {
ip->val = vp;
return;
}
for (last = ip->val; last->next != NULL; last = last->next)
;
last->next = vp;
}
bool
spdk_conf_section_match_prefix(const struct spdk_conf_section *sp, const char *name_prefix)
{
return strncasecmp(sp->name, name_prefix, strlen(name_prefix)) == 0;
}
const char *
spdk_conf_section_get_name(const struct spdk_conf_section *sp)
{
return sp->name;
}
int
spdk_conf_section_get_num(const struct spdk_conf_section *sp)
{
return sp->num;
}
char *
spdk_conf_section_get_nmval(struct spdk_conf_section *sp, const char *key, int idx1, int idx2)
{
struct spdk_conf_item *ip;
struct spdk_conf_value *vp;
int i;
ip = find_cf_nitem(sp, key, idx1);
if (ip == NULL) {
return NULL;
}
vp = ip->val;
if (vp == NULL) {
return NULL;
}
for (i = 0; vp != NULL; vp = vp->next, i++) {
if (i == idx2) {
return vp->value;
}
}
return NULL;
}
char *
spdk_conf_section_get_nval(struct spdk_conf_section *sp, const char *key, int idx)
{
struct spdk_conf_item *ip;
struct spdk_conf_value *vp;
ip = find_cf_nitem(sp, key, idx);
if (ip == NULL) {
return NULL;
}
vp = ip->val;
if (vp == NULL) {
return NULL;
}
return vp->value;
}
char *
spdk_conf_section_get_val(struct spdk_conf_section *sp, const char *key)
{
return spdk_conf_section_get_nval(sp, key, 0);
}
int
spdk_conf_section_get_intval(struct spdk_conf_section *sp, const char *key)
{
const char *v;
int value;
v = spdk_conf_section_get_nval(sp, key, 0);
if (v == NULL) {
return -1;
}
value = (int)strtol(v, NULL, 10);
return value;
}
bool
spdk_conf_section_get_boolval(struct spdk_conf_section *sp, const char *key, bool default_val)
{
const char *v;
v = spdk_conf_section_get_nval(sp, key, 0);
if (v == NULL) {
return default_val;
}
if (!strcasecmp(v, "Yes") || !strcasecmp(v, "Y") || !strcasecmp(v, "True")) {
return true;
}
if (!strcasecmp(v, "No") || !strcasecmp(v, "N") || !strcasecmp(v, "False")) {
return false;
}
return default_val;
}
static int
parse_line(struct spdk_conf *cp, char *lp)
{
struct spdk_conf_section *sp;
struct spdk_conf_item *ip;
struct spdk_conf_value *vp;
char *arg;
char *key;
char *val;
char *p;
int num;
arg = spdk_str_trim(lp);
if (arg == NULL) {
SPDK_ERRLOG("no section\n");
return -1;
}
if (arg[0] == '[') {
/* section */
arg++;
key = spdk_strsepq(&arg, "]");
if (key == NULL || arg != NULL) {
SPDK_ERRLOG("broken section\n");
return -1;
}
/* determine section number */
for (p = key; *p != '\0' && !isdigit((int) *p); p++)
;
if (*p != '\0') {
num = (int)strtol(p, NULL, 10);
} else {
num = 0;
}
sp = spdk_conf_find_section(cp, key);
if (sp == NULL) {
sp = allocate_cf_section();
append_cf_section(cp, sp);
}
cp->current_section = sp;
sp->name = strdup(key);
if (sp->name == NULL) {
perror("strdup sp->name");
return -1;
}
sp->num = num;
} else {
/* parameters */
sp = cp->current_section;
if (sp == NULL) {
SPDK_ERRLOG("unknown section\n");
return -1;
}
key = spdk_strsepq(&arg, CF_DELIM);
if (key == NULL) {
SPDK_ERRLOG("broken key\n");
return -1;
}
ip = allocate_cf_item();
if (ip == NULL) {
SPDK_ERRLOG("cannot allocate cf item\n");
return -1;
}
append_cf_item(sp, ip);
ip->key = strdup(key);
if (ip->key == NULL) {
perror("strdup ip->key");
return -1;
}
ip->val = NULL;
if (arg != NULL) {
/* key has value(s) */
while (arg != NULL) {
val = spdk_strsepq(&arg, CF_DELIM);
vp = allocate_cf_value();
if (vp == NULL) {
SPDK_ERRLOG("cannot allocate cf value\n");
return -1;
}
append_cf_value(ip, vp);
vp->value = strdup(val);
if (vp->value == NULL) {
perror("strdup vp->value");
return -1;
}
}
}
}
return 0;
}
static char *
fgets_line(FILE *fp)
{
char *dst, *dst2, *p;
size_t total, len;
dst = p = malloc(LIB_MAX_TMPBUF);
if (!dst) {
return NULL;
}
dst[0] = '\0';
total = 0;
while (fgets(p, LIB_MAX_TMPBUF, fp) != NULL) {
len = strlen(p);
total += len;
if (len + 1 < LIB_MAX_TMPBUF || dst[total - 1] == '\n') {
dst2 = realloc(dst, total + 1);
if (!dst2) {
free(dst);
return NULL;
} else {
return dst2;
}
}
dst2 = realloc(dst, total + LIB_MAX_TMPBUF);
if (!dst2) {
free(dst);
return NULL;
} else {
dst = dst2;
}
p = dst + total;
}
if (feof(fp) && total != 0) {
dst2 = realloc(dst, total + 2);
if (!dst2) {
free(dst);
return NULL;
} else {
dst = dst2;
}
dst[total] = '\n';
dst[total + 1] = '\0';
return dst;
}
free(dst);
return NULL;
}
int
spdk_conf_read(struct spdk_conf *cp, const char *file)
{
FILE *fp;
char *lp, *p;
char *lp2, *q;
int line;
int n, n2;
if (file == NULL || file[0] == '\0') {
return -1;
}
fp = fopen(file, "r");
if (fp == NULL) {
SPDK_ERRLOG("open error: %s\n", file);
return -1;
}
cp->file = strdup(file);
if (cp->file == NULL) {
perror("strdup cp->file");
fclose(fp);
return -1;
}
line = 1;
while ((lp = fgets_line(fp)) != NULL) {
/* skip spaces */
for (p = lp; *p != '\0' && isspace((int) *p); p++)
;
/* skip comment, empty line */
if (p[0] == '#' || p[0] == '\0') {
goto next_line;
}
/* concatenate line end with '\' */
n = strlen(p);
while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') {
n -= 2;
lp2 = fgets_line(fp);
if (lp2 == NULL) {
break;
}
line++;
n2 = strlen(lp2);
q = malloc(n + n2 + 1);
if (!q) {
free(lp2);
free(lp);
SPDK_ERRLOG("malloc failed at line %d of %s\n", line, cp->file);
fclose(fp);
return -1;
}
memcpy(q, p, n);
memcpy(q + n, lp2, n2);
q[n + n2] = '\0';
free(lp2);
free(lp);
p = lp = q;
n += n2;
}
/* parse one line */
if (parse_line(cp, p) < 0) {
SPDK_ERRLOG("parse error at line %d of %s\n", line, cp->file);
}
next_line:
line++;
free(lp);
}
fclose(fp);
return 0;
}
void
spdk_conf_set_as_default(struct spdk_conf *cp)
{
default_config = cp;
}