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.

conf.h 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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. #ifndef __CONF_H___
  20. #define __CONF_H___
  21. #include <fcgiapp.h>
  22. #include <fcgi_stdio.h> /* fcgi library; put it first*/
  23. #define PY_SSIZE_T_CLEAN
  24. #include <Python.h>
  25. #include "structmember.h"
  26. #include <unistd.h>
  27. #include <getopt.h>
  28. #include <limits.h>
  29. #include <semaphore.h>
  30. #include <sys/types.h>
  31. #include <sys/wait.h>
  32. #include "config.h"
  33. /**@file conf.h
  34. * @brief PyFCGI configuration handling headers
  35. * @ingroup conf_internal */
  36. /**@defgroup conf_internal PyFCGI configuration handling
  37. * @brief Informations about PyFCGI configuration handling
  38. */
  39. /**@defgroup conf_glob PYFCGI global (for all process) configurations
  40. * @see struct_pyfcgi_conf_s
  41. * @ingroup conf_internal */
  42. /**@brief Friendly name for @ref pyfcgi_conf_s
  43. * @see struct pyfcgi_conf_s
  44. * @ingroup conf_internal */
  45. typedef struct pyfcgi_conf_s pyfcgi_conf_t;
  46. typedef struct pyfcgi_conf_logger_s pyfcgi_conf_logger_t;
  47. typedef struct pyfcgi_context_s pyfcgi_context_t;
  48. typedef struct pyfcgi_semdata_s pyfcgi_semdata_t;
  49. typedef struct pyfcgi_shmdata_s pyfcgi_shmdata_t;
  50. /**@brief Configuration globals, inherited from parent process to childs
  51. * @ingroup conf_internal
  52. * @see pyfcgi_conf_s
  53. */
  54. pyfcgi_conf_t PyFCGI_conf;
  55. /* Include other PyFCGI stuff */
  56. #include "logger.h"
  57. #include "pyutils.h"
  58. #include "ipc.h"
  59. #include "monitor.h"
  60. /**@defgroup ret_status Function & process return status
  61. * @brief Constants
  62. * @ingroup proc_intern
  63. */
  64. /**@ingroup ret_status */
  65. #define EXIT_PYERR 4
  66. /**@ingroup ret_status */
  67. #define PYFCGI_TIMEOUT 8
  68. /**@ingroup ret_status */
  69. #define PYFCGI_ERR 16
  70. /**@ingroup ret_status */
  71. #define PYFCGI_WORKER_FAIL 32
  72. /**@ingroup ret_status */
  73. #define PYFCGI_MASTER_FAIL 64
  74. /**@ingroup ret_status */
  75. #define PYFCGI_FATAL 128
  76. /**@brief Backlog argument for socket creation */
  77. #define PYFCGI_SOCK_BACKLOG 100
  78. /**@brief Program name */
  79. #define PYFCGI_NAME "pyfcgi"
  80. /**@brief Short options specifications */
  81. #define PYFCGI_SHORT_OPT "C:l:e:E:Aw:W:m:ft:L:SPs:vVh"
  82. /**@brief Long options specifications */
  83. #define PYFCGI_LONG_OPT { \
  84. {"config", required_argument, 0, 'C'},\
  85. {"listen", required_argument, 0, 'l'},\
  86. {"pymodule", required_argument, 0, 'e'},\
  87. {"pyapp", required_argument, 0, 'E'},\
  88. {"alt-io", no_argument, 0, 'A'},\
  89. {"min-worker", required_argument, 0, 'w'},\
  90. {"max-worker", required_argument, 0, 'W'},\
  91. {"max-request", required_argument, 0, 'm'},\
  92. {"fast-spawn", no_argument, 0, 'f'},\
  93. {"timeout", required_argument, 0, 't'},\
  94. {"log", required_argument, 0, 'L'},\
  95. {"syslog", no_argument, 0, 'S'},\
  96. {"pid-file", required_argument, 0, 'P'},\
  97. {"socket-server", required_argument, 0, 's'},\
  98. {"verbose", no_argument, 0, 'v'},\
  99. {"version", no_argument, 0, 'V'},\
  100. {"help", no_argument, 0, 'h' },\
  101. {0, 0, 0, 0}\
  102. }
  103. /**@brief Data for help generation
  104. *
  105. * For each options {HELP_TEXT, ARGS_PLACEOLDER} are specified PLACEOLDER
  106. * is NULL for flags*/
  107. #define PYFCGI_OPT_HELP {\
  108. {"Load options from configuration file", "CONFIG"},\
  109. {"Listen socket path (a path for UNIX socket or a 'IPV4:PORT' string)", "SOCK_PATH"},\
  110. {"Search application function in given python module", "MODULE_NAME"},\
  111. {"Python application entrypoint function name", "FUNC_NAME"},\
  112. {"Use stdout to communicate with web server instead of entrypoint return as specified in PEP 333", NULL},\
  113. {"Minimum worker in the pool", "INT"},\
  114. {"Maximum worker in the pool", "INT"},\
  115. {"Request count after wich the worker is restarted (if 0 never restart)", "INT"},\
  116. {"If not given there is at least 1s between 2 child creation, else childs may be spawned in small burst", NULL},\
  117. {"Request timeout (before worker restarts)", "SECONDS"},\
  118. {"Add a logfile using syntax : 'LOGFILE[;FILT][;FMT]'", "LOGGER_SPEC"},\
  119. {"Use syslog for logging", NULL},\
  120. {"Create a PID file", "FILENAME"},\
  121. {"Indicate a socket (for example 'udp://localhost:8765' ) to listen on, replying stats & status", "SOCKET"},\
  122. {"Send all loglines on stderr", NULL},\
  123. {"Print PyFCGI and Python version and exit", NULL},\
  124. {"Display this help and exit", NULL},\
  125. }
  126. #define PYFCGI_HELP_TEXT "\n\
  127. Logger specification format 'LOGFILE[;FILT][;FMT]' with :\n\
  128. \t- LOGFILE the log file name\n\
  129. \t- FILT a number (in decimal or hexadicimal 0xXX) indicating wich\n\
  130. \t facility/level to log\n\
  131. \t- FMT the logline format in a special markup format using fields between { }\n\
  132. \t supported fields are :\n\
  133. \t\t- {datetime} {datetime:SIZE} {datetime:SIZE:FMT} defines a format and a \n\
  134. \t\t constant length for a datetime field. Default : {datetime:25:%F %T%z}\n\
  135. \t\t- {level} the loglevel \n\
  136. \t\t- {facility} the log facility\n\
  137. \t\t- {pid} the process PID\n\
  138. \t\t- {ident} the defined ident (set by process)\n\
  139. \t\t- {msg} the log message (can only appear once)\n\
  140. You can escape { and } by using {{ and }} and all field names can by\n\
  141. abbreviated to one character.\n\n\
  142. Socket URL specification format : PROT://HOST[:PORT] with PROT on of \n\
  143. \t- tcp for tcp sockets and HOST a valid INET address\n\
  144. \t- udp for udp sockets and HOST a valid INET address\n\
  145. \t- unix for file sockets and HOST a valid path\n\
  146. "
  147. #define PYENTRY_DEFAULT_FUN "application"
  148. /**@brief Stores a semaphore name and pointer */
  149. struct pyfcgi_semdata_s
  150. {
  151. /**@brief Semaphore names. Set by master process */
  152. char name[NAME_MAX - 4];
  153. /**@brief Semaphore pointer. */
  154. sem_t *sem;
  155. };
  156. /**@brief Stores an SHM name and pointer */
  157. struct pyfcgi_shmdata_s
  158. {
  159. /**@brief SHM name. Set by master process */
  160. char name[NAME_MAX];
  161. /**@brief SHM file descriptor */
  162. int fd;
  163. /**@brief SHM size */
  164. size_t len;
  165. /**@brief SHM pointer */
  166. void *ptr;
  167. };
  168. /**@brief Stores contextual informations about current process */
  169. struct pyfcgi_context_s {
  170. /**@brief Timestamp of master process starts */
  171. time_t uptime;
  172. /**@brief Stores current process PID */
  173. pid_t pid;
  174. /**@brief Stores parent process PID */
  175. pid_t ppid;
  176. /**@brief Returned by FCGX_OpenSocket for responder process */
  177. int fcgi_socket;
  178. /**@brief Request information (local to each worker) */
  179. FCGX_Request fcgi_request;
  180. /**@brief Stores the watchdog timer */
  181. timer_t wd_timer;
  182. /**@brief Security timer sending a sigkill */
  183. timer_t wd_timerkill;
  184. /**@brief Watchdog delay */
  185. struct timespec wd_delay;
  186. /**@brief SIGKILL watchdog delay */
  187. struct timespec wd_killdelay;
  188. /**@brief Watchdog sig restorer */
  189. struct sigaction wd_oldsig;
  190. /**@brief watchdog flag */
  191. short wd;
  192. /**@brief array of worker pids (pool handler context) */
  193. pid_t *wrk_pids;
  194. /**@brief workers count */
  195. unsigned int n_wrk;
  196. /**@brief Stores IPC flag used in init */
  197. pyfcgi_ipc_flag_t ipc_flag;
  198. /**@brief Stores python_path (not dupped by python) */
  199. wchar_t python_path[PATH_MAX];
  200. /**@brief Stores venv_path for python home (not dupped by python) */
  201. wchar_t venv_path[PATH_MAX];
  202. /**@brief Stores a part of the environ (containing wsgi.* keys) */
  203. PyObject *wsgi_dict;
  204. };
  205. /**@brief Structure containing configuration
  206. * @ingroup conf_internal
  207. * The structure is used for the global @ref PyFCGI_conf variable.
  208. * @see pyfcgi_conf_s
  209. */
  210. struct pyfcgi_conf_s
  211. {
  212. /** @brief Stores pidfile path */
  213. char *pidfile;
  214. /**@brief Stores the socket path argument
  215. * @ingroup conf_glob
  216. * Can be a file path for a UNIX socket or IPv4:PORT string */
  217. char *sock_path;
  218. /**@brief Entrypoint module name
  219. * @ingroup conf_glob */
  220. char *py_entrymod;
  221. /**@brief Entrypoint function name
  222. * @ingroup conf_glob */
  223. char *py_entryfun;
  224. /**@brief If 0 use stdout to communicate with webserver, else
  225. * PyFCGI will expect PEP333 compliant entrypoint */
  226. short pep333;
  227. /**@brief Minimum count worker in pool
  228. * @ingroup conf_glob */
  229. int min_wrk;
  230. /**@brief Maximum count workers in pool
  231. * @ingroup conf_glob */
  232. int max_wrk;
  233. /**@brief Maximum request before a worker restarts (0 for no restart)
  234. * @ingroup conf_glob */
  235. int max_reqs;
  236. //Pool handling conf
  237. /**@brief Idle timeout : starts GC workers after Xs idle */
  238. time_t worker_gc_timeout;
  239. /**@brief Fast spawn : if 1 spawn more childs faster
  240. *
  241. * When not activated there is at least 1s between 2 child creation,
  242. * else child are created in small burst ( ~2 childs burst)
  243. */
  244. short worker_fast_spawn;
  245. //watchdogs config
  246. /**@brief Worker timeout in seconds (if 0 no timeout)*/
  247. time_t worker_timeout;
  248. /**@brief Pool timeout in seconds (if 0 no timeout)*/
  249. time_t pool_timeout;
  250. // logger config
  251. /**@brief 0 is silent 1 means logs to stderr */
  252. short verbosity;
  253. /**@brief Logger configuration
  254. * @ingroup conf_glob */
  255. pyfcgi_conf_logger_t logs;
  256. /**@brief Context informations */
  257. pyfcgi_context_t context;
  258. /**@brief Semaphores informations
  259. *
  260. * - sems[0] is the worker state indicator : when a worker is idle the
  261. * sem is incremented
  262. * - sems[1] is the request counter sem : when a request is handled
  263. * this sem is incremented
  264. * - sems[2] is the stats/status SHM semaphore
  265. */
  266. pyfcgi_semdata_t sems[PYFCGI_NSEM];
  267. /**@brief Shared memory informations */
  268. pyfcgi_shmdata_t shm;
  269. /**@brief Stores a copy of the specified socket URL to listen to. */
  270. char *mon_socket;
  271. /**@brief If 1 force ipv4 */
  272. short ipv4;
  273. /**@brief If 1 force ipv6 */
  274. short ipv6;
  275. };
  276. /**@brief Print usage on FD 2 (stdout) */
  277. void usage();
  278. /**@brief Print pyfcgi & python version on given fd */
  279. void print_version(int);
  280. /**@brief Init conf with default values */
  281. void default_conf();
  282. /**@brief Parse arguments and store them in conf
  283. * @return 0 if no error */
  284. int parse_args(int argc, char *argv[]);
  285. /**@brief Check Python entrypoint import
  286. *
  287. * Forks into a childprocess that will start python and import the entrypoint.
  288. * The child process exit status indicate if the import was successfull or not
  289. * @return 0 if no errors 1 if import fails
  290. */
  291. int check_entrypoint_import();
  292. /**@brief Parse a logger declaration option
  293. * Expected format is : LOGFILE[;FILTER][;FMT]
  294. * @param logspec The logspec option value
  295. * @return 0 if no error else 1
  296. */
  297. int parse_optlog(const char* logspec);
  298. /**@defgroup pyfcgi_watchdog PyFCGI watchdogs
  299. * @brief Child process watchdogs mecanism
  300. *
  301. * @ingroup proc_intern
  302. */
  303. /**@brief Initialize the watchdog
  304. * @ingroup pyfcgi_watchdog
  305. * @param wd_sig_cleaner pointer on a signal handler (or NULL to
  306. * use @ref pyfcgi_wd_default_sighandler
  307. * @param delay a pointer on the watchdog timeout
  308. * @return 0 if no error else -1
  309. */
  310. int pyfcgi_wd_init(void (*wd_sig_cleaner)(int), const struct timespec *delay);
  311. /**@brief (Re)starts watchdog timer
  312. * @ingroup pyfcgi_watchdog
  313. *@return 0 if no error else -1 */
  314. int pyfcgi_wd_arm();
  315. /**@brief Temporarly disables watchdog
  316. * @ingroup pyfcgi_watchdog
  317. *@return 0 if no error else -1 */
  318. int pyfcgi_wd_pause();
  319. /**@brief Stop and delete a watchdog
  320. * @ingroup pyfcgi_watchdog
  321. * @return 0 if no error else -1*/
  322. int pyfcgi_wd_stop();
  323. /**@brief Watchdog default signal handler
  324. * @ingroup pyfcgi_watchdog
  325. * @param signum signal number*/
  326. void pyfcgi_wd_default_sighandler(int signum);
  327. /**@brief Return a string representing a status
  328. * @param status Status
  329. * @return a char* that should be freed
  330. */
  331. char *status2str(int status);
  332. void pyfcgi_sighandler_drop(int signum);
  333. #endif