source: Evidence/Config.cc@ 188

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