From 77f4101fcb296de31f4856376d2f93b2794fcc02 Mon Sep 17 00:00:00 2001 From: etobi Date: Sun, 10 Mar 2013 02:19:57 +0100 Subject: Refactoring: Moved storage related classes into their own files --- Makefile | 2 +- legacystorage.c | 144 ++++++++++++++++++++ legacystorage.h | 40 ++++++ osdteletext.c | 14 +- packedstorage.c | 131 ++++++++++++++++++ packedstorage.h | 36 +++++ pageid.h | 31 +++++ rootdir.c | 21 +++ rootdir.h | 22 +++ storage.c | 142 ++++++++++++++++++++ storage.h | 65 +++++++++ txtrecv.c | 409 -------------------------------------------------------- txtrecv.h | 110 +-------------- 13 files changed, 646 insertions(+), 521 deletions(-) create mode 100644 legacystorage.c create mode 100644 legacystorage.h create mode 100644 packedstorage.c create mode 100644 packedstorage.h create mode 100644 pageid.h create mode 100644 rootdir.c create mode 100644 rootdir.h create mode 100644 storage.c create mode 100644 storage.h diff --git a/Makefile b/Makefile index 9ef006c..dea6845 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o menu.o txtfont.o txtrecv.o txtrender.o displaybase.o display.o +OBJS = $(PLUGIN).o menu.o txtfont.o txtrecv.o txtrender.o displaybase.o display.o storage.o legacystorage.o packedstorage.o rootdir.o ### The main target: diff --git a/legacystorage.c b/legacystorage.c new file mode 100644 index 0000000..3bed512 --- /dev/null +++ b/legacystorage.c @@ -0,0 +1,144 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include "legacystorage.h" + +LegacyStorage::LegacyStorage(int maxMB) + : fsBlockSize(1), pageBytes(TELETEXT_PAGESIZE) +{ + initMaxStorage(maxMB); +} + +LegacyStorage::~LegacyStorage() { +} + +/* +static inline int FilesForMegabytes(double MB, int blocksize) { + double pageBytes; + if (TELETEXT_PAGESIZE<=blocksize) + pageBytes=blocksize; + else + pageBytes=((TELETEXT_PAGESIZE/blocksize)+1)*blocksize; + //reserve 10% for directories + return (int)( (1024.0 * 1024.0 * (MB-MB*0.1)) / pageBytes ); +}*/ + +int LegacyStorage::actualFileSize(int netFileSize) { + if (netFileSize<=0) + return 0; + if (netFileSize<=fsBlockSize) + return fsBlockSize; + else + return ((netFileSize/fsBlockSize)+1)*fsBlockSize; +} + +//max==0 means unlimited, max==-1 means a reasonable default value shall be calculated +void LegacyStorage::initMaxStorage(int maxMB) { + struct statfs fs; + if (statfs(getRootDir(), &fs)!=0) { + esyslog("OSD-Teletext: Error statfs'ing root directory \"%s\": %s, cache size uncontrolled", getRootDir(), strerror(errno)); + return; + } + fsBlockSize=fs.f_bsize; + + pageBytes=actualFileSize(TELETEXT_PAGESIZE); + + if (maxMB>=0) { + if (maxMB<3) { + esyslog("OSD-Teletext: Request to use at most %d MB for caching. This is not enough, using 3 MB", maxMB); + maxMB=3; + } + maxBytes=MEGABYTE(maxMB); + } else if (maxMB==-1) { + //calculate a default value + double blocksPerMeg = 1024.0 * 1024.0 / fs.f_bsize; + double capacityMB=fs.f_blocks / blocksPerMeg; + double freeMB=(fs.f_bavail / blocksPerMeg); + if (capacityMB<=50 || freeMB<50) { + //small (<=50MB) filesystems as root dir are assumed to be dedicated for use as a teletext cache + //for others, the maximum default size is set to 50 MB + maxBytes=MEGABYTE((int)freeMB); + //maxPages= FilesForMegabytes(freeMB, fs.f_bsize); + if (freeMB<3.0) { + esyslog("OSD-Teletext: Less than %.1f MB free on filesystem of root directory \"%s\"!", freeMB, getRootDir()); + maxBytes=MEGABYTE(3); + } + } else { + //the maximum default size is set to 50 MB + maxBytes=MEGABYTE(50); + } + //printf("Set maxBytes to %ld, %.2f %.2f\n", maxBytes, capacityMB, freeMB); + } +} + +void LegacyStorage::cleanUp() { + byteCount -= Storage::doCleanUp(); +} + +void LegacyStorage::registerFile(PageID page) { + //pageBytes is already effective size + if ( maxBytes && (byteCount+=pageBytes)>maxBytes ) + freeSpace(); +} + +StorageHandle LegacyStorage::openForReading(PageID page, bool countAsAccess) { + //the countAsAccess argument was intended for use in a LRU cache, currently unused + char filename[PATH_MAX]; + getFilename(filename, sizeof(filename), page); + StorageHandle ret=(StorageHandle)open(filename, O_RDONLY); + return ret; +} + +StorageHandle LegacyStorage::openForWriting(PageID page) { + static bool wroteError=false; + char filename[PATH_MAX]; + getFilename(filename, sizeof(filename), page); + bool existed=exists(filename); + //first try + StorageHandle fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd) { + if (!existed) + registerFile(page); + return fd; + } + //no space on disk? make some space available + if (errno == ENOSPC) + freeSpace(); + //second try + fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (!fd && !wroteError) { + //report error to syslog - once! + wroteError=true; + esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno)); + } + //make sure newly created files are counted + if (fd && !existed) + registerFile(page); + return fd; +} + +ssize_t LegacyStorage::write(const void *ptr, size_t size, StorageHandle stream) { + ssize_t written; + if (!(written=::write((int)stream, ptr, size)) ) { + switch (errno) { + case ENOSPC: + freeSpace(); + return ::write((int)stream, ptr, size); + case EINTR: + esyslog("OSD-Teletext: EINTR while writing. Please contact the author and tell him this happened."); + break; + default: + break; + } + } + return written; +} diff --git a/legacystorage.h b/legacystorage.h new file mode 100644 index 0000000..266a6fd --- /dev/null +++ b/legacystorage.h @@ -0,0 +1,40 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __LEGACYSTORAGE_H +#define __LEGACYSTORAGE_H + +#include "storage.h" + +class LegacyStorage : public Storage { +private: + void initMaxStorage(int maxMB=-1); +public: + LegacyStorage(int maxMB); + virtual ~LegacyStorage(); + virtual void cleanUp(); + + virtual StorageHandle openForWriting(PageID page); + virtual StorageHandle openForReading(PageID page, bool countAsAccess); + virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream); + virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) + { return ::read((int)stream, ptr, size); } + virtual void close(StorageHandle stream) + { ::close((int)stream); } +protected: + void registerFile(PageID page); + virtual int actualFileSize(int netFileSize); + //int maxPages; + long maxBytes; + int fsBlockSize; + int pageBytes; +}; + +#endif diff --git a/osdteletext.c b/osdteletext.c index 04e0b49..f03ef54 100644 --- a/osdteletext.c +++ b/osdteletext.c @@ -20,6 +20,8 @@ using namespace std; #include "menu.h" #include "txtrecv.h" #include "setup.h" +#include "legacystorage.h" +#include "packedstorage.h" #if defined(APIVERSNUM) && APIVERSNUM < 10739 #error "VDR-1.7.39 API version or greater is required!" @@ -175,10 +177,16 @@ bool cPluginTeletextosd::ProcessArgs(int argc, char *argv[]) bool cPluginTeletextosd::Start(void) { - // Start any background activities the plugin shall perform. - //Clean any files which might be remaining from the last session, + // Start any background activities the plugin shall perform. + //Clean any files which might be remaining from the last session, //perhaps due to a crash they have not been deleted. - storage = Storage::CreateInstance(storageSystem, maxStorage); + switch (storageSystem) { + case Storage::StorageSystemLegacy: + storage = new LegacyStorage(maxStorage); + case Storage::StorageSystemPacked: + default: + storage = new PackedStorage(maxStorage); + } initTexts(); if (startReceiver) diff --git a/packedstorage.c b/packedstorage.c new file mode 100644 index 0000000..f23866f --- /dev/null +++ b/packedstorage.c @@ -0,0 +1,131 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "packedstorage.h" + +PackedStorage::PackedStorage(int maxMB) + : LegacyStorage(maxMB) { +} +#define TOC_SIZE 8 +//The file structure is simple: +// TOC_SIZE*PageAddress contains the numbers of the following pages +// TOC_SIZE*TELETEXT_PAGESIZE contains the page data +//and the same again. +bool PackedStorage::seekTo(PageID page, int desc, bool create) { + lseek(desc, 0, SEEK_SET); + PageAddress addr[TOC_SIZE]; + + while (::read(desc, addr, sizeof(addr)) == sizeof(addr)) { + lseek(desc, 0, SEEK_CUR); + for (int index=0; indexmaxBytes ) + freeSpace(); + return (StorageHandle)desc; + } + //no space on disk? make some space available + if (errno == ENOSPC) + freeSpace(); + //second try + desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (desc==-1 && !wroteError) { + //report error to syslog - once! + wroteError=true; + esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno)); + } + + if (desc==-1) + return StorageHandle(); + else if (!seekTo(page, desc, true)) { + ::close(desc); + return StorageHandle(); + } + + if ( maxBytes && byteCount>maxBytes ) + freeSpace(); + return (StorageHandle)desc; +} + +StorageHandle PackedStorage::openForReading(PageID page, bool countAsAccess) { + int desc; + if ( (desc=(int)LegacyStorage::openForReading(page, false))!= -1 ) { + if (!seekTo(page, desc, false)) { + //this is not an error condition here, may and shall happen! + ::close(desc); + } else { + return (StorageHandle)desc; + } + } + return StorageHandle(); +} diff --git a/packedstorage.h b/packedstorage.h new file mode 100644 index 0000000..5df219b --- /dev/null +++ b/packedstorage.h @@ -0,0 +1,36 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __PACKEDSTORAGE_H +#define __PACKEDSTORAGE_H + +#include "legacystorage.h" + +class PackedStorage : public LegacyStorage { +public: + PackedStorage(int maxMB); + + virtual void getFilename(char *buffer, int bufLength, PageID page); + virtual StorageHandle openForWriting(PageID page); + virtual StorageHandle openForReading(PageID page, bool countAsAccess); +protected: + struct PageAddress { + bool operator==(const PageID &id) const + { return page==id.page && subPage==id.subPage; } + void operator=(const PageID &id) + { page=id.page; subPage=id.subPage; } + int page; + int subPage; + }; + bool seekTo(PageID page, int fd, bool create); + void registerFile(PageID page); +}; + +#endif diff --git a/pageid.h b/pageid.h new file mode 100644 index 0000000..cb2709d --- /dev/null +++ b/pageid.h @@ -0,0 +1,31 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __PAGEID_H +#define __PAGEID_H + +#include +#include +#include +#include + +#include +#include + +struct PageID { + PageID() { page=subPage=0; } + PageID(tChannelID id, int p, int s) { set(id, p, s); } + void set(tChannelID id, int p, int s) { channel=id; page=p; subPage=s; } + tChannelID channel; + int page; + int subPage; +}; + +#endif diff --git a/rootdir.c b/rootdir.c new file mode 100644 index 0000000..7076e0d --- /dev/null +++ b/rootdir.c @@ -0,0 +1,21 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "rootdir.h" + +const char *RootDir::root = "/var/cache/vdr/vtx"; + +void RootDir::setRootDir(const char *newRoot) { + root=newRoot; +} + +const char *RootDir::getRootDir() { + return root; +} diff --git a/rootdir.h b/rootdir.h new file mode 100644 index 0000000..90ce49c --- /dev/null +++ b/rootdir.h @@ -0,0 +1,22 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __ROOTDIR_H +#define __ROOTDIR_H + +class RootDir { +public: + static void setRootDir(const char *); + static const char *getRootDir(); +protected: + static const char *root; +}; + +#endif diff --git a/storage.c b/storage.c new file mode 100644 index 0000000..10bd74a --- /dev/null +++ b/storage.c @@ -0,0 +1,142 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "storage.h" + +int Storage::doCleanUp() { + DIR *top=opendir(root); + int pagesDeleted=0; + if (top) { + struct dirent *chandir, path; + struct stat chandirstat; + char fullPath[PATH_MAX]; + while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) { + if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0) + continue; + snprintf(fullPath, PATH_MAX, "%s/%s", root, chandir->d_name); + if (stat(fullPath, &chandirstat)==0) { + if (S_ISDIR(chandirstat.st_mode)) { + pagesDeleted+=cleanSubDir(fullPath); + } + } + } + closedir(top); + } else { + esyslog("OSD-Teletext: Error opening teletext storage directory \"%s\": %s", root, strerror(errno)); + } + return pagesDeleted; +} + +int Storage::cleanSubDir(const char *dir) { + static bool reportedError=false; //avoid filling up syslog + DIR *d=opendir(dir); + bool hadError=false; + int bytesDeleted=0; + if (d) { + struct dirent *txtfile, path; + struct stat txtfilestat; + char fullPath[PATH_MAX]; + int filesize; + while ( (!readdir_r(d, &path, &txtfile) && txtfile != NULL) ) { + int len=strlen(txtfile->d_name); + //check that the file end with .vtx to avoid accidents and disasters + if (strcmp(txtfile->d_name+len-4, ".vtx")==0) { + snprintf(fullPath, PATH_MAX, "%s/%s", dir, txtfile->d_name); + stat(fullPath, &txtfilestat); + filesize=actualFileSize(txtfilestat.st_size); + int ret=unlink(fullPath); + if (ret==0) + bytesDeleted+=filesize; + else + hadError=ret; + } + } + closedir(d); + rmdir(dir); + } else { + if (!reportedError) { + esyslog("OSD-Teletext: Error opening teletext storage subdirectory \"%s\": %s", dir, strerror(errno)); + reportedError=true; + } + } + + if (hadError && !reportedError) { + esyslog("OSD-Teletext: Error removing teletext storage subdirectory \"%s\": %s", dir, strerror(hadError)); + reportedError=true; + } + return bytesDeleted; +} + +Storage::Storage() + : byteCount(0), failedFreeSpace(false) +{ +} + +Storage::~Storage() { +} + +void Storage::freeSpace() { + //there might be a situation where only the current directory is left and + //occupies the whole space. We cannot delete anything. Don't waste time scanning. + if (failedFreeSpace) + return; + + //printf("freeSpace()\n"); + time_t min=time(0); + char minDir[PATH_MAX]; + char fullPath[PATH_MAX]; + DIR *top=opendir(getRootDir()); + if (top) { + int haveDir=0; + struct dirent *chandir, path; + struct stat chandirstat; + while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) { + if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0) + continue; + snprintf(fullPath, PATH_MAX, "%s/%s", getRootDir(), chandir->d_name); + if (stat(fullPath, &chandirstat)==0) { + if (S_ISDIR(chandirstat.st_mode)) { + if (chandirstat.st_ctime < min && strcmp(fullPath, currentDir)) { + min=chandirstat.st_ctime; + strcpy(minDir, fullPath); + haveDir++; + } + } + } + } + closedir(top); + + //if haveDir, only current directory present, which must not be deleted + if (haveDir>=2) + byteCount-=cleanSubDir(minDir); + else + failedFreeSpace=true; + } +} + +bool Storage::exists(const char* file) { + struct stat s; + return (stat(file, &s)==0); +} + +void Storage::getFilename(char *buffer, int bufLength, PageID page) { + snprintf(buffer, bufLength, "%s/%s/%03x_%02x.vtx", getRootDir(), + *page.channel.ToString(), page.page, page.subPage); +} + +void Storage::prepareDirectory(tChannelID chan) { + currentDir = cString::sprintf("%s/%s", root, *chan.ToString()); + if (!MakeDirs(currentDir, 1)) { + esyslog("OSD-Teletext: Error preparing directory for channel \"%s\"", + *chan.ToString()); + return; + } + failedFreeSpace=false; +} diff --git a/storage.h b/storage.h new file mode 100644 index 0000000..29ae022 --- /dev/null +++ b/storage.h @@ -0,0 +1,65 @@ +/*************************************************************** -*- c++ -*- + * Copyright (c) 2003,2004 by Marcel Wiesweg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef __STORAGE_H +#define __STORAGE_H + +#include "rootdir.h" +#include "pageid.h" + +#define TELETEXT_PAGESIZE 972 + +struct StorageHandle { +public: + StorageHandle() { handle=-1; } + StorageHandle(const StorageHandle &s) { handle=s.handle; } + StorageHandle(int h) { handle=h; } + StorageHandle &operator=(int h) { handle=h; return *this; } + StorageHandle &operator=(const StorageHandle &s) { handle=s.handle; return *this; } + operator bool() const { return handle!=-1; } + operator int() const { return handle; } +private: + int handle; +}; + +enum StorageSystem { StorageSystemLegacy, StorageSystemPacked }; + +class Storage : public RootDir { +public: + virtual ~Storage(); + enum StorageSystem { StorageSystemLegacy, StorageSystemPacked }; + //must be called before the first call to instance() + + //must be called before operation starts. Set all options (RootDir, maxStorage) before. + virtual void cleanUp() = 0; + + virtual void getFilename(char *buffer, int bufLength, PageID page); + virtual void prepareDirectory(tChannelID chan); + + virtual StorageHandle openForWriting(PageID page) = 0; + virtual StorageHandle openForReading(PageID page, bool countAsAccess) = 0; + virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream) = 0; + virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) = 0; + virtual void close(StorageHandle stream) = 0; +protected: + Storage(); + int cleanSubDir(const char *dir); + int doCleanUp(); + virtual int actualFileSize(int netFileSize) { return netFileSize; } + void freeSpace(); + bool exists(const char* file); + + long byteCount; + cString currentDir; +private: + bool failedFreeSpace; +}; + +#endif diff --git a/txtrecv.c b/txtrecv.c index 9aa8984..f59c804 100644 --- a/txtrecv.c +++ b/txtrecv.c @@ -27,415 +27,6 @@ #include #include -const char *RootDir::root = "/var/cache/vdr/vtx"; - -void RootDir::setRootDir(const char *newRoot) { - root=newRoot; -} - -const char *RootDir::getRootDir() { - return root; -} - -int Storage::doCleanUp() { - DIR *top=opendir(root); - int pagesDeleted=0; - if (top) { - struct dirent *chandir, path; - struct stat chandirstat; - char fullPath[PATH_MAX]; - while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) { - if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0) - continue; - snprintf(fullPath, PATH_MAX, "%s/%s", root, chandir->d_name); - if (stat(fullPath, &chandirstat)==0) { - if (S_ISDIR(chandirstat.st_mode)) { - pagesDeleted+=cleanSubDir(fullPath); - } - } - } - closedir(top); - } else { - esyslog("OSD-Teletext: Error opening teletext storage directory \"%s\": %s", root, strerror(errno)); - } - return pagesDeleted; -} - -int Storage::cleanSubDir(const char *dir) { - static bool reportedError=false; //avoid filling up syslog - DIR *d=opendir(dir); - bool hadError=false; - int bytesDeleted=0; - if (d) { - struct dirent *txtfile, path; - struct stat txtfilestat; - char fullPath[PATH_MAX]; - int filesize; - while ( (!readdir_r(d, &path, &txtfile) && txtfile != NULL) ) { - int len=strlen(txtfile->d_name); - //check that the file end with .vtx to avoid accidents and disasters - if (strcmp(txtfile->d_name+len-4, ".vtx")==0) { - snprintf(fullPath, PATH_MAX, "%s/%s", dir, txtfile->d_name); - stat(fullPath, &txtfilestat); - filesize=actualFileSize(txtfilestat.st_size); - int ret=unlink(fullPath); - if (ret==0) - bytesDeleted+=filesize; - else - hadError=ret; - } - } - closedir(d); - rmdir(dir); - } else { - if (!reportedError) { - esyslog("OSD-Teletext: Error opening teletext storage subdirectory \"%s\": %s", dir, strerror(errno)); - reportedError=true; - } - } - - if (hadError && !reportedError) { - esyslog("OSD-Teletext: Error removing teletext storage subdirectory \"%s\": %s", dir, strerror(hadError)); - reportedError=true; - } - return bytesDeleted; -} - -Storage::Storage() - : byteCount(0), failedFreeSpace(false) -{ -} - -Storage::~Storage() { -} - -Storage *Storage::CreateInstance(StorageSystem system, int storageLimit) { - switch (system) { - case StorageSystemLegacy: - return new LegacyStorage(storageLimit); - case StorageSystemPacked: - default: - return new PackedStorage(storageLimit); - } -} - -void Storage::freeSpace() { - //there might be a situation where only the current directory is left and - //occupies the whole space. We cannot delete anything. Don't waste time scanning. - if (failedFreeSpace) - return; - - //printf("freeSpace()\n"); - time_t min=time(0); - char minDir[PATH_MAX]; - char fullPath[PATH_MAX]; - DIR *top=opendir(getRootDir()); - if (top) { - int haveDir=0; - struct dirent *chandir, path; - struct stat chandirstat; - while ( (!readdir_r(top, &path, &chandir) && chandir != NULL) ) { - if (strcmp(chandir->d_name, "..")==0 || strcmp(chandir->d_name, ".")==0) - continue; - snprintf(fullPath, PATH_MAX, "%s/%s", getRootDir(), chandir->d_name); - if (stat(fullPath, &chandirstat)==0) { - if (S_ISDIR(chandirstat.st_mode)) { - if (chandirstat.st_ctime < min && strcmp(fullPath, currentDir)) { - min=chandirstat.st_ctime; - strcpy(minDir, fullPath); - haveDir++; - } - } - } - } - closedir(top); - - //if haveDir, only current directory present, which must not be deleted - if (haveDir>=2) - byteCount-=cleanSubDir(minDir); - else - failedFreeSpace=true; - } -} - -bool Storage::exists(const char* file) { - struct stat s; - return (stat(file, &s)==0); -} - -void Storage::getFilename(char *buffer, int bufLength, PageID page) { - snprintf(buffer, bufLength, "%s/%s/%03x_%02x.vtx", getRootDir(), - *page.channel.ToString(), page.page, page.subPage); -} - -void Storage::prepareDirectory(tChannelID chan) { - currentDir = cString::sprintf("%s/%s", root, *chan.ToString()); - if (!MakeDirs(currentDir, 1)) { - esyslog("OSD-Teletext: Error preparing directory for channel \"%s\"", - *chan.ToString()); - return; - } - failedFreeSpace=false; -} - -#define TELETEXT_PAGESIZE 972 - -LegacyStorage::LegacyStorage(int maxMB) - : fsBlockSize(1), pageBytes(TELETEXT_PAGESIZE) -{ - initMaxStorage(maxMB); -} - -LegacyStorage::~LegacyStorage() { -} - -/* -static inline int FilesForMegabytes(double MB, int blocksize) { - double pageBytes; - if (TELETEXT_PAGESIZE<=blocksize) - pageBytes=blocksize; - else - pageBytes=((TELETEXT_PAGESIZE/blocksize)+1)*blocksize; - //reserve 10% for directories - return (int)( (1024.0 * 1024.0 * (MB-MB*0.1)) / pageBytes ); -}*/ - -int LegacyStorage::actualFileSize(int netFileSize) { - if (netFileSize<=0) - return 0; - if (netFileSize<=fsBlockSize) - return fsBlockSize; - else - return ((netFileSize/fsBlockSize)+1)*fsBlockSize; -} - -//max==0 means unlimited, max==-1 means a reasonable default value shall be calculated -void LegacyStorage::initMaxStorage(int maxMB) { - struct statfs fs; - if (statfs(getRootDir(), &fs)!=0) { - esyslog("OSD-Teletext: Error statfs'ing root directory \"%s\": %s, cache size uncontrolled", getRootDir(), strerror(errno)); - return; - } - fsBlockSize=fs.f_bsize; - - pageBytes=actualFileSize(TELETEXT_PAGESIZE); - - if (maxMB>=0) { - if (maxMB<3) { - esyslog("OSD-Teletext: Request to use at most %d MB for caching. This is not enough, using 3 MB", maxMB); - maxMB=3; - } - maxBytes=MEGABYTE(maxMB); - } else if (maxMB==-1) { - //calculate a default value - double blocksPerMeg = 1024.0 * 1024.0 / fs.f_bsize; - double capacityMB=fs.f_blocks / blocksPerMeg; - double freeMB=(fs.f_bavail / blocksPerMeg); - if (capacityMB<=50 || freeMB<50) { - //small (<=50MB) filesystems as root dir are assumed to be dedicated for use as a teletext cache - //for others, the maximum default size is set to 50 MB - maxBytes=MEGABYTE((int)freeMB); - //maxPages= FilesForMegabytes(freeMB, fs.f_bsize); - if (freeMB<3.0) { - esyslog("OSD-Teletext: Less than %.1f MB free on filesystem of root directory \"%s\"!", freeMB, getRootDir()); - maxBytes=MEGABYTE(3); - } - } else { - //the maximum default size is set to 50 MB - maxBytes=MEGABYTE(50); - } - //printf("Set maxBytes to %ld, %.2f %.2f\n", maxBytes, capacityMB, freeMB); - } -} - -void LegacyStorage::cleanUp() { - byteCount -= Storage::doCleanUp(); -} - -void LegacyStorage::registerFile(PageID page) { - //pageBytes is already effective size - if ( maxBytes && (byteCount+=pageBytes)>maxBytes ) - freeSpace(); -} - -StorageHandle LegacyStorage::openForReading(PageID page, bool countAsAccess) { - //the countAsAccess argument was intended for use in a LRU cache, currently unused - char filename[PATH_MAX]; - getFilename(filename, sizeof(filename), page); - StorageHandle ret=(StorageHandle)open(filename, O_RDONLY); - return ret; -} - -StorageHandle LegacyStorage::openForWriting(PageID page) { - static bool wroteError=false; - char filename[PATH_MAX]; - getFilename(filename, sizeof(filename), page); - bool existed=exists(filename); - //first try - StorageHandle fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd) { - if (!existed) - registerFile(page); - return fd; - } - //no space on disk? make some space available - if (errno == ENOSPC) - freeSpace(); - //second try - fd=(StorageHandle)open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (!fd && !wroteError) { - //report error to syslog - once! - wroteError=true; - esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno)); - } - //make sure newly created files are counted - if (fd && !existed) - registerFile(page); - return fd; -} - -ssize_t LegacyStorage::write(const void *ptr, size_t size, StorageHandle stream) { - ssize_t written; - if (!(written=::write((int)stream, ptr, size)) ) { - switch (errno) { - case ENOSPC: - freeSpace(); - return ::write((int)stream, ptr, size); - case EINTR: - esyslog("OSD-Teletext: EINTR while writing. Please contact the author and tell him this happened."); - break; - default: - break; - } - } - return written; -} - - - -PackedStorage::PackedStorage(int maxMB) - : LegacyStorage(maxMB) { -} -#define TOC_SIZE 8 -//The file structure is simple: -// TOC_SIZE*PageAddress contains the numbers of the following pages -// TOC_SIZE*TELETEXT_PAGESIZE contains the page data -//and the same again. -bool PackedStorage::seekTo(PageID page, int desc, bool create) { - lseek(desc, 0, SEEK_SET); - PageAddress addr[TOC_SIZE]; - - while (::read(desc, addr, sizeof(addr)) == sizeof(addr)) { - lseek(desc, 0, SEEK_CUR); - for (int index=0; indexmaxBytes ) - freeSpace(); - return (StorageHandle)desc; - } - //no space on disk? make some space available - if (errno == ENOSPC) - freeSpace(); - //second try - desc=open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (desc==-1 && !wroteError) { - //report error to syslog - once! - wroteError=true; - esyslog("OSD-Teletext: Error opening teletext file %s: %s", filename, strerror(errno)); - } - - if (desc==-1) - return StorageHandle(); - else if (!seekTo(page, desc, true)) { - ::close(desc); - return StorageHandle(); - } - - if ( maxBytes && byteCount>maxBytes ) - freeSpace(); - return (StorageHandle)desc; -} - -StorageHandle PackedStorage::openForReading(PageID page, bool countAsAccess) { - int desc; - if ( (desc=(int)LegacyStorage::openForReading(page, false))!= -1 ) { - if (!seekTo(page, desc, false)) { - //this is not an error condition here, may and shall happen! - ::close(desc); - } else { - return (StorageHandle)desc; - } - } - return StorageHandle(); -} - - - - cTelePage::cTelePage(PageID t_page, uchar t_flags, uchar t_lang,int t_mag, Storage *s) : mag(t_mag), flags(t_flags), lang(t_lang), page(t_page), storage(s) { diff --git a/txtrecv.h b/txtrecv.h index 2558bb5..a1be1f4 100644 --- a/txtrecv.h +++ b/txtrecv.h @@ -8,7 +8,7 @@ * * ***************************************************************************/ -#ifndef __TXTRECV_H +#ifndef __TXTRECV_H #define __TXTRECV_H #include @@ -19,113 +19,7 @@ #include #include -struct PageID { - PageID() { page=subPage=0; } - PageID(tChannelID id, int p, int s) { set(id, p, s); } - void set(tChannelID id, int p, int s) - { channel=id; page=p; subPage=s; } - tChannelID channel; - int page; - int subPage; -}; - -struct StorageHandle { -public: - StorageHandle() { handle=-1; } - StorageHandle(const StorageHandle &s) { handle=s.handle; } - StorageHandle(int h) { handle=h; } - StorageHandle &operator=(int h) { handle=h; return *this; } - StorageHandle &operator=(const StorageHandle &s) { handle=s.handle; return *this; } - operator bool() const { return handle!=-1; } - operator int() const { return handle; } -private: - int handle; -}; - -class RootDir { -public: - static void setRootDir(const char *); - static const char *getRootDir(); -protected: - static const char *root; -}; - -class Storage : public RootDir { -public: - virtual ~Storage(); - enum StorageSystem { StorageSystemLegacy, StorageSystemPacked }; - //must be called before the first call to instance() - - static Storage *CreateInstance(StorageSystem system, int storageLimit); - - //must be called before operation starts. Set all options (RootDir, maxStorage) before. - virtual void cleanUp() = 0; - - virtual void getFilename(char *buffer, int bufLength, PageID page); - virtual void prepareDirectory(tChannelID chan); - - virtual StorageHandle openForWriting(PageID page) = 0; - virtual StorageHandle openForReading(PageID page, bool countAsAccess) = 0; - virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream) = 0; - virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) = 0; - virtual void close(StorageHandle stream) = 0; -protected: - Storage(); - int cleanSubDir(const char *dir); - int doCleanUp(); - virtual int actualFileSize(int netFileSize) { return netFileSize; } - void freeSpace(); - bool exists(const char* file); - - long byteCount; - cString currentDir; -private: - bool failedFreeSpace; -}; - -class LegacyStorage : public Storage { -private: - void initMaxStorage(int maxMB=-1); -public: - LegacyStorage(int maxMB); - virtual ~LegacyStorage(); - virtual void cleanUp(); - - virtual StorageHandle openForWriting(PageID page); - virtual StorageHandle openForReading(PageID page, bool countAsAccess); - virtual ssize_t write(const void *ptr, size_t size, StorageHandle stream); - virtual ssize_t read(void *ptr, size_t size, StorageHandle stream) - { return ::read((int)stream, ptr, size); } - virtual void close(StorageHandle stream) - { ::close((int)stream); } -protected: - void registerFile(PageID page); - virtual int actualFileSize(int netFileSize); - //int maxPages; - long maxBytes; - int fsBlockSize; - int pageBytes; -}; - -class PackedStorage : public LegacyStorage { -public: - PackedStorage(int maxMB); - - virtual void getFilename(char *buffer, int bufLength, PageID page); - virtual StorageHandle openForWriting(PageID page); - virtual StorageHandle openForReading(PageID page, bool countAsAccess); -protected: - struct PageAddress { - bool operator==(const PageID &id) const - { return page==id.page && subPage==id.subPage; } - void operator=(const PageID &id) - { page=id.page; subPage=id.subPage; } - int page; - int subPage; - }; - bool seekTo(PageID page, int fd, bool create); - void registerFile(PageID page); -}; +#include "storage.h" class cTelePage { private: -- cgit v1.2.3