/* gdbm.c */ #include "config.h" /* * This file is part of TeenyMUD II. * Copyright(C) 1993, 1994, 1995 by Jason Downs. * All rights reserved. * * TeenyMUD II 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 of the License, or * (at your option) any later version. * * TeenyMUD II 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 this program (see the file 'COPYING'); if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * */ #include <stdio.h> #include <sys/types.h> #ifdef HAVE_MALLOC_H #include <malloc.h> #endif /* HAVE_MALLOC_H */ #ifdef HAVE_STRING_H #include <string.h> #else #include <strings.h> #endif /* HAVE_STRING_H */ #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif /* HAVE_STDLIB_H */ #include <gdbm.h> #include "conf.h" #include "teeny.h" #include "teenydb.h" #include "externs.h" /* TeenyMUD GNU dbm interface. */ static GDBM_FILE gfd; static datum key, cont; static int *obj_work = (int *)NULL; static int work_siz = 0; static void gdbm_panic _ANSI_ARGS_((char *)); INLINE static char *gdbm_pack_data _ANSI_ARGS_((char *, char *, int)); static char *gdbm_pack_lock _ANSI_ARGS_((char *, struct boolexp *)); INLINE static char *gdbm_pack_string _ANSI_ARGS_((char *, char *)); static void gdbm_emergency_growbuf _ANSI_ARGS_((void)); INLINE static char *gdbm_unpack_data _ANSI_ARGS_((char *, char *, int)); static char *gdbm_unpack_lock _ANSI_ARGS_((struct boolexp **, char *)); INLINE static char *gdbm_unpack_string _ANSI_ARGS_((char **, char *)); extern gdbm_error gdbm_errno; #define ALIGN(p) (((int)p) % sizeof(int) == 0 ? ((int)p) : \ ((int)p) + sizeof(int) - ((int)p) % sizeof(int)) /* This is simply a wrapper around panic(). */ static void gdbm_panic(mesg) char *mesg; { panic("GDBM panic: %s\n", mesg); } int dbmfile_open(name, flags) char *name; int flags; { int cache = 10; int gflags = 0; if (flags & TEENY_DBMFAST) gflags |= GDBM_FAST; if (flags & TEENY_DBMREAD) { gflags |= GDBM_READER; } else { gflags |= GDBM_WRITER; } if((gfd = gdbm_open(name, MEDBUFFSIZ, gflags, 0600, gdbm_panic)) == NULL) { logfile(LOG_ERROR, "dbmfile_open: couldn't open %s, %s.\n", name, gdbm_strerror(gdbm_errno)); return(-1); } if(gdbm_setopt(gfd, GDBM_CACHESIZE, &cache, sizeof(int)) == -1) { logfile(LOG_ERROR, "dbmfile_open: couldn't set cache size, %s.\n", gdbm_strerror(gdbm_errno)); return(-1); } return(0); } int dbmfile_init(name, flags) char *name; int flags; { int cache = 10; int gflags = GDBM_NEWDB; if (flags & TEENY_DBMFAST) gflags |= GDBM_FAST; if((gfd = gdbm_open(name, MEDBUFFSIZ, gflags, 0600, gdbm_panic)) == NULL) { logfile(LOG_ERROR, "dbmfile_init: couldn't open %s, %s.\n", name, gdbm_strerror(gdbm_errno)); return(-1); } if(gdbm_setopt(gfd, GDBM_CACHESIZE, &cache, sizeof(int)) == -1) { logfile(LOG_ERROR, "dbmfile_init: couldn't set cache size, %s.\n", gdbm_strerror(gdbm_errno)); return(-1); } return(0); } void dbmfile_close() { gdbm_close(gfd); } INLINE static char *gdbm_pack_data(dest, source, size) char *dest, *source; int size; { bcopy((VOID *)source, (VOID *)dest, size); return((char *)(dest + size)); } static char *gdbm_pack_lock(dest, src) char *dest; struct boolexp *src; { if(src == (struct boolexp *)NULL) { short type = BOOLEXP_END; /* i just *had* to use short... */ return(gdbm_pack_data(dest, (char *)&type, sizeof(short))); } dest = gdbm_pack_data(dest, (char *)&(src->type), sizeof(short)); switch(src->type) { case BOOLEXP_AND: case BOOLEXP_OR: dest = gdbm_pack_lock((char *)ALIGN(dest), src->sub2); case BOOLEXP_NOT: dest = gdbm_pack_lock((char *)ALIGN(dest), src->sub1); break; case BOOLEXP_CONST: dest = gdbm_pack_data((char *)ALIGN(dest), (char *)&((src->dat).thing), sizeof(int)); break; case BOOLEXP_FLAG: dest = gdbm_pack_data((char *)ALIGN(dest), (char *)((src->dat).flags), sizeof(int) * FLAGS_LEN); break; case BOOLEXP_ATTR: dest = gdbm_pack_string((char *)ALIGN(dest), (src->dat).atr[0]); dest = gdbm_pack_string((char *)ALIGN(dest), (src->dat).atr[1]); break; default: logfile(LOG_ERROR, "gdbm_pack_lock: bad boolexp type (%d)\n", src->type); dest[-1] = BOOLEXP_END; } return(dest); } INLINE static char *gdbm_pack_string(dest, src) register char *dest, *src; { register int len; if(src == (char *)NULL){ dest[0] = '\0'; return((char *)(dest + 1)); } #ifdef notyet if(mudconf.enable_compress) { register char *csrc; csrc = compress(src); len = strlen(csrc)+1; bcopy((VOID *)csrc, (VOID *)dest, len); ty_free(csrc); } else { len = strlen(src)+1; bcopy((VOID *)src, (VOID *)dest, len); } #else len = strlen(src)+1; bcopy((VOID *)src, (VOID *)dest, len); #endif return((char *)(dest + len)); } static void gdbm_emergency_growbuf() { obj_work = (int *) ty_realloc(obj_work, work_siz + MEDBUFFSIZ, "gdbm_emergency_growbuf"); work_siz += MEDBUFFSIZ; } int disk_freeze(data) struct obj_data *data; { char *ptr; int *iptr; struct attr *attrs; int obj; register int idx; obj = data->objnum; if (!(DSC_FLAG2(main_index[obj]) & IN_MEMORY)) { logfile(LOG_ERROR, "disk_freeze: attempt to freeze non-resident object #%d\n", obj); return(-1); } if ((work_siz == 0) && DSC_SIZE(main_index[obj]) < 10240) { /* initialize our buffer */ obj_work = (int *) ty_malloc(10240L, "disk_freeze.obj_work"); work_siz = 10240L; } if (DSC_SIZE(main_index[obj]) > work_siz) { /* grow it */ ty_free((VOID *) obj_work); obj_work = (int *) ty_malloc(DSC_SIZE(main_index[obj]) + MEDBUFFSIZ, "disk_freeze.obj_work"); work_siz = DSC_SIZE(main_index[obj]) + MEDBUFFSIZ; } bzero((VOID *)obj_work, work_siz); /* malloc'd memory should always be aligned, damnit. */ iptr = obj_work; *iptr++ = data->quota; *iptr++ = data->loc; *iptr++ = data->contents; *iptr++ = data->exits; *iptr++ = data->rooms; *iptr++ = data->timestamp; *iptr++ = data->created; *iptr++ = data->usecnt; *iptr++ = data->charges; *iptr++ = data->semaphores; *iptr++ = data->cost; *iptr++ = data->queue; /* attributes */ *iptr++ = data->attr_total; ptr = (char *)iptr; for(idx = 0; idx < ATTR_WIDTH; idx++) { for(attrs = data->attributes[idx]; attrs != (struct attr *)NULL; attrs = attrs->next) { ptr = gdbm_pack_data((char *)ALIGN(ptr), (char *)&attrs->type, sizeof(int)); ptr = gdbm_pack_string((char *)ALIGN(ptr), attrs->name); if ((ptr - (char *)obj_work) > (work_siz - MEDBUFFSIZ)) gdbm_emergency_growbuf(); switch(attrs->type) { case ATTR_STRING: ptr = gdbm_pack_string((char *)ALIGN(ptr), (attrs->dat).str); break; case ATTR_LOCK: ptr = gdbm_pack_lock((char *)ALIGN(ptr), (attrs->dat).lock); break; } ptr = gdbm_pack_data((char *)ALIGN(ptr), (char *)&attrs->flags, sizeof(int)); if ((ptr - (char *)obj_work) > (work_siz = MEDBUFFSIZ)) gdbm_emergency_growbuf(); } } /* strings and such. */ ptr = gdbm_pack_string((char *)ALIGN(ptr), data->name); /* set it up. */ key.dptr = (char *) &obj; key.dsize = sizeof(int); cont.dptr = (char *) obj_work; cont.dsize = ptr - (char *)obj_work; /* store it. */ if(gdbm_store(gfd, key, cont, GDBM_REPLACE) != 0) { logfile(LOG_ERROR, "disk_freeze: failed to store object #%d, %s.\n", obj, gdbm_strerror(gdbm_errno)); mudstat.cache_errors++; return(-1); } /* all done! */ DSC_FLAG2(main_index[obj]) &= ~IN_MEMORY; free_obj(data); return(0); } INLINE static char *gdbm_unpack_data(dest, source, size) char *dest, *source; int size; { bcopy((VOID *)source, (VOID *)dest, size); return((char *)(source + size)); } static char *gdbm_unpack_lock(dest, source) struct boolexp **dest; char *source; { short type; source = gdbm_unpack_data((char *)&type, source, sizeof(short)); switch(type) { case BOOLEXP_END: *dest = (struct boolexp *)NULL; return(source); case BOOLEXP_AND: case BOOLEXP_OR: *dest = boolexp_alloc(); (*dest)->type = type; source = gdbm_unpack_lock(&(*dest)->sub2, (char *)ALIGN(source)); return(gdbm_unpack_lock(&(*dest)->sub1, (char *)ALIGN(source))); case BOOLEXP_NOT: *dest = boolexp_alloc(); (*dest)->type = type; return(gdbm_unpack_lock(&(*dest)->sub1, (char *)ALIGN(source))); case BOOLEXP_CONST: *dest = boolexp_alloc(); (*dest)->type = type; return(gdbm_unpack_data((char *)&((*dest)->dat).thing, (char *)ALIGN(source), sizeof(int))); case BOOLEXP_FLAG: *dest = boolexp_alloc(); (*dest)->type = type; return(gdbm_unpack_data((char *)((*dest)->dat).flags, (char *)ALIGN(source), sizeof(int) * FLAGS_LEN)); case BOOLEXP_ATTR: *dest = boolexp_alloc(); (*dest)->type = type; source = gdbm_unpack_string(&(((*dest)->dat).atr[0]), (char *)ALIGN(source)); return(gdbm_unpack_string(&(((*dest)->dat).atr[1]), (char *)ALIGN(source))); default: logfile(LOG_ERROR, "gdbm_unpack_lock: bad boolexp type (%c)\n", *source); *dest = (struct boolexp *)NULL; return(source); } } INLINE static char *gdbm_unpack_string(dest, source) char **dest, *source; { register char *ptr = source; if(ptr[0] == '\0') { *dest = (char *)NULL; return((char *)(source + 1)); } while(ptr[0] != '\0') ptr++; ptr++; #ifdef notyet *dest = uncompress(source); #else *dest = ty_strdup(source, "gdbm_unpack_string.ret"); #endif return(ptr); } struct obj_data *disk_thaw(obj) int obj; { struct obj_data *ret; int *iptr; char *ptr; register int attr_total, hashval; struct attr *curr; if (DSC_FLAG2(main_index[obj]) & IN_MEMORY) { logfile(LOG_ERROR, "disk_thaw: attempt to re-thaw thawed object #%d\n", obj); return (DSC_DATA(main_index[obj])); } /* set it up. */ key.dptr = (char *) &obj; key.dsize = sizeof(int); /* fetch it. */ cont = gdbm_fetch(gfd, key); if(cont.dptr == (char *)NULL) { logfile(LOG_ERROR, "disk_thaw: couldn't fetch object #%d, %s.\n", obj, gdbm_strerror(gdbm_errno)); return((struct obj_data *)NULL); } ret = (struct obj_data *) ty_malloc(sizeof(struct obj_data), "disk_thaw.ret"); ret->objnum = obj; /* gdbm data should be aligned. */ iptr = (int *)cont.dptr; ret->quota = *iptr++; ret->loc = *iptr++; ret->contents = *iptr++; ret->exits = *iptr++; ret->rooms = *iptr++; ret->timestamp = *iptr++; ret->created = *iptr++; ret->usecnt = *iptr++; ret->charges = *iptr++; ret->semaphores = *iptr++; ret->cost = *iptr++; ret->queue = *iptr++; /* attributes. */ bzero((VOID *)ret->attributes, sizeof(struct attr *) * ATTR_WIDTH); ret->attr_total = *iptr++; ptr = (char *)iptr; attr_total = ret->attr_total; while(attr_total) { curr = (struct attr *)ty_malloc(sizeof(struct attr), "disk_thaw.curr"); ptr = gdbm_unpack_data((char *)&curr->type, (char *)ALIGN(ptr), sizeof(short)); ptr = gdbm_unpack_string(&curr->name, (char *)ALIGN(ptr)); switch(curr->type) { case ATTR_STRING: ptr = gdbm_unpack_string(&(curr->dat).str, (char *)ALIGN(ptr)); break; case ATTR_LOCK: ptr = gdbm_unpack_lock(&(curr->dat).lock, (char *)ALIGN(ptr)); break; } ptr = gdbm_unpack_data((char *)&curr->flags, (char *)ALIGN(ptr), sizeof(int)); hashval = attr_hash(curr->name); curr->next = ret->attributes[hashval]; if(ret->attributes[hashval] == (struct attr *)NULL) curr->prev = curr; else { curr->prev = (ret->attributes[hashval])->prev; (ret->attributes[hashval])->prev = curr; } ret->attributes[hashval] = curr; attr_total--; } /* strings and such. */ ptr = gdbm_unpack_string(&ret->name, (char *)ALIGN(ptr)); DSC_DATA(main_index[obj]) = ret; DSC_FLAG2(main_index[obj]) |= IN_MEMORY; DSC_FLAG2(main_index[obj]) &= ~DIRTY; /* don't make any assumptions about free() */ free(cont.dptr); return(ret); } void disk_delete(data) struct obj_data *data; { key.dptr = (char *) &data->objnum; key.dsize = sizeof(int); if(gdbm_exists(gfd, key)) { if(gdbm_delete(gfd, key) != 0) { logfile(LOG_ERROR, "disk_delete: couldn't delete object #%d, %s.\n", data->objnum, gdbm_strerror(gdbm_errno)); } } }