summaryrefslogtreecommitdiffabout
path: root/lib/configuration.cc
Unidiff
Diffstat (limited to 'lib/configuration.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/configuration.cc474
1 files changed, 474 insertions, 0 deletions
diff --git a/lib/configuration.cc b/lib/configuration.cc
new file mode 100644
index 0000000..4ee1526
--- a/dev/null
+++ b/lib/configuration.cc
@@ -0,0 +1,474 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <unistd.h>
5 #include <fnmatch.h>
6 #include <cassert>
7 #include <stdexcept>
8 using namespace std;
9 #include <dotconf.h>
10 #include "sitecing/configuration.h"
11 #include "sitecing/sitecing_util.h"
12 #include "sitecing/scoreboard.h"
13#endif
14
15namespace sitecing {
16
17 configuration::configuration()
18 : flags(0), autobuild(false) { }
19 configuration::configuration(const string& cfile,bool ab)
20 : flags(0), autobuild(ab) {
21 parse(cfile);
22 }
23
24 enum dc_ctx {
25 DCC_ROOT = 1,
26 DCC_PATH = 2,
27 DCC_SCRC = 4
28 };
29 struct dc_context {
30 dc_ctx ctx;
31 configuration* cf;
32 list<config_options*> co;
33 };
34
35 static DOTCONF_CB(dco_root_source) { dc_context *dcc = (dc_context*)ctx;
36 dcc->cf->root_source = cmd->data.str;
37 dcc->cf->flags |= configuration::flag_root_source;
38 return NULL;
39 }
40 static DOTCONF_CB(dco_root_intermediate) { dc_context *dcc = (dc_context*)ctx;
41 dcc->cf->root_intermediate = cmd->data.str;
42 dcc->cf->flags |= configuration::flag_root_intermediate;
43 return NULL;
44 }
45 static DOTCONF_CB(dco_root_so) { dc_context *dcc = (dc_context*)ctx;
46 dcc->cf->root_so = cmd->data.str;
47 dcc->cf->flags |= configuration::flag_root_so;
48 return NULL;
49 }
50 static DOTCONF_CB(dco_listen_socket) { dc_context *dcc = (dc_context*)ctx;
51 dcc->cf->listen_socket = cmd->data.str;
52 dcc->cf->flags |= configuration::flag_listen_socket;
53 return NULL;
54 }
55 static DOTCONF_CB(dco_rc_file_name) { dc_context *dcc = (dc_context*)ctx;
56 dcc->cf->rc_file_name = cmd->data.str;
57 dcc->cf->flags |= configuration::flag_rc_file_name;
58 return NULL;
59 }
60 static DOTCONF_CB(dco_min_children) { dc_context *dcc = (dc_context*)ctx;
61 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
62 return "MinChildren is too big";
63 dcc->cf->min_children = cmd->data.value;
64 dcc->cf->flags |= configuration::flag_min_children;
65 return NULL;
66 }
67 static DOTCONF_CB(dco_max_children) { dc_context *dcc = (dc_context*)ctx;
68 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
69 return "MaxChildren is too big";
70 dcc->cf->max_children = cmd->data.value;
71 dcc->cf->flags |= configuration::flag_max_children;
72 return NULL;
73 }
74 static DOTCONF_CB(dco_min_spare_children) { dc_context *dcc = (dc_context*)ctx;
75 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
76 return "MinSpareChildren is too big";
77 dcc->cf->min_spare_children = cmd->data.value;
78 dcc->cf->flags |= configuration::flag_min_spare_children;
79 return NULL;
80 }
81 static DOTCONF_CB(dco_max_spare_children) { dc_context *dcc = (dc_context*)ctx;
82 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
83 return "MaxSpareChildren is too big";
84 dcc->cf->max_spare_children = cmd->data.value;
85 dcc->cf->flags |= configuration::flag_max_spare_children;
86 return NULL;
87 }
88 static DOTCONF_CB(dco_requests_per_child) { dc_context *dcc = (dc_context*)ctx;
89 dcc->cf->requests_per_child = cmd->data.value;
90 dcc->cf->flags |= configuration::flag_requests_per_child;
91 return NULL;
92 }
93 static DOTCONF_CB(dco_multi_process) { dc_context *dcc = (dc_context*)ctx;
94 dcc->cf->multi_process = cmd->data.value;
95 dcc->cf->flags |= configuration::flag_multi_process;
96 return NULL;
97 }
98 static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx;
99 dcc->cf->user = cmd->data.str;
100 dcc->cf->flags |= configuration::flag_user;
101 return NULL;
102 }
103 static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx;
104 dcc->cf->group = cmd->data.str;
105 dcc->cf->flags |= configuration::flag_group;
106 return NULL;
107 }
108 static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx;
109 dcc->cf->chroot = cmd->data.str;
110 dcc->cf->flags |= configuration::flag_chroot;
111 return NULL;
112 }
113 static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx;
114 dcc->cf->pid_file = cmd->data.str;
115 dcc->cf->flags |= configuration::flag_pid_file;
116 return NULL;
117 }
118 static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*) ctx;
119 dcc->cf->daemonize = cmd->data.value;
120 dcc->cf->flags |= configuration::flag_daemonize;
121 return NULL;
122 }
123
124 static DOTCONF_CB(dco_path) { dc_context *dcc = (dc_context*)ctx;
125 string path = cmd->data.str;
126 if(path[path.length()-1]=='>')
127 path.erase(path.length()-1);
128 // TODO: normalize path
129 dcc->co.push_front(&(dcc->cf->specs[path]));
130 dcc->ctx = DCC_PATH; // TODO: stack it, instead
131 return NULL;
132 }
133 static DOTCONF_CB(dco__path) { dc_context *dcc = (dc_context*)ctx;
134 dcc->co.pop_front();
135 assert(dcc->co.size());
136 dcc->ctx = DCC_ROOT; // TODO: stack it, instead
137 return NULL;
138 }
139
140 static DOTCONF_CB(dco_skeleton) { dc_context *dcc = (dc_context*)ctx;
141 dcc->co.front()->skeleton = cmd->data.str;
142 dcc->co.front()->flags |= config_options::flag_skeleton;
143 return NULL;
144 }
145 static DOTCONF_CB(dco_cpp_flags) { dc_context *dcc = (dc_context*)ctx;
146 for(char **arg=cmd->data.list;*arg;arg++)
147 dcc->co.front()->cpp_flags.push_back(*arg);
148 dcc->co.front()->flags |= config_options::flag_cpp_flags;
149 return NULL;
150 }
151 static DOTCONF_CB(dco_ld_flags) { dc_context *dcc = (dc_context*)ctx;
152 for(char **arg=cmd->data.list;*arg;arg++)
153 dcc->co.front()->ld_flags.push_back(*arg);
154 dcc->co.front()->flags |= config_options::flag_ld_flags;
155 return NULL;
156 }
157 static DOTCONF_CB(dco_intermediate_deps) { dc_context *dcc = (dc_context*) ctx;
158 for(char **arg=cmd->data.list;*arg;arg++)
159 dcc->co.front()->intermediate_deps.push_back(*arg);
160 dcc->co.front()->flags |= config_options::flag_intermediate_deps;
161 return NULL;
162 }
163 static DOTCONF_CB(dco_so_deps) { dc_context *dcc = (dc_context*) ctx;
164 for(char **arg=cmd->data.list;*arg;arg++)
165 dcc->co.front()->so_deps.push_back(*arg);
166 dcc->co.front()->flags |= config_options::flag_so_deps;
167 return NULL;
168 }
169 static DOTCONF_CB(dco_build) { dc_context *dcc = (dc_context*)ctx;
170 dcc->co.front()->build = cmd->data.value;
171 dcc->co.front()->flags |= config_options::flag_build;
172 return NULL;
173 }
174 static DOTCONF_CB(dco_cpp_deps) { dc_context *dcc = (dc_context*)ctx;
175 dcc->co.front()->cpp_deps = cmd->data.value;
176 dcc->co.front()->flags |= config_options::flag_cpp_deps;
177 return NULL;
178 }
179 static DOTCONF_CB(dco_exception_handler) { dc_context *dcc = (dc_context*)ctx;
180 dcc->co.front()->exception_handler = cmd->data.str;
181 dcc->co.front()->flags |= config_options::flag_exception_handler;
182 return NULL;
183 }
184 static DOTCONF_CB(dco_http_status_handler) { dc_context *dcc = (dc_context*)ctx;
185 if(cmd->arg_count!=2)
186 return "Invalid number of arguments";
187 dcc->co.front()->http_status_handlers[cmd->data.list[0]] = cmd->data.list[1];
188 dcc->co.front()->flags |= config_options::flag_http_status_handlers;
189 return NULL;
190 }
191 static DOTCONF_CB(dco_action) { dc_context *dcc = (dc_context*)ctx;
192 if(cmd->arg_count<2)
193 return "Invalid number of arguments";
194 try {
195 char **arg=cmd->data.list;
196 dcc->co.front()->action_handlers.push_back(config_options::action_handler_t(arg[0],arg[1]));
197 for(arg+=2;*arg;arg++)
198 dcc->co.front()->action_handlers.back().args.push_back(*arg);
199 dcc->co.front()->flags |= config_options::flag_action_handlers;
200 }catch(exception& e) {
201 return "Error processing Action directive"; // XXX: could be done better
202 }
203 return NULL;
204 }
205 static DOTCONF_CB(dco_auto_build_files) { dc_context *dcc = (dc_context*)ctx;
206 if(!( dcc->cf && dcc->cf->autobuild))
207 return NULL;
208 for(char **arg=cmd->data.list;*arg;arg++)
209 dcc->co.front()->auto_build_files.push_back(*arg);
210 dcc->co.front()->flags |= config_options::flag_auto_build_files;
211 return NULL;
212 }
213
214 static const configoption_t dc_options[] = {
215 { "RootSource", ARG_STR, dco_root_source, NULL, DCC_ROOT },
216 { "RootIntermediate", ARG_STR, dco_root_intermediate, NULL, DCC_ROOT },
217 { "RootSO", ARG_STR, dco_root_so, NULL, DCC_ROOT },
218 { "ListenSocket", ARG_STR, dco_listen_socket, NULL, DCC_ROOT },
219 { "RCFileName", ARG_STR, dco_rc_file_name, NULL, DCC_ROOT },
220 { "MinChildren", ARG_INT, dco_min_children, NULL, DCC_ROOT },
221 { "MaxChildren", ARG_INT, dco_max_children, NULL, DCC_ROOT },
222 { "MinSpareChildren", ARG_INT, dco_min_spare_children, NULL, DCC_ROOT },
223 { "MaxSpareChildren", ARG_INT, dco_max_spare_children, NULL, DCC_ROOT },
224 { "RequestsPerChild", ARG_INT, dco_requests_per_child, NULL, DCC_ROOT },
225 { "MultiProcess", ARG_TOGGLE, dco_multi_process, NULL, DCC_ROOT },
226 { "User", ARG_STR, dco_user, NULL, DCC_ROOT },
227 { "Group", ARG_STR, dco_group, NULL, DCC_ROOT },
228 { "Chroot", ARG_STR, dco_chroot, NULL, DCC_ROOT },
229 { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT },
230 { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT },
231 { "<Path", ARG_STR, dco_path, NULL, DCC_ROOT },
232 { "Skeleton", ARG_STR, dco_skeleton, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
233 { "CPPFLAGS", ARG_LIST, dco_cpp_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
234 { "LDFLAGS", ARG_LIST, dco_ld_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
235 { "Build", ARG_TOGGLE, dco_build, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
236 { "CPPDeps", ARG_TOGGLE, dco_cpp_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
237 { "ExceptionHandler", ARG_STR, dco_exception_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
238 { "HTTPStatusHandler", ARG_LIST, dco_http_status_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
239 { "IntermediateDeps", ARG_LIST, dco_intermediate_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
240 { "SODeps", ARG_LIST, dco_so_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
241 { "Action", ARG_LIST, dco_action, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
242 { "AutoBuildFiles", ARG_LIST, dco_auto_build_files, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
243 { "</Path>", ARG_NONE, dco__path, NULL, DCC_PATH },
244 LAST_OPTION
245 };
246
247 static const char *dc_context_checker(command_t *cmd,unsigned long mask) {
248 dc_context *dcc = (dc_context*)cmd->context;
249 if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) )
250 return NULL;
251 return "misplaced option";
252 }
253 static FUNC_ERRORHANDLER(dc_error_handler) {
254 throw konforka::exception(CODEPOINT,string("error parsing config file: ")+msg);
255 }
256
257 bool loaded_options::is_valid() {
258 struct stat nst;
259 if(stat(source_file.c_str(),&nst))
260 return false;
261 if(st.st_mtime!=nst.st_mtime)
262 return false;
263 return true;
264 }
265
266 loaded_options *configuration::lookup_loaded_options(const string& target) {
267 // we assume 'target' is a directory with trailing slash appended
268 string scrc = root_source+target;
269 if(flags&flag_rc_file_name)
270 scrc += rc_file_name;
271 else
272 scrc += ".scrc";
273 // TODO: normalize me, anyway.
274 if(access(scrc.c_str(),R_OK))
275 return 0; // TODO FIXME: this approach leaves already loaded .scrcs around in case of removal
276 loaded_specs_t::iterator i = loaded_specs.find(target);
277 if(i==loaded_specs.end() || !i->second.is_valid()) {
278 if(i!=loaded_specs.end())
279 loaded_specs.erase(i);
280 pair<loaded_specs_t::iterator,bool> ii = loaded_specs.insert(loaded_specs_t::value_type(target,loaded_options()));
281 assert(ii.first!=loaded_specs.end());
282 ii.first->second.parse(this,scrc);
283 i = ii.first;
284 }
285 assert(i!=loaded_specs.end());
286 return &(i->second);
287 }
288
289 config_options::action_handler_t *config_options::lookup_action_handler(const string& target) {
290 for(action_handlers_t::iterator i=action_handlers.begin();i!=action_handlers.end();++i) {
291 if(i->regex.search(target))
292 return &*i;
293 }
294 return NULL;
295 }
296
297 string config_options::lookup_http_status_handler(const string& status) {
298 http_status_handlers_t::const_iterator i = http_status_handlers.find(status);
299 string rv;
300 if(i!=http_status_handlers.end())
301 rv = i->second;
302 return rv;
303 }
304
305 string configuration::lookup_http_status_handler(const string& target,const string& status) {
306 string t = "/";
307 t += normalize_path(target,strip_leading_slash);
308 string rv;
309 for(;;) {
310 if(t[t.length()-1]=='/') {
311 loaded_options* lo = lookup_loaded_options(t);
312 if( lo && (lo->flags&config_options::flag_http_status_handlers) ) {
313 rv = lo->lookup_http_status_handler(status);
314 if(!rv.empty())
315 return rv;
316 }
317 }
318 specs_t::iterator i = specs.find(t);
319 if( i!=specs.end() && (i->second.flags&&config_options::flag_http_status_handlers) ) {
320 rv = i->second.lookup_http_status_handler(status);
321 if(!rv.empty())
322 return rv;
323 }
324 if(t.empty())
325 return rv;
326 string::size_type sl=t.rfind('/');
327 if(sl==string::npos) {
328 t.erase();
329 }else{
330 if(sl==(t.length()-1))
331 t.erase(sl);
332 else
333 t.erase(sl+1);
334 }
335 }
336 }
337
338 config_options::action_handler_t *configuration::lookup_action_handler(const string& target) {
339 string t = "/";
340 t += normalize_path(target,strip_leading_slash);
341 for(;;) {
342 if(t[t.length()-1]=='/') {
343 loaded_options* lo = lookup_loaded_options(t);
344 if( lo && (lo->flags&config_options::flag_action_handlers) ) {
345 config_options::action_handler_t *rv = lo->lookup_action_handler(target);
346 if(rv)
347 return rv;
348 }
349 }
350 specs_t::iterator i = specs.find(t);
351 if( i!=specs.end() && (i->second.flags&&config_options::flag_action_handlers) ) {
352 config_options::action_handler_t *rv = i->second.lookup_action_handler(target);
353 if(rv)
354 return rv;
355 }
356 if(t.empty())
357 return NULL;
358 string::size_type sl=t.rfind('/');
359 if(sl==string::npos) {
360 t.erase();
361 }else{
362 if(sl==(t.length()-1))
363 t.erase(sl);
364 else
365 t.erase(sl+1);
366 }
367 }
368 }
369
370 config_options* configuration::lookup_config(const string& target,int flag) {
371 string t = "/"; // always assume leading slash
372 t += normalize_path(target,strip_leading_slash);
373 // XXX: reconsider precedence
374 for(;;) {
375 if(t[t.length()-1]=='/') {
376 loaded_options* lo = lookup_loaded_options(t);
377 if( lo && (lo->flags&flag)==flag )
378 return lo;
379 }
380 specs_t::iterator i = specs.find(t);
381 if( i!=specs.end() && (i->second.flags&flag)==flag )
382 return &(i->second);
383 if(t.empty())
384 return NULL;
385 string::size_type sl=t.rfind('/');
386 if(sl==string::npos) {
387 t.erase();
388 }else{
389 if(sl==(t.length()-1))
390 t.erase(sl);
391 else
392 t.erase(sl+1);
393 }
394 }
395 }
396
397 bool config_options::match_autobuild_files(const char *fn,bool &rv) {
398 for(list<string>::reverse_iterator i=auto_build_files.rbegin();i!=auto_build_files.rend();++i) {
399 const char *pat = i->c_str();
400 bool plus = true;
401 if((*pat)=='+')
402 pat++;
403 else if((*pat)=='-') {
404 plus = false;
405 pat++;
406 }
407 if(!fnmatch(pat,fn,FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) {
408 rv = plus;
409 return true;
410 }
411 }
412 return false;
413 }
414
415 bool configuration::match_autobuild_files(const string& target,const char *fn) {
416 string t = "/";
417 t += normalize_path(target,strip_leading_slash|strip_trailing_slash);
418 t += "/";
419 bool rv = false;
420 for(;;) {
421 if(t[t.length()-1]=='/') {
422 loaded_options* lo = lookup_loaded_options(t);
423 if(lo && (lo->flags&config_options::flag_auto_build_files) && lo->match_autobuild_files(fn,rv) )
424 return rv;
425 }
426 specs_t::iterator i = specs.find(t);
427 if( i!=specs.end() && (i->second.flags&config_options::flag_auto_build_files) && i->second.match_autobuild_files(fn,rv) )
428 return rv;
429 if(t.empty())
430 return rv;
431 string::size_type sl=t.rfind('/');
432 if(sl==string::npos) {
433 t.erase();
434 }else{
435 if(sl==(t.length()-1))
436 t.erase(sl);
437 else
438 t.erase(sl+1);
439 }
440 }
441 }
442
443 void configuration::parse(const string& cfile) {
444 struct dc_context dcc;
445 dcc.cf = this;
446 dcc.ctx = DCC_ROOT;
447 dcc.co.push_front(&root_options());
448 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
449 if(!cf)
450 throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
451 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
452 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
453 if(!dotconf_command_loop(cf))
454 throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
455 dotconf_cleanup(cf);
456 }
457
458 void loaded_options::parse(configuration *config,const string& cfile) {
459 struct dc_context dcc;
460 dcc.cf = config;
461 dcc.ctx = DCC_SCRC;
462 dcc.co.push_front(this);
463 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
464 if(!cf)
465 throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
466 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
467 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
468 if(!dotconf_command_loop(cf))
469 throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
470 dotconf_cleanup(cf);
471 source_file = cfile;
472 stat(cfile.c_str(),&st); // TODO: handle errors?
473 }
474}