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 16KB

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