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.
 
 
 
 
 
 

624 lines
11 KiB

  1. #ifdef HAVE_CONFIG_H
  2. # include "config.h"
  3. #endif
  4. #include "manager.h"
  5. #include "cgicc_connection.h"
  6. #include <dlfcn.h>
  7. #include <dirent.h>
  8. #include <poll.h>
  9. #include <signal.h>
  10. #include <errno.h>
  11. #include <unistd.h>
  12. #include <stdlib.h>
  13. #include <semaphore.h>
  14. #include <sys/mman.h>
  15. #include <sys/wait.h>
  16. #include <sys/types.h>
  17. #include <algorithm>
  18. #include "thread_cache.h"
  19. #include "scgi.h"
  20. #include "cgi.h"
  21. #ifdef EN_FORK_CACHE
  22. # include "process_cache.h"
  23. #endif
  24. #ifdef EN_FCGI_BACKEND
  25. # include "fcgi.h"
  26. #endif
  27. #ifdef EN_TCP_CACHE
  28. # include "tcp_cache.h"
  29. #endif
  30. namespace cppcms {
  31. namespace details {
  32. class single_run : public web_application{
  33. public:
  34. single_run(manager &m) : web_application(m)
  35. {
  36. };
  37. virtual void execute()
  38. {
  39. base_factory &factory=*app.workers;
  40. shared_ptr<worker_thread> worker(factory(app));
  41. cgi_session *session=app.api->accept_session();
  42. if(session) {
  43. try {
  44. if(session->prepare()) {
  45. worker->run(session->get_connection());
  46. }
  47. }
  48. catch(...) {
  49. delete session;
  50. throw;
  51. }
  52. delete session;
  53. }
  54. }
  55. };
  56. fast_cgi_application *fast_cgi_application::handlers_owner=NULL;
  57. fast_cgi_application::event_t fast_cgi_application::wait()
  58. {
  59. while(!the_end) {
  60. struct pollfd fds;
  61. fds.fd=app.api->get_socket();
  62. fds.events=POLLIN | POLLERR;
  63. fds.revents=0;
  64. /* Wait for two events:
  65. * 1. New connection and then do accept
  66. * 2. Exit message - exit and do clean up */
  67. if(poll(&fds,1,-1)<0) {
  68. if(errno==EINTR && !the_end)
  69. continue;
  70. return EXIT;
  71. }
  72. else if(fds.revents) {
  73. if(fds.revents & POLLERR)
  74. return EXIT;
  75. return ACCEPT;
  76. }
  77. }
  78. return EXIT;
  79. }
  80. void fast_cgi_application::shutdown()
  81. {
  82. // Rise exit signal on
  83. // the selfpipe
  84. the_end=true;
  85. }
  86. void fast_cgi_application::handler(int id)
  87. {
  88. fast_cgi_application *ptr=fast_cgi_application::get_instance();
  89. if(ptr) {
  90. ptr->shutdown();
  91. }
  92. }
  93. void fast_cgi_application::set_signal_handlers()
  94. {
  95. handlers_owner=this;
  96. /* Signals defined by standard */
  97. signal(SIGTERM,handler);
  98. signal(SIGUSR1,handler);
  99. /* Additional signal */
  100. signal(SIGINT,handler);
  101. }
  102. fast_cgi_single_threaded_app::fast_cgi_single_threaded_app(manager &m) :
  103. fast_cgi_application( m)
  104. {
  105. base_factory &factory=*app.workers;
  106. worker=factory(app);
  107. }
  108. bool fast_cgi_single_threaded_app::run()
  109. {
  110. // Blocking loop
  111. event_t event=wait();
  112. if(event==EXIT) {
  113. return false;
  114. } // ELSE event==ACCEPT
  115. cgi_session *session=app.api->accept_session();
  116. if(session) {
  117. try {
  118. if(session->prepare()) {
  119. worker->run(session->get_connection());
  120. }
  121. }
  122. catch(...) {
  123. delete session;
  124. throw;
  125. }
  126. delete session;
  127. }
  128. return true;
  129. };
  130. void fast_cgi_application::execute()
  131. {
  132. the_end=false;
  133. set_signal_handlers();
  134. while(run()){
  135. /* Do Continue */
  136. }
  137. }
  138. fast_cgi_multiple_threaded_app::fast_cgi_multiple_threaded_app(manager &m):
  139. fast_cgi_application(m)
  140. {
  141. int threads_num=app.config.lval("server.threads",5);
  142. int buffer=app.config.lval("server.buffer",10);
  143. int i;
  144. threads_info=NULL;
  145. pids=NULL;
  146. size=threads_num;
  147. // Init Worker Threads
  148. workers.resize(size);
  149. base_factory &factory=*app.workers;
  150. for(i=0;i<size;i++) {
  151. workers[i]=factory(app);
  152. }
  153. // Setup Jobs Manager
  154. jobs.init(buffer);
  155. start_threads();
  156. }
  157. void *fast_cgi_multiple_threaded_app::thread_func(void *p)
  158. {
  159. info_t *params=(info_t *)p;
  160. int id=params->first;
  161. fast_cgi_multiple_threaded_app *self=params->second;
  162. cgi_session *session;
  163. while((session=self->jobs.pop())!=NULL) {
  164. try{
  165. if(session->prepare()){
  166. self->workers[id]->run(session->get_connection());
  167. }
  168. }
  169. catch(...){
  170. delete session;
  171. return NULL;
  172. }
  173. delete session;
  174. }
  175. return NULL;
  176. }
  177. void fast_cgi_multiple_threaded_app::start_threads()
  178. {
  179. int i;
  180. pids = new pthread_t [size];
  181. threads_info = new info_t [size];
  182. for(i=0;i<size;i++) {
  183. threads_info[i].first=i;
  184. threads_info[i].second=this;
  185. pthread_create(pids+i,NULL,thread_func,threads_info+i);
  186. }
  187. }
  188. void fast_cgi_multiple_threaded_app::wait_threads()
  189. {
  190. int i;
  191. for(i=0;i<size;i++) {
  192. pthread_join(pids[i],NULL);
  193. }
  194. }
  195. bool fast_cgi_multiple_threaded_app::run()
  196. {
  197. if(wait()==ACCEPT) {
  198. cgi_session *session=app.api->accept_session();
  199. if(session){
  200. jobs.push(session);
  201. }
  202. return true;
  203. }
  204. else {// Exit event
  205. int i;
  206. for(i=0;i<size;i++) {
  207. jobs.push(NULL);
  208. }
  209. wait_threads();
  210. return false;
  211. }
  212. }
  213. // Single instance of prefork
  214. prefork *prefork::self;
  215. void prefork::parent_handler(int s_catched)
  216. {
  217. int i;
  218. int s;
  219. if(self->exit_flag==0) {
  220. self->exit_flag=1;
  221. s=SIGTERM;
  222. }
  223. else {
  224. s=SIGKILL;
  225. }
  226. signal(SIGALRM,parent_handler);
  227. alarm(3);
  228. for(i=0;i<self->procs;i++) {
  229. kill(self->pids[i],s);
  230. }
  231. }
  232. void prefork::chaild_handler(int s)
  233. {
  234. self->exit_flag=1;
  235. signal(SIGALRM,chaild_handler);
  236. alarm(1);
  237. }
  238. void prefork::run()
  239. {
  240. signal(SIGTERM,chaild_handler);
  241. base_factory &factory=*app.workers;
  242. shared_ptr<worker_thread> worker=factory(app);
  243. int res,post_on_throw;
  244. int limit=app.config.lval("server.iterations_limit",-1);
  245. if(limit!=-1) {
  246. srand(getpid());
  247. limit=limit+(limit / 10 *(rand() % 100))/100;
  248. }
  249. int counter=0;
  250. while(!exit_flag){
  251. if(limit!=-1 && counter>limit)
  252. return;
  253. counter++;
  254. res=sem_wait(semaphore);
  255. if(res<0){
  256. if(errno==EINTR)
  257. continue;
  258. else {
  259. perror("sem_wait");
  260. exit(1);
  261. }
  262. }
  263. cgi_session *session=NULL;
  264. try{
  265. post_on_throw=1;
  266. struct pollfd fds;
  267. fds.fd=app.api->get_socket();
  268. fds.revents=0;
  269. fds.events=POLLIN | POLLERR;
  270. if(poll(&fds,1,-1)==1 && (fds.revents & POLLIN)) {
  271. session=app.api->accept_session();
  272. }
  273. post_on_throw=0;
  274. sem_post(semaphore);
  275. if(session && session->prepare()) {
  276. worker->run(session->get_connection());
  277. }
  278. }
  279. catch(cppcms_error const &e){
  280. if(post_on_throw) sem_post(semaphore);
  281. cerr<<e.what();
  282. }
  283. catch(...){
  284. if(post_on_throw) sem_post(semaphore);
  285. exit(1);
  286. }
  287. delete session;
  288. }
  289. }
  290. prefork::prefork(manager &m) :
  291. web_application(m)
  292. {
  293. procs=app.config.lval("server.procs",5);
  294. exit_flag=0;
  295. pids.resize(procs);
  296. self=this;
  297. }
  298. void prefork::execute()
  299. {
  300. int i;
  301. void *mem=mmap(NULL,sizeof(sem_t),PROT_READ | PROT_WRITE,
  302. MAP_SHARED |MAP_ANONYMOUS,-1,0);
  303. if(mem==MAP_FAILED) {
  304. throw cppcms_error(errno,"mmap failed");
  305. }
  306. semaphore=(sem_t*)mem;
  307. sem_init(semaphore,1,1);
  308. for(i=0;i<procs;i++) {
  309. pid_t pid=fork();
  310. if(pid<0) {
  311. perror("fork:");
  312. int j;
  313. for(j=0;j<i;j++) {
  314. kill(pids[j],SIGKILL);
  315. wait(NULL);
  316. }
  317. exit(1);
  318. }
  319. if(pid>0) {
  320. pids[i]=pid;
  321. }
  322. else { // pid==0
  323. run();
  324. return;
  325. }
  326. }
  327. /* Signals defined by standard */
  328. signal(SIGTERM,parent_handler);
  329. signal(SIGUSR1,parent_handler);
  330. /* Additional signal */
  331. signal(SIGINT,parent_handler);
  332. while(!prefork::exit_flag) {
  333. int stat;
  334. pid_t pid=wait(&stat);
  335. if(pid<0) {
  336. continue;
  337. }
  338. if(exit_flag) break;
  339. for(i=0;i<procs;i++) {
  340. if(pids[i]==pid) {
  341. if(!WIFEXITED(stat) || WEXITSTATUS(stat)!=0){
  342. if(WIFEXITED(stat)) {
  343. cerr<<"Chaild "<<pid<<" exited with "<<WEXITSTATUS(stat)<<endl;
  344. }
  345. else if(WIFSIGNALED(stat)) {
  346. cerr<<"Chaild "<<pid<<" killed by "<<WTERMSIG(stat)<<endl;
  347. }
  348. else {
  349. cerr<<"Chaild "<<pid<<" exited for unknown reason"<<endl;
  350. }
  351. }
  352. pid=fork();
  353. if(pid==0) {
  354. run();
  355. return;
  356. }
  357. pids[i]=pid;
  358. break;
  359. }
  360. }
  361. }
  362. for(i=0;i<procs;i++) {
  363. while(wait(NULL)<0 && errno==EINTR)
  364. ;
  365. }
  366. sem_close(semaphore);
  367. munmap(mem,sizeof(sem_t));
  368. }
  369. } // END oF Details
  370. cache_factory *manager::get_cache_factory()
  371. {
  372. string backend=config.sval("cache.backend","none");
  373. if(backend=="none") {
  374. return new cache_factory();
  375. }
  376. else if(backend=="threaded") {
  377. int n=config.lval("cache.limit",100);
  378. return new thread_cache_factory(n);
  379. }
  380. #ifdef EN_FORK_CACHE
  381. else if(backend=="fork") {
  382. size_t s=config.lval("cache.memsize",64);
  383. string f=config.sval("cache.file","");
  384. return new process_cache_factory(s*1024U,f=="" ? NULL: f.c_str());
  385. }
  386. #endif
  387. #ifdef EN_TCP_CACHE
  388. else if(backend=="tcp") {
  389. vector<long> const &ports=config.llist("cache.ports");
  390. vector<string> const &ips=config.slist("cache.ips");
  391. return new tcp_cache_factory(ips,ports);
  392. }
  393. #endif
  394. else {
  395. throw cppcms_error("Unkown cache backend:" + backend);
  396. }
  397. }
  398. cgi_api *manager::get_api()
  399. {
  400. string api=config.sval("server.api");
  401. if(api=="cgi") {
  402. return new cgi_cgi_api();
  403. }
  404. string socket=config.sval("server.socket","");
  405. int backlog=config.lval("server.buffer",1);
  406. if(api=="scgi" ) {
  407. return new scgi_api(socket.c_str(),backlog);
  408. }
  409. #ifdef EN_FCGI_BACKEND
  410. if(api=="fastcgi"){
  411. return new fcgi_api(socket.c_str(),backlog);
  412. }
  413. #endif
  414. throw cppcms_error("Unknown api:"+api);
  415. }
  416. web_application *manager::get_mod()
  417. {
  418. if(config.sval("server.api","")=="cgi") {
  419. return new details::single_run(*this);
  420. }
  421. string mod=config.sval("server.mod");
  422. if(mod=="process") {
  423. return new details::fast_cgi_single_threaded_app(*this);
  424. }
  425. if(mod=="thread") {
  426. return new details::fast_cgi_multiple_threaded_app(*this);
  427. }
  428. if(mod=="prefork") {
  429. return new details::prefork(*this);
  430. }
  431. throw cppcms_error("Unknown mod:" + mod);
  432. }
  433. void manager::execute()
  434. {
  435. if(!cache.get()) {
  436. set_cache(get_cache_factory());
  437. }
  438. if(!api.get()) {
  439. set_api(get_api());
  440. }
  441. if(!web_app.get()) {
  442. set_mod(get_mod());
  443. }
  444. if(!gettext.get()){
  445. set_gettext(get_gettext());
  446. }
  447. if(!workers.get()) {
  448. throw cppcms_error("No workers factory set up");
  449. }
  450. load_templates();
  451. web_app->execute();
  452. }
  453. void manager::load_templates()
  454. {
  455. string ext=config.sval("templates.ext",
  456. #ifdef __CYGWIN__
  457. ".dll"
  458. #else
  459. ".so"
  460. #endif
  461. );
  462. // FIXME to something that works with autotools
  463. unsigned len=ext.size();
  464. vector<string> const &dirs=config.slist("templates.dirs");
  465. for(vector<string>::const_iterator dir=dirs.begin();dir!=dirs.end();++dir) {
  466. DIR *d=::opendir(dir->c_str());
  467. if(!d) continue;
  468. for(struct dirent *entry=::readdir(d);entry;entry=::readdir(d)) {
  469. string filename=entry->d_name;
  470. if( filename.size()>len &&
  471. filename.substr(filename.size()-len,len) == ext )
  472. {
  473. void *handler=::dlopen((*dir + "/" + filename).c_str(),RTLD_LAZY);
  474. if(handler) templates_list.push_back(handler);
  475. }
  476. }
  477. ::closedir(d);
  478. }
  479. }
  480. manager::~manager()
  481. {
  482. for_each(templates_list.begin(),templates_list.end(),::dlclose);
  483. }
  484. void manager::set_worker(base_factory *w)
  485. {
  486. workers=auto_ptr<base_factory>(w);
  487. }
  488. void manager::set_cache(cache_factory *c)
  489. {
  490. cache=auto_ptr<cache_factory>(c);
  491. }
  492. void manager::set_api(cgi_api *a)
  493. {
  494. api=auto_ptr<cgi_api>(get_api());
  495. }
  496. void manager::set_mod(web_application *m)
  497. {
  498. web_app=auto_ptr<web_application>(m);
  499. }
  500. transtext::trans_factory *manager::get_gettext()
  501. {
  502. transtext::trans_factory *tmp=NULL;
  503. try{
  504. tmp=new transtext::trans_factory();
  505. tmp->load( config.sval ("locale.dir",""),
  506. config.slist("locale.lang_list"),
  507. config.sval ("locale.lang_default",""),
  508. config.slist ("locale.domain_list"),
  509. config.sval ("locale.domain_default",""));
  510. return tmp;
  511. }
  512. catch(...){
  513. delete tmp;
  514. throw;
  515. }
  516. }
  517. void manager::set_gettext(transtext::trans_factory *s)
  518. {
  519. gettext=auto_ptr<transtext::trans_factory>(s);
  520. }
  521. manager::manager()
  522. {
  523. config.load(0,NULL);
  524. }
  525. manager::manager(char const *f)
  526. {
  527. config.load(0,NULL,f);
  528. }
  529. manager::manager(int argc, char **argv)
  530. {
  531. config.load(argc,argv);
  532. }
  533. } // END OF CPPCMS