Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - Scripts -etc-init.d-

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

O diretório /etc/init.d/ (frequentemente um link simbólico para /etc/rc.d/init.d/ em distribuições derivadas do SVR4 e Red Hat) representa o núcleo imperativo e operacional da arquitetura SysVinit. Enquanto o arquivo /etc/inittab e o despachante /etc/rc.d/rc fornecem a máquina de estados e o sequenciamento, é dentro do /etc/init.d/ que reside a lógica algorítmica bruta necessária para instanciar, monitorar e exterminar processos no espaço do usuário.

Sob a ótica da engenharia de software, este diretório atua como um repositório centralizado de rotinas de controle. Os arquivos nele contidos não são arquivos de configuração declarativos (como os futuros Unit Files do systemd), mas sim scripts executáveis Turing-completos, tipicamente escritos em linguagens interpretadas aderentes ao padrão POSIX (geralmente sh ou bash). O design arquitetural exige que o estado de um daemon complexo (como um servidor de banco de dados que exige alocação de memória compartilhada IPC antes de iniciar) seja encapsulado inteiramente dentro da lógica destes scripts.


1. A Anatomia Estrutural e o Paradigma do "Case Statement"

Para que o orquestrador do sistema (o script rc) ou um administrador humano possa interagir uniformemente com centenas de serviços heterogêneos, foi estabelecido um contrato de interface estrito. Todo script residente em /etc/init.d/ deve ser capaz de aceitar e processar argumentos posicionais padronizados (verbos de ação) passados através da linha de comando ($1).

A implementação canônica desta interface é construída em torno de uma estrutura de controle case "$1" in. Os verbos mandatórios, exigidos pelo padrão LSB (Linux Standard Base), são:

  • start: Inicia o serviço. O script deve verificar as pré-condições (existência de arquivos de configuração, disponibilidade de rede), realizar as invocações binárias necessárias e retornar 0 em caso de sucesso.
  • stop: Interrompe o serviço. Deve localizar o processo em execução (via PID file ou inspeção da tabela de processos), enviar sinais de terminação (geralmente SIGTERM seguido de SIGKILL como fallback) e limpar arquivos residuais de estado (como sockets Unix).
  • restart: Uma macro de conveniência que, em implementações bem desenhadas, não é meramente a chamada de stop seguida de start, mas uma rotina que garante o desligamento síncrono absoluto antes da reinicialização, evitando a corrupção de portas TCP presas em estado TIME_WAIT.
  • reload: Instrui o daemon a reler seus arquivos de configuração sem interromper o processo pai (evitando a queda de conexões ativas). Geralmente implementado enviando o sinal SIGHUP (Signal Hang Up) ao PID principal.
  • force-reload: Executa um reload se o serviço suportar recarregamento a quente; caso contrário, realiza um restart completo (segurança de semântica).
  • status: Uma consulta puramente de leitura. Inspeciona o estado do daemon na memória e do seu arquivo de bloqueio (lockfile), retornando códigos de saída estritos (ex: 0 para rodando, 3 para parado, 1 para PID file existente mas processo morto).

2. LSB Headers (Linux Standard Base): A Tentativa de Resolução de Dependências

O tendão de Aquiles histórico do SysVinit era a dependência de ordem lexicográfica rígida através de links simbólicos (S10network, S22sshd). Se um administrador instalasse um novo serviço, ele tinha que adivinhar numericamente onde encaixá-lo. Para mitigar isso sem abandonar a arquitetura SysV, a especificação LSB (Linux Standard Base) Core Specification introduziu a padronização de blocos de comentários mágicos no início de cada script em /etc/init.d/.

Esses metadados, contidos entre ### BEGIN INIT INFO e ### END INIT INFO, transformaram scripts imperativos em entidades que podiam ter suas dependências analisadas topologicamente por ferramentas externas (como insserv no Debian ou chkconfig no Red Hat).

Estrutura de um Cabeçalho LSB Canônico:

### BEGIN INIT INFO
# Provides:          sshd
# Required-Start:    $network $local_fs $remote_fs
# Required-Stop:     $network $local_fs $remote_fs
# Should-Start:      auditd
# Should-Stop:       auditd
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: OpenBSD Secure Shell server
# Description:       The sshd daemon is the standard server for encrypted remote login.
### END INIT INFO

O Significado Semântico das Facilidades (Facilities)

  • Provides: Define o "nome lógico" do recurso que este script fornece. Outros scripts dependerão deste nome, não do nome do arquivo.
  • **Required-Start / Required-Stop**: Dependências absolutas (Hard Dependencies). O serviço se recusa a iniciar se as facilidades listadas não estiverem previamente ativas. Variáveis precedidas por cifrão (ex: $network, $local_fs) são Facilidades do Sistema Virtuais, que aglomeram múltiplos scripts base sob um único conceito.
  • **Should-Start / Should-Stop**: Dependências fracas (Soft Dependencies). Se a facilidade listar auditd estiver presente no sistema, o gerenciador de dependências deve garantir que auditd inicie antes de sshd. Porém, se auditd não estiver instalado, o boot não deve falhar.

A inserção destes metadados permitiu que utilitários de sistema gerassem dinamicamente a numeração dos links simbólicos em /etc/rcX.d/, resolvendo o grafo acíclico dirigido das dependências sem exigir intervenção humana.


3. Bibliotecas de Funções e Padronização de Comportamento

