Index: /Evidence/Bridge.cc
===================================================================
--- /Evidence/Bridge.cc	(revision 223)
+++ /Evidence/Bridge.cc	(revision 224)
@@ -7,5 +7,8 @@
   thus serialized by DIM and no Mutex is necessary.
 
-  Oliver Grimm, May 2010
+  Remote procedure calls are bridged through their underlying services
+  and commands.
+  
+  Oliver Grimm, June 2010
 
 \********************************************************************/
@@ -21,8 +24,11 @@
 using namespace std;
 
+
 // Class declaration
 class Bridge: public DimClient, public EvidenceServer {
 
 	struct Item {
+	  string Name;
+	  DimCommand *Command;
 	  DimStampedInfo *DataItem;
 	  DimService *Service;
@@ -34,5 +40,6 @@
 	
     void infoHandler();
-	void AddService(string, const char *);
+    void commandHandler();
+	void AddService(string, char *, int);
 	void RemoveService(string);
    
@@ -42,7 +49,5 @@
 }; 
 
-//
 // Constructor
-//
 Bridge::Bridge(char *Name, int Port): EvidenceServer(SERVER_NAME) {
 
@@ -54,16 +59,14 @@
 }
 
-//
+
 // Destructor: Delete all subscriptions and services
-//
 Bridge::~Bridge() {
 
-  while (List.size() != 0) RemoveService(List[0].DataItem->getName());  
+  while (List.size() != 0) RemoveService(List[0].Name);  
   delete ServerList;
 }
 
-//
+
 // Service subscription and repeating
-//
 void Bridge::infoHandler() {
 
@@ -80,5 +83,5 @@
 		RemoveService(string(Token)+"/SERVICE_LIST");
 	  }
-	  else AddService(string(Token)+"/SERVICE_LIST", "C");
+	  else AddService(string(Token)+"/SERVICE_LIST", (char *) "C", DimSERVICE);
 
 	  // Skip server IP address and process ID
@@ -91,13 +94,34 @@
   // If service is SERVICE_LIST, scan and subscribe/unsubscribe to services
   if (strstr(I->getName(), "/SERVICE_LIST") != NULL) {
-	char *Type, *Name = strtok(I->getString(), "+-!|");
+	char *Format, *Name = strtok(I->getString(), "+-!|");
 	while (Name != NULL) {
-	  // Only consider DIM services (not commands and RPCs)
-      if (((Type = strtok(NULL, "\n")) != NULL) &&
-		  (strstr(Type, "|CMD") == NULL) && (strstr(Type, "|RPC") == NULL)) {
-	    if (*I->getString() == '-' || *I->getString() == '!') RemoveService(Name);
+      if ((Format = strtok(NULL, "\n")) != NULL) {
+	    // Check if service added or removed/unavailable
+	    if (*I->getString() == '-' || *I->getString() == '!') {
+		  if (strstr(Format, "|RPC") != NULL) {
+	  		RemoveService(string(Name)+"/RpcIn");
+	  		RemoveService(string(Name)+"/RpcOut");
+		  }
+		  else RemoveService(Name);
+		}
 		else {
-		  Type[strlen(Type)-1] = '\0'; // Isolate service format
-		  AddService(Name, Type);
+		  // Determine type of service
+		  if (strstr(Format, "|CMD") != NULL) {
+			*(strstr(Format, "|CMD")) = '\0';
+			AddService(Name, Format, DimCOMMAND);
+		  }
+		  else if (strstr(Format, "|RPC") != NULL) {
+			*(strstr(Format, "|RPC")) = '\0';
+		    if (strchr(Format, ',') != NULL) {
+	  		  *strchr(Format, ',') = '\0';
+	  		  AddService(string(Name)+"/RpcIn", Format, DimCOMMAND);
+	  		  AddService(string(Name)+"/RpcOut", Format+strlen(Format)+1, DimSERVICE);
+			}
+			
+		  }
+		  else {
+			Format[strlen(Format)-1] = '\0';
+			AddService(Name, Format, DimSERVICE);
+		  }
 		}
 	  }
@@ -110,7 +134,4 @@
   for (int Service=0; Service<List.size(); Service++) if (I == List[Service].DataItem) {
 
-	// Ignores repeating DIS_DNS services
-	if (List[Service].Service == NULL) break;
-
     // Copy service data
     delete[] List[Service].Data;
@@ -125,36 +146,46 @@
 }
 
-//
-// Add service subscription
-//
-void Bridge::AddService(string Name, const char *Format) {
+
+// Command repeating
+void Bridge::commandHandler() {
+ 
+  sendCommandNB(getCommand()->getName(), getCommand()->getData(), getCommand()->getSize());
+}
+
+
+// Service subscription
+void Bridge::AddService(string Name, char *Format, int Type) {
+
+  // Do not forward  DIS_DNS and History services
+  if ((Name.find("DIS_DNS/") != string::npos) || (Name.find("History/") != string::npos)) return;
 
   // Check if already subscribed to this service
   for (int i=0; i<List.size(); i++) {
-	if (Name == List[i].DataItem->getName()) return;
-  }
-
-  // Create subscription and new service to secondary DNS (do not forward DIS_DNS services)
-  struct Item New;
-  
-  New.Data = NULL;
-  if (Name.find("DIS_DNS/") != string::npos) New.Service = NULL;
-  else New.Service = new DimService(Name.c_str(), (char *) Format, New.Data, 0);
-  New.DataItem = new DimStampedInfo(Name.c_str(), NO_LINK, this);
+	if (Name == List[i].Name) return;
+  }
+
+  // Create subscription and service to secondary DNS or new command
+  struct Item New = {Name, NULL, NULL, NULL, NULL};
+
+  if  (Type == DimSERVICE) {  
+	New.Service = new DimService(Name.c_str(), (char *) Format, New.Data, 0);
+	New.DataItem = new DimStampedInfo(Name.c_str(), NO_LINK, this);
+  }
+  else if (Type == DimCOMMAND) New.Command = new DimCommand(Name.c_str(), Format, this);
+
   List.push_back(New);
 }
 
 
-//
-// Remove service from watch list
-//
+// Remove service from watch list (unused pointer are NULL)
 void Bridge::RemoveService(string Name) {
 
-  // Find service index
   vector<struct Item>::iterator E;
-  for (E=List.begin(); E<List.end(); ++E) if (Name == (*E).DataItem->getName()) {
+  for (E=List.begin(); E<List.end(); ++E) if (Name == (*E).Name) {
 	delete (*E).DataItem;
 	delete (*E).Service;
 	delete[] (*E).Data;
+	delete (*E).Command;
+
 	List.erase(E);
   }
@@ -162,9 +193,8 @@
 
 
-//	    
 // Main program
-//
 int main(int argc, char *argv[]) {
 
+  // Check command line argument
   if (argc == 1) {
 	printf("Usage: %s <address of primary DNS> [port] (default port %d)\n", argv[0], DEFAULT_PORT);
Index: /Evidence/Config.cc
===================================================================
--- /Evidence/Config.cc	(revision 223)
+++ /Evidence/Config.cc	(revision 224)
@@ -74,5 +74,5 @@
   // Open configuration file
   if ((File = fopen(Filename, "r")) == NULL) {
-    Message(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno));
+    Message(FATAL, "Could not open configuration file '%s' (%s)", Filename, strerror(errno));
   }
 
@@ -81,5 +81,5 @@
     Message(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
   }
-  
+
   // Create DIM services
   ConfigChanged();
Index: /Evidence/DColl.cc
===================================================================
--- /Evidence/DColl.cc	(revision 223)
+++ /Evidence/DColl.cc	(revision 224)
@@ -95,5 +95,5 @@
   
   LogSizeMB = FileSize(LogFile)/1024.0/1024.0;
-  LogSizeService = new DimService(SERVER_NAME "/LogSizekB", LogSizeMB);
+  LogSizeService = new DimService(SERVER_NAME "/LogSizeMB", LogSizeMB);
 
   DataFilename = new DimService(SERVER_NAME "/CurrentFile", (char *) "");
@@ -216,5 +216,5 @@
 	// Create direcory if not existing (ignore error if already existing)
 	char *Dir;
-	if (asprintf(&Dir, "%s/%d%02d", BaseDir, T->tm_year+1900, T->tm_mon + 1) == -1) {
+	if (asprintf(&Dir, "%s/%d", BaseDir, T->tm_year+1900) == -1) {
 	  Message(FATAL, "asprintf() failed, could not create direcory name");	
 	}
Index: /Evidence/Edd/Edd.cc
===================================================================
--- /Evidence/Edd/Edd.cc	(revision 223)
+++ /Evidence/Edd/Edd.cc	(revision 224)
@@ -99,5 +99,5 @@
 
 // Update widget
-void EddLineDisplay::Update(QString Name, int Time, QByteArray Array, QString Format, QString Text) {
+void EddLineDisplay::Update(QString Name, int Time, QByteArray, QString Format, QString Text) {
 
   if (ServiceName != Name) return;
@@ -109,8 +109,11 @@
     setText("n/a");
     Pal.setColor(QPalette::Base, Qt::lightGray);
-  }
-  else {
-    // Backgound colour determined by last byte
-    switch (Array[Array.size()]) {
+	setPalette(Pal);
+	return;
+  }
+
+  // Message service backgound colour determined by severity 
+  if (Name.endsWith("/Message")) {
+    switch (Text.section(' ', 0, 0).toInt()) {
       case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
       case 1:  Pal.setColor(QPalette::Base, Qt::yellow); break;
@@ -119,12 +122,12 @@
       default: break;
     }
-	
-	if (Format[0].toUpper() != 'C') Text = Text.section(' ', Index, Index);
-
-	if (!ShowAsTime) setText(Text);
-	else setText(QDateTime::fromTime_t(Text.toInt()).toString());
-	setCursorPosition(0);
-  }
-  
+	Text = Text.section(' ', 1);
+  }	
+  else if (Format[0].toUpper() != 'C') Text = Text.section(' ', Index, Index);
+
+  if (!ShowAsTime) setText(Text);
+  else setText(QDateTime::fromTime_t(Text.toInt()).toString());
+
+  setCursorPosition(0);  
   setPalette(Pal);
 }
Index: /Evidence/Evidence.cc
===================================================================
--- /Evidence/Evidence.cc	(revision 223)
+++ /Evidence/Evidence.cc	(revision 224)
@@ -5,6 +5,6 @@
   - The server is started with the given name.
   - DIM exit and error handlers are implemented.
-  - The Message service is published (special format, see below).
-    It can be updated with the Message() method. The text will also be logged.
+  - A Message service is published with severity encoding. It can be updated
+    with the Message() method. The text will also be logged.
   - 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).
@@ -16,9 +16,9 @@
     DIMInfo service into text
   - A terminate-handler is installed for catching unhandled C++ exceptions.
-  
-  All memory allocated by the non-static methods will be freed by the
+
+  Memory allocated by the non-static methods will be freed by the
   class destructor.
   
-  Oliver Grimm, March 2009
+  Oliver Grimm, June 2010
  
 \********************************************************************/
@@ -37,6 +37,6 @@
 
   // Initialize
-  Status = NULL;
-  StdOutText = NULL;
+  MessageService = NULL;
+  MessageData = NULL;
   ExitRequest = false;
   ThisServer = this;
@@ -54,15 +54,14 @@
   // Subscribe to modify service for keeping track of config file changes
   ModifyInfo = new class ConfigUpdate();
-  
+
+  // Message service and initial message
+  MessageService = new DimService((ServerName+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
+
+  string Rev(EVIDENCE_REVISION); 
+  Message(INFO, "Server started (%s, compiled %s %s)", (Rev.substr(1, Rev.size()-3)).c_str(),__DATE__, __TIME__);
+
   // Start server
-  string Rev(EVIDENCE_REVISION);
-  Rev = Rev.substr(1, Rev.size()-3);
-  snprintf(InitMsg, sizeof(InitMsg), "Server started (%s, compiled %s %s)", Rev.c_str(),__DATE__, __TIME__);
-  
-  Status = new DimService((ServerName+"/Message").c_str(), (char *) "C", InitMsg, strlen(InitMsg)+1);
-  StdOut = new DimService((ServerName+"/Textout").c_str(), (char *) "");
-
   start(Name);
-  addExitHandler(this);
+  addExitHandler(this);  
 }
 
@@ -72,11 +71,8 @@
   Message(INFO, "Server stopped");
   
-  for (unsigned int i=0; i<ConfigList.size(); i++) {
-	delete[] ConfigList[i].Value;
-  }
+  for (unsigned int i=0; i<ConfigList.size(); i++) delete[] ConfigList[i].Value;
   delete ModifyInfo;
-  
-  delete Status;
-  delete StdOut;
+  delete MessageService;
+  delete MessageData;
 }
 
@@ -90,59 +86,65 @@
 // DIM error handler
 void EvidenceServer::errorHandler(int Severity, int Code, char *Text) {   
-  Message(ERROR, "%s (DIM error code %d, severity %d)\n", Text, Code, Severity);
-}
-
-// Set status of server
-//
-// The message format is special: after the string-terminating '\0' the Severity 
-// is given, terminated by another '\0'  The buffer for the DIM service must have
-// the same lifetime as the DIM service. If Severity is FATAL, exit() will be invoked.
+  Message(ERROR, "%s (DIM error code %d, DIM severity %d)\n", Text, Code, Severity);
+}
+
+// Set server message (if Severity is FATAL, exit() will be invoked)
 void EvidenceServer::Message(MessageType Severity, const char *Format, ...) {
 
   static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
   static char ErrorString[] = "vasprintf() failed in Message()";
-  static char SBuf[STATUS_SIZE];
-  char TBuf[STATUS_SIZE];
-  char *Tmp;
+  char *Text;
   
   // Assemble message from application
   va_list ArgumentPointer;
   va_start(ArgumentPointer, Format);
-  if (vasprintf(&Tmp, Format, ArgumentPointer) == -1) Tmp = ErrorString;
+  if (vasprintf(&Text, Format, ArgumentPointer) == -1) {
+	Text = ErrorString;
+	Severity = ERROR;
+  }
   va_end(ArgumentPointer);
 
-  // Create normal string
-  snprintf(TBuf, sizeof(TBuf), "%s (%s): %s", Status->getName(), StateString[Severity], Tmp);
-
-  // Create string with severity encoding
-  snprintf(SBuf, sizeof(SBuf), "%s*", Tmp);
-  SBuf[strlen(SBuf)-1] = '\0'; 		// new string terminiation replaces '*'
-  SBuf[strlen(SBuf)+1] = Severity;	// Severity after new string termination
-
-  if (Tmp != ErrorString) free(Tmp);
+  // Generate new Message structure and free text
+  struct Message *NewMsg = (struct Message *) new char [sizeof(struct Message)+strlen(Text)+1];
+  NewMsg->Severity = Severity;
+  strcpy(NewMsg->Text, Text);
+  if (Text != ErrorString) free(Text);
   
   // Send message to console and log file
-  printf("%s\n", TBuf);
-  DimClient::sendCommandNB("DColl/Log", TBuf);
-
-  // Update DIM status service (including severity encoding)
-  if (Status != NULL) Status->updateService(SBuf, strlen(SBuf)+2);
-
-  // Terminate if message type is fatal
+  printf("%s (%s): %s\n", MessageService->getName(), StateString[Severity], NewMsg->Text);
+  SendToLog("%s (%s): %s", MessageService->getName(), StateString[Severity], NewMsg->Text);
+
+  // Update DIM message service, then delete old message
+  if (MessageService != NULL) {
+	MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);
+  }
+  delete MessageData;
+  MessageData = NewMsg;
+
+  // Terminate if severity if FATAL  
   if (Severity == FATAL) exit(EXIT_FAILURE);
 }
 
-// Set text of StdOut service
-void EvidenceServer::SetStdOut(char *Text) {
-
-  // Copy text to permanent buffer
-  char *Tmp = new char[strlen(Text)+1];
-  strcpy(Tmp, Text);
-  StdOut->updateService(Tmp);
-
-  // Delete old buffer and save new buffer pointer
-  delete[] StdOutText;
-  StdOutText = Tmp;    
-}
+
+// Set to central logging server with non-blocking command (may be used in DIM handler)
+void EvidenceServer::SendToLog(const char *Format, ...) {
+
+  char *Buffer;
+  int Ret;
+
+  // Evaluate variable argument list
+  va_list ArgumentPointer;
+  va_start(ArgumentPointer, Format);
+  Ret = vasprintf(&Buffer, Format, ArgumentPointer);
+  va_end(ArgumentPointer);
+
+  // Send to logger
+  if (Ret != -1) {
+	DimClient::sendCommandNB("DColl/Log", Buffer);
+	free (Buffer);
+  }
+  else Message(ERROR, "Could not create logging text in SendToLog(), vasprintf() failed");
+}
+
 
 // Get configuration data
@@ -191,6 +193,6 @@
 
   // 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];
-  ConfigList[ItemNo].Name = Item;
   strcpy(ConfigList[ItemNo].Value, Result);
   ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime;
@@ -208,4 +210,5 @@
   static bool Called = false;
 
+  // At first invocation just request exit
   if (!Called) {
 	Called = true;
@@ -226,5 +229,5 @@
 
   if (Terminating) {
-	snprintf(Msg, sizeof(Msg), "%s: Terminate() called recursively, calling abort()", ThisServer->Status->getName());
+	snprintf(Msg, sizeof(Msg), "%s: Terminate() called recursively, calling abort()", ThisServer->MessageService->getName());
 	printf("%s\n", Msg);
 	DimClient::sendCommandNB("DColl/Log", Msg);
@@ -259,5 +262,5 @@
 
 // Translates DIMInfo to string (memory has to be freed by caller)
-// Static method: it cannot report memory allocation errors via Message()
+// Static method, cannot report memory allocation errors via Message() but returns NULL
 char *EvidenceServer::ToString(DimInfo *Item) {
 
@@ -266,4 +269,11 @@
   // 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)  
Index: /Evidence/Evidence.h
===================================================================
--- /Evidence/Evidence.h	(revision 223)
+++ /Evidence/Evidence.h	(revision 224)
@@ -19,12 +19,13 @@
 // Class declation of Evidence server
 class EvidenceServer: public DimServer {
+
   private:
 	// This class will contain in LastModifyTime always
 	// the unix time of the last config file update 
-	class ConfigUpdate: public DimClient {
-	  DimInfo *ModifyInfo;
+	class ConfigUpdate: public DimClient, public DimRpcInfo {
+ 	  DimInfo *ModifyInfo;
 	  
 	  public:
-	    ConfigUpdate() {
+	    ConfigUpdate(): DimRpcInfo("ConfigRequest", NO_LINK) {
 	      LastModifyTime = 0;
 		  ModifyInfo = new DimInfo("Config/ModifyTime", NO_LINK, this);
@@ -33,9 +34,20 @@
 		  delete ModifyInfo;
 		}
+	    void Request(const char *What){
+	      setData((char *) What);
+	    }
+
 	    void infoHandler(){
 	      if (EvidenceServer::ServiceOK(getInfo())) LastModifyTime = getInfo()->getInt();
 	    }
+
+	    void rpcInfoHandler(){
+		  //printf("Received %s\n", getString());
+	    }
+
 		int LastModifyTime;
 	};
+
+  private:
 
 	struct ConfigItem {
@@ -46,11 +58,13 @@
 	std::vector<struct ConfigItem> ConfigList;
 
+	struct Message {
+      int Severity;
+	  char Text[];
+	};
+
 	std::string ServerName;
-    DimService *Status, *StdOut;
+    DimService *MessageService;
+	struct Message *MessageData;
 	class ConfigUpdate *ModifyInfo;
-	
-	char InitMsg[STATUS_SIZE];
-	int LastModifyTime;
-	char *StdOutText;
 	
     static void SignalHandler(int); // static for signal()
@@ -66,5 +80,5 @@
 
 	void Message(MessageType, const char *, ...);
-	void SetStdOut(char *);
+	void SendToLog(const char *, ...);
 	char* GetConfig(std::string, const char * = NULL);
 	static char* ToString(DimInfo *);
Index: /Evidence/History.cc
===================================================================
--- /Evidence/History.cc	(revision 223)
+++ /Evidence/History.cc	(revision 224)
@@ -259,4 +259,5 @@
   // Set minimum required change if given in configuratrion
   char *Pnt = strstr(Change, Name.c_str());
+
   if (Pnt != NULL && *(Pnt+Name.size()) == ':') New.MinAbsChange = atof(Pnt+Name.size()+1);
   else New.MinAbsChange = 0;
Index: /Evidence/readme.txt
===================================================================
--- /Evidence/readme.txt	(revision 223)
+++ /Evidence/readme.txt	(revision 224)
@@ -29,5 +29,5 @@
 			FATAL. The erroneous expression is ignored in the following.
 25/5/2010	Service history remains available if service itself become unavailable. If not
-			yet in memory, reading from history file is tried. Improved error handling of
+			yet in memory, reading from file is tried. Improved error handling of
 			history files.
 28/5/2010	Changed name of 'State' service to 'Message' to better reflect its functionality.
@@ -35,7 +35,10 @@
 30/5/2010	Created Bridge server that repeats services from one DNS to another.
 			Service quality now also written to slow data file.
-31/5/2010	Configuration file format now follows semi-standard INI format.
+31/5/2010	Configuration file format follows semi-standard INI format.
 7/6/2010	Separated History service from DColl. History format changed, now includes
 			service format (allows history access also when service is unavailable).
+11/6/2010	Bridge does not forward history service
+17/6/2010	Added SendToLog() method. Changed severity encoding of Message service to
+			use standard DIM structure of format "I:1;C"
 
 
