/*
* 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;
}