/* disk.c */ #include "copyright.h" #include "config.h" #include <stdio.h> #ifdef STRING_H #include <string.h> #else #include <strings.h> #endif /* STRING_H */ #include <gdbm.h> #include "teeny.h" #include "db.h" /* * Interfaces TeenyMUSK to Gnu DBM. loosly based upon the old TeenyMUD disk.c * */ char *snarf_str_field(); int *snarf_lock(); char *stuff_lock(); char *stuff_str_field(); GDBM_FILE chunkfd; static char obj_id[128]; static int *obj_work = NULL; static long work_siz = 0; /* * Write the object specified by the descriptor out to disk. Will not bother * to freeze an object that isn't dirty. * */ void disk_freeze(d) struct dsc *d; { int *p; char *q; struct obj_data *dat; int total_size, obj; long ret; datum key, cont; if (!ResidentP(d)) { warning("disk_freeze", "attempt to freeze non-resident object"); return; } dat = DSC_DATA(d); obj = DSC_NUMBER(d); /* Is the data for this thing dirty? If not, patch up its */ /* descriptor to point at the disk, and free the memory */ if (!DirtyP(d)) { DSC_FLAGS(d) &= ~IN_MEMORY; free_obj(dat); return; } /* Step one. Write the thing into our workspace. */ if (work_siz == 0) { /* initialize our buffer */ obj_work = (int *) ty_malloc(10240 / sizeof(int), "disk_freeze"); work_siz = 10240 / sizeof(int); } if (DSC_SIZE(d) > work_siz) { /* grow it */ int *tmp; tmp = (int *) realloc(obj_work, (DSC_SIZE(d) + 32)); if (tmp == NULL) { log_error("disk_freeze: realloc of work buffer failed (%ld bytes), exiting\n", DSC_SIZE(d) + 32); exit(-1); } obj_work = tmp; work_siz = DSC_SIZE(d) + 32; } p = obj_work; *p++ = dat->pennies; *p++ = dat->loc; *p++ = dat->contents; *p++ = dat->exits; *p++ = dat->rooms; *p++ = dat->timestamp; /* Stuff variable length integer arrays. */ /* NOTE: Order is important. These arrays *will* be correctly */ /* aligned, since they fall immediately after a mess of other */ /* integers. Effectively, we start with a huge array of ints. */ p = (int *) stuff_lock(p, dat->lock); p = (int *) stuff_lock(p, dat->elock); q = stuff_lock(p, dat->destinations); /* Stuff variable length strings. Now alignment goes to hell */ /* Strings are zero terminated, so we don't care!!! */ q = stuff_str_field(q, dat->suc); q = stuff_str_field(q, dat->osuc); q = stuff_str_field(q, dat->fail); q = stuff_str_field(q, dat->ofail); q = stuff_str_field(q, dat->drop); q = stuff_str_field(q, dat->odrop); q = stuff_str_field(q, dat->desc); q = stuff_str_field(q, dat->enter); q = stuff_str_field(q, dat->oenter); q = stuff_str_field(q, dat->oxent); q = stuff_str_field(q, dat->leave); q = stuff_str_field(q, dat->oleave); q = stuff_str_field(q, dat->oxlea); q = stuff_str_field(q, dat->idesc); q = stuff_str_field(q, dat->odesc); q = stuff_str_field(q, dat->site); q = stuff_str_field(q, dat->password); q = stuff_str_field(q, dat->kill); q = stuff_str_field(q, dat->okill); q = stuff_str_field(q, dat->otel); q = stuff_str_field(q, dat->oxtel); q = stuff_str_field(q, dat->efail); q = stuff_str_field(q, dat->oefail); /* Step two. How big is this bugger? Write it out to disk */ total_size = q - (char *) obj_work; if (total_size != DSC_SIZE(d)) { warning("disk_freeze", "computed disk size != stated"); } (void) sprintf(obj_id, "#%d", obj); key.dptr = obj_id; key.dsize = strlen(obj_id) + 1; cont.dptr = (char *) obj_work; cont.dsize = total_size; ret = gdbm_store(chunkfd, key, cont, GDBM_REPLACE); if (ret != 0) { warning("disk_freeze", "failed write"); } /* Update descriptor */ DSC_FLAGS(d) &= ~IN_MEMORY; /* Free up the memory used. */ free_obj(dat); } void free_obj(obj) struct obj_data *obj; { ty_free((char *) (obj->lock)); ty_free((char *) (obj->elock)); ty_free((char *) (obj->destinations)); ty_free(obj->suc); ty_free(obj->osuc); ty_free(obj->fail); ty_free(obj->ofail); ty_free(obj->drop); ty_free(obj->odrop); ty_free(obj->desc); ty_free(obj->enter); ty_free(obj->oenter); ty_free(obj->leave); ty_free(obj->oleave); ty_free(obj->idesc); ty_free(obj->odesc); ty_free(obj->site); ty_free(obj->kill); ty_free(obj->okill); ty_free(obj->password); ty_free(obj->otel); ty_free(obj->oxtel); ty_free(obj->oxent); ty_free(obj->oxlea); ty_free(obj->efail); ty_free(obj->oefail); ty_free((char *) obj); } /* * Copies one of our internal arrays around. They start with a count of * number of ints, and are followed by that many ints. Yow. * */ char * stuff_lock(dst, src) int *dst, *src; { int count; if (src == NULL) { *dst++ = -1; /* Indicate empty lock */ return ((char *) dst); } count = *dst++ = *src++; /* The very first one is how many follow */ while (count > 0) { *dst++ = *src++; count--; } return ((char *) dst); } /* * Really just copies a string around, and returns a pointer to the first * byte after the string. * */ char * stuff_str_field(dst, src) char *dst, *src; { int len; if (src == NULL) { /* Shove in a zero length string */ *dst++ = '\0'; return (dst); } len = strlen(src); strcpy(dst, src); return (dst + len + 1); /* plus one for the \0 */ } /* * Read the object specified by number in from disk. */ struct obj_data * disk_thaw(d) struct dsc *d; { struct obj_data *new_obj; int len, obj; char *q; int *p; datum key, cont; extern void free(); if (ResidentP(d)) { warning("disk_thaw", "attempt to re-thaw thawed object"); return (DSC_DATA(d)); } obj = DSC_NUMBER(d); (void) sprintf(obj_id, "#%d", obj); key.dptr = obj_id; key.dsize = strlen(obj_id) + 1; cont = gdbm_fetch(chunkfd, key); if (cont.dptr == NULL) { warning("disk_thaw", "couldn't thaw chunk"); return (NULL); } /* OK. It's in memory. Snarf the data out */ len = cont.dsize; new_obj = (struct obj_data *) ty_malloc(sizeof(struct obj_data) ,"disk_thaw"); p = (int *) (cont.dptr); /* Fix up internal object data fields. */ new_obj->size = cont.dsize; new_obj->descriptor = d; new_obj->pennies = *p++; new_obj->loc = *p++; new_obj->contents = *p++; new_obj->exits = *p++; new_obj->rooms = *p++; new_obj->timestamp = *p++; /* Variable length field integer field. */ new_obj->lock = snarf_lock(&p); new_obj->elock = snarf_lock(&p); new_obj->destinations = snarf_lock(&p); q = (char *) p; len -= (q - (char *) (cont.dptr)); /* Subtract what we've chopped off */ /* Variable length string fields. */ new_obj->suc = snarf_str_field(&q, &len); new_obj->osuc = snarf_str_field(&q, &len); new_obj->fail = snarf_str_field(&q, &len); new_obj->ofail = snarf_str_field(&q, &len); new_obj->drop = snarf_str_field(&q, &len); new_obj->odrop = snarf_str_field(&q, &len); new_obj->desc = snarf_str_field(&q, &len); new_obj->enter = snarf_str_field(&q, &len); new_obj->oenter = snarf_str_field(&q, &len); new_obj->oxent = snarf_str_field(&q, &len); new_obj->leave = snarf_str_field(&q, &len); new_obj->oleave = snarf_str_field(&q, &len); new_obj->oxlea = snarf_str_field(&q, &len); new_obj->idesc = snarf_str_field(&q, &len); new_obj->odesc = snarf_str_field(&q, &len); new_obj->site = snarf_str_field(&q, &len); new_obj->password = snarf_str_field(&q, &len); new_obj->kill = snarf_str_field(&q, &len); new_obj->okill = snarf_str_field(&q, &len); new_obj->otel = snarf_str_field(&q, &len); new_obj->oxtel = snarf_str_field(&q, &len); new_obj->efail = snarf_str_field(&q, &len); new_obj->oefail = snarf_str_field(&q, &len); /* Update the descriptor */ DSC_DATA(d) = new_obj; DSC_FLAGS(d) |= IN_MEMORY; DSC_FLAGS(d) &= ~DIRTY; free((char *) cont.dptr); return (new_obj); } /* * Copy an array of our integers into new fresh memory. * * src had damn well better be aligned enough. * */ int * snarf_lock(src) int **src; { int len; int *p; len = *(*src); /* First thing is how many there are to * follow */ if (len == -1) { /* No lock, empty */ (*src)++; return (NULL); } len++; /* Add one for the length field, of course */ p = (int *) ty_malloc((len * sizeof(int)), "snarf_lock"); /* Ignore returned value, we care not. */ (void) stuff_lock(p, *src); *src += len; return (p); } /* * Grabs a variable length string field out of a buffer. String should be * zero terminated. */ char * snarf_str_field(src, remaining) char **src; int *remaining; { int len; char *ret; if (*remaining <= 0) { warning("snarf_str_field", "disk data out of synch"); return (NULL); } if (**src == '\0') { (*src)++; (*remaining)--; return (NULL); } len = strlen(*src) + 1; ret = ty_malloc(len, "snarf_str_field"); *remaining -= len; strcpy(ret, *src); (*src) += len; return (ret); } /* * Open a chunkfile. */ void db_fatal(); void open_chunkfile(name) char *name; { datum ver, key; static char tmp[] = "version"; if ((chunkfd = gdbm_open(name, 0, GDBM_WRITER, 0600, db_fatal)) == NULL) { fatal("open_chunkfile", "could not open chunk file"); } key.dptr = tmp; key.dsize = strlen(tmp) + 1; ver = gdbm_fetch(chunkfd, key); if (ver.dptr == NULL || strcmp(ver.dptr, version)) { fatal("open_chunkfile", "database version does not match server version"); } (void) free((char *) ver.dptr); } void init_chunkfile(name) char *name; { datum ver, key; static char tmp[] = "version"; if ((chunkfd = gdbm_open(name, 0, GDBM_NEWDB, 0600, db_fatal)) == NULL) { fatal("init_chunkfile", "could not open chunk file"); } key.dptr = tmp; key.dsize = strlen(tmp) + 1; ver.dptr = version; ver.dsize = strlen(version) + 1; if (gdbm_store(chunkfd, key, ver, GDBM_REPLACE) != 0) warning("init_chunkfile", "couldn't store version"); } void db_fatal(p) char *p; { log_error("FATAL: GDBM died with message: %s\n", p); exit(-1); } void close_chunkfile() { gdbm_close(chunkfd); } void disk_delete(num) int num; { char tmp[64]; datum key; (void) sprintf(tmp, "#%d", num); key.dptr = tmp; key.dsize = strlen(tmp) + 1; (void) gdbm_delete(chunkfd, key); } /* * Misc. flag checkers and such. */ extern struct dsc **main_index; extern int total_objects; int is_flag_set(obj, code) int obj; int code; { if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) { log_error("is_flag_set: bad obj passed as an argument, %d\n", obj); return 0; } return ((DSC_FLAGS(main_index[obj]) & code)); } int isplayer(obj) int obj; { if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) { warning("isplayer", "bad obj passed as an argument"); return 0; } return (PlayerP(main_index[obj])); } int isroom(obj) int obj; { if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) { warning("isroom", "bad obj passed as an argument"); return 0; } return (RoomP(main_index[obj])); } int isexit(obj) int obj; { if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) { warning("isexit", "bad obj passed as an argument"); return 0; } return (ExitP(main_index[obj])); } int isthing(obj) int obj; { if (obj < 0 || obj >= total_objects || main_index[obj] == NULL) { warning("isthing", "bad obj passed as an argument"); return 0; } return (ThingP(main_index[obj])); } int isgarbage(obj) int obj; { return (obj >= 0 && obj < total_objects && main_index[obj] == NULL); } /* Utility routines with nowhere else to go. */ /* * Drops an element from a list. You'd better be sure the element is actually * on the list. */ void list_drop(elt, place, code) int elt; int place; int code; { int current, last, next; int listcode; switch (code) { case CONTENTS_LIST: listcode = CONTENTS; break; case EXITS_LIST: listcode = EXITS; break; case ROOMS_LIST: listcode = ROOMS; break; default: warning("list_drop", "bad list code"); return; } if (get_int_elt(place, listcode, ¤t) == -1) { warning("list_drop", "bad list reference."); return; } last = -1; while (current != elt && current != -1) { last = current; if (get_int_elt(current, NEXT, ¤t) == -1) { warning("list_drop", "bad ref inside list"); return; } } /* Post Mortem. */ /* grab the next thing on the list anyway. */ if (get_int_elt(current, NEXT, &next) == -1) { warning("list_drop", "bad ref inside list"); return; } if (last == -1) { /* Was 1st thing on list */ if (set_int_elt(place, listcode, next) == -1) { warning("list_drop", "bad list reference."); return; } } else { if (set_int_elt(last, NEXT, next) == -1) { warning("list_drop", "bad list reference."); return; } } } /* * Adds a thing to the top of the contents or exits list of the specified * place. */ void list_add(thing, place, code) int thing; int place; int code; { int list; int listcode; switch (code) { case CONTENTS_LIST: listcode = CONTENTS; break; case EXITS_LIST: listcode = EXITS; break; case ROOMS_LIST: listcode = ROOMS; break; default: warning("list_add", "bad list code"); return; } if (get_int_elt(place, listcode, &list) == -1) { warning("list_add", "can't get contents"); return; } if (set_int_elt(thing, NEXT, list) == -1) { warning("list_add", "bad can't set NEXT"); return; } if (set_int_elt(place, listcode, thing) == -1) { warning("list_add", "can't set contents"); return; } }