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