Fix multiple shell/container/port bugs (#265 #270 #271 #272 #274 #266)

#265 / #272: systemd_user_available() now requires XDG_RUNTIME_DIR and a
live D-Bus socket before probing systemctl --user, preventing false-positives
in containers (K8s/containerd) where systemctl exists but D-Bus is absent.

#274: Remove 'export' from CLASH_FOR_LINUX_SHELL_LOADED guard in profile.sh
generation so the variable stays local to the sourcing shell and does not
leak into child shells (VSCode terminal, tmux pane, bash subshell), which
was causing alias.sh to be skipped and http_proxy not exported in children.

#270: Ensure compinit is loaded before bashcompinit in the generated zsh
completion script so that compdef is available when bashcompinit registers
completions, fixing 'command not found: compdef' on zsh setups that do not
call compinit themselves.

#271: Extend container_env_type() to detect cgroups v2 + containerd/K8s
environments where /proc/1/cgroup only contains '0::/' and neither
/.dockerenv nor legacy cgroup keywords are present. Detection now also
inspects PID 1 comm and overlay root filesystem as fallbacks.

#266: resolve_runtime_ports() accepts an optional config-file hint; when
MIXED_PORT is not explicitly set in the environment, the port is read from
the config file being normalized. normalize_runtime_config() passes its
target file, so a user's 'mixed-port: 7899' in their subscription YAML is
preserved instead of being silently overwritten with the default 7890.
This commit is contained in:
Arvin
2026-06-16 16:30:38 +08:00
parent f0043d8dcb
commit 5f30fcdfef
3 changed files with 52 additions and 4 deletions
+35 -2
View File
@@ -1956,7 +1956,12 @@ systemd_available() {
systemd_user_available() {
is_openwrt && return 1
command -v systemctl >/dev/null 2>&1 && systemctl --user list-unit-files >/dev/null 2>&1
command -v systemctl >/dev/null 2>&1 || return 1
# Require a real user D-Bus socket; avoids false-positives in containers
# where systemctl exists but D-Bus is absent (XDG_RUNTIME_DIR unset or no bus socket).
[ -n "${XDG_RUNTIME_DIR:-}" ] || return 1
[ -S "${XDG_RUNTIME_DIR}/bus" ] || return 1
systemctl --user daemon-reload >/dev/null 2>&1
}
is_root_user() {
@@ -1972,16 +1977,44 @@ tun_device_readable() {
}
container_env_type() {
# Docker: marker file present
if [ -f "/.dockerenv" ]; then
echo "docker"
return 0
fi
# cgroups v1: cgroup path contains runtime keywords
if grep -qaE '(docker|containerd|kubepods|lxc)' /proc/1/cgroup 2>/dev/null; then
echo "container"
return 0
fi
# cgroups v2 + containerd/K8s: /proc/1/cgroup is just "0::/"
# Fall back to namespace / filesystem checks
if [ -f /proc/1/cgroup ] && grep -q '^0::/' /proc/1/cgroup 2>/dev/null; then
# Running as a non-init process tree (PID 1 is not systemd/sysvinit)
local _init_comm
_init_comm="$(cat /proc/1/comm 2>/dev/null || true)"
case "${_init_comm:-}" in
systemd|sysvinit|init|launchd|openrc-init)
: ;; # real host init, continue to other checks
*)
# Non-standard PID 1 is a strong container signal
echo "container"
return 0
;;
esac
fi
# Overlay root filesystem is typical of container environments
if awk '$5 == "/" { print $1 }' /proc/mounts 2>/dev/null | grep -q 'overlay'; then
echo "container"
return 0
fi
# No D-Bus session and no /run/systemd/private is another container signal,
# but this overlaps with systemd checks elsewhere; skip here to avoid false-positives.
echo "host"
}
@@ -2548,7 +2581,7 @@ cat > "$profile_file" <<EOF
export PATH="$(command_install_dir):\$PATH"
if [ -n "\${BASH_VERSION:-}" ] && [ -z "\${CLASH_FOR_LINUX_SHELL_LOADED:-}" ]; then
export CLASH_FOR_LINUX_SHELL_LOADED="1"
CLASH_FOR_LINUX_SHELL_LOADED="1"
source "$alias_file"
fi
+4
View File
@@ -410,6 +410,10 @@ completion_emit_bash_script() {
completion_emit_zsh_script() {
cat <<'EOF'
# Ensure compinit is loaded before bashcompinit (provides compdef)
if ! command -v compinit >/dev/null 2>&1; then
autoload -Uz compinit && compinit -u 2>/dev/null
fi
autoload -Uz bashcompinit 2>/dev/null || return 0
bashcompinit >/dev/null 2>&1 || return 0
EOF
+13 -2
View File
@@ -806,7 +806,7 @@ normalize_runtime_config() {
[ -s "$file" ] || die "待规范化的配置文件不存在:$file"
resolved_ports="$(resolve_runtime_ports)"
resolved_ports="$(resolve_runtime_ports "$file")"
load_resolved_runtime_ports "$resolved_ports"
mixed_port="$MIXED_PORT_RESOLVED"
@@ -2038,12 +2038,23 @@ load_resolved_runtime_ports() {
}
resolve_runtime_ports() {
# Optional: path to a config file to read port from when MIXED_PORT is not set
local _hint_config_file="${1:-}"
local preferred_mixed preferred_controller preferred_dns
local controller_host preferred_controller_port
local mixed_port controller_port dns_port
local used_ports=""
preferred_mixed="${MIXED_PORT:-7890}"
preferred_mixed="${MIXED_PORT:-}"
# When MIXED_PORT is not explicitly configured, honour the port in the config file
if [ -z "${preferred_mixed:-}" ] && [ -n "${_hint_config_file:-}" ] && [ -s "${_hint_config_file}" ]; then
local _file_port
_file_port="$("$(yq_bin)" eval '.["mixed-port"] // .port // ""' "$_hint_config_file" 2>/dev/null | head -n 1)"
if [ -n "${_file_port:-}" ] && [ "$_file_port" != "null" ] && is_valid_port_number "$_file_port"; then
preferred_mixed="$_file_port"
fi
fi
preferred_mixed="${preferred_mixed:-7890}"
preferred_controller="${EXTERNAL_CONTROLLER:-0.0.0.0:9090}"
preferred_dns="${CLASH_DNS_PORT:-1053}"