Prever o PID do comando SSH previamente iniciado

4

Esta é a coisa mais estranha.

Em um script, inicio um túnel SSH assim:

ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar

Isso inicia uma instância ssh que entra no segundo plano e a execução do script continua. Em seguida, salve seu PID (para matá-lo mais tarde) usando o $ do bash! variável . Para que isso funcione, incluo & no comando ssh , mesmo que ele já entre em segundo plano sozinho (senão $! não contém nada). Assim, por exemplo, o seguinte script:

#!/bin/bash

ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar &
echo $!
pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar"

saídas

(some ssh output)
28062
28062

... duas vezes o mesmo PID, como esperado. Mas, agora, quando executo essa sequência exata de comandos do terminal, a saída PID por $! é errada (no sentido de que não é o PID da instância ssh ). Do terminal:

$ ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar &
[1] 28178
(some ssh output)
$ echo $!
28178
$ pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar"
28181

Nem sempre são 3 números separados. Eu também observei uma diferença de 1 ou 2. Mas nunca é o mesmo PID, como eu teria esperado e como é de fato o caso quando esta seqüência de comandos é executada dentro de um script.

  1. Alguém pode explicar por que isso está acontecendo? Eu pensei que poderia ser devido a chamada ssh inicial realmente bifurcando outro processo, mas então por que funciona dentro de um script?

  2. Isso também me fez duvidar se usar $! no meu script para obter o ssh PID como descrito acima sempre funcionará (embora tenha sido até agora). Isso é realmente confiável? Eu senti que era "mais limpo" do que usar pgrep ...

por Malte Skoruppa 18.09.2015 / 22:04

1 resposta

7

A variável $! do shell só conhece o pid do processo iniciado pelo shell. Como você suspeitava, a chamada ssh usando -f bifurca seu próprio processo para que ele possa ir para o plano de fundo, portanto, a árvore do processo geral se parece com [1]:

shell
|
+--ssh<1> (pid is $!)
   |
   +--ssh<2> (pid is different)

ssh<1> sai muito pouco depois da invocação; portanto, é improvável que o valor em $! seja útil. É ssh<2> que está realizando a comunicação remota e fazendo seu tunelamento para você, e a única maneira de obter seu PID de forma confiável é examinando a tabela de processos, como você está fazendo com pgrep [2]. O método pgrep provavelmente será o correto aqui.

Por que funciona no script, mas não de forma interativa, essa é provavelmente uma condição de corrida. Como você coloca o primeiro ssh em segundo plano, o shell e o ssh estão sendo executados simultaneamente, e ssh faz algumas autenticações criptográficas com CPU moderada e algumas viagens de ida e volta na rede. É provável que o pgrep que você executa no script esteja sendo executado antes que ssh<1> se forme para ir para o segundo plano. Para contornar isso, execute pgrep mais tarde, seja por uma chamada sleep , ou apenas ligando apenas quando você realmente precisar do PID mais tarde.

[1]: Tecnicamente, pode ser mais complicado do que isso, se ssh estiver usando um plano duplo clássico como plano de fundo. Nesse caso, haveria outro processo efêmero de ssh entre os dois.

[2]: A menos que você seja systemd e esteja usando cgroups ou algo para acompanhar todos os seus filhos. Que você não é.

    
por 18.09.2015 / 22:42