#ifdef USE_PCH #include "pch.h" #else #include #include #include #include using namespace std; #include #include #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 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 }, { "", 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 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 += 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 += 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 += 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::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 += 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? } }