Maneira correta de usar OnFailure no systemd

3

Eu tenho um serviço rodando software que gera alguns arquivos de configuração se eles não existem, e os leio se eles existirem. O problema que tenho enfrentado é que esses arquivos, às vezes, ficam corrompidos, tornando o software incapaz de iniciar e, assim, fazer com que o serviço falhe. Neste caso, gostaria de remover esses arquivos e reiniciar o serviço.

Eu tentei criar um serviço que deve ser executado em caso de falha, fazendo isso:

[Service]
ExecStart=/bin/run_program
OnFailure=software-fail.service

onde este serviço é:

[Service]
ExecStart=/bin/rm /file/to/delete
ExecStop=systemctl --user start software.service

O problema, no entanto, é que este serviço não é iniciado, mesmo quando o serviço falhou.
Eu tentei fazer

systemctl --user enable software-fail.service

mas começa sempre que o sistema é iniciado, como qualquer outro serviço.

Minha solução temporária é usar

ExecStopPost=/bin/rm /file/to/delete

mas esta não é uma maneira satisfatória de resolvê-lo, já que ele sempre excluirá o arquivo ao interromper o serviço, não importando se foi por falha ou não.

Saída quando falhando:

● software.service - Software
   Loaded: loaded (/home/trippelganger/.config/systemd/user/software.service; enabled;  vendor preset: enabled)
   Active: failed (Result: exit-code) since Fri 2018-05-04 09:05:26 CEST; 5s ago
  Process: 1839 ExecStart=/bin/run_program (code=exited, status=1/FAILURE)
 Main PID: 1839 (code=exited, status=1/FAILURE)



May 04 09:05:26 trippelganger systemd[595]: software.service: Main process exited, code=exited, status=1/FAILURE
May 04 09:05:26 trippelganger systemd[595]: software.service: Unit entered failed state.
May 04 09:05:26 trippelganger systemd[595]: software.service: Failed with result 'exit-code'.

Saída do systemctl - status do usuário-fail.service é:

● software-fail.service - Delete corrupt files
   Loaded: loaded (/home/trippelganger/.config/systemd/user/software-fail.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
    
por trippelganger 03.05.2018 / 17:07

2 respostas

2

OBSERVAÇÃO : Você provavelmente deseja usar ExecStopPost= em vez de OnFailure= aqui (veja minha outra resposta), mas isso está tentando abordar por que sua OnFailure= setup não está funcionando. / p>

O problema com OnFailure= não iniciar a unidade pode ser porque ela está na seção errada, ela precisa estar na seção [Unit] e não em [Service] .

Você pode tentar isso:

# software.service
[Unit]
Description=Software
OnFailure=software-fail.service

[Service]
ExecStart=/bin/run_program

E:

# software-fail.service
[Unit]
Description=Delete corrupt files

[Service]
ExecStart=/bin/rm /file/to/delete
ExecStop=/bin/systemctl --user start software.service

Eu posso fazer funcionar com essa configuração.

Mas note que usar OnFailure= não é o ideal aqui, já que você não pode realmente dizer por que o programa falhou, e encadear outro começo dele em ExecStop= chamando /bin/systemctl start diretamente é bem hacky ... solução usando ExecStopPost= e olhando para o status de saída é definitivamente superior.

Se você definir OnFailure= dentro de [Service] , systemd (pelo menos a versão 234 do Fedora 27) reclamará com:

software.service:6: Unknown lvalue 'OnFailure' in section 'Service'

Não tenho certeza se você está vendo isso em seus registros ou não ... (Talvez isso tenha sido adicionado no systemd recente?) Isso deve ser uma dica do que está acontecendo lá ... Espero que isso ajude.

    
por 04.05.2018 / 18:01
3

Para realizar alguma limpeza se o serviço falhar, use ExecStopPost= , que é executado se o serviço é bem-sucedido ou não.

No código executado em ExecStopPost= , você pode usar um dos $SERVICE_RESULT , $EXIT_CODE ou $EXIT_STATUS para determinar a condição de falha e agir de acordo. Consulte a documentação sobre essas variáveis de ambiente para verificar qual delas é mais adequada para você .

Então você pode usar Restart=on-failure para que o systemd tente reiniciar sua unidade quando ela falhar.

Juntando tudo, é assim que se parece. Assumindo que run_program sairá com status 2 sempre que os arquivos estiverem corrompidos (esperamos que você possa adaptar isso a outros cenários de falha da documentação acima), isso deve funcionar:

[Service]
ExecStart=/bin/run_program
ExecStopPost=/bin/sh -c 'if [ "$$EXIT_STATUS" = 2 ]; then rm /file/to/delete; fi'
Restart=on-failure

( OBSERVAÇÃO : O sinal de dólar duplo $$ é para escapar para systemd, então o shell vê $EXIT_STATUS e acessa essa variável. Usar um único sinal de dólar também funcionaria, mas, em seguida, o systemd faria essa substituição e o shell veria [ "2" = 2 ] , que também funciona ... De qualquer forma, você pode ignorar a maior parte disso colocando toda essa lógica em um shell script e chamando-a pelo caminho completo em ExecStopPost= , isso provavelmente seria melhor e você também poderia adicionar facilmente mais comandos ao script, como registrar a ação tomada para recuperar-se da condição de erro.

Espero que isto lhe dê indicações suficientes para descobrir como configurar isto corretamente, dada a sua situação particular!

    
por 03.05.2018 / 23:10