Inicialização de Aplicação Durante o Boot com SystemD na Raspberry Pi 8

Em distribuições baseadas em Linux modernas o SystemD é o mecanismo de inicialização padrão. Você já deve se ter deparado com uma tela parecida com a abaixo, cheia de “Oks” verdes:

systemd na raspberry pi

Essa é a “tela” do SystemD. Cada Ok verde desse quer dizer que um serviço foi inicializado com sucesso. Essa imagem foi retirada durante o boot de um Raspberry Pi 3B, o Raspbian utiliza o SystemD como sistema de inicialização.

Sendo assim o SystemD é uma ferramenta que vale muito a pena ser estudada. Por exemplo, você cria sua aplicação baseada em Linux e Raspberry Pi, e ela deve começar a trabalhar já durante a inicialização sem que alguém deva logar no sistema e a inicializar manualmente. O que você vai utilizar? SystemD!

SystemD – System and Service Manager

O SystemD é uma serie de programas e bibliotecas, desenvolvido inicialmente pela Red Hat, que juntos fornecem funcionalidades básicas para uma distribuição Linux como: configurações do comportamento de serviços como sequência correta de inicialização e desligamento. Entre os utilitários que nós temos acesso em um terminal o mais utilizado é o systemctlesse programa é utilizado para inicializar, parar e verificar o estado de serviços pelo SystemD. Vamos estudar a utilização dos comandos para o systemctl abaixo no exemplo.

SystemD – Exemplo

Nesse exemplo vamos imaginar que temos uma aplicação, serviço, script que precisa ser inicializado imediatamento apos ligar uma Raspberry Pi, e também realizar algumas rotinas durante o desligamento da nossa Raspberry Pi.

Scripts – Blink

Primeiramente precisamos do nosso “serviço” ou programa, script que será inicializado durante o boot. Para isso vamos escrever um simples blink em bash script utilizando o acesso à GPIO  da nossa Raspberry Pi via SysFS:

#!/bin/bash

echo 24 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio24/direction
while [ 1 ]do
	echo 1 > /sys/class/gpio/gpio24/value
	sleep 0.2s
	echo 0 > /sys/class/gpio/gpio24/value
	sleep 0.2s
done

Vamos salvar esse script como blink.sh, ele será o script de inicialização do nosso serviço e ficará piscando um led na GPIO 24 do Raspberry Pi:

circuito systemd na raspberry pi

Com o arquivo salvo ainda temos que dar o privilégio de execução à ele:

chmod +x blinkConclusão.sh

Agora podemos executar o arquivo e testar se o led vai piscar:

./blink.sh

Esse script vai executar infinitamente enquanto o processo existir, para fechar o processo digite “ctrl+c”.

Agora vamos criar um script chamado blinkShutdown.sh com o seguinte contéudo:

#!/bin/bash
for (( c=1; c<=5; c++ )) do 
        echo 1 > /sys/class/gpio/gpio24/value
        sleep 0.09s
        echo 0 > /sys/class/gpio/gpio24/value
        sleep 0.09s
done

echo 24 > /sys/class/gpio/unexport

Você pode ver que esse script é bem parecido com o anterior, mas nesse não escrevemos no /sys/class/gpio/export e nem no /sys/class/gpio/gpio24/direction pois isso já foi feito no script anterior, que será o script de start do nosso serviço. O blinkShutdown.sh será nosso script de stop, ele vai piscar por 5 vezes bem rapidamente e fará o unexport do pino 24. Temos também que dar privilégios de execução para ele:

chmod +x blinkShutdown.sh

Unit File – Descrição do Serviço

Com os scripts do nosso serviço prontos podemos escrever o nosso Unit File que é o arquivo que define um serviço para o SystemD:

[Unit]Description=Vamos piscar leds.
[Service]Type=simpleExecStart=/home/pi/blink.shExecStop=/home/pi/blinkShutdown.sh
[Install]WantedBy=multi-user.target

Vamos salvar esse arquivo como blink.service. 

