diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c index b532e68b9d304..26be0a7fa2ce1 100644 --- a/sapi/fpm/fpm/fpm_sockets.c +++ b/sapi/fpm/fpm/fpm_sockets.c @@ -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)); } diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index d10a6f3254b24..6ac0b00432b5d 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/sapi/fpm/tests/logreader.inc b/sapi/fpm/tests/logreader.inc index 96bcc9ec7cfb3..97b19adb027f6 100644 --- a/sapi/fpm/tests/logreader.inc +++ b/sapi/fpm/tests/logreader.inc @@ -16,7 +16,7 @@ class LogReader * * @var string|null */ - private ?string $currentSourceName; + private ?string $currentSourceName = null; /** * Log descriptors. diff --git a/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt b/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt new file mode 100644 index 0000000000000..a7b43d9519b0a --- /dev/null +++ b/sapi/fpm/tests/socket-uds-too-long-filename-start.phpt @@ -0,0 +1,74 @@ +--TEST-- +FPM: UNIX socket filename is too for start +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/socket-uds-too-long-filename-test.phpt b/sapi/fpm/tests/socket-uds-too-long-filename-test.phpt new file mode 100644 index 0000000000000..e7c54d86f4af0 --- /dev/null +++ b/sapi/fpm/tests/socket-uds-too-long-filename-test.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: UNIX socket filename is too for test +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index d013294f704da..0493adfd483ae 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -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); } } + } else { + $messages = null; + } - return $messages; + 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 null; + 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;