Index: Evidence/Evidence.cc
===================================================================
--- Evidence/Evidence.cc	(revision 224)
+++ Evidence/Evidence.cc	(revision 229)
@@ -9,15 +9,15 @@
   - If the severity of a Message() call is FATAL, exit() will be called (with
     this severity, the call to Message() is guranteed not to return).
-  - Configuration data can be requested by GetConfig(). 
+  - Configuration data can be requested by GetConfig() and non-blocking by GetConfigNB().
+  - If the configuration file changes the signal SIGUSR1 is emitted which is caught by the standard
+    signal handler. The handler invokes ConfigChanged() which can be redefined by the user application.
+	The signal is delivered only to the main thread (where the constructor is executed) and thus
+	blocking rpc can be made from it.
   - Signal handlers to ignore common signals are installed.
     These signals will then cause pause() to return which can be used
 	by the application to terminate gracefully.
-  - The static method ToString() converts the contents of a
-    DIMInfo service into text
+  - The static method ToString() converts the contents of a DIMInfo service into text
   - A terminate-handler is installed for catching unhandled C++ exceptions.
 
-  Memory allocated by the non-static methods will be freed by the
-  class destructor.
-  
   Oliver Grimm, June 2010
  
@@ -27,12 +27,159 @@
 using namespace std;
 
+EvidenceServer *ThisServer;
+
+//
+// Internal configuration class of EvidenceServer
+//
+// Data that might be accessed by two threads are protected by mutex
+
+// Constructor
+EvidenceServer::Config::Config(string Name): DimRpcInfo("ConfigRequest", NO_LINK), Name(Name) {
+
+  // Initialise
+  int Ret;
+  if ((Ret = pthread_mutex_init(&Mutex, NULL)) != 0) {
+    ThisServer->Message(ThisServer->FATAL, "pthread_mutex_init() failed in Config constructor (%s)", strerror(Ret));
+  }
+  CurrentItem = string();
+  ConfigTimeStamp = 0;
+  
+  // SIGUSR2 delivered to this thread if configuration file changes 
+  ThreadID = pthread_self();
+
+  // Subscribe to be informed on configuration file change
+  Service = new DimInfo("Config/ModifyTime", NO_LINK, this);
+}
+
+// Destructor
+EvidenceServer::Config::~Config() {
+  
+  delete Service;
+
+  int Ret;
+  if ((Ret = pthread_mutex_destroy(&Mutex)) != 0) {
+	ThisServer->Message(ThisServer->ERROR, "pthread_mutex_destroy() failed in Config destructor (%s)", strerror(Ret));
+  }
+}
+
+// Track last modification time of configuration file
+void EvidenceServer::Config::infoHandler() {
+
+  Lock();
+  ConfigTimeStamp = getInfo()->getInt();
+  Unlock();
+
+  if (pthread_kill(ThreadID, SIGUSR2) != 0) {
+	ThisServer->Message(ThisServer->WARN, "Could not send signal SIGUSR2 to main thread");
+  }
+}
+
+// Receive answer to remote procedure call
+void EvidenceServer::Config::rpcInfoHandler(){
+
+  Lock();
+  // Update map
+  List[CurrentItem].Value = string(getString(), getSize()-1);
+  List[CurrentItem].Time = ConfigTimeStamp;
+  // Clear to allow new rpc call
+  CurrentItem.clear(); 
+  Unlock();
+}
+
+// Return configuration data if still up to date or empty string otherwise
+string EvidenceServer::Config::GetConfig(string Item, string Default) {
+
+  string Result;
+
+  // If up-to-date data in configuration list available, return this
+  Lock();
+  if ((List.count(Item) > 0) && (List[Item].Time >= ConfigTimeStamp)) Result = List[Item].Value;
+  Unlock();
+  if (!Result.empty()) return Result;
+
+  // Blocking configuration request if in main thread
+  if (pthread_self() == ThreadID) {
+	DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
+	Config.setData((char *) (Name + " " + Item).c_str());
+
+	// Check if successful
+	if (!EvidenceServer::ServiceOK(&Config)) {
+      if (Default.empty()) {
+		ThisServer->Message(ThisServer->FATAL, "Configuration server unreachable, can't retrieve '%s'", Item.c_str());
+	  }
+	  else Result = Default;
+	}
+	else {
+	  if (Config.getSize() == 0) Result = Default;
+	  else Result = string(Config.getString(), Config.getSize()-1); // Retrieve string safely
+    }
+
+	// Update configuration map	
+	if (!Result.empty()) {
+	  Lock();
+	  List[Item].Value = Result;
+	  List[Item].Time = ConfigTimeStamp;
+	  Unlock();
+	}	
+  }
+
+  // Non-blocking configuration request fro other threads
+  if (pthread_self() != ThreadID) {
+ 	Lock();
+	if (List.count(Item) > 0) {
+	  // New request possible only when answer to previous request received
+	  if (CurrentItem.empty()) {
+		CurrentItem = Item;
+		setData(((char *) (Name + " " + Item).c_str()));
+	  }
+
+	  // Return current value
+	  Result = List[Item].Value;
+	  Unlock();
+	}
+  }
+
+  return Result;
+}
+
+// Locking and unlocking for list access. Signal SIGUSR2 is also blocked.
+void EvidenceServer::Config::Lock() {
+
+  int Ret = 0;
+  sigset_t Set;
+  //printf("Locking %u\n", pthread_self());
+  Ret += abs(sigemptyset(&Set));
+  Ret += abs(sigaddset(&Set, SIGUSR2));
+  Ret += abs(pthread_sigmask(SIG_BLOCK, &Set, NULL));
+  Ret += abs(pthread_mutex_lock(&Mutex));
+  
+  if (Ret != 0) {
+	ThisServer->Message(ThisServer->FATAL, "Thread related call failed in Config::Lock()");
+  }
+}
+
+void EvidenceServer::Config::Unlock() {
+
+  int Ret = 0;
+  sigset_t Set;
+  //printf("  Unlocking %u\n", pthread_self());
+
+  Ret += abs(pthread_mutex_unlock(&Mutex));
+  Ret += abs(sigemptyset(&Set));
+  Ret += abs(sigaddset(&Set, SIGUSR2));
+  Ret += abs(pthread_sigmask(SIG_UNBLOCK, &Set, NULL));
+
+  if (Ret != 0) {
+	ThisServer->Message(ThisServer->FATAL, "Thread related call failed in Config::Unlock()");
+  }
+}
+
+
 //////////////////////////
 // EvidenceServer Class //
 //////////////////////////
 
