diff --git a/lib/config/config.c b/lib/config/config.c new file mode 100644 index 000000000..5eb250ff2 --- /dev/null +++ b/lib/config/config.c @@ -0,0 +1,618 @@ +/* + * tools/lib/lvm_config.c + * + * Copyright (C) 2001 Sistina Software + * + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + * + */ + +/* + * Changelog + * + * 17/04/2001 - First version [Joe Thornber] + * + */ + +#include "config.h" +#include "pool.h" +#include "log.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + TOK_INT, + TOK_FLOAT, + TOK_STRING, + TOK_EQ, + TOK_SECTION_B, + TOK_SECTION_E, + TOK_ARRAY_B, + TOK_ARRAY_E, + TOK_IDENTIFIER, + TOK_COMMA, + TOK_EOF +}; + +struct parser { + const char *fb, *fe; /* file limits */ + + int t; /* token limits and type */ + const char *tb, *te; + + int fd; /* descriptor for file being parsed */ + int line; /* line number we are on */ + + struct pool *mem; +}; + +struct cs { + struct config_file cf; + struct pool *mem; +}; + +static void _get_token(struct parser *p); +static void _eat_space(struct parser *p); +static struct config_node *_file(struct parser *p); +static struct config_node *_section(struct parser *p); +static struct config_value *_value(struct parser *p); +static struct config_value *_type(struct parser *p); +static void _parse_error(struct parser *p, const char *file, int line, + const char *mess); +static int _match_aux(struct parser *p, int t); +static struct config_value *_create_value(struct parser *p); +static struct config_node *_create_node(struct parser *p); +static char *_dup_tok(struct parser *p); +static int _tok_match(const char *str, const char *b, const char *e); + +#define MAX_INDENT 32 + +#define match(t) do {\ + if (!_match_aux(p, (t))) {\ + _parse_error(p, __FILE__, __LINE__, "unexpected token"); \ + return 0;\ + } \ +} while(0); + +/* + * public interface + */ +struct config_file *create_config_file() +{ + struct cs *c; + struct pool *mem = create_pool(10 * 1024); + + if (!mem) { + stack; + return 0; + } + + if (!(c = pool_alloc(mem, sizeof(*c)))) { + stack; + destroy_pool(mem); + return 0; + } + + c->mem = mem; + return &c->cf; +} + +void destroy_config_file(struct config_file *cf) +{ + destroy_pool(((struct cs *) cf)->mem); +} + +int read_config(struct config_file *cf, const char *file) +{ + struct cs *c = (struct cs *) cf; + struct parser *p; + struct stat info; + int r = 1, fd; + + if (!(p = pool_alloc(c->mem, sizeof(*p)))) { + stack; + return 0; + } + p->mem = c->mem; + + /* memory map the file */ + if (stat(file, &info) || S_ISDIR(info.st_mode)) { + log_sys_err("stat"); + return 0; + } + + if ((fd = open(file, O_RDONLY)) < 0) { + log_sys_err("open"); + return 0; + } + + p->fb = mmap((caddr_t) 0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (p->fb == MAP_FAILED) { + log_sys_err("mmap"); + close(fd); + return 0; + } + p->fe = p->fb + info.st_size; + + /* parse */ + p->tb = p->te = p->fb; + p->line = 1; + _get_token(p); + if (!(cf->root = _file(p))) { + stack; + r = 0; + } + + /* unmap the file */ + if (munmap((char *) p->fb, info.st_size)) { + log_sys_err("munmap failed"); + r = 0; + } + + close(fd); + return r; +} + +static void _write_value(FILE *fp, struct config_value *v) +{ + switch (v->type) { + case CFG_STRING: + fprintf(fp, "\"%s\"", v->v.str); + break; + + case CFG_FLOAT: + fprintf(fp, "%f", v->v.r); + break; + + case CFG_INT: + fprintf(fp, "%d", v->v.i); + break; + } +} + +static int _write_config(struct config_node *n, FILE *fp, int level) +{ + char space[MAX_INDENT + 1]; + int l = (level < MAX_INDENT) ? level : MAX_INDENT; + int i; + + if (!n) + return 1; + + for (i = 0; i < l; i++) + space[i] = ' '; + space[i] = '\0'; + + while (n) { + fprintf(fp, "%s%s", space, n->key); + if (!n->v) { + /* it's a sub section */ + fprintf(fp, " {\n"); + _write_config(n->child, fp, level + 1); + fprintf(fp, "%s}", space); + } else { + /* it's a value */ + struct config_value *v = n->v; + fprintf(fp, "="); + if (v->next) { + fprintf(fp, "["); + while (v) { + _write_value(fp, v); + v = v->next; + if (v) + fprintf(fp, ", "); + } + fprintf(fp, "]"); + } else + _write_value(fp, v); + } + fprintf(fp, "\n"); + n = n->sib; + } + /* FIXME: add error checking */ + return 1; +} + +int write_config(struct config_file *cf, const char *file) +{ + int r = 1; + FILE *fp = fopen(file, "w"); + if (!fp) { + log_sys_err("open"); + return 0; + } + + if (!_write_config(cf->root, fp, 0)) { + stack; + r = 0; + } + fclose(fp); + return r; +} + +/* + * parser + */ +static struct config_node *_file(struct parser *p) +{ + struct config_node *root = 0, *n, *l; + while (p->t != TOK_EOF) { + if (!(n = _section(p))) { + stack; + return 0; + } + + if (!root) + root = n; + else + l->sib = n; + l = n; + } + return root; +} + +static struct config_node *_section(struct parser *p) +{ + /* IDENTIFIER '{' VALUE* '}' */ + struct config_node *root, *n, *l; + if (!(root = _create_node(p))) { + stack; + return 0; + } + + if (!(root->key = _dup_tok(p))) { + stack; + return 0; + } + + match (TOK_IDENTIFIER); + + if (p->t == TOK_SECTION_B) { + match(TOK_SECTION_B); + while (p->t != TOK_SECTION_E) { + if (!(n = _section(p))) { + stack; + return 0; + } + + if (!root->child) + root->child = n; + else + l->sib = n; + l = n; + } + match(TOK_SECTION_E); + } else { + match(TOK_EQ); + if (!(root->v = _value(p))) { + stack; + return 0; + } + } + + return root; +} + +static struct config_value *_value(struct parser *p) +{ + /* '[' TYPE* ']' | TYPE */ + struct config_value *h = 0, *l, *ll = 0; + if (p->t == TOK_ARRAY_B) { + match (TOK_ARRAY_B); + while (p->t != TOK_ARRAY_E) { + if (!(l = _type(p))) { + stack; + return 0; + } + + if (!h) + h = l; + else + ll->next = l; + ll = l; + + if (p->t == TOK_COMMA) + match(TOK_COMMA); + } + match(TOK_ARRAY_E); + } else + h = _type(p); + + return h; +} + +static struct config_value *_type(struct parser *p) { + /* [0-9]+ | [0-9]*\.[0-9]* | ".*" */ + struct config_value *v = _create_value(p); + + switch (p->t) { + case TOK_INT: + v->type = CFG_INT; + v->v.i = strtol(p->tb, 0, 10); /* FIXME: check error */ + match(TOK_INT); + break; + + case TOK_FLOAT: + v->type = CFG_FLOAT; + v->v.r = strtod(p->tb, 0); /* FIXME: check error */ + match(TOK_FLOAT); + break; + + case TOK_STRING: + v->type = CFG_STRING; + + p->tb++, p->te--; /* strip "'s */ + if (!(v->v.str = _dup_tok(p))) { + stack; + return 0; + } + p->te++; + match(TOK_STRING); + break; + + default: + _parse_error(p, __FILE__, __LINE__, "expected a value"); + return 0; + } + return v; +} + +static void _parse_error(struct parser *p, const char *file, int line, + const char *mess) +{ + plog(_LOG_ERR, file, line, "parse error at %d: %s", p->line, mess); +} + +static int _match_aux(struct parser *p, int t) +{ + if (p->t != t) + return 0; + + _get_token(p); + return 1; +} + +/* + * tokeniser + */ +static void _get_token(struct parser *p) +{ + p->tb = p->te; + _eat_space(p); + if (p->tb == p->fe) { + p->t = TOK_EOF; + return; + } + + p->t = TOK_INT; /* fudge so the fall through for + floats works */ + switch (*p->te) { + case '{': + p->t = TOK_SECTION_B; + p->te++; + break; + + case '}': + p->t = TOK_SECTION_E; + p->te++; + break; + + case '[': + p->t = TOK_ARRAY_B; + p->te++; + break; + + case ']': + p->t = TOK_ARRAY_E; + p->te++; + break; + + case ',': + p->t = TOK_COMMA; + p->te++; + break; + + case '=': + p->t = TOK_EQ; + p->te++; + break; + + case '"': + p->t = TOK_STRING; + p->te++; + while ((p->te != p->fe) && (*p->te != '"')) { + if ((*p->te == '\\') && (p->te + 1 != p->fe)) + p->te++; + p->te++; + } + + if (p->te != p->fe) + p->te++; + break; + + case '.': + p->t = TOK_FLOAT; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + p->te++; + while (p->te != p->fe) { + if (*p->te == '.') { + if (p->t == TOK_FLOAT) + break; + p->t = TOK_FLOAT; + } else if (!isdigit((int) *p->te)) + break; + p->te++; + } + break; + + default: + p->t = TOK_IDENTIFIER; + while ((p->te != p->fe) && !isspace(*p->te) && + (*p->te != '#') && (*p->te != '=')) + p->te++; + break; + } +} + +static void _eat_space(struct parser *p) +{ + while (p->tb != p->fe) { + if (*p->te == '#') { + while ((p->te != p->fe) && (*p->te != '\n')) + p->te++; + p->line++; + } + + else if (isspace(*p->te)) { + while ((p->te != p->fe) && isspace(*p->te)) { + if (*p->te == '\n') + p->line++; + p->te++; + } + } + + else + return; + + p->tb = p->te; + } +} + +/* + * memory management + */ +static struct config_value *_create_value(struct parser *p) +{ + struct config_value *v = pool_alloc(p->mem, sizeof(*v)); + memset(v, 0, sizeof(*v)); + return v; +} + +static struct config_node *_create_node(struct parser *p) +{ + struct config_node *n = pool_alloc(p->mem, sizeof(*n)); + memset(n, 0, sizeof(*n)); + return n; +} + +static char *_dup_tok(struct parser *p) +{ + int len = p->te - p->tb; + char *str = pool_alloc(p->mem, len + 1); + if (!str) { + stack; + return 0; + } + strncpy(str, p->tb, len); + str[len] = '\0'; + return str; +} + +/* + * utility functions + */ +struct config_node *find_config_node(struct config_node *cn, + const char *path, char sep) +{ + const char *e; + + while (cn) { + /* trim any leading slashes */ + while (*path && (*path == sep)) + path++; + + /* find the end of this segment */ + for (e = path; *e && (*e != sep); e++) + ; + + /* hunt for the node */ + while (cn) { + if (_tok_match(cn->key, path, e)) + break; + + cn = cn->sib; + } + + if (cn && *e) + cn = cn->child; + else + break; /* don't move into the last node */ + + path = e; + } + + return cn; +} + +const char * +find_config_str(struct config_node *cn, + const char *path, char sep, const char *fail) +{ + struct config_node *n = find_config_node(cn, path, sep); + + if (n && n->v->type == CFG_STRING) + return n->v->v.str; + + return fail; +} + +int find_config_int(struct config_node *cn, const char *path, + char sep, int fail) +{ + struct config_node *n = find_config_node(cn, path, sep); + + if (n && n->v->type == CFG_INT) + return n->v->v.i; + + return fail; +} + +float find_config_float(struct config_node *cn, const char *path, + char sep, float fail) +{ + struct config_node *n = find_config_node(cn, path, sep); + + if (n && n->v->type == CFG_FLOAT) + return n->v->v.r; + + return fail; + +} + +static int _tok_match(const char *str, const char *b, const char *e) +{ + while (*str && (b != e)) { + if (*str++ != *b++) + return 0; + } + + return !(*str || (b != e)); +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/lib/config/config.h b/lib/config/config.h new file mode 100644 index 000000000..7b8c40fdb --- /dev/null +++ b/lib/config/config.h @@ -0,0 +1,76 @@ +/* + * tools/lib/config.h + * + * Copyright (C) 2001 Sistina Software + * + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + */ + +#ifndef LVM_CONFIG_H +#define LVM_CONFIG_H + +enum { + CFG_STRING, + CFG_FLOAT, + CFG_INT, +}; + +struct config_value { + int type; + union { + int i; + float r; + char *str; + } v; + struct config_value *next; /* for arrays */ +}; + +struct config_node { + char *key; + struct config_node *sib, *child; + struct config_value *v; +}; + +struct config_file { + struct config_node *root; +}; + +struct config_file *create_config_file(); +void destroy_config_file(struct config_file *cf); + +int read_config(struct config_file *cf, const char *file); +int write_config(struct config_file *cf, const char *file); + +struct config_node *find_config_node(struct config_node *cn, + const char *path, char seperator); + +const char *find_config_str(struct config_node *cn, + const char *path, char sep, const char *fail); + +int find_config_int(struct config_node *cn, const char *path, + char sep, int fail); + +float find_config_float(struct config_node *cn, const char *path, + char sep, float fail); + +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/lib/log/log.c b/lib/log/log.c new file mode 100644 index 000000000..6f54eba9a --- /dev/null +++ b/lib/log/log.c @@ -0,0 +1,152 @@ +/* + * tools/lib/lvm_log.c + * + * Copyright (C) 2001 Sistina Software + * + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + * + */ + +/* + * Changelog + * + * 22/01/2001 - First version (Joe Thornber) + * + */ + +#include "log.h" +#include +#include + +static FILE *_log = 0; + +static int _verbose_level = 0; +static int _test = 0; +static int _debug_level = 0; +static int _syslog = 0; + +void init_log(FILE *fp) { + _log = fp; +} + +void init_syslog(int facility) { + openlog("lvm", LOG_PID, facility); + _syslog = 1; +} + +void fin_log() { + _log = 0; +} + +void fin_syslog() { + closelog(); + _syslog = 0; +} + +void init_verbose(int level) { + _verbose_level = level; +} + +void init_test(int level) { + _test = level; +} + +int test_mode() { + return _test; +} + +void init_debug(int level) { + _debug_level = level; +} + +int debug_level() { + return _debug_level; +} + +void print_log(int level, const char *file, int line, const char *format, ...) { + va_list ap; + + va_start(ap, format); + switch(level) { + case _LOG_DEBUG: + if (_verbose_level > 2) { + printf(" "); + vprintf(format, ap); + putchar('\n'); + } + break; + + case _LOG_INFO: + if (_verbose_level > 1) { + printf(" "); + vprintf(format, ap); + putchar('\n'); + } + break; + case _LOG_NOTICE: + if (_verbose_level) { + printf(" "); + vprintf(format, ap); + putchar('\n'); + } + break; + case _LOG_WARN: + printf(" "); + vprintf(format, ap); + putchar('\n'); + break; + case _LOG_ERR: + fprintf(stderr, " "); + vfprintf(stderr, format, ap); + fputc('\n',stderr); + break; + case _LOG_FATAL: + vfprintf(stderr, format, ap); + fputc('\n',stderr); + break; + default: + ; + } + va_end(ap); + + if (level > _debug_level) + return; + + if (_log) { + fprintf(_log, "%s:%d ", file, line); + + va_start(ap, format); + vfprintf(_log, format, ap); + va_end(ap); + + fprintf(_log, "\n"); + fflush(_log); + } + + if (_syslog) { + va_start(ap, format); + vsyslog(level, format, ap); + va_end(ap); + } +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ + diff --git a/lib/log/log.h b/lib/log/log.h new file mode 100644 index 000000000..07396a183 --- /dev/null +++ b/lib/log/log.h @@ -0,0 +1,97 @@ +/* + * tools/lib/lvm_log.h + * + * Copyright (C) 2001 Sistina Software + * + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + * + */ + +/* + * Changelog + * + * 22/01/2001 - First version (Joe Thornber) + * 25/04/2001 - Remove some spurious ##s that annoyed newer gcc versions. + * + */ + +#ifndef LVM_LOG_H +#define LVM_LOG_H + +#include + +#define _LOG_DEBUG 7 +#define _LOG_INFO 6 +#define _LOG_NOTICE 5 +#define _LOG_WARN 4 +#define _LOG_ERR 3 +#define _LOG_FATAL 2 + +void init_log(FILE *fp); +void fin_log(); + +void init_syslog(int facility); +void fin_syslog(void); + +void init_verbose(int level); +void init_test(int level); +void init_debug(int level); + +int test_mode(void); +int debug_level(void); + +void print_log(int level, const char *file, int line, const char *format, ...); + +#define plog(l, x...) print_log(l, __FILE__, __LINE__ , ## x) + +#define log_debug(x...) plog(_LOG_DEBUG, x) +#define log_info(x...) plog(_LOG_INFO, x) +#define log_notice(x...) plog(_LOG_NOTICE, x) +#define log_warn(x...) plog(_LOG_WARN, x) +#define log_err(x...) plog(_LOG_ERR, x) +#define log_fatal(x...) plog(_LOG_FATAL, x) +#define log_sys_err(x) log_debug("system call '%s' failed (%s)", \ + x, strerror(errno)) + +#define stack log_debug( "stack trace" ) + +/* + * Macros to use for messages: + * + * log_error - always print to stderr + * log_print - always print to stdout + * log_verbose - print to stdout if verbose is set (-v) + * log_very_verbose - print to stdout if verbose is set twice (-vv) + * log_debug - print to stdout if verbose is set three times (-vvv) + * + * In addition, messages will be logged to file or syslog if they + * are more serious than the log level specified with -d. + * + */ + +#define log_error(fmt, args...) log_err(fmt , ## args) +#define log_print(fmt, args...) log_warn(fmt , ## args) +#define log_verbose(fmt, args...) log_notice(fmt , ## args) +#define log_very_verbose(fmt, args...) log_info(fmt , ## args) + +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/lib/mm/dbg_malloc.c b/lib/mm/dbg_malloc.c new file mode 100644 index 000000000..e15256adc --- /dev/null +++ b/lib/mm/dbg_malloc.c @@ -0,0 +1,214 @@ +/* + * dbg_malloc.c + * + * Copyright (C) 2000, 2001 Sistina Software + * + * lvm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * lvm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Changelog + * + * 9/11/2000 - First version by Joe Thornber + * + * TODO: + * + * Thread safety seems to have fallen out, put lock back in. + */ + +#include +#include +#include + +#include "dbg_malloc.h" +#include "log.h" + +struct memblock { + struct memblock *prev, *next; /* All allocated blocks are linked */ + size_t length; /* Size of the requested block */ + int id; /* Index of the block */ + const char *file; /* File that allocated */ + int line; /* Line that allocated */ + void *magic; /* Address of this block */ +}; + +static struct { + unsigned int blocks, mblocks; + unsigned int bytes, mbytes; + +} _mem_stats = {0, 0, 0, 0}; + +static struct memblock *_head = 0; +static struct memblock *_tail = 0; + +void *malloc_aux(size_t s, const char *file, int line) +{ + struct memblock *nb; + size_t tsize = s + sizeof(*nb) + sizeof(unsigned long); + + if (!(nb = malloc(tsize))) { + log_error("couldn't allocate any memory, size = %u", s); + return 0; + } + + /* set up the file and line info */ + nb->file = file; + nb->line = line; + +#ifdef BOUNDS_CHECK + bounds_check(); +#endif + + /* setup fields */ + nb->magic = nb + 1; + nb->length = s; + nb->id = ++_mem_stats.blocks; + nb->next = 0; + nb->prev = _tail; + + /* link to tail of the list */ + if (!_head) + _head = _tail = nb; + else { + _tail->next = nb; + _tail = nb; + } + + /* stomp a pretty pattern across the new memory + and fill in the boundary bytes */ + { + char *ptr = (char *) (nb + 1); + int i; + for (i = 0; i < s; i++) + *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe; + + for (i = 0; i < sizeof(unsigned long); i++) + *ptr++ = (char) nb->id; + } + + if (_mem_stats.blocks > _mem_stats.mblocks) + _mem_stats.mblocks = _mem_stats.blocks; + + _mem_stats.bytes += s; + if (_mem_stats.bytes > _mem_stats.mbytes) + _mem_stats.mbytes = _mem_stats.bytes; + + return nb + 1; +} + +void free_aux(void *p) +{ + char *ptr; + int i; + struct memblock *mb = ((struct memblock *) p) - 1; + if (!p) + return; + +#ifdef BOUNDS_CHECK + bounds_check(); +#endif + + /* sanity check */ + assert(mb->magic == p); + + /* check data at the far boundary */ + ptr = ((char *) mb) + sizeof(struct memblock) + mb->length; + for (i = 0; i < sizeof(unsigned long); i++) + if(*ptr++ != (char) mb->id) + assert(!"Damage at far end of block"); + + /* have we freed this before ? */ + assert(mb->id != 0); + mb->id = 0; + + /* stomp a different pattern across the memory */ + ptr = ((char *) mb) + sizeof(struct memblock); + for (i = 0; i < mb->length; i++) + *ptr++ = i & 1 ? (char) 0xde : (char) 0xad; + + /* unlink */ + if (mb->prev) + mb->prev->next = mb->next; + else + _head = mb->next; + + if (mb->next) + mb->next->prev = mb->prev; + else + _tail = mb->prev; + + assert(_mem_stats.blocks); + _mem_stats.blocks--; + _mem_stats.bytes -= mb->length; + + /* free the memory */ + free(mb); +} + +void *realloc_aux(void *p, unsigned int s, const char *file, int line) +{ + void *r; + struct memblock *mb = ((struct memblock *) p) - 1; + + r = malloc_aux(s, file, line); + + if (p) { + memcpy(r, p, mb->length); + free_aux(p); + } + + return r; +} + +#ifdef DEBUG_MEM +int dump_memory(void) +{ + unsigned long tot = 0; + struct memblock *mb; + if (_head) + log_info("you have a memory leak:"); + + for (mb = _head; mb; mb = mb->next) { + print_log(_LOG_INFO, mb->file, mb->line, + "block %d at %p, size %d", + mb->id, mb->magic, mb->length); + tot += mb->length; + } + + if (_head) + log_info("%ld bytes leaked in total", tot); + + return 1; +} + +void bounds_check(void) +{ + struct memblock *mb = _head; + while (mb) { + int i; + char *ptr = ((char *) (mb + 1)) + mb->length; + for (i = 0; i < sizeof(unsigned long); i++) + if (*ptr++ != (char) mb->id) + assert(!"Memory smash"); + + mb = mb->next; + } +} +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/lib/mm/dbg_malloc.h b/lib/mm/dbg_malloc.h new file mode 100644 index 000000000..3dbc251d8 --- /dev/null +++ b/lib/mm/dbg_malloc.h @@ -0,0 +1,56 @@ +/* + * debug.h - what a wonderfully original name for a file. + * + * Copyright (C) 2000, 2001 Sistina Software + * + * lvm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * lvm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Changelog + * + * 9/11/2000 - First version by Joe Thornber + * + * TODO: + * + */ + +#ifndef DBG_MALLOC_H +#define DBG_MALLOC_H + +#ifdef DEBUG_MEM +void *malloc_aux(unsigned int s, const char *file, int line); +void free_aux(void *p); +void *realloc_aux(void *p, unsigned int s, const char *file, int line); +int dump_memory(void); +void bounds_check(void); + +#define dbg_malloc(s) malloc_aux((s), __FILE__, __LINE__) +#define dbg_free(p) free_aux(p) +#define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__) +#else +#define dbg_malloc(s) malloc(s) +#define dbg_free(p) free(p) +#define dbg_realloc(p, s) realloc(p, s) +#define dump_memory() +#define bounds_check() +#endif + +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/lib/mm/pool.c b/lib/mm/pool.c new file mode 100644 index 000000000..2df9b956c --- /dev/null +++ b/lib/mm/pool.c @@ -0,0 +1,231 @@ +/* + * tools/lib/pool.c + * + * Copyright (C) 2001 Sistina Software + * + * LVM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LVM; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "pool.h" +#include "dbg_malloc.h" +#include "log.h" + +#include + +struct chunk { + char *begin, *end; + struct chunk *prev; +}; + +struct pool { + struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free + list to stop 'bobbling' */ + size_t chunk_size; + size_t object_len; + unsigned object_alignment; +}; + +void _align_chunk(struct chunk *c, unsigned alignment); +struct chunk *_new_chunk(struct pool *p, size_t s); + +/* FIXME: alignment will need to be 4 to work on SUN boxes */ +#define DEFAULT_ALIGNMENT 4 + +struct pool *create_pool(size_t chunk_hint) +{ + size_t new_size = 1024; + struct pool *p = dbg_malloc(sizeof(*p)); + + if (!p) { + log_err("couldn't create pool"); + return 0; + } + memset(p, 0, sizeof(*p)); + + /* round chunk_hint up to the next power of 2 */ + p->chunk_size = chunk_hint + sizeof(struct chunk); + while (new_size < p->chunk_size) + new_size <<= 1; + p->chunk_size = new_size; + return p; +} + +void destroy_pool(struct pool *p) +{ + struct chunk *c, *pr; + dbg_free(p->spare_chunk); + c = p->chunk; + while (c) { + pr = c->prev; + dbg_free(c); + c = pr; + } + + dbg_free(p); +} + +void *pool_alloc(struct pool *p, size_t s) +{ + return pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT); +} + +void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment) +{ + struct chunk *c = p->chunk; + void *r; + + /* realign begin */ + if (c) + _align_chunk(c, alignment); + + /* have we got room ? */ + if(!c || (c->begin > c->end) || (c->end - c->begin < s)) { + /* allocate new chunk */ + int needed = s + alignment + sizeof(struct chunk); + c = _new_chunk(p, (needed > p->chunk_size) ? + needed : p->chunk_size); + _align_chunk(c, alignment); + } + + r = c->begin; + c->begin += s; + return r; +} + +void pool_free(struct pool *p, void *ptr) +{ + struct chunk *c = p->chunk; + + while (c) { + if (((char *) c < (char *) ptr) && + ((char *) c->end > (char *) ptr)) { + c->begin = ptr; + break; + } + + if (p->spare_chunk) + dbg_free(p->spare_chunk); + p->spare_chunk = c; + c = c->prev; + } + + if (!c) + log_warn("pool_free asked to free a pointer " + "that wasn't in the pool, doing nothing"); + else + p->chunk = c; +} + +void *pool_begin_object(struct pool *p, size_t hint, unsigned align) +{ + struct chunk *c = p->chunk; + + p->object_len = 0; + p->object_alignment = align; + + _align_chunk(c, align); + if (c->end - c->begin < hint) { + /* allocate a new chunk */ + c = _new_chunk(p, + hint > (p->chunk_size - sizeof(struct chunk)) ? + hint + sizeof(struct chunk) + align : + p->chunk_size); + _align_chunk(c, align); + } + + return c->begin; +} + +void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n) +{ + struct chunk *c = p->chunk; + + if (c->end - (c->begin + p->object_len) < n) { + /* move into a new chunk */ + if (p->object_len + n > (p->chunk_size / 2)) + _new_chunk(p, (p->object_len + n) * 2); + else + _new_chunk(p, p->chunk_size); + + _align_chunk(p->chunk, p->object_alignment); + memcpy(p->chunk->begin, c->begin, p->object_len); + c = p->chunk; + } + + memcpy(c->begin + p->object_len, buffer, n); + p->object_len += n; + return c->begin; +} + +void *pool_end_object(struct pool *p) +{ + struct chunk *c = p->chunk; + void *r = c->begin; + c->begin += p->object_len; + p->object_len = 0u; + p->object_alignment = DEFAULT_ALIGNMENT; + return r; +} + +void pool_abandon_object(struct pool *p) +{ + p->object_len = 0; + p->object_alignment = DEFAULT_ALIGNMENT; +} + +char *pool_strdup(struct pool *p, const char *str) +{ + char *ret = pool_alloc(p, strlen(str) + 1); + + if (ret) + strcpy(ret, str); + + return ret; +} + +void _align_chunk(struct chunk *c, unsigned alignment) +{ + c->begin += alignment - ((unsigned) c->begin & (alignment - 1)); +} + +struct chunk *_new_chunk(struct pool *p, size_t s) +{ + struct chunk *c; + + if (p->spare_chunk && + ((p->spare_chunk->end - (char *) p->spare_chunk) >= s)) { + /* reuse old chunk */ + c = p->spare_chunk; + p->spare_chunk = 0; + } else { + c = dbg_malloc(s); + c->end = (char *) c + s; + } + + c->prev = p->chunk; + c->begin = (char *) (c + 1); + p->chunk = c; + + return c; +} + +/* + * Local variables: + * c-file-style: "linux" + * End: + * vim:ai cin ts=8 + */ diff --git a/lib/mm/pool.h b/lib/mm/pool.h new file mode 100644 index 000000000..89b6438a5 --- /dev/null +++ b/lib/mm/pool.h @@ -0,0 +1,64 @@ +/* + * tools/lib/pool.h + * + * Copyright (C) 2001 Sistina Software + * + * LVM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LVM; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef POOL_H +#define POOL_H + +#include + +struct pool; + +/* constructor and destructor */ +struct pool *create_pool(size_t chunk_hint); +void destroy_pool(struct pool *p); + +/* simple allocation/free routines */ +void *pool_alloc(struct pool *p, size_t s); +void *pool_alloc_aligned(struct pool *p, size_t s, unsigned alignment); +void pool_free(struct pool *p, void *ptr); + +/* object building routines */ +void *pool_begin_object(struct pool *p, size_t hint, unsigned align); +void *pool_grow_object(struct pool *p, unsigned char *buffer, size_t n); +void *pool_end_object(struct pool *p); +void pool_abandon_object(struct pool *p); + +/* utilities */ +char *pool_strdup(struct pool *p, const char *str); + +static inline void *pool_zalloc(struct pool *p, size_t s) { + void *ptr = pool_alloc(p, s); + + if (ptr) + memset(ptr, 0, s); + + return ptr; +} + +#endif + +/* + * Local variables: + * c-file-style: "linux" + * End: + * vim:ai cin ts=4 + */ +