source: Evidence/Config.cc@ 253

Last change on this file since 253 was 253, checked in by ogrimm, 14 years ago
Added command ResetAlarm, Evidence servers now always safely translate a DIM string into a C string, added documentation, replaced several vectors my maps
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 ConfigChanged().
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 ConfigChanged();
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 ConfigChanged()
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 ConfigChanged();
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::ConfigChanged() {
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.ConfigChanged();
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.