#howto - Creare un servizio o timer di systemd

Scritto da il
Redatto da
ubuntu
systemd
bash

In questa guida vediamo come programmare un servizio per systemd, il tutto cercando di riassumere e semplificare le già abbondanti documentazioni della freedesktop ( qua per la sezione service, qua per il log degli eventi e qua per unit e install )

Un generico service

Vediamo innanzitutto la struttura di un generico service. I servizi si dividono in tre sezioni:

  • Unit, descrive il servizio, il modo in cui è avviato, i processi che dipendono da esso o quelli da cui dipende, il modo in cui si relaziona al sistema.

  • Service, descrive il comando o lo script eseguito, come viene eseguito, quante volte e quando considerarlo un fallimento.

  • Install, ulteriori specifiche su come il sistema deve abilitare il servizio, eventuali alias con cui collegarlo, quante unità attiva con se e la cartella in cui verrà collegato.

Ogni sezione ha diverse opzioni disponibili, vediamo uno schema generico:

[Unit]
Description=una descrizione
After=lista di servizi che vengono prima

Before=lista di servizi che vengono dopo

Condition...=se non verificata, il servizio non viene eseguito 
altro, esistono vari tipi di condition! ConditionHost,ConditionPathExist..etc

[Service]
Type=indica il tipo di servizio, simple, exec, forking, oneshot, dbus, notify o idle

RemainAfterExit=true o false, indica se il processo deve rimanere in esecuzione anche dopo l'avvio

ExecStart=qui inserite lo script o  il comando da eseguire 

ExecStop=qui indicare ciò che viene eseguito quando viene terminato il processo

Restart=indica se il processo deve essere eseguito più volte, ad esempio al successo, al fallimento, sempre... viene configurato con un timer (valori: no, on-success, on-failure, on-abnormal,on-abort,always...)

RestartSec=tempo prima di fare restart

TimeoutStartSec=indica quanto tempo deve bloccare l'avvio prima di dire che un servizio è o non è fallito

TimeoutStopSec=idem di sopra ma con lo script eseguito in chiusura

Standard...=StandardOutput e StandardError (sono due opzioni distinte), cioè dove vengono stampati errori o messaggi, i valori possono essere journal, tty, journal+console, file:/path/per/file (sarà cancellato se esiste), append:/path/per/file (aggiunge alla fine) e altri. Guardare il link sul logging

[Install]
WantedBy=indica la cartella in cui viene collegato il servizio

Tagliamo subito la testa al toro:

  • sono tutte necessarie? no!
  • ne esistono altre di opzioni? Sì, guarda i link di sopra

Inoltre potete mettere in alcune opzioni dei parametri speciali, ad esempio %L che sta per log directory, o %n che indica il nome del file di service, trovate tutto nelle documentazioni ufficiali.

Servizio di avvio

Scriviamo ad esempio un servizio di avvio generico, studiamone la struttura, testiamolo e mettiamolo in funzione. Un po’ come il vecchio file /etc/rc.local che forniva ubuntu nelle sue installazioni.

Innanzitutto scriviamo uno script che deve essere avviato ogni accensione, non importa cosa sia, ma ricordate che questo script sarà avviato con privilegi elevati (root).

Poniamo ad esempio che il file sia: /etc/avvio

All’interno ricordate che la prima riga deve essere#!/bin/bash

e dopo che avete finito lo script, dovete renderlo eseguibilesudo chmod +x /etc/avvio

Poi scrivete il file /etc/systemd/system/avvio.service

All’interno scrivete:

[Unit]
 Description=Esegue /etc/avvio
 ConditionPathExists=/etc/avvio

[Service]
 Type=oneshot
 ExecStart=/etc/avvio
 StandardOutput=journal

[Install]
 WantedBy=multi-user.target

Non è difficilissimo da leggere, ma facciamo un riepilogo:

La sezione [Unit] fornisce una descrizione del servizio attraverso l’opzione Description e poi verifica che lo script da eseguire esista tramite ConditionPathExist.

La parte [Service] ci dice che il comando è oneshot, cioè viene semplicemente eseguito, Lo StandardOutput dice dove avverrà il log della riuscita o del fallimento (ed eventuali stampe), nel nostro caso il journal.

La parte [Install] ci dice che il file verrà eseguito nella cartella multi-user tramite opzione WantedBy. Su questo spendo giusto qualche parolina per spiegare cosa sono i livelli di running:
Nei sistemi Linux l’avvio è sottoposto a più fasi, detti livelli:

  • livello 0 è il livello di spegnimento (poweroff ), raggiunto quando il pc viene spento
  • livello 1 livello di emergenza (rescue ), è intermedio tra l’avvio del sistema hardware e quello software
  • da livello 2 a livello 4 si parla di livelli utente (multi-user ), ed è la fase di avvio del nostro sistema
  • livello 5 è il livello grafico (graphic ), usato dal nostro display manager
  • livello 6 è il livello di spegnimento o riavvio (reboot ) in cui il sistema torna a livello 0

