Changeset 262 for Evidence/Evidence.cc


Ignore:
Timestamp:
07/26/10 17:53:31 (14 years ago)
Author:
ogrimm
Message:
Removed signaling to invoke ConfigChanged(), now it is run as separate thread. New command '/ResetMessage'
File:
1 edited

Legend:

Unmodified
Added
Removed
  • Evidence/Evidence.cc

    r255 r262  
    3131//
    3232
    33 // Constructor
    34 EvidenceServer::Config::Config(string Name): DimRpcInfo("ConfigRequest", NO_LINK), Name(Name) {
    35 
    36   // Initialise
     33// Constructor: Subscribe to be informed on configuration file change
     34EvidenceServer::Config::Config():       DimRpcInfo("ConfigRequest", NO_LINK),
     35                                                                        DimCommand((This->Name+"/ResetMessage").c_str(), (char *) "") {
     36
    3737  CurrentItem = string();
    3838  ConfigTimeStamp = 0;
    39   ThreadID = pthread_self();
    40 
    41   // Subscribe to be informed on configuration file change
    4239  Service = new DimInfo("Config/ModifyTime", NO_LINK, this);
    4340}
     
    4946}
    5047
     48// Reset message and severity
     49void EvidenceServer::Config::commandHandler() {
     50
     51  This->Message(INFO, "Message reset by %s (ID %d)", getClientName(), getClientId());
     52}
     53
    5154// Track last modification time of configuration file
    5255void EvidenceServer::Config::infoHandler() {
    5356
     57  pthread_t Thread;
     58  int Ret;
     59
     60  if (!ServiceOK(DimInfo::getInfo())) return;
     61return;
    5462  This->Lock();
    5563  ConfigTimeStamp = getInfo()->getInt();
    5664  This->Unlock();
    5765
    58   if (pthread_kill(ThreadID, This->ConfigSignal) != 0) {
    59         This->Message(WARN, "Could not send signal to main thread");
    60   }
    61 }
    62 
    63 // Receive answer to remote procedure call
     66  // Launch ConfigChanged() as detached thread
     67  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) EvidenceServer::CallConfigChanged, (void *) NULL)) != 0) {
     68    This->Message(ERROR, "pthread_create() failed in EvidenceServer::Config::infoHandler() (%s)\n", strerror(Ret));
     69  }
     70  else if ((Ret = pthread_detach(Thread)) != 0) {
     71        This->Message(ERROR, "pthread_detach() failed in EvidenceServer::Config::infoHandler() (%s)\n", strerror(Ret));
     72  } 
     73}
     74
     75// Answer to remote procedure call: Update map
    6476void EvidenceServer::Config::rpcInfoHandler(){
    6577
    6678  This->Lock();
    67   // Update map
    68   List[CurrentItem].Value = string(getString(), getSize()-1);
    69   List[CurrentItem].Time = ConfigTimeStamp;
     79  This->List[CurrentItem].Value = string(DimRpcInfo::getString(), DimRpcInfo::getSize()-1);
     80  This->List[CurrentItem].Time = ConfigTimeStamp;
     81
    7082  // Clear to allow new rpc call
    7183  CurrentItem.clear();
     
    7385}
    7486
    75 // Return configuration data if still up to date or empty string otherwise
    76 string EvidenceServer::Config::GetConfig(string Item, string Default) {
     87// Request configuration data possible only when answer to previous request received
     88string EvidenceServer::Config::RequestNB(string Item) {
     89
     90  This->Lock();
     91  if (CurrentItem.empty()) {
     92        CurrentItem = Item;
     93        setData(((char *) Item.c_str()));
     94  }
     95  This->Unlock();
     96}
     97
     98
     99//////////////////////////
     100// EvidenceServer Class //
     101//////////////////////////
     102
     103// Initialise
     104EvidenceServer *EvidenceServer::This = NULL;
     105pthread_mutex_t EvidenceServer::Mutex;
     106set<pthread_t> EvidenceServer::Threads;
     107
     108
     109// Constructor
     110EvidenceServer::EvidenceServer(string ServerName): Name(ServerName) {
     111
     112  // Initialize
     113  MessageService = NULL;
     114  MessageData = NULL;
     115  ExitRequest = false;
     116  This = this;
     117  Threads.insert(pthread_self());
     118
     119  // Initialise mutex
     120  int Ret;
     121  pthread_mutexattr_t Attr;
     122
     123  if ((Ret = pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
     124    Message(FATAL, "pthread_mutex_settype() failed (%s)", strerror(Ret));
     125  }
     126  if ((Ret = pthread_mutex_init(&Mutex, &Attr)) != 0) {
     127    Message(FATAL, "pthread_mutex_init() failed (%s)", strerror(Ret));
     128  }
     129
     130  // Catch some signals
     131  signal(SIGQUIT, &SignalHandler);  // CTRL-Backspace
     132  signal(SIGTERM, &SignalHandler);  // Termination signal
     133  signal(SIGINT, &SignalHandler);   // CTRL-C
     134  signal(SIGHUP, &SignalHandler);   // Terminal closed
     135 
     136  // Catch C++ unhandled exceptions
     137  set_terminate(Terminate);
     138
     139  // Message service and initial message
     140  MessageService = new DimService((Name+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
     141
     142  string Rev(EVIDENCE_REVISION);
     143  Message(INFO, "Server started (%s, compiled %s %s)", (Rev.substr(1, Rev.size()-3)).c_str(),__DATE__, __TIME__);
     144
     145  // Configuration class
     146  ConfClass = new class Config();
     147
     148  // Start server
     149  start(ServerName.c_str());
     150  addExitHandler(this);
     151}
     152
     153
     154// Destructor: Free memory
     155EvidenceServer::~EvidenceServer() {
     156
     157  Message(INFO, "Server stopped");
     158
     159  delete ConfClass;
     160
     161  int Ret;
     162  if ((Ret = pthread_mutex_destroy(&Mutex)) != 0) {
     163        Message(ERROR, "pthread_mutex_destroy() failed (%s)", strerror(Ret));
     164  }
     165 
     166  delete MessageService;
     167  delete[] MessageData;
     168}
     169
     170
     171// DIM exit handler
     172void EvidenceServer::exitHandler(int Code) {
     173
     174  Message(INFO, "Exit handler called (DIM exit code %d)", Code);
     175  exit(EXIT_SUCCESS);
     176}
     177
     178// DIM error handler
     179void EvidenceServer::errorHandler(int Severity, int Code, char *Text) {
     180
     181  Message(ERROR, "%s (DIM error code %d, DIM severity %d)\n", Text, Code, Severity);
     182}
     183
     184
     185// Set server message (if Severity is FATAL, exit() will be invoked)
     186void EvidenceServer::Message(MessageType Severity, const char *Format, ...) {
     187
     188  static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
     189  static char ErrorString[] = "vasprintf() failed in Message()";
     190  char *Text;
     191 
     192  // Assemble message from application
     193  va_list ArgumentPointer;
     194  va_start(ArgumentPointer, Format);
     195  if (vasprintf(&Text, Format, ArgumentPointer) == -1) {
     196        Text = ErrorString;
     197        Severity = ERROR;
     198  }
     199  va_end(ArgumentPointer);
     200
     201  // Generate new Message structure and free text
     202  struct Message *NewMsg = (struct Message *) new char [sizeof(struct Message)+strlen(Text)+1];
     203  NewMsg->Severity = Severity;
     204  strcpy(NewMsg->Text, Text);
     205  if (Text != ErrorString) free(Text);
     206 
     207  // Send message to console and log file
     208  printf("%s (%s): %s\n", MessageService->getName(), StateString[Severity], NewMsg->Text);
     209  SendToLog("%s (%s): %s", MessageService->getName(), StateString[Severity], NewMsg->Text);
     210
     211  // Update DIM message service
     212  if (MessageService != NULL) {
     213        MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);
     214  }
     215
     216  // Delete old message
     217  Lock();
     218  delete[] MessageData;
     219  MessageData = NewMsg;
     220  Unlock(); 
     221
     222  // Terminate if severity if FATAL 
     223  if (Severity == FATAL) exit(EXIT_FAILURE);
     224}
     225
     226
     227// Send to central logging server with non-blocking command
     228void EvidenceServer::SendToLog(const char *Format, ...) {
     229
     230  static char ErrorString[] = "vasprintf() failed in SendToLog()";
     231  char *Buffer;
     232  int Ret;
     233
     234  // Evaluate variable argument list
     235  va_list ArgumentPointer;
     236  va_start(ArgumentPointer, Format);
     237  Ret = vasprintf(&Buffer, Format, ArgumentPointer);
     238  va_end(ArgumentPointer);
     239
     240  // Send to logger
     241  if (Ret != -1) {
     242        DimClient::sendCommandNB("DColl/Log", Buffer);
     243        free (Buffer);
     244  }
     245  else DimClient::sendCommandNB("DColl/Log", ErrorString);
     246}
     247
     248
     249// Get configuration data
     250//
     251// Program terminates if data is missing and no default given. Actual configuration
     252// request will be made only if config file has modification since last request.
     253// If called from infoHandler(), a non-blocking request will be made
     254string EvidenceServer::GetConfig(string Item, string Default) {
    77255
    78256  string Result;
    79 
     257  bool Blocking = false;
     258 
    80259  // If up-to-date data in configuration list available, return this
    81   This->Lock();
    82   if ((List.count(Item) > 0) && (List[Item].Time >= ConfigTimeStamp)) Result = List[Item].Value;
    83   This->Unlock();
     260  Lock();
     261  if ((List.count(Item) > 0) && (List[Item].Time >= ConfClass->ConfigTimeStamp)) Result = List[Item].Value;
     262  if (Threads.count(pthread_self()) != 0) Blocking = true;
     263  Unlock();
    84264  if (!Result.empty()) return Result;
    85265
    86   // Blocking configuration request if in main thread
    87   if (pthread_self() == ThreadID) {
     266  // Blocking configuration request
     267  if (Blocking) {
    88268        DimRpcInfo Config((char *) "ConfigRequest", NO_LINK);
    89269        Config.setData((char *) (Name + " " + Item).c_str());
     
    92272        if (!EvidenceServer::ServiceOK(&Config)) {
    93273      if (Default.empty()) {
    94                 This->Message(FATAL, "Configuration server unreachable, can't retrieve '%s'", Item.c_str());
     274                Message(FATAL, "Configuration server unreachable, can't retrieve '%s'", Item.c_str());
    95275          }
    96276          else Result = Default;
     
    103283        // Update configuration map     
    104284        if (!Result.empty()) {
    105           This->Lock();
     285          Lock();
    106286          List[Item].Value = Result;
    107           List[Item].Time = ConfigTimeStamp;
    108           This->Unlock();
     287          List[Item].Time = ConfClass->ConfigTimeStamp;
     288          Unlock();
    109289        }       
    110290  }
    111291
    112   // Non-blocking configuration request from other threads
    113   if (pthread_self() != ThreadID) {
    114         This->Lock();
     292  // Non-blocking configuration request
     293  if (!Blocking) {
     294        Lock();
    115295        if (List.count(Item) > 0) {
    116           // New request possible only when answer to previous request received
    117           if (CurrentItem.empty()) {
    118                 CurrentItem = Item;
    119                 setData(((char *) (Name + " " + Item).c_str()));
    120           }
    121 
    122           // Return current value
     296          ConfClass->RequestNB(Name + " " + Item);
    123297          Result = List[Item].Value;
    124298        }
    125         This->Unlock();
    126   }
     299        Unlock();
     300  }
     301
     302  // Terminate if no configuration information found
     303  if (Result.empty()) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
    127304
    128305  return Result;
     
    130307
    131308
    132 //////////////////////////
    133 // EvidenceServer Class //
    134 //////////////////////////
    135 
    136 // Initialise
    137 int EvidenceServer::ConfigSignal = 0;
    138 EvidenceServer *EvidenceServer::This = NULL;
    139 
    140 // Constructor
    141 EvidenceServer::EvidenceServer(string ServerName): Name(ServerName) {
    142 
    143   // Initialize
    144   MessageService = NULL;
    145   MessageData = NULL;
    146   ExitRequest = false;
    147   This = this;
    148 
    149   // Initialise mutex
    150   int Ret;
    151   pthread_mutexattr_t Attr;
    152 
    153   if ((Ret = pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    154     Message(FATAL, "pthread_mutex_settype() failed (%s)", strerror(Ret));
    155   }
    156   if ((Ret = pthread_mutex_init(&Mutex, &Attr)) != 0) {
    157     Message(FATAL, "pthread_mutex_init() failed (%s)", strerror(Ret));
    158   }
    159 
    160   // Catch some signals
    161   signal(SIGQUIT, &SignalHandler);  // CTRL-Backspace
    162   signal(SIGTERM, &SignalHandler);  // Termination signal
    163   signal(SIGINT, &SignalHandler);   // CTRL-C
    164   signal(SIGHUP, &SignalHandler);   // Terminal closed
    165  
    166   // Catch C++ unhandled exceptions
    167   set_terminate(Terminate);
    168 
    169   // Configuration class (must be instantiate after signal handling installed)
    170   ConfClass = new class Config(Name);
    171 
    172   // Message service and initial message
    173   MessageService = new DimService((Name+"/Message").c_str(), (char *) "I:1;C", NULL, 0);
    174 
    175   string Rev(EVIDENCE_REVISION);
    176   Message(INFO, "Server started (%s, compiled %s %s)", (Rev.substr(1, Rev.size()-3)).c_str(),__DATE__, __TIME__);
    177 
    178   // Start server
    179   start(ServerName.c_str());
    180   addExitHandler(this);
    181 }
    182 
    183 // Destructor: Free memory
    184 EvidenceServer::~EvidenceServer() {
    185 
    186   Message(INFO, "Server stopped");
    187 
    188   delete ConfClass;
    189 
    190   int Ret;
    191   if ((Ret = pthread_mutex_destroy(&Mutex)) != 0) {
    192         Message(ERROR, "pthread_mutex_destroy() failed (%s)", strerror(Ret));
    193   }
    194  
    195   delete MessageService;
    196   delete[] MessageData; 
    197 }
    198 
    199 // DIM exit handler
    200 void EvidenceServer::exitHandler(int Code) {
    201 
    202   if (Code == 0) Message(INFO, "Message cleared by %s (ID %d)", getClientName(), getClientId());
    203   else {
    204         Message(INFO, "Exit handler called (DIM exit code %d)", Code);
    205         exit(EXIT_SUCCESS);
    206   }
    207 }
    208 
    209 // DIM error handler
    210 void EvidenceServer::errorHandler(int Severity, int Code, char *Text) {
    211 
    212   Message(ERROR, "%s (DIM error code %d, DIM severity %d)\n", Text, Code, Severity);
    213 }
    214 
    215 // Set server message (if Severity is FATAL, exit() will be invoked)
    216 void EvidenceServer::Message(MessageType Severity, const char *Format, ...) {
    217 
    218   static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
    219   static char ErrorString[] = "vasprintf() failed in Message()";
    220   char *Text;
    221  
    222   // Assemble message from application
    223   va_list ArgumentPointer;
    224   va_start(ArgumentPointer, Format);
    225   if (vasprintf(&Text, Format, ArgumentPointer) == -1) {
    226         Text = ErrorString;
    227         Severity = ERROR;
    228   }
    229   va_end(ArgumentPointer);
    230 
    231   // Generate new Message structure and free text
    232   struct Message *NewMsg = (struct Message *) new char [sizeof(struct Message)+strlen(Text)+1];
    233   NewMsg->Severity = Severity;
    234   strcpy(NewMsg->Text, Text);
    235   if (Text != ErrorString) free(Text);
    236  
    237   // Send message to console and log file
    238   printf("%s (%s): %s\n", MessageService->getName(), StateString[Severity], NewMsg->Text);
    239   SendToLog("%s (%s): %s", MessageService->getName(), StateString[Severity], NewMsg->Text);
    240 
    241   // Update DIM message service
    242   if (MessageService != NULL) {
    243         MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);
    244   }
    245 
    246   // Delete old message
    247   Lock();
    248   delete[] MessageData;
    249   MessageData = NewMsg;
    250   Unlock(); 
    251 
    252   // Terminate if severity if FATAL 
    253   if (Severity == FATAL) exit(EXIT_FAILURE);
    254 }
    255 
    256 
    257 // Send to central logging server with non-blocking command
    258 void EvidenceServer::SendToLog(const char *Format, ...) {
    259 
    260   static char ErrorString[] = "vasprintf() failed in SendToLog()";
    261   char *Buffer;
    262   int Ret;
    263 
    264   // Evaluate variable argument list
    265   va_list ArgumentPointer;
    266   va_start(ArgumentPointer, Format);
    267   Ret = vasprintf(&Buffer, Format, ArgumentPointer);
    268   va_end(ArgumentPointer);
    269 
    270   // Send to logger
    271   if (Ret != -1) {
    272         DimClient::sendCommandNB("DColl/Log", Buffer);
    273         free (Buffer);
    274   }
    275   else DimClient::sendCommandNB("DColl/Log", ErrorString);
    276 }
    277 
    278 
    279 // Get configuration data
    280 //
    281 // Program terminates if data is missing and no default given. Actual configuration
    282 // request will be made only if config file has modification since last request.
    283 // If called from infoHandler(), a non-blocking request will be made
    284 string EvidenceServer::GetConfig(string Item, string Default) {
    285 
    286   string Result = ConfClass->GetConfig(Item, Default);
    287   if (Result.empty()) Message(FATAL, "Missing configuration data '%s'", Item.c_str());
    288   return Result;
    289 }
    290 
    291 
    292 // Set signal emitted when configuraton file changes, signal handler calls ConfigChanged()
    293 void EvidenceServer::ActivateSignal(int Signal) {
    294 
    295   struct sigaction S;
    296 
    297   ConfigSignal = Signal;
    298   S.sa_handler = &SignalHandler;
    299   S.sa_flags = SA_RESTART;
    300   sigaction(Signal, &S, NULL);
    301 }
    302 
    303 
    304309// Locking and unlocking functions.
    305 // Signal blocked before locking (pthread_mutex_lock() not asynch-signal safe).
    306310// Message() is not used to avoid infinite recursion
    307311void EvidenceServer::Lock() {
    308312
    309313  int Ret;
    310   sigset_t Set;
    311 
    312   if (ConfigSignal != 0) {
    313         Ret = abs(sigemptyset(&Set));
    314         Ret += abs(sigaddset(&Set, ConfigSignal));
    315         Ret += abs(pthread_sigmask(SIG_BLOCK, &Set, NULL));
    316 
    317         if (Ret != 0) {
    318           printf("Signal masking failed in Lock()");
    319           SendToLog("Signal masking failed in Lock()");
    320           exit(EXIT_FAILURE);
    321         }
    322   }
    323 
    324   if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
     314
     315  if ((Ret = pthread_mutex_lock(&EvidenceServer::Mutex)) != 0) {
    325316        printf("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret));
    326317        SendToLog("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret));
     
    332323
    333324  int Ret;
    334   sigset_t Set;
    335 
    336   if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
     325
     326  if ((Ret = pthread_mutex_unlock(&EvidenceServer::Mutex)) != 0) {
    337327        printf("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret));
    338328        SendToLog("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret));
    339329        exit(EXIT_FAILURE);
    340   }
    341  
    342   if (ConfigSignal != 0) {
    343         Ret = abs(sigemptyset(&Set));
    344         Ret += abs(sigaddset(&Set, ConfigSignal));
    345         Ret += abs(pthread_sigmask(SIG_UNBLOCK, &Set, NULL));
    346 
    347         if (Ret != 0) {
    348           printf("Signal unmasking failed in Unlock()");
    349           SendToLog("Signal unmasking failed in Unlock()");
    350           exit(EXIT_FAILURE);
    351         }
    352   }
     330  } 
    353331}
    354332
    355333
    356334// ====== Static methods ======
     335
     336// Stub to call ConfigChanged() method of class as separate thread
     337// Thread set is used to determine if blocking or non-blocking rpc is to be used
     338void EvidenceServer::CallConfigChanged() {
     339
     340  EvidenceServer::Lock();
     341  EvidenceServer::Threads.insert(pthread_self());
     342  EvidenceServer::Unlock();
     343
     344  This->ConfigChanged();
     345 
     346  EvidenceServer::Lock();
     347  EvidenceServer::Threads.erase(pthread_self());
     348  EvidenceServer::Unlock();
     349}
     350
    357351
    358352// Signal handler (causes pause() and other syscalls to return)
     
    360354
    361355  static bool Called = false;
    362 
    363   // If signal indicates configuration change, invoke call-back
    364   if (Signal == EvidenceServer::ConfigSignal) {
    365     This->ConfigChanged();
    366         return;
    367   }
    368356
    369357  // At first invocation just request exit
     
    378366  exit(EXIT_FAILURE);
    379367}
     368
    380369
    381370// C++ exception handler (derived from gcc __verbose_terminate_handler())
     
    492481}
    493482
     483bool EvidenceServer::ServiceOK(DimCurrentInfo *Item) {
     484
     485  return !((Item->getSize() == strlen(NO_LINK)+1) && 
     486          (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
     487}
     488
    494489
    495490// Tokenize std::string using given delimeter list
Note: See TracChangeset for help on using the changeset viewer.