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.cc9
1 files changed, 5 insertions, 4 deletions
diff --git a/lib/configuration.cc b/lib/configuration.cc
index 4ee1526..6b21690 100644
--- a/lib/configuration.cc
+++ b/lib/configuration.cc
@@ -1,474 +1,475 @@
#ifdef USE_PCH
#include "pch.h"
#else
#include <unistd.h>
#include <fnmatch.h>
#include <cassert>
#include <stdexcept>
using namespace std;
+ #include <konforka/util.h>
#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);
+ t += konforka::normalize_path(target,konforka::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);
+ t += konforka::normalize_path(target,konforka::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);
+ t += konforka::normalize_path(target,konforka::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 += konforka::normalize_path(target,konforka::strip_leading_slash|konforka::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?
}
}