ssh + here document - o Ctrl + C vai para o lado remoto?

0

Como parte de uma série de tarefas que eu tenho que fazer, eu tenho que construir um script bash que acesse um computador remoto, execute 3 comandos, aguarde que um processo termine em SIGINT (ou simplesmente termine) e então faça alguma limpeza depois.

Atualmente, estou usando este código:

#! /bin/bash

# local preparations
# ...

ssh -t [email protected] <<-'COMMANDS'

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID" INT HUP
    wait $executePID

    #cleanup code here

    echo "done. Logging out"
    sleep 2
    logout
COMMANDS

# Final local cleanup

Este código parece funcionar apenas até o comando wait (builtin). wait parece estar consumindo todos os comandos que vêm depois dele e, assim, quando tento enviar um SIGINT ( Ctrl + C ), ele parece falhar execute o conteúdo do trap e todo o código de limpeza.

Como posso consertar isso para que funcione da maneira esperada?

Eu não tenho permissão para dividir este arquivo bash em vários e não tenho permissão para criar scripts no computador remoto, mesmo que temporários.
Ambos os computadores estão executando o mesmo sabor do Linux.

    
por brunoais 03.11.2016 / 09:54

1 resposta

1

Explicação

Não é sobre wait . Eu acho que é isso que acontece:

Você usa << , então stdin de ssh é redirecionado para algum descritor através do qual todo o documento aqui flui.

Esta outra resposta explica como ssh é capaz de capturar Ctrl + C quando -t é usado. Isso é o que importa:

On the client side, ssh will try to set the tty used by stdin to "raw" mode […] Setting raw mode means that characters that would normally send signals (such as Ctrl+C) are instead just inserted into the input stream.

No seu caso, não é o mesmo que stdin dos seus usos locais de shell. tty usado pelo seu shell local é deixado intacto, nunca é definido para o modo "raw".

Então quando você apertar Ctrl + C ele age localmente e termina ssh . Neste momento, o lado remoto recebe SIGHUP . Seu trap funciona e mata java . Eu acho que há uma armadilha aqui: um trap executa algum código em resposta a um dado sinal, mas não impede que o sinal tenha seu efeito normal. Portanto, parece-me que seu java seria eliminado mesmo sem um trap , porque é um trabalho do shell que é finalizado em resposta a SIGHUP .

O shell que é terminado pára de ler e interpretar seu stdin . É por isso que tudo que segue wait é descartado.

Solução

commands() { cat <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

}

stty raw -echo; cat <(commands) - | ssh -t [email protected]; stty -raw echo

A última linha é o comando atual. Primeiro nós preparamos tty então Ctrl + C não pode atuar localmente. Então nós concatenamos os comandos e a entrada padrão, nós a passamos para o shell remoto. Eu não posso fazer isso com aqui documento diretamente, a função command é uma solução alternativa (seria mais fácil usar um arquivo regular, mas você disse que não pode usar mais de um). Após ssh e cat sair, definimos o tty para seu estado normal.

Ter um pseudo-terminal é essencial, portanto, certifique-se de que ssh -t funcione (use -tt , se necessário).

O shell remoto deve ler (buffer) todos os comandos de commands , somente então ele pode obter Ctrl + C ao pressioná-lo. Eu acho que isso significa que você não pode ter muito código após wait . Além disso, o que você quiser fazer depois de SIGINT deve ser executado dentro do trap . Estas são as razões pelas quais usei uma única função cleanup que faz tudo.

O código não é infalível. Pressione Ctrl + C muito cedo ou várias vezes e você se encontrará no shell remoto ou com um pouco "quebrado" local tty . Neste último caso, use o comando reset para redefinir seu tty .

Você deve pressionar uma tecla (por exemplo, Enter ) depois de ver "Conexão para ... fechado". A razão é que cat não notará que o canal está quebrado (porque ssh não está mais) até tentar escrever algo nele.

Alternativa

Se por algum motivo a solução acima não funcionar, use esta alternativa mais simples. O documento aqui é exatamente como antes. Neste caso, não mexemos com tty , Ctrl + C termina local ssh como acontece com o seu código original. A diferença (em relação ao seu código) é a trap da limpeza. Eu acho que seria o suficiente para capturar apenas SIGHUP .

ssh -t [email protected] <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

Observação: quando trap for acionado, cleanup exibirá uma mensagem, mas você não a verá porque seu ssh local já está desconectado. Você verá a mensagem somente se java sair sem o trap . Ainda seu código de limpeza ( # cleanup code here ) deve ser executado.

    
por 23.09.2017 / 02:59