Aula 14: rc.d e startup scripts — gerenciando serviços no FreeBSD
O gerenciamento de serviços é uma das tarefas mais críticas na administração de qualquer sistema operacional. No FreeBSD, essa responsabilidade recai sobre o sistema rc.d — um framework maduro, modular e incrivelmente flexível que controla como os serviços são iniciados, parados, reiniciados e monitorados durante o boot e ao longo da operação do sistema. Dominar os rc.d e startup scripts significa assumir o controle total sobre o comportamento do seu servidor, garantindo que cada serviço essencial esteja disponível exatamente quando necessário e na ordem correta. Nesta aula, vamos desmontar esse mecanismo peça por peça, desde a teoria fundamental até a criação de scripts personalizados prontos para produção.
Por que este conhecimento é tão importante? Imagine um servidor que precisa iniciar o banco de dados PostgreSQL antes da aplicação web, ou um firewall que deve estar completamente ativo antes de qualquer interface de rede aceitar tráfego externo. Sem um controle refinado de dependências e ordenação, você corre o risco de enfrentar falhas intermitentes, timeouts misteriosos e serviços que simplesmente não sobem após um reboot. O rc.d resolve esses problemas com um sistema de dependências declarativo, palavras-chave padronizadas e uma hierarquia de diretórios que permite tanto a configuração global quanto ajustes por serviço. Em nossos projetos na JRT Technology Solutions, utilizamos diariamente scripts rc.d customizados para integrar aplicações legadas, monitorar processos críticos e garantir que ambientes de alta disponibilidade iniciem de forma previsível e ordenada.
Ao final desta aula, você será capaz de analisar qualquer script rc.d existente, modificar seu comportamento através de variáveis no /etc/rc.conf, criar do zero um script de inicialização para uma aplicação personalizada, depurar problemas de boot com o modo verbose e implementar verificações de saúde (health checks) que impedem a inicialização de serviços em condições inadequadas. Vamos trabalhar com exemplos reais, como um serviço web em Python e um daemon de monitoramento, que você poderá adaptar imediatamente para o seu ambiente.
Esta é a Aula 14 do curso “FreeBSD — Do Zero ao Avançado”, e ela marca nossa entrada no território intermediário-avançado da administração do sistema. Se você concluiu as aulas anteriores, já possui uma base sólida em instalação, gerenciamento de pacotes, sistema de arquivos ZFS e configuração de rede. Agora, vamos unir esses conhecimentos para construir uma infraestrutura de serviços robusta, preparada para os desafios do mundo real. Prepare seu terminal, acesse seu ambiente FreeBSD (máquina física, VM ou jail) e vamos mergulhar fundo no coração do boot do FreeBSD.
O que você vai aprender nesta aula
- Compreender a arquitetura do sistema rc.d e seu papel no boot do FreeBSD
- Diferenciar os diretórios /etc/rc.d, /usr/local/etc/rc.d e a ordem de precedência entre eles
- Ler e interpretar scripts rc.d existentes, identificando palavras-chave como PROVIDE, REQUIRE e BEFORE
- Configurar serviços através das variáveis no arquivo /etc/rc.conf e nos arquivos sob /etc/rc.conf.d/
- Criar um script rc.d personalizado completo, com suporte a start, stop, restart e status
- Implementar verificações de pré-inicialização (health checks) em scripts customizados
- Depurar problemas de inicialização usando rc_debug e o modo verbose
- Utilizar o comando service e sysrc para gerenciamento diário de serviços
- Aplicar boas práticas de segurança, logging e ordenação de dependências
Pré-requisitos e Ambiente
Para aproveitar ao máximo esta aula, você precisará de um sistema FreeBSD 13.2 ou superior instalado e funcional — pode ser uma instalação bare-metal, uma máquina virtual ou uma jail. É essencial ter acesso root ou privilégios de superusuário, pois manipularemos arquivos em /etc/rc.d/, /usr/local/etc/rc.d/ e editaremos o /etc/rc.conf. Você também deve estar confortável com o uso do editor de texto vi ou ee (recomendamos o ee para iniciantes, por sua interface mais amigável). Conhecimentos básicos de shell script (estruturas if/then, variáveis, funções) serão úteis, mas explicaremos cada construção à medida que aparecerem. Por fim, recomendo que você tenha concluído a Aula 13 sobre gerenciamento de pacotes e ports, pois faremos referência a serviços instalados via pkg.
Arquitetura do sistema rc.d — como o FreeBSD organiza a inicialização
O sistema de inicialização do FreeBSD é radicalmente diferente do System V init encontrado em distribuições Linux mais antigas, e também difere do systemd adotado pela maioria das distribuições modernas. O rc.d (que significa “run command dot d”, seguindo a convenção de diretórios Unix) é um framework baseado em scripts shell que opera sobre o /etc/rc — o script principal de inicialização. Quando o kernel termina de carregar, ele invoca o init (PID 1), que por sua vez executa o /etc/rc. Este script percorre todos os arquivos nos diretórios de scripts rc.d, ordenando-os com base em dependências declaradas e executando cada um com o argumento start.
Existem dois diretórios principais que armazenam scripts rc.d: /etc/rc.d/, que contém scripts para serviços do sistema base (como netif, routing, sshd, syslogd), e /usr/local/etc/rc.d/, que armazena scripts para aplicações de terceiros instaladas via ports ou pacotes (por exemplo, nginx, postgresql, mysql). Uma regra fundamental: nunca modifique scripts em /etc/rc.d/. Esses scripts são parte do sistema base e serão sobrescritos em atualizações. Para customizar o comportamento de serviços do sistema, utilize variáveis no /etc/rc.conf. Scripts personalizados que você criar devem sempre residir em /usr/local/etc/rc.d/.
A mágica da ordenação acontece através de palavras-chave especiais inseridas como comentários estruturados no início de cada script. Essas palavras-chave — PROVIDE, REQUIRE, BEFORE, KEYWORD — são analisadas pelo utilitário rcorder, que constrói um grafo de dependências e determina a sequência exata de inicialização. Por exemplo, o script do SSH (/etc/rc.d/sshd) declara REQUIRE: LOGIN cleanvar, indicando que ele precisa que os serviços LOGIN e cleanvar sejam iniciados antes. Isso garante que o SSH só seja ativado depois que o sistema de arquivos estiver limpo e os daemons de login estiverem prontos.
Além das dependências, o rc.d implementa um conceito elegante de “facilidades abstratas”. Um script pode declarar PROVIDE: DAEMON (uma facilidade genérica que representa “algum tipo de daemon está rodando”), e outro script pode declarar REQUIRE: DAEMON, garantindo que todos os serviços que provêm essa facilidade sejam iniciados primeiro. Isso cria um sistema de namespaces que desacopla scripts específicos de dependências rígidas, tornando o framework extremamente flexível. Em nossos projetos na JRT Technology Solutions, frequentemente utilizamos facilidades customizadas como PROVIDE: app_db_ready para coordenar a inicialização de stacks complexos.
Anatomia de um script rc.d — dissecando cada seção
Antes de criarmos nosso próprio script, vamos examinar detalhadamente um exemplo real. O script /usr/local/etc/rc.d/nginx (instalado pelo pacote nginx) é um excelente modelo. Abra-o com cat /usr/local/etc/rc.d/nginx e observe sua estrutura. Todo script rc.d é um shell script que segue um template rígido, garantindo consistência e previsibilidade em todo o ecossistema FreeBSD.
#!/bin/sh
# PROVIDE: nginx
# REQUIRE: LOGIN cleanvar
# KEYWORD: shutdown
. /etc/rc.subr
name="nginx"
rcvar="nginx_enable"
command="/usr/local/sbin/${name}"
pidfile="/var/run/${name}.pid"
required_files="/usr/local/etc/${name}.conf"
load_rc_config $name
run_rc_command "$1"
Vamos analisar cada linha deste script. A primeira linha (#!/bin/sh) é o shebang padrão — scripts rc.d devem usar /bin/sh, não bash ou outro shell. As três linhas seguintes (iniciadas com #) são as palavras-chave que o rcorder lê: PROVIDE: nginx declara que este script provê o serviço “nginx”; REQUIRE: LOGIN cleanvar indica dependências; KEYWORD: shutdown significa que este script deve ser executado também durante o desligamento (com argumento stop). Note que o # antes de PROVIDE/REQUIRE/KEYWORD não é um comentário comum — o rcorder especificamente procura por essas linhas.
A linha . /etc/rc.subr (o ponto seguido de espaço carrega o arquivo no shell atual, similar ao source) é o coração do framework. O arquivo /etc/rc.subr contém todas as funções auxiliares que os scripts rc.d utilizam: load_rc_config, run_rc_command, checkyesno, entre outras. Sem este arquivo, você teria que implementar manualmente toda a lógica de parsing do rc.conf, verificação de PID files, e manipulação de sinais. A beleza do sistema é que você só precisa definir variáveis e chamar essas funções — o trabalho pesado já está pronto.
As variáveis name, rcvar, command, pidfile e required_files são definições padronizadas que o rc.subr utiliza. name é o nome base do serviço; rcvar é a variável que será verificada no rc.conf para determinar se o serviço deve iniciar (por convenção, sempre nome_enable); command é o caminho completo para o binário do daemon; pidfile é onde o PID do processo será armazenado; required_files lista arquivos que devem existir antes da inicialização — se algum não for encontrado, o script aborta com erro. Finalmente, load_rc_config carrega as configurações do rc.conf e run_rc_command executa a ação solicitada (start, stop, restart, status, etc.).
Uma característica importante: observe que não há funções explícitas start_cmd, stop_cmd ou status_cmd neste script. O rc.subr fornece implementações padrão inteligentes que funcionam para a maioria dos daemons Unix tradicionais — elas utilizam o pidfile para gerenciar o processo e enviam sinais apropriados. Você só precisa sobrescrever esses métodos quando seu serviço exigir um tratamento especial (um processo em foreground que não cria pidfile, um script Python, ou um comando que requer flags adicionais). Veremos como fazer essas customizações na próxima seção.
Criando seu primeiro script rc.d personalizado — serviço Python de exemplo
Vamos agora colocar a teoria em prática. Criaremos um script rc.d para um serviço web simples escrito em Python que servirá uma API de monitoramento. Escolhemos Python deliberadamente porque ele ilustra um caso comum: um daemon que não cria seu próprio pidfile, que roda em foreground e que requer argumentos específicos na linha de comando. Este exemplo é diretamente aplicável a aplicações Node.js, Ruby, Go ou qualquer outra linguagem que você utilize em produção.
Passo 1: Primeiro, crie a aplicação Python de exemplo. Vamos posicioná-la em /usr/local/bin/monitor-api.py.
#!/usr/local/bin/python3
import http.server
import json
import os
import signal
import sys
class MonitorHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
response = {"status": "healthy", "service": "monitor-api"}
self.wfile.write(json.dumps(response).encode())
else:
self.send_response(404)
self.end_headers()
def sigterm_handler(signum, frame):
print("Recebido sinal de término, encerrando...")
sys.exit(0)
signal.signal(signal.SIGTERM, sigterm_handler)
if __name__ == '__main__':
port = int(os.environ.get('MONITOR_PORT', 8080))
server = http.server.HTTPServer(('127.0.0.1', port), MonitorHandler)
print(f"Monitor API iniciada na porta {port}")
server.serve_forever()
Este código cria um servidor HTTP simples que responde a requisições GET /health com um JSON indicando que o serviço está saudável. A porta pode ser configurada via variável de ambiente MONITOR_PORT, com padrão 8080. O tratamento de SIGTERM garante um encerramento limpo quando o rc.d enviar o sinal de stop. Torne o arquivo executável com:
chmod +x /usr/local/bin/monitor-api.py
Passo 2: Agora, crie o script rc.d em /usr/local/etc/rc.d/monitor_api. Este nome de arquivo é importante — por convenção, use snake_case e evite extensões.
#!/bin/sh
# PROVIDE: monitor_api
# REQUIRE: DAEMON NETWORKING
# KEYWORD: shutdown
. /etc/rc.subr
name="monitor_api"
rcvar="monitor_api_enable"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
command_args="-p ${pidfile} -o /var/log/${name}.log /usr/local/bin/monitor-api.py"
required_files="/usr/local/bin/monitor-api.py"
export MONITOR_PORT=8080
load_rc_config $name
run_rc_command "$1"
Vamos analisar as decisões de design neste script. Em vez de apontar command diretamente para o script Python, utilizamos o /usr/sbin/daemon — um utilitário nativo do FreeBSD que transforma qualquer processo foreground em um daemon de background, gerenciando o pidfile automaticamente. A flag -p ${pidfile} instrui o daemon a escrever o PID no arquivo especificado. A flag -o /var/log/${name}.log redireciona stdout e stderr para um arquivo de log, essencial para debugging. O restante da linha após essas flags é o comando real que será executado — nosso script Python.
A diretiva export MONITOR_PORT=8080 define a variável de ambiente antes da execução do daemon. Poderíamos também usar monitor_api_flags no /etc/rc.conf para passar argumentos adicionais. A linha required_files garante que o script Python exista antes de tentar iniciar o serviço, fornecendo uma verificação de sanidade básica. Note que REQUIRE: DAEMON NETWORKING garante que o stack de rede esteja operacional e que outros daemons básicos já tenham iniciado.
Passo 3: Torne o script executável e configure o sistema para iniciar o serviço:
chmod +x /usr/local/etc/rc.d/monitor_api
sysrc monitor_api_enable="YES"
O comando sysrc é uma ferramenta segura para editar o /etc/rc.conf — ele evita duplicatas e preserva a formatação. Você pode verificar a configuração com sysrc -a | grep monitor ou simplesmente com cat /etc/rc.conf | grep monitor.
Configuração detalhada via /etc/rc.conf e rc.conf.d
O arquivo /etc/rc.conf é o ponto central de configuração de inicialização no FreeBSD. Nele, você define quais serviços iniciam, com quais parâmetros e em quais condições. Para o nosso serviço monitor_api, já adicionamos a variável obrigatória monitor_api_enable=”YES”. Mas o sistema permite muito mais refinamento. Você pode, por exemplo, definir flags adicionais ou sobrescrever o local do pidfile:
# /etc/rc.conf — configuração completa para o serviço monitor_api
monitor_api_enable="YES"
monitor_api_flags="-t monitor_api" # tag para identificar no ps
monitor_api_pidfile="/var/run/monitor_api.pid"
Qualquer variável definida no script rc.d pode ser sobrescrita no /etc/rc.conf prefixando-a com o nome do serviço e um underscore. Se no script definimos pidfile=”/var/run/${name}.pid”, no rc.conf podemos definir monitor_api_pidfile=”/var/run/alt/monitor_api.pid” para alterar esse valor. Esta é a forma correta de customizar serviços sem modificar os scripts originais. O rc.subr lê todas as variáveis do rc.conf que correspondem ao padrão ${name}_* e as aplica ao ambiente do script.
A partir do FreeBSD 12, uma alternativa elegante para organizar configurações é o diretório /etc/rc.conf.d/. Em vez de poluir o /etc/rc.conf com configurações de dezenas de serviços, você pode criar arquivos individuais: /etc/rc.conf.d/monitor_api conteria apenas variáveis relacionadas a este serviço. O load_rc_config procura automaticamente por esses arquivos e os carrega após o /etc/rc.conf, permitindo sobrescritas. Em ambientes gerenciados pela JRT Technology Solutions, implementamos essa prática como padrão para facilitar o versionamento e a automação com Ansible ou Puppet.
# /etc/rc.conf.d/monitor_api — arquivo dedicado de configuração
monitor_api_enable="YES"
monitor_api_flags="-t monitor_api"
export MONITOR_PORT=9090
A capacidade de usar export diretamente nestes arquivos (como na última linha acima) permite configurar variáveis de ambiente sem modificar o script rc.d. Isso é particularmente útil para aplicações Twelve-Factor App que recebem configuração via ambiente.
Testando e verificando a configuração do serviço
Com o script criado e configurado, é hora de testar. O FreeBSD oferece várias camadas de verificação que permitem validar tudo antes mesmo de reiniciar o sistema. Vamos executar cada comando e analisar as saídas esperadas.
Teste 1 — Verificação de sintaxe do script:
sh -n /usr/local/etc/rc.d/monitor_api
O comando sh -n verifica a sintaxe do shell script sem executá-lo. Se não houver saída, a sintaxe está correta. Erros comuns incluem esquecer de fechar aspas ou parênteses. Este teste rápido evita dores de cabeça durante o boot.
Teste 2 — Inicialização manual com saída verbose:
service monitor_api onestart
A ação onestart é especial: ela inicia o serviço mesmo que a variável monitor_api_enable não esteja definida como “YES”. É ideal para testes antes de habilitar permanentemente. Veja a saída esperada:
Starting monitor_api.
Monitor API iniciada na porta 8080
Teste 3 — Verificação de status:
service monitor_api status
monitor_api is running as pid 12345.
Teste 4 — Verificação do pidfile e do processo:
cat /var/run/monitor_api.pid
ps aux | grep monitor_api
O primeiro comando deve mostrar o número do PID armazenado. O segundo deve listar o processo daemon supervisionando o script Python, e o próprio processo Python como filho. Note que o daemon aparece como processo pai — isso é normal e esperado.
Teste 5 — Teste funcional do serviço:
curl http://127.0.0.1:8080/health
{"status": "healthy", "service": "monitor-api"}
Teste 6 — Parada do serviço:
service monitor_api stop
Stopping monitor_api.
Waiting for PIDS: 12345.
Teste 7 — Reinicialização completa:
service monitor_api restart
Stopping monitor_api.
Waiting for PIDS: 12345.
Starting monitor_api.
Monitor API iniciada na porta 8080
Debugging avançado — modo verbose e rc_debug
Quando um serviço falha silenciosamente durante o boot, o modo verbose é sua ferramenta de investigação mais poderosa. O sistema rc.d possui variáveis de debug que, quando ativadas, produzem logs extremamente detalhados de cada etapa da inicialização. Para ativar o debug globalmente, adicione ao /etc/rc.conf:
rc_debug="YES"
rc_info="YES"
A variável rc_debug habilita mensagens detalhadas de cada script rc.d executado, mostrando exatamente quais comandos estão sendo invocados e com quais argumentos. Já rc_info exibe informações sobre a ordenação dos scripts, útil para depurar problemas de dependência. Após adicionar essas linhas, reinicie o sistema ou execute manualmente o /etc/rc com as variáveis exportadas:
sh -x /etc/rc 2>&1 | tee /tmp/boot_debug.log
O -x ativa o tracing do shell, mostrando cada linha executada. Redirecionamos para um arquivo porque a saída será massiva — facilmente milhares de linhas. Procure no arquivo por mensagens de erro, por “failed” ou pelo nome do seu serviço. Uma técnica eficaz que utilizamos na JRT Technology Solutions é habilitar o debug apenas para um serviço específico durante troubleshooting, modificando temporariamente seu script rc.d para incluir set -x após o shebang.
Implementando health checks e verificações de pré-inicialização
Um script rc.d bem escrito não apenas inicia um processo — ele verifica se as condições necessárias estão satisfeitas antes mesmo de tentar. Vamos evoluir nosso script monitor_api adicionando verificações de sanidade e um método de status mais informativo.
#!/bin/sh
# PROVIDE: monitor_api
# REQUIRE: DAEMON NETWORKING
# KEYWORD: shutdown
. /etc/rc.subr
name="monitor_api"
rcvar="monitor_api_enable"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
command_args="-p ${pidfile} -o /var/log/${name}.log /usr/local/bin/monitor-api.py"
required_files="/usr/local/bin/monitor-api.py"
export MONITOR_PORT=8080
# Health check customizado: verifica se a porta não está em uso
monitor_api_prestart()
{
local port=${MONITOR_PORT:-8080}
if sockstat -l4 -p $port | grep -q LISTEN; then
err 1 "Porta $port já está em uso por outro processo"
fi
if [ ! -w /var/log/ ]; then
warn "Diretório /var/log/ não é gravável. Logs podem falhar."
fi
return 0
}
# Status customizado: além de verificar PID, testa o endpoint de saúde
monitor_api_status()
{
local pid=$(check_pidfile $pidfile $command)
if [ -z "$pid" ]; then
echo "$name não está rodando."
return 1
fi
echo "$name está rodando como pid $pid."
if command -v curl > /dev/null 2>&1; then
if curl -sf http://127.0.0.1:${MONITOR_PORT:-8080}/health > /dev/null 2>&1; then
echo "Health check: OK"
else
echo "Health check: FALHOU (endpoint /health não responde)"
return 2
fi
fi
return 0
}
load_rc_config $name
run_rc_command "$1"
As funções monitor_api_prestart e monitor_api_status são automaticamente detectadas pelo rc.subr devido à convenção de nomenclatura ${name}_prestart e ${name}_status. A função prestart é executada antes do start e pode abortar a inicialização retornando um código diferente de zero — usamos a função utilitária err para isso, que exibe uma mensagem e sai com erro. O sockstat verifica se a porta já está ocupada, prevenindo falhas obscuras. A função status vai além da verificação padrão de PID: ela testa ativamente o endpoint de saúde, fornecendo uma confirmação de que o serviço não apenas está rodando, mas está funcional.
Comparativo de comandos para gerenciamento de serviços no FreeBSD
O FreeBSD oferece três interfaces principais para gerenciar serviços, cada uma com seu propósito específico. Compreender quando usar cada uma é essencial para uma administração eficiente.
| Comando | Propósito | Exemplo | Quando usar |
|---|---|---|---|
| service | Interface amigável para gerenciar serviços rc.d | service nginx restart | Uso diário, administração interativa e scripts de manutenção |
| /etc/rc.d/<script> diretamente | Acesso direto ao script de inicialização | /etc/rc.d/sshd status | Debugging, quando precisa ver o script específico sendo executado |
| sysrc | Gerenciamento seguro de variáveis no /etc/rc.conf | sysrc nginx_enable=YES | Automação, scripts de provisionamento, evitar duplicatas no rc.conf |
Palavras-chave rc.d e suas funções — tabela de referência completa
As palavras-chave nos cabeçalhos dos scripts rc.d controlam a ordenação, o escopo de execução e o comportamento durante shutdown. Dominar essas palavras-chave permite que você orquestre inicializações complexas com confiança.
| Palavra-chave | Função | Exemplo |
|---|---|---|
| PROVIDE | Declara o nome do serviço/facilidade que este script provê | PROVIDE: postgresql |
| REQUIRE | Lista serviços que devem ser iniciados ANTES deste | REQUIRE: DAEMON NETWORKING |
| BEFORE | Força que este script execute ANTES de outros listados | BEFORE: LOGIN |
| KEYWORD | Controla em quais fases do ciclo de vida o script é executado | KEYWORD: shutdown (executa stop no desligamento) | KEYWORD: nojail (não executa em jails) |
| ${name}_enable | Variável booleana que controla se o serviço inicia automaticamente | monitor_api_enable=”YES” no /etc/rc.conf |
| ${name}_flags | Argumentos extras passados ao comando do serviço | monitor_api_flags=”-t monitor” |
Erros comuns e como resolver
Ao trabalhar com rc.d e startup scripts, alguns erros aparecem com frequência, especialmente durante a criação de scripts personalizados. Abaixo estão os quatro problemas mais comuns que encontramos em nossos projetos na JRT Technology Solutions, junto com suas causas, sintomas e soluções completas.
- Erro 1: “Cannot ‘start’ <serviço>. Set <serviço>_enable to YES in /etc/rc.conf”
Causa: O serviço não está habilitado. A ação start padrão verifica a variável ${rcvar} (por exemplo, monitor_api_enable) e recusa iniciar se ela não estiver definida como “YES” no /etc/rc.conf.
Sintoma: O comando service monitor_api start exibe a mensagem de erro acima e retorna código 1.
Solução: Execute sysrc monitor_api_enable=”YES” para habilitar permanentemente, ou use service monitor_api onestart para iniciar sem verificar o enable (modo de teste). Verifique se a variável está realmente definida com sysrc -a | grep monitor_api. - Erro 2: “pidfile /var/run/<serviço>.pid does not exist” após start bem-sucedido
Causa: O daemon não está criando o arquivo PID esperado. Isso é comum quando você usa command=”seu_daemon” diretamente sem o wrapper /usr/sbin/daemon, e o programa não implementa criação de pidfile por conta própria.
Sintoma: O serviço parece iniciar (sem mensagens de erro), mas o service status retorna “<serviço> is not running” porque não encontra o pidfile.
Solução: Use /usr/sbin/daemon -p /var/run/<serviço>.pid seu_comando no campo command e command_args. Alternativamente, implemente a criação do pidfile no seu aplicativo ou sobrescreva a função start_cmd no script rc.d para utilizar echo $! > /var/run/<serviço>.pid após iniciar o processo em background. - Erro 3: “eval: <variável>: not found” durante parsing do rc.conf
Causa: Erro de sintaxe no /etc/rc.conf — geralmente aspas desbalanceadas, caracteres especiais não escapados, ou uso de variáveis de shell inválidas dentro do arquivo.
Sintoma: Durante o boot ou ao carregar configurações com load_rc_config, o sistema emite erros de parsing e serviços podem não iniciar.
Solução: Verifique a sintaxe do /etc/rc.conf com sh -n /etc/rc.conf. Procure por linhas com aspas não fechadas, espaços ao redor do sinal de igual (deve ser variavel=”valor” sem espaços), ou variáveis com nome contendo hífens. Reverta alterações recentes ou edite com vi para corrigir. Em emergências, inicialize em single-user mode para editar o arquivo. - Erro 4: Ordem de inicialização incorreta — dependências circulares ou serviços que iniciam antes do necessário
Causa: Palavras-chave REQUIRE e BEFORE mal especificadas nos scripts rc.d. Um script declara REQUIRE: A e outro declara REQUIRE: B, mas A e B têm dependências conflitantes que formam um ciclo.
Sintoma: Durante o boot, você vê mensagens como “WARNING: circular dependency detected” ou serviços falham porque suas dependências ainda não estão prontas.
Solução: Execute rcorder /etc/rc.d/* /usr/local/etc/rc.d/* 2>&1 | head -50 para ver a ordem calculada e identificar ciclos. Use rc_debug=”YES” no /etc/rc.conf para obter logs detalhados da ordenação. Simplifique as dependências — se um serviço só precisa de rede, use REQUIRE: NETWORKING em vez de listar serviços de rede específicos. Se necessário, use BEFORE com moderação para forçar ordenação sem criar ciclos.
Boas práticas e dicas avançadas para ambientes de produção
Após anos implementando infraestrutura FreeBSD para clientes de missão crítica, consolidamos na JRT Technology Solutions um conjunto de práticas que elevam a confiabilidade dos seus rc.d e startup scripts ao nível enterprise. A primeira e mais importante: sempre use o wrapper /usr/sbin/daemon para aplicações que não são daemons nativos. Ele fornece gerenciamento de pidfile, redirecionamento de logs, e reinicialização automática se configurado (via -r). A flag -r -R 5, por exemplo, reinicia o processo automaticamente até 5 vezes se ele morrer, comprando tempo para investigar sem causar outage completa.
Segundo, padronize o logging. Em vez de deixar cada aplicação escrever logs em locais arbitrários, configure todos os seus scripts rc.d para enviar saída para /var/log/<serviço>.log usando o daemon -o, e integre com o newsyslog para rotação automática. Adicione ao /etc/newsyslog.conf uma entrada para cada serviço:
# Formato: arquivo owner:group modo count size when flags
/var/log/monitor_api.log root:wheel 640 5 100 * J
Terceiro, implemente timeouts. O rc.subr possui mecanismos para evitar que serviços travados bloqueiem o boot indefinidamente. Adicione ao seu /etc/rc.conf:
rcshutdown_timeout="30" # segundos de espera no shutdown
rc_startmsgs="YES" # exibe mensagens durante o boot
Para serviços individuais, você pode definir <serviço>_timeout=”10″ para limitar o tempo de espera durante start/stop. Quarto, utilize jails para isolar serviços e testar scripts rc.d em ambiente controlado. Uma jail permite que você reinicie serviços, teste diferentes ordenações e simule falhas sem afetar o host. Em nossos laboratórios de treinamento, cada aluno recebe uma jail dedicada para experimentar à vontade.
Por fim, automatize a validação. Crie um script de pós-deploy que executa service <nome> status para todos os serviços habilitados, coleta os códigos de retorno e alerta se algum não estiver rodando. Combine isso com curl para health checks HTTP e nc para verificações de porta TCP. A confiabilidade vem da verificação contínua, não apenas da configuração inicial correta.
Integrando scripts rc.d com o ecossistema de jails do FreeBSD
Um dos recursos mais poderosos do FreeBSD é o suporte nativo a jails, e os scripts rc.d são totalmente integrados a esse ecossistema. Quando você cria uma jail, o sistema de inicialização trata cada jail como um ambiente isolado com seu próprio conjunto de scripts rc.d executando dentro dela. A palavra-chave KEYWORD: nojail em um script impede que ele seja executado dentro de jails — importante para serviços que só fazem sentido no host, como configuração de interfaces de rede físicas ou carregamento de módulos do kernel.
Para serviços que rodam em jails, o padrão recomendado é instalar o software dentro da jail e colocar o script rc.d correspondente em /usr/local/etc/rc.d/ dentro do sistema de arquivos da jail. O host não precisa conhecer esses serviços — ele simplesmente inicia a jail, e o processo de boot da jail executa seus próprios scripts. Para gerenciar serviços de dentro do host, use:
jexec <nome_da_jail> service <serviço> status
jexec <nome_da_jail> sysrc <serviço>_enable=YES
Esta abordagem mantém a separação de responsabilidades e permite que cada jail seja tratada como um servidor independente, facilitando migrações e backups. Em ambientes de alta disponibilidade que gerenciamos na JRT Technology Solutions, utilizamos jails com scripts rc.d customizados para serviços stateless que podem ser movidos entre hosts com tempo de inatividade mínimo.
Resumo da Aula 14
Nesta aula intensiva, dissecamos o sistema rc.d do FreeBSD — o framework que governa como os serviços nascem, vivem e morrem dentro do sistema operacional. Começamos pela arquitetura fundamental, entendendo a diferença entre /etc/rc.d/ (sistema base) e /usr/local/etc/rc.d/ (aplicações de terceiros e scripts personalizados). Exploramos a anatomia de um script rc.d linha por linha, compreendendo o papel crucial do /etc/rc.subr e das palavras-chave PROVIDE, REQUIRE, BEFORE e KEYWORD.
Na prática, criamos um serviço Python funcional e o envolvemos em um script rc.d completo, utilizando o utilitário daemon para gerenciamento de processos, implementando health checks com prestart e status customizados, e configurando o ambiente via /etc/rc.conf e /etc/rc.conf.d/. Aprendemos a depurar problemas com rc_debug e sh -x, e examinamos os quatro erros mais comuns que administradores enfrentam — junto com suas soluções definitivas.
Para ambientes de produção, estabelecemos boas práticas como uso consistente do daemon, integração com newsyslog para rotação de logs, timeouts para evitar bloqueios no boot, e a sinergia entre rc.d e jails. O domínio desses conceitos transforma você de um administrador que “aperta botões” para um engenheiro que projeta inicializações resilientes e previsíveis. Na próxima aula, mergulharemos no sistema de logging do FreeBSD — syslogd, newsyslog, e como centralizar logs de múltiplos servidores e jails para análise forense e monitoramento proativo.
Continue praticando: modifique o script monitor_api para adicionar uma funcionalidade de reload (sinal SIGHUP), crie um segundo serviço que dependa do primeiro usando REQUIRE: monitor_api, e teste o comportamento do sistema durante um reboot completo com ambos habilitados. A maestria vem da experimentação deliberada, e cada minuto investido aqui retornará em confiabilidade e paz de espírito quando seus servidores de produção enfrentarem seu próximo reboot inesperado.
Quer aprender na prática com especialistas?
A JRT Technology Solutions oferece treinamentos e implementação de FreeBSD para equipes corporativas.