Introduzione
Quando si sviluppa per Linux, si pone il compito di creare script interattivi che vengono eseguiti all'accensione o allo spegnimento del sistema. Nel sistema V questo era facile, ma con systemd apporta modifiche. Ma può avere i propri timer.
Perché abbiamo bisogno di obiettivi?
Viene spesso scritto che target funge da analogo del runlevel nel sistema V -init. Fondamentalmente non sono d'accordo. Ce ne sono di più e puoi dividere i pacchetti in gruppi e, ad esempio, avviare un gruppo di servizi con un comando ed eseguire azioni aggiuntive. Inoltre, non hanno gerarchia, solo dipendenze.
Esempio di target quando abilitato (panoramica delle funzionalità) con l'esecuzione di script interattivo
Descrizione dell'obiettivo stesso:
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
Questo target verrà avviato quando viene avviato multi-user.target e chiama installer.service. Tuttavia, potrebbero esserci diversi servizi di questo tipo.
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 infine, un esempio dello script in esecuzione:
#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer
La cosa più importante è selezionare final.target, il target a cui il sistema dovrebbe arrivare all'avvio. Durante il processo di avvio, systemd esaminerà le dipendenze e avvierà tutto ciò di cui ha bisogno.
Esistono diversi modi per selezionare final.target, per questo ho utilizzato l'opzione caricatore.
Il lancio finale è simile al seguente:
- Si avvia il bootloader
- Il bootloader inizia ad avviare il firmware passando il parametro final.target
- Systemd inizia ad avviare il sistema. Va in sequenza a installer.target o work.target da basic.target attraverso le relative dipendenze (ad esempio, multi-user.target). Questi ultimi portano il sistema a funzionare nella modalità desiderata
Preparazione del firmware per il lancio
Quando si crea il firmware, si pone sempre il compito di ripristinare lo stato del sistema all'avvio e di salvarlo allo spegnimento. Stato indica file di configurazione, dump del database, impostazioni dell'interfaccia, ecc.
Systemd esegue i processi nella stessa destinazione in parallelo. Esistono dipendenze che consentono di determinare la sequenza di avvio degli script.
Come funziona nel mio progetto (
- Il sistema si avvia
- Viene avviato il servizio settings_restore.service che verifica la presenza del file settings.txt nella sezione dati. Se non è presente, al suo posto viene inserito un file di riferimento e successivamente vengono ripristinate le impostazioni di sistema:
- password dell'amministratore
- Nome host,
- fuso orario
- locale
- Determina se vengono utilizzati tutti i media. Per impostazione predefinita, la dimensione dell'immagine è piccola, per facilitare la copia e la registrazione su supporto. All'avvio controlla se c'è ancora spazio inutilizzato. Se esiste, il disco viene ripartizionato.
- Generazione dell'ID macchina dall'indirizzo MAC. Questo è importante per ottenere lo stesso indirizzo tramite DHCP
- Impostazioni di rete
- Limita la dimensione dei log
- L'unità esterna viene preparata per il lavoro (se l'opzione corrispondente è abilitata e l'unità è nuova)
- Inizia postgresq
- Viene avviato il servizio di ripristino. È necessario per preparare zabbix stesso e il suo database:
- Controlla se esiste già un database zabbix. In caso contrario, viene creato dai dump di inizializzazione (inclusi con zabbix)
- viene creato un elenco di fusi orari (necessario per visualizzarli nell'interfaccia web)
- Viene trovato l'IP corrente, viene visualizzato in questione (invito ad accedere alla console)
- L'invito cambia: appare la frase Pronto a lavorare
- Il firmware è pronto per l'uso
I file di servizio sono importanti, sono loro che stabiliscono la sequenza del loro avvio
[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
Come puoi vedere, ho installato le dipendenze in modo che il mio script funzionasse prima e solo allora la rete si collegasse e il DBMS si avviasse.
E il secondo servizio (preparazione 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
Qui è un po’ più complicato: anche il lancio avviene in multi-user.target, ma DOPO aver avviato il DBMS postgresql e il mio setting_restore. Ma PRIMA di avviare i servizi zabbix.
Servizio timer per logrotate
Systemd può sostituire CRON. Sul serio. Inoltre, la precisione non è fino al minuto, ma fino al secondo (e se fosse necessario), oppure puoi creare un timer monotono, richiamato da un timeout da un evento.
Era il timer monotono che conta il tempo dall'avvio della macchina che ho creato.
Ciò richiederà 2 file
logrotateTimer.service - la descrizione effettiva del servizio:
[Unit]
Description=run logrotate
[Service]
ExecStart=logrotate /etc/logrotate.conf
TimeoutSec=300
È semplice: descrizione del comando di avvio.
Il secondo file logrotateTimer.timer è dove funzionano i timer:
[Unit]
Description=Run logrotate
[Timer]
OnBootSec=15min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.target
Cosa c'è qui:
- descrizione del timer
- Prima ora di avvio, a partire dall'avvio del sistema
- periodo di ulteriori lanci
- Dipendenza dal servizio timer. In effetti, questa è la stringa che costituisce il timer
Script interattivo allo spegnimento e obiettivo di spegnimento
In un altro sviluppo, ho dovuto eseguire una versione più complessa dello spegnimento della macchina, attraverso il mio obiettivo, per eseguire molte azioni. Di solito è consigliabile creare un servizio one-shot con l'opzione RemainAfterExit, ma ciò impedisce di creare uno script interattivo.
Ma il fatto è che i comandi lanciati dall'opzione ExecOnStop vengono eseguiti al di fuori del TTY! È facile controllare: incolla il comando tty e salva il suo output.
Pertanto, ho implementato l'arresto tramite il mio obiettivo. Non pretendo di essere corretto al 100%, ma funziona!
Come è stato fatto (in termini generali):
Ho creato un target my_shutdown.target, che non dipendeva da nessuno:
mio_spegnimento.target
[Unit]
Description=my shutdown
AllowIsolate=yes
Wants=my_shutdown.service
Quando si accede a questo target (tramite systemctl isolate my_shutdwn.target), viene avviato il servizio my_shutdown.service, il cui compito è semplice: eseguire lo 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
- All'interno di questo script eseguo le azioni necessarie. Puoi aggiungere molti script alla destinazione per flessibilità e comodità:
mio_spegnimento.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. Utilizzando i file /tmp/reboot e /tmp/shutdown. Non è possibile chiamare target con parametri. È possibile solo il servizio.
Ma utilizzo Target per avere flessibilità nel lavoro e un ordine di azioni garantito.
La cosa più interessante però è arrivata dopo. La macchina deve essere spenta/riavviata. E ci sono 2 opzioni:
- Sostituisci reboot, shutdown e altri comandi (sono ancora collegamenti simbolici a systemctl) con il tuo script. All'interno dello script, vai su my_shutdown.target. E gli script all'interno del target chiamano direttamente systemctl, ad esempio systemctl reboot
- Un'opzione più semplice, ma non mi piace. In tutte le interfacce, non chiamare shutdown/reboot/other, ma chiamare direttamente il target systemctl isolate my_shutdown.target
Ho scelto la prima opzione. In systemd, il riavvio (come lo spegnimento) sono collegamenti simbolici a systemd.
ls -l /sbin/poweroff
lrwxrwxrwx 1 root root 14 сен 30 18:23 /sbin/poweroff -> /bin/systemctl
Pertanto, puoi sostituirli con i tuoi script:
reboot
#!/bin/sh
touch /tmp/reboot
sudo systemctl isolate my_shutdown.target
fi
Fonte: habr.com