Skip to content

Commit

Permalink
fix(ssh): resolve relative ssh_config Include correctly
Browse files Browse the repository at this point in the history
As per ssh_config man, "Files without absolute paths are
assumed to be in ~/.ssh if included in a user configuration
file or /etc/ssh if included from the system configuration file."

So relative include base stays the same throughout recursion,
even if the system-wide config is included from user's config.

This commit also optimizes traversing ssh config includes.
Previously each config file was read with separate `sed` command
to extract `Include` directives. This can get quite slow if there are
a lot of included files.
Instead of reading each individual file, we can put all the files
at a current recursion level into a single sed invocation and
extract all the directives in one go.
  • Loading branch information
Rogach committed May 13, 2023
1 parent a17197f commit 4ac86e8
Showing 1 changed file with 36 additions and 25 deletions.
61 changes: 36 additions & 25 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -2111,33 +2111,44 @@ _included_ssh_config_files()
local configfile i files f
configfile=$1

# From man ssh_config:
# "Files without absolute paths are assumed to be in ~/.ssh if included
# in a user configuration file or /etc/ssh if included from the system
# configuration file."
# This behavior is not affected by the the including file location -
# if the system configuration file is included from the user's config,
# relative includes are still resolved in the user's ssh config directory.
local relative_include_base
if [[ $configfile == /etc/ssh* ]]; then
relative_include_base="/etc/ssh"
else
relative_include_base="$HOME/.ssh"
fi

local depth=1
local -a included
_comp_split included "$(command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\(.*\)$/\1/p' "${configfile}")" || return

for i in "${included[@]}"; do
# Check the origin of $configfile to complete relative included paths on included
# files according to ssh_config(5):
# "[...] Files without absolute paths are assumed to be in ~/.ssh if included in a user
# configuration file or /etc/ssh if included from the system configuration file.[...]"
if ! [[ $i =~ ^\~.*|^\/.* ]]; then
if [[ $configfile =~ ^\/etc\/ssh.* ]]; then
i="/etc/ssh/$i"
else
i="$HOME/.ssh/$i"
local -a include_files
included=("$configfile")

while ((${#included[@]} > 0 && depth++ < 16)); do
_comp_split include_files "$(command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\(.*\)$/\1/p' "${included[@]}")" || return
included=()
for i in "${include_files[@]}"; do
if [[ $i != [~/]* ]]; then
i="${relative_include_base}/${i}"
fi
fi
__expand_tilde_by_ref i
# In case the expanded variable contains multiple paths
_comp_expand_glob files '$i'
if ((${#files[@]})); then
for f in "${files[@]}"; do
if [[ -r $f && ! -d $f ]]; then
config+=("$f")
# The Included file is processed to look for Included files in itself
_included_ssh_config_files "$f"
fi
done
fi
__expand_tilde_by_ref i
# In case the expanded variable contains multiple paths
_comp_expand_glob files '$i'
if ((${#files[@]})); then
for f in "${files[@]}"; do
if [[ -r $f && ! -d $f ]]; then
config+=("$f")
included+=("$f")
fi
done
fi
done
done
} # _included_ssh_config_files()

Expand Down

0 comments on commit 4ac86e8

Please sign in to comment.