#!/bin/bash ## Title : ACHAR - Auto Completion of Hosts Add/Remove script ## Version : 0.2.1 ## GIT Repo : https://git.zaks.web.za/zaks-web/achar ## Author : Ze'ev Schurmann ## Company : ZAKS Web ## Website : https://www.zaks.web.za/ ## Reddit : https://www.reddit.com/r/ZAKSWeb ## License : GPL3 or Later ## Description: This tool allows for the management of bash auto completion of ## hostnames when using SSH or RSync in Bash. ## ## USAGE ## ## From the bash terminal, type: ## $ achar {command} {context} ## ## COMMANDS ## ## add - adds a host to your list of common hosts ## $ achar add username@hostname.domainname.tld ## $ achar add username@1.2.3.4 ## ## disable - disables auto completion of hostnames for current system user ## $ achar disable ## * does not delete the list of hosts stored at ~/.achar/hosts ## ## enable - enables auto completion of hostnames for current system user ## $ achar enable ## ## help - displays this block of text ## $ achar help ## ## license - displays the GPL3 and Later license ## $ achar license ## ## list - list all existing hosts from your list of common hosts ## $ achar list ## * You can use context to search for a specific hostname or part there of ## $ achar list username ## $ achar list part-of-hostname ## ## remove - removes an existing host from your list of common hosts ## $ achar remove username@hostname.domainname.tld ## $ achar remove username@1.2.3.4 ## ## reset - deletes all saved hosts ## $ achar reset ## ## uninstall - uninstalls achar from your system ## $ sudo achar uninstall ## # achar uninstall ## * does not delete the list of hosts stored at ~/.achar/hosts ## ## update - checks for an update to ACHAR and prompts you to install it ## $ achar update bashrc_array=("# enable host autocompletion for ssh and rsync" "[ -f ~/.achar/completions ] && [ -f ~/.achar/hosts ] && . ~/.achar/completions") completions_array=("# Include existing rsync completions" ". /usr/share/bash-completion/completions/rsync" "" "# Include existing ssh completions" ". /usr/share/bash-completion/completions/ssh" "" "# Function to add custom user@hostname completions" "_custom_hosts()" "{" " local cur=\"\${COMP_WORDS[COMP_CWORD]}\"" " local hosts=\$(cat ~/.achar/hosts)" " COMPREPLY=( \$(compgen -W \"\$hosts\" -- \"\$cur\") )" "}" "" "# Function to handle extended rsync completions" "_extended_rsync_completions()" "{" " local cur=\"\${COMP_WORDS[COMP_CWORD]}\"" "" " # Use custom host completions if '@' is detected" " if [[ \"\$cur\" == *@* ]]; then" " _custom_hosts" " else" " # Fallback to default rsync completion" " _rsync" " fi" "}" "" "# Extend the existing ssh completion function to include custom hosts" "_extended_ssh_completions()" "{" " local cur=\"\${COMP_WORDS[COMP_CWORD]}\"" "" " # Use custom host completions if '@' is detected" " if [[ \"\$cur\" == *@* ]]; then" " _custom_hosts" " else" " # Fallback to default ssh completion" " _ssh" " fi" "}" "" "# Preserve the original rsync completion function" "if declare -F _rsync &>/dev/null; then" " complete -r rsync" " complete -F _extended_rsync_completions rsync" "fi" "" "# Preserve the original ssh completion function" "if declare -F _ssh &>/dev/null; then" " complete -r ssh" " complete -F _extended_ssh_completions ssh" "fi") completions_md5="a148257d1f32705ef2c1f292e72d528c" function achar_add { local username_and_hostname username_and_hostname=$1 validate_achar_installation hosts validate_host "$username_and_hostname" if [[ -z $(cat ~/.achar/hosts | grep "$username_and_hostname") ]]; then echo $username_and_hostname >> ~/.achar/hosts else echo "$username_and_hostname is already added!" >&2 exit 1 fi } function achar_disable { validate_achar_installation disabled rm ~/.achar/completions for ((i=0; i<${#bashrc_array[@]}; i++)); do line=$(echo "${bashrc_array[$i]}" | sed 's/[]\/$*.^|[]/\\&/g') sed -i "/^$line$/d" ~/.bashrc done echo "Please start a new Bash Terminal Session for the change to take affect." } function achar_enable { validate_achar_installation enabled if [[ ! -d ~/.achar ]]; then mkdir ~/.achar fi if [[ ! -f ~/.achar/hosts ]]; then touch ~/.achar/hosts fi touch ~/.achar/completions for ((i=0; i<${#completions_array[@]}; i++)); do echo "${completions_array[$i]}" >> ~/.achar/completions done for ((i=0; i<${#bashrc_array[@]}; i++)); do echo "${bashrc_array[$i]}" >> ~/.bashrc done echo "Please start a new Bash Terminal Session for the change to take affect." } function achar_help { local script_path script_path="${BASH_SOURCE[0]}" echo while read -r line; do if [[ ${line:0:2} == "##" ]]; then echo "${line:3}" elif [[ ${line:0:2} == "#!" ]]; then donothing=0 else echo exit 0 fi done < "$script_path" } function achar_license { echo curl -s https://git.zaks.web.za/zaks-web/achar/raw/branch/main/LICENSE | fmt if [[ $? != 0 ]]; then echo "Could not get the license file." >&2 echo "Either you are not connected to the Internet or the update server is down." >&2 exit 1 fi echo } function achar_list { validate_achar_installation hosts if [[ -z $1 ]]; then cat ~/.achar/hosts | sort else cat ~/.achar/hosts | grep "$1" | sort fi } function achar_remove { local username_and_hostname username_and_hostname=$1 validate_achar_installation hosts validate_host $username_and_hostname if [[ -z $(cat ~/.achar/hosts | grep "$username_and_hostname") ]]; then echo "$username_and_hostname is not there!" >&2 exit 1 else sed -i "/^$username_and_hostname$/d" ~/.achar/hosts fi } function achar_reset { validate_achar_installation hosts echo "This will delete all saved hosts and" echo "and start from scratch." echo echo "Press ENTER to continue or CTRL+C to STOP!" read text rm ~/.achar/hosts touch ~/.achar/hosts } function achar_uninstall { local script_path local text script_path="${BASH_SOURCE[0]}" if [[ $(whoami) == "root" ]]; then echo "You are about to uninstall ACHAR." echo echo "If you want to disable the autocompletions, then" echo "you must run \"achar disable\" for each user." echo "This is recommended if you are planning to install" echo "a new version." echo echo "Press ENTER to continue or CTRL+C to STOP!" read text rm $script_path else echo "Please run with root privilages (sudo) in order to uninstall." exit 1 fi } function achar_update { local difference local script_path local stable_version_number local -a stable_version_number_array local test_stable local test_version local update_available local -a version_line local version_number local -a version_number_array script_path="${BASH_SOURCE[0]}" version_line=($(grep '^## Version' "$script_path")) version_number=${version_line[3]} stable_version_number=$(curl -s https://git.zaks.web.za/zaks-web/achar/raw/branch/main/stable_version) if [[ $? != 0 ]]; then echo "Could not check for latest version." >&2 echo "Either you are not connected to the Internet or the update server is down." >&2 exit 1 fi oldIFS=$IFS IFS="." version_number_array=($version_number) stable_version_number_array=($stable_version_number) IFS=$oldIFS if [[ ${#version_number_array[@]} -gt ${#stable_version_number_array[@]} ]]; then difference=$((${#version_number_array[@]}-${#stable_version_number_array[@]})) for ((i=0; i<$differnce; i++)); do $stable_version_number_array[${#stable_version_number_array[@]}]=0 done elif [[ ${#stable_version_number_array[@]} -gt ${#version_number_array[@]} ]]; then difference=$((${#version_number_array[@]}-${#stable_version_number_array[@]})) for ((i=0; i<$differnce; i++)); do $stable_version_number_array[${#stable_version_number_array[@]}]=0 done fi echo "Current version is $version_number" echo "Latest version is $stable_version_number" update_available="false" for ((i=0; i<${#version_number_array[@]}; i++)); do if [[ $update_available == "false" ]]; then test_stable=${stable_version_number_array[$i]##+(0)} test_version=${version_number_array[$i]##+(0)} if [[ -z $test_stable ]]; then test_stable=0 fi if [[ -z $test_version ]]; then test_version=0 fi if [[ ${#stable_version_number_array[@]} -gt ${#version_number_array[@]} ]]; then update_available="true" fi fi done if [[ $update_available == "true" ]]; then echo "Update is avaiable!" echo echo "First run \"achar disable\" for all users." echo "Then run \"achar uninstall\" as root or sudo." echo "Lastly, follow the instructions at" echo "https://git.zaks.web.za/zaks-web/achar" echo "to install the latest version." else echo "You currently have the latest version!" fi } function validate_achar_installation { local achar_installation_error_code="" local what_test what_test=$1 if [[ -d ~/.achar ]]; then achar_installation_error_code="A$achar_installation_error_code" else achar_installation_error_code="a$achar_installation_error_code" fi if [[ -f ~/.achar/hosts ]]; then achar_installation_error_code="H$achar_installation_error_code" else achar_installation_error_code="h$achar_installation_error_code" fi if [[ -f ~/.achar/completions ]]; then md5_test=$(cat ~/.achar/completions | md5sum) if [[ ${md5_test:0:32} == $completions_md5 ]]; then achar_installation_error_code="C$achar_installation_error_code" else echo "File corrupt: ~/.achar/completions" >&2 exit 1 fi else achar_installation_error_code="c$achar_installation_error_code" fi test=$(echo "${bashrc_array[1]}" | sed 's/[]\/$*.^|[]/\\&/g') if [[ -z $(cat ~/.bashrc | grep "${bashrc_array[0]}") ]] || [[ -z $(cat ~/.bashrc | grep "$test") ]]; then achar_installation_error_code="b$achar_installation_error_code" else achar_installation_error_code="B$achar_installation_error_code" fi if [[ $achar_installation_error_code == "BCHA" ]] && [[ $what_test == "enabled" ]]; then echo "ACHAR is already enabled!" exit 1 elif [[ $achar_installation_error_code == "bcha" ]] && [[ $what_test == "disabled" ]]; then echo "ACHAR is already disabled!" exit 1 elif [[ $achar_installation_error_code == "bcHA" ]] && [[ $what_test == "disabled" ]]; then echo "ACHAR is already disabled!" exit 1 elif [[ $achar_installation_error_code == "bcha" ]] && [[ -z $what_test ]]; then echo "ACHAR is disabled!" exit 1 elif [[ $achar_installation_error_code == "bcha" ]] && [[ $what_test == "hosts" ]]; then echo "ACHAR is disabled!" exit 1 elif [[ $achar_installation_error_code != "BCHA" ]] && [[ $achar_installation_error_code != "bcha" ]] && [[ -z $what_test ]]; then echo "Something is wrong with your setup!" >&2 if [[ ${achar_installation_error_code:0:1} == "b" ]]; then echo "Required code missing from ~/.bashrc" >&2 fi if [[ ${achar_installation_error_code:1:1} == "c" ]]; then echo "File missing: ~/.achar/completions" >&2 fi if [[ ${achar_installation_error_code:2:1} == "h" ]]; then echo "File missing: ~/.achar/hosts" >&2 fi if [[ ${achar_installation_error_code:3:1} == "a" ]]; then echo "Folder missing: ~/.achar/" >&2 fi exit 1 fi } function validate_host { local hostname local -a temp_array local username_and_hostname local username if [[ -z $1 ]]; then echo "Unexpected Error!" exit 1 fi username_and_hostname="$1" temp_array=($username_and_hostname) if [[ ${#temp_array[@]} != 1 ]]; then echo "Invalid Hostname!" exit 1 fi old_ifs=$IFS IFS="@" temp_array=($username_and_hostname) IFS=$old_ifs if [[ ${#temp_array[@]} != 2 ]]; then echo "Invalid Hostname!" exit 1 fi username=${temp_array[0]} hostname=${temp_array[1]} if [[ $hostname =~ ^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then donothing=0 elif nslookup "$hostname" > /dev/null 2>&1; then donothing=0 else echo "Invalid Domain Name or IP address!" exit 1 fi if [[ ${username:0:1} =~ ^[0-9]$ ]] || [[ ! $username =~ ^[A-Za-z0-9_.-]+$ ]]; then echo "Username is not a valid format!" echo "Must contain \"A-Z\", \"a-z\", \"0-9\", \"_\", \".\" and \"-\"." exit 1 fi } if [[ -z $1 ]]; then achar_help elif [[ ${1,,} == "add" ]]; then if [[ -z $2 ]]; then echo "Please include a username@hostname" >&2 exit 1 else achar_add $2 fi elif [[ ${1,,} == "disable" ]]; then achar_disable elif [[ ${1,,} == "enable" ]]; then achar_enable elif [[ ${1,,} == "help" ]]; then achar_help elif [[ ${1,,} == "license" ]]; then achar_license elif [[ ${1,,} == "list" ]]; then if [[ -z $2 ]]; then achar_list else achar_list $2 fi elif [[ ${1,,} == "remove" ]]; then if [[ -z $2 ]]; then echo "Please include a username@hostname" >&2 exit 1 else achar_remove $2 fi elif [[ ${1,,} == "reset" ]]; then achar_reset elif [[ ${1,,} == "uninstall" ]]; then achar_uninstall elif [[ ${1,,} == "update" ]]; then achar_update else achar_help fi exit 0