/********************************************************************\ Configuration server for the Evidence Control System - The configuration file is opened without buffering to catch changes without closing/opening. - 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" is updated with the current UNIX time to inform applications. The initial value of the server is the last file modification time. - The employed line buffer has conservatively at least the size of the configuration file. If needed, it will be enlarged. Oliver Grimm, November 2009 \********************************************************************/ #define DEFAULT_CONFIG "../config/Evidence.conf" #define SERVER_NAME "Config" #include "Evidence.h" #include #include #include // // Class derived from DimRpc // class EvidenceConfig: public DimRpc, public EvidenceServer { private: FILE *File; char *Buffer; unsigned int BufferLength; DimService *ConfigModified; int ModifyTime; void rpcHandler(); public: EvidenceConfig(const char *); ~EvidenceConfig(); void ConfigChanged(); }; // Constructor EvidenceConfig::EvidenceConfig(const char *Filename): DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) { // Open configuration file if ((File = fopen(Filename, "r")) == NULL) { State(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno)); } // Create DIM service to indicate changes of configuration file struct stat Stat; if (stat(Filename, &Stat) == -1) { State(WARN, "Could not read last modification time of configuration file '%s' (%s)", Filename, strerror(errno)); ModifyTime = 0; } else ModifyTime = Stat.st_mtime; ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime); // Disable buffering, so file modifications are immediately seen if (setvbuf(File, NULL, _IONBF, 0) != 0) { State(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename); } Buffer = NULL; // Will be allocated in rpcHandler() BufferLength = 0; } // Destructor EvidenceConfig::~EvidenceConfig() { if (File != NULL) fclose(File); delete[] Buffer; } // Implementation of response to configuration request void EvidenceConfig::rpcHandler() { char *Token1,*Token2,*Token3, *Request = getString(); struct stat FileStatus; // Check if Buffer[] is large enough to hold full file, enlarge if necessary if (fstat(fileno(File), &FileStatus) == -1) { State(FATAL, "Could not determine size of configuration file to allocate buffer (%s)", strerror(errno)); } else if(BufferLength < FileStatus.st_size) { delete[] Buffer; Buffer = new char [FileStatus.st_size]; BufferLength = FileStatus.st_size; } // Search for configuration item rewind(File); while (fgets(Buffer, BufferLength, File) != NULL) { // Combine lines that end with '+' while (Buffer[strlen(Buffer)-2] == '+') { if (fgets(Buffer+strlen(Buffer)-2, BufferLength-(strlen(Buffer)-2), File) == NULL) break; } // Ignore comments for (int i=0; i0) && isspace(*(Token3+strlen(Token3)-1))) *(Token3+strlen(Token3)-1) = '\0'; setData(Token3); break; } } // If configuration data not found, send empty string if (feof(File)!=0) setData((char *) ""); State(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.", DimServer::getClientName(), DimServer::getClientId(), Request, feof(File)!=0 ? "n/a" : Token3); } // Signalisation of configuration change void EvidenceConfig::ConfigChanged() { ModifyTime = time(NULL); ConfigModified->updateService(); } // // 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.State(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.State(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(); } if (Notify != -1) close(Notify); }