Skip to content

Instantly share code, notes, and snippets.

@efrecon
Last active June 18, 2019 09:50
Show Gist options
  • Save efrecon/1f66a9abb8f364b77313bb3a3166a7d0 to your computer and use it in GitHub Desktop.
Save efrecon/1f66a9abb8f364b77313bb3a3166a7d0 to your computer and use it in GitHub Desktop.
Automounts using sshfs

SSHFS Auto Mounter

The purpose of this project is to facilitate SSHFS mounting at the command-line. In short, this script will read a configuration file to mount remote locations onto the local filesystem using SSHFS. The script supposes that you have ensured that your local account is able to access the remote filesystem using the specified user.

The script is tuned to minimise command-line entry and get you going as quickly as possible, automatically mounting a specific directory, at a specific host onto the local file system at a (configurable) standard location. The format of the configuration file is also tuned to minimise entry.

When run with no mounting specification (see below), the script will attempt to (re)mount all specifications from the configuration files if mounting had been lost. This can be run from a crontab or similar to ensure access to a number of remote locations at all time.

Configuration File Format

The file should always end with an empty line or a comment. Empty lines are ignored, comments are lines starting with the # (hash) character. Otherwise lines should contain a number of fields separated by the : (colon) sign. There should be at least one field. The fields are understood as follows:

  1. User and hostname specification, e.g. user@hostname, where the user is optional (and in which case there should not be an @ sign). When no user is present, the auto mounter will try looking for one in your private ssh configuration file at ~/.ssh/config. When none is found, the user will finally default to your username.
  2. Directory at remote location to mount locally. When this is empty, the entire home of the remote user will be mounted locally
  3. Port number at the remote location. When no port is present, the auto mounter will try looking for one in your private ssh configuration file at ~/.ssh/config. When none is found, this will finally default to 22.
  4. An alias to address the remote location and be used at the command-line for quicker typing, when empty, this will default to the user and hostname specification.

Options

  • -c or --config: Specifies the location of the configuration file. This defaults to automount.cfg in XDG_CONFIG_HOME, which itself defaults to ~/.config.
  • -o or --options: Specifies a list of specific options to give to sshfs when mounting the remote location. This defaults to a number of options meant to keep the connection alive as much as possible and resist network failures or hickups.
  • -f or --force: Force remounting of the remote, this will skip netcat accessibility check and might be necessary when tunneling through a bastion host.
  • -r or --root: Specifies the root location of where remote locations will be mounted on the local filesystem. This defaults to the ~/mnt/ directory. Sub-directories with the user and hostname specification will be automatically created.
  • -u or --unmount: Unmount instead of the default, which is mounting the remote location.
  • -n or --netcat: Location of netcat binary, which is used to test if the remote is accessible and responding at the specified port.

Apart from the options above, the script will take none or a series of remote specifications to be mounted or unmounted (depending on the presence of the -u flag). Specifications are either the entire combination of username and hostname, or the alias specified in the configuration file. When requested to mount, only remote locations that are not yet mounted will be attempted to be mounted (unless the -f flag is specified). When no specification are given, the script will attempt to mount (or unmount) all known locations from the configuration file.

