/* $Id: files.c,v 1.2 2006/09/16 12:17:17 maxim Exp $ * */ #include #include #include #include #include #include #include #include "configure.h" #include "common/settings.h" #include "common/misc.h" #include "common/str.h" #include "config.h" #include "core.h" #include "files.h" #define FILES_HASH 1024 #define FILES_STEP 64 typedef struct FileInfo_ FileInfo; struct FileInfo_ { int fd; struct stat s; char name[PATH_LEN]; unsigned hash; int hits; int count; time_t opened; time_t checked; time_t freed; //void* map; FileInfo* next; FileInfo* prev; FileInfo* nextFreed; FileInfo* prevFreed; }; static FileInfo** files = NULL; static FileInfo** filesByFD = NULL; static FileInfo* freeFileInfo = NULL; static FileInfo* freed = NULL; static FileInfo* tail = NULL; static int count = 0; static int maxCount = 0; static int hashSize = 0; static int hits = 0; static int opens = 0; char initFiles() { if (!files) { if (!((hashSize = biRound(config->connections, 64)) && (files = CALLOC(sizeof(FileInfo*) * hashSize--)) && (filesByFD = CALLOC(sizeof(FileInfo*) * fdLimit)))) return FAILED; count = maxCount = hits = opens = 0; } else { if (!(filesByFD = REALLOC(filesByFD, sizeof(FileInfo*) * fdLimit))) return FAILED; } return OK; } void freeFiles() { unsigned h; for (h = 0; h <= hashSize; h++) { FileInfo* f = files[h]; while(f) { FileInfo* next = f->next; debug("freeFiles: close %d:%s (%d hits)", f->fd, f->name, f->hits); f->next = freeFileInfo; freeFileInfo = f; if (close(f->fd)) message(SYS|ERROR, "cannot close %s", f->name); f = next; } } freed = tail = NULL; files = FREE(files); filesByFD = FREE(filesByFD); } void resetTodayFiles() { unsigned h; for (h = 0; h <= hashSize; h++) { FileInfo* f = files[h]; while(f) { f->hits = 0; f = f->next; } } maxCount = opens = hits = 0; } int getFD(char* name, int length) { FileInfo* f; struct stat s; unsigned h, fullHash = hash(name); int fd; debug("getFD(%s)", name); if (!files) { errno = EFAULT; return -1; } h = fullHash & hashSize; f = files[h]; while(f) { if (fullHash == f->hash && !strcmp(name, f->name)) { if (now - f->checked >= config->filesCheck) { debug("getFD: need check (last: %s)", time2str(f->checked, TODAY_TIME)); if (stat(name, &s) < 0) return -1; if (s.st_mtime != f->s.st_mtime || s.st_size != f->s.st_size) break; f->checked = now; } hits++; f->hits++; if (!f->count) { debug("return fd %d from freed to active"); if (f->nextFreed) f->nextFreed->prevFreed = f->prevFreed; else tail = f->prevFreed; if (f->prevFreed) f->prevFreed->nextFreed = f->nextFreed; else freed = f->nextFreed; } f->count++; return f->fd; } f = f->next; } debug("getFD: %sopen it", f ? "re" : ""); if (!f) if (stat(name, &s) < 0) return -1; if ((fd = open(name, O_RDONLY)) < 0) return -1; if (fd >= fdLimit) { message(ERROR, "cannot open %s: too big fd (%d > %d)", name, fd, fdLimit); close(fd); errno = EMFILE; return -1; } if (!(f = freeFileInfo)) { debug("getFD: allocating new files block"); freeFileInfo = MALLOC(FILES_STEP * sizeof(FileInfo)); if (!freeFileInfo) { errno = ENOMEM; return -1; } for (f = freeFileInfo; f < freeFileInfo + FILES_STEP - 1; f++) f->next = f + 1; f->next = NULL; f = freeFileInfo; } freeFileInfo = freeFileInfo->next; if ((f->next = files[h])) f->next->prev = f; files[h] = f; f->prev = NULL; f->fd = fd; f->hash = fullHash; memcpy(f->name, name, length + 1); f->count = 1; f->hits = 0; f->opened = f->checked = now; f->freed = 0; memcpy(&(f->s), &s, sizeof(struct stat)); filesByFD[fd] = f; if (++count > maxCount) maxCount = count; opens++; return fd; } struct stat* getFDStat(int fd) { FileInfo* f = filesByFD[fd]; return f ? &(f->s) : NULL; } int freeFD(int fd) { FileInfo* f; if (fd >= fdLimit) { message(ERROR, "strange: freeFD for big fd (%d > %d)", fd, fdLimit); return close(fd); } f = filesByFD[fd]; if (!f) { message(ERROR, "strange: cannot find fd %d in list", fd); return close(fd); } debug("freeFD(%d): %s (%d)", fd, f->name, f->count); f->count--; if (!f->count) { if ((f->nextFreed = freed)) f->nextFreed->prevFreed = f; else tail = f; freed = f; f->prevFreed = NULL; f->freed = now; } return 0; } void cleanupFiles() { debug("cleanupFiles"); while(tail) { if (now - tail->freed <= config->filesKeep) break; debug("close %d:%s (%d hits)", tail->fd, tail->name, tail->hits); --count; filesByFD[tail->fd] = NULL; if (close(tail->fd)) message(SYS|ERROR, "cannot close %s", tail->name); if (tail->next) tail->next->prev = tail->prev; if (tail->prev) tail->prev->next = tail->next; else files[tail->hash & hashSize] = tail->next; if (tail->prevFreed) tail->prevFreed->nextFreed = tail->nextFreed; else freed = NULL; tail->next = freeFileInfo; freeFileInfo = tail; tail = tail->prevFreed; } } int getFilesStats(char* buffer, int size, char show, char* uri) { int l = SNPRINTF(buffer, size, "\n" "Files pool (current/max/opens/hits): %s / %s / %s / %s\n", uri, show ? "" : "?files", itos(count), itos(maxCount), itos(opens), itos(hits)); if (show) { unsigned h; l += SNPRINTF(buffer + l, size - l, "%7s %4s %4s %s\n", "Hits", "Conn", "Time", "Filename"); for (h = 0; h <= hashSize && size > l; h++) { FileInfo* f = files[h]; while(f && size > l) { l += SNPRINTF(buffer + l, size - l, "%7s %4d %4s %s\n", itos(f->hits), f->count, humanPeriod(now - f->opened), f->name); f = f->next; } } } return l; }