Changeset 262
- Timestamp:
- 07/26/10 17:53:31 (14 years ago)
- Location:
- Evidence
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
Evidence/Alarm.cc
r255 r262 28 28 29 29 // 30 // Data handling class30 // Class declaration 31 31 // 32 32 class AlarmHandler: public DimClient, public EvidenceServer { … … 148 148 List[i].Level = 0; 149 149 List[i].WarnedLevel = 0; 150 if (Server != "DIS_DNS") sendCommandNB((Server+"/EXIT").c_str(), (int) 0);150 sendCommandNB((Server+"/ResetMessage").c_str(), (int) 0); 151 151 } 152 152 -
Evidence/Bridge.cc
r253 r262 50 50 51 51 public: 52 Bridge( char *, int);52 Bridge(); 53 53 ~Bridge(); 54 54 }; 55 55 56 // Constructor 57 Bridge::Bridge(char *Name, int Port): EvidenceServer(SERVER_NAME) { 58 59 // Set primary DIM network to subscribe from 60 DimClient::setDnsNode(Name, Port); 61 62 // Initialise and request notification of configuration changes 56 // Constructor (ConfigChanged() is automatically called at start-up) 57 Bridge::Bridge(): EvidenceServer(SERVER_NAME) { 58 59 // Initialise 63 60 ServerList = NULL; 64 61 GetConfig("cmdallow", " "); 65 ActivateSignal(SIGUSR2);66 67 ConfigChanged();68 62 } 69 63 … … 92 86 93 87 // Check if configuratiomn changed 94 if (ExcludeString == GetConfig("exclude")) return; 95 ExcludeString = GetConfig("exclude"); 96 97 // Remove all previous subscriptions 98 Undo(); 99 88 string NewExcludeString = GetConfig("exclude"); 89 90 Lock(); 91 if (ExcludeString != NewExcludeString) ExcludeString.swap(NewExcludeString); 92 Unlock(); 93 if (ExcludeString == NewExcludeString) return; 94 100 95 // Compile regular expressions 101 96 regex_t R; 97 vector<regex_t> RegEx; 98 102 99 vector<string> Exclude = Tokenize(ExcludeString, " \t"); 103 100 for (int i=0; i<Exclude.size(); i++) { … … 111 108 } 112 109 113 // Subscribe to top-level server list 110 // Remove previous data, set new regular expressions and subscribe to top-level server list 111 Lock(); 112 Undo(); 113 Bridge::RegEx = RegEx; 114 114 ServerList = new DimInfo((char *) "DIS_DNS/SERVER_LIST", NO_LINK, this); 115 Unlock(); 115 116 } 116 117 … … 267 268 exit(EXIT_FAILURE); 268 269 } 269 270 271 // Set primary DIM network to subscribe from 272 DimClient::setDnsNode(argv[1], argc>2 ? atoi(argv[2]) : DEFAULT_PORT); 273 270 274 // Static ensures calling of destructor by exit() 271 static Bridge Class (argv[1], argc>2 ? atoi(argv[2]) : DEFAULT_PORT);275 static Bridge Class; 272 276 273 277 // Sleep until signal caught -
Evidence/Config.cc
r253 r262 11 11 12 12 A mutex is used for preventing concurrent access to the configuration data from 13 the methods rpcHandler() and ConfigChanged().13 the methods rpcHandler() and ReadConfig(). 14 14 15 15 Oliver Grimm, July 2010 … … 48 48 ~EvidenceConfig(); 49 49 50 void ConfigChanged();50 void ReadConfig(); 51 51 }; 52 52 … … 56 56 DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) { 57 57 58 ConfigModified = NULL; // Will be allocated in ConfigChanged()58 ConfigModified = NULL; // Will be allocated in ReadConfig() 59 59 ConfigContent = NULL; 60 60 FileContent = NULL; … … 71 71 72 72 // Create DIM services 73 ConfigChanged();73 ReadConfig(); 74 74 } 75 75 … … 106 106 107 107 // Signalisation of configuration change 108 void EvidenceConfig:: ConfigChanged() {108 void EvidenceConfig::ReadConfig() { 109 109 110 110 static int ModifyTime; … … 249 249 while (!Config.ExitRequest) { 250 250 if (Notify != -1) { 251 if (read(Notify, &Event, sizeof(Event)) == sizeof(Event)) Config. ConfigChanged();251 if (read(Notify, &Event, sizeof(Event)) == sizeof(Event)) Config.ReadConfig(); 252 252 } 253 253 else pause(); -
Evidence/Doc/Evidence.tex
r255 r262 130 130 \item Provides a method for configuration requests. If the configuration data is not available, the application terminates with a message of FATAL severity unless default data is given. 131 131 \item Provides a method for safely translating DIM service data into text. 132 \item Implements the virtual DIM methods \lstinline{exitHandler()}. It can be called through a standard DIM command \lstinline{SvrName/EXIT}, taking a single integer as argument. Upon first invocation, the handler just sets the flag \lstinline{ExitRequest} which should be handled by the application. Upon second invocation, it will call \lstinline{exit()}. A special functionality is given to the argument value 0: it instructs the server to reset its message severity to INFO, without exiting. This is used by the \lstinline{Alarm} server if it receives a command to reset an alarm level, but is also available to the user. The user application can override this handler. 132 \item Implements the virtual DIM methods \lstinline{exitHandler()}. It can be called through a standard DIM command \lstinline{SvrName/EXIT}, taking a single integer as argument. Upon first invocation, the handler just sets the flag \lstinline{ExitRequest} which should be handled by the application. Upon second invocation, it will call \lstinline{exit()}. The user application can override this handler. 133 \item Provides a DIM command \lstinline{SvrName/ResetMessage}. It will set the message service to INFO severity with the information which client issued the command. This can be used to remove a WARN, ERROR or FATAL serverity once the problem has been fixed. The \lstinline{Alarm} server uses this command if it is instructed to reset an alarm level. The command takes no arguments. 133 134 \item Implements the virtual DIM methods \lstinline{errorHandler()}. The error handler will issue a message with ERROR severity that contains the DIM error code. The user application can override this handler. 134 135 \item Installs signal handler for SIGQUIT (ctrl-backspace), SIGTERM, SIGINT (ctrl-c), and SIGHUP (terminal closed). The signal handler sets first \lstinline{ExitRequest}, and on second invocation calls \lstinline{exit()}. After instantiating the class, the programmer may override the handlers. … … 157 158 void SendToLog(const char *, ...); 158 159 string GetConfig(string, string = string()); 159 void ActivateSignal(int);160 virtual void ConfigChanged() {}; 160 161 void Lock(); 161 162 void Unlock(); … … 163 164 static bool ServiceOK(DimInfo *); 164 165 static bool ServiceOK(DimRpcInfo *); 166 static bool ServiceOK(DimCurrentInfo *); 165 167 static vector<string> Tokenize(const string &, const string & = " "); 166 168 … … 179 181 \underline{\lstinline{GetConfig()}} issues, on first invocation, a DIM remote procedure call to the configuration server to retrieve the required data and returns it as a string. The second argument gives the data to be returned in case the server is unavailable or cannot provide the requested data. If in this case the second string is empty, the program terminates with a FATAL message. Using the service \lstinline{Config/ModifyTime}, the server keeps track of changes to the configuration file in the background. Upon subsequent requests for the same configuration data, it only issues a remote procedure call again if the file changed in the meantime. If not, the same data already retrieved is returned. This way, this function can be repeatedly called, even at high rate, without generating unnecessary load to the configuration server (as the configuration file does not change frequently). 180 182 181 \underline{\lstinline{ActivateSignal()}} is used to define a signal that should be emitted to the main thread in case the configuration file changes. See Sect.\,\ref{ConfigHandling} for details. No signal will be emitted if not set by this routine.182 183 The methods \underline{\lstinline{Lock()}} and \underline{\lstinline{Unlock()}} work on an internal mutex.\footnote{Its type is \lstinline{PTHREAD_MUTEX_ERRORCHECK}. In case an already locked mutex is re-locked, the corresponding system call will therefore return a error and thus avoid dead-locking. Error messages from \lstinline{Lock()} and \lstinline{Unlock()} are written to the console and to the log file. They are not published using \lstinline{Message()} since this method itself uses locking and calling it would result in an infinite recursion.} They are used by \lstinline{GetConfig()} but are also available for the user application to serialize access from multiple threads. If a signal is set by \lstinline{ActivateSignal()}, it is masked before locking and unmasked after unlocking.Calling functions in the locked state should be avoided as it might result in re-locking.183 The virtual method \underline{\lstinline{ConfigChanged()}} is executed in a separate thread when the configuration file changes. It can be reimplemented by the application. Calls to \lstinline{GetConfig()} from this method will be blocking and thus result in updated configuration data. 184 185 The methods \underline{\lstinline{Lock()}} and \underline{\lstinline{Unlock()}} work on an internal mutex.\footnote{Its type is \lstinline{PTHREAD_MUTEX_ERRORCHECK}. In case an already locked mutex is re-locked, the corresponding system call will therefore return a error and thus avoid dead-locking. Error messages from \lstinline{Lock()} and \lstinline{Unlock()} are written to the console and to the log file. They are not published using \lstinline{Message()} since this method itself uses locking and calling it would result in an infinite recursion.} They are used by \lstinline{GetConfig()} but are also available for the user application to serialize access from multiple threads. Calling functions in the locked state should be avoided as it might result in re-locking. 184 186 185 187 The static method \underline{\lstinline{ToString()}} translates the contents of a DIM service safely into a string that is returned. As no consistency between a service format and the contained data is guaranteed by DIM, precautions are necessary to avoid buffer overruns. The method currently handles the standardized message format \lstinline{"I:1;C"}, arrays of numbers and strings. All other formats are translated into a hex representation. The arguments are the DIM service format, a pointer to the service data and the data size in bytes. It is thread safe as it uses only the arguments and dynamically allocated storage. 186 188 187 The two variants of the static method\underline{\lstinline{ServiceOK()}} take a pointer to a received service update or result of a remote procedure call (as available in the respective handlers) and safely checks if its contents is identical to the constant \lstinline{NO_LINK}. If so, they return false. If using the same constant in the service declaration, this provides a safe way of being informed if a particular service becomes unavailable. Then, the handler is called once for that service with the data content \lstinline{NO_LINK}.189 The static methods \underline{\lstinline{ServiceOK()}} take a pointer to a received service update or result of a remote procedure call (as available in the respective handlers) and safely checks if its contents is identical to the constant \lstinline{NO_LINK}. If so, they return false. If using the same constant in the service declaration, this provides a safe way of being informed if a particular service becomes unavailable. Then, the handler is called once for that service with the data content \lstinline{NO_LINK}. 188 190 189 191 \underline{\lstinline{Tokenize()}} takes the string from the first argument, tokenizes it using the characters contained the second argument as delimeters, and returns a vector of strings containing all tokens. … … 358 360 As a first step in achieving this, the application should not store the obtained configuration data internally, but always re-request it using the method \lstinline{GetConfig()} described in Sect.\,\ref{EvidenceServer-Methods}. This method will only issue a remote procedure call to the \lstinline{Config} server if the configuration file has been modified since the last invocation. So calling this method even at high rate will not load the configuration server at all if the configuraton file is unchanged, but will yield up-to-date information if it did change. 359 361 360 The remote procedure call is blocking when called from the main thread , and non-blocking, using an \lstinline{rpcInfoHandler()}, when called from any other thread (especially also from the DIM handler thread). Blocking execution means that the remote procedure call will wait until the data has arrived from the server before returning to the application, whereas non-blocking execution will return immediately and invoke a handler later when the data arrived. This procedure is necessary since a blocking remote procedure call from \lstinline{infoHandler()} will result in a dead-lock.362 The remote procedure call is blocking when called from the main thread or from the method \lstinline{ConfigChanged()} (which runs in a separate thread). It is non-blocking, using an \lstinline{rpcInfoHandler()}, when called from any other thread, especially also from the DIM handler thread. Blocking execution means that the remote procedure call will wait until the data has arrived from the server before returning to the application, whereas non-blocking execution will return immediately and invoke a handler later when the data arrived. This procedure is necessary since a blocking remote procedure call from \lstinline{infoHandler()} will result in a dead-lock. 361 363 362 364 In the non-blocking case, the call to \lstinline{GetConfig()} returns still the previous, non-updated data even if the configuration file changed. The result of the non-blocking remote procedure call can only be processed by DIM once the current and all queued handler invocations have finished. When this is done, updated data will be returned by subsequent calls to \lstinline{GetConfig()}. … … 364 366 \subsection{\lstinline{ConfigChanged()}} 365 367 366 An alternative, albeit for the programmer more demanding, procedure for semi-automatic updates on configuration information is to implement the method \lstinline{ConfigChanged()} in the user class. This method can be invoked through a signaling mechanism by the \lstinline{EvidenceServer} class. 367 368 To this end, first \lstinline{ActivateSignal()} has to be called with a signal number that should be used for announcing configuration changes (for example, \lstinline{SIGUSR1}). That signal is send to the main thread when the service \lstinline{Config/ModifyTime} changes. The signal is caught by the internal signal handler of the \lstinline{EvidenceServer} class which in turn calls the method \lstinline{ConfigChanged()}. It is declared as \lstinline{virtual} in the class and defined as an empty function. The user application can override this by declaring and defining the method itself. This method is then executed in the main thread and thus \lstinline{GetConfig()} will use blocking connections to get always up-to-date data. 369 370 However, it must be kept in mind that this routine is running as part of the signal handler. The normal main thread execution was interrupted at an arbitrary point by the signal, thus suitable protection must be employed by the programmer when accessing common data structures. To ease that, the \lstinline{EvidenceServer} class contains the pair of methods \lstinline{Lock()} and \lstinline{Unlock()} that work on an class internal mutex. When the mutex is acquired, also the signal declared by \lstinline{ActivateSignal()} is disabled, thus preventing interruptions in case a configuration update occurs in the locked state. The mutex type is \lstinline{PTHREAD_MUTEX_ERRORCHECK} and therefore includes error checking: no dead-lock will occur if double locking, but the program will terminate with a \lstinline{FATAL} message. 368 An alternative, albeit for the programmer more demanding, procedure for semi-automatic updates on configuration information is to reimplement the virtual method \lstinline{ConfigChanged()} in the user class. This method is invoked as a separate thread by the \lstinline{EvidenceServer} class whenever the service \lstinline{Config/ModifyTime} changes (and also at program start-up). As it is not running within the DIM handler thread, \lstinline{GetConfig()} will use blocking connections to get immediately up-to-date data when called from \lstinline{ConfigChanged()}. 369 370 Running in a separate thread requires suitable protection by the programmer when accessing common data structures. To ease that, the \lstinline{EvidenceServer} class contains the pair of methods \lstinline{Lock()} and \lstinline{Unlock()} that work on an class internal mutex. The mutex type is \lstinline{PTHREAD_MUTEX_ERRORCHECK} and therefore includes error checking: no dead-lock will occur if double locking, but the program will terminate with a \lstinline{FATAL} message. 371 371 372 372 -
Evidence/Evidence.cc
r255 r262 31 31 // 32 32 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 34 EvidenceServer::Config::Config(): DimRpcInfo("ConfigRequest", NO_LINK), 35 DimCommand((This->Name+"/ResetMessage").c_str(), (char *) "") { 36 37 37 CurrentItem = string(); 38 38 ConfigTimeStamp = 0; 39 ThreadID = pthread_self();40 41 // Subscribe to be informed on configuration file change42 39 Service = new DimInfo("Config/ModifyTime", NO_LINK, this); 43 40 } … … 49 46 } 50 47 48 // Reset message and severity 49 void EvidenceServer::Config::commandHandler() { 50 51 This->Message(INFO, "Message reset by %s (ID %d)", getClientName(), getClientId()); 52 } 53 51 54 // Track last modification time of configuration file 52 55 void EvidenceServer::Config::infoHandler() { 53 56 57 pthread_t Thread; 58 int Ret; 59 60 if (!ServiceOK(DimInfo::getInfo())) return; 61 return; 54 62 This->Lock(); 55 63 ConfigTimeStamp = getInfo()->getInt(); 56 64 This->Unlock(); 57 65 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 64 76 void EvidenceServer::Config::rpcInfoHandler(){ 65 77 66 78 This->Lock(); 67 // Update map68 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 70 82 // Clear to allow new rpc call 71 83 CurrentItem.clear(); … … 73 85 } 74 86 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 88 string 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 104 EvidenceServer *EvidenceServer::This = NULL; 105 pthread_mutex_t EvidenceServer::Mutex; 106 set<pthread_t> EvidenceServer::Threads; 107 108 109 // Constructor 110 EvidenceServer::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 155 EvidenceServer::~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 172 void 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 179 void 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) 186 void 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 228 void 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 254 string EvidenceServer::GetConfig(string Item, string Default) { 77 255 78 256 string Result; 79 257 bool Blocking = false; 258 80 259 // 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(); 84 264 if (!Result.empty()) return Result; 85 265 86 // Blocking configuration request if in main thread87 if ( pthread_self() == ThreadID) {266 // Blocking configuration request 267 if (Blocking) { 88 268 DimRpcInfo Config((char *) "ConfigRequest", NO_LINK); 89 269 Config.setData((char *) (Name + " " + Item).c_str()); … … 92 272 if (!EvidenceServer::ServiceOK(&Config)) { 93 273 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()); 95 275 } 96 276 else Result = Default; … … 103 283 // Update configuration map 104 284 if (!Result.empty()) { 105 This->Lock();285 Lock(); 106 286 List[Item].Value = Result; 107 List[Item].Time = Conf igTimeStamp;108 This->Unlock();287 List[Item].Time = ConfClass->ConfigTimeStamp; 288 Unlock(); 109 289 } 110 290 } 111 291 112 // Non-blocking configuration request from other threads113 if ( pthread_self() != ThreadID) {114 This->Lock();292 // Non-blocking configuration request 293 if (!Blocking) { 294 Lock(); 115 295 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); 123 297 Result = List[Item].Value; 124 298 } 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()); 127 304 128 305 return Result; … … 130 307 131 308 132 //////////////////////////133 // EvidenceServer Class //134 //////////////////////////135 136 // Initialise137 int EvidenceServer::ConfigSignal = 0;138 EvidenceServer *EvidenceServer::This = NULL;139 140 // Constructor141 EvidenceServer::EvidenceServer(string ServerName): Name(ServerName) {142 143 // Initialize144 MessageService = NULL;145 MessageData = NULL;146 ExitRequest = false;147 This = this;148 149 // Initialise mutex150 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 signals161 signal(SIGQUIT, &SignalHandler); // CTRL-Backspace162 signal(SIGTERM, &SignalHandler); // Termination signal163 signal(SIGINT, &SignalHandler); // CTRL-C164 signal(SIGHUP, &SignalHandler); // Terminal closed165 166 // Catch C++ unhandled exceptions167 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 message173 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 server179 start(ServerName.c_str());180 addExitHandler(this);181 }182 183 // Destructor: Free memory184 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 handler200 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 handler210 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 application223 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 text232 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 file238 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 service242 if (MessageService != NULL) {243 MessageService->updateService(NewMsg, sizeof(struct Message)+strlen(NewMsg->Text)+1);244 }245 246 // Delete old message247 Lock();248 delete[] MessageData;249 MessageData = NewMsg;250 Unlock();251 252 // Terminate if severity if FATAL253 if (Severity == FATAL) exit(EXIT_FAILURE);254 }255 256 257 // Send to central logging server with non-blocking command258 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 list265 va_list ArgumentPointer;266 va_start(ArgumentPointer, Format);267 Ret = vasprintf(&Buffer, Format, ArgumentPointer);268 va_end(ArgumentPointer);269 270 // Send to logger271 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 data280 //281 // Program terminates if data is missing and no default given. Actual configuration282 // request will be made only if config file has modification since last request.283 // If called from infoHandler(), a non-blocking request will be made284 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 304 309 // Locking and unlocking functions. 305 // Signal blocked before locking (pthread_mutex_lock() not asynch-signal safe).306 310 // Message() is not used to avoid infinite recursion 307 311 void EvidenceServer::Lock() { 308 312 309 313 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) { 325 316 printf("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret)); 326 317 SendToLog("pthread_mutex_lock() failed in Lock() (%s)", strerror(Ret)); … … 332 323 333 324 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) { 337 327 printf("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret)); 338 328 SendToLog("pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret)); 339 329 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 } 353 331 } 354 332 355 333 356 334 // ====== 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 338 void 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 357 351 358 352 // Signal handler (causes pause() and other syscalls to return) … … 360 354 361 355 static bool Called = false; 362 363 // If signal indicates configuration change, invoke call-back364 if (Signal == EvidenceServer::ConfigSignal) {365 This->ConfigChanged();366 return;367 }368 356 369 357 // At first invocation just request exit … … 378 366 exit(EXIT_FAILURE); 379 367 } 368 380 369 381 370 // C++ exception handler (derived from gcc __verbose_terminate_handler()) … … 492 481 } 493 482 483 bool EvidenceServer::ServiceOK(DimCurrentInfo *Item) { 484 485 return !((Item->getSize() == strlen(NO_LINK)+1) && 486 (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0)); 487 } 488 494 489 495 490 // Tokenize std::string using given delimeter list -
Evidence/Evidence.h
r253 r262 10 10 #include <vector> 11 11 #include <map> 12 #include <set> 12 13 13 14 #include <exception> … … 20 21 #define EVIDENCE_REVISION "$Revision$" 21 22 23 void ConfigChanged(); 24 22 25 // Class declation of Evidence server 23 26 class EvidenceServer: public DimServer { 24 27 25 28 // Internal class for configuration requests 26 class Config: public Dim Client, public DimRpcInfo {29 class Config: public DimInfo, public DimCommand, public DimRpcInfo { 27 30 28 std::string Name;29 31 DimInfo *Service; 30 int ConfigTimeStamp;31 32 std::string CurrentItem; 32 pthread_t ThreadID;33 33 34 struct Item { 35 std::string Value; 36 int Time; 37 }; 38 std::map<std::string, struct Item> List; 39 34 void commandHandler(); 40 35 void infoHandler(); 41 36 void rpcInfoHandler(); 42 37 43 38 public: 44 Config( std::string);39 Config(); 45 40 ~Config(); 46 41 47 std::string GetConfig(std::string, std::string); 42 int ConfigTimeStamp; 43 std::string RequestNB(std::string); 48 44 }; 45 46 struct Item { 47 std::string Value; 48 int Time; 49 }; 50 std::map<std::string, struct Item> List; 49 51 50 52 struct Message { … … 57 59 struct Message *MessageData; 58 60 class Config *ConfClass; 59 static int ConfigSignal; // static since accessed in signal handler60 pthread_mutex_t Mutex;61 static pthread_mutex_t Mutex; 62 static std::set<pthread_t> Threads; 61 63 static EvidenceServer *This; 62 64 … … 65 67 virtual void errorHandler(int, int, char *); 66 68 virtual void exitHandler(int); 67 virtual void ConfigChanged() {}; 69 static void CallConfigChanged(); // static for phread_create() 68 70 69 71 public: … … 74 76 75 77 void Message(MessageType, const char *, ...); 76 void SendToLog(const char *, ...);78 static void SendToLog(const char *, ...); 77 79 std::string GetConfig(std::string, std::string = std::string()); 78 v oid ActivateSignal(int);79 void Lock();80 void Unlock();80 virtual void ConfigChanged() {}; 81 static void Lock(); 82 static void Unlock(); 81 83 static std::string ToString(char *, void *, int); 82 84 static bool ServiceOK(DimInfo *); 83 85 static bool ServiceOK(DimRpcInfo *); 86 static bool ServiceOK(DimCurrentInfo *); 84 87 static std::vector<std::string> Tokenize(const std::string &, const std::string & = " "); 85 88 -
Evidence/readme.txt
r255 r262 28 28 23/6/2010 GetConfig() returns std::string. Non-blocking configuration request in case 29 29 GetConfig() not called from main thread. Access to configuration information 30 internally mutex protected. With ActivateSignal() a signal can be set that is send 31 to main thread upon configuration file change, the build-in signal handler then 32 invokes the (virtual) method ConfigChanged(). 30 internally mutex protected. 33 31 24/6/2010 Workaround for erroneous /SERVICE_LIST updates. Added static tokenize method to 34 32 Evidence class. … … 46 44 21/7/2010 Lock()/Unlock() do not report errors via Message(), but print to console and use 47 45 SendToLog(). That avoids a recursion problem since Message() also uses locking. 48 The general exitHandler() will react in a special way to code 0: it will reset 49 the message severity. This feature is used by Alarm if it receives a command to 50 reset an alarm level. 46 26/7/2010 General command '/ResetMessage' will reset message text and severity. This feature is 47 used by Alarm if it receives a command to reset an alarm level. 48 ConfigChanged() is called as separate thread when configuration file changes. Thread ID 49 is checked in GetConfig() and also from this thread it will make blocking requests.
Note:
See TracChangeset
for help on using the changeset viewer.