diff --git a/README b/README index 82c0d4b..1109fdc 100644 --- a/README +++ b/README @@ -9,10 +9,10 @@ Building & running pyfcgi : $ ./configure $ make # 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 # 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 @@ -23,7 +23,7 @@ Example : linking against a debug build of python : --------- $ ./configure PYTHON_CONFIG_PATH=/usr/bin/python3dm-config --enable-debug $ 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 : ------------------------- diff --git a/docs/man/man1/pyfcgi.1 b/docs/man/man1/pyfcgi.1 index 02ce046..7372b33 100644 --- a/docs/man/man1/pyfcgi.1 +++ b/docs/man/man1/pyfcgi.1 @@ -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 .nh .SH NAME @@ -24,11 +24,16 @@ Display help and exit Display pyfcgi and Python version and exit .RE .PP -\fB-c --config=FILE\fP +\fB-C --config=FILE\fP .RS 4 load a configuration file .RE .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 .RS 4 python entrypoint module name @@ -134,11 +139,11 @@ unix with HOST a valid PATH .PP To run foo_pep333\&.entrypoint() PEP333 application : .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 Logfile example : .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" .PP Written by Yann Weber diff --git a/include/conf.h b/include/conf.h index 6695520..943007f 100644 --- a/include/conf.h +++ b/include/conf.h @@ -74,12 +74,16 @@ pyfcgi_conf_t PyFCGI_conf; /**@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 "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 { \ {"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'},\ @@ -100,6 +104,7 @@ pyfcgi_conf_t PyFCGI_conf; #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},\ @@ -176,6 +181,12 @@ struct pyfcgi_context_s { /**@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 */ @@ -215,6 +226,10 @@ 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; diff --git a/src/conf.c b/src/conf.c index c49eaed..6c8e5bb 100644 --- a/src/conf.c +++ b/src/conf.c @@ -40,6 +40,7 @@ void print_version(int fd) void default_conf() { memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t)); + PyFCGI_conf.sock_path = "127.0.0.1:9000"; PyFCGI_conf.context.pid = getpid(); PyFCGI_conf.min_wrk = 1; PyFCGI_conf.max_wrk = 5; @@ -72,6 +73,9 @@ int parse_args(int argc, char *argv[]) case 'C': dprintf(2, "Config parser not yet implemented :'(\n"); exit(1); + case 'l': + PyFCGI_conf.sock_path = strdup(optarg); + break; case 'e': PyFCGI_conf.py_entrymod = strdup(optarg); break; diff --git a/src/main.c b/src/main.c index 39e9b83..c4620bd 100644 --- a/src/main.c +++ b/src/main.c @@ -362,8 +362,11 @@ if ($programname contains 'pyfcgi') then { * Display help and exit * @par -V --version * Display pyfcgi and Python version and exit - * @par -c --config=FILE + * @par -C --config=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 * python entrypoint module name * @par -E --pyapp=FUNC_NAME diff --git a/src/pyworker.c b/src/pyworker.c index dcb70ef..6f85be2 100644 --- a/src/pyworker.c +++ b/src/pyworker.c @@ -46,6 +46,7 @@ int work333(int wrk_id) int count, pipe_out[2], pipe_err[2]; int max_reqs; struct timeval start, stop; + FCGX_Request *request; max_reqs = PyFCGI_conf.max_reqs; @@ -72,13 +73,28 @@ int work333(int wrk_id) 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_set_idle(); // requests accepting loop count = 0; 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(); sem_post(PyFCGI_SEM(SEM_WREQS).sem); // increment request counter discarding OF gettimeofday(&start, NULL); @@ -136,7 +152,7 @@ int work333(int wrk_id) FCGX_FClose(out_stream); FCGX_FClose(in_stream); FCGX_FClose(err_stream); - FCGI_Finish(); + FCGX_Finish_r(request); gettimeofday(&stop, NULL); stop.tv_sec = stop.tv_sec - start.tv_sec; diff --git a/src/responder.c b/src/responder.c index 45d7370..bd63ad4 100644 --- a/src/responder.c +++ b/src/responder.c @@ -92,6 +92,22 @@ void init_context() } bzero(PyFCGI_conf.context.wrk_pids, 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()