/*
* 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 .
*/
#ifndef __CONF_H___
#define __CONF_H___
#include
#include /* fcgi library; put it first*/
#define PY_SSIZE_T_CLEAN
#include
#include "structmember.h"
#include
#include
#include
#include
#include
#include
#include "config.h"
/**@defgroup conf_internal PYFCGI configuration handling
*/
/**@defgroup conf_glob PYFCGI global (for all process) configurations
* @see struct_pyfcgi_conf_s
* @ingroup conf_internal */
/**@brief Friendly name for @ref struct pyfcgi_conf_s
* @see struct pyfcgi_conf_s */
typedef struct pyfcgi_conf_s pyfcgi_conf_t;
typedef struct pyfcgi_conf_logger_s pyfcgi_conf_logger_t;
typedef struct pyfcgi_context_s pyfcgi_context_t;
typedef struct pyfcgi_semdata_s pyfcgi_semdata_t;
typedef struct pyfcgi_shmdata_s pyfcgi_shmdata_t;
/**@brief Configuration globals, inherited from parent to childs
* @ingroup conf_internal
*/
pyfcgi_conf_t PyFCGI_conf;
/* Include other PyFCGI stuff */
#include "logger.h"
#include "pyutils.h"
#include "ipc.h"
#include "monitor.h"
/**@defgroup ret_status Function & process return status
*/
/**@ingroup ret_status */
#define EXIT_PYERR 4
/**@ingroup ret_status */
#define PYFCGI_TIMEOUT 8
/**@ingroup ret_status */
#define PYFCGI_ERR 16
/**@ingroup ret_status */
#define PYFCGI_WORKER_FAIL 32
/**@ingroup ret_status */
#define PYFCGI_MASTER_FAIL 64
/**@ingroup ret_status */
#define PYFCGI_FATAL 128
/**@brief Backlog argument for socket creation */
#define PYFCGI_SOCK_BACKLOG 100
#define PYFCGI_NAME "spawn-fcgi [OPTIONS] -- pyfcgi"
#define PYFCGI_SHORT_OPT "C:l:e:E:Aw:W:m:ft:L:SPs:vVh"
#define PYFCGI_LONG_OPT { \
{"config", required_argument, 0, 'C'},\
{"listen", required_argument, 0, 'l'},\
{"pymodule", required_argument, 0, 'e'},\
{"pyapp", required_argument, 0, 'E'},\
{"alt-io", no_argument, 0, 'A'},\
{"min-worker", required_argument, 0, 'w'},\
{"max-worker", required_argument, 0, 'W'},\
{"max-request", required_argument, 0, 'm'},\
{"fast-spawn", no_argument, 0, 'f'},\
{"timeout", required_argument, 0, 't'},\
{"log", required_argument, 0, 'L'},\
{"syslog", no_argument, 0, 'S'},\
{"pid-file", required_argument, 0, 'P'},\
{"socket-server", required_argument, 0, 's'},\
{"verbose", no_argument, 0, 'v'},\
{"version", no_argument, 0, 'V'},\
{"help", no_argument, 0, 'h' },\
{0, 0, 0, 0}\
}
#define PYFCGI_OPT_HELP {\
{"Load options from configuration file", "CONFIG"},\
{"Listen socket path (a path for UNIX socket or a 'IPV4:PORT' string)", "SOCK_PATH"},\
{"Search application function in given python module", "MODULE_NAME"},\
{"Python application entrypoint function name", "FUNC_NAME"},\
{"Use stdout to communicate with web server instead of entrypoint return as specified in PEP 333", NULL},\
{"Minimum worker in the pool", "INT"},\
{"Maximum worker in the pool", "INT"},\
{"Request count after wich the worker is restarted (if 0 never restart)", "INT"},\
{"If not given there is at least 1s between 2 child creation, else childs may be spawned in small burst", NULL},\
{"Request timeout (before worker restarts)", "SECONDS"},\
{"Add a logfile using syntax : 'LOGFILE[;FILT][;FMT]'", "LOGGER_SPEC"},\
{"Use syslog for logging", NULL},\
{"Create a PID file", "FILENAME"},\
{"Indicate a socket (for example 'udp://localhost:8765' ) to listen on, replying stats & status", "SOCKET"},\
{"Send all loglines on stderr", NULL},\
{"Print PyFCGI and Python version and exit", NULL},\
{"Display this help and exit", NULL},\
}
#define PYFCGI_HELP_TEXT "\n\
Logger specification format 'LOGFILE[;FILT][;FMT]' with :\n\
\t- LOGFILE the log file name\n\
\t- FILT a number (in decimal or hexadicimal 0xXX) indicating wich\n\
\t facility/level to log\n\
\t- FMT the logline format in a special markup format using fields between { }\n\
\t supported fields are :\n\
\t\t- {datetime} {datetime:SIZE} {datetime:SIZE:FMT} defines a format and a \n\
\t\t constant length for a datetime field. Default : {datetime:25:%F %T%z}\n\
\t\t- {level} the loglevel \n\
\t\t- {facility} the log facility\n\
\t\t- {pid} the process PID\n\
\t\t- {ident} the defined ident (set by process)\n\
\t\t- {msg} the log message (can only appear once)\n\
You can escape { and } by using {{ and }} and all field names can by\n\
abbreviated to one character.\n\n\
Socket URL specification format : PROT://HOST[:PORT] with PROT on of \n\
\t- tcp for tcp sockets and HOST a valid INET address\n\
\t- udp for udp sockets and HOST a valid INET address\n\
\t- unix for file sockets and HOST a valid path\n\
"
#define PYENTRY_DEFAULT_FUN "application"
/**@brief Stores a semaphore name and pointer */
struct pyfcgi_semdata_s
{
/**@brief Semaphore names. Set by master process */
char name[NAME_MAX - 4];
/**@brief Semaphore pointer. */
sem_t *sem;
};
/**@brief Stores an SHM name and pointer */
struct pyfcgi_shmdata_s
{
/**@brief SHM name. Set by master process */
char name[NAME_MAX];
/**@brief SHM file descriptor */
int fd;
/**@brief SHM size */
size_t len;
/**@brief SHM pointer */
void *ptr;
};
/**@brief Stores contextual informations about current process */
struct pyfcgi_context_s {
/**@brief Timestamp of master process starts */
time_t uptime;
/**@brief Stores current process PID */
pid_t pid;
/**@brief Stores parent process PID */
pid_t ppid;
/**@brief Returned by FCGX_OpenSocket for responder process */
int fcgi_socket;
/**@brief Request information (local to each worker) */
FCGX_Request fcgi_request;
/**@brief Stores the watchdog timer */
timer_t wd_timer;
/**@brief Security timer sending a sigkill */
timer_t wd_timerkill;
/**@brief Watchdog delay */
struct timespec wd_delay;
/**@brief SIGKILL watchdog delay */
struct timespec wd_killdelay;
/**@brief Watchdog sig restorer */
struct sigaction wd_oldsig;
/**@brief watchdog flag */
short wd;
/**@brief array of worker pids (pool handler context) */
pid_t *wrk_pids;
/**@brief workers count */
unsigned int n_wrk;
/**@brief Stores IPC flag used in init */
pyfcgi_ipc_flag_t ipc_flag;
/**@brief Stores python_path (not dupped by python) */
wchar_t python_path[PATH_MAX];
/**@brief Stores venv_path for python home (not dupped by python) */
wchar_t venv_path[PATH_MAX];
/**@brief Stores a part of the environ (containing wsgi.* keys) */
PyObject *wsgi_dict;
};
/**@brief Structure containing configuration
* @ingroup conf_internal
* The structure is used for the global @ref pyfcgi_conf variable.
* @see pyfcgi_conf_t
*/
struct pyfcgi_conf_s
{
/** @brief Stores pidfile path */
char *pidfile;
/**@brief Stores the socket path argument
* @ingroup conf_glob
* Can be a file path for a UNIX socket or IPv4:PORT string */
char *sock_path;
/**@brief Entrypoint module name
* @ingroup conf_glob */
char *py_entrymod;
/**@brief Entrypoint function name
* @ingroup conf_glob */
char *py_entryfun;
/**@brief If 0 use stdout to communicate with webserver, else
* PyFCGI will expect PEP333 compliant entrypoint */
short pep333;
/**@brief Minimum count worker in pool
* @ingroup conf_glob */
int min_wrk;
/**@brief Maximum count workers in pool
* @ingroup conf_glob */
int max_wrk;
/**@brief Maximum request before a worker restarts (0 for no restart)
* @ingroup conf_glob */
int max_reqs;
//Pool handling conf
/**@brief Idle timeout : starts GC workers after Xs idle */
time_t worker_gc_timeout;
/**@brief Fast spawn : if 1 spawn more childs faster
*
* When not activated there is at least 1s between 2 child creation,
* else child are created in small burst ( ~2 childs burst)
*/
short worker_fast_spawn;
//watchdogs config
/**@brief Worker timeout in seconds (if 0 no timeout)*/
time_t worker_timeout;
/**@brief Pool timeout in seconds (if 0 no timeout)*/
time_t pool_timeout;
// logger config
/**@brief 0 is silent 1 means logs to stderr */
short verbosity;
/**@brief Logger configuration
* @ingroupe conf_glob */
pyfcgi_conf_logger_t logs;
/**@brief Context informations */
pyfcgi_context_t context;
/**@brief Semaphores informations
*
* - sems[0] is the worker state indicator : when a worker is idle the
* sem is incremented
* - sems[1] is the request counter sem : when a request is handled
* this sem is incremented
* - sems[2] is the stats/status SHM semaphore
*/
pyfcgi_semdata_t sems[PYFCGI_NSEM];
/**@brief Shared memory informations */
pyfcgi_shmdata_t shm;
/**@brief Stores a copy of the specified socket URL to listen to. */
char *mon_socket;
/**@brief If 1 force ipv4 */
short ipv4;
/**@brief If 1 force ipv6 */
short ipv6;
};
/**@brief Print usage on FD 2 (stdout) */
void usage();
/**@brief Print pyfcgi & python version on given fd */
void print_version(int);
/**@brief Init conf with default values */
void default_conf();
/**@brief Parse arguments and store them in conf
* @return 0 if no error */
int parse_args(int argc, char *argv[]);
int check_entrypoint_import();
int parse_optlog(const char*);
/**@brief Initialize the watchdog
* @param void (*wd_sig_cleaner)(int) pointer on a signal handler (or NULL to
* use @ref pyfcgi_default_sighandler
* @param const struct timespec* delay a pointer on the watchdog timeout
* @return 0 if no error else -1
*/
int pyfcgi_wd_init(void (*wd_sig_cleaner)(int), const struct timespec *delay);
int pyfcgi_wd_arm();
int pyfcgi_wd_pause();
int pyfcgi_wd_stop();
/**@brief Watchdog default signal handler */
void pyfcgi_wd_default_sighandler(int signum);
/**@brief Return a string representing a status
* @param int status
* @return a char* that should be freed
*/
char *status2str(int);
void pyfcgi_sighandler_drop(int signum);
#endif