chadjoan

init.d/autossh.sh 2021-12-01

Dec 1st, 2021
1,108
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/sbin/openrc-run
  2. # Copyright 2011 Chad Joan
  3. # Distributed under the terms of the Boost License
  4.  
  5. depend() {
  6.     need sshd
  7.     use net dns logger NetworkManager
  8.     after dns sshd
  9. }
  10.  
  11. _print_ssh_command() {
  12.     eerror "  ssh -L $monitor_port:127.0.0.1:$monitor_port -R $monitor_port:127.0.0.1:$monitor_port2 -N -p $connect_port -R $listen_port:localhost:22 \"$bastion_username@$bastion_hostname\""
  13. }
  14.  
  15. # Returns "0" if things are fine, "1" if autossh/ssh crashed somehow.
  16. # If there are problems, then this will attempt to stop the daemon, kill any
  17. # related processes, and remove the pidfile.
  18. # If things are fine, this will declare AUTOSSH_PID and SSH_PID.
  19. _validate() {
  20.     success="0"
  21.  
  22.     # Make sure autossh is still running.
  23.     AUTOSSH_PID=$(cat $PID_PATH)
  24.     AUTOSSH_PID_TEST=$(ps -o pid= --pid $AUTOSSH_PID)
  25.     if [ -z "$AUTOSSH_PID_TEST" ]; then
  26.         eerror "Could not find process for recorded autossh pid $AUTOSSH_PID"
  27.         eerror "This means it crashed for some reason."
  28.         eerror "This is probably something autossh specific, rather than a problem"
  29.         eerror "with ssh connectivity.  For example, choosing the same monitor port"
  30.         eerror "for two different autossh services can cause one service to block"
  31.         eerror "the local listening port that the other need, thus causing autossh"
  32.         eerror "to exit with an error status."
  33.         eerror "If you want to test the ssh connectivity itself, here is an "
  34.         eerror "estimation of the command used by autossh:"
  35.         _print_ssh_command
  36.         success=1
  37.     fi
  38.  
  39.     # Make sure the child process (ssh itself, as spawned by autossh)
  40.     # exists.
  41.     AUTOSSH_PID=$(cat $PID_PATH)
  42.     if [ "$success" == "0" -a -n "$AUTOSSH_PID" ]; then
  43.         SSH_PID=$(ps -o pid= --ppid $AUTOSSH_PID)
  44.         if [ -z "$SSH_PID" ]; then
  45.             if [ "$verbosity" == "loud" ]; then
  46.                 eerror "Did not detect child PID for autossh instance with PID $AUTOSSH_PID."
  47.                 eerror "This likely indicates a connection failure."
  48.                 eerror "If you want to find out why, try running the following command manually:"
  49.                 _print_ssh_command
  50.                 eerror "The autossh instance will now be stopped."
  51.             fi
  52.             success=1
  53.  
  54.             # If autossh is still running, kill it.
  55.             AUTOSSH_PID_TEST=$(ps -o pid= --pid $AUTOSSH_PID)
  56.             if [ -n "$AUTOSSH_PID_TEST" ]; then
  57.                 start-stop-daemon --stop --quiet \
  58.                     --pidfile $PID_PATH \
  59.                     --signal 9 \
  60.                     --exec $EPREFIX/usr/bin/autossh
  61.                 kill $AUTOSSH_PID
  62.             fi
  63.  
  64.             # Delete the pidfile.
  65.             if [ -f "$PID_PATH" ]; then
  66.                 rm $PID_PATH
  67.             fi
  68.         fi
  69.     fi
  70.  
  71.     return $success
  72. }
  73.  
  74. _require_config_var() {
  75.     var_name=$1
  76.     eval var_contents=\$$var_name
  77.     if [ -z "$var_contents" ] ; then
  78.         eend "Required setting '${var_name}_$CONFIG_SUFFIX' not defined in /etc/conf.d/autossh; aborting."
  79.         exit 1
  80.     fi
  81. }
  82.  
  83. _get_config() {
  84.     # Removes the text before (and including) the first dot in the current
  85.     # service's filename, and assigns the result to the CONFIG_SUFFIX variable.
  86.     # Thus, if /etc/init.d/autossh.foo (presumably symlinked to this script)
  87.     # is started/stopped/whatever, then CONFIG_SUFFIX will equal "foo".
  88.     CONFIG_SUFFIX=${RC_SVCNAME#*.}
  89.  
  90.     # The contents of the /etc/init.d/autossh file will define various
  91.     # variables that define how we are supposed to interact with the machine
  92.     # we are tunneling from.  These "eval" statements will pick out the
  93.     # definitions that are relevant to the current named service, and assign
  94.     # them some easier-to-use variable names (global to this script).
  95.     # For example, the above /etc/init.d/autossh.foo service would pick out
  96.     # variables like host_foo, monitor_port_foo, and listen_port_foo, then
  97.     # assign the contained values to host, monitor_port, and list_port,
  98.     # respectively.
  99.     eval local_username=\$local_username_$CONFIG_SUFFIX
  100.     eval bastion_hostname=\$bastion_hostname_$CONFIG_SUFFIX
  101.     eval bastion_username=\$bastion_username_$CONFIG_SUFFIX
  102.     eval monitor_port=\$monitor_port_$CONFIG_SUFFIX
  103.     eval listen_port=\$listen_port_$CONFIG_SUFFIX
  104.     eval connect_port=\$connect_port_$CONFIG_SUFFIX
  105.     eval verbosity=\$verbosity_$CONFIG_SUFFIX
  106.  
  107.     _require_config_var "bastion_hostname"
  108.     _require_config_var "bastion_username"
  109.     _require_config_var "monitor_port"
  110.     _require_config_var "listen_port"
  111.  
  112.     if [ -z "$local_username" ]; then
  113.         # TODO: Get current user?
  114.         local_username="root"
  115.     fi
  116.  
  117.     if [ -z "$connect_port" ]; then
  118.         connect_port="22"
  119.     fi
  120.  
  121.     if [ -z "$verbosity" ]; then
  122.         # Be noisy by default.  Folks can quiet the service after any
  123.         # (very likely) troubleshooting is finished.
  124.         verbosity="loud"
  125.     fi
  126.  
  127.     # TODO: Configurability of monitor_port2 parameter.
  128.     monitor_port2=$(($monitor_port+1))
  129.  
  130.     # Now for some simpler stuff.  These make it easier to keep track of
  131.     # our pidfile and logfile.
  132.     PID_PATH="$EPREFIX/var/run/$RC_SVCNAME.pid"
  133.     LOG_PATH="$EPREFIX/var/log/$RC_SVCNAME.log"
  134. }
  135.  
  136. start() {
  137.     _get_config
  138.  
  139.     if [ "$verbosity" == "loud" ]; then
  140.         ebegin "Starting $RC_SVCNAME"
  141.     fi
  142.  
  143.     #einfo "local_username   == $local_username"
  144.     #einfo "bastion_username == $bastion_username"
  145.     #einfo "bastion_hostname == $bastion_hostname"
  146.     #einfo "monitor_port     == $monitor_port"
  147.     #einfo "listen_port      == $listen_port"
  148.     #einfo "connect_port     == $connect_port"
  149.     #einfo "PID_PATH         == $PID_PATH"
  150.  
  151.     #   AUTOSSH_PIDFILE="$PID_PATH" \
  152.     env \
  153.         AUTOSSH_GATETIME=2 \
  154.         AUTOSSH_FIRST_POLL=10 \
  155.         AUTOSSH_POLL=60 \
  156.         AUTOSSH_LOGLEVEL=3 \
  157.         AUTOSSH_DEBUG=1 \
  158.     start-stop-daemon --start --quiet \
  159.         --background \
  160.         --make-pidfile --pidfile $PID_PATH \
  161.         --stdout $LOG_PATH --stderr $LOG_PATH \
  162.         --exec $EPREFIX/usr/bin/autossh \
  163.         --user $local_username \
  164.         -- -M$monitor_port -N -p $connect_port -R $listen_port:localhost:22 "$bastion_username@$bastion_hostname"
  165.  
  166.     success=$?
  167.  
  168.     # Check to make sure things worked.
  169.     if [ "$success" == "0" -a -f "$PID_PATH" ]; then
  170.  
  171.         ## If the ssh tunnel fails, it won't happen right away.
  172.         ## We scan for a successful connection every 100ms and then give up
  173.         ## after (approximately) 2 seconds.
  174.         ## If it really did succeed, then the code afterwards will double check
  175.         ## it and then agree.
  176.         ## If it didn't succeed, then we'll have to wait a couple seconds
  177.         ## and then the later checks will notice missing PIDs or processes
  178.         ## then fail the service.
  179.         #for i in `seq 1 20`;
  180.         #do
  181.         #   #ssh -q 127.0.0.1:$monitor_port2 exit
  182.         #   netcat -z 127.0.0.1 $monitor_port2
  183.         #   if [ $? ]; then
  184.         #       break
  185.         #   fi
  186.         #   #connected=`netstat -ntl | grep -Pi "tcp\d*\s+\d+\s+\d+\s+127\.0\.0\.1:$monitor_port2\s+0\.0\.0\.0:\*\s+LISTEN"`
  187.         #   #einfo "connected = '$connected'"
  188.         #   #if [ $i -gt 2 ]; then
  189.         #   #if [ -n "$connected" ]; then break; fi
  190.         #   #fi
  191.         #   sleep 0.1
  192.         #done
  193.  
  194.         # So far all of the above failed to provide an accurate-and-fast
  195.         # positive confirmation.  Time is precious, so I'm going to give up
  196.         # for now and use a shitty constant delay.  0.2 seems to work well.
  197.         sleep 0.2
  198.  
  199.         _validate
  200.     fi
  201.  
  202.     if [ "$success" != "0" -a "$verbosity" == "loud" ]; then
  203.         eend "Failed to start $RC_SVCNAME"
  204.     fi
  205.  
  206.     return $success
  207. }
  208.  
  209. stop() {
  210.     _get_config
  211.     if [ "$verbosity" == "$loud" ]; then
  212.         ebegin "Stopping $RC_SVCNAME"
  213.     fi
  214.     #einfo "bastion_hostname == $bastion_hostname"
  215.     #einfo "monitor_port     == $monitor_port"
  216.     #einfo "listen_port      == $listen_port"
  217.  
  218.     _validate
  219.     success=$?
  220.  
  221.     # In this function we will use the following convention for the $success
  222.     # variable:
  223.     # 0 = Success
  224.     # 1 = Failed to stop autossh
  225.     # 2 = autossh already crashed
  226.     if [ "$success" == "1" ]; then success="2"; fi
  227.  
  228.     # If it isn't already messed up, try to stop it.
  229.     if [ "$success" == "0" ]; then
  230.         # Use the start-stop-daemon to attempt to stop autossh and clean
  231.         # up any other state that this script might miss.
  232.         if [ -f "$PID_PATH" ]; then
  233.             start-stop-daemon --stop --quiet \
  234.                 --pidfile $PID_PATH \
  235.                 --exec $EPREFIX/usr/bin/autossh
  236.                 #--signal 9 \
  237.             if [ "$?" != 0 ]; then success="1"; fi
  238.         else
  239.             ewarn "$PID_PATH does not exist.  There may be a stray autossh instance running."
  240.             success="1"
  241.         fi
  242.  
  243.         # Just incase the autossh process doesn't go down peacefully, we can
  244.         # use the SSH_PID variable defined by _validate to kill the ssh
  245.         # process. (First make sure the ssh process is still running.)
  246.         # Typically this will convince autossh to close itself.
  247.         SSH_PID=$(ps -o pid= --pid $SSH_PID)
  248.         if [ -n "$SSH_PID" ]; then
  249.             kill $SSH_PID
  250.             if [ "$?" != "0" ]; then success=1; fi
  251.         fi
  252.  
  253.         # One last chance: we'll look for proof-positive this time.
  254.         AUTOSSH_PID=$(ps -o pid= --pid $AUTOSSH_PID)
  255.         if [ -z "$AUTOSSH_PID" ]; then success=0; fi
  256.     fi
  257.  
  258.     if [ "$success" == "2" ]; then
  259.         ewarn "$RC_SVCNAME: autossh was already stopped or crashed."
  260.         success=0
  261.     fi
  262.  
  263.     if [ "$success" != "0" -a "$verbosity" == "loud" ]; then
  264.         eend "Failed to stop $RC_SVCNAME"
  265.     fi
  266.  
  267.     return $success
  268. }
  269.  
  270.  
RAW Paste Data