btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * 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;
}