PyFCGI now handles fcgi socket creation

Add a -l --listen option to set the socket path or address:port
Fix a bug using thread safe functions from libfcgi
This commit is contained in:
Yann Weber 2019-10-28 17:16:15 +01:00
commit f685f83369
7 changed files with 70 additions and 11 deletions

6
README
View file

@ -9,10 +9,10 @@ Building & running pyfcgi :
$ ./configure $ ./configure
$ make $ make
# To run foo_pep333.entrypoint() PEP333 application # To run foo_pep333.entrypoint() PEP333 application
$ spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint $ src/pyfcgi -l '127.0.0.1:9000' -S -e foo_pep333 -E entrypoint
or or
# To run foo.entrypoint() sending to FCGI python stdout # To run foo.entrypoint() sending to FCGI python stdout
$ spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo -E entrypoint -A $ src/pyfcgi -l '127.0.0.1:9000' -S -e foo -E entrypoint -A
configure script determine python flags, libs & includes paths using configure script determine python flags, libs & includes paths using
@ -23,7 +23,7 @@ Example : linking against a debug build of python :
--------- ---------
$ ./configure PYTHON_CONFIG_PATH=/usr/bin/python3dm-config --enable-debug $ ./configure PYTHON_CONFIG_PATH=/usr/bin/python3dm-config --enable-debug
$ make clean && make $ make clean && make
$ valgrind --log-file=/tmp/val.log --trace-children=yes spawn-fcgi -d . -n -p 9000 -a 127.0.0.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo.log;0xff;{datetime} {msg} {ident}' $ valgrind --log-file=/tmp/val.log --trace-children=yes src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo.log;0xff;{datetime} {msg} {ident}'
logging to file example : logging to file example :
------------------------- -------------------------

View file

@ -1,4 +1,4 @@
.TH "pyfcgi" 1 "Sat Aug 24 2019" "Version 0.0.1" "PyFCGI" \" -*- nroff -*- .TH "pyfcgi" 1 "Mon Oct 28 2019" "Version 0.0.1" "PyFCGI" \" -*- nroff -*-
.ad l .ad l
.nh .nh
.SH NAME .SH NAME
@ -24,11 +24,16 @@ Display help and exit
Display pyfcgi and Python version and exit Display pyfcgi and Python version and exit
.RE .RE
.PP .PP
\fB-c --config=FILE\fP \fB-C --config=FILE\fP
.RS 4 .RS 4
load a configuration file load a configuration file
.RE .RE
.PP .PP
\fB-l --listen=SOCK_PATH\fP
.RS 4
fcgi listen socket path\&. For TCP socket use 'IPv4:PORT' syntax ( '127\&.0\&.0\&.1:9000' by default)
.RE
.PP
\fB-e --pymodule=MODULE_NAME\fP \fB-e --pymodule=MODULE_NAME\fP
.RS 4 .RS 4
python entrypoint module name python entrypoint module name
@ -134,11 +139,11 @@ unix with HOST a valid PATH
.PP .PP
To run foo_pep333\&.entrypoint() PEP333 application : To run foo_pep333\&.entrypoint() PEP333 application :
.PP .PP
spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S \\ -e foo_pep333 -E entrypoint spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint
.PP .PP
Logfile example : Logfile example :
.PP .PP
spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S \\ -e foo_pep333 -E entrypoint \\ -L '/tmp/foo\&.log;0xff;{datetime} {msg} {ident}' spawn-fcgi -d \&. -n -p 9000 -a 127\&.0\&.0\&.1 -- src/pyfcgi -S -e foo_pep333 -E entrypoint -L '/tmp/foo\&.log;0xff;{datetime} {msg} {ident}'
.SH "AUTHOR" .SH "AUTHOR"
.PP .PP
Written by Yann Weber <yann.weber@member.fsf.org> Written by Yann Weber <yann.weber@member.fsf.org>

View file

@ -74,12 +74,16 @@ pyfcgi_conf_t PyFCGI_conf;
/**@ingroup ret_status */ /**@ingroup ret_status */
#define PYFCGI_FATAL 128 #define PYFCGI_FATAL 128
/**@brief Backlog argument for socket creation */
#define PYFCGI_SOCK_BACKLOG 100
#define PYFCGI_NAME "spawn-fcgi [OPTIONS] -- pyfcgi" #define PYFCGI_NAME "spawn-fcgi [OPTIONS] -- pyfcgi"
#define PYFCGI_SHORT_OPT "Ce:E:Aw:W:m:ft:L:SPs:vVh" #define PYFCGI_SHORT_OPT "C:l:e:E:Aw:W:m:ft:L:SPs:vVh"
#define PYFCGI_LONG_OPT { \ #define PYFCGI_LONG_OPT { \
{"config", required_argument, 0, 'C'},\ {"config", required_argument, 0, 'C'},\
{"listen", required_argument, 0, 'l'},\
{"pymodule", required_argument, 0, 'e'},\ {"pymodule", required_argument, 0, 'e'},\
{"pyapp", required_argument, 0, 'E'},\ {"pyapp", required_argument, 0, 'E'},\
{"alt-io", no_argument, 0, 'A'},\ {"alt-io", no_argument, 0, 'A'},\
@ -100,6 +104,7 @@ pyfcgi_conf_t PyFCGI_conf;
#define PYFCGI_OPT_HELP {\ #define PYFCGI_OPT_HELP {\
{"Load options from configuration file", "CONFIG"},\ {"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"},\ {"Search application function in given python module", "MODULE_NAME"},\
{"Python application entrypoint function name", "FUNC_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},\ {"Use stdout to communicate with web server instead of entrypoint return as specified in PEP 333", NULL},\
@ -176,6 +181,12 @@ struct pyfcgi_context_s {
/**@brief Stores parent process PID */ /**@brief Stores parent process PID */
pid_t ppid; 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 */ /**@brief Stores the watchdog timer */
timer_t wd_timer; timer_t wd_timer;
/**@brief Security timer sending a sigkill */ /**@brief Security timer sending a sigkill */
@ -215,6 +226,10 @@ struct pyfcgi_conf_s
/** @brief Stores pidfile path */ /** @brief Stores pidfile path */
char *pidfile; 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 /**@brief Entrypoint module name
* @ingroup conf_glob */ * @ingroup conf_glob */
char *py_entrymod; char *py_entrymod;

View file

@ -40,6 +40,7 @@ void print_version(int fd)
void default_conf() void default_conf()
{ {
memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t)); memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t));
PyFCGI_conf.sock_path = "127.0.0.1:9000";
PyFCGI_conf.context.pid = getpid(); PyFCGI_conf.context.pid = getpid();
PyFCGI_conf.min_wrk = 1; PyFCGI_conf.min_wrk = 1;
PyFCGI_conf.max_wrk = 5; PyFCGI_conf.max_wrk = 5;
@ -72,6 +73,9 @@ int parse_args(int argc, char *argv[])
case 'C': case 'C':
dprintf(2, "Config parser not yet implemented :'(\n"); dprintf(2, "Config parser not yet implemented :'(\n");
exit(1); exit(1);
case 'l':
PyFCGI_conf.sock_path = strdup(optarg);
break;
case 'e': case 'e':
PyFCGI_conf.py_entrymod = strdup(optarg); PyFCGI_conf.py_entrymod = strdup(optarg);
break; break;

