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.
 
 
 
 
 
 

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