]> git.vomp.tv Git - jsonserver.git/commitdiff
Config file and options for log file, docroot and port number
authorChris Tallon <chris@vomp.tv>
Sat, 30 Mar 2013 16:57:33 +0000 (16:57 +0000)
committerChris Tallon <chris@vomp.tv>
Sat, 30 Mar 2013 16:57:33 +0000 (16:57 +0000)
Makefile
config.c [new file with mode: 0644]
config.h [new file with mode: 0644]
jsonserver.c
jsonserver.conf.sample [new file with mode: 0644]

index 586985c92fb23a747fb8cd0c2e603bbfaca68a84..7b48229d3f168be1c4aa42048becf5a9090aec2d 100644 (file)
--- 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 (file)
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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#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
index 2e4ede009208273e5244bcacf3d2fdae308b4f88..cd4013a55f3ae7678e413084f7f44530afa72ab0 100644 (file)
@@ -6,10 +6,12 @@
  */
 
 #include <vdr/plugin.h>
+#include <stdio.h>
 
 #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 (file)
index 0000000..4fdc122
--- /dev/null
@@ -0,0 +1,5 @@
+[General]
+Log file = /var/log/jsonserver.log
+JS App Dir = /srv/vdrweb
+Port number = 8005
+