Changeset 229 for Evidence/Evidence.cc


Ignore:
Timestamp:
06/24/10 07:51:15 (14 years ago)
Author:
ogrimm
Message:
Config requests non-blocking if not made from main thread, adapted all servers to GetConfig() returning std::string, workaround for erroneous SERVICE_LIST
File:
1 edited

Legend:

Unmodified
Added
Removed
  • Evidence/Evidence.cc

    r224 r229  
    99  - If the severity of a Message() call is FATAL, exit() will be called (with
    1010    this severity, the call to Message() is guranteed not to return).
    11   - Configuration data can be requested by GetConfig().
     11  - Configuration data can be requested by GetConfig() and non-blocking by GetConfigNB().
     12  - If the configuration file changes the signal SIGUSR1 is emitted which is caught by the standard
     13    signal handler. The handler invokes ConfigChanged() which can be redefined by the user application.
     14        The signal is delivered only to the main thread (where the constructor is executed) and thus
     15        blocking rpc can be made from it.
    1216  - Signal handlers to ignore common signals are installed.
    1317    These signals will then cause pause() to return which can be used
    1418        by the application to terminate gracefully.
    15   - The static method ToString() converts the contents of a
    16     DIMInfo service into text
     19  - The static method ToString() converts the contents of a DIMInfo service into text
    1720  - A terminate-handler is installed for catching unhandled C++ exceptions.
    1821
    19   Memory allocated by the non-static methods will be freed by the
    20   class destructor.
    21  
    2222  Oliver Grimm, June 2010
    2323 
     
    2727using namespace std;
    2828
     29EvidenceServer *ThisServer;
     30
     31//
     32// Internal configuration class of EvidenceServer
     33//
     34// Data that might be accessed by two threads are protected by mutex
     35
     36// Constructor
     37EvidenceServer::Config::Config(string Name): DimRpcInfo("ConfigRequest", NO_LINK), Name(Name) {
     38
     39  // Initialise
     40  int Ret;
     41  if ((Ret = pthread_mutex_init(&Mutex, NULL)) != 0) {
     42    ThisServer->Message(ThisServer->FATAL, "pthread_mutex_init() failed in Config constructor (%s)", strerror(Ret));
     43  }
     44  CurrentItem = string();
     45  ConfigTimeStamp = 0;
     46 
     47  // SIGUSR2 delivered to this thread if configuration file changes
     48  ThreadID = pthread_self();
     49
     50  // Subscribe to be informed on configuration file change
     51  Service = new DimInfo("Config/ModifyTime", NO_LINK, this);
     52}
     53
     54// Destructor
     55EvidenceServer::Config::~Config() {
     56 
     57  delete Service;
     58
     59  int Ret;
     60  if ((Ret = pthread_mutex_destroy(&Mutex)) != 0) {
     61        ThisServer->Message(ThisServer->ERROR, "pthread_mutex_destroy() failed in Config destructor (%s)", strerror(Ret));
     62  }
     63}
     64
     65// Track last modification time of configuration file
     66void EvidenceServer::Config::infoHandler() {
     67
     68  Lock();
     69  ConfigTimeStamp = getInfo()->getInt();
     70  Unlock();
     71
     72  if (pthread_kill(ThreadID, SIGUSR2) != 0) {
     73        ThisServer->Message(ThisServer->WARN, "Could not send signal SIGUSR2 to main thread");
     74  }
     75}
     76
     77// Receive answer to remote procedure call
     78void EvidenceServer::Config::rpcInfoHandler(){
     79
     80  Lock();
     81  // Update map
     82  List[CurrentItem].Value = string(getString(), getSize()-1);
     83  List[CurrentItem].Time = ConfigTimeStamp;
     84  // Clear to allow new rpc call
     85  CurrentItem.clear();
     86  Unlock();
     87}
     88
     89// Return configuration data if still up to date or empty string otherwise
     90string EvidenceServer::Config::GetConfig(string Item, string Default) {
     91
     92  string Result;
     93
     94  // If up-to-date data in configuration list available, return this
     95  Lock();
     96  if ((List.count(Item) > 0) && (List[Item].Time >= ConfigTimeStamp)) Result = List[Item].Value;
     97  Unlock();
     98  if (!Result.empty()) return Result;
     99
     100  // Blocking configuration request if in main thread
     101  if (pthread_self() == ThreadID) {
     102        DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
     103        Config.setData((char *) (Name + " " + Item).c_str());
     104
     105        // Check if successful
     106        if (!EvidenceServer::ServiceOK(&Config)) {
     107      if (Default.empty()) {
     108                ThisServer->Message(ThisServer->FATAL, "Configuration server unreachable, can't retrieve '%s'", Item.c_str());
     109          }
     110          else Result = Default;
     111        }
     112        else {
     113          if (Config.getSize() == 0) Result = Default;
     114          else Result = string(Config.getString(), Config.getSize()-1); // Retrieve string safely
     115    }
     116
     117        // Update configuration map     
     118        if (!Result.empty()) {
     119          Lock();
     120          List[Item].Value = Result;
     121          List[Item].Time = ConfigTimeStamp;
     122          Unlock();
     123        }       
     124  }
     125
     126  // Non-blocking configuration request fro other threads
     127  if (pthread_self() != ThreadID) {
     128        Lock();
     129        if (List.count(Item) > 0) {
     130          // New request possible only when answer to previous request received
     131          if (CurrentItem.empty()) {
     132                CurrentItem = Item;
     133                setData(((char *) (Name + " " + Item).c_str()));
     134          }
     135
     136          // Return current value
     137          Result = List[Item].Value;
     138          Unlock();
     139        }
     140  }
     141
     142  return Result;
     143}
     144
     145// Locking and unlocking for list access. Signal SIGUSR2 is also blocked.
     146void EvidenceServer::Config::Lock() {
     147
     148  int Ret = 0;
     149  sigset_t Set;
     150  //printf("Locking %u\n", pthread_self());
     151  Ret += abs(sigemptyset(&Set));
     152  Ret += abs(sigaddset(&Set, SIGUSR2));
     153  Ret += abs(pthread_sigmask(SIG_BLOCK, &Set, NULL));
     154  Ret += abs(pthread_mutex_lock(&Mutex));
     155 
     156  if (Ret != 0) {
     157        ThisServer->Message(ThisServer->FATAL, "Thread related call failed in Config::Lock()");
     158  }
     159}
     160
     161void EvidenceServer::Config::Unlock() {
     162
     163  int Ret = 0;
     164  sigset_t Set;
     165  //printf("  Unlocking %u\n", pthread_self());
     166
     167  Ret += abs(pthread_mutex_unlock(&Mutex));
     168  Ret += abs(sigemptyset(&Set));
     169  Ret += abs(sigaddset(&Set, SIGUSR2));
     170  Ret += abs(pthread_sigmask(SIG_UNBLOCK, &Set, NULL));
     171
     172  if (Ret != 0) {
     173        ThisServer->Message(ThisServer->FATAL, "Thread related call failed in Config::Unlock()");
     174  }
     175}
     176
     177
    29178//////////////////////////
    30179// EvidenceServer Class //
    31180//////////////////////////
    32181
    33 EvidenceServer *ThisServer;
    34 
    35182// Constructor starts server with given name
    36 EvidenceServer::EvidenceServer(const char *Name) {
     183EvidenceServer::EvidenceServer(const char *ServerName): Name(ServerName) {
    37184
    38185  // Initialize
     
    41188  ExitRequest = false;
    42189  ThisServer = this;
    43   ServerName = Name;
    44190
    45191  // Catch some signals
     
    48194  signal(SIGINT, &SignalHandler);   // CTRL-C
    49195  signal(SIGHUP, &SignalHandler);   // Terminal closed
     196 
     197  struct sigaction S;
     198  S.sa_handler = &SignalHandler;
     199  S.sa_flags = SA_RESTART;
     200  sigaction(SIGUSR2, &S, NULL);
    50201
    51202  // Catch C++ unhandled exceptions
    52203  set_terminate(Terminate);
    53204
    54   // Subscribe to modify service for keeping track of config file changes
    55   ModifyInfo = new class ConfigUpdate();
     205  // Configuration class (instantiate after signal handling for SIGUSR2 installed)
     206  ConfClass = new class Config(Name);
    56207
    57208  // Message service and initial message
    58   MessageService = new DimService((ServerName+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
     209  MessageService = new DimService((Name+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
    59210
    60211  string Rev(EVIDENCE_REVISION);
     
    62213
    63214  // Start server
    64   start(Name);
    65   addExitHandler(this); 
     215  start(ServerName);
     216  addExitHandler(this);
    66217}
    67218
     
    71222  Message(INFO, "Server stopped");
    72223 
    73   for (unsigned int i=0; i<ConfigList.size(); i++) delete[] ConfigList[i].Value;
    74   delete ModifyInfo;
     224  delete ConfClass;
    75225  delete MessageService;
    76226  delete MessageData;
     
    152302// Program terminates if data is missing and no default given. Actual configuration
    153303// request will be made only if config file has modification since last request.
    154 // The memory allocated by all calls to this function will be freed by
    155 // the destructor.
    156 char* EvidenceServer::GetConfig(string Item, const char *Default) {
    157  
    158   int ItemNo = -1;
    159  
    160   // Check if configuration request already in list
    161   for (unsigned int i=0; i<ConfigList.size(); i++) if (ConfigList[i].Name == Item) {
    162         // Return original value if still up to date
    163         if (ConfigList[i].Time >= ModifyInfo->LastModifyTime) return ConfigList[i].Value;
    164 
    165         // Otherwise, free memory of old value
    166         delete[] ConfigList[i].Value;
    167         ItemNo = i;
    168         break;
    169   }
    170 
    171   // Make configuration request
    172   DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
    173   Config.setData((char *) (ServerName + " " + Item).c_str());
    174   char *Result = Config.getString();
    175 
    176   // Terminate if not successful
    177   if (!EvidenceServer::ServiceOK(&Config)) {
    178     if (Default == NULL) Message(FATAL, "Configuration server unreachable, can't get '%s'", Item.c_str());
    179         Result = (char *) Default;
    180   }
    181 
    182   if (strlen(Result) == 0) {
    183     if (Default == NULL) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
    184         Result = (char *) Default;
    185   }
    186 
    187   // Enlarge list if necessary
    188   if (ItemNo == -1) {
    189     struct ConfigItem New;
    190     ConfigList.push_back(New);
    191         ItemNo = ConfigList.size()-1;
    192   }
    193 
    194   // Create new entry in item list, allocate memory and copy data to this memory
    195   ConfigList[ItemNo].Name = Item;
    196   ConfigList[ItemNo].Value = new char [strlen(Result)+1];
    197   strcpy(ConfigList[ItemNo].Value, Result);
    198   ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime;
    199 
    200   // Return address to configuration value 
    201   return ConfigList[ItemNo].Value;
    202 }
    203 
     304// If called from infoHandler(), a non-blocking request will be made
     305string EvidenceServer::GetConfig(string Item, string Default) {
     306
     307  string Result = ConfClass->GetConfig(Item, Default);
     308  if (Result.empty()) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
     309  return Result;
     310}
    204311
    205312// ====== Static methods ======
     
    209316
    210317  static bool Called = false;
     318
     319  // If SIGUSR2, invoke call-back for configuraton change
     320  if (Signal == SIGUSR2) {
     321    ThisServer->ConfigChanged();
     322        return;
     323  }
    211324
    212325  // At first invocation just request exit
     
    261374
    262375
    263 // Translates DIMInfo to string (memory has to be freed by caller)
    264 // Static method, cannot report memory allocation errors via Message() but returns NULL
    265 char *EvidenceServer::ToString(DimInfo *Item) {
    266 
    267   char *Text;
    268 
     376// Translates DIMInfo safely to string (assures no inivalid memory accesses are made)
     377string EvidenceServer::ToString(DimInfo *I) {
     378
     379  ostringstream Text;
     380 
    269381  // Safety check
    270   if (Item->getSize() < 1) return NULL;
    271 
    272   // Message structure format handled
    273   if (strcmp(Item->getFormat(), "I:1;C") == 0) {
    274         struct Message *Msg = (struct Message *) Item->getData();
    275         if (asprintf(&Text, "%d %s", Msg->Severity, Msg->Text) == -1) return NULL;
    276         else return Text;
    277   }
    278  
    279   // Structure: print hex representation (3 characters per byte) 
    280   if (strlen(Item->getFormat()) != 1) {
    281     int Size = 3*Item->getSize()+1, N;
    282     if ((Text = (char *) malloc(Size)) == NULL) return NULL;
    283        
    284         char *CurrPos = Text;
    285         for (int i=0; i<Item->getSize(); i++) {
    286           N = snprintf(CurrPos, Size, "%02x ", *((char *) Item->getData() + i));
    287           if (N<0 || N>=Size) {
    288             free(Text);
    289                 if (asprintf(&Text, "Structure length %d bytes, buffer overflow in ToString()", Item->getSize()) == -1) return NULL;
    290                 else return Text;
    291           }
    292           Size -= N;
    293           CurrPos += N;
     382  if (I->getSize() < 1) return string();
     383
     384  // 'Message' service format handled here
     385  if (strcmp(I->getFormat(), "I:1;C") == 0 && I->getSize()>=(int) sizeof(struct Message)) {
     386        struct Message *Msg = (struct Message *) I->getData();
     387        // Safely extract string and limit to length of C string (padding might make it longer)
     388        string MsgText(Msg->Text, I->getSize()-sizeof(struct Message));
     389    //MsgText.erase(strlen(MsgText.c_str()));
     390        Text << Msg->Severity << " " << MsgText.erase(strlen(MsgText.c_str()));
     391
     392        return Text.str();
     393  }
     394 
     395  // Structure: print hex representation
     396  if (strlen(I->getFormat()) != 1) {
     397        for (int i=0; i<I->getSize(); i++) {
     398          Text << setw(2) << hex << *((char *) I->getData() + i) << " ";
    294399        }
    295         return Text;
    296   }
    297 
    298   // String: terminating \0 is enforced
    299   if (toupper(*(Item->getFormat())) == 'C') { 
    300     *(Item->getString() + Item->getSize() - 1) = '\0';
    301         if (asprintf(&Text, "%s", Item->getString()) == -1) return NULL;
    302         return Text;
     400        return Text.str();
     401  }
     402
     403  // String if format "C" and terminated with \0
     404  if (strcmp(I->getFormat(),"C")==0 && *((char *) I->getData()+I->getSize()-1)=='\0') { 
     405    Text << I->getString();
     406        return Text.str();
    303407  }
    304408
    305409  // Number array
    306410  int Size;
    307   switch (toupper(*(Item->getFormat()))) {
     411  switch (*(I->getFormat())) {
     412    case 'C': Size = sizeof(char);              break;
    308413    case 'I':
    309414    case 'L': Size = sizeof(int);               break;
     
    312417    case 'D': Size = sizeof(double);    break;
    313418    case 'X': Size = sizeof(long long); break;
    314     default: return NULL;
    315   }
    316 
    317   int Max, Mem = Item->getSize()*Size*4+1;
    318   char *Pos;
    319 
    320   if ((Text = (char *) malloc(Mem)) == NULL) return NULL;
    321 
    322   *Text = '\0';
    323   for (int i=0; i<Item->getSize()/Size; i++) {
    324         Pos = Text+strlen(Text);
    325         Max = Mem-strlen(Text);
    326 
    327         switch (toupper(*(Item->getFormat()))) {
     419    default: return string();
     420  }
     421
     422  for (int i=0; i<I->getSize()/Size; i++) {
     423        // Space between entries
     424    if (i != 0) Text << " ";
     425
     426        // Translate data
     427        switch (*(I->getFormat())) {
     428      case 'C': Text << *((char *) I->getData() + i);
     429                                break;
    328430      case 'I':
    329       case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i));
     431      case 'L': Text << *((int *) I->getData() + i);
    330432                                break;
    331       case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i));
     433      case 'S': Text << *((short *) I->getData() + i);
    332434                                break;
    333       case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i));
     435      case 'F': Text << *((float *) I->getData() + i);
    334436                                break;
    335       case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i));
     437      case 'D': Text << *((double *) I->getData() + i);
    336438                                break;
    337       case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i));
     439      case 'X': Text << *((long long *) I->getData() + i);
    338440                                break;
    339441        }
    340442  }
    341443 
    342   return Text;
    343 }
     444  return Text.str();
     445}
     446
    344447
    345448// Checks if service contents indicates not available
     
    355458          (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
    356459}
     460
     461
     462// Tokenize std::string using given delimeter list
     463vector<string> EvidenceServer::Tokenize(const string &String, const string &Delimiters) {
     464
     465  vector<string> Tokens;
     466  string::size_type Next, EndNext=0;
     467   
     468  while (EndNext != string::npos) {
     469    // Find next token
     470    Next = String.find_first_not_of(Delimiters, EndNext);
     471    EndNext = String.find_first_of(Delimiters, Next);
     472
     473        // Stop if end of string reached
     474        if (Next == string::npos) break;
     475
     476    // Add token to vector.
     477    Tokens.push_back(String.substr(Next, EndNext - Next));
     478  }
     479 
     480  return Tokens;
     481}
     482
    357483
    358484///////////////////////////
Note: See TracChangeset for help on using the changeset viewer.