Changeset 203


Ignore:
Timestamp:
04/26/10 15:56:18 (15 years ago)
Author:
ogrimm
Message:
Config provides service containing all configuration data, config parsing improved
Location:
Evidence
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • Evidence/Config.cc

    r187 r203  
    33  Configuration server for the Evidence Control System
    44
    5   - The configuration file is opened without buffering to catch changes
    6     without closing/opening.
    75  - The name of a configuration file can be given as command line argument
    86  - If a configuration file change is detected through inotify, the service
    9     "Config/ModifyTime" is updated with the current UNIX time to inform applications.
    10         The initial value of the server is the last file modification time.
    11   - The employed line buffer has conservatively at least the size of the
    12     configuration file. If needed, it will be enlarged.
     7    "Config/ModifyTime" contains the last modification UNIX time.
     8  - The contents of the configuration file is available through Config/ConfigData.
     9  - The current parser removes all tabs, multiple, leading and trailing spaces, and
     10    concatenates lines ending with '+'.
     11
     12  A mutex is used for preventing concurrent access to the configuration data from
     13  the methods rpcHandler() and ConfigChanged().
    1314         
    14   Oliver Grimm, November 2009
     15  Oliver Grimm, April 2010
    1516
    1617\********************************************************************/
     
    2425#include <sys/stat.h>
    2526#include <sys/inotify.h>
     27#include <sstream>
     28
     29using namespace std;
    2630
    2731//
     
    3135
    3236  private:
     37        struct Item {
     38          string Name;
     39          string Data;
     40        };
     41        vector<struct Item> List;
     42
    3343    FILE *File;
    34     char *Buffer;
    35     unsigned int BufferLength;
     44        char *FileContent;
    3645    DimService *ConfigModified;
    37     int ModifyTime;
     46        DimService *ConfigContent;
     47    pthread_mutex_t Mutex;
    3848
    3949    void rpcHandler();
     
    5161        DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) {
    5262
     63  ConfigModified = NULL;        // Will be allocated in ConfigChanged()
     64  ConfigContent = NULL;
     65  FileContent = NULL;
     66
     67  // Initialise mutex (errno is not set by pthread_mutex_init())
     68  if (pthread_mutex_init(&Mutex, NULL) != 0) {
     69    State(FATAL, "pthread_mutex_init() failed");
     70  }
     71
    5372  // Open configuration file
    5473  if ((File = fopen(Filename, "r")) == NULL) {
     
    5675  }
    5776
    58   // Create DIM service to indicate changes of configuration file
    59   struct stat Stat;
    60   if (stat(Filename, &Stat) == -1) {
    61     State(WARN, "Could not read last modification time of configuration file '%s' (%s)", Filename, strerror(errno));
    62         ModifyTime = 0;
    63   }
    64   else ModifyTime = Stat.st_mtime;   
    65   ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
    66 
    6777  // Disable buffering, so file modifications are immediately seen
    6878  if (setvbuf(File, NULL, _IONBF, 0) != 0) {
    6979    State(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
    7080  }
    71    
    72   Buffer = NULL;    // Will be allocated in rpcHandler()
    73   BufferLength = 0;
     81 
     82  // Create DIM services
     83  ConfigChanged();
    7484}
    7585
     
    7787EvidenceConfig::~EvidenceConfig() {
    7888
    79   if (File != NULL) fclose(File); 
    80   delete[] Buffer;
    81 }
     89  if (File != NULL && fclose(File) != 0) State(ERROR, "Error closing configuration file (%s)", strerror(errno));
     90
     91  delete ConfigModified;
     92  delete ConfigContent;
     93  delete[] FileContent;
     94
     95  if (pthread_mutex_destroy(&Mutex) != 0) State(ERROR, "pthread_mutex_destroy() failed");
     96}
     97
    8298
    8399// Implementation of response to configuration request
    84100void EvidenceConfig::rpcHandler() {
    85101
    86   char *Token1,*Token2,*Token3, *Request = getString();
    87   struct stat FileStatus;
    88  
    89   // Check if Buffer[] is large enough to hold full file, enlarge if necessary
    90   if (fstat(fileno(File), &FileStatus) == -1) {
    91      State(FATAL, "Could not determine size of configuration file to allocate buffer (%s)", strerror(errno));
    92   }
    93   else if(BufferLength < FileStatus.st_size) {
    94     delete[] Buffer;
    95     Buffer = new char [FileStatus.st_size];
    96     BufferLength = FileStatus.st_size;   
    97   }
    98  
    99   // Search for configuration item
    100   rewind(File);
    101   while (fgets(Buffer, BufferLength, File) != NULL) {
     102  string Response;
     103 
     104  // Lock because ConfigChange() might access concurrently
     105  if (pthread_mutex_lock(&Mutex) != 0) State(ERROR, "pthread_mutex_lock() failed in rpcHandler()");
     106
     107  // Search for config data in list
     108  for (int i=0; i<List.size(); i++) {
     109    if (List[i].Name == getString()) Response = List[i].Data;
     110  }
     111 
     112  // Unlock
     113  if (pthread_mutex_unlock(&Mutex) != 0) State(ERROR, "pthread_mutex_unlock() failed in rpcHandler()");
     114
     115  // Send data and update Status
     116  setData((char *) Response.c_str());
    102117   
    103     // Combine lines that end with '+'
    104     while (Buffer[strlen(Buffer)-2] == '+') {
    105       if (fgets(Buffer+strlen(Buffer)-2, BufferLength-(strlen(Buffer)-2), File) == NULL) break;
    106     }
    107    
    108     // Ignore comments
    109     for (int i=0; i<strlen(Buffer); i++) if (Buffer[i] == '#') Buffer[i] = '\0';
    110    
    111     // Extract tokens
    112     Token1 = strtok(Buffer, " \t:");
    113     Token2 = strtok(NULL, " \t:");
    114     Token3 = strtok(NULL, "\n");
    115    
    116     // Check if all tokens existing
    117     if(Token1==NULL || Token2==NULL || Token3==NULL) continue;
    118 
    119     // Check for match and then send data (removing whitespace and both ends)
    120     if (strstr(Request, Token1)!=NULL && strstr(Request, Token2)!=NULL) {
    121           while (isspace(*Token3) != 0) Token3++;
    122           while ((strlen(Token3)>0) && isspace(*(Token3+strlen(Token3)-1))) *(Token3+strlen(Token3)-1) = '\0';
    123       setData(Token3);
    124       break;
    125     }
    126   }
    127  
    128   // If configuration data not found, send empty string
    129   if (feof(File)!=0) setData((char *) "");
    130  
    131118  State(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
    132119                DimServer::getClientName(),
    133120                DimServer::getClientId(),
    134                 Request, feof(File)!=0 ? "n/a" : Token3);
    135 }
     121                getString(), Response.c_str());
     122}
     123
    136124
    137125// Signalisation of configuration change
    138126void EvidenceConfig::ConfigChanged() {
    139127
    140   ModifyTime = time(NULL);
    141   ConfigModified->updateService();
    142 }
     128  static int ModifyTime;
     129
     130  //
     131  // Part A: Handle updates to DIM services
     132  //
     133 
     134  // Access status information for configuration file (if fileno() returns -1, fstat() reports error)
     135  struct stat Stat;
     136  if (fstat(fileno(File), &Stat) == -1) {
     137    State(ERROR, "Error with stat() system call for configuration file, cannot update configuration file information (%s)", strerror(errno));
     138        delete ConfigModified;
     139        delete ConfigContent;
     140        ConfigModified = NULL;
     141        ConfigContent = NULL;
     142        return;
     143  }
     144
     145  // Create/update DIM service to indicate changes of configuration file
     146  ModifyTime = Stat.st_mtime;   
     147  if (ConfigModified == NULL) ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
     148  else ConfigModified->updateService();
     149 
     150  // Read new configuration file (enfore \0 termination)
     151  char *NewContent = new char [Stat.st_size+1];
     152  rewind(File);
     153  if (fread(NewContent, sizeof(char), Stat.st_size, File) != Stat.st_size) {
     154    State(FATAL, "Could not read configuration file");
     155  }
     156  NewContent[Stat.st_size] = '\0';
     157
     158  // Create/update DIM service that contains the current configuration file
     159  if (ConfigContent == NULL )ConfigContent = new DimService(SERVER_NAME"/ConfigData", NewContent);
     160  else ConfigContent->updateService(NewContent);
     161
     162  delete[] FileContent;
     163  FileContent = NewContent;
     164
     165  //
     166  // Part B: Interpret configuration list
     167  //
     168
     169  stringstream In(FileContent), Out;
     170  string Line;
     171  struct Item New;
     172  size_t Pos;
     173 
     174  // First clean up and concatenate lines
     175  while (getline(In, Line).good()) {
     176    // Remove comments
     177        if (Line.find('#') != string::npos) Line.erase(Line.find('#'));
     178    // Repace all tabs by spaces
     179    while (Line.find("\t") != string::npos) Line[Line.find("\t")] = ' ';
     180        // Remove leading spaces
     181        while (!Line.empty() && isspace(Line[0])) Line.erase(0, 1);
     182        // Remove trailing spaces
     183        while (!Line.empty() && isspace(Line[Line.size()-1])) Line.erase(Line.size()-1);
     184        // Remove empty lines
     185        if (Line.empty()) continue;
     186        // Concatenate if line ends with '+'
     187    if (Line[Line.size()-1] != '+') Out << Line << endl;
     188        else Out << Line.erase(Line.size()-1);
     189  };
     190 
     191  In.str(Out.str());
     192  In.clear();
     193
     194  // Interpret data
     195  while (getline(In, Line).good()) {
     196    // Remove multiple spaces
     197    while (Line.find("  ") != string::npos) Line.erase(Line.find("  "), 1);
     198
     199        // Find second space character
     200    Pos = Line.find(" ", Line.find(" ") + 1);
     201        if(Pos == string::npos) continue;
     202
     203        // Extract configuration name and data
     204        New.Name = string(Line, 0, Pos);
     205        New.Data = string(Line, Pos+1);
     206
     207        // Add to configuration list
     208        if (pthread_mutex_lock(&Mutex) != 0) State(ERROR, "pthread_mutex_lock() failed in ConfigChanged()");
     209        List.push_back(New);
     210        if (pthread_mutex_unlock(&Mutex) != 0) State(ERROR, "pthread_mutex_unlock() failed in ConfigChanged()");
     211  };
     212}
     213
    143214
    144215//         
  • Evidence/DColl.cc

    r199 r203  
    319319
    320320        if (Text != NULL) {
    321           // Replace all control characters by white space
    322           for (int i=0; i<strlen(Text); i++) if (iscntrl(Text[i])) Text[i] = ' ';
     321          // Replace new line by '\' and all other control characters by white space
     322          for (int i=0; i<strlen(Text); i++) {
     323                if (Text[i] == '\n') Text[i] = '\\';
     324                else if (iscntrl(Text[i])) Text[i] = ' ';
     325          }
    323326
    324327          // Write to file
Note: See TracChangeset for help on using the changeset viewer.