#!/bin/bash
# This supposes key exchange has been done so sshfs works without password! Typical usage is:
# automount.sh -f host to force mounting a host that has been configured
# automount.sh from a cron job, that will check for all configured hosts and keep the mounting alive.
# Format of the config file, apart from comments and empty lines that are ignored is:
# username@hostname:dir:port:nickname
# Where the username, the port, the (remote) dir and the nickname can be ignored
# (default to running user and 22)
# set -x
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
CONFIG="${XDG_CONFIG_HOME%/}/automount.cfg"
ROOT="${HOME}/mnt"
FORCE=0
TIMEOUT=5
if [ "$(uname -s)" = "Darwin" ]; then
OPTIONS="-o intr,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,cache=no,nolocalcaches"
else
OPTIONS="-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3"
fi
UNMOUNT=0
NETCAT=""
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-c|--config)
CONFIG="$2"
shift # past argument
shift # past value
;;
-o|--options)
OPTIONS="$2"
shift # past argument
shift # past value
;;
-f|--force)
FORCE=1
shift # past argument
;;
-r|--root)
ROOT="$2"
shift # past argument
shift # past value
;;
-t|--timeout)
TIMEOUT="$2"
shift # past argument
shift # past value
;;
-u|--unmount)
# (forced) Unmount only.
UNMOUNT=1
shift # past argument
;;
-n|--netcat)
NETCAT="$2"
shift # past argument
shift # past value
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
# Try good defaults for netcat
if [ -z "${NETCAT}" ]; then
nc=$(which ncat)
if [ -n "${nc}" ]; then
NETCAT=${nc}
fi
fi
if [ -z "${NETCAT}" ]; then
nc=$(which netcat)
if [ -n "${nc}" ]; then
NETCAT=${nc}
fi
fi
checkmount() {
local spec=$1
sed '/^[[:space:]]*$/d' ${CONFIG} | sed '/^[[:space:]]*#/d' | while read line; do
line="${line}::::"; # Add plenty of colons for empty default fields
hspc=$(echo ${line} | cut -d: -f1)
dir=$(echo ${line} | cut -d: -f2)
p=$(echo ${line} | cut -d: -f3)
alias=$(echo ${line} | cut -d: -f4)
user=$(echo ${hspc} | cut -d@ -s -f1)
h=$(echo ${hspc} | cut -d@ -f2)
if [ -z "${p}" ]; then
if [ -e "${HOME}/.ssh/config" ]; then
p=$(grep -E "(Host\s+${h}|Port\s+)" ${HOME}/.ssh/config | tail -1 | sed -E 's/[[:space:]]*[Pp]ort[[:space:]]//')
if [ -z "$p" ]; then
p=22
fi
else
p=22
fi
fi
if [ -z "${user}" ]; then
if [ -e "${HOME}/.ssh/config" ]; then
user=$(grep -E "(Host\s+${h}|User\s+)" ${HOME}/.ssh/config | tail -1 | sed -E "s/^\s+User\s+//g")
if [ -z "${user}" ]; then
user="$USER"
fi
else
user="$USER"
fi
fi
if [ -z "${alias}" ]; then
alias="$hspc"
fi
if [ "${spec}" == "${alias}" -o "${spec}" == "${hspc}" ]; then
# Unmount if we'd force the unmounting or if we'd be requested to
# unmount only.
if [ "$FORCE" == "1" -o "$UNMOUNT" == "1" ]; then
while [ -n "$(mount|grep ${ROOT}/$alias)" ]; do
echo "Unmounting ${alias}"
if [ "$(uname -s)" = "Darwin" ]; then
umount ${ROOT}/${alias}
else
fusermount -uz ${ROOT}/${alias}
fi
sleep 1
done
fi
# Nothing else to do if we were requested to unmount only.
if [ "$UNMOUNT" == "0" ]; then
# Otherwise check if the host is already mounted and, if not,
# mount it at the proper location.
MOUNTED=$(mount|grep "${ROOT}/$alias")
if [ -z "$MOUNTED" ]; then
# (auto)make directory for the mountpoint
if [ ! -d "${ROOT}/${alias}" ]; then
echo "Making directory: ${ROOT}/${alias}"
mkdir -p "${ROOT}/${alias}"
fi
if [ "$(uname -s)" = "Darwin" ]; then
MOPTS="${OPTIONS} -o volname=$alias"
else
MOPTS="$OPTIONS"
fi
# Only attempt to mount if we actually can access the remote
# host at the (ssh) port.
if [ "$FORCE" == "1" ]; then
echo "Forced mounting ${hspc} (port $p)"
sshfs "${user}@${h}:${dir}" "${ROOT}/${alias}" -p ${p} ${MOPTS}
else
if [ -z "${NETCAT}" ]; then
echo "Blind mounting ${hspc} (port $p)"
sshfs "${user}@${h}:${dir}" "${ROOT}/${alias}" -p ${p} ${MOPTS}
else
if ${NETCAT} -w ${TIMEOUT} -z ${h} $p ; then
echo "Mounting ${hspc} (port $p)"
sshfs "${user}@${h}:${dir}" "${ROOT}/${alias}" -p ${p} ${MOPTS}
else
echo "No connection to ${hspc}:$p"
fi
fi
fi
fi
fi
fi
done
}
if [ "$#" == "0" ]; then
echo "Checking all hosts"
sed '/^[[:space:]]*$/d' ${CONFIG} | sed '/^[[:space:]]*#/d' | while read line; do
line="${line}::::"; # Add plenty of colons for empty default fields
hspc=$(echo ${line} | cut -d: -f1)
user=$(echo ${hspc} | cut -d@ -s -f1)
h=$(echo ${hspc} | cut -d@ -f2)
alias=$(echo ${line} | cut -d: -f4)
if [ -z "${alias}" ]; then
alias="$hspc"
fi
checkmount $alias
done
else
for spec in $@; do
checkmount $spec
done
fi
@efrecon
Copy link
Author

efrecon commented May 18, 2018

sshfs -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,IdentityFile=/home/me/.ssh/id_rsa user@server:/home/user/dir dirshare/ should help keeping the connection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment