Skip to content

Instantly share code, notes, and snippets.

@ggets
Last active June 22, 2019 13:20
Show Gist options
  • Save ggets/954b5a1744dbc4b08b5d8edfaae3c3aa to your computer and use it in GitHub Desktop.
Save ggets/954b5a1744dbc4b08b5d8edfaae3c3aa to your computer and use it in GitHub Desktop.
SSHFS Mount script for winfsp implementation of sshfs
#!/bin/bash
################################################################################
### SSHFS Mount script for winfsp implementation of sshfs ###
### -by GGetsov ###
### ------------------------------------------------------------------------ ###
### Requires: Cygwin, bash (with arrays support), Powershell, expect ###
### sshfs (WinFSP), jq, procps, grep and find (both preferably GNU) ###
################################################################################
arrtest[0]="1"||(echo "Failure: arrays not supported in this version of bash."&&exit 2)
#var
ver="1.7.0"
rev="22.06.2019"
declare -A b #binary tools
declare -A p #parameters
declare -A c #credentials
declare -A r #previous instance
#fn
e(){
${b[echo]} -e "${@}"
}
err(){
e "${1:-Insufficient arguments. Type \"${0} --help\" to see usage help.}"
}
b(){
[ -z ${1+x} ]&&err "No commands to autodetect!"&&exit 1
while [[ "${#}" > "0" ]];do
t="$(type -t ${1})"
if [[ "${t}" == "builtin" ]]||[[ "${t}" == "function" ]]||[[ "${t}" == "alias" ]];then
b[${1}]="${1}"
elif [[ "${t}" == "file" ]];then
b[${1}]="$(type -p ${1})"
else
err "Could not find the command: ${1}"&&exit 1
fi
shift
done
}
pshell (){
cmd /c powershell -NonInteractive -Command - < <(e "${@}")
}
#tools
b type echo printf jq find expect grep sleep sshfs procps taskkill cat
#arg
[ -z ${1+x} ]&&err&&exit 1
while [[ "${#}" > "0" ]];do
case "${1}" in
--help)
e "CygWin SSHFS mount script by hkr v${ver} (rev${rev})"
e " Usage:"
e " -d, --disconnect: Disconnect (Optional. Requires 'host' or 'mdir')"
e " -h, --host: Host (Mandatory. Format: 'host')"
e " -u, --user: SSH user name (Optional. Format: 'user')"
e " -r, --rdir: Remote directory (Optional. Format: '/dir'. Default: '/')"
e " -m, --mdir: Local directory to mount to (Mandatory. Format: '/dir' (cygwin))"
e " -p, --port: Port for SSH (Optional. Default: 22)"
e ""
e " --test: Only test print the string, don't execute"
e ""
exit 0
;;
-d|--disconnect)
p[disconnect]="1"
;;
-h|--host)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[host]="${2}"
shift
;;
-u|--user)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[user]="${2}"
shift
;;
-p|--port)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[port]="${2}"
shift
;;
-r|--rdir)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[rdir]="${2}"
shift
;;
-m|--mdir)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[mdir]="${2}"
shift
;;
--test)
p[test]="1"
;;
*)
;;
esac
shift
done
#argcheck:
[ -z ${p[disconnect]+x} ]&&{
[ -z ${p[host]+x} ]||[ -z ${p[mdir]+x} ]&&err&&exit 1
}||{
[ -z ${p[host]+x} ]&&[ -z ${p[mdir]+x} ]&&err&&exit 1
}
p[user]="${p[user]:=root}" # default user name is 'root'
p[rdir]="${p[rdir]:=/}" # default remote dir is '/'
p[port]="${p[port]:=22}" # default port is 22
#main
p[mdir]="/computer/r/ssh/${p[mdir]}" # subdir of R:\ssh
d="0"
i="0"
s="3"
grepmatch=""
dispmatch=""
[ ! -z ${p[host]+x} ]&&{
[[ "${#grepmatch}" > "0" ]]&&grepmatch="${grepmatch} "
[[ "${#dispmatch}" > "0" ]]&&dispmatch="${dispmatch} "
grepmatch="${grepmatch}@${p[host]}:${p[rdir]}"
dispmatch="${dispmatch}ssh://${p[host]}:${p[rdir]}"
}
[ ! -z ${p[mdir]+x} ]&&{
[[ "${#grepmatch}" > "0" ]]&&grepmatch="${grepmatch} "
[[ "${#dispmatch}" > "0" ]]&&dispmatch="${dispmatch} "
grepmatch="${grepmatch}${p[mdir]//\[/\\[}"
dispmatch="${dispmatch}${p[mdir]}"
}
r[proc]="$(${b[procps]} -wC sshfs -o pid,cmd --no-heading 2>/dev/null|${b[grep]} -i "${grepmatch}")"
r[proc]="${r[proc]#"${r[proc]%%[!$'\t\r\n ']*}"}"
IFS=$'\n'
cnt=(${r[proc]})
unset IFS
r[cnt]="${#cnt[@]}"
r[pid]="${r[proc]%%[$'\t ']*}"
r[cmd]="${r[proc]#*[$'\t ']}"
[[ "${r[cnt]}" > "0" ]]&&[ -f "/proc/${r[pid]}/winpid" ]&&{
r[winpid]="$(${b[cat]} "/proc/${r[pid]}/winpid")"
r[winpid]="${r[winpid]//[$'\t\r\n ']}"
}
[ ! -z ${p[disconnect]} ]&&{
[[ "${r[cnt]}" > "0" ]]&&{
[[ "${#r[winpid]}" > "0" ]]&&{
e "Disconnecting \"${dispmatch}\"..."
${b[taskkill]} /f /pid ${r[winpid]}
}||e "Could not get Windows PID to kill the process."
}||e "\"${dispmatch}\" not connected."
}||{
[[ "${r[cnt]}" > "0" ]]&&e "Already connected. (PID: ${r[pid]})"||{
${b[find]} "${p[mdir]}" -maxdepth 0 >/dev/null 2>&1&&e "\"${p[mdir]}\" is in use or already mounted."||{
while [[ "${d}" == "0" ]]&&[[ "${i}" < "${s}" ]];do
o="$(pshell "
\$c=\$host.ui.PromptForCredential(\"SSH Password\",\"Enter the SSH server's credentials for 'ssh://${p[host]}:${p[port]}'.\",\"${p[user]}\",\"\")
if(\$c -ne \$null){
\$c.GetNetworkCredential()|convertTo-json -compress
}
")"
[ -z ${o:+x} ]&&{
e "Canceled."
d="1"
}||{
c[u]="$(e ${o}|${b[jq]} -cj '.UserName')"
c[p]="$(e ${o}|${b[jq]} -cj '.Password')"
user="$p[user]}"
[ -z ${c[u]:+x} ]||u="${c[u]}"
p[addr]="${u}@${p[host]}:${p[rdir]}"
e -n "Connecting to \"ssh://${p[host]}:${p[port]}\" as ${u}... "
[ -z ${p[test]+x} ]&&{
${b[expect]} - >/dev/null 2>&1 <<EOBT
spawn ${b[sshfs]} "${p[addr]}" {${p[mdir]}} -o port=${p[port]} -o idmap=user -o rellinks -o "StrictHostKeyChecking=no"
expect "password:"
send ${c[p]}\n\n
interact
EOBT
}||{
c[p]="$(${b[printf]} "%${#c[p]}s")"
c[p]="${c[p]// /*}"
${b[echo]}
${b[echo]} "${b[expect]} - >/dev/null 2>&1 <<EOBT
spawn ${b[sshfs]} \"${p[addr]}\" {${p[mdir]}} -o port=${p[port]} -o idmap=user -o rellinks -o \"StrictHostKeyChecking=no\"
expect \"password:\"
send ${c[p]}\n\n
interact
EOBT"
exit 0
}
[[ "${?}" == "0" ]]&&{
${b[sleep]} 1
${b[find]} "${p[mdir]}" -maxdepth 0 >/dev/null 2>&1&&{
e "Connected."
d="1"
}||
e "($[${i}+1]/${s}) Failed. Could not connect. Wrong password?"
}||
e "($[${i}+1]/${s}) Failed. Could not mount. No network or host is not available."
}
i=$[${i}+1]
done
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment