/
zOMGdev
/
cppcms
Обзор
Документация
Войти
/
zOMGdev
/
cppcms
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.1.0
src/service.cpp
1 037 строк
24 KB
Artyom Beilis
Closing feature request #37 add facility option
28 фев 2017, 23:43
28 фев 2017, 23:43
b45a957
Код
Авторство
О чём код?
/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include <cppcms/defs.h> #ifdef CPPCMS_WIN32 #define NOMINMAX #include <winsock2.h> #include <windows.h> #include <winsvc.h> #include <process.h> #else #include <errno.h> #endif #ifndef CPPCMS_WIN32 #if defined(__sun) #define _POSIX_PTHREAD_SEMANTICS #endif #include <signal.h> #endif #include <cppcms/service.h> #include "service_impl.h" #include <cppcms/applications_pool.h> #include <cppcms/application.h> #include <cppcms/thread_pool.h> #include <cppcms/cppcms_error.h> #include <cppcms/mount_point.h> #include <cppcms/forwarder.h> #include <cppcms/mem_bind.h> #include <cppcms/plugin.h> #include "cgi_acceptor.h" #ifndef CPPCMS_WIN32 #include "prefork_acceptor.h" #endif #include "cgi_api.h" #ifdef CPPCMS_HAS_SCGI # include "scgi_api.h" #endif #ifdef CPPCMS_HAS_HTTP # include "http_api.h" #endif #ifdef CPPCMS_HAS_FCGI # include "fastcgi_api.h" #endif #ifdef CPPCMS_WIN_NATIVE #include "winservice.h" #endif #include "cached_settings.h" #include "logging.h" #include <cppcms/cache_pool.h> #include "internal_file_server.h" #include <cppcms/json.h> #include <cppcms/localization.h> #include <cppcms/views_pool.h> #include <cppcms/session_pool.h> #include <booster/log.h> #ifdef CPPCMS_POSIX #include <sys/wait.h> #include <syslog.h> #endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <booster/nowide/fstream.h> #include <booster/thread.h> #include <cppcms/config.h> #ifndef CPPCMS_WIN32 #include "daemonize.h" #endif #include <booster/regex.h> #include <booster/aio/io_service.h> #include <booster/aio/socket.h> #include <booster/aio/buffer.h> #include <booster/aio/reactor.h> namespace cppcms { service::service(json::value const &v) : impl_(new impl::service()) { impl_->settings_.reset(new json::value(v)); setup(); } service::service(int argc,char *argv[]) : impl_(new impl::service()) { impl_->args_.assign(argv,argv+argc); json::value val = load_settings(argc,argv); impl_->settings_.reset(new json::value()); impl_->settings_->swap(val); setup(); } json::value service::load_settings(int argc,char *argv[]) { json::value val; using std::string; std::string file_name; for(int i=1;i<argc;i++) { if(argv[i]==std::string("-c")) { if(!file_name.empty()) { throw cppcms_error("Switch -c can't be used twice"); } if(i+1 < argc) file_name=argv[i+1]; else throw cppcms_error("Switch -c requires configuration file parameters"); i++; } else if(argv[i]==std::string("-U")) break; } if(file_name.empty()) { char const *e = getenv("CPPCMS_CONFIG"); if(e) file_name = e; } if(!file_name.empty()) { booster::nowide::ifstream fin(file_name.c_str()); if(!fin) throw cppcms_error("Failed to open filename:"+file_name); int line_no=0; if(!val.load(fin,true,&line_no)) { std::ostringstream ss; ss<<"Error reading configuration file "<<file_name<<" in line:"<<line_no; throw cppcms_error(ss.str()); } } booster::regex r("^--((\\w+)(-\\w+)*)=((true)|(false)|(null)|(-?\\d+(\\.\\d+)?([eE][\\+-]?\\d+)?)|([\\[\\{].*[\\]\\}])|(.*))$"); for(int i=1;i<argc;i++) { std::string arg=argv[i]; if(arg=="-c") { i++; continue; } if(arg=="-U") break; if(arg.substr(0,2)=="--") { booster::cmatch m; if(!booster::regex_match(arg.c_str(),m,r)) throw cppcms_error("Invalid switch: "+arg); std::string path=m[1]; for(unsigned i=0;i<path.size();i++) if(path[i]=='-') path[i]='.'; if(!m[5].str().empty()) val.set(path,true); else if(!m[6].str().empty()) val.set(path,false); else if(!m[7].str().empty()) val.set(path,json::null()); else if(!m[8].str().empty()) val.set(path,atof(m[8].str().c_str())); else if(!m[11].str().empty()) { std::istringstream ss(m[11].str()); json::value tmp; if(!tmp.load(ss,true)) throw cppcms_error("Invadid json expresson: " + m[11].str()); val.set(path,tmp); } else val.set(path,m[4].str()); } } if(val.is_undefined()) { throw cppcms_error("No configuration defined"); } return val; } namespace { int reactor_type(std::string const &name) { using booster::aio::reactor; if(name=="select") return reactor::use_select; if(name=="poll") return reactor::use_poll; if(name=="epoll") return reactor::use_epoll; if(name=="devpoll") return reactor::use_dev_poll; if(name=="kqueue") return reactor::use_kqueue; return reactor::use_default; } } impl::cached_settings const &service::cached_settings() { return *impl_->cached_settings_; } void service::setup() { impl_->cached_settings_.reset(new impl::cached_settings(settings())); impl::setup_logging(settings()); impl_->id_=0; int reactor=reactor_type(settings().get("service.reactor","default")); impl_->io_service_.reset(new io::io_service(reactor)); impl_->sig_.reset(new io::stream_socket(*impl_->io_service_)); impl_->breaker_.reset(new io::stream_socket(*impl_->io_service_)); impl_->plugins_.reset(new cppcms::plugin::scope(settings())); impl_->applications_pool_.reset(new cppcms::applications_pool(*this,0)); impl_->views_pool_.reset(new cppcms::views::manager(settings())); impl_->cache_pool_.reset(new cppcms::cache_pool(settings())); impl_->session_pool_.reset(new cppcms::session_pool(*this)); if(settings().get("file_server.enable",false)) { int flags = app::synchronous; if(settings().get("file_server.async",false)) { flags = app::asynchronous; } applications_pool().mount(create_pool<cppcms::impl::file_server>(),mount_point(""),flags); } } void impl::setup_logging(json::value const &settings) { using namespace booster::log; level_type level = logger::string_to_level(settings.get("logging.level","error")); logger::instance().set_default_level(level); if( ( settings.find("logging.file").is_undefined() && settings.find("logging.syslog").is_undefined() && settings.find("logging.stderr").is_undefined() ) || settings.get("logging.stderr",false)==true ) { logger::instance().add_sink(booster::shared_ptr<sink>(new sinks::standard_error())); } if(settings.get("logging.syslog.enable",false)==true) { #ifndef CPPCMS_POSIX throw cppcms_error("Syslog is not availible on Windows"); #else std::string id = settings.get("logging.syslog.id",""); std::string facility = settings.get("logging.syslog.facility","user"); std::vector<std::string> vops = settings.get("logging.syslog.options",std::vector<std::string>()); std::string sfacility = settings.get("logging.syslog.options",""); int facility_code = 0; int ops = 0; for(unsigned i=0;i<vops.size();i++) { std::string const &op=vops[i]; if(op=="LOG_CONS") ops|=LOG_CONS; else if(op=="LOG_NDELAY") ops|=LOG_NDELAY; else if(op=="LOG_NOWAIT") ops|=LOG_NOWAIT; else if(op=="LOG_ODELAY") ops|=LOG_ODELAY; #ifdef LOG_PERROR else if(op=="LOG_PERROR") ops|=LOG_PERROR; #endif else if(op=="LOG_PID") ops|=LOG_PID; } if(!facility.empty()) { if(facility == "user") { facility_code = LOG_USER; } else if(facility == "local0") { facility_code = LOG_LOCAL0; } else if(facility == "local1") { facility_code = LOG_LOCAL1; } else if(facility == "local2") { facility_code = LOG_LOCAL2; } else if(facility == "local3") { facility_code = LOG_LOCAL3; } else if(facility == "local4") { facility_code = LOG_LOCAL4; } else if(facility == "local5") { facility_code = LOG_LOCAL5; } else if(facility == "local6") { facility_code = LOG_LOCAL6; } else if(facility == "local7") { facility_code = LOG_LOCAL7; } else { throw cppcms_error("Unsupported syslog facility: " + facility); } } if(id.empty()) logger::instance().add_sink(booster::shared_ptr<sink>(new sinks::syslog(ops,facility_code))); else logger::instance().add_sink(booster::shared_ptr<sink>(new sinks::syslog(id,ops,facility_code))); #endif } std::string log_file; if(!(log_file=settings.get("logging.file.name","")).empty()) { booster::shared_ptr<sinks::file> file(new sinks::file()); int max_files=0; if((max_files = settings.get("logging.file.max_files",0)) > 0) file->max_files(max_files); bool append = false; if((append = settings.get("logging.file.append",false))==true) file->append(); file->open(log_file); std::string tz = settings.get("logging.file.timezone",""); file->set_timezone(tz); logger::instance().add_sink(file); } } cppcms::session_pool &service::session_pool() { return *impl_->session_pool_; } cppcms::cache_pool &service::cache_pool() { return *impl_->cache_pool_; } cppcms::views::manager &service::views_pool() { return *impl_->views_pool_; } service::~service() { } int service::threads_no() { return cached_settings().service.worker_threads; } namespace { cppcms::service *the_service; #if defined(CPPCMS_WIN32) BOOL WINAPI handler(DWORD ctrl_type) { switch (ctrl_type) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT: if(the_service) the_service->shutdown(); return TRUE; default: return FALSE; } } class apache_event_handler { public: apache_event_handler(HANDLE h=0) : handle_(h) { } void operator()() const { if( ::WaitForSingleObject(handle_,INFINITE) == WAIT_OBJECT_0 ) { if(the_service) { the_service->shutdown(); } } } private: HANDLE handle_; }; #else void handler(int /*nothing*/) { if(the_service) the_service->shutdown(); } #endif } // anon namespace { struct stop_binder { stop_binder(void (cppcms::service::*member)(),cppcms::service *s) : m(member),srv(s) {} void (cppcms::service::*m)(); cppcms::service *srv; void operator()(booster::system::error_code const &,size_t) { (srv->*m)(); } }; } void service::setup_exit_handling() { io::socket_pair(*impl_->sig_,*impl_->breaker_); static char c; impl_->breaker_->async_read_some(io::buffer(&c,1),stop_binder(&service::stop,this)); impl_->notification_socket_=impl_->sig_->native(); if(settings().get("service.disable_global_exit_handling",false)) return; the_service=this; #ifdef CPPCMS_WIN32 SetConsoleCtrlHandler(handler, TRUE); char *event = getenv("_FCGI_SHUTDOWN_EVENT_"); if(event) { apache_event_handler handler((HANDLE)(atoi(event))); booster::thread wait_for_event(handler); } #else struct sigaction sa; memset(&sa,0,sizeof(sa)); sa.sa_handler=handler; sigaction(SIGINT,&sa,0); sigaction(SIGTERM,&sa,0); sigaction(SIGUSR1,&sa,0); #endif } void service::shutdown() { char c='A'; #ifdef CPPCMS_WIN32 if(send(impl_->notification_socket_,&c,1,0) <= 0) { perror("notification failed"); exit(1); } #else for(;;){ int res=::write(impl_->notification_socket_,&c,1); if(res<0 && errno == EINTR) continue; if(res<=0) { perror("shudown notification failed"); exit(1); } return; } #endif } void service::after_fork(booster::function<void()> const &cb) { impl_->on_fork_.push_back(cb); } void service::after_fork_exec() { for(unsigned i=0;i<impl_->on_fork_.size();i++) impl_->on_fork_[i](); impl_->on_fork_.clear(); } cppcms::forwarder &service::forwarder() { if(!impl_->forwarder_.get()) { impl_->forwarder_.reset(new cppcms::forwarder()); if(settings().find("forwarding.rules").type()==json::is_array) { json::array rules=settings().at("forwarding.rules").array(); for(unsigned i=0;i<rules.size();i++) { mount_point mp; if(rules[i].find("host").type()==json::is_string) mp.host(booster::regex(rules[i].get<std::string>("host"))); if(rules[i].find("script_name").type()==json::is_string) mp.script_name(booster::regex(rules[i].get<std::string>("script_name"))); if(rules[i].find("path_info").type()==json::is_string) mp.path_info(booster::regex(rules[i].get<std::string>("path_info"))); std::string ip=rules[i].get<std::string>("ip"); int port=rules[i].get<int>("port"); impl_->forwarder_->add_forwarding_rule(booster::shared_ptr<mount_point>(new mount_point(mp)),ip,port); } } } return *impl_->forwarder_; } void service::run_prepare() { generator(); forwarder(); session_pool().init(); start_acceptor(); } void service::run_acceptor() { for(unsigned i=0;i<impl_->acceptors_.size();i++) impl_->acceptors_[i]->async_accept(); } void service::run_event_loop() { impl_->get_io_service().run(); } #ifdef CPPCMS_WIN32 void service::run_win_console() { run_prepare(); prefork(); // not really prefork after_fork_exec(); thread_pool(); run_acceptor(); setup_exit_handling(); run_event_loop(); } #endif #ifdef CPPCMS_WIN_NATIVE void service::win_service_prepare() { run_prepare(); prefork(); // not really prefork after_fork_exec(); thread_pool(); run_acceptor(); setup_exit_handling(); } void service::win_service_exec() { run_event_loop(); } void service::run_win_service() { using cppcms::util::mem_bind; impl::winservice::instance().prepare(mem_bind(&service::win_service_prepare,this)); impl::winservice::instance().exec(mem_bind(&service::win_service_exec,this)); impl::winservice::instance().stop(mem_bind(&service::shutdown,this)); int argc=impl_->args_.size(); std::vector<char*> argv(argc+1,static_cast<char*>(0)); for(int i=0;i<argc;i++) { argv[i]=const_cast<char *>(impl_->args_[i].c_str()); } impl::winservice::instance().run(*impl_->settings_,argc,&argv[0]); } #endif #ifdef CPPCMS_WIN32 void service::run() { #ifdef CPPCMS_WIN_NATIVE if(settings().get("winservice.mode","console") == "console") run_win_console(); else run_win_service(); #else run_win_console(); #endif } #else // POSIX void service::run() { run_prepare(); impl::daemonizer godaemon(settings()); if(prefork()) { return; } thread_pool(); // make sure we start it if(impl_->prefork_acceptor_.get()) impl_->prefork_acceptor_->start(); after_fork_exec(); run_acceptor(); setup_exit_handling(); run_event_loop(); } #endif int service::procs_no() { int procs=cached_settings().service.worker_processes; if(procs < 0) procs = 0; #ifdef CPPCMS_WIN32 if(procs > 0) throw cppcms_error("Prefork is not supported under Windows and Cygwin"); #endif return procs; } #ifdef CPPCMS_WIN32 bool service::prefork() { procs_no(); impl_->id_ = 1; return false; } #else // UNIX static void dummy(int /*n*/) {} bool service::prefork() { sigset_t pipemask; sigemptyset(&pipemask); sigaddset(&pipemask,SIGPIPE); sigprocmask(SIG_BLOCK,&pipemask,0); int procs=procs_no(); if(procs==0) { impl_->id_ = 1; return false; } std::vector<int> pids(procs,0); for(int i=0;i<procs;i++) { int pid=::fork(); if(pid < 0) { int err=errno; for(int j=0;j<i;j++) { ::kill(pids[j],SIGTERM); } for(int j=0;j<i;j++) { int stat; ::waitpid(pids[j],&stat,0); } throw cppcms_error(err,"fork failed"); } else if(pid==0) { impl_->id_ = i+1; return false; // chaild } else { pids[i]=pid; } } sigset_t set,prev; sigemptyset(&set); sigaddset(&set,SIGTERM); sigaddset(&set,SIGINT); sigaddset(&set,SIGCHLD); sigaddset(&set,SIGQUIT); sigprocmask(SIG_BLOCK,&set,&prev); // Enable delivery of SIGCHLD struct sigaction sa_new,sa_old; memset(&sa_new,0,sizeof(sa_new)); sa_new.sa_handler=dummy; ::sigaction(SIGCHLD,&sa_new,&sa_old); int sig; do { sig=0; sigwait(&set,&sig); BOOSTER_INFO("cppcms") << "Catched signal " << sig; if(sig==SIGCHLD) { int status; int pid = ::waitpid(0,&status,WNOHANG); if(pid > 0) { std::vector<int>::iterator p; p = std::find(pids.begin(),pids.end(),pid); // Ingnore all processes that are not my own childrens if(p!=pids.end()) { // TODO: Make better error handling if(!WIFEXITED(status) || WEXITSTATUS(status)!=0){ if(WIFEXITED(status)) { BOOSTER_CRITICAL("cppcms") <<"Chaild exited with "<<WEXITSTATUS(status); } else if(WIFSIGNALED(status)) { BOOSTER_CRITICAL("cppcms") <<"Chaild killed by "<<WTERMSIG(status); } else { BOOSTER_CRITICAL("cppcms") <<"Chaild exited for unknown reason"; } impl_->id_ = p - pids.begin() + 1; *p=-1; pid = ::fork(); if(pid < 0) { int err=errno; BOOSTER_ALERT("cppcms") <<"Failed to create process: " <<strerror(err); } else if(pid == 0) { ::sigaction(SIGCHLD,&sa_old,NULL); sigprocmask(SIG_SETMASK,&prev,NULL); return false; } else { *p=pid; impl_->id_ = 0; } } else { *p=-1; } } } } }while(sig!=SIGINT && sig!=SIGTERM && sig!=SIGQUIT); sigprocmask(SIG_SETMASK,&prev,NULL); BOOSTER_INFO("cppcms") << "Shutting down"; BOOSTER_DEBUG("cppcms") << "Killing Children"; for(int i=0;i<procs;i++) { if(pids[i]<0) continue; ::kill(pids[i],SIGTERM); } for(int i=0;i<procs;i++) { if(pids[i]<0) continue; int stat; ::waitpid(pids[i],&stat,0); } BOOSTER_DEBUG("cppcms") << "Children are dead"; return true; } #endif int service::process_id() { return impl_->id_; } std::auto_ptr<cppcms::impl::cgi::acceptor> service::setup_acceptor(json::value const &v,int backlog,int port_shift) { using namespace cppcms::impl::cgi; std::string api=v.get<std::string>("api"); std::string socket=v.get("socket",""); std::string ip; int port=0; bool tcp; std::auto_ptr<acceptor> a; if(socket.empty()) { ip=v.get("ip","127.0.0.1"); port=v.get("port",8080) + port_shift; tcp=true; } else { if( !v.find("port").is_undefined() || !v.find("ip").is_undefined()) { throw cppcms_error("Can't define both UNIX socket and TCP port/ip"); } tcp=false; } if(tcp) { #ifdef CPPCMS_HAS_SCGI if(api=="scgi") a = scgi_api_tcp_socket_factory(*this,ip,port,backlog); #endif #ifdef CPPCMS_HAS_FCGI if(api=="fastcgi") a = fastcgi_api_tcp_socket_factory(*this,ip,port,backlog); #endif #ifdef CPPCMS_HAS_HTTP if(api=="http") a = http_api_factory(*this,ip,port,backlog); #endif } else { #ifdef CPPCMS_WIN_NATIVE throw cppcms_error("Unix domain sockets are not supported under Windows... (isn't it obvious?)"); #elif defined CPPCMS_CYGWIN throw cppcms_error("CppCMS uses native Win32 sockets under cygwin, so Unix sockets are not supported"); #else #ifdef CPPCMS_HAS_SCGI if(api=="scgi") { if(socket=="stdin") a = scgi_api_unix_socket_factory(*this,backlog); else a = scgi_api_unix_socket_factory(*this,socket,backlog); } #endif #ifdef CPPCMS_HAS_FCGI if(api=="fastcgi") { if(socket=="stdin") a = fastcgi_api_unix_socket_factory(*this,backlog); else a = fastcgi_api_unix_socket_factory(*this,socket,backlog); } #endif #ifdef CPPCMS_HAS_HTTP if(api=="http") throw cppcms_error("HTTP API is not supported over Unix Domain sockets"); #endif #endif } if(!a.get()) throw cppcms_error("Unknown api: " + api); return a; } void service::start_acceptor(bool after_fork) { using namespace impl::cgi; int backlog=settings().get("service.backlog",threads_no() * std::max(procs_no(),1) * 2); bool preforking = procs_no() > 1 && !after_fork; if(preforking) #ifndef CPPCMS_WIN32 impl_->prefork_acceptor_.reset(new impl::prefork_acceptor(this)); #else throw cppcms_error("Preforking is not supported under Windows"); #endif if( settings().find("service.list").type()!=json::is_undefined && settings().find("service.api").type()!=json::is_undefined) { throw cppcms_error("Can't specify both service.api and service.list"); } if(settings().find("service.api").type()!=json::is_undefined) { booster::shared_ptr<acceptor> ac(setup_acceptor(settings()["service"],backlog)); #ifndef CPPCMS_WIN32 if(preforking) { impl_->prefork_acceptor_->add_acceptor(ac); } else #endif { impl_->acceptors_.push_back(ac); } } if(settings().find("service.list").type()!=json::is_undefined) { json::array list=settings()["service"]["list"].array(); if(list.empty()) throw cppcms_error("At least one service should be provided in service.list"); for(unsigned i=0;i<list.size();i++) { booster::shared_ptr<acceptor> ac(setup_acceptor(list[i],backlog)); #ifndef CPPCMS_WIN32 if(preforking) { impl_->prefork_acceptor_->add_acceptor(ac); } else #endif { impl_->acceptors_.push_back(ac); } } } } cppcms::applications_pool &service::applications_pool() { return *impl_->applications_pool_; } cppcms::thread_pool &service::thread_pool() { if(!impl_->thread_pool_.get()) { impl_->thread_pool_.reset(new cppcms::thread_pool(threads_no())); } return *impl_->thread_pool_; } json::value const &service::settings() { return *impl_->settings_; } cppcms::impl::service &service::impl() { return *impl_; } void service::post(booster::function<void()> const &handler) { impl_->get_io_service().post(handler); } void service::stop() { for(unsigned i=0;i<impl_->acceptors_.size();i++) { if(impl_->acceptors_[i]) impl_->acceptors_[i]->stop(); } #ifndef CPPCMS_WIN32 impl_->prefork_acceptor_.reset(); #endif thread_pool().stop(); impl_->get_io_service().stop(); } locale::generator const &service::generator() { if(impl_->locale_generator_.get()) return *impl_->locale_generator_.get(); typedef std::vector<std::string> vstr_type; std::string default_backend = settings().get("localization.backend",""); if(default_backend.empty()) impl_->locale_generator_.reset(new locale::generator()); else { locale::localization_backend_manager mgr = locale::localization_backend_manager::global(); mgr.select(default_backend); impl_->locale_generator_.reset(new locale::generator(mgr)); } locale::generator &gen= *impl_->locale_generator_; gen.characters(locale::char_facet); std::string enc = settings().get("localization.encoding",""); vstr_type paths= settings().get("localization.messages.paths",vstr_type()); vstr_type domains = settings().get("localization.messages.domains",vstr_type()); if(!paths.empty() && !domains.empty()) { unsigned i; for(i=0;i<paths.size();i++) { gen.add_messages_path(paths[i]); } for(i=0;i<domains.size();i++) gen.add_messages_domain(domains[i]); } vstr_type locales = settings().get("localization.locales",vstr_type()); gen.locale_cache_enabled(true); if(locales.empty()) { gen(""); impl_->default_locale_=gen(""); if(std::use_facet<locale::info>(impl_->default_locale_).name()=="C") BOOSTER_WARNING("cppcms") << "The default system locale is 'C', the encoding is set to US-ASCII. " << "It is recommended to specify the locale name explicitly"; } else { for(unsigned i=0;i<locales.size();i++) { std::locale tmp = gen(locales[i]); locale::info const &inf = std::use_facet<locale::info>(tmp); if(std::use_facet<locale::info>(tmp).name()=="C" || inf.encoding()=="us-ascii") { if(locales[i].empty()) { BOOSTER_WARNING("cppcms") << "The default system locale is 'C', the encoding is set to US-ASCII. " << "It is recommended to specify the locale name explicitly"; } else if(locales[i].find('.')==std::string::npos) { BOOSTER_WARNING("cppcms") << "The encoding for locale `" << locales[i] << "' is not specified " << "the encoding is set to US-ASCII. It is recommended to specify the locale name explicitly"; } } } impl_->default_locale_=gen(locales[0]); } return *impl_->locale_generator_.get(); } std::locale service::locale() { generator(); return impl_->default_locale_; } std::locale service::locale(std::string const &name) { return generator().generate(name); } booster::aio::io_service &service::get_io_service() { return impl_->get_io_service(); } namespace impl { service::service() { } service::~service() { acceptors_.clear(); thread_pool_.reset(); sig_.reset(); breaker_.reset(); io_service_.reset(); // applications pool should be destroyed after // io_service, because soma apps may try unregister themselfs applications_pool_.reset(); locale_generator_.reset(); settings_.reset(); } } // impl plugin::scope &service::plugins() { return *impl_->plugins_; } } // cppcms