01 - Estrutura (Unit, Service, Install)
video: https://youtu.be/... no frontmatter da nota.A anatomia interna de um Unit File do systemd é fundamentada em uma sintaxe declarativa derivada do formato INI. O parser do systemd (escrito em C) lê esses arquivos em busca de seções (denotadas por chaves []) que agrupam pares de chave-valor (diretivas). Ao contrário dos scripts imperativos do SysVinit, o systemd não executa este arquivo de cima para baixo; ele o compila em uma estrutura de dados orientada a objetos na memória.
A estrutura de um arquivo .service canônico é universalmente dividida em três domínios semânticos distintos: [Unit] (ontologia e topologia), [Service] (comportamento de execução do binário) e [Install] (regras de persistência e ativação estática). Compreender a interação ortogonal entre essas seções é o pré-requisito para a orquestração avançada do espaço de usuário.
1. A Seção [Unit]: Meta-Contexto e Topologia no DAG
A seção [Unit] é genérica e aplica-se a quase todos os 11 tipos de unidades do systemd. Sua responsabilidade arquitetural dupla é fornecer metadados descritivos e, criticamente, posicionar a unidade dentro do Grafo Acíclico Dirigido (DAG) de inicialização.
O erro mais comum na engenharia de Unit Files é a confusão entre dependência de existência (Requirement) e dependência temporal (Ordering). No systemd, estes dois eixos são estritamente ortogonais.
1.1. Dependências de Requisito (Existence)
Estas diretivas determinam se outras unidades devem ser ativadas em conjunto com a unidade atual. Elas não ditam ordem. Se A requer B, e ambas forem chamadas, o systemd iniciará as duas no mesmo exato milissegundo.
Wants=: A dependência fraca (Soft Dependency). Se a unidade atual for iniciada, as unidades listadas emWants=também serão enfileiradas para início. Se qualquer uma das unidades requeridas falhar em iniciar, a unidade atual continua sua execução incólume. É o mecanismo padrão e recomendado para maximizar a resiliência.Requires=: A dependência forte (Hard Dependency). Similar aoWants=, mas impõe uma restrição de sucesso. Se a unidade requerida falhar durante a inicialização, a transição da unidade atual é abortada imediatamente. Contudo, se a unidade requerida for terminada ou crashar subitamente muito tempo após o boot, a unidade atual não será parada automaticamente.BindsTo=: A restrição mais severa. Adiciona à semântica doRequires=a propagação de estado de morte. Se a unidade referenciada morrer, falhar ou for interrompida manualmente a qualquer momento do ciclo de vida do sistema operacional, a unidade atual será imediatamente terminada pelo PID 1. Indispensável para acoplar daemons a interfaces de rede efêmeras (ex:BindsTo=sys-subsystem-net-devices-eth0.device).PartOf=: Propaga ações manuais do administrador, mas não falhas. Se a unidade referenciada for parada ou reiniciada (viasystemctl stop/restart), esta unidade sofrerá a mesma ação.Conflicts=: O antônimo da dependência. Define exclusão mútua. Se a unidade atual for iniciada, as unidades listadas aqui serão paradas. Se a unidade atual e a conflitante forem instruídas a iniciar simultaneamente, osystemdabortará a operação por impasses insolúveis no DAG.
1.2. Dependências Temporais (Ordering)
Para resolver condições de corrida (ex: o banco de dados deve estar com socket aberto antes do servidor web subir), o systemd introduz o eixo temporal.
- **
Before=eAfter=**: Se a unidade atual possuiAfter=network.targete ambas as unidades forem enfileiradas para início, osystemdreterá a unidade atual na fila de jobs até que a unidadenetwork.targetreporte o status deactive. - Ortogonalidade Crítica: Declarar
After=Bnão instrui osystemda iniciarB. Se apenas a unidade atual for solicitada e ela tiverAfter=B, mas não tiverWants=B, osystemda iniciará imediatamente, ignorando oAfter. O ordenamento só se aplica se ambas as unidades estiverem presentes na mesma transação no DAG.
1.3. Predicados Condicionais
Antes de instanciar a unidade, o parser avalia sentenças lógicas.
ConditionPathExists=: A unidade só prossegue se o arquivo especificado existir. Útil para condicionar a inicialização de um daemon à existência de seu arquivo de configuração primário.ConditionVirtualization=: Avalia se o sistema roda em metal nativo, KVM, LXC ou chroot, permitindo que serviços específicos de hardware abortem graciosamente sem gerar falhas (estado condition failed, não failed).
2. A Seção [Service]: A Máquina de Execução e o Cgroup
Restrita exclusivamente aos arquivos .service, esta seção é o coração operacional do sistema. Ela mapeia o objeto declarativo para um processo real (ou árvore de processos) no espaço de usuário e orquestra a chamada de sistema execve().
2.1. O Modelo de Execução (Type=)
O valor de Type= altera fundamentalmente como o PID 1 define o que significa "iniciar com sucesso" para a unidade. Este é o parâmetro arquitetural mais complexo de um .service.
Type=simple(O Padrão Otimista): Osystemdrealiza ofork(), executa o binário definido emExecStart=e, no milissegundo seguinte, marca a unidade comoactive (running). Ele não aguarda o daemon alocar memória, ler configurações ou abrir portas. Se o processo morrer um segundo depois por erro de sintaxe na configuração, a unidade mudará parafailed. Isso proporciona paralelismo extremo, mas exige que outros serviços usem Socket Activation para não falharem ao tentar conectar muito cedo.Type=exec: Similar aosimple, mas osystemdaguarda especificamente o retorno da syscallexecve()dentro do processo filho. Isso garante que o binário principal foi pelo menos localizado no disco e as permissões de execução foram validadas antes de marcar a unidade como ativa.Type=forking(Retrocompatibilidade SysV): Desenhado para daemons tradicionais C/C++ que executam double-fork e rodam em background. Osystemdinvoca oExecStart=e espera que o processo pai morra (retorne saída0). Quando o pai morre, osystemdmarca o serviço como ativo.-
Restrição: Como o PID original foi descartado, o
systemdprecisa deduzir qual processo é o daemon principal dentro do cgroup. O uso da diretivaPIDFile=/var/run/daemon.pidtorna-se quase mandatório para evitar ambiguidades se o processo filho fizer forks adicionais rapidamente. -
Type=oneshot: Destinado a scripts de inicialização efêmeros, não a daemons residentes (ex: aplicar regras de firewall viaiptables). Osystemdmarca o estado como activating, aguarda o processo terminar a execução com código de saída0e, em seguida, marca a unidade como inactive (dead) (ou active (exited) se a diretivaRemainAfterExit=yesfor utilizada). As unidades que tiveremAfter=para este serviço ficarão bloqueadas até que o comando termine completamente. Type=dbus: O serviço é considerado inicializado apenas quando adquire um nome específico (declarado na diretivaBusName=) no barramento D-Bus do sistema. Indispensável para daemons como oNetworkManageroupolkit.Type=notify(O Padrão Moderno): A evolução definitiva do gerenciamento de estado. O binário executa no modelosimple, mas osystemdo mantém no estado activating. O processo C/Go/Python do daemon, após terminar sua rotina interna (ler configs, instanciar memória), invoca ativamente a bibliotecalibsystemd(chamadasd_notify(0, "READY=1")) através de um socket IPC injetado no ambiente pelo PID 1 ($NOTIFY_SOCKET). Só então osystemdmarca a unidade como ativada e libera as unidades que dependem dela. Elimina completamente as condições de corrida cegas.
2.2. Diretivas de Invocação e Controle
ExecStart=: O comando absoluto (com caminho completo, ex:/usr/sbin/sshd -D) a ser executado. Osystemdnão processa variáveis shell ou operadores como>e|por razões de segurança. Para usar recursos shell, deve-se invocarExecStart=/bin/bash -c "comando | filtro". Pode ser declarado múltiplas vezes em tiposoneshot.ExecStop=: Opcional. O comando a executar antes do encerramento forçado. Se omitido, osystemdnão lê arquivos PID; ele localiza ocgroupatrelado ao serviço e envia umSIGTERMpara absolutamente todas as threads internas simultaneamente. ApósTimeoutStopSec=(padrão 90s), envia umSIGKILLdestrutivo.ExecReload=: O comando acionado viasystemctl reload. Geralmente envia um sinal ao processo principal utilizando a variável dinâmica injetada:ExecReload=/bin/kill -HUP $MAINPID.- **
Environment=eEnvironmentFile=**: Injeta variáveis de ambiente estéreis diretamente no contexto doexecve(), ignorando a poluição do perfil do usuário (~/.bashrc).
2.3. Resiliência e Sandboxing
Restart=: A política de ressurreição do daemon. Pode assumir valores comoon-failure(reinicia apenas se o código de saída for diferente de 0, se receber segfault ou sofrer timeout do watchdog) oualways(reinicia até mesmo se for encerrado com saída 0).RestartSec=: O tempo de backoff (dormência) antes da tentativa de ressurreição, para evitar a exaustão da CPU (tight loop) se o binário estiver corrompido.- Controle de Usuário:
User=eGroup=forçam a queda de privilégios (setuid/setgid) antes da invocação doExecStart=. O processo jamais roda como root, mitigando vetores críticos de RCE (Execução Remota de Código).
3. A Seção [Install]: A Ancoragem de Estado (Enablement)
A arquitetura do systemd distingue rigidamente a execução transitória (Start) da execução persistente (Enable). Iniciar um serviço apenas injeta a unidade na transação atual do DAG em memória. Se a máquina reiniciar, o serviço não voltará, pois não há registro em disco dessa intenção de design.
A seção [Install] não é lida pelo PID 1 durante o boot. Ela é processada exclusivamente pela ferramenta systemctl (rodando em espaço de usuário interativo) quando o administrador executa o comando systemctl enable ou systemctl disable.
3.1. A Semântica do WantedBy= e RequiredBy=
Para que um serviço inicie durante o boot, ele precisa ser tracionado por uma unidade que já faz parte do caminho crítico de inicialização (como o multi-user.target). A seção [Install] declara as dependências inversas.
Quando o Unit File declara WantedBy=multi-user.target, e o comando systemctl enable é invocado, o systemctl executa uma operação simétrica no sistema de arquivos estático (em /etc/systemd/system/):
- Ele localiza o diretório auxiliar do alvo (ex:
/etc/systemd/system/multi-user.target.wants/). Se não existir, ele o cria. - Ele gera um link simbólico (symlink) neste diretório apontando para o arquivo original do serviço.
- No próximo boot, ao carregar a unidade
multi-user.target, o parser dosystemdvarrerá automaticamente a pasta.wants/associada. Ele injetará umWants=implícito para cada symlink encontrado ali.
O WantedBy= estabelece uma dependência fraca reversa persistida fisicamente no disco. O RequiredBy= gera o symlink no diretório homônimo .requires/, estabelecendo uma dependência forte reversa persistente.
3.2. Aliasing e Corolários
Alias=: Cria nomes alternativos persistentes. Ao executar oenable, cria-se um symlink com o nome alternativo apontando para a unidade real. Permite que o comandosystemctl restart meubanco.serviceatue sobrepostgresql-14.service, facilitando scripts de automação e transições de versão.Also=: Ao habilitar esta unidade, instrui osystemctla habilitar silenciosamente e simultaneamente as outras unidades listadas, gerenciando conjuntos de unidades (como acoplar a habilitação de um.socketjunto ao.serviceque o consome).