Entendendo o padrão LSB Init do Linux

Quando você faz um script de inicialização para o Linux, você precisa (ou deve) criar um cabeçalho no arquivo do script. Este cabeçalho, apesar de possuir apenas ‘linhas comentadas’ e vai impedir seu script de funcionar, caso não esteja implementado corretamente. Este padrão (LSB) foi concebido pela Linux Foundation como uma forma de diminuir a diferença entre as infinitas distribuições de Linux, reduzindo o custo de portabilidade entre elas. Este padrão trata uma gama grande de itens, mas o foco deste post é nos scripts de inicialização.

Os scripts init são utilizados para iniciar, parar, reiniciar, recarregar (forçadamente ou não) e verificar o status de serviços e/ou softwares e são armazenados na pasta /etc/init.d/.

Sendo assim, se você vai criar um script de inicialização para o seu servidor de Minecraft, o seu init script será: /etc/init.d/minecraft. Para que o seu script esteja aderente ao padrão LSB, ele deve possuir:

  • No mínimo, diretivas para os comandos: start, stop, restart, force-reload e status;
  • Código de saída (exit code) adequado;
  • Ter documentado as dependencias dele (run-time dependencies)
  • [Opcional] Mensagens para sucesso (log_success_msg) e erro (log_failure_msg)

O padrão LSB provê uma série de funções que podem ser utilizadas e estão disponíveis no /lib/lsb/init-functions. Obviamente, você não precisa utilizar todas, mas se estiver com dúvidas quanto a forma de alguma função, este é um bom material de consulta. Outro detalhe importante é que estes scripts vão salvar o PID do seu processo em um arquivo no diretório /var/run. Isso pode ser util, caso algum script precise verificar o status do seu processo.

 

Bom, vamos ao script em si. Abaixo está um exemplo simples de um script para inicializar o banco de dados Postgre.

De acordo com a dica passada pelo leitor Edivaldo Alcantra, a partir da versão ubuntu2016-4 -debian 8

