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.h 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. #define EXIT_PYERR 42
  76. #define WPIPER_SIG 30
  77. #define PYENTRY_FUNNAME "entrypoint"
  78. #define PIPER_STACK_SZ (1024 * 1024 * 4)
  79. //extern char **environ;
  80. typedef unsigned long int pywrkid_t;
  81. typedef struct piper_args_s piper_args_t;
  82. struct piper_args_s
  83. {
  84. int wrk_id, pystdout, pystderr, ctl_pipe;
  85. //int req_id;
  86. FCGX_Stream *out;
  87. struct sigaction *act;
  88. };
  89. /**@todo TODO on all request (when updating env ?) update stdin so
  90. * python will be able to read the request content */
  91. /**@brief the function that initialize the python worker
  92. * @ingroup worker_process
  93. * @param char* python_entrypoint a path to a python entrypoint
  94. * @param int worker uid
  95. * @param int semid for FCGI access
  96. * @param int max request before worker restart
  97. * @return 0 if exit avec max requests
  98. */
  99. int work(char*, int, int, int);
  100. /**@brief function for a subprocess designed to read stdin & stdout from
  101. * python & forward them to syslog or FCGI
  102. * @ingroup worker_process
  103. * @param int worker id
  104. * @param int request id
  105. * @param int pystdout
  106. * @param int pystderr
  107. * @param int ctlpipe a pipe closed when the process is ready
  108. * @note Exit after signal sent by parent & when poll indicate the pipes are
  109. * empty
  110. */
  111. /*
  112. void worker_piper(int, int, int, int, int, FCGX_Stream*);
  113. */
  114. int worker_piper(void*);
  115. /**@brief worker piper sigaction handler
  116. */
  117. void worker_piper_sighandler(int);
  118. /**@brief Attempt to read the request size from ctl pipe
  119. * @param int ctl pipe read fd
  120. * @param size_t* rep_sz
  121. * @return 0 if no error
  122. */
  123. int ctl_get_rep_sz(int, size_t*);
  124. /**@brief Import & return the python entrypoint callable
  125. */
  126. PyObject* import_entrypoint(char* py_entrypoint);
  127. /**@brief Fetch stdout & stderr python flush() function
  128. * @param PyObject* pystdout_flush
  129. * @param PyObject* pystderr_flush
  130. */
  131. void fetch_pyflush(PyObject**, PyObject**);
  132. /**@brief Add . to the embed pythonpath
  133. */
  134. void update_python_path();
  135. /**@brief Create two pipes for stdout & stderr
  136. * @ingroup worker_process
  137. * @params int[2] pystdout
  138. * @params int[2] pystderr
  139. */
  140. void update_python_fd(int[2], int[2]);
  141. /**@brief Clear then update python sys.environ using current FCI environ
  142. * @ingroup worker_process
  143. * @note The environ has to be set without a call to os.putenv, the problem
  144. * is that the os.environ is a special mapping calling putenv on setitem...
  145. * For these reason the os.environ will be replaced by a new dict instance for
  146. * each request...
  147. * @param PyObject* os module
  148. */
  149. void update_pyenv(PyObject*, char**);
  150. void log_expt(int priority);
  151. #endif