mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
2988fa3c21
Helping with understanding we will not try to deref NULL pointer, as if the sizes are initialized to NULL it also means 'mem' would be NULL, but thats too hard to model so make it obvious.
399 lines
8.7 KiB
C
399 lines
8.7 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
|
|
*/
|
|
|
|
#define _REENTRANT
|
|
|
|
#include "tool.h"
|
|
|
|
#include "daemon-io.h"
|
|
#include "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;
|
|
|
|
dm_free(append);
|
|
}
|
|
|
|
return 1;
|
|
fail:
|
|
dm_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_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, "%d") || !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 = dm_realloc(buf->mem, buf->allocated);
|
|
if (new)
|
|
buf->mem = new;
|
|
else { /* utter failure */
|
|
dm_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)
|
|
{
|
|
dm_free(buf->mem);
|
|
buffer_init(buf);
|
|
}
|
|
|
|
void buffer_init(struct buffer *buf)
|
|
{
|
|
buf->allocated = buf->used = 0;
|
|
buf->mem = 0;
|
|
}
|