source: Evidence/Config/Config.cc@ 142

Last change on this file since 142 was 142, checked in by ogrimm, 15 years ago
Various updates
File size: 5.1 KB
Line 
1/********************************************************************\
2
3 Configuration server for the Evidence Control System
4
5 - The configuration file is opened without buffering to catch changes
6 without closing/opening.
7 - The name of a configuration file can be given as command line argument
8 - If a configuration file change is detected through inotify, the service
9 "Config/ModifyTime" is updated with the current UNIX time to inform applications.
10 The initial value of the server is the last file modification time.
11 - The employed line buffer has conservatively at least the size of the
12 configuration file. If needed, it will be enlarged.
13
14 Oliver Grimm, November 2009
15
16\********************************************************************/
17
18#define DEFAULT_CONFIG "configuration.txt"
19#define SERVER_NAME "Config"
20
21#include "../Evidence.h"
22#include <ctype.h>
23#include <sys/stat.h>
24#include <sys/inotify.h>
25
26//
27// Class derived from DimRpc
28//
29class EvidenceConfig: public DimRpc, public EvidenceServer {
30
31 private:
32 FILE *File;
33 char *Buffer;
34 unsigned int BufferLength;
35 DimService *ConfigModified;
36 int ModifyTime;
37
38 void rpcHandler();
39
40 public:
41 EvidenceConfig(const char *);
42 ~EvidenceConfig();
43
44 void ConfigChanged();
45};
46
47// Constructor
48EvidenceConfig::EvidenceConfig(const char *Filename):
49 DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) {
50
51 // Open configuration file
52 if ((File = fopen(Filename, "r")) == NULL) {
53 State(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno));
54 }
55
56 // Create DIM service to indicate changes of configuration file
57 struct stat Stat;
58 if (stat(Filename, &Stat) == -1) {
59 State(WARN, "Could not read last modification time of configuration file '%s' (%s)", Filename, strerror(errno));
60 ModifyTime = 0;
61 }
62 else ModifyTime = Stat.st_mtime;
63 ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
64
65 // Disable buffering, so file modifications are immediately seen
66 if (setvbuf(File, NULL, _IONBF, 0) != 0) {
67 State(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
68 }
69
70 Buffer = NULL; // Will be allocated in rpcHandler()
71 BufferLength = 0;
72}
73
74// Destructor
75EvidenceConfig::~EvidenceConfig() {
76
77 if (File != NULL) fclose(File);
78 delete[] Buffer;
79}
80
81// Implementation of response to configuration request
82void EvidenceConfig::rpcHandler() {
83
84 char *Token1,*Token2,*Token3, *Request = getString();
85 struct stat FileStatus;
86
87 // Check if Buffer[] is large enough to hold full file, enlarge if necessary
88 if (fstat(fileno(File), &FileStatus) == -1) {
89 State(FATAL, "Could not determine size of configuration file to allocate buffer (%s)", strerror(errno));
90 }
91 else if(BufferLength < FileStatus.st_size) {
92 delete[] Buffer;
93 Buffer = new char [FileStatus.st_size];
94 BufferLength = FileStatus.st_size;
95 }
96
97 // Search for configuration item
98 rewind(File);
99 while (fgets(Buffer, BufferLength, File) != NULL) {
100
101 // Combine lines that end with '+'
102 while (Buffer[strlen(Buffer)-2] == '+') {
103 if (fgets(Buffer+strlen(Buffer)-2, BufferLength-(strlen(Buffer)-2), File) == NULL) break;
104 }
105
106 // Ignore comments
107 for (int i=0; i<strlen(Buffer); i++) if (Buffer[i] == '#') Buffer[i] = '\0';
108
109 // Extract tokens
110 Token1 = strtok(Buffer, " \t:");
111 Token2 = strtok(NULL, " \t:");
112 Token3 = strtok(NULL, "\n");
113
114 // Check if all tokens existing
115 if(Token1==NULL || Token2==NULL || Token3==NULL) continue;
116
117 // Check for match and then send data (removing trainlin whitespace)
118 if (strstr(Request, Token1)!=NULL && strstr(Request, Token2)!=NULL) {
119 while (isspace(*Token3) != 0) Token3++;
120 setData(Token3);
121 break;
122 }
123 }
124
125 // If configuration data not found, send empty string
126 if (feof(File)!=0) setData((char *) "");
127
128 State(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
129 DimServer::getClientName(),
130 DimServer::getClientId(),
131 Request, feof(File)!=0 ? "n/a" : Token3);
132}
133
134// Signalisation of configuration change
135void EvidenceConfig::ConfigChanged() {
136
137 ModifyTime = time(NULL);
138 ConfigModified->updateService();
139}
140
141//
142// Declaring class static ensures destructor is called when exit() is invoked
143//
144int main(int argc, char *argv[]) {
145
146 static EvidenceConfig Config(argc<2 ? DEFAULT_CONFIG : argv[1]);
147
148 int Notify;
149 struct inotify_event Event;
150
151 if ((Notify = inotify_init()) == -1) {
152 Config.State(EvidenceConfig::WARN, "inotify_init() failed, cannot monitor changes of configuration file (%s)\n", strerror(errno));
153 }
154 else if (inotify_add_watch(Notify, argc<2 ? DEFAULT_CONFIG : argv[1], IN_MODIFY) == -1) {
155 Config.State(EvidenceConfig::WARN, "Could not set inotify watch on configuration file (%s)\n", strerror(errno));
156 close(Notify);
157 Notify = -1;
158 }
159
160 // Sleep until file changes or signal caught
161 while (!EvidenceServer::ExitRequest) {
162 if (Notify != -1) {
163 read(Notify, &Event, sizeof(Event));
164 Config.ConfigChanged();
165 }
166 else pause();
167 }
168
169 if (Notify != -1) close(Notify);
170}
Note: See TracBrowser for help on using the repository browser.