ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1004 lines
23 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #define CPPCMS_SOURCE
  9. #include <cppcms/defs.h>
  10. #ifdef CPPCMS_WIN32
  11. #define NOMINMAX
  12. #include <winsock2.h>
  13. #include <windows.h>
  14. #include <winsvc.h>
  15. #include <process.h>
  16. #else
  17. #include <errno.h>
  18. #endif
  19. #ifndef CPPCMS_WIN32
  20. #if defined(__sun)
  21. #define _POSIX_PTHREAD_SEMANTICS
  22. #endif
  23. #include <signal.h>
  24. #endif
  25. #include <cppcms/service.h>
  26. #include "service_impl.h"
  27. #include <cppcms/applications_pool.h>
  28. #include <cppcms/application.h>
  29. #include <cppcms/thread_pool.h>
  30. #include <cppcms/cppcms_error.h>
  31. #include <cppcms/mount_point.h>
  32. #include <cppcms/forwarder.h>
  33. #include "cgi_acceptor.h"
  34. #ifndef CPPCMS_WIN32
  35. #include "prefork_acceptor.h"
  36. #endif
  37. #include "cgi_api.h"
  38. #ifdef CPPCMS_HAS_SCGI
  39. # include "scgi_api.h"
  40. #endif
  41. #ifdef CPPCMS_HAS_HTTP
  42. # include "http_api.h"
  43. #endif
  44. #ifdef CPPCMS_HAS_FCGI
  45. # include "fastcgi_api.h"
  46. #endif
  47. #ifdef CPPCMS_WIN_NATIVE
  48. #include "winservice.h"
  49. #endif
  50. #include "cached_settings.h"
  51. #include "logging.h"
  52. #include <cppcms/cache_pool.h>
  53. #include "internal_file_server.h"
  54. #include <cppcms/json.h>
  55. #include <cppcms/localization.h>
  56. #include <cppcms/views_pool.h>
  57. #include <cppcms/session_pool.h>
  58. #include <booster/log.h>
  59. #ifdef CPPCMS_POSIX
  60. #include <sys/wait.h>
  61. #include <syslog.h>
  62. #endif
  63. #include <stdio.h>
  64. #include <string.h>
  65. #include <stdlib.h>
  66. #include <iostream>
  67. #include <booster/nowide/fstream.h>
  68. #include <booster/thread.h>
  69. #include <cppcms/config.h>
  70. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  71. # include <boost/bind.hpp>
  72. #else // Internal Boost
  73. # include <cppcms_boost/bind.hpp>
  74. namespace boost = cppcms_boost;
  75. #endif
  76. #ifndef CPPCMS_WIN32
  77. #include "daemonize.h"
  78. #endif
  79. #include <booster/regex.h>
  80. #include <booster/aio/io_service.h>
  81. #include <booster/aio/socket.h>
  82. #include <booster/aio/buffer.h>
  83. #include <booster/aio/reactor.h>
  84. namespace cppcms {
  85. service::service(json::value const &v) :
  86. impl_(new impl::service())
  87. {
  88. impl_->settings_.reset(new json::value(v));
  89. setup();
  90. }
  91. service::service(int argc,char *argv[]) :
  92. impl_(new impl::service())
  93. {
  94. impl_->args_.assign(argv,argv+argc);
  95. json::value val = load_settings(argc,argv);
  96. impl_->settings_.reset(new json::value());
  97. impl_->settings_->swap(val);
  98. setup();
  99. }
  100. json::value service::load_settings(int argc,char *argv[])
  101. {
  102. json::value val;
  103. using std::string;
  104. std::string file_name;
  105. for(int i=1;i<argc;i++) {
  106. if(argv[i]==std::string("-c")) {
  107. if(!file_name.empty()) {
  108. throw cppcms_error("Switch -c can't be used twice");
  109. }
  110. if(i+1 < argc)
  111. file_name=argv[i+1];
  112. else
  113. throw cppcms_error("Switch -c requires configuration file parameters");
  114. i++;
  115. }
  116. else if(argv[i]==std::string("-U"))
  117. break;
  118. }
  119. if(file_name.empty()) {
  120. char const *e = getenv("CPPCMS_CONFIG");
  121. if(e) file_name = e;
  122. }
  123. if(!file_name.empty()) {
  124. booster::nowide::ifstream fin(file_name.c_str());
  125. if(!fin)
  126. throw cppcms_error("Failed to open filename:"+file_name);
  127. int line_no=0;
  128. if(!val.load(fin,true,&line_no)) {
  129. std::ostringstream ss;
  130. ss<<"Error reading configuration file "<<file_name<<" in line:"<<line_no;
  131. throw cppcms_error(ss.str());
  132. }
  133. }
  134. booster::regex r("^--((\\w+)(-\\w+)*)=((true)|(false)|(null)|(-?\\d+(\\.\\d+)?([eE][\\+-]?\\d+)?)|([\\[\\{].*[\\]\\}])|(.*))$");
  135. for(int i=1;i<argc;i++) {
  136. std::string arg=argv[i];
  137. if(arg=="-c") {
  138. i++;
  139. continue;
  140. }
  141. if(arg=="-U")
  142. break;
  143. if(arg.substr(0,2)=="--") {
  144. booster::cmatch m;
  145. if(!booster::regex_match(arg.c_str(),m,r))
  146. throw cppcms_error("Invalid switch: "+arg);
  147. std::string path=m[1];
  148. for(unsigned i=0;i<path.size();i++)
  149. if(path[i]=='-')
  150. path[i]='.';
  151. if(!m[5].str().empty())
  152. val.set(path,true);
  153. else if(!m[6].str().empty())
  154. val.set(path,false);
  155. else if(!m[7].str().empty())
  156. val.set(path,json::null());
  157. else if(!m[8].str().empty())
  158. val.set(path,atof(m[8].str().c_str()));
  159. else if(!m[11].str().empty()) {
  160. std::istringstream ss(m[11].str());
  161. json::value tmp;
  162. if(!tmp.load(ss,true))
  163. throw cppcms_error("Invadid json expresson: " + m[11].str());
  164. val.set(path,tmp);
  165. }
  166. else
  167. val.set(path,m[4].str());
  168. }
  169. }
  170. if(val.is_undefined()) {
  171. throw cppcms_error("No configuration defined");
  172. }
  173. return val;
  174. }
  175. namespace {
  176. int reactor_type(std::string const &name)
  177. {
  178. using booster::aio::reactor;
  179. if(name=="select")
  180. return reactor::use_select;
  181. if(name=="poll")
  182. return reactor::use_poll;
  183. if(name=="epoll")
  184. return reactor::use_epoll;
  185. if(name=="devpoll")
  186. return reactor::use_dev_poll;
  187. if(name=="kqueue")
  188. return reactor::use_kqueue;
  189. return reactor::use_default;
  190. }
  191. }
  192. impl::cached_settings const &service::cached_settings()
  193. {
  194. return *impl_->cached_settings_;
  195. }
  196. void service::setup()
  197. {
  198. impl_->cached_settings_.reset(new impl::cached_settings(settings()));
  199. impl::setup_logging(settings());
  200. impl_->id_=0;
  201. int reactor=reactor_type(settings().get("service.reactor","default"));
  202. impl_->io_service_.reset(new io::io_service(reactor));
  203. impl_->sig_.reset(new io::stream_socket(*impl_->io_service_));
  204. impl_->breaker_.reset(new io::stream_socket(*impl_->io_service_));
  205. int apps=settings().get("service.applications_pool_size",threads_no()*2);
  206. impl_->applications_pool_.reset(new cppcms::applications_pool(*this,apps));
  207. impl_->views_pool_.reset(new cppcms::views::manager(settings()));
  208. impl_->cache_pool_.reset(new cppcms::cache_pool(settings()));
  209. impl_->session_pool_.reset(new cppcms::session_pool(*this));
  210. if(settings().get("file_server.enable",false)) {
  211. if(settings().get("file_server.async",false)) {
  212. impl_->async_file_server_ = new cppcms::impl::file_server(*this,true);
  213. applications_pool().mount(impl_->async_file_server_,mount_point(""));
  214. }
  215. else {
  216. applications_pool().mount(applications_factory<cppcms::impl::file_server>(),mount_point(""));
  217. }
  218. }
  219. }
  220. void impl::setup_logging(json::value const &settings)
  221. {
  222. using namespace booster::log;
  223. level_type level = logger::string_to_level(settings.get("logging.level","error"));
  224. logger::instance().set_default_level(level);
  225. if( (
  226. settings.find("logging.file").is_undefined()
  227. && settings.find("logging.syslog").is_undefined()
  228. && settings.find("logging.stderr").is_undefined()
  229. )
  230. ||
  231. settings.get("logging.stderr",false)==true
  232. )
  233. {
  234. logger::instance().add_sink(booster::shared_ptr<sink>(new sinks::standard_error()));
  235. }
  236. if(settings.get("logging.syslog.enable",false)==true) {
  237. #ifndef CPPCMS_POSIX
  238. throw cppcms_error("Syslog is not availible on Windows");
  239. #else
  240. std::string id = settings.get("logging.syslog.id","");
  241. std::vector<std::string> vops = settings.get("logging.syslog.options",std::vector<std::string>());
  242. std::string sfacility = settings.get("logging.syslog.options","");
  243. int ops = 0;
  244. for(unsigned i=0;i<vops.size();i++) {
  245. std::string const &op=vops[i];
  246. if(op=="LOG_CONS") ops|=LOG_CONS;
  247. else if(op=="LOG_NDELAY") ops|=LOG_NDELAY;
  248. else if(op=="LOG_NOWAIT") ops|=LOG_NOWAIT;
  249. else if(op=="LOG_ODELAY") ops|=LOG_ODELAY;
  250. #ifdef LOG_PERROR
  251. else if(op=="LOG_PERROR") ops|=LOG_PERROR;
  252. #endif
  253. else if(op=="LOG_PID") ops|=LOG_PID;
  254. }
  255. if(id.empty())
  256. ::openlog(0,ops,0);
  257. else
  258. ::openlog(id.c_str(),ops,0);
  259. logger::instance().add_sink(booster::shared_ptr<sink>(new sinks::syslog()));
  260. #endif
  261. }
  262. std::string log_file;
  263. if(!(log_file=settings.get("logging.file.name","")).empty()) {
  264. booster::shared_ptr<sinks::file> file(new sinks::file());
  265. int max_files=0;
  266. if((max_files = settings.get("logging.file.max_files",0)) > 0)
  267. file->max_files(max_files);
  268. bool append = false;
  269. if((append = settings.get("logging.file.append",false))==true)
  270. file->append();
  271. file->open(log_file);
  272. std::string tz = settings.get("logging.file.timezone","");
  273. file->set_timezone(tz);
  274. logger::instance().add_sink(file);
  275. }
  276. }
  277. cppcms::session_pool &service::session_pool()
  278. {
  279. return *impl_->session_pool_;
  280. }
  281. cppcms::cache_pool &service::cache_pool()
  282. {
  283. return *impl_->cache_pool_;
  284. }
  285. cppcms::views::manager &service::views_pool()
  286. {
  287. return *impl_->views_pool_;
  288. }
  289. service::~service()
  290. {
  291. }
  292. int service::threads_no()
  293. {
  294. return cached_settings().service.worker_threads;
  295. }
  296. namespace {
  297. cppcms::service *the_service;
  298. #if defined(CPPCMS_WIN32)
  299. BOOL WINAPI handler(DWORD ctrl_type)
  300. {
  301. switch (ctrl_type)
  302. {
  303. case CTRL_C_EVENT:
  304. case CTRL_BREAK_EVENT:
  305. case CTRL_CLOSE_EVENT:
  306. case CTRL_SHUTDOWN_EVENT:
  307. if(the_service)
  308. the_service->shutdown();
  309. return TRUE;
  310. default:
  311. return FALSE;
  312. }
  313. }
  314. class apache_event_handler {
  315. public:
  316. apache_event_handler(HANDLE h=0) : handle_(h)
  317. {
  318. }
  319. void operator()() const
  320. {
  321. if( ::WaitForSingleObject(handle_,INFINITE) == WAIT_OBJECT_0 ) {
  322. if(the_service) {
  323. the_service->shutdown();
  324. }
  325. }
  326. }
  327. private:
  328. HANDLE handle_;
  329. };
  330. #else
  331. void handler(int /*nothing*/)
  332. {
  333. if(the_service)
  334. the_service->shutdown();
  335. }
  336. #endif
  337. } // anon
  338. void service::setup_exit_handling()
  339. {
  340. io::socket_pair(*impl_->sig_,*impl_->breaker_);
  341. static char c;
  342. impl_->breaker_->async_read_some(io::buffer(&c,1),
  343. boost::bind(&service::stop,this));
  344. impl_->notification_socket_=impl_->sig_->native();
  345. if(settings().get("service.disable_global_exit_handling",false))
  346. return;
  347. the_service=this;
  348. #ifdef CPPCMS_WIN32
  349. SetConsoleCtrlHandler(handler, TRUE);
  350. char *event = getenv("_FCGI_SHUTDOWN_EVENT_");
  351. if(event) {
  352. apache_event_handler handler((HANDLE)(atoi(event)));
  353. booster::thread wait_for_event(handler);
  354. }
  355. #else
  356. struct sigaction sa;
  357. memset(&sa,0,sizeof(sa));
  358. sa.sa_handler=handler;
  359. sigaction(SIGINT,&sa,0);
  360. sigaction(SIGTERM,&sa,0);
  361. sigaction(SIGUSR1,&sa,0);
  362. #endif
  363. }
  364. void service::shutdown()
  365. {
  366. char c='A';
  367. #ifdef CPPCMS_WIN32
  368. if(send(impl_->notification_socket_,&c,1,0) <= 0) {
  369. perror("notification failed");
  370. exit(1);
  371. }
  372. #else
  373. for(;;){
  374. int res=::write(impl_->notification_socket_,&c,1);
  375. if(res<0 && errno == EINTR)
  376. continue;
  377. if(res<=0) {
  378. perror("shudown notification failed");
  379. exit(1);
  380. }
  381. return;
  382. }
  383. #endif
  384. }
  385. void service::after_fork(booster::function<void()> const &cb)
  386. {
  387. impl_->on_fork_.push_back(cb);
  388. }
  389. void service::after_fork_exec()
  390. {
  391. for(unsigned i=0;i<impl_->on_fork_.size();i++)
  392. impl_->on_fork_[i]();
  393. impl_->on_fork_.clear();
  394. }
  395. cppcms::forwarder &service::forwarder()
  396. {
  397. if(!impl_->forwarder_.get()) {
  398. impl_->forwarder_.reset(new cppcms::forwarder());
  399. if(settings().find("forwarding.rules").type()==json::is_array) {
  400. json::array rules=settings().at("forwarding.rules").array();
  401. for(unsigned i=0;i<rules.size();i++) {
  402. mount_point mp;
  403. if(rules[i].find("host").type()==json::is_string)
  404. mp.host(booster::regex(rules[i].get<std::string>("host")));
  405. if(rules[i].find("script_name").type()==json::is_string)
  406. mp.script_name(booster::regex(rules[i].get<std::string>("script_name")));
  407. if(rules[i].find("path_info").type()==json::is_string)
  408. mp.path_info(booster::regex(rules[i].get<std::string>("path_info")));
  409. std::string ip=rules[i].get<std::string>("ip");
  410. int port=rules[i].get<int>("port");
  411. impl_->forwarder_->add_forwarding_rule(booster::shared_ptr<mount_point>(new mount_point(mp)),ip,port);
  412. }
  413. }
  414. }
  415. return *impl_->forwarder_;
  416. }
  417. void service::run_prepare()
  418. {
  419. generator();
  420. forwarder();
  421. session_pool().init();
  422. start_acceptor();
  423. }
  424. void service::run_acceptor()
  425. {
  426. for(unsigned i=0;i<impl_->acceptors_.size();i++)
  427. impl_->acceptors_[i]->async_accept();
  428. }
  429. void service::run_event_loop()
  430. {
  431. impl_->get_io_service().run();
  432. }
  433. #ifdef CPPCMS_WIN32
  434. void service::run_win_console()
  435. {
  436. run_prepare();
  437. prefork(); // not really prefork
  438. after_fork_exec();
  439. thread_pool();
  440. run_acceptor();
  441. setup_exit_handling();
  442. run_event_loop();
  443. }
  444. #endif
  445. #ifdef CPPCMS_WIN_NATIVE
  446. void service::win_service_prepare()
  447. {
  448. run_prepare();
  449. prefork(); // not really prefork
  450. after_fork_exec();
  451. thread_pool();
  452. run_acceptor();
  453. setup_exit_handling();
  454. }
  455. void service::win_service_exec()
  456. {
  457. run_event_loop();
  458. }
  459. void service::run_win_service()
  460. {
  461. impl::winservice::instance().prepare(boost::bind(&service::win_service_prepare,this));
  462. impl::winservice::instance().exec(boost::bind(&service::win_service_exec,this));
  463. impl::winservice::instance().stop(boost::bind(&service::shutdown,this));
  464. int argc=impl_->args_.size();
  465. std::vector<char*> argv(argc+1,static_cast<char*>(0));
  466. for(int i=0;i<argc;i++) {
  467. argv[i]=const_cast<char *>(impl_->args_[i].c_str());
  468. }
  469. impl::winservice::instance().run(*impl_->settings_,argc,&argv[0]);
  470. }
  471. #endif
  472. #ifdef CPPCMS_WIN32
  473. void service::run()
  474. {
  475. #ifdef CPPCMS_WIN_NATIVE
  476. if(settings().get("winservice.mode","console") == "console")
  477. run_win_console();
  478. else
  479. run_win_service();
  480. #else
  481. run_win_console();
  482. #endif
  483. }
  484. #else // POSIX
  485. void service::run()
  486. {
  487. run_prepare();
  488. impl::daemonizer godaemon(settings());
  489. if(prefork()) {
  490. return;
  491. }
  492. thread_pool(); // make sure we start it
  493. if(impl_->prefork_acceptor_.get())
  494. impl_->prefork_acceptor_->start();
  495. after_fork_exec();
  496. run_acceptor();
  497. setup_exit_handling();
  498. run_event_loop();
  499. }
  500. #endif
  501. int service::procs_no()
  502. {
  503. int procs=cached_settings().service.worker_processes;
  504. if(procs < 0)
  505. procs = 0;
  506. #ifdef CPPCMS_WIN32
  507. if(procs > 0)
  508. throw cppcms_error("Prefork is not supported under Windows and Cygwin");
  509. #endif
  510. return procs;
  511. }
  512. #ifdef CPPCMS_WIN32
  513. bool service::prefork()
  514. {
  515. procs_no();
  516. impl_->id_ = 1;
  517. return false;
  518. }
  519. #else // UNIX
  520. static void dummy(int /*n*/) {}
  521. bool service::prefork()
  522. {
  523. sigset_t pipemask;
  524. sigemptyset(&pipemask);
  525. sigaddset(&pipemask,SIGPIPE);
  526. sigprocmask(SIG_BLOCK,&pipemask,0);
  527. int procs=procs_no();
  528. if(procs==0) {
  529. impl_->id_ = 1;
  530. return false;
  531. }
  532. std::vector<int> pids(procs,0);
  533. for(int i=0;i<procs;i++) {
  534. int pid=::fork();
  535. if(pid < 0) {
  536. int err=errno;
  537. for(int j=0;j<i;j++) {
  538. ::kill(pids[j],SIGTERM);
  539. }
  540. for(int j=0;j<i;j++) {
  541. int stat;
  542. ::waitpid(pids[j],&stat,0);
  543. }
  544. throw cppcms_error(err,"fork failed");
  545. }
  546. else if(pid==0) {
  547. impl_->id_ = i+1;
  548. return false; // chaild
  549. }
  550. else {
  551. pids[i]=pid;
  552. }
  553. }
  554. sigset_t set,prev;
  555. sigemptyset(&set);
  556. sigaddset(&set,SIGTERM);
  557. sigaddset(&set,SIGINT);
  558. sigaddset(&set,SIGCHLD);
  559. sigaddset(&set,SIGQUIT);
  560. sigprocmask(SIG_BLOCK,&set,&prev);
  561. // Enable delivery of SIGCHLD
  562. struct sigaction sa_new,sa_old;
  563. memset(&sa_new,0,sizeof(sa_new));
  564. sa_new.sa_handler=dummy;
  565. ::sigaction(SIGCHLD,&sa_new,&sa_old);
  566. int sig;
  567. do {
  568. sig=0;
  569. sigwait(&set,&sig);
  570. BOOSTER_INFO("cppcms") << "Catched signal " << sig;
  571. if(sig==SIGCHLD) {
  572. int status;
  573. int pid = ::waitpid(0,&status,WNOHANG);
  574. if(pid > 0) {
  575. std::vector<int>::iterator p;
  576. p = std::find(pids.begin(),pids.end(),pid);
  577. // Ingnore all processes that are not my own childrens
  578. if(p!=pids.end()) {
  579. // TODO: Make better error handling
  580. if(!WIFEXITED(status) || WEXITSTATUS(status)!=0){
  581. if(WIFEXITED(status)) {
  582. BOOSTER_CRITICAL("cppcms") <<"Chaild exited with "<<WEXITSTATUS(status);
  583. }
  584. else if(WIFSIGNALED(status)) {
  585. BOOSTER_CRITICAL("cppcms") <<"Chaild killed by "<<WTERMSIG(status);
  586. }
  587. else {
  588. BOOSTER_CRITICAL("cppcms") <<"Chaild exited for unknown reason";
  589. }
  590. impl_->id_ = p - pids.begin() + 1;
  591. *p=-1;
  592. pid = ::fork();
  593. if(pid < 0) {
  594. int err=errno;
  595. BOOSTER_ALERT("cppcms") <<"Failed to create process: " <<strerror(err);
  596. }
  597. else if(pid == 0) {
  598. ::sigaction(SIGCHLD,&sa_old,NULL);
  599. sigprocmask(SIG_SETMASK,&prev,NULL);
  600. return false;
  601. }
  602. else {
  603. *p=pid;
  604. impl_->id_ = 0;
  605. }
  606. }
  607. else {
  608. *p=-1;
  609. }
  610. }
  611. }
  612. }
  613. }while(sig!=SIGINT && sig!=SIGTERM && sig!=SIGQUIT);
  614. sigprocmask(SIG_SETMASK,&prev,NULL);
  615. BOOSTER_INFO("cppcms") << "Shutting down";
  616. BOOSTER_DEBUG("cppcms") << "Killing Children";
  617. for(int i=0;i<procs;i++) {
  618. if(pids[i]<0)
  619. continue;
  620. ::kill(pids[i],SIGTERM);
  621. }
  622. for(int i=0;i<procs;i++) {
  623. if(pids[i]<0)
  624. continue;
  625. int stat;
  626. ::waitpid(pids[i],&stat,0);
  627. }
  628. BOOSTER_DEBUG("cppcms") << "Children are dead";
  629. return true;
  630. }
  631. #endif
  632. int service::process_id()
  633. {
  634. return impl_->id_;
  635. }
  636. std::auto_ptr<cppcms::impl::cgi::acceptor> service::setup_acceptor(json::value const &v,int backlog,int port_shift)
  637. {
  638. using namespace cppcms::impl::cgi;
  639. std::string api=v.get<std::string>("api");
  640. std::string socket=v.get("socket","");
  641. std::string ip;
  642. int port=0;
  643. bool tcp;
  644. std::auto_ptr<acceptor> a;
  645. if(socket.empty()) {
  646. ip=v.get("ip","127.0.0.1");
  647. port=v.get("port",8080) + port_shift;
  648. tcp=true;
  649. }
  650. else {
  651. if( !v.find("port").is_undefined()
  652. || !v.find("ip").is_undefined())
  653. {
  654. throw cppcms_error("Can't define both UNIX socket and TCP port/ip");
  655. }
  656. tcp=false;
  657. }
  658. if(tcp) {
  659. #ifdef CPPCMS_HAS_SCGI
  660. if(api=="scgi")
  661. a = scgi_api_tcp_socket_factory(*this,ip,port,backlog);
  662. #endif
  663. #ifdef CPPCMS_HAS_FCGI
  664. if(api=="fastcgi")
  665. a = fastcgi_api_tcp_socket_factory(*this,ip,port,backlog);
  666. #endif
  667. #ifdef CPPCMS_HAS_HTTP
  668. if(api=="http")
  669. a = http_api_factory(*this,ip,port,backlog);
  670. #endif
  671. }
  672. else {
  673. #ifdef CPPCMS_WIN_NATIVE
  674. throw cppcms_error("Unix domain sockets are not supported under Windows... (isn't it obvious?)");
  675. #elif defined CPPCMS_CYGWIN
  676. throw cppcms_error("CppCMS uses native Win32 sockets under cygwin, so Unix sockets are not supported");
  677. #else
  678. #ifdef CPPCMS_HAS_SCGI
  679. if(api=="scgi") {
  680. if(socket=="stdin")
  681. a = scgi_api_unix_socket_factory(*this,backlog);
  682. else
  683. a = scgi_api_unix_socket_factory(*this,socket,backlog);
  684. }
  685. #endif
  686. #ifdef CPPCMS_HAS_FCGI
  687. if(api=="fastcgi") {
  688. if(socket=="stdin")
  689. a = fastcgi_api_unix_socket_factory(*this,backlog);
  690. else
  691. a = fastcgi_api_unix_socket_factory(*this,socket,backlog);
  692. }
  693. #endif
  694. #ifdef CPPCMS_HAS_HTTP
  695. if(api=="http")
  696. throw cppcms_error("HTTP API is not supported over Unix Domain sockets");
  697. #endif
  698. #endif
  699. }
  700. if(!a.get())
  701. throw cppcms_error("Unknown api: " + api);
  702. return a;
  703. }
  704. void service::start_acceptor(bool after_fork)
  705. {
  706. using namespace impl::cgi;
  707. int backlog=settings().get("service.backlog",threads_no() * std::max(procs_no(),1) * 2);
  708. bool preforking = procs_no() > 1 && !after_fork;
  709. if(preforking)
  710. #ifndef CPPCMS_WIN32
  711. impl_->prefork_acceptor_.reset(new impl::prefork_acceptor(this));
  712. #else
  713. throw cppcms_error("Preforking is not supported under Windows");
  714. #endif
  715. if( settings().find("service.list").type()!=json::is_undefined
  716. && settings().find("service.api").type()!=json::is_undefined)
  717. {
  718. throw cppcms_error("Can't specify both service.api and service.list");
  719. }
  720. if(settings().find("service.api").type()!=json::is_undefined) {
  721. booster::shared_ptr<acceptor> ac(setup_acceptor(settings()["service"],backlog));
  722. #ifndef CPPCMS_WIN32
  723. if(preforking) {
  724. impl_->prefork_acceptor_->add_acceptor(ac);
  725. }
  726. else
  727. #endif
  728. {
  729. impl_->acceptors_.push_back(ac);
  730. }
  731. }
  732. if(settings().find("service.list").type()!=json::is_undefined) {
  733. json::array list=settings()["service"]["list"].array();
  734. if(list.empty())
  735. throw cppcms_error("At least one service should be provided in service.list");
  736. for(unsigned i=0;i<list.size();i++) {
  737. booster::shared_ptr<acceptor> ac(setup_acceptor(list[i],backlog));
  738. #ifndef CPPCMS_WIN32
  739. if(preforking) {
  740. impl_->prefork_acceptor_->add_acceptor(ac);
  741. }
  742. else
  743. #endif
  744. {
  745. impl_->acceptors_.push_back(ac);
  746. }
  747. }
  748. }
  749. }
  750. cppcms::applications_pool &service::applications_pool()
  751. {
  752. return *impl_->applications_pool_;
  753. }
  754. cppcms::thread_pool &service::thread_pool()
  755. {
  756. if(!impl_->thread_pool_.get()) {
  757. impl_->thread_pool_.reset(new cppcms::thread_pool(threads_no()));
  758. }
  759. return *impl_->thread_pool_;
  760. }
  761. json::value const &service::settings()
  762. {
  763. return *impl_->settings_;
  764. }
  765. cppcms::impl::service &service::impl()
  766. {
  767. return *impl_;
  768. }
  769. void service::post(booster::function<void()> const &handler)
  770. {
  771. impl_->get_io_service().post(handler);
  772. }
  773. void service::stop()
  774. {
  775. for(unsigned i=0;i<impl_->acceptors_.size();i++) {
  776. if(impl_->acceptors_[i])
  777. impl_->acceptors_[i]->stop();
  778. }
  779. #ifndef CPPCMS_WIN32
  780. impl_->prefork_acceptor_.reset();
  781. #endif
  782. thread_pool().stop();
  783. impl_->get_io_service().stop();
  784. }
  785. locale::generator const &service::generator()
  786. {
  787. if(impl_->locale_generator_.get())
  788. return *impl_->locale_generator_.get();
  789. typedef std::vector<std::string> vstr_type;
  790. std::string default_backend = settings().get("localization.backend","");
  791. if(default_backend.empty())
  792. impl_->locale_generator_.reset(new locale::generator());
  793. else {
  794. locale::localization_backend_manager mgr = locale::localization_backend_manager::global();
  795. mgr.select(default_backend);
  796. impl_->locale_generator_.reset(new locale::generator(mgr));
  797. }
  798. locale::generator &gen= *impl_->locale_generator_;
  799. gen.characters(locale::char_facet);
  800. std::string enc = settings().get("localization.encoding","");
  801. vstr_type paths= settings().get("localization.messages.paths",vstr_type());
  802. vstr_type domains = settings().get("localization.messages.domains",vstr_type());
  803. if(!paths.empty() && !domains.empty()) {
  804. unsigned i;
  805. for(i=0;i<paths.size();i++) {
  806. gen.add_messages_path(paths[i]);
  807. }
  808. for(i=0;i<domains.size();i++)
  809. gen.add_messages_domain(domains[i]);
  810. }
  811. vstr_type locales = settings().get("localization.locales",vstr_type());
  812. gen.locale_cache_enabled(true);
  813. if(locales.empty()) {
  814. gen("");
  815. impl_->default_locale_=gen("");
  816. if(std::use_facet<locale::info>(impl_->default_locale_).name()=="C")
  817. BOOSTER_WARNING("cppcms")
  818. << "The default system locale is 'C', the encoding is set to US-ASCII. "
  819. << "It is recommended to specify the locale name explicitly";
  820. }
  821. else {
  822. for(unsigned i=0;i<locales.size();i++) {
  823. std::locale tmp = gen(locales[i]);
  824. locale::info const &inf = std::use_facet<locale::info>(tmp);
  825. if(std::use_facet<locale::info>(tmp).name()=="C" || inf.encoding()=="us-ascii") {
  826. if(locales[i].empty()) {
  827. BOOSTER_WARNING("cppcms")
  828. << "The default system locale is 'C', the encoding is set to US-ASCII. "
  829. << "It is recommended to specify the locale name explicitly";
  830. }
  831. else if(locales[i].find('.')==std::string::npos) {
  832. BOOSTER_WARNING("cppcms")
  833. << "The encoding for locale `" << locales[i] << "' is not specified "
  834. << "the encoding is set to US-ASCII. It is recommended to specify the locale name explicitly";
  835. }
  836. }
  837. }
  838. impl_->default_locale_=gen(locales[0]);
  839. }
  840. return *impl_->locale_generator_.get();
  841. }
  842. std::locale service::locale()
  843. {
  844. generator();
  845. return impl_->default_locale_;
  846. }
  847. std::locale service::locale(std::string const &name)
  848. {
  849. return generator().generate(name);
  850. }
  851. booster::aio::io_service &service::get_io_service()
  852. {
  853. return impl_->get_io_service();
  854. }
  855. namespace impl {
  856. service::service()
  857. {
  858. }
  859. service::~service()
  860. {
  861. async_file_server_ = 0;
  862. acceptors_.clear();
  863. thread_pool_.reset();
  864. sig_.reset();
  865. breaker_.reset();
  866. io_service_.reset();
  867. // applications pool should be destroyed after
  868. // io_service, because soma apps may try unregister themselfs
  869. applications_pool_.reset();
  870. locale_generator_.reset();
  871. settings_.reset();
  872. }
  873. } // impl
  874. } // cppcms