
Introducere
At the development stage under Linux, tasks arise for creating interactive scripts that are executed when the system is started or shut down. In System V, this was done easily, but systemd makes adjustments. However, it has its own timers.
Why target is needed
It is often stated that targets serve as an analogy to runlevels in System V init. I fundamentally disagree. There are more of them, and you can group packages, for example, starting a group of services with a single command and performing additional actions. Moreover, they do not have a hierarchy, only dependencies.
Example of a target at startup (overview of capabilities) with the execution of an interactive script
Description of the target itself:
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.serviceThis target will start when multi-user.target is activated and will invoke installer.service. There can be several such services.
cat installer.service
[Unit]
# description
Description=installer interactive dialog
[Service]
# Start once when everything else is up
Type=idle
# Startup command - call the script
ExecStart=\/usr\/bin\/installer.sh
# Interactive user interaction through tty3
StandardInput=tty
TTYPath=\/dev\/tty3
TTYReset=yes
TTYVHangup=yes
[Install]
WantedBy=installer.targetAnd finally, an example of an executed script:
#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answerThe most important thing is to choose final.target — the target to which the system should reach upon startup. During the boot process, systemd will go through the dependencies and start everything necessary.
You can select final.target in various ways; I used the bootloader option for this.
The final boot looks like this:
- The bootloader starts
- The bootloader begins to execute the firmware, passing the parameter final.target
- Systemd begins to start the system. It sequentially progresses to installer.target or work.target from basic.target through their dependencies (for example, multi-user.target). The latter brings the system to operate in the desired mode.
Preparing the firmware for launch
When creating firmware, the task of restoring the system state at startup and preserving it at shutdown always arises. The state refers to configuration files, database dumps, interface settings, etc.
Systemd starts processes in one target in parallel. There are dependencies that allow determining the order of script execution.
How this works in my project ( )
- The system starts
- Se lansează serviciul settings_restore.service. Acesta verifică existența fișierului settings.txt în directorul de date. Dacă nu este disponibil, se plasează un fișier de referință în locul său. Apoi, se restaurează setările sistemului:
- parola de administrator
- hostname,
- fus orar
- localizare
- Determinare a utilizării complete a mediului. În mod implicit, dimensiunea imaginii este mică — pentru ușurința copierii și scrierii pe mediu. La pornire, se verifică dacă există spațiu inutilizat. Dacă există, discul este redistribuit.
- Generarea machine-id din adresa MAC. Acest lucru este important pentru obținerea aceleași adrese prin DHCP.
- Setările rețelei
- Se limitează dimensiunea log-urilor
- Se pregătește un disc extern (dacă opțiunea corespunzătoare este activată și discul este nou)
- se lansează postgresq
- se lansează serviciul restore. Acesta este necesar pentru pregătirea zabbix-ului și a bazei sale de date:
- Se verifică dacă baza de date zabbix există deja. Dacă nu, aceasta se creează din dump-urile inițiale (inclusă în livrarea zabbix)
- se creează o listă de fusuri orare (necesară pentru afișarea lor în interfața web)
- Se determină IP-ul curent, care este afișat în invitația de intrare în consolă
- Se schimbă invitația — apare fraza Ready to work
- Firmware-ul este pregătit pentru lucru
Fișierele serviciilor sunt importante, ele stabilesc secvența de lansare a acestora
[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.targetAșa cum se poate observa, am stabilit dependențele astfel încât mai întâi să ruleze scriptul meu, și abia apoi să se activeze rețeaua și să se pornească SGBD-ul.
Și al doilea serviciu (pregătirea zabbix-ului)
#!/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.targetAici este puțin mai complicat. Lansarea este de asemenea în multi-user.target, dar DUPĂ lansarea SGBD-ului postgresql și a meu setting_restore. Dar ÎNAINTE de lansarea serviciilor zabbix.
Serviciul cu timer pentru logrotate
Systemd poate înlocui CRON. Serios. De fapt, precizia nu este până la minut, ci până la secundă (de parcă ar fi nevoie). Se poate crea un timer monoton, invocat după un timeout de eveniment.
Exact un timer monoton, care contabilizează timpul de la pornirea mașinii, am creat.
Pentru aceasta sunt necesare 2 fișiere
logrotateTimer.service — descrierea serviciului:
[Unit]
Description=run logrotate
[Service]
ExecStart=logrotate \/etc\/logrotate.conf
TimeoutSec=300Totul este simplu — descrierea comenzii de lansare.
Al doilea fișier logrotateTimer.timer — acesta stabilește funcționarea timerelor:
[Unit]
Description=Run logrotate
[Timer]
OnBootSec=15min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.targetCe este aici:
- descrierea temporizatorului
- Timpul primei activări, începând de la încărcarea sistemului
- perioada activărilor ulterioare
- Dependența de serviciul de temporizare. Practic, aceasta este linia care face temporizatorul
Script interactiv la oprire și ținta sa de oprire
Într-o altă dezvoltare, a trebuit să creez o variantă mai complexă de oprire a mașinii — printr-o țintă proprie, pentru a efectua multe acțiuni. De obicei, este recomandat să se creeze un serviciu oneshot cu opțiunea RemainAfterExit, dar aceasta nu permite crearea unui script interactiv.
Problema este că comenzile efectuate cu opțiunea ExecOnStop se execută în afara TTY! Verificarea este simplă — inserați comanda tty și salvați ieșirea sa.
De aceea, am implementat oprirea prin ținta mea. Nu pretind 100% corectitudine, dar funcționează!
Cum a fost realizat (în linii mari):
Am creat ținta my_shutdown.target, care nu depindea de nimeni:
my_shutdown.target
[Unit]
Description=my shutdown
AllowIsolate=yes
Wants=my_shutdown.service Când trece în această țintă (prin systemctl isolate my_shutdown.target), pornește serviciul my_shutdown.service, al cărui scop este simplu — să execute scriptul 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- În cadrul acestui script, execut acțiunile necesare. Se pot adăuga multe scripturi în țintă, pentru flexibilitate și confort:
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/
$commandNotă. Utilizarea fișierelor \/tmp\/reboot și \/tmp\/shutdown. Nu se poate apela ținta cu parametri. Numai serviciul poate fi apelat.
Dar folosesc ținta pentru a avea flexibilitate în utilizare și un ordin garantat de execuție a acțiunilor.
Totuși, cel mai interesant a fost apoi. Mașina trebuie oprită\/repornită. Și aici sunt 2 variante:
- Să înlocuiesc comenzile reboot, shutdown și altele (care sunt oricum linkuri simbolice către systemctl) cu propriul meu script. În interiorul scriptului — trecerea în my_shutdown.target. Iar scripturile din țintă apoi apelează direct systemctl, de exemplu, systemctl reboot
- O variantă mai simplă, dar care nu îmi place. În toate interfețele să nu apeleze shutdown\/reboot\/alte comenzi, ci să aplice direct ținta systemctl isolate my_shutdown.target
Am ales prima variantă. În systemd, reboot (la fel ca și poweroff) sunt linkuri simbolice către systemd.
ls -l \/sbin\/poweroff
lrwxrwxrwx 1 root root 14 sep 30 18:23 \/sbin\/poweroff -> \/bin\/systemctlDe aceea, acestea pot fi înlocuite cu scripturile proprii:
repornire
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fiSursa: habr.com
