diff --git a/NEWS b/NEWS index ab372893ce661..993bdda130cd9 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS with a given skeleton, locale, collapse type and identity fallback. (BogdanUngureanu) +- Opcache: + . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during + preloading). (Arnaud, welcomycozyhom) + - Standard: . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 9ef74973a4dff..e6a2b90e8fffc 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4524,16 +4524,6 @@ static void preload_load(size_t orig_map_ptr_static_last) } } - if (EG(zend_constants)) { - EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; - } - if (EG(function_table)) { - EG(persistent_functions_count) = EG(function_table)->nNumUsed; - } - if (EG(class_table)) { - EG(persistent_classes_count) = EG(class_table)->nNumUsed; - } - size_t old_map_ptr_last = CG(map_ptr_last); if (zend_map_ptr_static_last != ZCSG(map_ptr_static_last) || old_map_ptr_last != ZCSG(map_ptr_last)) { CG(map_ptr_last) = ZCSG(map_ptr_last); @@ -4803,6 +4793,12 @@ static zend_result accel_preload(const char *config, bool in_child) preload_load(orig_map_ptr_static_last); + /* Update persistent counts, as shutdown will discard anything past + * that, and these tables are aliases to global ones at this point. */ + EG(persistent_functions_count) = EG(function_table)->nNumUsed; + EG(persistent_classes_count) = EG(class_table)->nNumUsed; + EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; + /* Store individual scripts with unlinked classes */ HANDLE_BLOCK_INTERRUPTIONS(); SHM_UNPROTECT(); @@ -5094,13 +5090,45 @@ static zend_result accel_finish_startup(void) exit(ret == SUCCESS ? 0 : 1); } else { /* parent */ - int status; +# ifdef HAVE_SIGPROCMASK + /* Interrupting the waitpid() call below with a signal would cause the + * process to exit. This is fine when the signal disposition is set to + * terminate the process, but not otherwise. + * When running the apache2handler, preloading is performed in the + * control process. SIGUSR1 and SIGHUP are used to tell the control + * process to restart children. Exiting when these signals are received + * would unexpectedly shutdown the server instead of restarting it. + * Block the USR1 and HUP signals from being delivered during the + * syscall when running the apache2handler SAPI, as these are not + * supposed to terminate the process. See GH-20051. */ + bool is_apache2handler = strcmp(sapi_module.name, "apache2handler") == 0; + sigset_t set, oldset; + if (is_apache2handler) { + if (sigemptyset(&set) + || sigaddset(&set, SIGUSR1) + || sigaddset(&set, SIGHUP)) { + ZEND_UNREACHABLE(); + } + if (sigprocmask(SIG_BLOCK, &set, &oldset)) { + ZEND_UNREACHABLE(); + } + } +# endif + int status; if (waitpid(pid, &status, 0) < 0) { zend_shared_alloc_unlock(); zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid); } +# ifdef HAVE_SIGPROCMASK + if (is_apache2handler) { + if (sigprocmask(SIG_SETMASK, &oldset, NULL)) { + ZEND_UNREACHABLE(); + } + } +# endif + if (ZCSG(preload_script)) { preload_load(zend_map_ptr_static_last); } diff --git a/sapi/fpm/fpm/zlog.c b/sapi/fpm/fpm/zlog.c index 803a768234bd6..b34d455e6bd6c 100644 --- a/sapi/fpm/fpm/zlog.c +++ b/sapi/fpm/fpm/zlog.c @@ -466,7 +466,7 @@ static ssize_t zlog_stream_buf_flush(struct zlog_stream *stream) /* {{{ */ } #endif - if (external_logger != NULL) { + if (stream->use_external_logger) { external_logger(stream->flags & ZLOG_LEVEL_MASK, stream->buf.data + stream->prefix_len, stream->len - stream->prefix_len); } @@ -539,9 +539,11 @@ static inline void zlog_stream_init_internal( stream->fd = fd > -1 ? fd : STDERR_FILENO; stream->buf_init_size = capacity; if (flags & ZLOG_ACCESS_LOG) { + stream->use_external_logger = 0; stream->use_buffer = 1; stream->use_stderr = fd < 0; } else { + stream->use_external_logger = external_logger != NULL; stream->use_buffer = zlog_buffering || external_logger != NULL || stream->use_syslog; stream->use_stderr = fd < 0 || ( diff --git a/sapi/fpm/fpm/zlog.h b/sapi/fpm/fpm/zlog.h index 6886a0ae807da..8b4a80155f073 100644 --- a/sapi/fpm/fpm/zlog.h +++ b/sapi/fpm/fpm/zlog.h @@ -77,6 +77,7 @@ struct zlog_stream { unsigned int decorate:1; unsigned int is_stdout:1; unsigned int over_limit:1; + unsigned int use_external_logger:1; int fd; int line; int child_pid; diff --git a/sapi/fpm/tests/gh19989-access-log-fcgi-stderr.phpt b/sapi/fpm/tests/gh19989-access-log-fcgi-stderr.phpt new file mode 100644 index 0000000000000..6875774de3fac --- /dev/null +++ b/sapi/fpm/tests/gh19989-access-log-fcgi-stderr.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: GH-19989 - Access log going to fcgi error stream +--SKIPIF-- + +--FILE-- +start(['--prefix', $prefix]); +$tester->expectLogStartNotices(); +$response = $tester->request()->expectBody('OK'); +$response->expectNoError(); +$tester->expectAccessLog("'GET /gh19989-access-log-fcgi-stderr.src.php' 200"); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); +$tester->checkAccessLog(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- +