mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Warn when fpm socket was not registered on the expected path
This might happen if the UDS length limit is exceeded. Co-authored-by: Jakub Zelenka <bukka@php.net> Closes GH-11066
This commit is contained in:
parent
72e2e25066
commit
08b57772b0
8 changed files with 217 additions and 10 deletions
4
NEWS
4
NEWS
|
@ -22,6 +22,10 @@ PHP NEWS
|
|||
. Added DOMElement::className and DOMElement::id. (nielsdos)
|
||||
. Added DOMParentNode::replaceChildren(). (nielsdos)
|
||||
|
||||
- FPM:
|
||||
. Added warning to log when fpm socket was not registered on the expected
|
||||
path. (Joshua Behrens, Jakub Zelenka)
|
||||
|
||||
- Intl:
|
||||
. Fix memory leak in MessageFormatter::format() on failure. (Girgias)
|
||||
|
||||
|
|
|
@ -429,6 +429,9 @@ PHP 8.3 UPGRADE NOTES
|
|||
current system user. Previously, calling FFI::load() was not possible during
|
||||
preloading if the opcache.preload_user directive was set.
|
||||
|
||||
- FPM:
|
||||
. FPM CLI test now fails if the socket path is longer than supported by OS.
|
||||
|
||||
- Opcache:
|
||||
. In the cli and phpdbg SAPIs, preloading does not require the
|
||||
opcache.preload_user directive to be set anymore when running as root. In
|
||||
|
|
|
@ -396,9 +396,25 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /*
|
|||
static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
|
||||
{
|
||||
struct sockaddr_un sa_un;
|
||||
size_t socket_length = sizeof(sa_un.sun_path);
|
||||
size_t address_length = strlen(wp->config->listen_address);
|
||||
|
||||
memset(&sa_un, 0, sizeof(sa_un));
|
||||
strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
|
||||
strlcpy(sa_un.sun_path, wp->config->listen_address, socket_length);
|
||||
|
||||
if (address_length >= socket_length) {
|
||||
zlog(
|
||||
ZLOG_WARNING,
|
||||
"[pool %s] cannot bind to UNIX socket '%s' as path is too long (found length: %zu, "
|
||||
"maximal length: %zu), trying cut socket path instead '%s'",
|
||||
wp->config->name,
|
||||
wp->config->listen_address,
|
||||
address_length,
|
||||
socket_length,
|
||||
sa_un.sun_path
|
||||
);
|
||||
}
|
||||
|
||||
sa_un.sun_family = AF_UNIX;
|
||||
return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
|
@ -63,6 +64,33 @@ static struct passwd *fpm_unix_get_passwd(struct fpm_worker_pool_s *wp, const ch
|
|||
return pwd;
|
||||
}
|
||||
|
||||
static inline bool fpm_unix_check_listen_address(struct fpm_worker_pool_s *wp, const char *address, int flags)
|
||||
{
|
||||
if (wp->listen_address_domain != FPM_AF_UNIX) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sockaddr_un test_socket;
|
||||
size_t address_length = strlen(address);
|
||||
size_t socket_length = sizeof(test_socket.sun_path);
|
||||
|
||||
if (address_length < socket_length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
zlog(
|
||||
flags,
|
||||
"[pool %s] cannot bind to UNIX socket '%s' as path is too long (found length: %zu, "
|
||||
"maximal length: %zu)",
|
||||
wp->config->name,
|
||||
address,
|
||||
address_length,
|
||||
socket_length
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool fpm_unix_check_passwd(struct fpm_worker_pool_s *wp, const char *name, int flags)
|
||||
{
|
||||
return !name || fpm_unix_is_id(name) || fpm_unix_get_passwd(wp, name, flags);
|
||||
|
@ -90,6 +118,7 @@ bool fpm_unix_test_config(struct fpm_worker_pool_s *wp)
|
|||
return (
|
||||
fpm_unix_check_passwd(wp, config->user, ZLOG_ERROR) &&
|
||||
fpm_unix_check_group(wp, config->group, ZLOG_ERROR) &&
|
||||
fpm_unix_check_listen_address(wp, config->listen_address, ZLOG_SYSERROR) &&
|
||||
fpm_unix_check_passwd(wp, config->listen_owner, ZLOG_SYSERROR) &&
|
||||
fpm_unix_check_group(wp, config->listen_group, ZLOG_SYSERROR)
|
||||
);
|
||||
|
@ -273,7 +302,7 @@ int fpm_unix_set_socket_permissions(struct fpm_worker_pool_s *wp, const char *pa
|
|||
/* Copy the new ACL entry from config */
|
||||
for (i=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, i, &entryconf) ; i=ACL_NEXT_ENTRY) {
|
||||
if (0 > acl_create_entry (&aclfile, &entryfile) ||
|
||||
0 > acl_copy_entry(entryfile, entryconf)) {
|
||||
0 > acl_copy_entry(entryfile, entryconf)) {
|
||||
zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path);
|
||||
acl_free(aclfile);
|
||||
return -1;
|
||||
|
|
|
@ -16,7 +16,7 @@ class LogReader
|
|||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private ?string $currentSourceName;
|
||||
private ?string $currentSourceName = null;
|
||||
|
||||
/**
|
||||
* Log descriptors.
|
||||
|
|
74
sapi/fpm/tests/socket-uds-too-long-filename-start.phpt
Normal file
74
sapi/fpm/tests/socket-uds-too-long-filename-start.phpt
Normal file
|
@ -0,0 +1,74 @@
|
|||
--TEST--
|
||||
FPM: UNIX socket filename is too for start
|
||||
--SKIPIF--
|
||||
<?php
|
||||
include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
$socketFilePrefix = __DIR__ . '/socket-file';
|
||||
$socketFile = sprintf(
|
||||
"%s-fpm-unix-socket-too-long-filename-but-starts-anyway%s.sock",
|
||||
$socketFilePrefix,
|
||||
str_repeat('-0000', 11)
|
||||
);
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
|
||||
[fpm_pool]
|
||||
listen = $socketFile
|
||||
pm = static
|
||||
pm.max_children = 1
|
||||
catch_workers_output = yes
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg);
|
||||
$tester->start();
|
||||
$tester->expectLogStartNotices();
|
||||
$tester->expectLogPattern(
|
||||
sprintf(
|
||||
'/\[pool fpm_pool\] cannot bind to UNIX socket \'%s\' as path is too long '
|
||||
. '\(found length: %d, maximal length: \d+\), trying cut socket path instead \'.+\'/',
|
||||
preg_quote($socketFile, '/'),
|
||||
strlen($socketFile)
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
$files = glob($socketFilePrefix . '*');
|
||||
|
||||
if ($files === []) {
|
||||
echo 'Socket files were not found.' . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($socketFile === $files[0]) {
|
||||
// this means the socket file path length is not an issue (anymore). Might be not long enough
|
||||
echo 'Socket file is the same as configured.' . PHP_EOL;
|
||||
}
|
||||
|
||||
$tester->terminate();
|
||||
$tester->expectLogTerminatingNotices();
|
||||
$tester->close();
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
|
||||
// cleanup socket file if php-fpm was not killed
|
||||
$socketFile = sprintf(
|
||||
"/socket-file-fpm-unix-socket-too-long-filename-but-starts-anyway%s.sock",
|
||||
__DIR__,
|
||||
str_repeat('-0000', 11)
|
||||
);
|
||||
|
||||
if (is_file($socketFile)) {
|
||||
unlink($socketFile);
|
||||
}
|
||||
?>
|
47
sapi/fpm/tests/socket-uds-too-long-filename-test.phpt
Normal file
47
sapi/fpm/tests/socket-uds-too-long-filename-test.phpt
Normal file
|
@ -0,0 +1,47 @@
|
|||
--TEST--
|
||||
FPM: UNIX socket filename is too for test
|
||||
--SKIPIF--
|
||||
<?php
|
||||
include "skipif.inc"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require_once "tester.inc";
|
||||
|
||||
$socketFilePrefix = __DIR__ . '/socket-file';
|
||||
$socketFile = sprintf(
|
||||
"/socket-file-fpm-unix-socket-too-long-filename-but-starts-anyway%s.sock",
|
||||
__DIR__,
|
||||
str_repeat('-0000', 11)
|
||||
);
|
||||
|
||||
$cfg = <<<EOT
|
||||
[global]
|
||||
error_log = {{FILE:LOG}}
|
||||
|
||||
[fpm_pool]
|
||||
listen = $socketFile
|
||||
pm = static
|
||||
pm.max_children = 1
|
||||
catch_workers_output = yes
|
||||
EOT;
|
||||
|
||||
$tester = new FPM\Tester($cfg);
|
||||
$tester->testConfig(true, [
|
||||
sprintf(
|
||||
'/cannot bind to UNIX socket \'%s\' as path is too long '
|
||||
. '\(found length: %d, maximal length: \d+\)/',
|
||||
preg_quote($socketFile, '/'),
|
||||
strlen($socketFile)
|
||||
),
|
||||
'/FPM initialization failed/',
|
||||
]);
|
||||
?>
|
||||
Done
|
||||
--EXPECT--
|
||||
Done
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once "tester.inc";
|
||||
FPM\Tester::clean();
|
||||
?>
|
|
@ -382,26 +382,50 @@ class Tester
|
|||
* @return null|array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testConfig($silent = false)
|
||||
public function testConfig($silent = false, array|string|null $expectedPattern = null): ?array
|
||||
{
|
||||
$configFile = $this->createConfig();
|
||||
$cmd = self::findExecutable() . ' -n -tt -y ' . $configFile . ' 2>&1';
|
||||
$this->trace('Testing config using command', $cmd, true);
|
||||
exec($cmd, $output, $code);
|
||||
$found = 0;
|
||||
if ($expectedPattern !== null) {
|
||||
$expectedPatterns = is_array($expectedPattern) ? $expectedPattern : [$expectedPattern];
|
||||
}
|
||||
if ($code) {
|
||||
$messages = [];
|
||||
foreach ($output as $outputLine) {
|
||||
$message = preg_replace("/\[.+?\]/", "", $outputLine, 1);
|
||||
if ($expectedPattern !== null) {
|
||||
for ($i = 0; $i < count($expectedPatterns); $i++) {
|
||||
$pattern = $expectedPatterns[$i];
|
||||
if ($pattern !== null && preg_match($pattern, $message)) {
|
||||
$found++;
|
||||
$expectedPatterns[$i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$messages[] = $message;
|
||||
if ( ! $silent) {
|
||||
$this->error($message, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
} else {
|
||||
$messages = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
if ($expectedPattern !== null && $found < count($expectedPatterns)) {
|
||||
$missingPatterns = array_filter($expectedPatterns);
|
||||
$errorMessage = sprintf(
|
||||
"The expected config %s %s %s not been found",
|
||||
count($missingPatterns) > 1 ? 'patterns' : 'pattern',
|
||||
implode(', ', $missingPatterns),
|
||||
count($missingPatterns) > 1 ? 'have' : 'has',
|
||||
);
|
||||
$this->error($errorMessage);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1155,9 +1179,19 @@ class Tester
|
|||
return $address;
|
||||
}
|
||||
|
||||
return sys_get_temp_dir() . '/' .
|
||||
hash('crc32', dirname($address)) . '-' .
|
||||
basename($address);
|
||||
$addressPart = hash('crc32', dirname($address)) . '-' . basename($address);
|
||||
|
||||
// is longer on Mac, than on Linux
|
||||
$tmpDirAddress = sys_get_temp_dir() . '/' . $addressPart;
|
||||
;
|
||||
|
||||
if (strlen($tmpDirAddress) <= 104) {
|
||||
return $tmpDirAddress;
|
||||
}
|
||||
|
||||
$srcRootAddress = dirname(__DIR__, 3) . '/' . $addressPart;
|
||||
|
||||
return $srcRootAddress;
|
||||
}
|
||||
|
||||
return $this->getHost($type) . ':' . $port;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue