800b18d028
Add disable_sections_merge() procedure that will allow to have multiple sections with a same name. This behaviour is how FIO treats such sections and so will be used in bdevperf config file. Change-Id: If221daeb7753d91b5d2608d25ccbb16f2d43ccce Signed-off-by: Vitaliy Mysak <vitaliy.mysak@intel.com> Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/3433 Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: Mellanox Build Bot
705 lines
12 KiB
C
705 lines
12 KiB
C
/*-
|
|
* 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;
|
|
bool merge_sections;
|
|
};
|
|
|
|
#define CF_DELIM " \t"
|
|
#define CF_DELIM_KEY " \t="
|
|
|
|
#define LIB_MAX_TMPBUF 1024
|
|
|
|
static struct spdk_conf *default_config = NULL;
|
|
|
|
struct spdk_conf *
|
|
spdk_conf_allocate(void)
|
|
{
|
|
struct spdk_conf *ret = calloc(1, sizeof(struct spdk_conf));
|
|
|
|
if (ret) {
|
|
ret->merge_sections = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)spdk_strtol(v, 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)spdk_strtol(p, 10);
|
|
} else {
|
|
num = 0;
|
|
}
|
|
|
|
if (cp->merge_sections) {
|
|
sp = spdk_conf_find_section(cp, key);
|
|
} else {
|
|
sp = NULL;
|
|
}
|
|
|
|
if (sp == NULL) {
|
|
sp = allocate_cf_section();
|
|
append_cf_section(cp, sp);
|
|
|
|
sp->name = strdup(key);
|
|
if (sp->name == NULL) {
|
|
SPDK_ERRLOG("cannot duplicate %s to sp->name\n", key);
|
|
return -1;
|
|
}
|
|
}
|
|
cp->current_section = sp;
|
|
|
|
|
|
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_KEY);
|
|
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) {
|
|
SPDK_ERRLOG("cannot make duplicate of %s\n", 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) {
|
|
SPDK_ERRLOG("cannot duplicate %s to vp->value\n", val);
|
|
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;
|
|
}
|
|
SPDK_ERRLOG("INI configuration has been deprecated and will be removed in a future release. Please switch to JSON-RPC.\n");
|
|
|
|
fp = fopen(file, "r");
|
|
if (fp == NULL) {
|
|
SPDK_ERRLOG("open error: %s\n", file);
|
|
return -1;
|
|
}
|
|
|
|
cp->file = strdup(file);
|
|
if (cp->file == NULL) {
|
|
SPDK_ERRLOG("cannot duplicate %s to cp->file\n", 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;
|
|
}
|
|
|
|
void
|
|
spdk_conf_disable_sections_merge(struct spdk_conf *cp)
|
|
{
|
|
cp->merge_sections = false;
|
|
}
|