pastebin - collaborative debugging tool
rovema.kpaste.net RSS


mount_sshnfs - mount NFSv4 filesystem through ssh tunnel
Posted by Anonymous on Fri 28th Jul 2023 13:41
raw | new post
view followups (newest first): mount_sshnfs - mount NFSv4 filesystem through ssh tunnel by Anonymous
modification of post by Anonymous (view diff)

  1. #!/bin/ksh93
  2.  
  3. #
  4. # mount_sshnfs - mount NFSv4 filesystem through ssh tunnel
  5. #
  6.  
  7. #
  8. # Example usage:
  9. #
  10. # 1. Mount&&unmount /export/home/rmainz on NFS server "/export/home/rmainz":
  11. # $ mkdir -p /foobarmnt
  12. # $ ksh mount_sshnfs.ksh mount ssh+nfs://rmainz@derfwpc5131/export/home/rmainz /foobarmnt
  13. # $ mount_sshnfs.ksh umount /foobarmnt
  14. #
  15. #
  16. # 2. Mount&&unmount /export/home/rmainz on NFS server "/export/home/rmainz" via SSH jumphost rmainz@10.49.20.131:
  17. # $ mkdir -p /foobarmnt
  18. # $ ksh mount_sshnfs.ksh mount -o ro,mount_sshnfs_jumphost=rmainz@10.49.20.131 ssh+nfs://rmainz@derfwpc5131/export/home/rmainz /foobarmnt
  19. # $ mount_sshnfs.ksh umount /foobarmnt
  20. #
  21. #
  22. # 3. See help and subcommand help:
  23. # $ mount_sshnfs.ksh --man
  24. # $ mount_sshnfs.ksh mount --man
  25. # $ mount_sshnfs.ksh umount --man
  26. #
  27.  
  28. #
  29. # Written by Roland Mainz <roland.mainz@nrubsig.org>
  30. #
  31.  
  32. function usage
  33. {
  34.         (( OPTIND=0 ))
  35.         getopts -a "${1}" "${2}" OPT '-?'
  36.         return 2
  37. }
  38.  
  39.  
  40. #    
  41. # simple netstat -n parser
  42. #    
  43. function netstat_list_connections
  44. {
  45.         set -o nounset
  46.         nameref data=$1
  47.  
  48.         compound out=( typeset stdout stderr ; integer res )
  49.  
  50.         out.stderr="${ { out.stdout="${ LC_ALL='POSIX' PATH='/usr/bin:/bin' netstat -a -n ; (( out.res=$? )) ; }" ; } 2>&1 ; }"
  51.         if (( out.res != 0 )) || [[ ${out.stderr} != '' ]] ; then
  52.                 return 1
  53.         fi
  54.  
  55.         typeset -a data.connections
  56.         typeset l
  57.         typeset leftover
  58.         integer dci=0 # data.connections array index
  59.  
  60.         while read l ; do
  61.                 leftover="${l/~(Elrx)
  62.                 (?: # non-capturing group
  63.                         #
  64.                         # regex group for tcp,udp
  65.                         #
  66.                         (tcp|tcp6|udp|udp6|raw|raw6|sctp|sctp6) # Proto
  67.                         [[:space:]]+
  68.                         ([[:digit:]]+)                  # Recv-Q
  69.                         [[:space:]]+
  70.                         ([[:digit:]]+)                  # Send-Q
  71.                         [[:space:]]+
  72.                         ([^[:space:]]+)                 # Local Address
  73.                         [[:space:]]+
  74.                         ([^[:space:]]+)                 # Foreign Address
  75.                         (?:
  76.                                 |
  77.                                 [[:space:]]+
  78.                                 ([^[:space:]]*?)        # State (Optional)
  79.                         )
  80.                 |
  81.                         #
  82.                         # regex for unix
  83.                         #
  84.                         (unix)                          # Proto
  85.                         [[:space:]]+
  86.                         ([[:digit:]]+)                  # RefCnt
  87.                         [[:space:]]+
  88.                         (\[.+?\])                       # Flags
  89.                         [[:space:]]+
  90.                         ([^[:space:]]+)                 # Type
  91.                         [[:space:]]+
  92.                         ([^[:space:]]*?)                # State (optional)
  93.                         [[:space:]]+
  94.                         ([[:digit:]]+)                  # I-Node
  95.                         (?:
  96.                                 |
  97.                                 [[:space:]]+
  98.                                 ([^[:space:]]+)         # Path (optional)
  99.                         )
  100.                 )
  101.                         /X}"
  102.  
  103.                 # If the regex above did not match then .sh.match
  104.                 # remains untouched, so we might see data from the
  105.                 # previous round.
  106.                 # So we check the "leftover" var whether it just
  107.                 # contains the dummy value of "X" to indicate a
  108.                 # successful regex match
  109.                 if [[ "$leftover" == 'X' ]] ; then
  110.                         #print -v .sh.match
  111.  
  112.                         if [[ "${.sh.match[1]-}" != '' ]] ; then
  113.                                 nameref dcn=data.connections[$dci]
  114.  
  115.                                 typeset dcn.proto="${.sh.match[1]}"
  116.                                 typeset dcn.recv_q="${.sh.match[2]}"
  117.                                 typeset dcn.send_q="${.sh.match[3]}"
  118.                                 typeset dcn.local_address="${.sh.match[4]}"
  119.                                 typeset dcn.foreign_address="${.sh.match[5]}"
  120.                                 typeset dcn.state="${.sh.match[6]}"
  121.                                 ((dci++))
  122.                         elif [[ "${.sh.match[7]-}" != '' ]] ; then
  123.                                 nameref dcn=data.connections[$dci]
  124.  
  125.                                 typeset dcn.proto="${.sh.match[7]}"
  126.                                 typeset dcn.refcnt="${.sh.match[8]}"
  127.                                 typeset dcn.flags="${.sh.match[9]}"
  128.                                 typeset dcn.type="${.sh.match[10]}"
  129.                                 [[ "${.sh.match[11]}" != '' ]] && typeset dcn.state="${.sh.match[11]}"
  130.                                 typeset dcn.inode="${.sh.match[12]}"
  131.                                 [[ "${.sh.match[13]}" != '' ]] && typeset dcn.path="${.sh.match[13]}"
  132.                                 ((dci++))
  133.                         fi
  134.                 else
  135.                         true
  136.                         #printf $"leftover=%q\n" "${leftover}"
  137.                 fi
  138.         done <<<"${out.stdout}"
  139.  
  140.         return 0
  141. }
  142.  
  143. function netstat_list_active_local_tcp_connections
  144. {
  145.         set -o nounset
  146.         nameref ar=$1
  147.         compound c
  148.         integer port
  149.         integer i
  150.  
  151.         netstat_list_connections c || return 1
  152.         #print -v c
  153.  
  154.         [[ -v ar ]] || integer -a ar
  155.  
  156.         for i in "${!c.connections[@]}" ; do
  157.                 nameref n=c.connections[$i]
  158.  
  159.                 # look for only for TCP connections which match
  160.                 # 127.0.*.* or IPv6 ::1 for localhost
  161.                 # 0.0.0.0 or IPv6 :: for all addresses (e.g. servers)
  162.                 if [[ "${n.proto}" == ~(El)tcp && \
  163.                         "${n.local_address}" == ~(Elr)((127\.0\..+|::1)|(::|0\.0\.0\.0|)):[[:digit:]]+ ]] ; then
  164.  
  165.                         port="${n.local_address##*:}"
  166.                         #printf $"port = %d\n" port
  167.  
  168.                         (( ar[port]=1 ))
  169.                 fi
  170.         done
  171.  
  172.         return 0
  173. }
  174.  
  175. function netstat_find_next_free_local_tcp_port
  176. {
  177.         set -o nounset
  178.         compound c=( integer -a ar )
  179.         nameref ret_free_port=$1
  180.         integer start_port
  181.         integer end_port
  182.         integer i
  183.  
  184.         netstat_list_active_local_tcp_connections c.ar || return 1
  185.  
  186.         #print -v c
  187.  
  188.         (( start_port=$2 ))
  189.         if (( $# > 2 )) ; then
  190.                 (( end_port=$3 ))
  191.         else
  192.                 (( end_port=65535 ))
  193.         fi
  194.  
  195.         for ((i=start_port ; i < end_port ; i++ )) ; do
  196.                 if [[ ! -v c.ar[i] ]] ; then
  197.                         (( ret_free_port=i ))
  198.                         return 0
  199.                 fi
  200.         done
  201.  
  202.         return 1
  203. }
  204.  
  205.  
  206. #
  207. # parse_rfc1738_url - parse RFC 1838 URLs
  208. #
  209. # Output variables are named after RFC 1838 Section 5 ("BNF for
  210. # specific URL schemes")
  211. #
  212. function parse_rfc1738_url
  213. {
  214.         set -o nounset
  215.  
  216.         typeset url="$2"
  217.         typeset leftover
  218.         nameref data="$1" # output compound variable
  219.  
  220.         # ~(E) is POSIX extended regular expression matching (instead
  221.         # of shell pattern), "x" means "multiline", "l" means "left
  222.         # anchor", "r" means "right anchor"
  223.         leftover="${url/~(Elrx)
  224.                 (.+?)                           # scheme
  225.                 :\/\/                           # '://'
  226.                 (                               # login
  227.                         (?:
  228.                                 (.+?)           # user (optional)
  229.                                 (?::(.+))?      # password (optional)
  230.                                 @
  231.                         )?
  232.                         (                       # hostport
  233.                                 (.+?)           # host
  234.                                 (?::([[:digit:]]+))? # port (optional)
  235.                         )
  236.                 )
  237.                 (?:\/(.*?))?/X}"                # path (optional)
  238.  
  239.         # All parsed data should be captured via eregex in .sh.match - if
  240.         # there is anything left (except the 'X') then the input string did
  241.         # not properly match the eregex
  242.         [[ "$leftover" == 'X' ]] ||
  243.                 { print -u2 -f $"%s: Parser error, leftover=%q\n" \
  244.                         "$0" "$leftover" ; return 1 ; }
  245.  
  246.         data.url="${.sh.match[0]}"
  247.         data.scheme="${.sh.match[1]}"
  248.         data.login="${.sh.match[2]}"
  249.         # FIXME: This should use [[ ! -v .sh.match[3] ]], but ksh93u has bugs
  250.         [[ "${.sh.match[3]-}" != '' ]] && data.user="${.sh.match[3]}"
  251.         [[ "${.sh.match[4]-}" != '' ]] && data.password="${.sh.match[4]}"
  252.         data.hostport="${.sh.match[5]}"
  253.         data.host="${.sh.match[6]}"
  254.         [[ "${.sh.match[7]-}" != '' ]] && integer data.port="${.sh.match[7]}"
  255.         [[ "${.sh.match[8]-}" != '' ]] && data.uripath="${.sh.match[8]}"
  256.  
  257.         return 0
  258. }
  259.  
  260.  
  261. function parse_sshnfs_url
  262. {
  263.         typeset url="$2"
  264.         nameref data="$1"
  265.  
  266.         parse_rfc1738_url data "$url" || return 1
  267.  
  268.         [[ "${data.scheme}" == ~(Elr)(ssh\+nfs|nfs) ]] || \
  269.                 { print -u2 -f $"%s: Not a nfs:// or ssh+nfs:// url\n" "$0" ; return 1 ; }
  270.         [[ "${data.host}" != '' ]] || { print -u2 -f $"%s: NFS hostname missing\n" "$0" ; return 1 ; }
  271.         [[ "${data.uripath}" != '' ]] || { print -u2 -f $"%s: NFS path missing\n" "$0" ; return 1 ; }
  272.  
  273.         return 0
  274. }
  275.  
  276.  
  277. function mountpoint2configfilename
  278. {
  279.         nameref configfilename=$1
  280.         typeset mountpoint="$2"
  281.  
  282.         #
  283.         # FIXME:
  284.         # - We should urlencode more than just '/'
  285.         # - We should strip the leading '/'
  286.         # - We should use realpath(1) for mountpoints here
  287.         #
  288.  
  289.         # .cpv means ComPound Variable"
  290.         configfilename="/tmp/mount_sshnfs/${mountpoint//\//%2f}.cpv"
  291.         return 0
  292. }
  293.  
  294.  
  295. function cmd_mount
  296. {
  297.         set -o nounset
  298.         nameref c=$1
  299.  
  300.         # fixme: Need better text layout for $ mount_sshnfs mount --man #
  301.         typeset -r mount_sshnfs_cmdmount_usage=$'+
  302.         [-?\n@(#)\$Id: mount_sshnfs mount (Roland Mainz) 2023-07-24 \$\n]
  303.         [-author?Roland Mainz <roland.mainz@nrubsig.org>]
  304.         [+NAME?mount_sshnfs mount - mount NFSv4 filesystem through ssh
  305.                 tunnel]
  306.         [+DESCRIPTION?\bmount_sshnfs mount\b mounts a NFSv4 filesystem
  307.                 through a ssh tunnel.]
  308.         [o:options?Use the specified mount options.
  309.                 The opts argument is a comma-separated list.\n
  310.                 options starting with mount_sshnfs_jumphost_* will be
  311.                 consumed by mount_sshnfs, all other options will be
  312.                 passed through to mount.nfs.
  313.                
  314.                 mount_sshnfs options are:
  315.                 -o mount_sshnfs_jumphost=user@host:port - ssh jumphost]:[options]
  316.  
  317.         url mountpoint
  318.        
  319.         [+SEE ALSO?\bksh93\b(1),\bssh\b(1),\bmount.nfs\b(8),\bnfs\b(5)]
  320.         '
  321.         typeset mydebug=false   # fixme: should be "bool" for ksh93v
  322.         typeset c.url
  323.         typeset c.mountpoint
  324.         typeset config_filename
  325.  
  326.         typeset -a c.mount_nfs_options
  327.         integer i
  328.         integer saved_optind_m1 # saved OPTIND-1
  329.         typeset s               # generic temporary string variable
  330.  
  331.         # remove subcmd name (in this case 'mount')
  332.         unset c.args[0]
  333.  
  334.         #
  335.         # Expand MOUNT_SSHNFS_CMDMOUNT_OPTIONS before arguments given to
  336.         # mount_sshnfs.ksh.
  337.         # By default we use IFS=$' \t\n' for argument splitting
  338.         #
  339.         c.args=( ${MOUNT_SSHNFS_CMDMOUNT_OPTIONS-} "${c.args[@]}" )
  340.  
  341.         #
  342.         # Argument parsing
  343.         #
  344.         while getopts -a "${progname} mount" "${mount_sshnfs_cmdmount_usage}" OPT "${c.args[@]}" ; do
  345.                 case "${OPT}" in
  346.                         'o')
  347.                                 #
  348.                                 # Split options like "-o foo=bar,baz=BAM"
  349.                                 # into "-o foo=bar -o baz=BAM" for easier
  350.                                 # processing below
  351.                                 IFS=$','
  352.                                 c.mount_nfs_options=( "${c.mount_nfs_options[@]}" ${OPTARG} )
  353.                                 IFS=$' \t\n'
  354.                                 ;;
  355.                         *)
  356.                                 usage "${progname} mount" "${mount_sshnfs_cmdmount_usage}"
  357.                                 return $?
  358.                                 ;;
  359.                 esac
  360.         done
  361.  
  362.         (( saved_optind_m1=OPTIND-1 ))
  363.  
  364.         # remove options we just parsed from c.args
  365.         for ((i=0 ; i < saved_optind_m1 ; i++)) ; do
  366.                 unset c.args[$i]
  367.         done
  368.  
  369.  
  370.         #
  371.         # Get remaining arguments
  372.         #
  373.         c.url="${c.args[saved_optind_m1+0]-}"
  374.         c.mountpoint="${c.args[saved_optind_m1+1]-}"
  375.  
  376.  
  377.         #
  378.         # Filter out our options, other options are passed to mount.nfs
  379.         #
  380.         for ((i=0 ; i < ${#c.mount_nfs_options[@]} ; i++)) ; do
  381.                 s="${c.mount_nfs_options[$i]}"
  382.  
  383.                 #
  384.                 # Intercept options starting with eregex mount_sshnfs.+
  385.                 #
  386.                 if [[ "$s" == ~(Elr)mount_sshnfs.+=.+ ]] ; then
  387.                         case "$s" in
  388.                                 ~(Eli)mount_sshnfs_jumphost=)
  389.                                         [[ ! -v c.ssh_jumphost_args ]] && typeset -a c.ssh_jumphost_args
  390.                                         c.ssh_jumphost_args+=( "-J" "${c.mount_nfs_options[i]/~(Eli)mount_sshnfs_jumphost=}" )
  391.                                         ;;
  392.                                 *)
  393.                                         usage "${progname} mount" "${mount_sshnfs_cmdmount_usage}"
  394.                                         return $?
  395.                                         ;;
  396.                         esac
  397.                         unset c.mount_nfs_options[$i]
  398.                 fi
  399.         done
  400.  
  401.  
  402.         #
  403.         # Parse url
  404.         #
  405.         parse_sshnfs_url c.nfs_server "${c.url}" || return 1
  406.  
  407.         mountpoint2configfilename config_filename "${c.mountpoint}"
  408.  
  409.         if [[ -f "${config_filename}" ]] ; then
  410.                 print -u2 -f $"%s: Config file %q for mount point %q found.\n" \
  411.                         "$0" \
  412.                         "$config_filename" \
  413.                         "${c.mountpoint}"
  414.                 return 1
  415.         fi
  416.  
  417.         #
  418.         # Prechecks for writing the config file
  419.         #
  420.         mkdir -p '/tmp/mount_sshnfs/'
  421.         if [[ ! -w '/tmp/mount_sshnfs/' ]] ; then
  422.                 print -u2 -f $"%s: mount_nfs data directory %q not writeable.\n" \
  423.                         "$0" \
  424.                         '/tmp/mount_sshnfs/'
  425.                 return 1
  426.         fi
  427.  
  428.         ${mydebug} && print -v c
  429.  
  430.         case "${c.nfs_server.scheme}" in
  431.                 'ssh+nfs')
  432.                         #
  433.                         # Find free local forwarding port
  434.                         #
  435.  
  436.                         # port on THIS machine
  437.                         integer c.local_forward_port
  438.  
  439.                         (( i=34049 ))
  440.                         netstat_find_next_free_local_tcp_port c.local_forward_port $i
  441.  
  442.                         c.ssh_control_socket_name="/tmp/mount_sshnfs/mount_sshnfs_ssh-control-socket_logname${LOGNAME}_ppid${PPID}_pid$$"
  443.  
  444.                         #
  445.                         # Find SSH login user name for NFS server
  446.                         #
  447.                         if [[ -v c.nfs_server.user ]] ; then
  448.                                 typeset c.nfsserver_ssh_login_name="${c.nfs_server.user}"
  449.                         fi
  450.                         # fixme: Implement NFSServerSSHLoginName
  451.                         if [[ ! -v c.nfsserver_ssh_login_name ]] ; then
  452.                                 # default user name if neither URL nor
  453.                                 # "-o NFSServerSSHLoginName=..." were given
  454.                                 typeset c.nfsserver_ssh_login_name="$LOGNAME"
  455.                         fi
  456.  
  457.                         #
  458.                         # Forward NFS port from server to local machine
  459.                         #
  460.                         # Notes:
  461.                         # - We use $ ssh -M ... # here as a way to terminate the port
  462.                         # forwarding process later using "-O exit" without the need
  463.                         # for a pid
  464.                         #
  465.                         print -u2 -f $"# Please enter the login data for NFS server (%s):\n" \
  466.                                 "${c.nfsserver_ssh_login_name}@${c.nfs_server.host}"
  467.  
  468.                         #
  469.                         # Notes:
  470.                         # - fixme: c.nfs_server.port is fixed
  471.                         # for ssh+nfs://-URLs, so for now we
  472.                         # have to hardcode TCP/2049 for now
  473.                         # - We use aes128-cbc,aes128-ctr ciphers for better
  474.                         # throughput (see https://bash-prompt.net/guides/bash-ssh-ciphers/
  475.                         # for a benchmark) and lower latency, as NFS is
  476.                         # a bit latency-sensitive
  477.                         # - We turn compression off, as it incrases latency
  478.                         #
  479.                         ssh \
  480.                                 -L "${c.local_forward_port}:localhost:2049" \
  481.                                 -M -S "${c.ssh_control_socket_name}" \
  482.                                 -N \
  483.                                 -f -o 'ExitOnForwardFailure=yes' \
  484.                                 -o 'Compression=no' \
  485.                                 -o 'Ciphers=aes128-cbc,aes128-ctr' \
  486.                                 "${c.ssh_jumphost_args[@]}" \
  487.                                 "${c.nfsserver_ssh_login_name}@${c.nfs_server.host}"
  488.                         if (( $? != 0 )) ; then
  489.                                 print -u2 -f $"%s: NFS forwarding ssh failed with error code %d\n" "$0" $?
  490.                                 return 1
  491.                         fi
  492.  
  493.                         # debug
  494.                         ${mydebug} && \
  495.                                 ssh \
  496.                                         -S "${c.ssh_control_socket_name}" \
  497.                                         -O 'check' \
  498.                                         "${c.nfsserver_ssh_login_name}@${c.nfs_server.host}"
  499.  
  500.  
  501.                         #
  502.                         # Build argument list for mount.nfs ...
  503.                         #
  504.                         typeset -a mount_args
  505.                         mount_args+=( '-vvv' )
  506.                         mount_args+=( '-t' 'nfs' )
  507.                         for s in "${c.mount_nfs_options[@]}" ; do
  508.                                 mount_args+=( '-o' "$s" )
  509.                         done
  510.                         mount_args+=( '-o' 'vers=4.2' )
  511.                         mount_args+=( '-o' "port=${c.local_forward_port}" )
  512.                         mount_args+=( "localhost:/${c.nfs_server.uripath}" )
  513.                         mount_args+=( "${c.mountpoint}" )
  514.  
  515.                         #
  516.                         # ... and do the mount
  517.                         #
  518.                         mount "${mount_args[@]}"
  519.                         (( retval=$? ))
  520.  
  521.                         if (( retval != 0 )) ; then
  522.                                 #
  523.                                 # Quit ssh port forwarding process
  524.                                 #
  525.                                 ssh \
  526.                                         -S "${c.ssh_control_socket_name}" \
  527.                                         -O 'exit' \
  528.                                         "${c.nfsserver_ssh_login_name}@${c.nfs_server.host}"
  529.                                 return $retval
  530.                         fi
  531.  
  532.  
  533.                         #
  534.                         # Save status data
  535.                         #
  536.                         compound mnt_config=(
  537.                                 typeset url="${c.url}"
  538.                                 typeset mountpoint="${c.mountpoint}"
  539.                                 typeset ssh_control_socket_name="${c.ssh_control_socket_name}"
  540.                                 typeset nfsserver_ssh_login_name="${c.nfsserver_ssh_login_name}"
  541.                                 typeset nfsserver_host="${c.nfs_server.host}"
  542.                         )
  543.                         print -v mnt_config >"$config_filename"
  544.  
  545.                         return 0
  546.                         ;;
  547.                 # fixme: Implement nfs://-URLs
  548.                 *)
  549.                         print -u2 -f $"%s: Unknown URL scheme %q\n" "$0" "${c.nfs_server.scheme}"
  550.                         return 2
  551.                         ;;
  552.         esac
  553.  
  554.         # notreached
  555. }
  556.  
  557.  
  558. function cmd_umount
  559. {
  560.         set -o nounset
  561.         nameref c=$1
  562.         integer retval
  563.         integer saved_optind_m1 # saved OPTIND-1
  564.  
  565.         typeset mydebug=false # fixme: should be "bool" for ksh93v
  566.         # fixme: Need better text layout for $ mount_sshnfs mount --man #
  567.         typeset -r mount_sshnfs_cmdumount_usage=$'+
  568.         [-?\n@(#)\$Id: mount_sshnfs umount (Roland Mainz) 2023-07-24 \$\n]
  569.         [-author?Roland Mainz <roland.mainz@nrubsig.org>]
  570.         [+NAME?mount_sshnfs umount - unmount NFSv4 filesystem mounted
  571.                 via mount_sshnfs mount]
  572.         [+DESCRIPTION?\bmount_sshnfs umount\b unmounts a NFSv4
  573.                 filesystem previously mounted via mount_sshnfs mount.]
  574.  
  575.         mountpoint
  576.        
  577.         [+SEE ALSO?\bksh93\b(1),\bssh\b(1),\bmount.nfs\b(8),\bnfs\b(5)]
  578.         '
  579.  
  580.         # remove subcmd name (in this case 'umount')
  581.         unset c.args[0]
  582.  
  583.         #
  584.         # Expand MOUNT_SSHNFS_CMDUMOUNT_OPTIONS before arguments given to
  585.         # mount_sshnfs.ksh.
  586.         # By default we use IFS=$' \t\n' for argument splitting
  587.         #
  588.         c.args=( ${MOUNT_SSHNFS_CMDUMOUNT_OPTIONS-} "${c.args[@]}" )
  589.  
  590.         #
  591.         # Argument parsing
  592.         #
  593.         while getopts -a "${progname} umount" "${mount_sshnfs_cmdumount_usage}" OPT "${c.args[@]}" ; do
  594.                 case "${OPT}" in
  595.                         *)
  596.                                 usage "${progname} umount" "${mount_sshnfs_cmdumount_usage}"
  597.                                 return $?
  598.                                 ;;
  599.                 esac
  600.         done
  601.  
  602.         (( saved_optind_m1=OPTIND-1 ))
  603.  
  604.         # remove options we just parsed from c.args
  605.         for ((i=0 ; i < saved_optind_m1 ; i++)) ; do
  606.                 unset c.args[$i]
  607.         done
  608.  
  609.  
  610.         #
  611.         # Get remaining arguments
  612.         #
  613.         c.mountpoint="${c.args[saved_optind_m1+0]-}"
  614.  
  615.         #
  616.         # Read configuration file for this mountpoint
  617.         #
  618.         typeset config_filename
  619.         mountpoint2configfilename config_filename "${c.mountpoint}"
  620.  
  621.         if [[ ! -f "${config_filename}" ]] ; then
  622.                 print -u2 -f $"%s: Config file %q for mount point %q not found.\n" \
  623.                         "$0" \
  624.                         "$config_filename" \
  625.                         "${c.mountpoint}"
  626.                 return 1
  627.         fi
  628.  
  629.         compound mnt_config
  630.         read -C mnt_config <"${config_filename}" || return 1
  631.  
  632.         ${mydebug} && print -v mnt_config
  633.  
  634.         #
  635.         # Do the unmount
  636.         #
  637.         umount "${c.mountpoint}"
  638.         (( retval=$? ))
  639.  
  640.         if (( retval != 0 )) ; then
  641.                 return $retval
  642.         fi
  643.  
  644.         #
  645.         # Quit ssh port forwarding process
  646.         #
  647.         ssh \
  648.                 -S "${mnt_config.ssh_control_socket_name}" \
  649.                 -O 'exit' \
  650.                 "${mnt_config.nfsserver_ssh_login_name}@${mnt_config.nfsserver_host}"
  651.  
  652.         rm -f "${config_filename}"
  653.         return 0
  654. }
  655.  
  656.  
  657. function main
  658. {
  659.         set -o nounset
  660.  
  661.         # fixme: Need better text layout for $ mount_sshnfs --man #
  662.         typeset -r mount_sshnfs_usage=$'+
  663.         [-?\n@(#)\$Id: mount_sshnfs (Roland Mainz) 2023-07-24 \$\n]
  664.         [-author?Roland Mainz <roland.mainz@nrubsig.org>]
  665.         [+NAME?mount_sshnfs - mount/umount NFSv4 filesystem via ssh
  666.                 tunnel]
  667.         [+DESCRIPTION?\bmount_sshnfs\b mounts/unmounts a NFSv4
  668.                 filesystem via ssh tunnel.]
  669.         [D:debug?Enable debugging.]
  670.  
  671.         mount [options]
  672.         umount [options]
  673.         status [options]
  674.         restart_forwarding [options]
  675.        
  676.         [+SEE ALSO?\bksh93\b(1),\bssh\b(1),\bmount.nfs\b(8),\bnfs\b(5)]
  677.         '
  678.  
  679.         compound c
  680.         typeset -a c.args
  681.         integer saved_optind_m1 # saved OPTIND-1
  682.  
  683.         #
  684.         # Expand MOUNT_SSHNFS_OPTIONS before arguments given to
  685.         # mount_sshnfs.ksh.
  686.         # By default we use IFS=$' \t\n' for argument splitting
  687.         #
  688.         c.args=( ${MOUNT_SSHNFS_OPTIONS-} "$@" )
  689.  
  690.         #
  691.         # Argument parsing
  692.         #
  693.         while getopts -a "${progname}" "${mount_sshnfs_usage}" OPT "${c.args[@]}" ; do
  694.                 case "${OPT}" in
  695.                         'D')
  696.                                 # fixme: Implement debugging option
  697.                                 ;;
  698.                         *)
  699.                                 usage "${progname}" "${mount_sshnfs_usage}"
  700.                                 return $?
  701.                                 ;;
  702.                 esac
  703.         done
  704.  
  705.         (( saved_optind_m1=OPTIND-1 ))
  706.  
  707.         # remove options we just parsed from c.args
  708.         for ((i=0 ; i < saved_optind_m1 ; i++)) ; do
  709.                 unset c.args[$i]
  710.         done
  711.  
  712.         #
  713.         # c.args mighth be a sparse array (e.g. "([1]=aaa [2]=bbb [4]=ccc)")
  714.         # right now after we removed processed options/arguments.
  715.         # For easier processing below we "reflow" the array back to a
  716.         # normal linear layout (e.g. ([0]=aaa [1]=bbb [2]=ccc)
  717.         #
  718.         c.args=( "${c.args[@]}" )
  719.  
  720.         #
  721.         # Subcommand dispatcher
  722.         #
  723.         case "${c.args[0]-}" in
  724.                 'mount')
  725.                         cmd_mount c
  726.                         return $?
  727.                         ;;
  728.                 'umount')
  729.                         cmd_umount c
  730.                         return $?
  731.                         ;;
  732.                 'status' | 'restart_forwarding')
  733.                         print -u2 -f $"%s: not implemented yet\n" "$0"
  734.                         return 2
  735.                         ;;
  736.                 *)
  737.                         print -u2 -f $"%s: Unknown command %q\n" \
  738.                                 "$0" "${c.args[0]-}"
  739.                         usage "${progname}" "${mount_sshnfs_usage}"
  740.                         return 1
  741.                         ;;
  742.         esac
  743.  
  744.         # notreached
  745. }
  746.  
  747.  
  748. #
  749. # main
  750. #
  751. builtin mkdir
  752. builtin basename
  753.  
  754. typeset progname="${ basename "${0}" ; }"
  755.  
  756. main "$@"
  757. exit $?
  758.  
  759. # EOF.

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at