Agora vamos entender a estrutura desse arquivo:

  • [Unit]

    • Geralmente a primeira seção de um Unit File é aonde estão as informações de ajuda e descrição, e dependências sobre nosso serviço.

      • Diretiva Description vai dentro de Unit como o nome já entrega é uma descrição breve do nosso serviço, é o valor dessa diretiva que será escrita na “tela” do SystemD após o Ok verde, isso se nosso serviço for inicializado com sucesso. Caso contrário o valor será escrito após um Failed vermelho.

  • [Service]

    • Dentro de Service vamos ter as configurações sobre ações do nosso serviço.

      • Diretiva Type descreve a forma de como serão executados os processos, scripts, programas do nosso serviço.

      • Diretiva ExecStart arquivo do programa, aplicação, script que será executado ao inicializar o serviço. No nosso exemplo vamos colocar o caminho do arquivo blink.sh

      • Diretiva ExecStop arquivo do programa, aplicação, script que será executado ao parar o serviço. No nosso exemplo vamos colocar o caminho do arquivo blinkShutdown.sh

  • [Install]

    • Dentro de Install vamos descrever o comportamento de inicialização do serviço.

      • Diretiva WantedBy irá dizer ao SystemD quando nosso serviço será inicializado. Geralmente o SystemD tem uma ordem bem definida de quais serviços serão inicializados primeiramente, e quais serviços tem relações de dependências e devem esperar. O valor dessa diretiva então é o serviço, ou grupo alvo, ao qual nosso serviço faz parte e deve ser inicializado em conjunto. No nosso caso o padrão é o multi-user.target que geralmente nesse ponto as dependências mais básicas para que nossa distro Linux funcione corretamente já foram inicializadas.

Para colocar nosso serviço disponível para o SystemD temos que copiar o nosso blink.service para o diretório padrão de services do SystemD:

sudo cp blink.service /lib/systemd/system/

Com nosso Unit File no diretório do SystemD já podemos utilizar o utilitário systemctl para testar nosso serviço:

sudo systemctl start blink

Se tudo ocorrer certo nosso led começara a piscar, lembrando que no caso do comando systemctl start o SystemD vai executar nosso blink.sh como definimos em ExecStart. Também é importante chamar a atenção que quando utilizamos o systemctl podemos passar o nove inteiro no nosso Unit File ou só o nome antes da extensão .service, no nosso caso blink.service eu posso usar somente o nome blink que o utilitário ira entende.

Podemos também parar o serviço:

sudo systemctl stop blink

Nesse caso ele vai executar o blinkShutdown.sh como definimos em ExecStop, irá piscar o led rapidamente 5x e pronto nosso serviço está parado.

SystemD – Habilitar Serviço Durante Boot

Ok, mas nesse ponto se reiniciarmos nosso Raspbian podemos ver que o led não vai começar a piscar sozinho. Precisamos de mais um comando, esse muito importante pois irá habilitar nosso serviço pra executar durante o boot e durante o encerramento do Raspbian:

sudo systemctl enable blink

Pronto, agora temos um serviço habilitado a ser executado pelo SystemD de forma automática durante o boot e encerramento. Parar testar e só reiniciar o Raspbian.

Se você estiver utilizando o Raspberry Pi via serial, como estou utilizando, você poderá ver na “tela” do SystemD, antes de login, o nosso serviço sendo inicializado e recebendo um Ok:

circuito systemd na raspberry pi

Para testar se o led vai piscar 5x rapidamente e desligar, durante o encerramento, é só desligar o Raspbian ou reiniciar novamente. Também podemos ver durante o encerramento nosso serviço sendo listado com um Ok:

circuito systemd na raspberry pi

SystemD – Solução de Problemas

Caso ocorra um problema durante a inicialização de algum serviço ou ele comece a se comportar de forma inesperada podemos utilizar o systemctl  pra verificar seu estado e logs para solucionar o problema:

systemd na raspberry pi

No caso acima o systemctl status nos retornou que o serviço está ativo e funcionando corretamente.

