#!/bin/sh # # ssh_nfs_mnt: A ssh+nfs hack to mount nfs dirs on a PC (e.g. mobile laptop). # # Usage: # # ssh_nfs_mnt (mount the shares) # ssh_nfs_mnt -w (mount the shares and wait for ssh to finish) # ssh_nfs_mnt -u (unmount the shares) # # You can also specify any of the parameters described below on the cmd # line (after any -w or -u option), e.g.: # # ssh_nfs_mnt nfs_dirs=/home nfs_server=runge@hyades0.lbl.gov # # Notes: # # Since it does the local mounting, this script will run sudo(1) # when necessary. Hopefully your sudo(1) will only ask for the # password the first time (i.e. subsequent sudo's will not need # the password unless a timeout of 5 mins is reached). # # If you are not using an ssh-agent, you will need to type your # remote system password/passphrase twice (once for collecting # rpcinfo output and again for the actual port redir). # # If your userid number (run the "id" command to see what it is) # differs between the remote NFS server and the local machine # you may not be able to write and/or read the mounted files. # If possible try to make the number of the userid and groupid be # the same on both machines. Your mount(8) command may allow for # userid remapping. # # REMEMBER: before unmounting (via the -u option), you must cd # every shell out of the mounted areas and no application can have # any mounted file open. If you see 'device is busy' errors you # will know you forgot to do this. # # Except under -w wait mode, the ssh port redir command does a # 'sleep 60' so the mount performed by this script will have to # take place in that time period (note you may be prompted for a # password). Also, if you unmount (-u) quickly, you will have to # wait for the 'sleep 60' by the ssh to finish and the ports freed. # # For MacOS X (Darwin) you will need to make sure local servers nfs, # mountd, and portmap are NOT running before you run this script. # See the details below. If you find your nfs client OS is in # the same boat as MacOS X, specify uname=Darwin on the cmdline. # # For debugging output, use "-d" as the first argument. # # Only tested for Linux NFS servers. # Only tested for Linux NFS clients. # nfs_server="hyades0.lbl.gov" # user@hostname (passed to ssh) works also. # # nfs_server: this is used to ssh to the server machine. Note on Darwin # since the ssh must be run via sudo (for the low ports, portmapper 111, # etc) you may always need to supply the "user@" part and/or supply a # password (because ssh as root won't find your keys). If SSH_AUTH_SOCK # exists in the environment it will be used in the sudo. nfs_ip="10.0.0.220" # server IP relative to nfs_server. # # nfs_ip: Note localhost or 127.0.0.1 is another possibility here but # that probably requires an edit of /etc/exportfs on the nfs server # side to add 127.0.0.1 to a list, so it is likely better to use a real # network interface IP here. nfs_dirs="/opt/snf /home" # The remote shares to mount. # # nfs_dirs: A comma separated list is also acceptable (e.g. if you # specify the parmeter on the command line and want to avoid spaces). mountpoint="$HOME/hyades0" # Local directory to mount the shares in. #mountpoint="/tmp/hyades0" #mountpoint="/var/tmp/hyades0" # # Each share is a subdir of this directory, e.g. /tmp/hyades0/opt/snf # or $HOME/hyades0/opt/snf. Take your pick of the 3 suggestions above or # make your own. Note: the mountpoint dir should be on a local filesystem # (your $HOME may not be...). mount_opts="" # Supply any extra mount(8) opts here. # # You may want to use mount_opts for performance reasons, e.g. over # a high latency link. See your OS mount(8), mount_nfs(8), or nfs(5) # docs for more info. # # Consider: noatime, nodiratime, actimeo (or acregmin, acregmax, acdirmin, # acdirmax), timeo, retrans, rdirplus, etc. This example seems to give # an improvement: # # mount_opts=timeo=15,actimeo=300 # These are the default mount(8) options used, change them if you need to: # default_opts="tcp,nfsvers=3,rsize=8192,wsize=8192,intr,rw,nosuid" sleep=60 # Time of the initial sleep(1) in the # remote ssh cmd, e.g: ssh .... 'sleep 60' # (not used in wait mode -w). keepalive=30 # Period to send a "nfs ping" by running df(1). # (not used in wait mode -w). # keepalive: Some NFS clients will disconnect the TCP connection after, # say, 5 mins of inactivity. If the ssh initial sleep has finished # at that point the ssh connection will be closed. We are screwed at # that point because when NFS activity starts back up there is no ssh # TCP tunnel to the server for it to reconnect through. This option # tries to keep the link active by running df(1) frequently enough # (every keepalive seconds). Set to 0 to disable. # Port redirs for nfs and mountd port via ssh: # # port1 and port2 below are ssh redirection ports for nfs and mountd, # respectively. # # These are fixed local ports for now, but this could cause problems # with multiple instances of this script or hung redirs... # # You can use port1=NNNN, port2=MMMM on the command line to pick different # ones if you need to. # # On Darwin (MacOS X) NFS clients port1 and port2 below will be not # be used at all, the actual remote server ports will be used instead # (i.e. 2049 -> remote:2049). Of course this means those port numbers # and also portmap 111 may not be in use locally (i.e. you cannot run # the Darwin client as an NFS server at the same time). # port1=61001 # nfs redirection port (e.g. 61001 -> remote:2049) port2=61002 # mountd redirection port (e.g. 61002 -> remote:833) # Misc. commands: # ssh=ssh # you can override these too if you like. sudo=sudo ############################################################################# uname=${uname:-`uname`} localhost=localhost # Print out help and exit: # if [ "X$1" = "X-h" -o "X$1" = "X-help" -o "X$1" = "X--help" ]; then tail +2 $0 | sed -e '/^##########/ q' exit 0 fi # Debug mode: # if [ "X$1" = "X-d" -o "X$1" = "X--debug" ]; then shift set -xv fi echo "" # Function for keepalive: # df_keepalive() { n=$1 t=0 pid="" tmp=/tmp/df.$$.out while [ 1 ] do sleep $n t=`expr $t + $n` # Try to kill any hung df(1): # if [ "X$pid" != "X" ]; then for sig in INT TERM KILL do kill -$sig $pid 1>/dev/null 2>&1 sleep 1 done pid="" fi # Do the df "ping" in the background: # df 1>$tmp 2>&1 & pid=$! if [ $t -lt 60 ]; then # Too early to check. rm -f $tmp continue fi # Check if our mountpoints are no longer mounted (and # exit if they are) # sleep 5 if [ -s $tmp ]; then got=0 for dir in $nfs_dirs do if grep -i "^$localhost:$dir" $tmp > /dev/null; then got=`expr $got + 1` else if [ 1 = 0 ]; then echo No match for $dir cat $tmp fi fi done if [ $got = 0 ]; then rm -f $tmp echo echo "ssh_nfs_mnt: keepalive exiting at `date`" exit fi fi rm -f $tmp done exit } # Function to build and clean mountpoint path: # get_mountpoint() { dir_t=$1 mnt_t=`echo "$mountpoint/$dir_t" | sed -e 's,//*,/,g'` echo "$mnt_t" } # Function for mounting: # mount_them() { echo; echo "mounting the NFS shares..."; echo sleep 1 opts=$default_opts if [ $uname = "Linux" ]; then # port= and mountport= seem to be linux specific... opts="$opts,port=$port1,mountport=$port2" fi if [ "X$mount_opts" != "X" ]; then opts="$opts,$mount_opts" fi for dir in $nfs_dirs do mp=`get_mountpoint $dir` echo "mount -o $opts $localhost:$dir $mp" echo " (a local sudo password may be needed...)" echo $sudo mount -o $opts $localhost:$dir $mp sleep 1 done echo # Show them what we got: # echo "df(1) output:" df | grep "^$localhost:" | sed -e 's/^/ /' } # Function for unmounting: # unmount_them() { # Unmount all of them: # for dir in $nfs_dirs do # XXX could check if it is really mounted, e.g. /proc/mounts # or df(1). mp=`get_mountpoint $dir` echo umount $mp echo " (a local sudo password may be needed...)" echo $sudo umount $mp done } ############################################################################ # Unmount mode: # if [ "X$1" = "X-u" ]; then shift # They may specify nfs_dirs=... # for arg in $* do eval $arg done nfs_dirs=`echo "$nfs_dirs" | tr ',' ' '` unmount_them exit $? fi # Wait mode: # waitmode="" if [ "X$1" = "X-w" ]; then shift waitmode=1 fi # Apply command line parameter=value options: # for arg in $* do eval $arg done nfs_dirs=`echo "$nfs_dirs" | tr ',' ' '` if echo "$nfs_dirs" | grep '^[ ]*$' > /dev/null; then echo "nfs_dirs is empty, nothing to mount!" exit 0 fi # Create local mount point directories: # mkdir -p $mountpoint || exit 1 for dir in $nfs_dirs do mp=`get_mountpoint $dir` echo mkdir -p $mp mkdir -p $mp || exit 1 done # Retrieve the rpcinfo output so we can determine ports: # echo; echo "fetching remote 'rpcinfo -p' output via ssh..." echo " (a remote SSH password/passphrase may be needed...)" echo rpcinfo=`$ssh $nfs_server 'rpcinfo -p'` # nfs_port=`echo "$rpcinfo" | grep -w nfs | grep tcp | grep -w 3 \ | awk '{print $4}' | tail -1` mountd_port=`echo "$rpcinfo" | grep -w mountd | grep tcp | grep -w 3 \ | awk '{print $4}' | tail -1` # if [ "X$nfs_port" = "X" ]; then echo "Could not determine nfs port from:" echo "$rpcinfo" exit 1 fi if [ "X$mountd_port" = "X" ]; then echo "Could not determine mountd port from:" echo "$rpcinfo" exit 1 fi # Do the ssh port redir. # echo; echo "running ssh for port redirection..."; echo portmap="" cmd="$ssh" s_str=" (a remote SSH password/passphrase may be needed...)" if [ $uname = "Darwin" ]; then # # We need to do some additional hacks on MacOS X: # - redir portmap (111) # - run ssh via sudo (for low ports) # - use the port numbers 1:1 for the redir. # portmap="-L 111:$nfs_ip:111" cmd="$sudo $ssh" if [ "X$SSH_AUTH_SOCK" != "X" ]; then cmd="$sudo env SSH_AUTH_SOCK=$SSH_AUTH_SOCK $ssh" fi port1=$nfs_port port2=$mountd_port s_str=" (a local sudo password or remote SSH password/passphrase may be needed...)" fi ssh_cmd="$cmd -L $port1:$nfs_ip:$nfs_port -L $port2:$nfs_ip:$mountd_port \ $portmap -C -f $nfs_server" if [ "X$waitmode" = "X" ]; then echo "$cmd -L $port1:$nfs_ip:$nfs_port -L $port2:$nfs_ip:$mountd_port $portmap ..." echo "$s_str"; echo # Run ssh: # $ssh_cmd "sleep $sleep" # Start keepalive if instructed to: # if [ "X$keepalive" != "X0" -a "X$keepalive" != "X" ]; then echo echo "running df keepalive every $keepalive seconds..." echo df_keepalive $keepalive & fi # Try mounting them... # mount_them sleep 1 echo echo "To unmount run: " echo echo " ssh_nfs_mnt -u ..." echo echo "where ... is an optional nfs_dirs=... if you are not using the default." echo else # Run ssh watching for flag to disappear: # flag="/tmp/ssh_nfs_mnt.flag.$$" echo $ssh $nfs_server "touch $flag" echo " (a remote SSH password/passphrase may be needed...)" $ssh $nfs_server "touch $flag" echo echo "$cmd -L $port1:$nfs_ip:$nfs_port -L $port2:$nfs_ip:$mountd_port $portmap ..." echo "$s_str"; echo $ssh_cmd "perl -e 'while (1) {sleep 1; exit unless -f \$ARGV[0]}' $flag; echo SSH DONE." # Try mounting them... # mount_them # Wait for user response: # sleep 2 echo printf "Press Enter to unmount shares and terminate ssh> " read x echo sleep 1 # Unmount and signal ssh to exit.. # unmount_them echo $ssh $nfs_server "rm -f $flag" echo " (a remote SSH password/passphrase may be needed...)" $ssh $nfs_server "rm -f $flag" # Unmount once more for good measure: # sleep 1 unmount_them 1>/dev/null 2>&1 fi