Systemd, scripts interativos e timers


1. Introdução


Ao desenvolver para linux, há tarefas de criação de scripts interativos que são executados quando o sistema é ligado ou desligado. No sistema V, isso foi feito facilmente, mas com o systemd ele faz ajustes. Mas pode fazer seus temporizadores.


Por que precisamos de um alvo


Costuma-se escrever que o destino serve como um análogo do nível de execução no sistema V-init. Eu discordo fundamentalmente. Existem mais deles e você pode separar pacotes em grupos e, por exemplo, executar um grupo de serviços com uma equipe e executar ações adicionais. Além disso, eles não têm hierarquia, apenas dependências.


Exemplo de destino na inicialização (visão geral do recurso) com o lançamento de um script interativo


Descrição do próprio destino:


cat installer.target [Unit] Description=My installer Requires=multi-user.target Conflicts=rescue.service rescue.target After=multi-user.target rescue.service rescue.target AllowIsolate=yes Wants=installer.service 

Esse destino será iniciado quando o multi-user.target for iniciado e chamar installer.service. Além disso, pode haver vários desses serviços.


 cat installer.service [Unit] #  Description=installer interactive dialog [Service] #   ,     Type=idle #   -   ExecStart=/usr/bin/installer.sh #      tty3 StandardInput=tty TTYPath=/dev/tty3 TTYReset=yes TTYVHangup=yes [Install] WantedBy=installer.target 

E, finalmente, um exemplo de um script executável:


 #!/bin/bash #   tty3 chvt 3 echo "Install, y/n ?" read user_answer 

O mais importante é escolher final.target - target, ao qual o sistema deve chegar na inicialização. No processo de inicialização, o systemd examinará as dependências e executará tudo o que você precisa.
Existem várias maneiras de selecionar final.target, usei a opção bootloader para isso.


O lançamento final é assim:


  1. O carregador de inicialização inicia
  2. O carregador de inicialização inicia o firmware passando o parâmetro final.target
  3. Systemd inicia a inicialização do sistema. Ele vai seqüencialmente para installer.target ou work.target do basic.target através de suas dependências (por exemplo, multiusuário.target). Este último e levar o sistema a trabalhar no modo desejado

Preparando o firmware para inicialização


Ao criar firmware, sempre há a tarefa de restaurar o estado do sistema na inicialização e salvá-lo quando desligado. Estado significa arquivos de configuração, despejos de banco de dados, configurações de interface e assim por diante.


O Systemd inicia o processo em um destino em paralelo. Existem dependências que permitem determinar a sequência de execução do script.