No caso abaixo eu modifiquei nosso Unit File colocando o nome errado do arquivo do script para gerar um erro, confira o retorno do systemctl status:

systemd na raspberry pi

Conclusão

O SystemD é uma ferramenta muito poderosa que nos dá inúmeras possibilidades de configurações para inicialização de aplicações durante o boot de nossos sistemas embarcados baseados em distribuições Linux. Ele ainda vai além configurando comportamentos para encerramento da nossa aplicação, em caso de desligamento da placa e recuperação de falhas.

Vale muito a pena investir em tempo de estudo no SystemD.

Faça seu comentário

Acesse sua conta e participe

8 Comentários

  1. Pra mim não rolou ainda.
    Retorna o erro abaixo:
    update-rc.d: error: cannot find a LSB script for [meu script]

  2. Matheus, Boa tarde.
    Tenho um problema que nao consigo de jeito nenhum fazer funcionar.
    Sou meio leigo no assunto de Linux mas fuço bastante e dou meus pulos.
    Tenho um Raspberry PI 3 com Raspbian ligado num monitor e quero apenas que quando ele ligar, carregue de forma automatica o Chromium.
    Assisti seu video mas nao sei e no meu caso se aplica.
    POderia me dar uma luz?

    1. Antonio,

      Dê uma olhadinha nesse tutorial, tem um script que você pode aproveitar 🙂

      Abraços!
      Diogo – Equipe MakerHero

  3. Participei da Raspberry JAM Osasco, a palestra de Matheus Castello foi um diferencial bem vindo, tive ideia exata da arquitetura usada no projeto, mesmo com pouco tempo apresentou um ótimo conteúdo ! Difícil um jovem com um conhecimento exímio da área. Ganhou um divulgador do seu trabalho.

    Abraços
    Vanderlei Franco
    fb.com/professorvanderleifranco
    instagram.com/vanderfranco/

    1. Olá Vanderlei!

      O Matheus é um dos nossos SuperMakeres!

      Se quiser conhecer mais sobre o nosso projeto:
      https://www.makerhero.com/maker-hero/

      Abraços!

      André Rocha – Equipe MakerHero

  4. Parabéns pelo artigo Matheus, muito bem explicado.

    Vou aproveitar pra deixar uma dica também pra quem quer inicializar uma aplicação que utilize a interface (GUI), utilizei em um projeto, basta adicionar algumas coisas no arquivo.service, exemplo abaixo:

    [Unit]
    Description= Nome do seu serviço

    [Service]
    Environment=DISPLAY=:0
    Environment=XAUTHORITY=/home/pi/.Xauthority
    ExecStart=caminho do seu arquivo
    Restart=always
    RestartSec=10s
    KillMode=process
    TimeoutSec=infinity

    [Install]
    WantedBy=graphical.target

    Para conectar a tela principal então configuramos (Environment=DISPLAY=:0), e informamos à nossa aplicação onde encontrar as credenciais necessárias para usar o sistema X windows com (Environment=XAUTHORITY=/home/pi/.Xauthority)
    Nos meus teste percebi que não da pra saber muito bem quando o sistema X será iniciado, por isso aquelas duas linhas (Restart=always ; RestartSec=10s) que fazem o programa tentar reiniciar a cada 10 segundos caso ele falhe ou saia. O (KillMode=process) diz ao systemd para matar qualquer processo associado ao programa se o serviço falhar e o (TimeoutSec = infinity) significa que nós não queremos parar de tentar executar o programa.

    Na parte do [Install] o (WantedBy=graphical.target) diz para o sistema aguardar iniciar a interface gráfica para executar o programa.

    Valeu.

    1. Olá Viktor, obrigado pelas dicas, mas e no caso de por exemplo, eu precisar rodar um script python que acessa a gpio e tem que ser executado pelo sudo , como eu faria pra carregá-lo pelo systemD ?

      Obrigado

    2. Excelente, foi muito útil Viktor, parabéns e obrigado!