From 5cb25a2d32df8073df7c191dd6c7694b4495af62 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 4 Feb 2021 15:18:45 +0100 Subject: Fix CLI server worker support If we create separate listening sockets in each worker using SO_REUSEADDR, then an incoming connection may be load-balanced to a process that is already busy, either due to a long-running request, or because it is a recursive request (in which case we would deadlock). Instead, only create one listening socket, and only create worker forks afterwards. This way the incoming request will be served by one of the workers that is currently listening for an incoming connection. --- sapi/cli/php_cli_server.c | 106 ++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 59 deletions(-) (limited to 'sapi/cli') diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index a32b51e3d3..4fe975d6a4 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -498,58 +498,7 @@ const zend_function_entry server_additional_functions[] = { static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */ { - char *workers; - - if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) { - return FAILURE; - } - - if ((workers = getenv("PHP_CLI_SERVER_WORKERS"))) { -#ifndef SO_REUSEPORT - fprintf(stderr, "platform does not support SO_REUSEPORT, cannot create workers\n"); -#elif HAVE_FORK - ZEND_ATOL(php_cli_server_workers_max, workers); - - if (php_cli_server_workers_max > 1) { - zend_long php_cli_server_worker; - - php_cli_server_workers = calloc( - php_cli_server_workers_max, sizeof(pid_t)); - if (!php_cli_server_workers) { - php_cli_server_workers_max = 1; - - return SUCCESS; - } - - php_cli_server_master = getpid(); - - for (php_cli_server_worker = 0; - php_cli_server_worker < php_cli_server_workers_max; - php_cli_server_worker++) { - pid_t pid = fork(); - - if (pid == FAILURE) { - /* no more forks allowed, work with what we have ... */ - php_cli_server_workers_max = - php_cli_server_worker + 1; - return SUCCESS; - } else if (pid == SUCCESS) { - return SUCCESS; - } else { - php_cli_server_workers[ - php_cli_server_worker - ] = pid; - } - } - } else { - fprintf(stderr, "number of workers must be larger than 1\n"); - } -#else - fprintf(stderr, "forking is not supported on this platform\n"); -#endif - } - - return SUCCESS; + return php_module_startup(sapi_module, &cli_server_module_entry, 1); } /* }}} */ static size_t sapi_cli_server_ub_write(const char *str, size_t str_length) /* {{{ */ @@ -1317,13 +1266,6 @@ static php_socket_t php_network_listen_socket(const char *host, int *port, int s } #endif -#if defined(HAVE_FORK) && defined(SO_REUSEPORT) - if (php_cli_server_workers_max > 1) { - int val = 1; - setsockopt(retval, SOL_SOCKET, SO_REUSEPORT, (char*)&val, sizeof(val)); - } -#endif - if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) { err = php_socket_errno(); if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) { @@ -2412,6 +2354,50 @@ static char *php_cli_server_parse_addr(const char *addr, int *pport) { return pestrndup(addr, end - addr, 1); } +static void php_cli_server_startup_workers() { + char *workers = getenv("PHP_CLI_SERVER_WORKERS"); + if (!workers) { + return; + } + +#if HAVE_FORK + ZEND_ATOL(php_cli_server_workers_max, workers); + if (php_cli_server_workers_max > 1) { + zend_long php_cli_server_worker; + + php_cli_server_workers = calloc( + php_cli_server_workers_max, sizeof(pid_t)); + if (!php_cli_server_workers) { + php_cli_server_workers_max = 1; + return; + } + + php_cli_server_master = getpid(); + + for (php_cli_server_worker = 0; + php_cli_server_worker < php_cli_server_workers_max; + php_cli_server_worker++) { + pid_t pid = fork(); + + if (pid == FAILURE) { + /* no more forks allowed, work with what we have ... */ + php_cli_server_workers_max = + php_cli_server_worker + 1; + return; + } else if (pid == SUCCESS) { + return; + } else { + php_cli_server_workers[php_cli_server_worker] = pid; + } + } + } else { + fprintf(stderr, "number of workers must be larger than 1\n"); + } +#else + fprintf(stderr, "forking is not supported on this platform\n"); +#endif +} + static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router) /* {{{ */ { int retval = SUCCESS; @@ -2441,6 +2427,8 @@ static int php_cli_server_ctor(php_cli_server *server, const char *addr, const c } server->server_sock = server_sock; + php_cli_server_startup_workers(); + err = php_cli_server_poller_ctor(&server->poller); if (SUCCESS != err) { goto out; -- cgit v1.2.1