Básico dos arquivos de unidade do systemd

A seguir algumas das principais opções da seção [Service].

Type=

Define como o systemd tratará o processo executado por ExecStart=.

forking

Binários que usem o clássico comportamento de criar um processo separado do inicial via fork()/exit() como descrito em daemon(7). Essa opção existe para compatibilidade; deve ser evitada se possível quando o programa suportar rodar diretamente sem daemonização.

simple

O padrão quando Type= não for especificado. Para daemons que não usem fork()/exit(). Não leva em conta o estado de execução para dependências. No momento em que o binário começar a ser executado, o systemd considerará que sua inicialização está completa (o que nem sempre é o caso) e prosseguirá.

oneshot

Mesma coisa que simple, mas apenas inicia dependências quando o processo terminar sua execução. Não serve para daemons.

idle

Mesma coisa que simple, porém com modificação para rodar o binário depois que não existirem mais unidades pendentes (com limite máximo de 5 segundos). Sem utilidade para daemons.

notify

Melhor de todos. Tem as vantagens de simple e leva em conta para dependências quando o daemon está pronto. Precisa que o código do daemon seja modificado para usar a API de notificação do systemd. Ou seja, o código do programa precisa ser levemente alterado. Exemplo: rsyslog.


PIDFile=

Válido para serviços do tipo forking. Indica o arquivo contendo o processo principal do daemon. Se não for especificado, o systemd tentará autodetectar.

ExecStartPre=

Serve para rodar algum preparativo antes do daemon ser iniciado. Pode ser repetido. Os comandos serão executados em série, um depois do outro. Não foi feito para rodar processos duradouros. Processos que eventualmente façam a dança fork()/exit() não são suportados: no momento em que o processo pai chamar exit(), o comando será considerado finalizado e processos filhos serão terminados na marra.

ExecStart=

Opção mais importante: especifica o que deve ser executado. Com Type=oneshot, pode ser repetido e os comandos serão executados em série, um após o outro.

ExecStartPost=

Idem a ExecStartPre=, mas executado após ExecStart= finalizar.

RemainAfterExit=

Ignora quando o(s) processo(s) de ExecStart= finalizar(em) e continua tratando o serviço como ativo, apenas rodando eventuais comandos de ExecStop= quando for manualmente parado. Costuma ser usado em conjunto com Type=oneshot para rodar programas que precisem ser executados uma única vez.

EnvironmentFile=

Informa um arquivo contendo definições de variáveis (FOO=bar), que são substituídas dentro da unidade. Muito usado para importar configurações de /etc/sysconfig.

ExecStop=

No momento de finalizar, o systemd envia SIGTERM (ou o sinal configurado em KillSignal=) e, caso ainda existem processos ativos, SIGKILL depois de um temporizador (90s, configurável). Com o uso de cgroups, nenhum consegue fugir. O daemon precisa reagir corretamente ao sinal. Quando não o fizer, o comando necessário para terminá-lo é especificado aqui.

Para enviar sinal durante a execução:
# systemctl kill --signal=<sinal> <nome>.service (caso --signal= não seja especificado, usa SIGTERM)

Para escolher quem receberá o sinal:
# systemctl kill --signal=<sinal> --kill-who=<main|control|all> <nome>.service

all → todos os processos. (padrão)
main → apenas o processo principal.
control → processos iniciados por ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop= e o processo pai de ExecStart= caso seja do tipo forking.


KillMode=

Mesmo com ExecStop= presente, os sinais descritos acima ainda são enviados automaticamente após sua execução. KillMode= configura como os processos serão finalizados pelo mecanismo interno.

control-group → todos os processos do daemon passarão pelo rodo. Configuração padrão.
process → apenas o processo principal.
mixed → receberá SIGTERM (ou o sinal configurado em KillSignal=) apenas o processo principal, porém o SIGKILL final será aplicado a todos os processos do serviço em questão. Geralmente é a melhor configuração para daemons que gerenciem processos filhos. Suportado desde a versão 209.
none → nenhum processo.


