Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
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.

pyworker.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*
  2. * Copyright (C) 2019 Weber Yann
  3. *
  4. * This file is part of PyFCGI.
  5. *
  6. * PyFCGI is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * any later version.
  10. *
  11. * PyFCGI is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PyFCGI. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "pyworker.h"
  20. static short _wtimeout;
  21. /**@brief 1 if worker idle else 0 */
  22. static short _worker_idle;
  23. /**@brief Indicate that a worker is idle */
  24. static inline void worker_set_idle();
  25. /**@brief Indicate that a worker is busy */
  26. static inline void worker_set_busy();
  27. /**@brief Process results from a pep333 worker
  28. * @param FCGX_Stream* out stream from libFCGI
  29. * @param PyObject* application function returned value
  30. */
  31. static inline int work333_send_result(FCGX_Stream*, PyObject* ret);
  32. static int worker_piper_sigrcv = 0;
  33. int work333(int wrk_id)
  34. {
  35. PyObject *entry_fun, *pyflush[2], *py_osmod, *entry_ret, *environ,
  36. *start_response, *args;
  37. FCGX_Stream *in_stream, *out_stream, *err_stream;
  38. char **envp;
  39. int count, pipe_out[2], pipe_err[2];
  40. int max_reqs;
  41. struct timeval start, stop;
  42. max_reqs = PyFCGI_conf.max_reqs;
  43. pyfcgi_log(LOG_INFO, "Worker started with PEP333 App");
  44. pyinit();
  45. pyfcgi_log(LOG_DEBUG, "Python started");
  46. update_python_fd(pipe_out, pipe_err);
  47. fetch_pyflush(&(pyflush[0]), &(pyflush[1]));
  48. //importing os
  49. py_osmod = python_osmod();
  50. // loading module
  51. entry_fun = import_entrypoint();
  52. pyfcgi_log(LOG_INFO, "Waiting request with %s.%s()",
  53. PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
  54. if(!entry_fun) //but exit if import failed
  55. {
  56. pyfcgi_log(LOG_ALERT, "Unable to import entrypoint");
  57. exit(PYFCGI_FATAL);
  58. }
  59. start_response = get_start_response();
  60. _worker_idle = 0;
  61. worker_set_idle();
  62. // requests accepting loop
  63. count = 0;
  64. while ((!count || count != max_reqs) &&
  65. FCGX_Accept(&in_stream, &out_stream, &err_stream, &envp) >= 0)
  66. {
  67. pyfcgi_wd_arm();
  68. sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
  69. gettimeofday(&start, NULL);
  70. worker_set_busy();
  71. count++;
  72. environ = update_pyenv(py_osmod, envp);
  73. _wtimeout = 0;
  74. libpyfcgi_clean_response();
  75. libpyfcgi.out = out_stream;
  76. libpyfcgi.in = in_stream;
  77. args = Py_BuildValue("OO", environ, start_response);
  78. // Call application function
  79. entry_ret = PyObject_CallObject(entry_fun, args);
  80. if(entry_ret && entry_ret != Py_None )
  81. {
  82. Py_INCREF(entry_ret);
  83. }
  84. if(_wtimeout)
  85. {
  86. PyObject *type, *exc, *tb, *tmp;
  87. // Changing exception to a TimeoutError, but keeping
  88. // the traceback to display
  89. PyErr_Fetch(&type, &exc, &tb);
  90. PyErr_SetString(PyExc_TimeoutError, "Request timeout");
  91. PyErr_Fetch(&type, &exc, &tmp);
  92. PyErr_Restore(type, exc, tb);
  93. log_expt(LOG_ERR);
  94. libpyfcgi_timeout(); // Sending headers if possible
  95. }
  96. if(PyErr_Occurred())
  97. {
  98. pyfcgi_log(LOG_ERR, "PEP333 entrypoint function triggered an exception");
  99. log_expt(LOG_ERR);
  100. }
  101. // able to process returned value
  102. // Simulate python call of libpyfcgi.write_body()
  103. if(entry_ret && entry_ret != Py_None)
  104. {
  105. _pyfcgi_write_body(entry_ret);
  106. if(PyErr_Occurred())
  107. {
  108. log_expt(LOG_ERR);
  109. }
  110. Py_DECREF(entry_ret);
  111. }
  112. // clean stuffs
  113. Py_DECREF(args);
  114. Py_DECREF(environ);
  115. // flush & logs pystdout & pystderr
  116. worker_log_pipes(pipe_out[0], pipe_err[0], pyflush);
  117. FCGX_FClose(out_stream);
  118. FCGX_FClose(in_stream);
  119. FCGX_FClose(err_stream);
  120. FCGI_Finish();
  121. gettimeofday(&stop, NULL);
  122. stop.tv_sec = stop.tv_sec - start.tv_sec;
  123. stop.tv_usec = stop.tv_usec - start.tv_usec;
  124. if(stop.tv_usec < 0)
  125. {
  126. stop.tv_usec += 1000000;
  127. stop.tv_sec -= 1;
  128. }
  129. pyfcgi_wd_pause();
  130. if(_wtimeout)
  131. {
  132. exit(PYFCGI_TIMEOUT);
  133. }
  134. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d END [OK] %lu bytes in %ld.%06lds",
  135. wrk_id, count, libpyfcgi.rep_sz, stop.tv_sec, stop.tv_usec);
  136. worker_set_idle();
  137. }
  138. worker_set_busy();
  139. return 0;
  140. }
  141. static inline int work333_send_result(FCGX_Stream *out, PyObject* ret)
  142. {
  143. return 0;
  144. }
  145. int work(int wrk_id)
  146. {
  147. PyObject *entry_fun, *pystdout_flush, *pystderr_flush, *py_osmod,
  148. *environ;
  149. FCGX_Stream *in_stream, *out_stream, *err_stream;
  150. char **envp;
  151. int count, pipe_out[2], pipe_err[2], pipe_ctl[2], err, piper_status;
  152. int max_reqs;
  153. struct sigaction act;
  154. struct timeval start, stop;
  155. sigset_t emptyset;
  156. char buf[PIPE_BUF];
  157. size_t rep_sz;
  158. piper_args_t piper_args;
  159. char *piper_stack;
  160. max_reqs = PyFCGI_conf.max_reqs;
  161. piper_args.wrk_id = wrk_id;
  162. piper_args.act = &act;
  163. // preparing sigaction for piper
  164. if(sigemptyset(&emptyset))
  165. {
  166. err = errno;
  167. pyfcgi_log( LOG_ALERT, "sigemptyset fails in piper : %s",
  168. strerror(err));
  169. exit(err);
  170. }
  171. act.sa_handler = worker_piper_sighandler;
  172. act.sa_mask = emptyset;
  173. //act.sa_flags = SA_RESTART;
  174. act.sa_flags = 0;
  175. act.sa_restorer = NULL;
  176. if( !(piper_stack = malloc(PIPER_STACK_SZ)) )
  177. {
  178. err = errno;
  179. pyfcgi_log(LOG_ALERT, "Error while allocating piper stack : %s",
  180. strerror(err));
  181. exit(err);
  182. }
  183. pyfcgi_log(LOG_INFO, "Worker %d started", wrk_id);
  184. update_python_path(); // add cwd to python path
  185. Py_Initialize(); // "start" python
  186. update_python_fd(pipe_out, pipe_err);
  187. piper_args.pystdout = pipe_out[0];
  188. piper_args.pystderr = pipe_err[0];
  189. fetch_pyflush(&pystdout_flush, &pystderr_flush);
  190. pyfcgi_log( LOG_INFO,
  191. "Worker[%d] Python started", wrk_id);
  192. //importing os
  193. py_osmod = python_osmod();
  194. // loading module
  195. entry_fun = import_entrypoint();
  196. pyfcgi_log( LOG_INFO,
  197. "Worker[%d] Waiting request with %s.%s()", wrk_id,
  198. PyFCGI_conf.py_entrymod, PyFCGI_conf.py_entryfun);
  199. if(!entry_fun) //but exit if import failed
  200. {
  201. pyfcgi_log(LOG_ALERT, "Unable to import entrypoint");
  202. exit(PYFCGI_FATAL);
  203. }
  204. if(pipe(pipe_ctl) == -1)
  205. {
  206. err = errno;
  207. pyfcgi_log(LOG_ALERT,
  208. "Worker[%d] Pipe fails for piper ctl : %s",
  209. wrk_id, strerror(err));
  210. exit(err);
  211. }
  212. piper_args.ctl_pipe = pipe_ctl[1];
  213. _worker_idle = 0;
  214. worker_set_idle();
  215. count = 0;
  216. while ((!count || count != max_reqs) && FCGX_Accept(&in_stream, &out_stream, &err_stream, &envp) >= 0)
  217. {
  218. pyfcgi_wd_arm();
  219. sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
  220. gettimeofday(&start, NULL);
  221. worker_set_busy();
  222. count++;
  223. piper_args.out = out_stream;
  224. //piper_args.req_id = count;
  225. worker_piper_sigrcv = 0;
  226. // if(!PyFCGI_conf.pep333)
  227. /**@todo avoid clone on each request... (using named pipes ?) */
  228. pid_t pid = clone(worker_piper, piper_stack + PIPER_STACK_SZ - 1,
  229. CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | \
  230. SIGCHLD | \
  231. CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_VM,
  232. &piper_args);
  233. if(pid < 0)
  234. {
  235. err = errno;
  236. pyfcgi_log(LOG_ALERT,
  237. "Worker[%d] req #%d Fork failed for piper : %s",
  238. wrk_id, count, strerror(err));
  239. exit(err);
  240. }
  241. environ = update_pyenv(py_osmod, envp);
  242. Py_DECREF(environ);
  243. //close(pipe_ctl[1]);
  244. PyObject_CallObject(entry_fun, NULL);
  245. if(PyErr_Occurred())
  246. {
  247. log_expt(LOG_ERR);
  248. }
  249. else
  250. {
  251. /*
  252. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d funcall [OK]",
  253. wrk_id, count);
  254. */
  255. }
  256. PyObject_CallObject(pystdout_flush, NULL);
  257. PyObject_CallObject(pystderr_flush, NULL);
  258. read(pipe_ctl[0], &buf, 1); // unblock when child ready
  259. FCGX_FClose(in_stream);
  260. FCGX_FClose(err_stream);
  261. //close(pipe_ctl[0]);
  262. kill(pid, WPIPER_SIG); //indicate child python call ended
  263. waitpid(pid, &piper_status, 0);
  264. if(WIFSIGNALED(piper_status))
  265. {
  266. pyfcgi_log(LOG_ERR,
  267. "Woker[%d] req #%d piper terminated by sig %d",
  268. wrk_id, count, WTERMSIG(piper_status));
  269. exit(40);
  270. }
  271. else if(WEXITSTATUS(piper_status))
  272. {
  273. pyfcgi_log(LOG_ERR,
  274. "Woker[%d] req #%d piper exited with error status %d",
  275. wrk_id, count, WEXITSTATUS(piper_status));
  276. exit(41);
  277. }
  278. FCGI_Finish();
  279. if(ctl_get_rep_sz(pipe_ctl[0], &rep_sz))
  280. {
  281. rep_sz = 0;
  282. }
  283. //Increase sem showing the worker is idle
  284. worker_set_idle();
  285. gettimeofday(&stop, NULL);
  286. stop.tv_sec = stop.tv_sec - start.tv_sec;
  287. stop.tv_usec = stop.tv_usec - start.tv_usec;
  288. if(stop.tv_usec < 0)
  289. {
  290. stop.tv_usec += 1000000;
  291. stop.tv_sec -= 1;
  292. }
  293. pyfcgi_wd_pause();
  294. pyfcgi_log(LOG_DEBUG, "Worker[%d] request %d END [OK] %lu bytes in %ld.%06lds",
  295. wrk_id, count, rep_sz, stop.tv_sec, stop.tv_usec);
  296. }
  297. worker_set_busy();
  298. free(piper_stack);
  299. Py_Exit(count == max_reqs ?0:EXIT_PYERR);
  300. }
  301. /*
  302. void worker_piper(int wrk_id, int req_id, int pystdout, int pystderr,
  303. int ctl_pipe, FCGX_Stream* out)
  304. */
  305. int worker_piper(void *ptr)
  306. {
  307. piper_args_t *args;
  308. struct pollfd fds[2];
  309. int err, ret, cont, poll_ret;
  310. short revents, closed, nfds;
  311. char buf[PIPE_BUF];
  312. size_t out_sz;
  313. args = ptr;
  314. int wrk_id, pystdout, pystderr, ctl_pipe;
  315. //int req_id;
  316. FCGX_Stream *out;
  317. struct sigaction *act;
  318. wrk_id = args->wrk_id;
  319. //req_id = args->req_id;
  320. pystdout = args->pystdout;
  321. pystderr = args->pystderr;
  322. ctl_pipe = args->ctl_pipe;
  323. out = args->out;
  324. act = args->act;
  325. if(sigaction(WPIPER_SIG, act, NULL))
  326. {
  327. err = errno;
  328. pyfcgi_log( LOG_ALERT, "Unable to sigaction in piper : %s",
  329. strerror(err));
  330. exit(err);
  331. }
  332. write(ctl_pipe, " ", 1);
  333. //pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d piper", wrk_id, req_id);
  334. fds[0].fd = pystderr;
  335. fds[1].fd = pystdout;
  336. fds[0].events = fds[1].events = POLLIN;
  337. fds[0].revents = fds[1].revents = 0;
  338. nfds = 2;
  339. closed = out_sz = 0;
  340. cont = 2;
  341. while(cont)
  342. {
  343. poll_ret = poll(fds, nfds, 0);
  344. //pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d poll_ret = %d", wrk_id, req_id, poll_ret);
  345. if(poll_ret < 0)
  346. {
  347. err = errno;
  348. if(err == EINTR)
  349. {
  350. continue;
  351. }
  352. pyfcgi_log( LOG_WARNING, "Poll error : %s",
  353. strerror(err));
  354. fds[0].revents = fds[1].revents = 0;
  355. continue;
  356. }
  357. if(!poll_ret && worker_piper_sigrcv)
  358. {
  359. cont--;
  360. continue;
  361. }
  362. if(poll_ret && (revents = fds[1].revents))
  363. {
  364. /*
  365. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d STDOUT evt !",
  366. wrk_id, req_id);
  367. */
  368. poll_ret--;
  369. if(revents & POLLIN)
  370. {
  371. /*
  372. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d POLLIN STDOUT !",
  373. wrk_id, req_id);
  374. */
  375. ret = read(pystdout, buf, PIPE_BUF);
  376. /*
  377. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d read(stdout) ret %d",
  378. wrk_id, req_id, ret);
  379. */
  380. if(ret < 0)
  381. {
  382. err = errno;
  383. if(err == EINTR)
  384. {
  385. continue;
  386. }
  387. pyfcgi_log( LOG_ERR,
  388. "Error reading python stdout : %s",
  389. strerror(err));
  390. exit(err);
  391. }
  392. //buf[ret] = '\0';
  393. ret = FCGX_PutStr(buf, ret, out);
  394. out_sz += ret;
  395. if(ret < PIPE_BUF && worker_piper_sigrcv)
  396. {
  397. FCGX_FClose(out);
  398. FCGI_Finish();
  399. write(ctl_pipe, &out_sz, sizeof(size_t));
  400. closed = 1;
  401. nfds = 1;
  402. }
  403. }
  404. //TODO handle other poll events
  405. }
  406. if(poll_ret && (revents = fds[0].revents))
  407. {
  408. /*
  409. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d STDERR evt !",
  410. wrk_id, req_id);
  411. */
  412. if(revents & POLLIN)
  413. {
  414. /*
  415. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d POLLIN STDERR !",
  416. wrk_id, req_id);
  417. */
  418. while(1)
  419. {
  420. ret = read(pystderr, buf, PIPE_BUF);
  421. if(ret < 0)
  422. {
  423. err = errno;
  424. if(err == EINTR)
  425. {
  426. continue;
  427. }
  428. pyfcgi_log( LOG_ERR,
  429. "Error reading python stdout : %s",
  430. strerror(err));
  431. exit(err);
  432. }
  433. buf[ret] = '\0';
  434. pyfcgi_log(LOG_ERR,
  435. "Worker[%d] Python error : %s",
  436. wrk_id, buf);
  437. break;
  438. }
  439. }
  440. //TODO handle other poll events
  441. }
  442. fds[0].revents = fds[1].revents = 0;
  443. }
  444. /*
  445. pyfcgi_log(LOG_DEBUG, "Worker[%d] req #%d piper exiting...",
  446. wrk_id, req_id);
  447. */
  448. if(!closed)
  449. {
  450. FCGX_FClose(out);
  451. FCGI_Finish();
  452. write(ctl_pipe, &out_sz, sizeof(size_t));
  453. }
  454. return 0;
  455. }
  456. void worker_log_pipes(int pipe_std, int pipe_err, PyObject *flush[2])
  457. {
  458. size_t ret;
  459. char buff[4096];
  460. int i, poll_ret;
  461. int loglevels[2] = {LOG_INFO, LOG_WARNING};
  462. struct pollfd fds[2];
  463. short isflush[2] = {0,0};
  464. char *outnames[2] = {"sys.stdout", "sys.stderr"};
  465. fds[0].fd = pipe_std;
  466. fds[1].fd = pipe_err;
  467. fds[0].events = fds[1].events = POLLIN;
  468. fds[0].revents = fds[1].revents = 0;
  469. for(i=0; i<2; i++)
  470. {
  471. do
  472. {
  473. poll_ret = poll(&(fds[i]), 1, 0);
  474. if(poll_ret)
  475. {
  476. ret = read(pipe_std, buff, 4096);
  477. if(ret == -1)
  478. {
  479. pyfcgi_log(LOG_ERR, "Error reading python %s pipe : %s",
  480. outnames[i], strerror(errno));
  481. exit(PYFCGI_WORKER_FAIL);
  482. }
  483. pyfcgi_log(loglevels[i], "Pyton %s : '%s'",
  484. outnames[i], buff);
  485. }
  486. if(!isflush[i] && (!poll_ret || ret < 4096))
  487. {
  488. isflush[i] = 1;
  489. PyObject_CallObject(flush[i], NULL);
  490. if(PyErr_Occurred())
  491. {
  492. pyfcgi_log(LOG_ERR, "Exception while flushing %s",
  493. outnames[i]);
  494. log_expt(LOG_ERR);
  495. exit(PYFCGI_WORKER_FAIL);
  496. }
  497. }
  498. }while(poll_ret && !isflush[i]);
  499. }
  500. }
  501. void worker_piper_sighandler(int signum)
  502. {
  503. worker_piper_sigrcv = 1;
  504. //pyfcgi_log(LOG_DEBUG, "SIG");
  505. }
  506. int ctl_get_rep_sz(int ctl_pipe, size_t* rep_sz)
  507. {
  508. struct pollfd fds;
  509. int ret, err;
  510. fds.fd = ctl_pipe;
  511. fds.events = POLLIN;
  512. fds.revents = 0;
  513. if( (ret = poll(&fds, 1, 0)) < 0)
  514. {
  515. err = errno;
  516. pyfcgi_log(LOG_ERR, "Failed to poll ctl pipe : %s",
  517. strerror(err));
  518. return -1;
  519. }
  520. if(!ret)
  521. {
  522. pyfcgi_log(LOG_ERR, "No data in ctl pipe for rep_sz...");
  523. return -1;
  524. }
  525. ret = read(ctl_pipe, rep_sz, sizeof(size_t));
  526. if(ret < 0)
  527. {
  528. pyfcgi_log(LOG_ERR, "Error reading ctl pipe : %s",
  529. strerror(errno));
  530. return -1;
  531. }
  532. if(ret < sizeof(size_t))
  533. {
  534. pyfcgi_log(LOG_ERR, "Incomplete read from ctl pipe, no rep_sz...");
  535. return -1;
  536. }
  537. return 0;
  538. }
  539. static void worker_set_idle()
  540. {
  541. int err;
  542. if(_worker_idle) { return; }
  543. if(sem_post(PyFCGI_SEM(SEM_WSTATE).sem) < 0)
  544. {
  545. err = errno;
  546. pyfcgi_log(LOG_ERR, "error incrementing the WSTATE semaphore : %s",
  547. strerror(err));
  548. return;
  549. }
  550. _worker_idle = 1;
  551. }
  552. static void worker_set_busy()
  553. {
  554. int err;
  555. if(!_worker_idle) { return; }
  556. /**@todo The pool handler WILL decrement the sem to figure if the pool
  557. * is busy -__- Using sem_wait make sure that the worker will be able
  558. * to set busy, but it can also freeze if not able to set busy....
  559. * sem_timedwait require to get abstime -_- The better way is
  560. * maybe to nanosleep 0.01s and retry a sem_trywait... SysV sem are
  561. * better T_T
  562. */
  563. #ifdef DEBUG
  564. if(sem_trywait(PyFCGI_SEM(SEM_WSTATE).sem) < 0)
  565. #else
  566. if(sem_wait(PyFCGI_SEM(SEM_WSTATE).sem) < 0)
  567. #endif
  568. {
  569. err = errno;
  570. if(err == EAGAIN)
  571. { //panic
  572. pyfcgi_log(LOG_ALERT, "Unable to set busy ! WSTATE sem is allready 0 !!!");
  573. _worker_idle = 0;
  574. return;
  575. }
  576. pyfcgi_log(LOG_ERR, "error decrementing the WSTATE semaphore : %s",
  577. strerror(err));
  578. return;
  579. }
  580. _worker_idle = 0;
  581. }
  582. void worker_sighandler(int signum)
  583. {
  584. pyfcgi_log(LOG_INFO, "%s signal received, exiting...", strsignal(signum));
  585. worker_set_busy();
  586. pyfcgi_IPC_close(IPC_WSTATE | IPC_WREQS);
  587. exit(0);
  588. }
  589. void worker_sigalrmhandler(int signum)
  590. {
  591. _wtimeout = 1;
  592. if(!_worker_idle)
  593. {
  594. // Call PyErr_Interrupt() in order to stop python
  595. PyErr_SetInterrupt();
  596. }
  597. worker_set_busy();
  598. pyfcgi_IPC_close(IPC_WSTATE | IPC_WREQS);
  599. return;
  600. }