/* * Copyright (C) 2019 Weber Yann * * This file is part of PyFCGI. * * PyFCGI is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * PyFCGI is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PyFCGI. If not, see . */ /**@defgroup worker_process Worker processes * @brief Processus handling python execution with @ref work() function * * This kind of process are @ref spawn() by a @ref work_master_proc * @section Running Python * Running embed python in FCGI mean serving multiple requests with a single * python process, meaning : * - environnement variable of the living Python process need to be updated * - we need a way to communicate with python code * * @subsection worker_proc_ipc Communicating with python * sys.stdout & sys.stderr will be used to communicate in python. Shortly * after python initialize @ref update_python_fd() update these two file * objects with freshly created pipes. * * @subsubsection worker_proc_ipc_piper The piper process * * When a request arrive, the worker process fork again in a @ref worker_piper() child * process that will read the two pipes. Sending stdout to FCGI using printf * and stderr to syslog. * * @subsection worker_proc_environ Updating os.environ * os.environ is a special kind of dictionnary, automatically calling * os.putenv() function. This function will attempt (and succeed) to update * the real process environnement, making a nasty conflict with libfcgi (trying * to free an environnement it did not create anymore...). * * The os.environ is replaced by a simple python dict by @ref update_pyenv() * once a request. * * @see update_python_path() * * @ingroup processes * @ingroup work_master_proc */ /**@file pyworker.h * @ingroup worker_process */ #ifndef _PYWORKER__H___ #define _PYWORKER__H___ #include "config.h" #include #include /* fcgi library; put it first*/ #define PY_SSIZE_T_CLEAN #include #include #include #include #include #include #include #include #include #include #include #include #include "logger.h" #define EXIT_PYERR 42 #define WPIPER_SIG 30 #define PYENTRY_FUNNAME "entrypoint" #define PIPER_STACK_SZ (1024 * 1024 * 4) //extern char **environ; typedef unsigned long int pywrkid_t; typedef struct piper_args_s piper_args_t; struct piper_args_s { int wrk_id, pystdout, pystderr, ctl_pipe; //int req_id; FCGX_Stream *out; struct sigaction *act; }; /**@todo TODO on all request (when updating env ?) update stdin so * python will be able to read the request content */ /**@brief the function that initialize the python worker * @ingroup worker_process * @param char* python_entrypoint a path to a python entrypoint * @param int worker uid * @param int semid for FCGI access * @param int max request before worker restart * @return 0 if exit avec max requests */ int work(int, int); /**@brief function for a subprocess designed to read stdin & stdout from * python & forward them to syslog or FCGI * @ingroup worker_process * @param int worker id * @param int request id * @param int pystdout * @param int pystderr * @param int ctlpipe a pipe closed when the process is ready * @note Exit after signal sent by parent & when poll indicate the pipes are * empty */ /* void worker_piper(int, int, int, int, int, FCGX_Stream*); */ int worker_piper(void*); /**@brief worker piper sigaction handler */ void worker_piper_sighandler(int); /**@brief Attempt to read the request size from ctl pipe * @param int ctl pipe read fd * @param size_t* rep_sz * @return 0 if no error */ int ctl_get_rep_sz(int, size_t*); /**@brief Import & return the python entrypoint callable * from PyFCGI_conf.py_entrymod & PyFCGI_conf.py_entryfun */ PyObject* import_entrypoint(); /**@brief Fetch stdout & stderr python flush() function * @param PyObject* pystdout_flush * @param PyObject* pystderr_flush */ void fetch_pyflush(PyObject**, PyObject**); /**@brief Add . to the embed pythonpath */ void update_python_path(); /**@brief Create two pipes for stdout & stderr * @ingroup worker_process * @params int[2] pystdout * @params int[2] pystderr */ void update_python_fd(int[2], int[2]); /**@brief Clear then update python sys.environ using current FCI environ * @ingroup worker_process * @note The environ has to be set without a call to os.putenv, the problem * is that the os.environ is a special mapping calling putenv on setitem... * For these reason the os.environ will be replaced by a new dict instance for * each request... * @param PyObject* os module */ void update_pyenv(PyObject*, char**); void log_expt(int priority); #endif