mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
0b19387dae
Ensure configure.h is always 1st. included header. Maybe we could eventually introduce gcc -include option, but for now this better uses dependency tracking. Also move _REENTRANT and _GNU_SOURCE into configure.h so it doesn't need to be present in various source files. This ensures consistent compilation of headers like stdio.h since it may produce different declaration.
412 lines
9.0 KiB
C
412 lines
9.0 KiB
C
/*
|
|
* Copyright (C) 2011-2012 Red Hat, Inc.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "tools/tool.h"
|
|
|
|
#include "daemon-io.h"
|
|
#include "device_mapper/misc/dm-logging.h"
|
|
|
|
#include <math.h> /* fabs() */
|
|
#include <float.h> /* DBL_EPSILON */
|
|
|
|
int buffer_append_vf(struct buffer *buf, va_list ap)
|
|
{
|
|
char *append;
|
|
char *next;
|
|
int keylen;
|
|
int64_t value;
|
|
char *string;
|
|
char *block;
|
|
|
|
while ((next = va_arg(ap, char *))) {
|
|
append = NULL;
|
|
if (!strchr(next, '=')) {
|
|
log_error(INTERNAL_ERROR "Bad format string at '%s'", next);
|
|
goto fail;
|
|
}
|
|
keylen = strchr(next, '=') - next;
|
|
if (strstr(next, "%d")) {
|
|
/* Use of plain %d is prohibited, use FMTd64 */
|
|
log_error(INTERNAL_ERROR "Do not use %%d and use correct 64bit form");
|
|
goto fail;
|
|
}
|
|
if (strstr(next, FMTd64)) {
|
|
value = va_arg(ap, int64_t);
|
|
if (dm_asprintf(&append, "%.*s= %" PRId64 "\n", keylen, next, value) < 0)
|
|
goto fail;
|
|
} else if (strstr(next, "%s")) {
|
|
string = va_arg(ap, char *);
|
|
if (dm_asprintf(&append, "%.*s= \"%s\"\n", keylen, next, string) < 0)
|
|
goto fail;
|
|
} else if (strstr(next, "%b")) {
|
|
if (!(block = va_arg(ap, char *)))
|
|
continue;
|
|
if (dm_asprintf(&append, "%.*s%s", keylen, next, block) < 0)
|
|
goto fail;
|
|
} else if (dm_asprintf(&append, "%s", next) < 0)
|
|
goto fail;
|
|
|
|
if (!append ||
|
|
!buffer_append(buf, append))
|
|
goto fail;
|
|
|
|
free(append);
|
|
}
|
|
|
|
return 1;
|
|
fail:
|
|
free(append);
|
|
return 0;
|
|
}
|
|
|
|
int buffer_append_f(struct buffer *buf, ...)
|
|
{
|
|
int res;
|
|
va_list ap;
|
|
|
|
va_start(ap, buf);
|
|
res = buffer_append_vf(buf, ap);
|
|
va_end(ap);
|
|
|
|
return res;
|
|
}
|
|
|
|
int set_flag(struct dm_config_tree *cft, struct dm_config_node *parent,
|
|
const char *field, const char *flag, int want)
|
|
{
|
|
struct dm_config_value *value = NULL, *pred = NULL;
|
|
struct dm_config_node *node = dm_config_find_node(parent->child, field);
|
|
struct dm_config_value *new;
|
|
|
|
if (node)
|
|
value = node->v;
|
|
|
|
while (value && value->type != DM_CFG_EMPTY_ARRAY && strcmp(value->v.str, flag)) {
|
|
pred = value;
|
|
value = value->next;
|
|
}
|
|
|
|
if (value && want)
|
|
return 1;
|
|
|
|
if (!value && !want)
|
|
return 1;
|
|
|
|
if (value && !want) {
|
|
if (pred) {
|
|
pred->next = value->next;
|
|
} else if (value == node->v && value->next) {
|
|
node->v = value->next;
|
|
} else {
|
|
node->v->type = DM_CFG_EMPTY_ARRAY;
|
|
}
|
|
}
|
|
|
|
if (!value && want) {
|
|
if (!node) {
|
|
if (!(node = dm_config_create_node(cft, field)))
|
|
return 0;
|
|
node->sib = parent->child;
|
|
if (!(node->v = dm_config_create_value(cft)))
|
|
return 0;
|
|
node->v->type = DM_CFG_EMPTY_ARRAY;
|
|
node->parent = parent;
|
|
parent->child = node;
|
|
}
|
|
if (!(new = dm_config_create_value(cft))) {
|
|
/* FIXME error reporting */
|
|
return 0;
|
|
}
|
|
new->type = DM_CFG_STRING;
|
|
new->v.str = flag;
|
|
new->next = node->v;
|
|
node->v = new;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void chain_node(struct dm_config_node *cn,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib)
|
|
{
|
|
cn->parent = parent;
|
|
cn->sib = NULL;
|
|
|
|
if (parent && parent->child && !pre_sib) { /* find the last one */
|
|
pre_sib = parent->child;
|
|
while (pre_sib && pre_sib->sib)
|
|
pre_sib = pre_sib->sib;
|
|
}
|
|
|
|
if (parent && !parent->child)
|
|
parent->child = cn;
|
|
if (pre_sib) {
|
|
cn->sib = pre_sib->sib;
|
|
pre_sib->sib = cn;
|
|
}
|
|
|
|
}
|
|
|
|
struct dm_config_tree *config_tree_from_string_without_dup_node_check(const char *config_settings)
|
|
{
|
|
struct dm_config_tree *cft;
|
|
|
|
if (!(cft = dm_config_create()))
|
|
return_NULL;
|
|
|
|
if (!dm_config_parse_without_dup_node_check(cft, config_settings, config_settings + strlen(config_settings))) {
|
|
dm_config_destroy(cft);
|
|
return_NULL;
|
|
}
|
|
|
|
return cft;
|
|
}
|
|
|
|
struct dm_config_node *make_config_node(struct dm_config_tree *cft,
|
|
const char *key,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib)
|
|
{
|
|
struct dm_config_node *cn;
|
|
|
|
if (!(cn = dm_config_create_node(cft, key)))
|
|
return NULL;
|
|
|
|
cn->v = NULL;
|
|
cn->child = NULL;
|
|
|
|
chain_node(cn, parent, pre_sib);
|
|
|
|
return cn;
|
|
}
|
|
|
|
struct dm_config_node *make_text_node(struct dm_config_tree *cft,
|
|
const char *key,
|
|
const char *value,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib)
|
|
{
|
|
struct dm_config_node *cn;
|
|
|
|
if (!(cn = make_config_node(cft, key, parent, pre_sib)) ||
|
|
!(cn->v = dm_config_create_value(cft)))
|
|
return NULL;
|
|
|
|
cn->v->type = DM_CFG_STRING;
|
|
cn->v->v.str = value;
|
|
return cn;
|
|
}
|
|
|
|
struct dm_config_node *make_int_node(struct dm_config_tree *cft,
|
|
const char *key,
|
|
int64_t value,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib)
|
|
{
|
|
struct dm_config_node *cn;
|
|
|
|
if (!(cn = make_config_node(cft, key, parent, pre_sib)) ||
|
|
!(cn->v = dm_config_create_value(cft)))
|
|
return NULL;
|
|
|
|
cn->v->type = DM_CFG_INT;
|
|
cn->v->v.i = value;
|
|
return cn;
|
|
}
|
|
|
|
/*
|
|
* FIXME: return 1 even if VA list is empty and return the
|
|
* dm_config_node* result as output parameter
|
|
*/
|
|
struct dm_config_node *config_make_nodes_v(struct dm_config_tree *cft,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib,
|
|
va_list ap)
|
|
{
|
|
const char *next;
|
|
struct dm_config_node *first = NULL;
|
|
struct dm_config_node *cn;
|
|
const char *fmt;
|
|
char *key;
|
|
|
|
while ((next = va_arg(ap, char *))) {
|
|
cn = NULL;
|
|
fmt = strchr(next, '=');
|
|
|
|
if (!fmt) {
|
|
log_error(INTERNAL_ERROR "Bad format string '%s'", fmt);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(key = dm_pool_strdup(cft->mem, next))) {
|
|
log_error("Failed to duplicate node key.");
|
|
return NULL;
|
|
}
|
|
|
|
key[fmt - next] = '\0';
|
|
fmt += 2;
|
|
|
|
if (!strcmp(fmt, FMTd64)) {
|
|
int64_t value = va_arg(ap, int64_t);
|
|
if (!(cn = make_int_node(cft, key, value, parent, pre_sib)))
|
|
return 0;
|
|
} else if (!strcmp(fmt, "%s")) {
|
|
char *value = va_arg(ap, char *);
|
|
if (!(cn = make_text_node(cft, key, value, parent, pre_sib)))
|
|
return 0;
|
|
} else if (!strcmp(fmt, "%t")) {
|
|
struct dm_config_tree *tree = va_arg(ap, struct dm_config_tree *);
|
|
cn = dm_config_clone_node(cft, tree->root, 1);
|
|
if (!cn)
|
|
return 0;
|
|
cn->key = key;
|
|
chain_node(cn, parent, pre_sib);
|
|
} else {
|
|
log_error(INTERNAL_ERROR "Bad format string '%s'", fmt);
|
|
return NULL;
|
|
}
|
|
if (!first)
|
|
first = cn;
|
|
if (cn)
|
|
pre_sib = cn;
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
struct dm_config_node *config_make_nodes(struct dm_config_tree *cft,
|
|
struct dm_config_node *parent,
|
|
struct dm_config_node *pre_sib,
|
|
...)
|
|
{
|
|
struct dm_config_node *res;
|
|
va_list ap;
|
|
|
|
va_start(ap, pre_sib);
|
|
res = config_make_nodes_v(cft, parent, pre_sib, ap);
|
|
va_end(ap);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Test if the doubles are close enough to be considered equal */
|
|
static int _close_enough(double d1, double d2)
|
|
{
|
|
return fabs(d1 - d2) < DBL_EPSILON;
|
|
}
|
|
|
|
int compare_value(struct dm_config_value *a, struct dm_config_value *b)
|
|
{
|
|
int r = 0;
|
|
|
|
if (a->type > b->type)
|
|
return 1;
|
|
if (a->type < b->type)
|
|
return -1;
|
|
|
|
switch (a->type) {
|
|
case DM_CFG_STRING: r = strcmp(a->v.str, b->v.str); break;
|
|
case DM_CFG_FLOAT: r = _close_enough(a->v.f, b->v.f) ? 0 : (a->v.f > b->v.f) ? 1 : -1; break;
|
|
case DM_CFG_INT: r = (a->v.i == b->v.i) ? 0 : (a->v.i > b->v.i) ? 1 : -1; break;
|
|
case DM_CFG_EMPTY_ARRAY: return 0;
|
|
}
|
|
|
|
if (r == 0 && a->next && b->next)
|
|
r = compare_value(a->next, b->next);
|
|
return r;
|
|
}
|
|
|
|
int compare_config(struct dm_config_node *a, struct dm_config_node *b)
|
|
{
|
|
int result = 0;
|
|
if (a->v && b->v)
|
|
result = compare_value(a->v, b->v);
|
|
if (a->v && !b->v)
|
|
result = 1;
|
|
if (!a->v && b->v)
|
|
result = -1;
|
|
if (a->child && b->child)
|
|
result = compare_config(a->child, b->child);
|
|
|
|
if (result) {
|
|
// DEBUGLOG("config inequality at %s / %s", a->key, b->key);
|
|
return result;
|
|
}
|
|
|
|
if (a->sib && b->sib)
|
|
result = compare_config(a->sib, b->sib);
|
|
if (a->sib && !b->sib)
|
|
result = 1;
|
|
if (!a->sib && b->sib)
|
|
result = -1;
|
|
|
|
return result;
|
|
}
|
|
|
|
int buffer_realloc(struct buffer *buf, int needed)
|
|
{
|
|
char *new;
|
|
int alloc = buf->allocated;
|
|
if (alloc < needed)
|
|
alloc = needed;
|
|
|
|
buf->allocated += alloc;
|
|
new = realloc(buf->mem, buf->allocated);
|
|
if (new)
|
|
buf->mem = new;
|
|
else { /* utter failure */
|
|
free(buf->mem);
|
|
buf->mem = 0;
|
|
buf->allocated = buf->used = 0;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int buffer_append(struct buffer *buf, const char *string)
|
|
{
|
|
int len = strlen(string);
|
|
|
|
if ((!buf->mem || (buf->allocated - buf->used <= len)) &&
|
|
!buffer_realloc(buf, len + 1))
|
|
return 0;
|
|
|
|
strcpy(buf->mem + buf->used, string);
|
|
buf->used += len;
|
|
return 1;
|
|
}
|
|
|
|
int buffer_line(const char *line, void *baton)
|
|
{
|
|
struct buffer *buf = baton;
|
|
if (!buffer_append(buf, line))
|
|
return 0;
|
|
if (!buffer_append(buf, "\n"))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void buffer_destroy(struct buffer *buf)
|
|
{
|
|
free(buf->mem);
|
|
buffer_init(buf);
|
|
}
|
|
|
|
void buffer_init(struct buffer *buf)
|
|
{
|
|
buf->allocated = buf->used = 0;
|
|
buf->mem = 0;
|
|
}
|