Il sistema dei livelli è stato ridefinito su systemd con i target, che ha aggiunto diversi stadi come lo sleep.target o il target network, qua trovate tutti i target.
Ora dovrebbe esservi chiaro perchè, a meno che non sia uno script abbastanza importante, è difficile vedere services con target diversi da multi-user.target.

Testate il nuovo servizio scritto con: systemctl start avvio.servizio

Potete poi chiedere che venga avviato insiema al sistema con systemctl enable avvio.service

Sul nostro canale feed di telegram trovate altri trick per l’amministrazione dei servizi di systemd.

Trim SSD all’avvio

Proviamo quindi a fare un servizio per fare il trim di un SSD all’avvio del PC:

[Unit]
 Description=Esegue il trim dell'ssd

[Service]
 Type=oneshot
 ExecStart=fstrim -v /
 StandardOutput=journal

[Install]
 WantedBy=multi-user.target

Come notate non ho richiamato uno script, ma direttamente il comando che volevo eseguire.
Ovviamente è la stessa cosa: potrei richiamare lo script /etc/avvio.sh dentro il quale ho scritto l’operazione, ma questo tendezialmente potrebbe portarmi a scrivere più operazioni nello stesso file. Inoltre, È sempre bene separare il più possibile la lista di operazioni, questo per avere sempre chiaro eventualmente i dettagli sul fallimento dell’avvio o anche semplicemente perché queste operazioni andranno ad aumentare i tempi di avvio del nostro sistema e potremmo voler sapere quali di queste operazioni vogliamo eliminare.

systemd timer

Ad ogni service si può associare un timer, questa pratica potrebbe essere utile ad esempio se vogliamo che un determinato script venga ripetuto ogni settimana oppure ogni minuto piuttosto che ogni accesso. Associamo al nostro avvio.service un avvio.timer e inseriamo anche questo nella cartella /etc/systemd/system Scriviamo al suo interno:

[Unit]
Description=un timer associato ad avvio.service

[Timer]
Unit=avvio.service
OnUnitActiveSec=1us
OnUnitInactiveSec=10s

[Install]
WantedBy=multi-user.target

Sulle sezioni Unit e Install ho poco da dire, sono le stesse di sopra. Diversa la sezione Timer però; in questa sezione dobbiamo inserire fondamentalmente alune informazioni: che unità far partire, quando il timer deve partire, ogni quanto il comando deve ripetersi.

Innanzitutto:

  • Unit=per indicare la unità da avviare

Per quanto riguarda i tempi di attivazione invece:

  • OnActiveSec=quanti secondi dopo l’avvio del timer
  • OnBootSec=quanti secondi dopo l’avvio del pc
  • OnStartupSec=quanti secondi dopo l’avvio di systemd (poco usato)
  • OnUnitActiveSec=quanti secondi dopo l’attivazione dell’unità di riferimento
  • OnUnitInactiveSec=quanti secondi dopo che l’unità diventa inattiva

Le unità di tempo impostabili sono

  • us =microsecondo
  • ms =millisecondo
  • s =sec
  • m =minuto
  • h =ora
  • d =giorno
  • w =settimana
  • M =mese
  • y =anno

I tempi di attivazione, se combinati, danno vita al tempo di ripetizione. Supponiamo di avere OnActiveSec=1us e OnUnitInactiveSec=10s, il timer una volta dato lo start da systemd si avvierebbe subito (1 microsecondo ), terminerebbe il job, e l’unità diventerebbe inattiva attivando il timer da 10s. Possiamo ovviamente usare anche OnUnitActiveSec con la stessa logica.

Possiamo attivare un giorno preciso di attivazione attraverso:

  • OnCalendar =valore in formato yyyy-MM-gg hh:mm:ss o simili

Abbiamo inoltre le alcune opzioni speciali:

  • AccuracySec =una volta raggiunto il timeout del timer, quanti secondi può ritardare l’opzione se il processo non può essere avviato subito
  • WakeSystem =se il sistema è sospeso, può svegliarlo se questo parametro è impostato a true
  • RemainAfterElapse =se il timer non ha un tempo di ripetizione, può rimanere in memoria se impostato a true

Attenzione i timer di systemd non sono precisi sotto il minuto, e hanno un delay anche notevole a volte. Ha più senso usare i timer di systemd per job settimanali o giornalieri che non devono essere precisi al secondo, anche impostando AccuracySec ho riscontrato ritardi.
Qui potete trovare ulteriori informazioni sui timer di systemd

Per avviare un servizio tramite un timer, non va avviato il servizio, ma il timer stesso: systemctl start avvio.timer

In caso di dubbi o particolari domande, potete entrare nel gruppo Telegram ufficiale di linux/hub.

history_edu Revisioni