btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * logcache.c
 */

/*
 * $Id $
 */

#include "copyright.h"
#include "config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

#include "mudconf.h"
#include "externs.h"
#include "interface.h"
#include "flags.h"
#include "command.h"
#include "attrs.h"
#include "rbtree.h"
#include <errno.h>

#ifdef DEBUG_LOGCACHE
#define DEBUG
#endif
#include "debug.h"


/* The LOGFILE_TIMEOUT field describes how long a mux should keep an idle
 * open. LOGFILE_TIMEOUT seconds after the last write, it will close. The
 * timer is reset on each write. */
#define LOGFILE_TIMEOUT 300 // Five Minutes

struct logfile_t {
    char *filename;
    int fd;
    struct event ev;
};

rbtree *logfiles = NULL;

static int logcache_compare(void *vleft, void *vright, void *arg) {
    return strcmp((char *)vleft, (char *)vright);
}

static int logcache_close(struct logfile_t *log) {
    dprintk("closing logfile '%s'.", log->filename);
    if(evtimer_pending(&log->ev, NULL)) {
        evtimer_del(&log->ev);
    }
    close(log->fd);
    rb_delete(logfiles, log->filename);
    if(log->filename) 
        free(log->filename);
    log->filename = NULL;
    log->fd = -1;
    free(log);
    return 1;
}

static void logcache_expire(int fd, short event, void *arg) {
    dprintk("Expiring '%s'.", ((struct logfile_t *)arg)->filename);
    logcache_close((struct logfile_t *)arg);
}

static int _logcache_list(void *key, void *data, int depth, void *arg) {
    struct timeval tv;
    struct logfile_t *log = (struct logfile_t *)data;
    dbref player = *(dbref *)arg;
    evtimer_pending(&log->ev, &tv);
    notify_printf(player, "%-40s%d", log->filename, tv.tv_sec-mudstate.now);
    return 1;
}
    
void logcache_list(dbref player) {
    notify(player, "/--------------------------- Open Logfiles");
    if(rb_size(logfiles) == 0) {
        notify(player, "- There are no open logfile handles.");
        return;
    }
    notify(player, "Filename                               Timeout");  
    rb_walk(logfiles, WALK_INORDER, _logcache_list, &player);
}

static int logcache_open(char *filename) {
    int fd;
    struct logfile_t *newlog;
    struct timeval tv = { LOGFILE_TIMEOUT, 0 };

    if(rb_exists(logfiles, filename)) {
        fprintf(stderr, "Serious braindamage, logcache_open() called for already open logfile.\n");
        return 0;
    }
    
    fd = open(filename, O_RDWR|O_APPEND|O_CREAT, 0644);
    if(fd < 0) {
        fprintf(stderr, "Failed to open logfile %s because open() failed with code: %d -  %s\n", 
                filename, errno, strerror(errno));
        return 0;
    }
    if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
        log_perror("LOGCACHE", "FAIL", NULL, "fcntl(fd, F_SETFD, FD_CLOEXEC)");
    }
    
    newlog = malloc(sizeof(struct logfile_t));
    newlog->fd = fd;
    newlog->filename = strdup(filename);
    evtimer_set(&newlog->ev, logcache_expire, newlog);
    evtimer_add(&newlog->ev, &tv);
    rb_insert(logfiles, newlog->filename, newlog);
    dprintk("opened logfile '%s' fd = %d.", filename, fd);
    return 1;
}

void logcache_init() {
    if(!logfiles) {
        dprintk("logcache initialized.");
        logfiles = rb_init(logcache_compare, NULL);
    } else {
        dprintk("REDUNDANT CALL TO logcache_init()!");
    }
}

static int _logcache_destruct(void *key, void *data, int depth, void *arg) {
    struct logfile_t *log = (struct logfile_t *)data;
    logcache_close(log);
    return 1;
}

void logcache_destruct() {
    dprintk("logcache destructing.");
    if(!logfiles) {
        dprintk("logcache_destruct() CALLED WHILE UNITIALIZED!");
        return;
    }
    rb_walk(logfiles, WALK_INORDER, _logcache_destruct, NULL);
    rb_destroy(logfiles);
    logfiles = NULL;
}

int logcache_writelog(char *fname, char *fdata) {
    struct logfile_t *log;
    struct timeval tv = { LOGFILE_TIMEOUT, 0 };
    int len;
   
    if(!logfiles) logcache_init();

    len = strlen(fdata);

    log = rb_find(logfiles, fname);

    if(!log) {
        if(logcache_open(fname) < 0) {
            return 0;
        }
        log = rb_find(logfiles, fname);
        if(!log) {
            return 0;
        }
    }

    if(evtimer_pending(&log->ev, NULL)) {
        event_del(&log->ev);
        event_add(&log->ev, &tv);
    }

    if(write(log->fd, fdata, len) < 0) {
        fprintf(stderr, "System failed to write data to file with error '%s' on logfile '%s'. Closing.\n", 
                strerror(errno), log->filename);
        logcache_close(log);
    }
    return 1;
}