summaryrefslogtreecommitdiffabout
path: root/lib/configuration.cc
Side-by-side diff
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 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <unistd.h>
+ #include <fnmatch.h>
+ #include <cassert>
+ #include <stdexcept>
+ using namespace std;
+ #include <dotconf.h>
+ #include "sitecing/configuration.h"
+ #include "sitecing/sitecing_util.h"
+ #include "sitecing/scoreboard.h"
+#endif
+
+namespace sitecing {
+
+ configuration::configuration()
+ : flags(0), autobuild(false) { }
+ configuration::configuration(const string& cfile,bool ab)
+ : flags(0), autobuild(ab) {
+ parse(cfile);
+ }
+
+ enum dc_ctx {
+ DCC_ROOT = 1,
+ DCC_PATH = 2,
+ DCC_SCRC = 4
+ };
+ struct dc_context {
+ dc_ctx ctx;
+ configuration* cf;
+ list<config_options*> co;
+ };
+
+ static DOTCONF_CB(dco_root_source) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_source = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_source;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_root_intermediate) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_intermediate = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_intermediate;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_root_so) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_so = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_so;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_listen_socket) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->listen_socket = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_listen_socket;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_rc_file_name) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->rc_file_name = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_rc_file_name;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_min_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MinChildren is too big";
+ dcc->cf->min_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_min_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_max_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MaxChildren is too big";
+ dcc->cf->max_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_max_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_min_spare_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MinSpareChildren is too big";
+ dcc->cf->min_spare_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_min_spare_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_max_spare_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MaxSpareChildren is too big";
+ dcc->cf->max_spare_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_max_spare_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_requests_per_child) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->requests_per_child = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_requests_per_child;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_multi_process) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->multi_process = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_multi_process;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->user = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_user;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->group = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_group;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->chroot = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_chroot;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->pid_file = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_pid_file;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*) ctx;
+ dcc->cf->daemonize = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_daemonize;
+ return NULL;
+ }
+
+ static DOTCONF_CB(dco_path) { dc_context *dcc = (dc_context*)ctx;
+ string path = cmd->data.str;
+ if(path[path.length()-1]=='>')
+ path.erase(path.length()-1);
+ // TODO: normalize path
+ dcc->co.push_front(&(dcc->cf->specs[path]));
+ dcc->ctx = DCC_PATH; // TODO: stack it, instead
+ return NULL;
+ }
+ static DOTCONF_CB(dco__path) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.pop_front();
+ assert(dcc->co.size());
+ dcc->ctx = DCC_ROOT; // TODO: stack it, instead
+ return NULL;
+ }
+
+ static DOTCONF_CB(dco_skeleton) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->skeleton = cmd->data.str;
+ dcc->co.front()->flags |= config_options::flag_skeleton;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_cpp_flags) { dc_context *dcc = (dc_context*)ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->cpp_flags.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_cpp_flags;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_ld_flags) { dc_context *dcc = (dc_context*)ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->ld_flags.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_ld_flags;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_intermediate_deps) { dc_context *dcc = (dc_context*) ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->intermediate_deps.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_intermediate_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_so_deps) { dc_context *dcc = (dc_context*) ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->so_deps.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_so_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_build) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->build = cmd->data.value;
+ dcc->co.front()->flags |= config_options::flag_build;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_cpp_deps) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->cpp_deps = cmd->data.value;
+ dcc->co.front()->flags |= config_options::flag_cpp_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_exception_handler) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->exception_handler = cmd->data.str;
+ dcc->co.front()->flags |= config_options::flag_exception_handler;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_http_status_handler) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->arg_count!=2)
+ return "Invalid number of arguments";
+ dcc->co.front()->http_status_handlers[cmd->data.list[0]] = cmd->data.list[1];
+ dcc->co.front()->flags |= config_options::flag_http_status_handlers;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_action) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->arg_count<2)
+ return "Invalid number of arguments";
+ try {
+ char **arg=cmd->data.list;
+ dcc->co.front()->action_handlers.push_back(config_options::action_handler_t(arg[0],arg[1]));
+ for(arg+=2;*arg;arg++)
+ dcc->co.front()->action_handlers.back().args.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_action_handlers;
+ }catch(exception& e) {
+ return "Error processing Action directive"; // XXX: could be done better
+ }
+ return NULL;
+ }
+ static DOTCONF_CB(dco_auto_build_files) { dc_context *dcc = (dc_context*)ctx;
+ if(!( dcc->cf && dcc->cf->autobuild))
+ return NULL;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->auto_build_files.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_auto_build_files;
+ return NULL;
+ }
+
+ static const configoption_t dc_options[] = {
+ { "RootSource", ARG_STR, dco_root_source, NULL, DCC_ROOT },
+ { "RootIntermediate", ARG_STR, dco_root_intermediate, NULL, DCC_ROOT },
+ { "RootSO", ARG_STR, dco_root_so, NULL, DCC_ROOT },
+ { "ListenSocket", ARG_STR, dco_listen_socket, NULL, DCC_ROOT },
+ { "RCFileName", ARG_STR, dco_rc_file_name, NULL, DCC_ROOT },
+ { "MinChildren", ARG_INT, dco_min_children, NULL, DCC_ROOT },
+ { "MaxChildren", ARG_INT, dco_max_children, NULL, DCC_ROOT },
+ { "MinSpareChildren", ARG_INT, dco_min_spare_children, NULL, DCC_ROOT },
+ { "MaxSpareChildren", ARG_INT, dco_max_spare_children, NULL, DCC_ROOT },
+ { "RequestsPerChild", ARG_INT, dco_requests_per_child, NULL, DCC_ROOT },
+ { "MultiProcess", ARG_TOGGLE, dco_multi_process, NULL, DCC_ROOT },
+ { "User", ARG_STR, dco_user, NULL, DCC_ROOT },
+ { "Group", ARG_STR, dco_group, NULL, DCC_ROOT },
+ { "Chroot", ARG_STR, dco_chroot, NULL, DCC_ROOT },
+ { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT },
+ { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT },
+ { "<Path", ARG_STR, dco_path, NULL, DCC_ROOT },
+ { "Skeleton", ARG_STR, dco_skeleton, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "CPPFLAGS", ARG_LIST, dco_cpp_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "LDFLAGS", ARG_LIST, dco_ld_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "Build", ARG_TOGGLE, dco_build, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "CPPDeps", ARG_TOGGLE, dco_cpp_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "ExceptionHandler", ARG_STR, dco_exception_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "HTTPStatusHandler", ARG_LIST, dco_http_status_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "IntermediateDeps", ARG_LIST, dco_intermediate_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "SODeps", ARG_LIST, dco_so_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "Action", ARG_LIST, dco_action, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "AutoBuildFiles", ARG_LIST, dco_auto_build_files, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "</Path>", ARG_NONE, dco__path, NULL, DCC_PATH },
+ LAST_OPTION
+ };
+
+ static const char *dc_context_checker(command_t *cmd,unsigned long mask) {
+ dc_context *dcc = (dc_context*)cmd->context;
+ if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) )
+ return NULL;
+ return "misplaced option";
+ }
+ static FUNC_ERRORHANDLER(dc_error_handler) {
+ throw konforka::exception(CODEPOINT,string("error parsing config file: ")+msg);
+ }
+
+ bool loaded_options::is_valid() {
+ struct stat nst;
+ if(stat(source_file.c_str(),&nst))
+ return false;
+ if(st.st_mtime!=nst.st_mtime)
+ return false;
+ return true;
+ }
+
+ loaded_options *configuration::lookup_loaded_options(const string& target) {
+ // we assume 'target' is a directory with trailing slash appended
+ string scrc = root_source+target;
+ if(flags&flag_rc_file_name)
+ scrc += rc_file_name;
+ else
+ scrc += ".scrc";
+ // TODO: normalize me, anyway.
+ if(access(scrc.c_str(),R_OK))
+ return 0; // TODO FIXME: this approach leaves already loaded .scrcs around in case of removal
+ loaded_specs_t::iterator i = loaded_specs.find(target);
+ if(i==loaded_specs.end() || !i->second.is_valid()) {
+ if(i!=loaded_specs.end())
+ loaded_specs.erase(i);
+ pair<loaded_specs_t::iterator,bool> ii = loaded_specs.insert(loaded_specs_t::value_type(target,loaded_options()));
+ assert(ii.first!=loaded_specs.end());
+ ii.first->second.parse(this,scrc);
+ i = ii.first;
+ }
+ assert(i!=loaded_specs.end());
+ return &(i->second);
+ }
+
+ config_options::action_handler_t *config_options::lookup_action_handler(const string& target) {
+ for(action_handlers_t::iterator i=action_handlers.begin();i!=action_handlers.end();++i) {
+ if(i->regex.search(target))
+ return &*i;
+ }
+ return NULL;
+ }
+
+ string config_options::lookup_http_status_handler(const string& status) {
+ http_status_handlers_t::const_iterator i = http_status_handlers.find(status);
+ string rv;
+ if(i!=http_status_handlers.end())
+ rv = i->second;
+ return rv;
+ }
+
+ string configuration::lookup_http_status_handler(const string& target,const string& status) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash);
+ string rv;
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&config_options::flag_http_status_handlers) ) {
+ rv = lo->lookup_http_status_handler(status);
+ if(!rv.empty())
+ return rv;
+ }
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&&config_options::flag_http_status_handlers) ) {
+ rv = i->second.lookup_http_status_handler(status);
+ if(!rv.empty())
+ return rv;
+ }
+ if(t.empty())
+ return rv;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ config_options::action_handler_t *configuration::lookup_action_handler(const string& target) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash);
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&config_options::flag_action_handlers) ) {
+ config_options::action_handler_t *rv = lo->lookup_action_handler(target);
+ if(rv)
+ return rv;
+ }
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&&config_options::flag_action_handlers) ) {
+ config_options::action_handler_t *rv = i->second.lookup_action_handler(target);
+ if(rv)
+ return rv;
+ }
+ if(t.empty())
+ return NULL;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ config_options* configuration::lookup_config(const string& target,int flag) {
+ string t = "/"; // always assume leading slash
+ t += normalize_path(target,strip_leading_slash);
+ // XXX: reconsider precedence
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&flag)==flag )
+ return lo;
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&flag)==flag )
+ return &(i->second);
+ if(t.empty())
+ return NULL;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ bool config_options::match_autobuild_files(const char *fn,bool &rv) {
+ for(list<string>::reverse_iterator i=auto_build_files.rbegin();i!=auto_build_files.rend();++i) {
+ const char *pat = i->c_str();
+ bool plus = true;
+ if((*pat)=='+')
+ pat++;
+ else if((*pat)=='-') {
+ plus = false;
+ pat++;
+ }
+ if(!fnmatch(pat,fn,FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) {
+ rv = plus;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool configuration::match_autobuild_files(const string& target,const char *fn) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash|strip_trailing_slash);
+ t += "/";
+ bool rv = false;
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if(lo && (lo->flags&config_options::flag_auto_build_files) && lo->match_autobuild_files(fn,rv) )
+ return rv;
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&config_options::flag_auto_build_files) && i->second.match_autobuild_files(fn,rv) )
+ return rv;
+ if(t.empty())
+ return rv;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ void configuration::parse(const string& cfile) {
+ struct dc_context dcc;
+ dcc.cf = this;
+ dcc.ctx = DCC_ROOT;
+ dcc.co.push_front(&root_options());
+ configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
+ if(!cf)
+ throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
+ cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
+ cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
+ if(!dotconf_command_loop(cf))
+ throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
+ dotconf_cleanup(cf);
+ }
+
+ void loaded_options::parse(configuration *config,const string& cfile) {
+ struct dc_context dcc;
+ dcc.cf = config;
+ dcc.ctx = DCC_SCRC;
+ dcc.co.push_front(this);
+ configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
+ if(!cf)
+ throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
+ cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
+ cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
+ if(!dotconf_command_loop(cf))
+ throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
+ dotconf_cleanup(cf);
+ source_file = cfile;
+ stat(cfile.c_str(),&st); // TODO: handle errors?
+ }
+}