ExecReload=

Quando o daemon suportar algum tipo de reload, usado para recarregar configurações sem interromper o serviço, ExecReload= instrui como executá-lo. Geralmente contém um comando do próprio binário ou então /usr/bin/kill -<sinal> $MAINPID. Essa é uma variável mantida pelo systemd contendo o PID do processo principal. Não existe padronização de como binários diversos lidam com reload, por isso o systemd não generalizou o trabalho. É comum SIGHUP ser suficiente, mas só lendo a documentação do programa para ter certeza. Caso a unidade não contenha a entrada, você pode usar systemctl kill como comentado acima para enviar sinais diretamente durante a execução.

StandardOutput=

Controla onde a saída dos processos (STDOUT) do serviço em questão será conectada. Por padrão, joga no journal. Opções disponíveis: inherit, null, tty, syslog, kmsg, journal, syslog+console, kmsg+console, journal+console ou socket.

StandardError=

Idem acima, porém para STDERR. O padrão é inherit, que usa a mesma configuração de StandardOutput=. Ou seja: journal.



Quem se depara pela primeira vez com o systemd pode pensar que é possível usar shell script dentro dos arquivos de unidade. Atenção: são arquivos INI-style e não shell scripts.

Até a versão 238, os comandos colocados em Exec<foo> precisam ter seu caminho absoluto (/usr/bin/blabla) especificado. A partir da 239, se o comando não tiver caminho absoluto, será procurado no PATH configurado durante a compilação, que pode não ser o mesmo do shell em uso. Veja na saída de systemd-path search-binaries-default quais caminhos são usados.

Repetindo: os arquivos de unidade não são shell scripts. Argumentos de linha de comando contendo espaços devem ser colocados assim:

ExecStart=/usr/bin/blabla "--bla=foo bar"

Os comandos especificados nas opções Exec<foo> podem ser prefixados com um hífen, que indica para o systemd desconsiderar exit codes anormais (ou arquivo inexistente para EnvironmentFile=), mantendo o serviço como ativo e não em estado de falha — o que cancela a execução de comandos posteriores. Por exemplo: se o programa de ExecStartPre= terminar com um exit code anormal, o serviço entrará em estado de falha e ExecStart= não será executado. SuccessExitStatus= permite modificar quais valores são tratados como sucesso.

Se você realmente precisar de shell, existem duas opções. Para coisas simples, é possível invocar diretamente o Bash (ou outro shell qualquer) assim:

ExecStart=/usr/bin/bash -c '<lista de comandos>'

Ou então crie um script (/usr/local é um bom lugar), dê permissão de execução e referencie-o com o caminho completo em ExecStart= num serviço do tipo simple ou, se você quiser que o script termine antes de prosseguir, oneshot.

Por fim, o systemd é compatível com com scripts SysV. Coloque-os em /etc/init.d. Depois de rodar systemctl daemon-reload, será criado um serviço virtual com o nome de cada script e a extensão .service. Note que scripts SysV precisam seguir uma padronização também; não é qualquer shell script que qualifica-se como tal (1, 2). As (poucas) incompatibilidades estão documentadas aqui: Compatibility with SysV.

Isso é o básico para facilitar a interpretação dos arquivos. Não comentei sobre opções de ordenação da seção [Unit] (Requires=, BindsTo=, Before=, After=, etc.) nem de instalação de [Install].

Lembre que não devemos editar diretamente os arquivos presentes em /usr/lib, que é área do gerenciador de pacotes. Os manuais são excelentes, valem a leitura: systemd.index.

Relacionado:
Modificar parâmetros dos arquivos de unidade do systemd
Olho de Cylon do systemd
Ordenação no systemd
systemctl cat/edit
systemctl enable|disable|mask --now
systemctl revert

Comentários