source: Evidence/Config.cc@ 9347

Last change on this file since 9347 was 262, checked in by ogrimm, 14 years ago
Removed signaling to invoke ConfigChanged(), now it is run as separate thread. New command '/ResetMessage'
File size: 7.4 KB
Line 
1/********************************************************************\
2
3 Configuration server for the Evidence Control System
4
5 - The name of a configuration file can be given as command line argument
6 - If a configuration file change is detected through inotify, the service
7 "Config/ModifyTime" contains the last modification UNIX time.
8 - The contents of the configuration file is available through Config/ConfigData.
9 - The parser removes all tabs, multiple, leading and trailing spaces, and
10 concatenates all lines until the next item.
11
12 A mutex is used for preventing concurrent access to the configuration data from
13 the methods rpcHandler() and ReadConfig().
14
15 Oliver Grimm, July 2010
16
17\********************************************************************/
18
19#define SERVER_NAME "Config"
20
21#include "Evidence.h"
22
23#include <ctype.h>
24#include <sys/stat.h>
25#include <sys/inotify.h>
26#include <sstream>
27
28using namespace std;
29
30//
31// Class derived from DimRpc
32//
33class EvidenceConfig: public DimRpc, public EvidenceServer {
34
35 private:
36 map<string, string> Map;
37 FILE *File;
38 char *FileContent;
39 DimService *ConfigModified;
40 DimService *ConfigContent;
41
42 void rpcHandler();
43 void AddItem(string, string, string);
44 string RemoveSpaces(string &);
45
46 public:
47 EvidenceConfig(const char *);
48 ~EvidenceConfig();
49
50 void ReadConfig();
51};
52
53
54// Constructor
55EvidenceConfig::EvidenceConfig(const char *Filename):
56 DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) {
57
58 ConfigModified = NULL; // Will be allocated in ReadConfig()
59 ConfigContent = NULL;
60 FileContent = NULL;
61
62 // Open configuration file
63 if ((File = fopen(Filename, "r")) == NULL) {
64 Message(FATAL, "Could not open configuration file '%s' (%s)", Filename, strerror(errno));
65 }
66
67 // Disable buffering, so file modifications are immediately seen
68 if (setvbuf(File, NULL, _IONBF, 0) != 0) {
69 Message(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
70 }
71
72 // Create DIM services
73 ReadConfig();
74}
75
76// Destructor
77EvidenceConfig::~EvidenceConfig() {
78
79 if (File != NULL && fclose(File) != 0) Message(ERROR, "Error closing configuration file (%s)", strerror(errno));
80
81 delete ConfigModified;
82 delete ConfigContent;
83 delete[] FileContent;
84}
85
86
87// Implementation of response to configuration request
88void EvidenceConfig::rpcHandler() {
89
90 string Response, Item = ToString((char *) "C", getData(), getSize());
91
92 // Search for config data in list (default response is empty string)
93 // Lock because ConfigChange() might also access concurrently
94 Lock();
95 if (Map.count(Item) != 0) Response = Map[Item];
96 Unlock();
97
98 // Send data and update Status
99 setData((char *) Response.c_str());
100
101 Message(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
102 DimServer::getClientName(), DimServer::getClientId(),
103 Item.c_str(), Response.c_str());
104}
105
106
107// Signalisation of configuration change
108void EvidenceConfig::ReadConfig() {
109
110 static int ModifyTime;
111
112 //
113 // Part A: Handle updates to DIM services
114 //
115
116 // Access status information for configuration file (if fileno() returns -1, fstat() reports error)
117 struct stat Stat;
118 if (fstat(fileno(File), &Stat) == -1) {
119 Message(ERROR, "Error with stat() system call for configuration file, cannot update configuration file information (%s)", strerror(errno));
120 delete ConfigModified;
121 delete ConfigContent;
122 ConfigModified = NULL;
123 ConfigContent = NULL;
124 return;
125 }
126
127 // Create/update DIM service to indicate changes of configuration file
128 ModifyTime = Stat.st_mtime;
129 if (ConfigModified == NULL) ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
130 else ConfigModified->updateService();
131
132 // Read new configuration file (enfore \0 termination)
133 char *NewContent = new char [Stat.st_size+1];
134 rewind(File);
135 if (fread(NewContent, sizeof(char), Stat.st_size, File) != Stat.st_size) {
136 Message(FATAL, "Could not read configuration file");
137 }
138 NewContent[Stat.st_size] = '\0';
139
140 // Create/update DIM service that contains the current configuration file
141 if (ConfigContent == NULL )ConfigContent = new DimService(SERVER_NAME"/ConfigData", NewContent);
142 else ConfigContent->updateService(NewContent);
143
144 delete[] FileContent;
145 FileContent = NewContent;
146
147 //
148 // Part B: Interpret configuration list
149 //
150
151 stringstream In(FileContent), Out;
152 string Section, Item, Line, Data;
153
154 // First clean up and concatenate lines
155 while (getline(In, Line).good()) {
156 // Remove comments
157 if (Line.find('#') != string::npos) Line.erase(Line.find('#'));
158 // Replace all tabs by spaces
159 while (Line.find("\t") != string::npos) Line[Line.find("\t")] = ' ';
160 // Remove empty lines
161 if (RemoveSpaces(Line).empty()) continue;
162 // Concatenate if line ends with '\'
163 if (Line[Line.size()-1] != '\\') Out << Line << endl;
164 else Out << Line.erase(Line.size()-1);
165 };
166
167 In.str(Out.str());
168 In.clear();
169
170 // Interpret data
171 while (getline(In, Line).good()) {
172
173 // Check if current line is section heading (contains only [xxx])
174 if (Line.find('[')==0 && Line.find(']')==Line.size()-1) {
175 // Add previous item to list (if any)
176 AddItem(Section, Item, Data);
177 Item.clear();
178 Data.clear();
179 // Extract new section name
180 Section = Line.substr(1, Line.size()-2);
181 continue;
182 }
183
184 // Check if current line contains equal sign (defines new item name)
185 if((Line.find('=')) != string::npos) {
186 // Add previous item to list
187 AddItem(Section, Item, Data);
188 // Extract parameter name and data
189 Item = string(Line, 0, Line.find('=')-1);
190 Data = string(Line, Line.find('=')+1, string::npos);
191 }
192 else Data += ' ' + Line; // Concatenate lines
193 }
194 // Add last item
195 AddItem(Section, Item, Data);
196}
197
198// Add item to configuration list
199void EvidenceConfig::AddItem(string Section, string Parameter, string Data) {
200
201 // Clean up strings
202 RemoveSpaces(Parameter);
203 RemoveSpaces(Data);
204 if (Section.empty() || Parameter.empty() || Data.empty()) return;
205
206 // Add to configuration list
207 Lock();
208 Map[Section + " " + Parameter] = Data;
209 Unlock();
210}
211
212// Removes whitespace
213string EvidenceConfig::RemoveSpaces(string &Text) {
214
215 // Remove leading spaces
216 while (!Text.empty() && isspace(Text[0])) Text.erase(0, 1);
217 // Remove trailing spaces
218 while (!Text.empty() && isspace(Text[Text.size()-1])) Text.erase(Text.size()-1);
219 // Remove multiple spaces
220 while (Text.find(" ") != string::npos) Text.erase(Text.find(" "), 1);
221
222 return Text;
223}
224
225//
226// Declaring class static ensures destructor is called when exit() is invoked
227//
228int main(int argc, char *argv[]) {
229
230 if (argc != 2) {
231 printf("Usage: %s <Configuration-File>\n", argv[0]);
232 exit(EXIT_FAILURE);
233 }
234
235 static EvidenceConfig Config(argv[1]);
236 int Notify;
237 struct inotify_event Event;
238
239 if ((Notify = inotify_init()) == -1) {
240 Config.Message(EvidenceConfig::WARN, "inotify_init() failed, cannot monitor changes of configuration file (%s)\n", strerror(errno));
241 }
242 else if (inotify_add_watch(Notify, argv[1], IN_MODIFY) == -1) {
243 Config.Message(EvidenceConfig::WARN, "Could not set inotify watch on configuration file (%s)\n", strerror(errno));
244 close(Notify);
245 Notify = -1;
246 }
247
248 // Sleep until file changes or signal caught
249 while (!Config.ExitRequest) {
250 if (Notify != -1) {
251 if (read(Notify, &Event, sizeof(Event)) == sizeof(Event)) Config.ReadConfig();
252 }
253 else pause();
254 }
255
256 // Closing will also free all inotify watches
257 if (Notify != -1) close(Notify);
258}
Note: See TracBrowser for help on using the repository browser.