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.
 
 
 
 
 
 

641 lines
14 KiB

  1. #define CPPCMS_SOURCE
  2. #include "defs.h"
  3. #ifndef CPPCMS_WIN32
  4. #if defined(__sun)
  5. #define _POSIX_PTHREAD_SEMANTICS
  6. #endif
  7. #include <signal.h>
  8. #endif
  9. #include "asio_config.h"
  10. #include "service.h"
  11. #include "service_impl.h"
  12. #include "applications_pool.h"
  13. #include "thread_pool.h"
  14. #include "cppcms_error.h"
  15. #include "cgi_acceptor.h"
  16. #include "cgi_api.h"
  17. #ifdef CPPCMS_HAS_SCGI
  18. # include "scgi_api.h"
  19. #endif
  20. #ifdef CPPCMS_HAS_HTTP
  21. # include "http_api.h"
  22. #endif
  23. #ifdef CPPCMS_HAS_FCGI
  24. # include "fastcgi_api.h"
  25. #endif
  26. #include "cache_pool.h"
  27. #include "internal_file_server.h"
  28. #include "json.h"
  29. #include "localization.h"
  30. #include "views_pool.h"
  31. #include "session_pool.h"
  32. #ifdef CPPCMS_POSIX
  33. #include <sys/wait.h>
  34. #endif
  35. #include <stdio.h>
  36. #include <string.h>
  37. #include <stdlib.h>
  38. #include <iostream>
  39. #include <fstream>
  40. #include "config.h"
  41. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  42. # include <boost/lexical_cast.hpp>
  43. # include <boost/regex.hpp>
  44. #else // Internal Boost
  45. # include <cppcms_boost/lexical_cast.hpp>
  46. # include <cppcms_boost/regex.hpp>
  47. namespace boost = cppcms_boost;
  48. #endif
  49. namespace cppcms {
  50. service::service(json::value const &v) :
  51. impl_(new impl::service())
  52. {
  53. impl_->settings_.reset(new json::value(v));
  54. setup();
  55. }
  56. service::service(int argc,char *argv[]) :
  57. impl_(new impl::service())
  58. {
  59. impl_->settings_.reset(new json::value());
  60. load_settings(argc,argv);
  61. setup();
  62. }
  63. void service::load_settings(int argc,char *argv[])
  64. {
  65. using std::string;
  66. std::string file_name;
  67. for(int i=1;i<argc;i++) {
  68. if(argv[i]==std::string("-c")) {
  69. if(!file_name.empty()) {
  70. throw cppcms_error("Switch -c can't be used twice");
  71. }
  72. if(i+1 < argc)
  73. file_name=argv[i+1];
  74. else
  75. throw cppcms_error("Switch -c requires configuration file parameters");
  76. i++;
  77. }
  78. else if(argv[i]==std::string("-U"))
  79. break;
  80. }
  81. if(file_name.empty()) {
  82. char const *e = getenv("CPPCMS_CONFIG");
  83. if(e) file_name = e;
  84. }
  85. json::value &val=*impl_->settings_;
  86. if(!file_name.empty()) {
  87. std::ifstream fin(file_name.c_str());
  88. if(!fin)
  89. throw cppcms_error("Failed to open filename:"+file_name);
  90. int line_no=0;
  91. if(!val.load(fin,true,&line_no)) {
  92. std::ostringstream ss;
  93. ss<<"Error reading configurarion file "<<file_name<<" in line:"<<line_no;
  94. throw cppcms_error(ss.str());
  95. }
  96. }
  97. boost::regex r("^--((\\w+)(-\\w+)*)=((true)|(false)|(null)|(-?\\d+(\\.\\d+)?([eE][\\+-]?\\d+)?)|([\\[\\{].*[\\]\\}])|(.*))$");
  98. for(int i=1;i<argc;i++) {
  99. std::string arg=argv[i];
  100. if(arg=="-c") {
  101. i++;
  102. continue;
  103. }
  104. if(arg=="-U")
  105. break;
  106. if(arg.substr(0,2)=="--") {
  107. boost::cmatch m;
  108. if(!boost::regex_match(arg.c_str(),m,r))
  109. throw cppcms_error("Invalid switch: "+arg);
  110. std::string path=m[1];
  111. for(unsigned i=0;i<path.size();i++)
  112. if(path[i]=='-')
  113. path[i]='.';
  114. if(!m[5].str().empty())
  115. val.set(path,true);
  116. else if(!m[6].str().empty())
  117. val.set(path,false);
  118. else if(!m[7].str().empty())
  119. val.set(path,json::null());
  120. else if(!m[8].str().empty())
  121. val.set(path,boost::lexical_cast<double>(m[8].str()));
  122. else if(!m[11].str().empty()) {
  123. std::istringstream ss(m[11].str());
  124. json::value tmp;
  125. if(!tmp.load(ss,true))
  126. throw cppcms_error("Invadid json expresson: " + m[11].str());
  127. val.set(path,tmp);
  128. }
  129. else
  130. val.set(path,m[4].str());
  131. }
  132. }
  133. if(val.is_undefined()) {
  134. throw cppcms_error("No configuration defined");
  135. }
  136. }
  137. void service::setup()
  138. {
  139. impl_->id_=0;
  140. int apps=settings().get("service.applications_pool_size",threads_no()*2);
  141. impl_->applications_pool_.reset(new cppcms::applications_pool(*this,apps));
  142. impl_->views_pool_.reset(new cppcms::views_pool(settings()));
  143. impl_->cache_pool_.reset(new cppcms::cache_pool(settings()));
  144. impl_->session_pool_.reset(new cppcms::session_pool(*this));
  145. }
  146. cppcms::session_pool &service::session_pool()
  147. {
  148. return *impl_->session_pool_;
  149. }
  150. cppcms::cache_pool &service::cache_pool()
  151. {
  152. return *impl_->cache_pool_;
  153. }
  154. cppcms::views_pool &service::views_pool()
  155. {
  156. return *impl_->views_pool_;
  157. }
  158. service::~service()
  159. {
  160. }
  161. int service::threads_no()
  162. {
  163. return settings().get("service.worker_threads",5);
  164. }
  165. namespace {
  166. cppcms::service *the_service;
  167. #if defined(CPPCMS_WIN32)
  168. void make_socket_pair(boost::asio::ip::tcp::socket &s1,boost::asio::ip::tcp::socket &s2)
  169. {
  170. boost::asio::ip::tcp::acceptor acceptor(s1.get_io_service(),
  171. boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0));
  172. boost::asio::ip::tcp::endpoint server_endpoint = acceptor.local_endpoint();
  173. server_endpoint.address(boost::asio::ip::address_v4::loopback());
  174. s1.lowest_layer().connect(server_endpoint);
  175. acceptor.accept(s2.lowest_layer());
  176. }
  177. BOOL WINAPI handler(DWORD ctrl_type)
  178. {
  179. switch (ctrl_type)
  180. {
  181. case CTRL_C_EVENT:
  182. case CTRL_BREAK_EVENT:
  183. case CTRL_CLOSE_EVENT:
  184. case CTRL_SHUTDOWN_EVENT:
  185. the_service->shutdown();
  186. return TRUE;
  187. default:
  188. return FALSE;
  189. }
  190. }
  191. #else
  192. void make_socket_pair(boost::asio::local::stream_protocol::socket &s1,boost::asio::local::stream_protocol::socket &s2)
  193. {
  194. boost::asio::local::connect_pair(s1,s2);
  195. }
  196. void handler(int nothing)
  197. {
  198. the_service->shutdown();
  199. }
  200. #endif
  201. } // anon
  202. void service::setup_exit_handling()
  203. {
  204. make_socket_pair(*impl_->sig_,*impl_->breaker_);
  205. static char c;
  206. impl_->breaker_->async_read_some(boost::asio::buffer(&c,1),
  207. boost::bind(&service::stop,this));
  208. impl_->notification_socket_=impl_->sig_->native();
  209. if(settings().get("service.disable_global_exit_handling",false))
  210. return;
  211. the_service=this;
  212. #ifdef CPPCMS_WIN32
  213. SetConsoleCtrlHandler(handler, TRUE);
  214. #else
  215. struct sigaction sa;
  216. memset(&sa,0,sizeof(sa));
  217. sa.sa_handler=handler;
  218. sigaction(SIGINT,&sa,0);
  219. sigaction(SIGTERM,&sa,0);
  220. sigaction(SIGUSR1,&sa,0);
  221. #endif
  222. }
  223. void service::shutdown()
  224. {
  225. char c='A';
  226. #ifdef CPPCMS_WIN32
  227. if(send(impl_->notification_socket_,&c,1,0) <= 0) {
  228. perror("notification failed");
  229. exit(1);
  230. }
  231. #else
  232. for(;;){
  233. int res=::write(impl_->notification_socket_,&c,1);
  234. if(res<0 && errno == EINTR)
  235. continue;
  236. if(res<=0) {
  237. perror("shudown notification failed");
  238. exit(1);
  239. }
  240. return;
  241. }
  242. #endif
  243. }
  244. void service::after_fork(util::callback0 const &cb)
  245. {
  246. impl_->on_fork_.push_back(cb);
  247. }
  248. void service::run()
  249. {
  250. generator();
  251. session_pool().init();
  252. start_acceptor();
  253. if(settings().get("file_server.enable",false))
  254. applications_pool().mount(applications_factory<cppcms::impl::file_server>(),"");
  255. if(prefork()) {
  256. return;
  257. }
  258. thread_pool(); // make sure we start it
  259. for(unsigned i=0;i<impl_->on_fork_.size();i++)
  260. impl_->on_fork_[i]();
  261. impl_->on_fork_.clear();
  262. impl_->acceptor_->async_accept();
  263. setup_exit_handling();
  264. impl_->get_io_service().run();
  265. }
  266. int service::procs_no()
  267. {
  268. int procs=settings().get("service.worker_processes",0);
  269. if(procs < 0)
  270. procs = 0;
  271. #ifdef CPPCMS_WIN32
  272. if(procs > 0)
  273. throw cppcms_error("Prefork is not supported under Windows");
  274. #endif
  275. return procs;
  276. }
  277. #ifdef CPPCMS_WIN32
  278. bool service::prefork()
  279. {
  280. procs_no();
  281. impl_->id_ = 1;
  282. return false;
  283. }
  284. #else // UNIX
  285. static void dummy(int n) {}
  286. bool service::prefork()
  287. {
  288. int procs=procs_no();
  289. if(procs==0) {
  290. impl_->id_ = 1;
  291. return false;
  292. }
  293. std::vector<int> pids(procs,0);
  294. for(int i=0;i<procs;i++) {
  295. int pid=::fork();
  296. if(pid < 0) {
  297. int err=errno;
  298. for(int j=0;j<i;j++) {
  299. ::kill(pids[j],SIGTERM);
  300. int stat;
  301. ::waitpid(pids[j],&stat,0);
  302. }
  303. throw cppcms_error(err,"fork failed");
  304. }
  305. else if(pid==0) {
  306. impl_->id_ = i+1;
  307. return false; // chaild
  308. }
  309. else {
  310. pids[i]=pid;
  311. }
  312. }
  313. sigset_t set,prev;
  314. sigemptyset(&set);
  315. sigaddset(&set,SIGTERM);
  316. sigaddset(&set,SIGINT);
  317. sigaddset(&set,SIGCHLD);
  318. sigaddset(&set,SIGQUIT);
  319. sigprocmask(SIG_BLOCK,&set,&prev);
  320. // Enable delivery of SIGCHLD
  321. struct sigaction sa_new,sa_old;
  322. memset(&sa_new,0,sizeof(sa_new));
  323. sa_new.sa_handler=dummy;
  324. ::sigaction(SIGCHLD,&sa_new,&sa_old);
  325. int sig;
  326. do {
  327. sig=0;
  328. sigwait(&set,&sig);
  329. if(sig==SIGCHLD) {
  330. int status;
  331. int pid = ::waitpid(0,&status,WNOHANG);
  332. if(pid > 0) {
  333. std::vector<int>::iterator p;
  334. p = std::find(pids.begin(),pids.end(),pid);
  335. // Ingnore all processes that are not my own childrens
  336. if(p!=pids.end()) {
  337. // TODO: Make better error handling
  338. if(!WIFEXITED(status) || WEXITSTATUS(status)!=0){
  339. if(WIFEXITED(status)) {
  340. std::cerr<<"Chaild exited with "<<WEXITSTATUS(status)<<std::endl;
  341. }
  342. else if(WIFSIGNALED(status)) {
  343. std::cerr<<"Chaild killed by "<<WTERMSIG(status)<<std::endl;
  344. }
  345. else {
  346. std::cerr<<"Chaild exited for unknown reason"<<std::endl;
  347. }
  348. impl_->id_ = p - pids.begin() + 1;
  349. *p=-1;
  350. pid = ::fork();
  351. if(pid < 0) {
  352. int err=errno;
  353. std::cerr<<"Failed to create process: " <<strerror(err)<<std::endl;
  354. }
  355. else if(pid == 0) {
  356. ::sigaction(SIGCHLD,&sa_old,NULL);
  357. sigprocmask(SIG_SETMASK,&prev,NULL);
  358. return false;
  359. }
  360. else {
  361. *p=pid;
  362. impl_->id_ = 0;
  363. }
  364. }
  365. else {
  366. *p=-1;
  367. }
  368. }
  369. }
  370. }
  371. }while(sig!=SIGINT && sig!=SIGTERM && sig!=SIGQUIT);
  372. sigprocmask(SIG_SETMASK,&prev,NULL);
  373. for(int i=0;i<procs;i++) {
  374. if(pids[i]<0)
  375. continue;
  376. ::kill(pids[i],SIGTERM);
  377. int stat;
  378. ::waitpid(pids[i],&stat,0);
  379. }
  380. return true;
  381. }
  382. #endif
  383. int service::process_id()
  384. {
  385. return impl_->id_;
  386. }
  387. void service::start_acceptor()
  388. {
  389. using namespace impl::cgi;
  390. std::string api=settings().get<std::string>("service.api");
  391. std::string socket=settings().get("service.socket","");
  392. int backlog=settings().get("service.backlog",threads_no() * 2);
  393. std::string ip;
  394. int port=0;
  395. bool tcp;
  396. if(socket.empty()) {
  397. ip=settings().get("service.ip","127.0.0.1");
  398. port=settings().get("service.port",8080);
  399. tcp=true;
  400. }
  401. else {
  402. if( !settings().find("service.port").is_undefined()
  403. || !settings().find("service.ip").is_undefined())
  404. {
  405. throw cppcms_error("Can't define both UNIX socket and TCP port/ip");
  406. }
  407. tcp=false;
  408. }
  409. impl_->acceptor_.reset();
  410. if(tcp) {
  411. #ifdef CPPCMS_HAS_SCGI
  412. if(api=="scgi")
  413. impl_->acceptor_ = scgi_api_tcp_socket_factory(*this,ip,port,backlog);
  414. #endif
  415. #ifdef CPPCMS_HAS_FCGI
  416. if(api=="fastcgi")
  417. impl_->acceptor_ = fastcgi_api_tcp_socket_factory(*this,ip,port,backlog);
  418. #endif
  419. #ifdef CPPCMS_HAS_HTTP
  420. if(api=="http")
  421. impl_->acceptor_ = http_api_factory(*this,ip,port,backlog);
  422. #endif
  423. }
  424. else {
  425. #ifdef CPPCMS_WIN_NATIVE
  426. throw cppcms_error("Unix domain sockets are not supported under Windows... (isn't it obvious?)");
  427. #elif defined CPPCMS_CYGWIN
  428. throw cppcms_error("CppCMS uses native Win32 sockets under cygwin, so Unix sockets are not supported");
  429. #else
  430. #ifdef CPPCMS_HAS_SCGI
  431. if(api=="scgi") {
  432. if(socket=="stdin")
  433. impl_->acceptor_ = scgi_api_unix_socket_factory(*this,backlog);
  434. else
  435. impl_->acceptor_ = scgi_api_unix_socket_factory(*this,socket,backlog);
  436. }
  437. #endif
  438. #ifdef CPPCMS_HAS_FCGI
  439. if(api=="fastcgi") {
  440. if(socket=="stdin")
  441. impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,backlog);
  442. else
  443. impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,socket,backlog);
  444. }
  445. #endif
  446. #ifdef CPPCMS_HAS_HTTP
  447. if(api=="http")
  448. throw cppcms_error("HTTP API is not supported over Unix Domain sockets");
  449. #endif
  450. #endif
  451. }
  452. if(!impl_->acceptor_.get())
  453. throw cppcms_error("Unknown service.api: " + api);
  454. }
  455. cppcms::applications_pool &service::applications_pool()
  456. {
  457. return *impl_->applications_pool_;
  458. }
  459. cppcms::thread_pool &service::thread_pool()
  460. {
  461. if(!impl_->thread_pool_.get()) {
  462. impl_->thread_pool_.reset(new cppcms::thread_pool(threads_no()));
  463. }
  464. return *impl_->thread_pool_;
  465. }
  466. json::value const &service::settings()
  467. {
  468. return *impl_->settings_;
  469. }
  470. cppcms::impl::service &service::impl()
  471. {
  472. return *impl_;
  473. }
  474. void service::post(util::callback0 const &handler)
  475. {
  476. impl_->get_io_service().post(handler);
  477. }
  478. void service::stop()
  479. {
  480. if(impl_->acceptor_.get())
  481. impl_->acceptor_->stop();
  482. thread_pool().stop();
  483. impl_->get_io_service().stop();
  484. }
  485. locale::generator const &service::generator()
  486. {
  487. if(impl_->locale_generator_.get())
  488. return *impl_->locale_generator_.get();
  489. typedef std::vector<std::string> vstr_type;
  490. impl_->locale_generator_.reset(new locale::generator());
  491. locale::generator &gen= *impl_->locale_generator_;
  492. gen.characters(locale::char_facet);
  493. std::string enc = settings().get("localization.encoding","");
  494. if(!enc.empty())
  495. gen.octet_encoding(enc);
  496. vstr_type paths= settings().get("localization.messages.paths",vstr_type());
  497. vstr_type domains = settings().get("localization.messages.domains",vstr_type());
  498. if(!paths.empty() && !domains.empty()) {
  499. unsigned i;
  500. for(i=0;i<paths.size();i++)
  501. gen.add_messages_path(paths[i]);
  502. for(i=0;i<domains.size();i++)
  503. gen.add_messages_domain(domains[i]);
  504. }
  505. vstr_type locales = settings().get("localization.locales",vstr_type());
  506. if(locales.empty()) {
  507. gen.preload("");
  508. impl_->default_locale_=gen("");
  509. }
  510. else {
  511. for(unsigned i=0;i<locales.size();i++)
  512. gen.preload(locales[i]);
  513. impl_->default_locale_=gen(locales[0]);
  514. }
  515. return *impl_->locale_generator_.get();
  516. }
  517. std::locale service::locale()
  518. {
  519. generator();
  520. return impl_->default_locale_;
  521. }
  522. std::locale service::locale(std::string const &name)
  523. {
  524. return generator().get(name);
  525. }
  526. namespace impl {
  527. service::service() :
  528. io_service_(new boost::asio::io_service()),
  529. sig_(new loopback_socket_type(*io_service_)),
  530. breaker_(new loopback_socket_type(*io_service_))
  531. {
  532. }
  533. service::~service()
  534. {
  535. acceptor_.reset();
  536. thread_pool_.reset();
  537. sig_.reset();
  538. breaker_.reset();
  539. io_service_.reset();
  540. // applications pool should be destroyed after
  541. // io_service, because soma apps may try unregister themselfs
  542. applications_pool_.reset();
  543. locale_generator_.reset();
  544. settings_.reset();
  545. }
  546. } // impl
  547. } // cppcms