O Cron apenas ocasionalmente envia e-mail em saída e erros

2

No Debian 8.1, estou usando um recurso Bash para detectar se o site stackoverflow.com está acessível:

(echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"

Isso é específico do Bash e não funcionará em sh , o shell padrão de cron .

Se, de propósito, tentarmos o script em sh , obteremos:

$ /bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent

Portanto, se eu apenas colocar o seguinte no meu crontab pessoal (sem definir SHELL to /bin/bash ) via crontab -e , espero que uma vez por minuto, o script seja executado e, portanto, espero também obter o erro acima enviado por email uma vez por minuto:

* * * * * (echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"

E, de fato, exatamente como esperado, vemos a partir de /var/log/syslog que a entrada é executada uma vez por minuto:

# sudo grep stackoverflow /var/log/syslog
Aug 24 18:58:01 localhost CRON[13719]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
Aug 24 18:59:01 localhost CRON[13723]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
Aug 24 19:00:01 localhost CRON[13727]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
...

Durante as últimas ~ 2 horas, isso foi executado mais de 120 vezes, já que posso verificar com o piping da saída para wc -l .

No entanto , destes > 120 vezes o comando shell (para repetir: o comando shell é inválido para /bin/sh ) foi executado, eu só tenho três e-mails:

O primeiro às 19:10:01, o segundo às 20:15:01 e o terceiro às 20:57:01.

O conteúdo de todos os três e-mails é lido exatamente como esperado e contém exatamente a mensagem de erro esperada da execução do script em um shell incompatível (propositalmente). Por exemplo, a segunda mensagem que recebi lê (e as outras duas são virtualmente idênticas):

From [email protected]  Mon Aug 24 20:15:01 2015
From: [email protected] (Cron Daemon)
To: [email protected]
Subject: Cron  (echo >/dev/tcp/stackoverflow.com/80)&>/dev/null || echo "stackoverflow unreachable"
...

/bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent'

De /var/log/mail.log , vejo que esses três e-mails foram os únicos e-mails enviados e recebidos nas últimas horas.

Assim, onde estão os > 100 e-mails adicionais que esperamos receber do cron devido à saída acima criada pelo script incorreto?

Para resumir:

  1. O correio está configurado corretamente neste sistema, posso enviar e receber e-mails sem problemas com /usr/bin/sendmail .
  2. Cron é configurado corretamente, percebe a tarefa como esperado e a executa precisamente nos horários configurados. Eu tentei muitas outras tarefas e opções de agendamento, e o cron executou todas exatamente como esperado.
  3. O script sempre grava a saída (veja abaixo) e esperamos que o cron envie a saída para mim por correio para cada chamada.
  4. A saída é enviada para mim apenas ocasionalmente e aparentemente ignorada na maioria dos casos.

Existem muitas maneiras de contornar o erro óbvio que levou às observações acima:

  1. posso definir SHELL=/bin/bash no meu crontab .
  2. Eu posso criar um heartbeat.sh com #!/bin/bash e invocar isso.
  3. Eu posso invocar o script com /bin/bash -c ... em crontab .
  4. etc, todos consertando o erro de usar um recurso específico do Bash dentro de sh .

No entanto, tudo isso não aborda o problema principal desta questão, que é que, neste caso, cron não envia emails de maneira confiável, embora o script sempre crie saída.

Eu verifiquei que o script sempre cria saída criando wrong.sh (que novamente de propósito usa o shell /bin/sh inadequado, para produzir o mesmo erro que cron deve ver):

#!/bin/sh
(echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"

Agora posso invocar o script em um loop e ver se existe um caso em que ele termina sem criar saída. Usando Bash :

$ while true; do [[ -n $(./wrong.sh 2>&1 ) ]]; echo  $?; done | grep -v 0

Mesmo em milhares de invocações, não consegui reproduzir um caso em que o script é concluído sem criar saída.

Qual pode ser a causa desse comportamento imprevisível? Alguém pode reproduzir isso? Para mim, parece que pode haver uma condição de corrida em que o cron pode perder a saída de um script, possivelmente envolvendo principalmente casos em que o erro deriva do próprio shell. Obrigada!

    
por mat 24.08.2015 / 21:42

2 respostas

4

Após mais testes, suspeito que o & esteja mexendo com seus resultados. Como você aponta, &>/dev/null é sintaxe bash , não sh . Como resultado, sh está criando um subshell e fazendo o background. Claro, o echo do subshell cria stderr, mas minha teoria é a seguinte:

  1. o cron não está pegando o stderr do subshell, e
  2. o plano de fundo do subshell sempre é concluído com êxito, evitando assim o || echo ... .

... fazendo com que o cron job não tenha saída e, portanto, não envie e-mails. Com base na minha leitura da fonte vixie-cron, parece que o stderr e o stdout do job seriam capturados pelo cron, mas ele deve estar se perdendo pelo subshell.

Teste você mesmo em um ambiente / bin / sh (supondo que você não tenha um arquivo chamado 'bar' aqui):

(grep foo bar) &
echo $?
    
por 25.08.2015 / 04:03
3

Eu poderia reproduzir o fenômeno no Ubuntu 15.04 com o seguinte crontab:

* * * * * { echo job 0; } & sleep 5
* * * * * { echo job 1; } &
* * * * * { sleep 5; echo job 2; } &

Eu recebi e-mails do cron com job 0 a cada minuto, e-mails com job 1 ocasionalmente (5-6 vezes nos últimos 10 minutos), sem e-mails com job 2 .

Portanto, parece que o cron aguarda a saída do processo filho e, em seguida, envia um email com toda a saída stdout / stderr que ele pode fazer slurp nesse momento. A saída atrasada do processo neto órfão é simplesmente descartada.

    
por 25.08.2015 / 06:50