Os scripts de inicialização do Linux são utilizados desde os primórdios dos tempos (oh!) para não apenas colocar serviços e daemons para rodar na inicialização, mas para controlar os serviços em si no dia-a-dia. Tarefas como: descobrir se o postfix está rodando; iniciar aquele serviço que não subiu direito; reiniciar ou recarregar um daemon que você mudou a configuração; entre outros. E neste quesito, durante anos os scripts init no Linux foram lineares e simples… Entretanto, muita coisa mudou e foram surgindo outras funcionalidades.
Quando falamos em Init, podemos dizer que é o primeiro processo executado pelo Linux. Logo após o kernel ser carregado em memória, ele tem a opção de executar um programa. Este programa é o que chamamos de init. O init geralmente é o responsável por dizer quais os programas e/ou serviços devem ser iniciados logo após o carregamento do kernel e a detecção de hardware da máquina. Curiosamente, se você utilizar o comando ps, verá que o processo de PID número 1 é o sistema de init da sua distribuição.
Os scripts init clássicos, que surgiram nas primeiras distribuições Linux e continuam até hoje em muitas, são essas: SystemV e BSD-like. Estes sistemas eram lineares e bloqueadores, ou seja, um serviço vai sendo iniciado após o outro. Se um deles travar (demorando demais), a inicialização de todo o sistema vai ter que ficar esperando. Isso dá uma simplicidade grande na hora de adicionar serviços e criar scripts.
Com o tempo as distribuições Linux foram evoluindo e os desenvolvedores resolveram fazer melhorias nestes sistemas de inicialização. Algumas das melhorias tem como o objetivo:
- Poder executar serviços paralelamente;
- Lidar melhor com as dependências entre um serviço e outro (exemplo: precisa de rede para iniciar um servidor web);
- Adicionar ou remover dispositivos (pendrives, discos, entre outros) e recarregar certos serviços quando isso ocorrer;
- Não depender apenas de um arquivo com o PID para saber se o serviço está rodando bem…
E dentre algumas novas implementações de sistemas init, as que ganharam mais reconhecimento e uso foram: Upstart e systemd.
Na prática, se você colocar um shell script que faz tudo que você quer na inicialização e mandar o kernel carregar este script (parâmetro init=/seu/script), ele vai fazê-lo. Mas convenhamos: isso não é nada prático! :) Melhor mesmo é usar o que já fizeram de bom, que é o que vamos aprender aqui.
BSD-like
O sistema BSD-like começou como falei antes: um shell script que é executado logo após o kernel e roda tudo que você quer. Mexer nele era bem simples: bastava editar o arquivo /etc/rc e sucesso. O conteúdo do script é executado linearmente como um shell script comum. Houve algumas melhorias com o tempo, em que você além de poder mexer no arquivo /etc/rc, pode também criar shell scripts executáveis dentro do diretório /etc/rc.d, que podem ser executados em uma ordem específica, começando a implementar as funcionalidades de dependências.
Por ser um sistema init que não é tão usado pelas distribuições Linux, sai fora do escopo e por isso não entraremos em detalhes.
SystemV
O sistema SystemV foi um dos mais utilizados (e por mais tempo) em diversas distribuições Linux e por isso é o mais conhecido entre os usuários. Ele funciona da seguinte forma:
- O kernel carrega o programa init, que é o binário principal do SystemV init;
- O init lê o arquivo de configuração /etc/inittab e identifica a linha que começa com si::sysinit: (system initialization). Esta linha contém o shell script que o init vai carregar logo após o kernel;
- O shell script /etc/rc.sysinit é então executado. Ele contém todas as tarefas básicas do sistema pós-kernel: monta os psudeo sistemas de arquivos (proc, sys, entre outros), carrega módulos do kernel, define configurações de kernel (sysctl), monta e verifica sistemas de arquivos, entre outros;
- Quando o script completa sua execução, o init espera por um nível de execução (runlevel). Como ele não tem nenhum ainda, ele lê a linha do /etc/inittab que tem a definição initdefault, e muda para este runlevel. Por exemplo, em sistemas baseados em Red Hat:
# inicializa com o runlevel 3 (modo multi-usuário completo) id:3:initdefault:
- Ainda no /etc/inittab, existem linhas que indicam o que vai ser executado em cada runlevel. Neste caso, o SystemV usa as seguintes linhas:
l0:0:wait:/etc/rc.d/rc 0 l1:1:wait:/etc/rc.d/rc 1 l2:2:wait:/etc/rc.d/rc 2 l3:3:wait:/etc/rc.d/rc 3 l4:4:wait:/etc/rc.d/rc 4 l5:5:wait:/etc/rc.d/rc 5 l6:6:wait:/etc/rc.d/rc 6
Cada linha dessa executa o script /etc/rc.d/rc com o parâmetro do runlevel que o init mudou. Por exemplo, se o runlevel for 3, o init executa o /etc/rc.d/rc 3.
- O script /etc/rc.d/rc executa todos os arquivos executáveis do diretório /etc/rcX.d, onde X é o runlevel. Se o runlevel for 3, ele vai usar o diretório /etc/rc3.d;
- Dentro desses diretório, há diversos links simbólicos começando com K (de kill, ou seja, para o processo) ou S (de start, ou seja, inicia o processo). Os arquivos são executados em ordem alfabética, então ele executa o que começa com K e depois o que começa com S;
- Por fim, esses arquivos são todos links simbólicos para os reais scripts no diretório /etc/init.d. Estes scripts aceitam como parâmetros start (quando o link começa com S, o init chama o script com o parâmetro start) e stop (quando o link começa com K, o init chama o script com o parâmetro stop).
Ufa!
Formato do script init
O shell script do /etc/init.d deve aceitar, no mínimo, os parâmetros start e stop. Nada melhor que um exemplo para mostrar:
- Exemplo: /etc/init.d/meu-servico
#!/bin/bash # ### BEGIN INIT INFO # Provides: meu-servico # Required-Start: $network # Required-Stop: $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: O Meu Serviço # Description: Uma descricao mais completa sobre o meu serviço ### END INIT INFO # chkconfig: 2345 95 20 start() { echo 'Eu executei start!' > /var/log/meu-servico.log } stop() { echo 'Eu executei stop!' > /var/log/meu-servico.log } restart() { stop start } case "$1" in start) start ;; stop) stop ;; restart) restart ;; *) echo $"Usage: $0 {start|stop|restart}" exit 1 esac exit $?
Depois é só tornar o script executável:
chmod 755 /etc/init.d/meu-servico
- O conteúdo entre o bloco BEGIN INIT INFO / END INIT INFO contém informações no padrão LSB e é recomendado usar. Estas informações incluem: que runlevels o script deve iniciar e parar, do que ele depende, descrições, entre outros. Para mais informações, consulte esta página no site do Debian.
- A linha no começo, com o chkconfig:, é uma definição para as distribuições baseadas em Red Hat (que usam o comando chkconfig para gerenciar esses scripts). Os números depois dele são: runlevels que devem ser escritos, a prioridade de start e a prioridade de stop.
Tenha em mente também que são shell scripts, então apesar do funcionamento ser geralmente o mesmo, o código pode variar bastante de script para script. Há uma diversidade de scripts em cada serviço de cada distribuição. Por isso, antes de fazer um script para seu serviço, dê uma olhada nos que já existem para sua distribuição e não se esqueça de aprender shell script :-)
Usando
Na prática, você já pode executar o script init com os comandos:
service meu-servico start service meu-servico stop service meu-servico restart
ou (prefira o service aos comandos diretos abaixo):
/etc/init.d/meu-servico start /etc/init.d/meu-servico stop /etc/init.d/meu-servico restart
Execute os comandos e veja o arquivo de log /var/log/meu-servico.log para cada um deles.
Em outros scripts, geralmente há também o parâmetro status, que dá a atual situação do serviço: ele está rodando ou não? O status funciona assim:
- Quando o serviço inicia, ele cria um arquivo contendo o PID do próprio serviço. Esses arquivos geralmente ficam em /var/run e terminam com extensão .pid;
- O shell script de init ao receber o parâmetro status procura esse arquivo. Se ele existir, o processo está rodando no PID contido no arquivo. Se o arquivo não existir, é porque o serviço não está rodando.
- Quando o serviço para, ele remove o arquivo contendo o seu PID.
Gerenciando os serviços na inicialização (runlevel)
Com o script colocado no lugar certo e com permissão de execução, existem comandos que gerenciam estes scripts e os colocam (fazem os links simbólicos) nos diretórios /etc/rcX.d corretamente.
- Distribuições baseadas em Red Hat
# inclui um serviço chkconfig --add meu-servico # habilita o serviço na inicialização chkconfig meu-servico on # desabilita o serviço na inicialização chkconfig meu-servico off # retira todos os links simbolicos chkconfig --del meu-servico # lista todos os serviços, e quando estão habilitados e desabilitados em quais runlevels chkconfig --list # lista apenas um serviço chkconfig --list meu-servico
- Distribuições baseadas em Debian e Ubuntu
# inclui um serviço com prioridade 20 (o link fica como S20meu-servico) update-rc.d meu-servico defaults # inclui um serviço com prioridade 80 (o link fica como S90meu-servico) update-rc.d meu-servico defaults 80 # retira todos os links simbolicos (-f é para forçar mesmo que o script exista no /etc/init.d) update-rc.d -f meu-servico remove
Bônus! Como funciona o /etc/inittab
O arquivo /etc/inittab contém a configuração principal do SystemV init. Na prática, para tudo que o comando init faz, este arquivo é consultado primeiro. Nele, temos uma configuração para cada linha. Anteriormente vimos que o init, ao mudar para o runlevel 3, executou uma série de arquivos dentro do /etc/rc3.d. Veja a linha dentro do /etc/inittab que fez isso:
l3:3:wait:/etc/rc.d/rc 3
Podemos dividir essa linha em campos separados por dois pontos (:), tendo assim os campos:
id:runlevels:ação:comando
Cada campo representa:
- id: um ID unico para identificar a linha, com até 4 caracteres.
- runlevels: quais os runlevels que essa linha vai funcionar (2345 significa que funciona no 2, no 3, no 4 e no 5)
- ação: o que fazer com essa linha (que ação tomar), explicado mais pra frente
- comando: qual comando/processo deve ser executado
Voltando ao exemplo anterior:
l3:3:wait:/etc/rc.d/rc 3
Significa: a linha de configuração l3 vai funcionar quando o runlevel for 3, vai executar o comando /etc/rc.d/rc 3 e esperar (wait) até o runlevel acabar ou mudar. Particularmente, o campo de ação pode ter várias opções. Algumas delas:
- respawn: executa o comando, e quando ele finalizar, executa-o novamente. Exemplos: terminais console (gettys);
- wait: executa o comando uma vez, mesmo quando ele finalizar, espera até o runlevel acabar ou mudar para saber o que fazer;
- sysinit: o primeiro comando que é executado logo após a inicialização (já explicado anteriormente);
- initdefault: qual o primeiro runlevel que o init ficar logo após a inicialização;
- ctrlaltdel: qual comando executar quando alguem precionar CTRL+ALT+DEL no terminal.
Existem outras ações, descritas também na página de manual inittab[5].
Você pode fazer alterações no init e mandar recarregá-lo através do comando:
# faz o init ler o /etc/inittab novamente (reload) init q
E se você quer mudar de um runlevel para outro manualmente, basta fornecer o número do runlevel assim:
# muda para o runlevel 1 init 1 # muda para o runlevel 5 init 5 # muda para o runlevel 6 (reinicia a maquina) init 6
Vamos entender as linhas melhor com alguns exemplos:
x:5:respawn:/etc/X11/prefdm -nodaemon
Quando o sistema for pro runlevel 5, executar o comando /etc/X11/prefdm -nodaemon (que é o ambiente gráfico em sistemas baseados em Red Hat). Se o usuário sair do sistema gráfico, ele executa novamente (respawn).
2:2345:respawn:/sbin/mingetty tty2
Se o runlevel for 2, 3, 4 ou 5, executar o comando /sbin/mingetty tty2. Este comando é o console em modo texto do Linux (CTRL+ALT+F2). Se o usuário fizer logout no console, o processo é finalizado, mas o init executa novamente devido ao respawn.
id:6:initdefault:
Essa é engraçada e geralmente a gente faz pra sacanear os amigos :P A linha diz para o init mudar inicialmente para o runlevel 6, que nas distribuições significa um reboot. Em outras palavras, a máquina fica reiniciando infinitamente.
Enfim, os scripts do /etc/inittab geralmente suprem a maioria dos casos. Mas particularmente, a ação de “respawn” do inittab é muito interessante e pode ser usada para você forçar um processo estar sempre em execução, não importa o que aconteça.
Outros
Além dos dois clássicos, existem outros tipos de sistemas init, como falado anteriormente. Podemos citar (e escrever sobre, em uma segunda parte deste tutorial):
Referências
- init – Wikipedia
- Debian Wiki – LSB Init Scripts
- How-To: Managing Services With Update-Rc.D
- man 5 inittab