/********************************************************************\ Configuration server for the Evidence Control System - The name of a configuration file can be given as command line argument - If a configuration file change is detected through inotify, the service "Config/ModifyTime" contains the last modification UNIX time. - The contents of the configuration file is available through Config/ConfigData. - The current parser removes all tabs, multiple, leading and trailing spaces, and concatenates lines ending with '+'. A mutex is used for preventing concurrent access to the configuration data from the methods rpcHandler() and ConfigChanged(). Oliver Grimm, April 2010 \********************************************************************/ #define DEFAULT_CONFIG "../config/Evidence.conf" #define SERVER_NAME "Config" #include "Evidence.h" #include #include #include #include using namespace std; // // Class derived from DimRpc // class EvidenceConfig: public DimRpc, public EvidenceServer { private: struct Item { string Name; string Data; }; vector List; FILE *File; char *FileContent; DimService *ConfigModified; DimService *ConfigContent; pthread_mutex_t Mutex; void rpcHandler(); public: EvidenceConfig(const char *); ~EvidenceConfig(); void ConfigChanged(); }; // Constructor EvidenceConfig::EvidenceConfig(const char *Filename): DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) { ConfigModified = NULL; // Will be allocated in ConfigChanged() ConfigContent = NULL; FileContent = NULL; // Initialise mutex (errno is not set by pthread_mutex_init()) if (pthread_mutex_init(&Mutex, NULL) != 0) { Message(FATAL, "pthread_mutex_init() failed"); } // Open configuration file if ((File = fopen(Filename, "r")) == NULL) { Message(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno)); } // Disable buffering, so file modifications are immediately seen if (setvbuf(File, NULL, _IONBF, 0) != 0) { Message(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename); } // Create DIM services ConfigChanged(); } // Destructor EvidenceConfig::~EvidenceConfig() { if (File != NULL && fclose(File) != 0) Message(ERROR, "Error closing configuration file (%s)", strerror(errno)); delete ConfigModified; delete ConfigContent; delete[] FileContent; if (pthread_mutex_destroy(&Mutex) != 0) Message(ERROR, "pthread_mutex_destroy() failed"); } // Implementation of response to configuration request void EvidenceConfig::rpcHandler() { string Response; // Lock because ConfigChange() might access concurrently if (pthread_mutex_lock(&Mutex) != 0) Message(ERROR, "pthread_mutex_lock() failed in rpcHandler()"); // Search for config data in list (default response is empty string) for (int i=0; iupdateService(); // Read new configuration file (enfore \0 termination) char *NewContent = new char [Stat.st_size+1]; rewind(File); if (fread(NewContent, sizeof(char), Stat.st_size, File) != Stat.st_size) { Message(FATAL, "Could not read configuration file"); } NewContent[Stat.st_size] = '\0'; // Create/update DIM service that contains the current configuration file if (ConfigContent == NULL )ConfigContent = new DimService(SERVER_NAME"/ConfigData", NewContent); else ConfigContent->updateService(NewContent); delete[] FileContent; FileContent = NewContent; // // Part B: Interpret configuration list // stringstream In(FileContent), Out; string Line; struct Item New; size_t Pos; // First clean up and concatenate lines while (getline(In, Line).good()) { // Remove comments if (Line.find('#') != string::npos) Line.erase(Line.find('#')); // Replace all tabs by spaces while (Line.find("\t") != string::npos) Line[Line.find("\t")] = ' '; // Remove leading spaces while (!Line.empty() && isspace(Line[0])) Line.erase(0, 1); // Remove trailing spaces while (!Line.empty() && isspace(Line[Line.size()-1])) Line.erase(Line.size()-1); // Remove empty lines if (Line.empty()) continue; // Concatenate if line ends with '+' if (Line[Line.size()-1] != '+') Out << Line << endl; else Out << Line.erase(Line.size()-1); }; In.str(Out.str()); In.clear(); // Interpret data while (getline(In, Line).good()) { // Remove multiple spaces while (Line.find(" ") != string::npos) Line.erase(Line.find(" "), 1); // Find second space character Pos = Line.find(" ", Line.find(" ") + 1); if(Pos == string::npos) continue; // Extract configuration name and data New.Name = string(Line, 0, Pos); New.Data = string(Line, Pos+1); // Add to configuration list if (pthread_mutex_lock(&Mutex) != 0) Message(ERROR, "pthread_mutex_lock() failed in ConfigChanged()"); List.push_back(New); if (pthread_mutex_unlock(&Mutex) != 0) Message(ERROR, "pthread_mutex_unlock() failed in ConfigChanged()"); }; } // // Declaring class static ensures destructor is called when exit() is invoked // int main(int argc, char *argv[]) { static EvidenceConfig Config(argc<2 ? DEFAULT_CONFIG : argv[1]); int Notify; struct inotify_event Event; if ((Notify = inotify_init()) == -1) { Config.Message(EvidenceConfig::WARN, "inotify_init() failed, cannot monitor changes of configuration file (%s)\n", strerror(errno)); } else if (inotify_add_watch(Notify, argc<2 ? DEFAULT_CONFIG : argv[1], IN_MODIFY) == -1) { Config.Message(EvidenceConfig::WARN, "Could not set inotify watch on configuration file (%s)\n", strerror(errno)); close(Notify); Notify = -1; } // Sleep until file changes or signal caught while (!Config.ExitRequest) { if (Notify != -1) { read(Notify, &Event, sizeof(Event)); Config.ConfigChanged(); } else pause(); } // Closing will also free all inotify watches if (Notify != -1) close(Notify); }