Envia SIGTERM para filho no Bash

69

Eu tenho um script Bash, que é semelhante a este:

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

Agora, se o bash shell executando o script receber um sinal SIGTERM, ele também deve enviar um SIGTERM para o servidor em execução (que bloqueia, portanto, não é possível capturar). Isso é possível?

    
por Lorenz 26.07.2014 / 20:54

4 respostas

71

Tente:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

Normalmente, bash ignorará quaisquer sinais enquanto um processo filho estiver sendo executado. A inicialização do servidor com & colocará em segundo plano no sistema de controle de tarefas do shell, com $! mantendo o PID do servidor (a ser usado com wait e kill ). Chamar wait irá então esperar que o trabalho com o PID (o servidor) especificado termine, ou que quaisquer sinais sejam disparados .

Quando o shell recebe SIGTERM (ou o servidor sai independentemente), a chamada wait retornará (saindo com o código de saída do servidor ou com o número de sinal + 127 se um sinal foi recebido). Posteriormente, se o shell receber SIGTERM, ele chamará a função _term especificada como o manipulador de traps SIGTERM antes de sair (no qual fazemos qualquer limpeza e propagamos manualmente o sinal para o processo do servidor usando kill ).

    
por 26.07.2014 / 22:21
64

O Bash não encaminha sinais como SIGTERM para processos nos quais está atualmente aguardando. Se você quiser terminar seu script seguindo para seu servidor (permitindo que ele lide com sinais e qualquer outra coisa, como se você tivesse iniciado o servidor diretamente), você deve usar exec , que será > substituir o shell com o processo sendo aberto :

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

Se você precisar manter o shell por algum motivo (ou seja, você precisa fazer alguma limpeza após o término do servidor), você deve usar uma combinação de trap , wait e kill . Veja resposta do SensorSmith .

    
por 14.04.2015 / 03:52
6

Andreas Veithen assinala que, se você não precisar retornar da chamada (como no exemplo do OP) simplesmente chamar o comando exec é suficiente ( resposta de @Stuart P. Bentley ). Caso contrário, o "tradicional" trap 'kill $CHILDPID' TERM (@ resposta do cuonglm) é um começo, mas a chamada wait retorna, na verdade, depois que o manipulador de trap é executado, o que ainda pode ser antes que o processo filho seja realmente encerrado. Portanto, uma chamada "extra" para wait é aconselhável ( @responde de user1463361 ).

Embora isso seja uma melhoria, ele ainda tem uma condição de corrida, o que significa que o processo nunca pode sair (a menos que o sinalizador repita o envio do sinal TERM). A janela de vulnerabilidade é entre registrar o manipulador de armadilhas e registrar o PID da criança.

O seguinte elimina essa vulnerabilidade (empacotada em funções para reutilização).

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term
    
por 18.05.2018 / 20:12
2

A solução fornecida não funciona para mim porque o processo foi eliminado antes que o comando de espera fosse concluído. Eu encontrei o artigo link , o último trecho funciona bem no meu caso de aplicativo iniciado em o OpenShift com corredor sh personalizado. O script sh é necessário porque preciso ter a capacidade de obter dumps de encadeamento, o que é impossível no caso de o PID do processo Java ser 1.

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
    
por 29.03.2018 / 11:27