/********************************************************************\ General code to start a server of the Evidence Control System - The server is started with the given name. - DIM exit and error handlers are implemented. - The Status service is published (special format, see below). It can be updated with the State() method. The text will also be logged. - If the severity of a State() call is FATAL, exit() will be called (with this severity, the call to State() is guranteed not to return). - Configuration data can be requested by GetConfig(). - 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 - A terminate-handler is installed for catching unhandled C++ exceptions. All memory allocated by the non-static methods will be freed by the class destructor. Oliver Grimm, March 2009 \********************************************************************/ #include "Evidence.h" using namespace std; ////////////////////////// // EvidenceServer Class // ////////////////////////// EvidenceServer *ThisServer; // Constructor starts server with given name EvidenceServer::EvidenceServer(const char *Name) { // Initialize Status = NULL; ExitRequest = false; ThisServer = this; ServerName = Name; // Catch some signals signal(SIGQUIT, &SignalHandler); // CTRL-Backspace signal(SIGTERM, &SignalHandler); // Termination signal signal(SIGINT, &SignalHandler); // CTRL-C signal(SIGHUP, &SignalHandler); // Terminal closed // Catch C++ unhandled exceptions set_terminate(Terminate); // Subscribe to modify service for keeping track of config file changes ModifyInfo = new class ConfigUpdate(); // 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+"/Status").c_str(), (char *) "C", InitMsg, strlen(InitMsg)+1); start(Name); addExitHandler(this); } // Destructor: Free memory EvidenceServer::~EvidenceServer() { State(INFO, "Server stopped"); for (unsigned int i=0; igetName(), StateString[Severity], Tmp); // Create string with severity encoding snprintf(SBuf, sizeof(SBuf), "%s**", Tmp); SBuf[strlen(SBuf)-2] = '\0'; SBuf[strlen(SBuf)+1] = Severity; if (Tmp != ErrorString) free(Tmp); // 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 if (Severity == FATAL) exit(EXIT_FAILURE); } // Get configuration data // // 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= 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", (char *) ""); Config.setData((char *) (ServerName + " " + Item).c_str()); char *Result = Config.getString(); // Terminate if not successful if (strlen(Result) == 0) { if (Default == NULL) State(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].Value = new char [strlen(Result)+1]; ConfigList[ItemNo].Name = Item; strcpy(ConfigList[ItemNo].Value, Result); ConfigList[ItemNo].Time = ModifyInfo->LastModifyTime; // Return address to configuration value return ConfigList[ItemNo].Value; } // ====== Static methods ====== // Signal handler (causes pause() and other syscalls to return) void EvidenceServer::SignalHandler(int Signal) { static bool Called = false; if (!Called) { Called = true; ThisServer->ExitRequest = true; return; } // If invoked twice, call exit() ThisServer->State(ThisServer->WARN, "Signal handler called again, invoking exit() (signal %d)", Signal); exit(EXIT_FAILURE); } // C++ exception handler (derived from gcc __verbose_terminate_handler()) void EvidenceServer::Terminate() { static char Msg[STATUS_SIZE]; static bool Terminating = false; if (Terminating) { snprintf(Msg, sizeof(Msg), "%s: Terminate() called recursively, calling abort()", ThisServer->Status->getName()); printf("%s\n", Msg); DimClient::sendCommandNB("DColl/Log", Msg); abort(); } Terminating = true; // Make sure there was an exception; terminate is also called for an // attempt to rethrow when there is no suitable exception. type_info *Type = abi::__cxa_current_exception_type(); if (Type != NULL) { int Status = -1; char *Demangled = NULL; Demangled = abi::__cxa_demangle(Type->name(), 0, 0, &Status); snprintf(Msg, sizeof(Msg), "Terminate() called after throwing an instance of '%s'", Status==0 ? Demangled : Type->name()); free(Demangled); // If exception derived from std::exception, more information. try { __throw_exception_again; } catch (exception &E) { snprintf(Msg+strlen(Msg), sizeof(Msg)-strlen(Msg), " (what(): %s)", E.what()); } catch (...) { } } else snprintf(Msg, sizeof(Msg), "Terminate() called without an active exception"); ThisServer->State(FATAL, Msg); } // Translates DIMInfo to string (memory has to be freed by caller) char *EvidenceServer::ToString(DimInfo *Item) { char *Text; // Safety check if (Item->getSize() < 1) return NULL; // 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; igetSize(); 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; } 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; } // Number array int Size; switch (toupper(*(Item->getFormat()))) { case 'I': case 'L': Size = sizeof(int); break; case 'S': Size = sizeof(short); break; case 'F': Size = sizeof(float); break; 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; igetSize()/Size; i++) { Pos = Text+strlen(Text); Max = Mem-strlen(Text); switch (toupper(*(Item->getFormat()))) { case 'I': case 'L': snprintf(Pos, Max, "%d ", *((int *) Item->getData() + i)); break; case 'S': snprintf(Pos, Max, "%hd ", *((short *) Item->getData() + i)); break; case 'F': snprintf(Pos, Max, "%.5f ", *((float *) Item->getData() + i)); break; case 'D': snprintf(Pos, Max, "%.5f ", *((double *) Item->getData() + i)); break; case 'X': snprintf(Pos, Max, "%lld ", *((long long *) Item->getData() + i)); break; } } return Text; } // Checks if service contents indicates not available bool EvidenceServer::ServiceOK(DimInfo *Item) { return !((Item->getSize() == strlen(NO_LINK)+1) && (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0)); } /////////////////////////// // EvidenceHistory Class // /////////////////////////// // Organisaztion of history buffer // F | T S D | T S D | 0 0 ...... | T S D | T S D | 0 -1 // // F: Offset of oldest entry T: Time S: Size D: Data // F, T, S: int // Marker for history buffer const int EvidenceHistory::WrapMark[] = {0, -1}; const int EvidenceHistory::EndMark[] = {0, 0}; // Constructor EvidenceHistory::EvidenceHistory(std::string Name, int Delay): Name(Name+".hist"), Delay(Delay) { Buffer = NULL; LastUpdate = 0; } // Destructor EvidenceHistory::~EvidenceHistory() { delete[] Buffer; } // Requests service history bool EvidenceHistory::GetHistory() { // Check if last buffer update less than minimum delay in the past if ((Buffer != NULL) && (time(NULL)-LastUpdate < Delay)) { Offset = *(int *) Buffer; return true; } LastUpdate = time(NULL); // Check if service available DimCurrentInfo Info(Name.c_str(), NO_LINK); if (((Info.getSize() == strlen(NO_LINK)+1) && (memcmp(Info.getData(), NO_LINK, Info.getSize()) == 0))) return false; delete[] Buffer; BufferSize = Info.getSize(); Buffer = new char [BufferSize]; memcpy(Buffer, Info.getData(), BufferSize); Offset = *(int *) Buffer; return true; } // Returns next item in history buffer bool EvidenceHistory::Next(int &Time, int &Size, void *&Data) { if (Buffer == NULL) return false; // Check for wrap around if (memcmp(Buffer+Offset, WrapMark, sizeof(WrapMark)) == 0) Offset = 4; // Check if at end of ring buffer if (memcmp(Buffer+Offset, EndMark, sizeof(EndMark)) == 0) return false; Time = *(int *) (Buffer + Offset); Size = *(int *) (Buffer + Offset + sizeof(int)); Data = Buffer + Offset + 2*sizeof(int); Offset += *((int *) (Buffer + Offset) + 1) + 2*sizeof(int); return true; }