Changeset 229 for Evidence/Evidence.cc
- Timestamp:
- 06/24/10 07:51:15 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
Evidence/Evidence.cc
r224 r229 9 9 - If the severity of a Message() call is FATAL, exit() will be called (with 10 10 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. 12 16 - Signal handlers to ignore common signals are installed. 13 17 These signals will then cause pause() to return which can be used 14 18 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 17 20 - A terminate-handler is installed for catching unhandled C++ exceptions. 18 21 19 Memory allocated by the non-static methods will be freed by the20 class destructor.21 22 22 Oliver Grimm, June 2010 23 23 … … 27 27 using namespace std; 28 28 29 EvidenceServer *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 37 EvidenceServer::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 55 EvidenceServer::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 66 void 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 78 void 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 90 string 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. 146 void 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 161 void 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 29 178 ////////////////////////// 30 179 // EvidenceServer Class // 31 180 ////////////////////////// 32 181 33 EvidenceServer *ThisServer;34 35 182 // Constructor starts server with given name 36 EvidenceServer::EvidenceServer(const char * Name) {183 EvidenceServer::EvidenceServer(const char *ServerName): Name(ServerName) { 37 184 38 185 // Initialize … … 41 188 ExitRequest = false; 42 189 ThisServer = this; 43 ServerName = Name;44 190 45 191 // Catch some signals … … 48 194 signal(SIGINT, &SignalHandler); // CTRL-C 49 195 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); 50 201 51 202 // Catch C++ unhandled exceptions 52 203 set_terminate(Terminate); 53 204 54 // Subscribe to modify service for keeping track of config file changes55 ModifyInfo = new class ConfigUpdate();205 // Configuration class (instantiate after signal handling for SIGUSR2 installed) 206 ConfClass = new class Config(Name); 56 207 57 208 // 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); 59 210 60 211 string Rev(EVIDENCE_REVISION); … … 62 213 63 214 // Start server 64 start( Name);65 addExitHandler(this); 215 start(ServerName); 216 addExitHandler(this); 66 217 } 67 218 … … 71 222 Message(INFO, "Server stopped"); 72 223 73 for (unsigned int i=0; i<ConfigList.size(); i++) delete[] ConfigList[i].Value; 74 delete ModifyInfo; 224 delete ConfClass; 75 225 delete MessageService; 76 226 delete MessageData; … … 152 302 // Program terminates if data is missing and no default given. Actual configuration 153 303 // 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 305 string 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 } 204 311 205 312 // ====== Static methods ====== … … 209 316 210 317 static bool Called = false; 318 319 // If SIGUSR2, invoke call-back for configuraton change 320 if (Signal == SIGUSR2) { 321 ThisServer->ConfigChanged(); 322 return; 323 } 211 324 212 325 // At first invocation just request exit … … 261 374 262 375 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) 377 string EvidenceServer::ToString(DimInfo *I) { 378 379 ostringstream Text; 380 269 381 // 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) << " "; 294 399 } 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(); 303 407 } 304 408 305 409 // Number array 306 410 int Size; 307 switch (toupper(*(Item->getFormat()))) { 411 switch (*(I->getFormat())) { 412 case 'C': Size = sizeof(char); break; 308 413 case 'I': 309 414 case 'L': Size = sizeof(int); break; … … 312 417 case 'D': Size = sizeof(double); break; 313 418 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; 328 430 case 'I': 329 case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i));431 case 'L': Text << *((int *) I->getData() + i); 330 432 break; 331 case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i));433 case 'S': Text << *((short *) I->getData() + i); 332 434 break; 333 case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i));435 case 'F': Text << *((float *) I->getData() + i); 334 436 break; 335 case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i));437 case 'D': Text << *((double *) I->getData() + i); 336 438 break; 337 case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i));439 case 'X': Text << *((long long *) I->getData() + i); 338 440 break; 339 441 } 340 442 } 341 443 342 return Text; 343 } 444 return Text.str(); 445 } 446 344 447 345 448 // Checks if service contents indicates not available … … 355 458 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0)); 356 459 } 460 461 462 // Tokenize std::string using given delimeter list 463 vector<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 357 483 358 484 ///////////////////////////
Note:
See TracChangeset
for help on using the changeset viewer.