View file

@ -362,8 +362,11 @@ if ($programname contains 'pyfcgi') then {
* Display help and exit * Display help and exit
* @par -V --version * @par -V --version
* Display pyfcgi and Python version and exit * Display pyfcgi and Python version and exit
* @par -c --config=FILE * @par -C --config=FILE
* load a configuration file * load a configuration file
* @par -l --listen=SOCK_PATH
* fcgi listen socket path. For TCP socket use "IPv4:PORT" syntax (
* "127.0.0.1:9000" by default)
* @par -e --pymodule=MODULE_NAME * @par -e --pymodule=MODULE_NAME
* python entrypoint module name * python entrypoint module name
* @par -E --pyapp=FUNC_NAME * @par -E --pyapp=FUNC_NAME

View file

@ -46,6 +46,7 @@ int work333(int wrk_id)
int count, pipe_out[2], pipe_err[2]; int count, pipe_out[2], pipe_err[2];
int max_reqs; int max_reqs;
struct timeval start, stop; struct timeval start, stop;
FCGX_Request *request;
max_reqs = PyFCGI_conf.max_reqs; max_reqs = PyFCGI_conf.max_reqs;
@ -72,13 +73,28 @@ int work333(int wrk_id)
start_response = get_start_response(); start_response = get_start_response();
// Initialise FCGI request
request = &(PyFCGI_conf.context.fcgi_request);
if(FCGX_InitRequest(request, PyFCGI_conf.context.fcgi_socket,
FCGI_FAIL_ACCEPT_ON_INTR) == -1)
{
pyfcgi_log(LOG_ALERT, "Unable to init FCGI request");
exit(PYFCGI_FATAL);
}
_worker_idle = 0; _worker_idle = 0;
worker_set_idle(); worker_set_idle();
// requests accepting loop // requests accepting loop
count = 0; count = 0;
while ((!count || count != max_reqs) && while ((!count || count != max_reqs) &&
FCGX_Accept(&in_stream, &out_stream, &err_stream, &envp) >= 0) FCGX_Accept_r(request) == 0)
{ {
in_stream = request->in;
out_stream = request->out;
err_stream = request->err;
envp = request->envp;
pyfcgi_wd_arm(); pyfcgi_wd_arm();
sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF
gettimeofday(&start, NULL); gettimeofday(&start, NULL);
@ -136,7 +152,7 @@ int work333(int wrk_id)
FCGX_FClose(out_stream); FCGX_FClose(out_stream);
FCGX_FClose(in_stream); FCGX_FClose(in_stream);
FCGX_FClose(err_stream); FCGX_FClose(err_stream);
FCGI_Finish(); FCGX_Finish_r(request);
gettimeofday(&stop, NULL); gettimeofday(&stop, NULL);
stop.tv_sec = stop.tv_sec - start.tv_sec; stop.tv_sec = stop.tv_sec - start.tv_sec;

View file

@ -92,6 +92,22 @@ void init_context()
} }
bzero(PyFCGI_conf.context.wrk_pids, bzero(PyFCGI_conf.context.wrk_pids,
sizeof(pid_t) * (PyFCGI_conf.max_wrk + 1)); sizeof(pid_t) * (PyFCGI_conf.max_wrk + 1));
// Open FCGI listen socket
PyFCGI_conf.context.fcgi_socket = FCGX_OpenSocket(PyFCGI_conf.sock_path,
PYFCGI_SOCK_BACKLOG);
if(PyFCGI_conf.context.fcgi_socket == -1)
{
pyfcgi_log(LOG_ALERT, "Unable to open socket : '%s'",
PyFCGI_conf.sock_path);
clean_exit(PYFCGI_FATAL);
}
if(FCGX_Init() != 0)
{
pyfcgi_log(LOG_ALERT, "Unable to init libfcgi");
clean_exit(PYFCGI_FATAL);
}
pyfcgi_log(LOG_INFO, "Listening on %s", PyFCGI_conf.sock_path);
} }
int responder_loop() int responder_loop()