Isso é só o cabeçalho? Sim.  Vamos detalhar o que ele faz:

  • Todo o seu cabeçalho deve estar contido entre ### BEGIN INIT INFO e ### END INIT INFO;
  • Provides: especifica o nome do que este script está inicializando. Este nome deve ser único.
  • Required-start: Define quais são as dependencias deste script. Neste caso, para que o seu serviço de postgre (postgre_de_teste) possa ser iniciado, o postgresql e o networking já precisarão ter sido executados. Este ponto é importante para as inicializações sejam posicionadas de forma correta pelo comando update-rc.d.
    Lembrete: o postgresql que está ali possui um init script próprio com o nome postgresql no campo provides
  • Required-stop: Similar ao required-start, este item mostra o que precisa ser desligado assim que este serviço parar. Neste exemplo, assim que o nosso serviço (postgre_de_teste) parar, o networking e o postgresql serão desligados também;
  • Default-Start e Default-Stop: definições dos níveis de execução (run-levels) que este serviço precisa para ser iniciado ou finalizado;
  • Short-Description e Description: Bem auto explicativos, estes campos servem para descrever o que este serviço vai fazer. O short-description é limitado a 1 linha, mas o description pode ter várias. (lembre-se de incluir o # no inicio da linha);

 

Vamos caminhar um pouco com o script. A primeira coisa que devemos acrescentar é:

Estas linhas vão permitir que você utilize as funções padrões. Agora, vamos acrescentar algumas variáveis ao nosso script:

No código acima, acrescentamos 3 variáveis: NOME, DAEMON e PIDFILE. Elas serão utilizadas em diversos pontos do scripts. É importante chamar atenção para a variável DAEMON: Ela indica onde está o executável que será chamado por este script.

 

Como  o seguro morreu de velho, é bom colocar uma verificação de segurança:

Este teste verifica se o seu executável existe. Se não existir, encerra o script, retornando exit code 5.

 

Antes de passarmos para a próxima parte deste script, é bom relembrar uma coisa: Quando você chama um script passando uma linha de argumento, toda a linha de comando pode ser acessada durante a execução.

Exemplo:

O script acima vai ter a variável $0 com o nome do script (get_food.sh) e a $1 com o argumento passado (bacon).

Ok, continuando…

Comando de inicialização (start) do serviço. Todo o script  de start do serviço ficará dentro deste pedaço:

O ‘comando’ esac (case ao contrário) indica que o case acabou. Da mesma forma que o fi indica que o if acabou.

Primeira parte do Start:

O código acima verifica se o arquivo PID existe. Se existir, checa se o processo já está sendo executado e aborta se estiver, pois não faz sentido tentar executar novamente o mesmo serviço.

 

O próximo passo é iniciar o serviço em si…

A primeira coisa que o código acima faz é salvar uma mensagem de log “Iniciando o serviço postgre_de_teste“. Depois ele utiliza a função start-stop-daemon para inciar o serviço em si, passando os seguintes argumentos:

  • –start (ou -s): Ação desejada;
  • –quiet (ou -q): Omite mensagens informativas, exibindo apenas mensagens de erro;
  • –oknodo (ou -o): Retorna exit code 0 no lugar de 1, se não for possível iniciar o serviço (daemon);
  • –pidfile (ou -p): Utilizado para informar o nome do arquivo PID;
  • –exec (ou -x): Utilizado para informar o que deve ser executado;

Se este comando der certo, retorna 0, caso contrário, retorna 1 (indicando erro).

 

O próximo case é o stop, utilizado para encerrar o serviço.

No fonte acima, primeiro verificamos se o arquivo PID existe. Se existir, tentamos encerrar o processo e removemos o PID (afinal, o conteúdo dele não será mais válido). Se o arquivo PID não existir, consideramos que o processo não está sendo executado.

 

O próximo case é utilizado para reiniciarmos (restart) o serviço:

O script acima faz uma espécie de recursividade. Perceba que ele está utilizando a variável $0 (que contém o nome do script em questão) e utiliza ele para disparar outra instancia deste mesmo script 2x: uma para parar o serviço e outra para iniciar novamente.

Seria o equivalente a fazer isso:

O comando sleep faz com que a sequência de execução pause por 2 segundos. Temos agora mais dois cases para ver: Status e Reload.

 

Abaixo está o fonte para o case de Status:

Neste script, verificamos se o arquivo PID existe. Se existir, verificamos o status do processo (status_of_proc). Se o arquivo não existir, partimos do pressuposto de que o processo não está em execução.

 

Por útlimo, temos o case reload

Para este comando, também utilizamos o start-stop-daemon. Vou listar apenas os que não apareceram anteriormente:

  • –stop (ou -K): Encerra o processo. Indica a ação desejada;
  • –signal (ou -s): Envia um sinal de que algo deve ser feito. O padrão é TERM, que indica finalização rápida. O sinal USR1 indica que os arquivos de log devem ser reabertos.

 

Por último, mas não menos importante: um case final, que vai indicar como utilizar este script, caso seja passado algum parametro inválido:

 

Passamos por muitos cases e pedaços de script, então acho que vale lembrar: Eles devem estar entre o comando case $1 in e o esac.

 

 

Referências:
1. https://wiki.debian.org/LSBInitScripts

2. https://wiki.linuxfoundation.org/lsb/start

3. https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/iniscrptfunc.html

4. https://manpages.debian.org/wheezy/dpkg/start-stop-daemon.8.en.html

5. http://www.thegeekstuff.com/2012/03/lsbinit-script/

6. https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/

The following two tabs change content below.
Breno RdV
Ex-Psicólogo, com quase uma década de experiência em Recursos Humanos e Gestão de Pessoas, atual desenvolvedor e Analista de Sistemas, trabalhando com PowerBuilder, C#, PowerShell e expandindo horizontes para Python, Xamarin, PHP, Angular e (por que não?) Unity.

Comments

comments

Posted in Conhecimento Técnico, Dev and tagged , , , .

One Comment

  1. Pingback: Executar script ao inicializar o Linux (Ubuntu) – Raccoon Ninja

Comments are closed.