Como funciona no meu projeto ( https://habr.com/en/post/477008/ https://github.com/skif-web/monitor )


  1. O sistema inicia
  2. O serviço settings_restore.service é iniciado e verifica o arquivo settings.txt na seção de dados. Se não estiver lá, o arquivo de referência é colocado em seu lugar.Em seguida, as configurações do sistema são restauradas:
    • senha de administrador
    • nome do host
    • fuso horário
    • localidade
    • Determina se toda a mídia está em uso. Por padrão, o tamanho da imagem é pequeno - para a conveniência de copiar e gravar na mídia. Na inicialização, é verificado se ainda há espaço não utilizado. Se houver - o disco será reparticionado.
    • Gere o ID da máquina a partir do endereço MAC. Isso é importante para obter o mesmo endereço via DHCP.
    • Configurações de rede
    • O tamanho do log é limitado
    • A unidade externa está preparada para o trabalho (se a opção correspondente estiver ativada e a unidade for nova)
  3. Execute o postgresq
  4. o serviço de restauração é iniciado. É necessário preparar o próprio zabbix e seu banco de dados:
    • Verifica se já existe um banco de dados zabbix. Caso contrário, ele é criado a partir da inicialização dos dumps (eles são fornecidos com o zabbix)
    • uma lista de fusos horários é criada (necessária para exibi-los na interface da web)
    • O IP atual é encontrado, é exibido em questão (convite para entrar no console)
  5. O convite muda - a frase Pronto para trabalhar aparece
  6. O firmware está pronto para uso.

Os arquivos de serviço são importantes, são eles que definem a sequência para seu lançamento


 [Unit] Description=restore system settings Before=network.service prepare.service postgresql.service systemd-networkd.service systemd-resolved.service [Service] Type=oneshot ExecStart=/usr/bin/settings_restore.sh [Install] WantedBy=multi-user.target 

Como você pode ver, eu fiz as dependências para executar primeiro o meu script, e só então a rede aumentaria e o DBMS seria iniciado.


E o segundo serviço (preparando o zabbix)


 #!/bin/sh [Unit] Description=monitor prepare system After=postgresql.service settings_restore.service Before=zabbix-server.service zabbix-agent.service [Service] Type=oneshot ExecStart=/usr/bin/prepare.sh [Install] WantedBy=multi-user.target 

Isso é um pouco mais complicado.O lançamento também está no multi-user.target, mas DEPOIS de executar o DBMS do postgresql e meu setting_restore. Mas ANTES de executar os serviços zabbix.


Serviço com um timer para logrotate


O Systemd pode substituir o CRON. Sério. Além disso, a precisão não é de um minuto, mas de um segundo (e se for necessário) E você pode criar um cronômetro monótono, chamado pelo tempo limite do evento.
Foi o cronômetro monótono que conta o tempo desde o início da máquina que eu criei.
Isso exigirá 2 arquivos
logrotateTimer.service - a descrição real do serviço:


 [Unit] Description=run logrotate [Service] ExecStart=logrotate /etc/logrotate.conf TimeoutSec=300 

É simples - uma descrição do comando de inicialização.
O segundo arquivo logrotateTimer.timer é o que define os cronômetros para funcionarem:


 [Unit] Description=Run logrotate [Timer] OnBootSec=15min OnUnitActiveSec=15min [Install] WantedBy=timers.target 

O que há lá:


  • descrição do temporizador
  • Primeira vez iniciando na inicialização do sistema
  • período de novos lançamentos
  • Dependência de Serviço do Temporizador: Na verdade, esta é a linha e faz o temporizador

Script interativo no encerramento e destino de encerramento personalizado


Em outro desenvolvimento, tive que criar uma versão mais complexa do desligamento da máquina - através do meu próprio destino, a fim de executar muitas ações. Geralmente, é recomendável criar o serviço oneshot com a opção RemainAfterExit, mas isso impede a criação de um script interativo.


Mas o fato é que os comandos lançados pela opção ExecOnStop são executados fora do TTY! A verificação é simples - insira o comando tty e salve sua saída.


Portanto, implementei o desligamento através do meu destino. Não pretendo estar 100% correto, mas funciona!
Como foi feito (em termos gerais):
Criou o destino my_shutdown.target, que não dependia de ninguém:
my_shutdown.target


 [Unit] Description=my shutdown AllowIsolate=yes Wants=my_shutdown.service 

Ao mudar para esse destino (via systemctl isolar my_shutdwn.target), ele lançou o serviço my_shutdown.service, cuja tarefa é simples - executar o script my_shutdown.sh:


 [Unit] Description=MY shutdown [Service] Type=oneshot ExecStart=/usr/bin/my_shutdown.sh StandardInput=tty TTYPath=/dev/tty3 TTYReset=yes TTYVHangup=yes WantedBy=my_shutdown.target 

  • Dentro deste script, eu executo as ações necessárias. Você pode adicionar muitos scripts ao destino, para flexibilidade e conveniência:

my_shutdown.sh


 #!/bin/bash --login if [ -f /tmp/reboot ];then command="systemctl reboot" elif [ -f /tmp/shutdown ]; then command="systemctl poweroff" fi #    #, cp /home/user/data.txt /storage/user/ $command 

Nota Usando os arquivos / tmp / reboot e / tmp / shutdown. Você não pode chamar target com parâmetros. Você só pode atender.


Mas eu uso o target para ter flexibilidade no trabalho e uma sequência garantida de ações.


No entanto, a coisa mais interessante foi depois. A máquina deve ser desligada / reiniciada. E existem 2 opções:


  • Substitua reboot, shutdown e outros comandos (eles ainda são links simbólicos no systemctl) por seu próprio script.No interior do script, vá para my_shutdown.target. E os scripts dentro do destino chamam diretamente systemctl, por exemplo, reinicialização do systemctl
  • Um mais simples, mas eu não gosto da opção. Em todas as interfaces, não chame shutdown / reboot / others, mas chame diretamente o systemctl de destino isolate my_shutdown.target

Eu escolhi a primeira opção. No systemd, reinicialização (como poweroff) são links simbólicos no systemd.


 ls -l /sbin/poweroff lrwxrwxrwx 1 root root 14  30 18:23 /sbin/poweroff -> /bin/systemctl 

Portanto, eles podem ser substituídos por seus próprios scripts:
reiniciar


 #!/bin/sh touch /tmp/reboot sudo systemctl isolate my_shutdown.target fi 

Source: https://habr.com/ru/post/pt478846/


All Articles