You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Linux sessionclean脚本读取php-fpm配置的session.save_path

如何让PHP sessionclean脚本优先读取php-fpm配置中的session.save_path

问题背景

  • 目标:让系统内置的/usr/lib/php/sessionclean脚本,优先使用php-fpm pool配置中php_admin_value[session.save_path]定义的会话路径;若无自定义配置,则 fallback 到默认的/var/lib/php/sessions
  • 当前问题:该脚本仅从对应PHP版本的php.ini中读取默认路径,完全忽略php-fpm pool配置里的自定义路径(这也是为什么php -i显示默认路径,但phpinfo页面显示自定义路径的核心原因——PHP CLI进程不会加载fpm pool配置)

问题根源

原脚本通过调用PHP CLI读取配置,但PHP CLI不会加载php-fpm的pool配置文件(通常位于/etc/php/${version}/fpm/pool.d/下的.conf文件),因此无法获取到php_admin_value或php_value覆盖后的session.save_path值。

解决方案:修改sessionclean脚本

我们需要在原脚本中添加逻辑,遍历php-fpm的pool配置文件,提取其中的session.save_path配置并优先使用;若未找到自定义配置,则沿用原逻辑从php.ini读取路径。同时修复原脚本中仅处理最后一个会话路径的touch逻辑bug。

以下是修改后的完整脚本:

#!/bin/sh -e
#
# sessionclean - a script to cleanup stale PHP sessions
#
# Copyright 2013-2015 Ondřej Surý <ondrej@sury.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

SAPIS="apache2:apache2 apache2filter:apache2 cgi:php@VERSION@ fpm:php-fpm@VERSION@ cli:php@VERSION@"

# Iterate through all web SAPIs
(
proc_names=""
session_paths=""
for version in $(/usr/sbin/phpquery -V); do
    for sapi in ${SAPIS}; do
        conf_dir=${sapi%%:*}
        proc_name=${sapi##*:}
        if [ -e "/etc/php/${version}/${conf_dir}/php.ini" ] && [ -x "/usr/bin/php${version}" ]; then
            # Get all session variables once so we don't need to start PHP to get each config option
            session_config=$(PHP_INI_SCAN_DIR=/etc/php/${version}/${conf_dir}/conf.d/ "/usr/bin/php${version}" -c "/etc/php/${version}/${conf_dir}/php.ini" -d "error_reporting='~E_ALL'" -r 'foreach(ini_get_all("session") as $k => $v) echo "$k=" . $v["local_value"] . "\n";')
            save_handler=$(echo "$session_config" | sed -ne 's/^session\.save_handler=\(.*\)$/\1/p')
            # 优先从php-fpm pool配置中读取session.save_path
            fpm_save_path=""
            if [ "$conf_dir" = "fpm" ]; then
                # 遍历所有fpm pool配置文件
                for pool_conf in /etc/php/${version}/fpm/pool.d/*.conf; do
                    # 提取php_admin_value或php_value中的session.save_path
                    pool_path=$(sed -ne 's/^[[:space:]]*php_admin_value\[session.save_path\][[:space:]]*=[[:space:]]*"\(.*\)"$/\1/p' "$pool_conf")
                    if [ -z "$pool_path" ]; then
                        pool_path=$(sed -ne 's/^[[:space:]]*php_value\[session.save_path\][[:space:]]*=[[:space:]]*"\(.*\)"$/\1/p' "$pool_conf")
                    fi
                    if [ -n "$pool_path" ] && [ -d "$pool_path" ]; then
                        fpm_save_path="$pool_path"
                        break
                    fi
                done
            fi
            # 路径优先级:fpm pool配置 > php.ini配置
            if [ -n "$fpm_save_path" ]; then
                save_path="$fpm_save_path"
            else
                save_path=$(echo "$session_config" | sed -ne 's/^session\.save_path=\(.*;\)\?\(.*\)$/\2/p')
            fi
            gc_maxlifetime=$(($(echo "$session_config" | sed -ne 's/^session\.gc_maxlifetime=\(.*\)$/\1/p')/60))
            
            if [ "$save_handler" = "files" ] && [ -d "$save_path" ]; then
                proc_names="$proc_names $(echo "$proc_name" | sed -e "s,@VERSION@,$version,")";
                printf "%s:%s\n" "$save_path" "$gc_maxlifetime"
                # 保存所有有效会话路径,用于后续更新open session时间戳
                session_paths="$session_paths$save_path"$'\n'
            fi
        fi
    done
done
# 更新所有open session文件的时间戳,避免被误清理
for pid in $(pidof $proc_names); do
    while IFS= read -r sp; do
        [ -z "$sp" ] && continue
        find "/proc/$pid/fd" -ignore_readdir_race -lname "$sp/sess_*" -exec touch -c {} \; 2>/dev/null
    done <<EOF
$session_paths
EOF
done ) | \
    sort -rn -t: -k2,2 | \
    sort -u -t: -k 1,1 | \
    while IFS=: read -r save_path gc_maxlifetime; do
        # 删除过期会话文件
        find -O3 "$save_path/" -ignore_readdir_race -depth -mindepth 1 -name 'sess_*' -type f -cmin "+$gc_maxlifetime" -delete
    done

exit 0

关键修改说明

  1. 添加fpm pool配置读取逻辑:当处理fpm类型的SAPI时,遍历/etc/php/${version}/fpm/pool.d/下的所有.conf文件,优先提取php_admin_value[session.save_path]的值,若不存在则尝试php_value[session.save_path]
  2. 路径优先级处理:优先使用fpm pool配置中的路径,无自定义配置时才使用php.ini中的默认路径
  3. 修复open session时间戳更新逻辑:原脚本仅处理最后一个会话路径的进程文件,修改为遍历所有收集到的有效路径,确保所有正在使用的会话文件都不会被误清理

验证方法

  1. 替换原脚本后,确保执行权限正常:chmod +x /usr/lib/php/sessionclean
  2. 手动执行脚本:/usr/lib/php/sessionclean
  3. 检查自定义会话路径下的过期文件是否被正确清理

内容的提问来源于stack exchange,提问作者Saad Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 15:48:28