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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. /**@defgroup worker_process Worker processes
  20. * @brief Processus handling python execution with @ref work() function
  21. *
  22. * This kind of process are @ref spawn() by a @ref work_master_proc
  23. * @section Running Python
  24. * Running embed python in FCGI mean serving multiple requests with a single
  25. * python process, meaning :
  26. * - environnement variable of the living Python process need to be updated
  27. * - we need a way to communicate with python code
  28. *
  29. * @subsection worker_proc_ipc Communicating with python
  30. * sys.stdout & sys.stderr will be used to communicate in python. Shortly
  31. * after python initialize @ref update_python_fd() update these two file
  32. * objects with freshly created pipes.
  33. *
  34. * @subsubsection worker_proc_ipc_piper The piper process
  35. *
  36. * When a request arrive, the worker process fork again in a @ref worker_piper() child
  37. * process that will read the two pipes. Sending stdout to FCGI using printf
  38. * and stderr to syslog.
  39. *
  40. * @subsection worker_proc_environ Updating os.environ
  41. * os.environ is a special kind of dictionnary, automatically calling
  42. * os.putenv() function. This function will attempt (and succeed) to update
  43. * the real process environnement, making a nasty conflict with libfcgi (trying
  44. * to free an environnement it did not create anymore...).
  45. *
  46. * The os.environ is replaced by a simple python dict by @ref update_pyenv()
  47. * once a request.
  48. *
  49. * @see update_python_path()
  50. *
  51. * @ingroup processes
  52. * @ingroup work_master_proc
  53. */
  54. /**@file pyworker.h
  55. * @ingroup worker_process
  56. */
  57. #ifndef _PYWORKER__H___
  58. #define _PYWORKER__H___
  59. #include "config.h"
  60. #include <fcgiapp.h>
  61. #include <fcgi_stdio.h> /* fcgi library; put it first*/
  62. #define PY_SSIZE_T_CLEAN
  63. #include <Python.h>
  64. #include <unistd.h>
  65. #include <fcntl.h>
  66. #include <syslog.h>
  67. #include <signal.h>
  68. #include <limits.h>
  69. #include <poll.h>
  70. #include <sched.h>
  71. #include <sys/types.h>
  72. #include <sys/ipc.h>
  73. #include <sys/sem.h>
  74. #include <sys/wait.h>
  75. #include "logger.h"
  76. #include "pyutils.h"
  77. #define EXIT_PYERR 42
  78. #define WPIPER_SIG 30
  79. #define PIPER_STACK_SZ (1024 * 1024 * 4)
  80. //extern char **environ;
  81. typedef unsigned long int pywrkid_t;
  82. typedef struct piper_args_s piper_args_t;
  83. struct piper_args_s
  84. {
  85. int wrk_id, pystdout, pystderr, ctl_pipe;
  86. //int req_id;
  87. FCGX_Stream *out;
  88. struct sigaction *act;
  89. };
  90. /**@todo TODO on all request (when updating env ?) update stdin so
  91. * python will be able to read the request content */
  92. /**@brief the function that initialize the python worker
  93. * @ingroup worker_process
  94. * @param char* python_entrypoint a path to a python entrypoint
  95. * @param int worker uid
  96. * @param int semid for FCGI access
  97. * @param int max request before worker restart
  98. * @return 0 if exit avec max requests
  99. */
  100. int work(int, int);
  101. /**@brief function for a subprocess designed to read stdin & stdout from
  102. * python & forward them to syslog or FCGI
  103. * @ingroup worker_process
  104. * @param int worker id
  105. * @param int request id
  106. * @param int pystdout
  107. * @param int pystderr
  108. * @param int ctlpipe a pipe closed when the process is ready
  109. * @note Exit after signal sent by parent & when poll indicate the pipes are
  110. * empty
  111. */
  112. /*
  113. void worker_piper(int, int, int, int, int, FCGX_Stream*);
  114. */
  115. int worker_piper(void*);
  116. /**@brief worker piper sigaction handler
  117. */
  118. void worker_piper_sighandler(int);
  119. /**@brief Attempt to read the request size from ctl pipe
  120. * @param int ctl pipe read fd
  121. * @param size_t* rep_sz
  122. * @return 0 if no error
  123. */
  124. int ctl_get_rep_sz(int, size_t*);
  125. /**@brief Fetch stdout & stderr python flush() function
  126. * @param PyObject* pystdout_flush
  127. * @param PyObject* pystderr_flush
  128. */
  129. void fetch_pyflush(PyObject**, PyObject**);
  130. /**@brief Create two pipes for stdout & stderr
  131. * @ingroup worker_process
  132. * @params int[2] pystdout
  133. * @params int[2] pystderr
  134. */
  135. void update_python_fd(int[2], int[2]);
  136. /**@brief Clear then update python sys.environ using current FCI environ
  137. * @ingroup worker_process
  138. * @note The environ has to be set without a call to os.putenv, the problem
  139. * is that the os.environ is a special mapping calling putenv on setitem...
  140. * For these reason the os.environ will be replaced by a new dict instance for
  141. * each request...
  142. * @param PyObject* os module
  143. */
  144. void update_pyenv(PyObject*, char**);
  145. #endif