fragen stichworte

Remote-synchrone und dann asynchrone Ausführung mit SSH

TL; DR

Ich möchte ein Skript aus der Ferne ausführen, das synchron beginnt (damit der lokale Befehl ssh fehlschlägt, wenn die vorbereitenden Befehle fehlschlagen) und dann asynchron ausgeführt wird, wodurch der Befehl local ssh abgebrochen wird.

Details

Ich habe ein Skript auf einem Remote-Server, das langsam eine Multi-Gigabyte-Datei herunterlädt. Ich möchte dieses Skript über SSH vom lokalen Server mit dem SSH-Remoteserver 'nohup/path/to/script arguments' starten, aber die SSH-Verbindung beenden, wenn ich weiß, dass das Skript den Download erfolgreich gestartet hat. Nach dem Start dient die SSH-Verbindung keinem nützlichen Zweck. Während des Herunterladens schlägt sie irgendwo systematisch fehl und blockiert die Ausführung auf dem lokalen Server.

Ich kann nicht einfach ssh -f oder ssh & ausführen, da der Befehl auf dem lokalen Server fehlgeschlagen ist, wenn das Remote-Skript nicht gestartet wird, die ersten Befehle vor dem Download fehlschlagen oder die Remote Server ist nicht erreichbar.

Ich habe verschiedene nohup und screen Tricks ausprobiert. Das nächste, was ich bekam, war folgendes:

  • Vom lokalen Server gestartet:

    ssh -t me@remote-server 'screen -S long-download/path/to/download.sh'
    

    oder

    ssh -t me@remote-server 'nohup screen -S long-download/path/to/download.sh'
    
  • Wenn download.sh auf dem Remote-Server gestartet wurde:

    preliminary-instructions
    # if anything fails so far then the local server's ssh command should fail synchronously
    download-command &
    some-more-checking
    screen -d long-download # we can now safely end the ssh session
    

Aber irgendwie wird screen immer noch getötet…

Beispielwiedergabecode

  • Auf einem Remote-Server in /path/to/test.sh:

    #!/bin/bash
    # uncomment this line to validate synchronous failure
    sleep 10 && echo foo >/tmp/bar &
    screen -d long-download
    
  • Lokal:

    ssh -t me@remote-server 'nohup screen -S long-download/path/to/test.sh'
    

antworten

Schauen Sie sich die ssh-Subsysteme an. Ich mache etwas ähnliches, aber nicht mit einem Skript (d. H. Ich verwende a.out/elf-Programme.) Aber ich habe es gerade mit einem Bash-Skript getestet und es funktioniert.

Von der ssh-Manpage:

    -s      May be used to request invocation of a subsystem on the remote
         system.  Subsystems are a feature of the SSH2 protocol which
         facilitate the use of SSH as a secure transport for other appli-
         cations (eg. sftp(1)).  The subsystem is specified as the remote
         command.

Verwenden Sie -s (Kleinschreibung) auf dem Client, um das Subsystem

anzugeben

Fügen Sie einen anderen Subsystem-Eintrag zu sshd_config auf dem Server hinzu, der auf Ihr Skript verweist.

   # override default of no subsystems
Subsystem       logger /usr/local/libexec/my-logger.sh
Subsystem       sftp   /usr/libexec/sftp-server

Ihr Skript sollte wie jede andere Datei mit Ausführungsberechtigungen gestartet werden. Mein Beispiel:

#!/usr/local/bin/bash
logger "testing 1.2.3"
echo "return text from my-logger"
exec 0>&- # close stdin
exec 0<&- 
exec 1>&- # close stdout
exec 1<&- 
exec 2>&- # close stderr 
exec 2<&- 
logger "subsystem: nohup new script"
nohup/path/to/another/script/logger2.sh &  
logger "subsystem: the subsystem started script is now done"
exit 0  

Sie sollten in der Lage sein, nützlichen Fehlertext für Erfolg oder Fehler zurückzugeben. Die ssh-Verbindung (und somit stdin, stdout und stderr) ist solange aktiv, bis das Subsystem sie beendet. Das Subsystem kann weiterlaufen oder nicht.

Lassen Sie Ihr anderes Skript (logger2.sh oben) 5-10 Minuten lang schlafen, sodass Sie netstat verwenden können, um die ssh-Verbindung zu sehen, den ssh-Client wieder in der Shell und z. ps ax zeigt, dass das Skript noch im Hintergrund läuft. Dieses Skript muss möglicherweise auch fd's schließen. Es ist nur ein schnelles Beispiel. Viel Glück.

Alternative Lösung zu BostonDriver's Antwort: screen -d -m.

In async-launcher.sh:

#!/bin/bash
echo connected &&
#       preliminary-command &&
        screen -S index-fetch -d -m $(cd ${0%/*} && pwd -P)/long-download.sh

sleep 5    # ensure long-download.sh didn't immediately fail
if ((`screen -ls | grep index-fetch | wc -l`)); then
        echo ok
else
        echo ko
        exit 1
fi

Und long-download.sh kann alle asynchronen Befehle enthalten.

Starten Sie mit einem einfachen ssh me@remote-server/path/to/async-launcher.sh. Hat auch einen zusätzlichen Vorteil: Der Bildschirm kann zu einem späteren Zeitpunkt wieder angebracht werden.