Systemd, scripts interactivos y temporizadores

Systemd, scripts interactivos y temporizadores

introducción

Al desarrollar para Linux, surge la tarea de crear scripts interactivos que se ejecutan cuando se enciende o apaga el sistema. En el sistema V esto era fácil, pero con systemd hace ajustes. Pero puede tener sus propios temporizadores.

¿Por qué necesitamos objetivos?

A menudo se escribe que el objetivo sirve como análogo del nivel de ejecución en el sistema V -init. Estoy fundamentalmente en desacuerdo. Hay más y puede dividir paquetes en grupos y, por ejemplo, iniciar un grupo de servicios con un comando y realizar acciones adicionales. Además, no tienen jerarquía, sólo dependencias.

Ejemplo de destino cuando está habilitado (descripción general de funciones) con secuencia de comandos interactiva en ejecución

Descripción del objetivo en sí:

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

Este objetivo se iniciará cuando se inicie multi-user.target y llame al installer.service. Sin embargo, puede haber varios de estos servicios.

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

Y finalmente, un ejemplo del script en ejecución:

#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer

Lo más importante es seleccionar final.target, el objetivo al que debe llegar el sistema al inicio. Durante el proceso de inicio, systemd revisará las dependencias y ejecutará todo lo que necesite.
Hay diferentes formas de seleccionar final.target, utilicé la opción del cargador para esto.

El lanzamiento final se ve así:

  1. Se inicia el gestor de arranque
  2. El gestor de arranque comienza a ejecutar el firmware pasando el parámetro final.target
  3. Systemd comienza a iniciar el sistema. Va secuencialmente a installer.target o work.target desde basic.target a través de sus dependencias (por ejemplo, multi-user.target). Estos últimos hacen que el sistema funcione en el modo deseado.

Preparando el firmware para el lanzamiento

Al crear firmware, siempre surge la tarea de restaurar el estado del sistema al inicio y guardarlo al apagarlo. Estado significa archivos de configuración, volcados de bases de datos, configuraciones de interfaz, etc.

Systemd ejecuta procesos en el mismo objetivo en paralelo. Existen dependencias que le permiten determinar la secuencia de inicio de los scripts.

¿Cómo funciona en mi proyecto ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. El sistema comienza
  2. Se inicia el servicio settings_restore.service, que comprueba la presencia del archivo settings.txt en la sección de datos. Si no está allí, en su lugar se coloca un archivo de referencia y luego se restaura la configuración del sistema:
    • contraseña de administrador
    • nombre de host,
    • zona horaria
    • lugar
    • Determina si se están utilizando todos los medios. De forma predeterminada, el tamaño de la imagen es pequeño para facilitar la copia y grabación en un medio. Al inicio, comprueba si todavía queda espacio sin utilizar. Si es así, el disco se vuelve a particionar.
    • Generando ID de máquina a partir de la dirección MAC. Esto es importante para obtener la misma dirección a través de DHCP.
    • Ajustes de red
    • Limita el tamaño de los registros.
    • El disco externo se está preparando para funcionar (si la opción correspondiente está habilitada y el disco es nuevo)
  3. Iniciar postgresq
  4. Se inicia el servicio de restauración. Es necesario preparar el propio zabbix y su base de datos:
    • Comprueba si ya existe una base de datos zabbix. Si no, se crea a partir de volcados de inicialización (incluidos con zabbix)
    • se crea una lista de zonas horarias (necesaria para mostrarlas en la interfaz web)
    • Se encuentra la IP actual, se muestra en el problema (invitación a iniciar sesión en la consola)
  5. La invitación cambia - aparece la frase Listo para trabajar
  6. El firmware está listo para usar.

Los archivos de servicio son importantes, son los que marcan la secuencia de su lanzamiento.

[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 puede ver, instalé dependencias para que mi script funcionara primero, y solo entonces la red se activaría y se iniciaría el DBMS.

Y el segundo servicio (preparación de 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

Es un poco más complicado aquí. El lanzamiento también se realiza en multi-user.target, pero DESPUÉS de iniciar el DBMS postgresql y mi configuración_restore. Pero ANTES de iniciar los servicios de zabbix.

Servicio de temporizador para logrotate

Systemd puede reemplazar a CRON. En serio. Además, la precisión no es de un minuto, sino de un segundo (y si es necesario), o puede crear un temporizador monótono, llamado por el tiempo de espera de un evento.
Era el monótono cronómetro que cuenta el tiempo desde el inicio de la máquina que creé.
Esto requerirá 2 archivos
logrotateTimer.service - la descripción real del servicio:

[Unit]
Description=run logrotate

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

Es simple: descripción del comando de inicio.
El segundo archivo logrotateTimer.timer es donde funcionan los temporizadores:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

Que hay aquí:

  • descripción del temporizador
  • Primera hora de inicio, a partir del inicio del sistema
  • período de nuevos lanzamientos
  • Dependencia del servicio del temporizador. De hecho, esta es la cadena que hace que el temporizador

Script interactivo al apagar y su objetivo de apagado

En otro desarrollo, tuve que hacer una versión más compleja de apagar la máquina, a través de mi propio objetivo, para poder realizar muchas acciones. Generalmente se recomienda crear un servicio oneshot con la opción RemainAfterExit, pero esto le impide crear un script interactivo.

¡Pero el hecho es que los comandos lanzados por la opción ExecOnStop se ejecutan fuera del TTY! Es fácil de comprobar: pegue el comando tty y guarde su resultado.

Por lo tanto, implementé el cierre a través de mi objetivo. No pretendo ser 100% correcto, ¡pero funciona!
Cómo se hizo (en términos generales):
Creé un objetivo my_shutdown.target, que no dependía de nadie:
mi_apagado.objetivo

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

Al ir a este objetivo (a través de systemctl aislar my_shutdwn.target), lanzó el servicio my_shutdown.service, cuya tarea es simple: ejecutar el 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 de este script realizo las acciones necesarias. Puede agregar muchos scripts al destino para mayor flexibilidad y conveniencia:

mi_apagado.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 los archivos /tmp/reboot y /tmp/shutdown. No puedes llamar al objetivo con parámetros. Sólo el servicio es posible.

Pero uso target para tener flexibilidad en el trabajo y un orden de acciones garantizado.

Sin embargo, lo más interesante vino después. Es necesario apagar/reiniciar la máquina. Y hay 2 opciones:

  • Reemplace los comandos de reinicio, apagado y otros (todavía son enlaces simbólicos a systemctl) con su script. Dentro del script, vaya a my_shutdown.target. Y los scripts dentro del objetivo llaman a systemctl directamente, por ejemplo, systemctl reboot
  • Una opción más sencilla, pero no me gusta. En todas las interfaces, no llame a apagado/reinicio/otro, sino que llame directamente al systemctl de destino aislar my_shutdown.target

Elegí la primera opción. En systemd, reiniciar (como apagar) son enlaces simbólicos a systemd.

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

Por lo tanto, puedes reemplazarlos con tus propios scripts:
reiniciar

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

Fuente: habr.com

Añadir un comentario