-EvidenceServer *ThisServer;
-
 // Constructor starts server with given name
-EvidenceServer::EvidenceServer(const char *Name) {
+EvidenceServer::EvidenceServer(const char *ServerName): Name(ServerName) {
 
   // Initialize
@@ -41,5 +188,4 @@
   ExitRequest = false;
   ThisServer = this;
-  ServerName = Name;
 
   // Catch some signals
@@ -48,13 +194,18 @@
   signal(SIGINT, &SignalHandler);   // CTRL-C
   signal(SIGHUP, &SignalHandler);   // Terminal closed
+  
+  struct sigaction S;
+  S.sa_handler = &SignalHandler;
+  S.sa_flags = SA_RESTART;
+  sigaction(SIGUSR2, &S, NULL);
 
   // Catch C++ unhandled exceptions
   set_terminate(Terminate);
 
-  // Subscribe to modify service for keeping track of config file changes
-  ModifyInfo = new class ConfigUpdate();
+  // Configuration class (instantiate after signal handling for SIGUSR2 installed)
+  ConfClass = new class Config(Name);
 
   // Message service and initial message
-  MessageService = new DimService((ServerName+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
+  MessageService = new DimService((Name+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
 
   string Rev(EVIDENCE_REVISION); 
@@ -62,6 +213,6 @@
 
   // Start server
-  start(Name);
-  addExitHandler(this);  
+  start(ServerName);
+  addExitHandler(this);
 }
 
@@ -71,6 +222,5 @@
   Message(INFO, "Server stopped");
   
-  for (unsigned int i=0; i<ConfigList.size(); i++) delete[] ConfigList[i].Value;
-  delete ModifyInfo;
+  delete ConfClass;
   delete MessageService;
   delete MessageData;
@@ -152,54 +302,11 @@
 // Program terminates if data is missing and no default given. Actual configuration
 // request will be made only if config file has modification since last request.
-// The memory allocated by all calls to this function will be freed by
-// the destructor.
-char* EvidenceServer::GetConfig(string Item, const char *Default) {
-  
-  int ItemNo = -1;
-  
-  // Check if configuration request already in list
-  for (unsigned int i=0; i<ConfigList.size(); i++) if (ConfigList[i].Name == Item) {
-	// Return original value if still up to date
-	if (ConfigList[i].Time >= ModifyInfo->LastModifyTime) return ConfigList[i].Value;
-
-	// Otherwise, free memory of old value
-	delete[] ConfigList[i].Value;
-	ItemNo = i;
-	break;
-  } 
-
-  // Make configuration request
-  DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
-  Config.setData((char *) (ServerName + " " + Item).c_str());
-  char *Result = Config.getString();
-
-  // Terminate if not successful
-  if (!EvidenceServer::ServiceOK(&Config)) {
-    if (Default == NULL) Message(FATAL, "Configuration server unreachable, can't get '%s'", Item.c_str());
-	Result = (char *) Default;
-  }
-
-  if (strlen(Result) == 0) {
-    if (Default == NULL) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
-	Result = (char *) Default;
-  }
-
-  // Enlarge list if necessary
-  if (ItemNo == -1) {
-    struct ConfigItem New;
-    ConfigList.push_back(New);
-	ItemNo = ConfigList.size()-1;
-  }
-
-  // Create new entry in item list, allocate memory and copy data to this memory
-  ConfigList[ItemNo].Name = Item;
-  ConfigList[ItemNo].Value = new char [strlen(Result)+1];
-  strcpy(ConfigList[ItemNo].Value, Result);
-  ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime;
-
-  // Return address to configuration value  
-  return ConfigList[ItemNo].Value;
-}
-
+// If called from infoHandler(), a non-blocking request will be made
+string EvidenceServer::GetConfig(string Item, string Default) {
+
+  string Result = ConfClass->GetConfig(Item, Default);
+  if (Result.empty()) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
+  return Result;
+}
 
 // ====== Static methods ======
@@ -209,4 +316,10 @@
 
   static bool Called = false;
+
+  // If SIGUSR2, invoke call-back for configuraton change
+  if (Signal == SIGUSR2) {
+    ThisServer->ConfigChanged();
+	return;
+  }
 
   // At first invocation just request exit
@@ -261,49 +374,41 @@
 
 
-// Translates DIMInfo to string (memory has to be freed by caller)
-// Static method, cannot report memory allocation errors via Message() but returns NULL
-char *EvidenceServer::ToString(DimInfo *Item) {
-
-  char *Text;
-
+// Translates DIMInfo safely to string (assures no inivalid memory accesses are made)
+string EvidenceServer::ToString(DimInfo *I) {
+
+  ostringstream Text;
+  
   // Safety check
-  if (Item->getSize() < 1) return NULL;
-
-  // Message structure format handled
-  if (strcmp(Item->getFormat(), "I:1;C") == 0) {
-	struct Message *Msg = (struct Message *) Item->getData();
-	if (asprintf(&Text, "%d %s", Msg->Severity, Msg->Text) == -1) return NULL;
-	else return Text;
-  }
-  
-  // Structure: print hex representation (3 characters per byte)  
-  if (strlen(Item->getFormat()) != 1) {
-    int Size = 3*Item->getSize()+1, N;
-    if ((Text = (char *) malloc(Size)) == NULL) return NULL;
-	
-	char *CurrPos = Text;
-	for (int i=0; i<Item->getSize(); i++) {
-	  N = snprintf(CurrPos, Size, "%02x ", *((char *) Item->getData() + i));
-	  if (N<0 || N>=Size) {
-	    free(Text);
-		if (asprintf(&Text, "Structure length %d bytes, buffer overflow in ToString()", Item->getSize()) == -1) return NULL;
-		else return Text;
-	  }
-	  Size -= N;
-	  CurrPos += N;
+  if (I->getSize() < 1) return string();
+
+  // 'Message' service format handled here
+  if (strcmp(I->getFormat(), "I:1;C") == 0 && I->getSize()>=(int) sizeof(struct Message)) {
+	struct Message *Msg = (struct Message *) I->getData();
+	// Safely extract string and limit to length of C string (padding might make it longer)
+	string MsgText(Msg->Text, I->getSize()-sizeof(struct Message));
+    //MsgText.erase(strlen(MsgText.c_str()));
+	Text << Msg->Severity << " " << MsgText.erase(strlen(MsgText.c_str()));
+
+	return Text.str();
+  }
+  
+  // Structure: print hex representation 
+  if (strlen(I->getFormat()) != 1) {
+	for (int i=0; i<I->getSize(); i++) {
+	  Text << setw(2) << hex << *((char *) I->getData() + i) << " ";
 	} 
-	return Text;
-  }
-
-  // String: terminating \0 is enforced
-  if (toupper(*(Item->getFormat())) == 'C') {  
-    *(Item->getString() + Item->getSize() - 1) = '\0'; 
-	if (asprintf(&Text, "%s", Item->getString()) == -1) return NULL;
-	return Text;
+	return Text.str();
+  }
+
+  // String if format "C" and terminated with \0
+  if (strcmp(I->getFormat(),"C")==0 && *((char *) I->getData()+I->getSize()-1)=='\0') {  
+    Text << I->getString(); 
+	return Text.str();
   }
 
   // Number array
   int Size;
-  switch (toupper(*(Item->getFormat()))) {
+  switch (*(I->getFormat())) {
+    case 'C': Size = sizeof(char);		break;
     case 'I':
     case 'L': Size = sizeof(int);		break;
@@ -312,34 +417,32 @@
     case 'D': Size = sizeof(double);	break;
     case 'X': Size = sizeof(long long);	break;
-    default: return NULL;
-  }
-
-  int Max, Mem = Item->getSize()*Size*4+1;
-  char *Pos;
-
-  if ((Text = (char *) malloc(Mem)) == NULL) return NULL;
-
-  *Text = '\0';
-  for (int i=0; i<Item->getSize()/Size; i++) {
-	Pos = Text+strlen(Text);
-	Max = Mem-strlen(Text);
-
-	switch (toupper(*(Item->getFormat()))) {
+    default: return string();
+  }
+
+  for (int i=0; i<I->getSize()/Size; i++) {
+	// Space between entries
+    if (i != 0) Text << " ";
+
+	// Translate data
+	switch (*(I->getFormat())) {
+      case 'C': Text << *((char *) I->getData() + i);
+				break;
       case 'I':
-      case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i));
+      case 'L': Text << *((int *) I->getData() + i);
 				break;
-      case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i));
+      case 'S': Text << *((short *) I->getData() + i);
 				break;
-      case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i));
+      case 'F': Text << *((float *) I->getData() + i);
 				break;
-      case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i));
+      case 'D': Text << *((double *) I->getData() + i);
 				break;
-      case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i));
+      case 'X': Text << *((long long *) I->getData() + i);
 				break;
 	}
   }
   
-  return Text;
-}
+  return Text.str();
+}
+
 
 // Checks if service contents indicates not available
@@ -355,4 +458,27 @@
 	  (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
 }
+
+
+// Tokenize std::string using given delimeter list
+vector<string> EvidenceServer::Tokenize(const string &String, const string &Delimiters) {
+
+  vector<string> Tokens;
+  string::size_type Next, EndNext=0;
+    
+  while (EndNext != string::npos) {
+    // Find next token
+    Next = String.find_first_not_of(Delimiters, EndNext);
+    EndNext = String.find_first_of(Delimiters, Next);
+
+	// Stop if end of string reached
+	if (Next == string::npos) break;
+
+    // Add token to vector.
+    Tokens.push_back(String.substr(Next, EndNext - Next));
+  }
+  
+  return Tokens;
+}
+
 
 ///////////////////////////
