From 65ba5a9fe152cff85d5511e439cd74c681c1e7b5 Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Sat, 30 Mar 2013 16:57:33 +0000 Subject: [PATCH] Config file and options for log file, docroot and port number --- Makefile | 2 +- config.c | 524 +++++++++++++++++++++++++++++++++++++++++ config.h | 80 +++++++ jsonserver.c | 82 ++++++- jsonserver.conf.sample | 5 + 5 files changed, 686 insertions(+), 7 deletions(-) create mode 100644 config.c create mode 100644 config.h create mode 100644 jsonserver.conf.sample diff --git a/Makefile b/Makefile index 586985c..7b48229 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o mongoose.o handler.o log.o +OBJS = $(PLUGIN).o mongoose.o handler.o log.o config.o ### The main target: diff --git a/config.c b/config.c new file mode 100644 index 0000000..3866abc --- /dev/null +++ b/config.c @@ -0,0 +1,524 @@ +/* + Copyright 2004-2005 Chris Tallon + Copyright 2004-2005 University Of Bradford + + This file is part of VOMP. + + VOMP 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. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +Config::Config() +{ + initted = 0; + lastLineLength = 0; + log = Log::getInstance(); +} + +int Config::init(char* takeFileName) +{ + if (initted) return 1; + + pthread_mutex_init(&fileLock, NULL); + + if (strlen(takeFileName) > (MAX_FILENAME_LENGTH - 1)) + { + log->log("Config", Log::DEBUG, "Config error: Config filename too long"); + return 0; + } + + strcpy(fileName, takeFileName); + strcpy(fileNameTemp, takeFileName); + strcat(fileNameTemp, ".tmp"); + + file = fopen(fileName, "r"); + if (!file) + { + file = fopen(fileName, "w"); + if (!file) + { + log->log("Config", Log::DEBUG, "Config error: Could not access config file"); + return 0; + } + } + fclose(file); + + initted = 1; + log->log("Config", Log::DEBUG, "Opened config file: %s", fileName); + + return 1; +} + +int Config::status() +{ + return initted; +} + +int Config::shutdown() +{ + if (!initted) return 1; + + pthread_mutex_lock(&fileLock); + initted = 0; + pthread_mutex_unlock(&fileLock); + pthread_mutex_destroy(&fileLock); + + return 1; +} + +int Config::openFile() +{ + if (!initted) return 0; + if (pthread_mutex_lock(&fileLock)) + { + log->log("Config", Log::DEBUG, "Config error: Could not get lock"); + return 0; + } + if (!initted) + { + log->log("Config", Log::DEBUG, "Config error: Initted 0 after lock"); + pthread_mutex_unlock(&fileLock); + return 0; + } + + file = fopen(fileName, "r"); + if (!file) + { + log->log("Config", Log::DEBUG, "Config error: Could not open config file"); + pthread_mutex_unlock(&fileLock); + return 0; + } + return 1; +} + +void Config::closeFile() +{ + if (!initted) return; + + fclose(file); + file = NULL; + pthread_mutex_unlock(&fileLock); +} + +int Config::readLine() +{ + if (!initted || !file) { log->log("Config", Log::DEBUG, "1"); return 0; } + if (!fgets(buffer, BUFFER_LENGTH-1, file)) { /*log->log("Config", Log::DEBUG, "2");*/ return 0; } + lastLineLength = strlen(buffer); +// log->log("Config", Log::DEBUG, "buffer before trim: '%s'", buffer); + trim(buffer); +// log->log("Config", Log::DEBUG, "buffer after trim: '%s'", buffer); + return 1; +} + +// START HERE + +FILE* Config::copyToHere(long position) +{ + FILE* newFile = fopen(fileNameTemp, "w"); + + if (!newFile) return NULL; + + long newPos = 0; + rewind(file); + + while (newPos < position) + { + fgets(buffer, BUFFER_LENGTH-1, file); + fputs(buffer, newFile); + newPos += strlen(buffer); + } + return newFile; +} + +int Config::copyRest(FILE* newFile) +{ + if (newFile) + { + while(fgets(buffer, BUFFER_LENGTH-1, file)) + { + fputs(buffer, newFile); + } + + fclose(newFile); + } + fclose(file); + file = NULL; + + if (newFile) rename(fileNameTemp, fileName); + + pthread_mutex_unlock(&fileLock); + return 1; +} + +int Config::deleteValue(const char* section, char* key) +{ + if (!initted) return 0; + if (!openFile()) return 0; + + if (!findSection(section)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Section %s not found", section); + return 0; + } + if (!findKey(key)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Key %s not found", key); + return 0; + } + + FILE* newFile = copyToHere(ftell(file) - lastLineLength); + fgets(buffer, BUFFER_LENGTH-1, file); + + return copyRest(newFile); +} + +int Config::setValueLong(const char* section, char* key, long newValue) +{ + char longBuffer[50]; + sprintf(longBuffer, "%li", newValue); + return setValueString(section, key, longBuffer); +} + +int Config::setValueLongLong(char* section, char* key, long long newValue) +{ + char longBuffer[50]; + sprintf(longBuffer, "%lli", newValue); + return setValueString(section, key, longBuffer); +} + +int Config::setValueDouble(char* section, char* key, double newValue) +{ + char doubleBuffer[50]; + sprintf(doubleBuffer, "%f", newValue); + return setValueString(section, key, doubleBuffer); +} + +int Config::setValueString(const char* section, const char* key, const char* newValue) +{ + if (!initted) return 0; + if (!openFile()) return 0; + + if (findSection(section)) + { + if (findKey(key)) + { + FILE* newFile = copyToHere(ftell(file) - lastLineLength); + if (!newFile) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Could not write temp config file"); + return 0; + } + + fgets(buffer, BUFFER_LENGTH-1, file); + fprintf(newFile, "%s = %s\n", key, newValue); + return copyRest(newFile); + } + else + { + rewind(file); + findSection(section); + FILE* newFile = copyToHere(ftell(file)); + if (!newFile) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Could not write temp config file"); + return 0; + } + + fprintf(newFile, "%s = %s\n", key, newValue); + return copyRest(newFile); + } + } + else + { + // section not found + fseek(file, 0, SEEK_END); + FILE* newFile = copyToHere(ftell(file)); + if (!newFile) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Could not write temp config file"); + return 0; + } + + fprintf(newFile, "[%s]\n%s = %s\n", section, key, newValue); + return copyRest(newFile); + } +} + +char* Config::getSectionKeyNames(const char* section, int& numberOfReturns, int& allKeysSize) +{ + numberOfReturns = 0; + allKeysSize = 0; + char* allKeys = NULL; + int allKeysIndex = 0; + int keyLength; + char* equalspos; + + if (!initted) return NULL; + if (!openFile()) return NULL; + if (!findSection(section)) return NULL; + + char foundKey[BUFFER_LENGTH]; + + while(readLine()) + { + // Is this line a section header? if so, exit + if ((buffer[0] == '[') && (buffer[strlen(buffer)-1] == ']')) break; + + equalspos = strstr(buffer, "="); + if (!equalspos) continue; // if there is no = then it's not a key + memcpy(foundKey, buffer, equalspos-buffer); + foundKey[equalspos-buffer] = '\0'; + trim(foundKey); + keyLength = strlen(foundKey); + allKeysSize += keyLength + 1; + allKeys = (char*)realloc(allKeys, allKeysSize); + memcpy(&allKeys[allKeysIndex], foundKey, keyLength); + allKeysIndex += keyLength; + allKeys[allKeysIndex] = '\0'; + allKeysIndex++; + numberOfReturns++; + } + + closeFile(); + return allKeys; +} + + +// END HERE + +int Config::findSection(const char* section) +{ + if (!initted || !file) return 0; + if (strlen(section) > (BUFFER_LENGTH-2)) + { + log->log("Config", Log::DEBUG, "Config error: Section given exceeds max length"); + return 0; + } + + char toFind[BUFFER_LENGTH]; + toFind[0] = '['; + toFind[1] = '\0'; + strcat(toFind, section); + strcat(toFind, "]"); + + while(readLine()) + { +// log->log("Config", Log::DEBUG, "to find '%s' this line '%s'", toFind, buffer); + if (!strcmp(toFind, buffer)) return 1; + } + return 0; +} + +int Config::findKey(const char* key) +{ + if (!initted || !file) return 0; + + if (strlen(key) > (BUFFER_LENGTH-1)) + { + log->log("Config", Log::DEBUG, "Config error: Key given exceeds max length"); + return 0; + } + + char prepForTest[BUFFER_LENGTH]; + + // do a rough search first, this could match substrings that we don't want + while(readLine()) + { + // Is this line a section header? if so, exit + if ((buffer[0] == '[') && (buffer[strlen(buffer)-1] == ']')) return 0; + if (strstr(buffer, key)) + { + // rough search found match + char* equalspos = strstr(buffer, "="); + if (!equalspos) continue; + memcpy(prepForTest, buffer, equalspos-buffer); + prepForTest[equalspos-buffer] = '\0'; + trim(prepForTest); + + if (!strcmp(key, prepForTest)) + { + // in buffer, set all up to equals to space, then trim! + for(char* curPos = buffer; curPos <= equalspos; curPos++) + { + *curPos = ' '; + } + trim(buffer); + return 1; + } + } + } + return 0; +} + +char* Config::getValueString(const char* section, const char* key) +{ + if (!initted) return NULL; + if (!openFile()) return NULL; + + if (!findSection(section)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Section %s not found", section); + return 0; + } + if (!findKey(key)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Key %s not found", key); + return 0; + } + + char* returnString = new char[strlen(buffer)+1]; + strcpy(returnString, buffer); + + closeFile(); + + return returnString; +} + +long Config::getValueLong(const char* section, const char* key, int* failure) +{ + *failure = 1; + if (!initted) return 0; + if (!openFile()) return 0; + + if (!findSection(section)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Section %s not found", section); + return 0; + } + if (!findKey(key)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Key %s not found", key); + return 0; + } + *failure = 0; + + char* check; + long retVal = strtol(buffer, &check, 10); + if ((retVal == 0) && (check == buffer)) *failure = 1; + closeFile(); + + return retVal; +} + +long long Config::getValueLongLong(char* section, char* key, int* failure) +{ + *failure = 1; + if (!initted) return 0; + if (!openFile()) return 0; + + if (!findSection(section)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Section %s not found", section); + return 0; + } + if (!findKey(key)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Key %s not found", key); + return 0; + } + *failure = 0; + + char* check; + long long retVal = strtoll(buffer, &check, 10); + if ((retVal == 0) && (check == buffer)) *failure = 1; + closeFile(); + + return retVal; +} + +double Config::getValueDouble(char* section, char* key, int* failure) +{ + *failure = 1; + if (!initted) return 0; + if (!openFile()) return 0; + + if (!findSection(section)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Section %s not found", section); + return 0; + } + if (!findKey(key)) + { + closeFile(); + log->log("Config", Log::DEBUG, "Config error: Key %s not found", key); + return 0; + } + + *failure = 0; + + char* check; + double retVal = strtod(buffer, &check); + if ((retVal == 0) && (check == buffer)) *failure = 1; + + closeFile(); + + return retVal; +} + + + +void Config::trim(char* str) +{ + int pos, len, start, end; + + // Kill comments + len = strlen(str); + for(pos = 0; pos < len; pos++) + { + if ((str[pos] == '#') || (str[pos] == ';')) + { + // Mod. If #/; is at start of line ok. Else, if it is after a space, ok. + + if ((pos == 0) || (isspace(str[pos - 1]))) + { + str[pos] = '\0'; + break; + } + + } + } + + len = strlen(str); + end = len; + if (!len) return; + + start = 0; + while(isspace(str[start])) start++; + while(isspace(str[end-1])) + { + end--; + if (end == 0) + { + str[0] = '\0'; + return; + } + } + for(pos = start; pos < end; pos++) str[pos - start] = str[pos]; + str[end - start] = '\0'; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..2e4a2dd --- /dev/null +++ b/config.h @@ -0,0 +1,80 @@ +/* + Copyright 2004-2005 Chris Tallon + Copyright 2004-2005 University Of Bradford + + This file is part of VOMP. + + VOMP 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. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include +#include +#include + +#include "log.h" + +#define MAX_FILENAME_LENGTH 500 +#define BUFFER_LENGTH 1500 + +class Config +{ + public: + Config(); + + int init(char* fileName); + int shutdown(); + int status(); + + char* getValueString(const char* section, const char* key); + long getValueLong(const char* section, const char* key, int* failure); + long long getValueLongLong(char* section, char* key, int* failure); + double getValueDouble(char* section, char* key, int* failure); + + int setValueString(const char* section, const char* key, const char* newValue); + int setValueLong(const char* section, char* key, long newValue); + int setValueLongLong(char* section, char* key, long long newValue); + int setValueDouble(char* section, char* key, double newValue); + + int deleteValue(const char* section, char* key); // err.. delete "key". + char* getSectionKeyNames(const char* section, int& numberOfReturns, int& length); + + private: + pthread_mutex_t fileLock; + int initted; + int lastLineLength; + Log* log; + + char fileName[MAX_FILENAME_LENGTH]; + char fileNameTemp[MAX_FILENAME_LENGTH]; + + FILE* file; + char buffer[BUFFER_LENGTH]; + + int openFile(); + void closeFile(); + int readLine(); + int findSection(const char* section); + int findKey(const char* key); + void trim(char* sting); + FILE* copyToHere(long position); + int copyRest(FILE* newFile); +}; + +#endif diff --git a/jsonserver.c b/jsonserver.c index 2e4ede0..cd4013a 100644 --- a/jsonserver.c +++ b/jsonserver.c @@ -6,10 +6,12 @@ */ #include +#include #include "mongoose.h" #include "handler.h" #include "log.h" +#include "config.h" static const char *VERSION = "0.0.1"; static const char *DESCRIPTION = "JSON data server plugin for VDR"; @@ -19,7 +21,10 @@ class cPluginJsonserver : public cPlugin { private: // Add any member variables or functions you may need here. struct mg_context *mg; - Log* log; + Log* log; + Config* config; + char* cfgDocRoot; + char* cfgPort; public: cPluginJsonserver(void); virtual ~cPluginJsonserver(); @@ -49,15 +54,20 @@ cPluginJsonserver::cPluginJsonserver(void) // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! - log = new Log(); - if (log) log->init(Log::DEBUG, "/tmp/jsonserver.log"); + config = NULL; + log = NULL; + cfgDocRoot = NULL; + cfgPort = NULL; } cPluginJsonserver::~cPluginJsonserver() { // Clean up after yourself! - delete log; + if (log) delete log; log = NULL; + + if (config) delete config; + config = NULL; } const char *cPluginJsonserver::CommandLineHelp(void) @@ -81,12 +91,70 @@ bool cPluginJsonserver::Initialize(void) bool cPluginJsonserver::Start(void) { // Start any background activities the plugin shall perform. + const char* configDir = cPlugin::ConfigDirectory("jsonserver"); + if (!configDir) + { + dsyslog("jsonserver: Error: Could not get config dir from VDR"); + return false; + } + char* configFile; + asprintf(&configFile, "%s/jsonserver.conf", configDir); + dsyslog(configFile); + + log = new Log(); + + config = new Config(); + if (config->init(configFile)) + { + dsyslog("jsonserver: Config file found"); + free(configFile); + } + else + { + dsyslog("jsonserver: Error: Config file not found"); + free(configFile); + delete config; + config = NULL; + return false; + } + + char* cfgLogFilename = config->getValueString("General", "Log file"); + if (cfgLogFilename) + { + log->init(Log::DEBUG, cfgLogFilename); + delete[] cfgLogFilename; + log->log("Main", Log::INFO, "Logging started"); + } + else + { + dsyslog("jsonserver: Logging disabled"); + } + + cfgDocRoot = config->getValueString("General", "JS App Dir"); + if (!cfgDocRoot) + { + log->log("Main", Log::CRIT, "Config General/JS App Dir not found"); + dsyslog("jsonserver: Error: Could not load JS App Dir from plugin config file"); + delete log; + delete config; + log = NULL; + config = NULL; + return false; + } + + cfgPort = config->getValueString("General", "Port number"); + if (!cfgPort) + { + cfgPort = new char[5]; + strcpy(cfgPort, "8005"); + } + // Make Mongoose options const char *options[] = { - "document_root", "/home/chris/dvb/vdr-1.7.32/PLUGINS/src/jsonserver/docroot", - "listening_ports", "8005", + "document_root", cfgDocRoot, + "listening_ports", cfgPort, "num_threads", "5", NULL }; @@ -105,6 +173,8 @@ void cPluginJsonserver::Stop(void) { // Stop any background activities the plugin is performing. mg_stop(mg); + if (cfgDocRoot) delete[] cfgDocRoot; cfgDocRoot = NULL; + if (cfgPort) delete[] cfgPort; cfgPort = NULL; } void cPluginJsonserver::Housekeeping(void) diff --git a/jsonserver.conf.sample b/jsonserver.conf.sample new file mode 100644 index 0000000..4fdc122 --- /dev/null +++ b/jsonserver.conf.sample @@ -0,0 +1,5 @@ +[General] +Log file = /var/log/jsonserver.log +JS App Dir = /srv/vdrweb +Port number = 8005 + -- 2.39.5