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.
 
 
 
 
 
 

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