Guida alla configurazione di servizi con systemd
In questo articolo esploreremo cosa è systemd, che problemi risolve in ambito embedded e come possiamo scrivere un file di configurazione per una nostra applicazione.
Per prima cosa diciamo che systemd è un gestore di servizi per Linux; un servizio è un qualsiasi programma in esecuzione sul sistema. Systemd è compatibile con gli script di init SysV, ma offre funzionalità avanzate come:
- avvio dei servizi in parallelo
- attivazione di servizi tramite socket o DBus
- gestione delle dipendenze tra servizi diversi
- isolamento dei processi tramite cgroups
Queste caratteristiche consentono di semplificare la configurazione dei servizi al boot di un sistema embedded, nonché di controllare in maniera precisa ogni singolo servizio.
Pensiamo ad esempio ad una applicazione GUI che durante il suo funzionamento necessita di lanciare altri processi, come ad esempio un player musicale. In un normale sistema tali processi hanno vita propria rispetto al programma che li ha lanciati, quindi per assicurarsi di fermare tutta la GUI è necessario fare il kill di tutti i processi che la GUI può lanciare.
Con systemd tutto questo non è necessario perché i processi lanciati dalla GUI fanno parte dello stesso servizio e quindi basta fermare il servizio e systemd sa quali processi vanno fermati.
Configurazione dei servizi
Ogni servizio è descritto da un file di configurazione, che ha un formato molto simile ai file INI.
Il file più semplice è composto di 3 sezioni, Unit, Service and Install
[Unit]
Contiene le informazioni di base del servizio. I campi più importanti di questa sezione sono:
- Description: una riga che descriva il servizio;
- Wants: lista di servizi che sono necessari per questo servizio. Se il vostro programma usa l’attivazione via socket o dbus, systemd gestisce automaticamente le dipendenze e questo campo non è necessario;
- After: aspetta che i servizi indicati in questo campo sia partiti prima di far partire questo processo. Anche questo campo può essere omesso se si usa l’attivazione via socket o dbus.
[Service]
Questa sezione specifica qual è l’eseguibile da lanciare e il modo con cui verrà lanciata. I campi più importanti sono:
- ExecStart: il path assoluto dell’eseguibile;
- Environment: viene usato per impostare le variabili di ambiente per il processo. Ad esempio: Environment=EGLFS_HIDE_CURSOR=1 “POINTERCAL_FILE=/path/with spaces/pointercal” Da notare che se la variabile contiene spazi, bisogna mettere tra virgolette anche il nome della variabile.
- Type: è usato da systemd per capire quale processo sia il “processo principale”. È utile quando ci sono dei demoni che fanno fork un po’ di volte prima di partire; al contrario, non è particolarmente utile nel caso di una singola applicazione da lanciare. Basta impostarlo a simple;
- Restart: configura come e quando il servizio deve essere fatto partire. Per i sistemi embedded che devono stare sempre accesi il valore sensato è always;
- SuccessExitStatus: una lista di segnali o numeri (diversi da 0) che indicano una terminazione corretta del programma.
Le fork multiple sono usate molto per creare demoni a partire dalla shell; la documentazione è molto dettagliata a riguardo.
[Install]
Questa sezione è utile per decidere quando un servizio va abilitato o disabilitato. Nel gergo systemd, un servizio è abilitato se viene avviato automaticamente al boot; solitamente questo è il comportamento voluto per i sistemi embedded. Il tutto si riduce a:
[Install]
WantedBy=multi-user.target
Esempio completo
Ecco un esempio completo di un file di configurazione per un’applicazione GUI che vogliamo far partire al boot.
[Unit]
Description=Description for myprogram
Wants=rpcbind.service
After=rpcbind.service
[Service]
Environment=“MY_ENV_VAR=/some/path/to/file with space.ini”
ExecStart=/usr/local/myprogram
Type=simple
[Install]
WantedBy=multi-user.target
Con questo abbiamo concluso; per maggiori informazioni vi invito a leggere la documentazione di systemd e la guida alle variabili dei file di configurazione.