Escrever um script robusto para inicializar um daemon é uma tarefa crivada de edge cases (condições de corrida, processos renegados, controle de TTY). Para não reinventar a roda a cada novo pacote, os scripts em /etc/init.d/ não operam de forma isolada. Eles realizam o source (inclusão de código em tempo de execução) de bibliotecas de funções C e Bash profundamente testadas pela distribuição.

  • Em sistemas baseados em Red Hat/CentOS: . /etc/rc.d/init.d/functions
  • Em sistemas baseados em Debian/Ubuntu: . /lib/lsb/init-functions

As Funções Vitais

Estas bibliotecas injetam funções shell críticas que o script utilizará para manipular o binário:

  1. daemon: Uma função wrapper transcendental. Se o programador do servidor C/C++ não implementou a rotina de double-fork para colocar o processo em background, a função daemon o faz pelo sistema. Além disso, ela aplica limites de recursos (via ulimit), altera o valor nice (prioridade de CPU) e garante que o daemon inicie no diretório raiz (/) para não travar a desmontagem de sistemas de arquivos arbitrários.
  2. killproc: A função responsável pelo extermínio seguro. Diferente do comando grosseiro killall, o killproc lê o PID file exato do serviço, envia o sinal SIGTERM, entra em um loop temporal monitorando o /proc/<PID>, e só emite um SIGKILL (sinal 9) se o processo se recusar a morrer após um tempo de tolerância.
  3. status: Avalia instantaneamente a saúde da árvore de processos vinculada ao serviço, determinando se ele está operante.
  4. **success / failure**: Funções estéticas e de contabilidade. Elas imprimem a icônica mensagem verde [ OK ] ou vermelha [FAILED] no alinhamento direito da tela do console físico durante o boot.

4. Gerenciamento de Estado: PID Files e Lock Subsystem

Como o SysVinit não mantém o rastreamento interno de quais PIDs pertencem a quais serviços (ele perde o controle após o fork original), os scripts em /etc/init.d/ são obrigados a manter registros de estado no disco físico (ou, modernamente, em partições na RAM tmpfs).

O Arquivo PID (/var/run/ ou /run/)

Quando o daemon inicializa com sucesso, ele (ou o wrapper do script) deve escrever seu Process ID em um arquivo de texto, tipicamente /var/run/nome_do_servico.pid. Este arquivo é a única ponte referencial entre o nome do serviço e a tabela do kernel.
Vulnerabilidade: O fenômeno do "PID Recycling". Se o daemon sofrer um crash sem deletar seu arquivo PID, e o kernel reciclar aquele número de PID atribuindo-o a um novo processo de usuário aleatório, comandos como /etc/init.d/servico status retornarão falsamente que o serviço está rodando, e um comando stop assassinará um processo inocente.

O Subsistema de Bloqueio (/var/lock/subsys/)

Especialmente proeminente em sistemas Red Hat, o lockfile atende a um propósito distinto do PID file. Quando o script de inicialização tem sucesso, ele cria um arquivo vazio em /var/lock/subsys/nome_do_servico.
O propósito deste arquivo é servir de bandeira para o script de encerramento (rc) durante o reboot ou shutdown do sistema. Quando o runlevel 0 ou 6 é invocado, o script rc olha para a pasta subsys. Ele só invocará os links K (Kill) para os serviços que possuírem um arquivo correspondente neste diretório. Se o arquivo não existir, o SysVinit presume que o serviço não estava rodando e economiza tempo pulando a rotina de interrupção daquele daemon. Se o servidor falhar eletricamente e não limpar o lockfile, o próximo boot tentará excluir esses arquivos obsoletos em sua fase sysinit.


5. O Contexto de Execução, Idempotência e Segurança

O último pilar da engenharia de scripts em /etc/init.d/ diz respeito ao ambiente estéril necessário para a previsibilidade. Um script SysVinit pode ser invocado de duas formas completamente diferentes:

  1. Pelo processo do kernel (init) durante o boot, com um ambiente (Environment Variables) virtualmente nulo e vazio.
  2. Por um administrador conectado via SSH (sudo /etc/init.d/apache2 restart), herdando dezenas de variáveis interativas (como LD_LIBRARY_PATH, ALIAS, LANG ou PATH contaminados).

Para evitar comportamentos esquizofrênicos dependendo de quem o chamou, um script bem programado aplica sanitização sanitária rigorosa no topo de seu código:

  • Redefinição restrita do PATH: PATH=/sbin:/usr/sbin:/bin:/usr/bin
  • Remoção do local de localização para evitar problemas de parsing de logs: LC_ALL=C ou LANG=C
  • Uso de caminhos absolutos para todos os binários (ex: nunca apenas grep, mas /bin/grep).

Idempotência: A característica mais fundamental requerida de um script em /etc/init.d/. Um script é idempotente se, quando instruído a realizar uma ação que já reflete o estado atual, ele terminar graciosamente sem causar efeitos colaterais deletérios.

  • Se o administrador rodar start em um serviço já iniciado, o script não deve instanciar uma segunda cópia do daemon ou corromper portas ativas. Ele deve detectar o PID, ignorar a invocação binária e retornar código 0 (sucesso).
  • Se rodar stop em um serviço já morto, ele deve limpar eventuais arquivos de lock obsoletos e retornar sucesso, evitando que pipelines automatizadas de gerência de configuração (como Ansible ou Puppet) quebrem devido a códigos de erro não previstos em rotinas de conformidade.