Quando si avvia il computer, i molti messaggi che scorrono sulla console visualizzano molte inizializzazioni e configurazioni automatiche che vengono eseguite. Può capitare di voler modificare un po' come funziona questa fase, il che significa che è necessario conoscerla bene. Questo è lo scopo di questa sezione.
Su sistemi con un BIOS, all'inizio, esso prende il controllo del computer, inizializza i controller e l'hardware ed identifica i dischi ed i bridge, tutto questo contemporaneamente. Successivamente cerca il Master Boot Record (MBR) del primo disco nell'ordine di avvio e carica il codice ivi contenuto (primo stadio). Questo codice esegue poi il secondo stadio ed infine esegue il bootloader.
A differenza del BIOS, UEFI è più sofisticato, conosce i filesystem e può leggere la tabella delle partizioni. L'interfaccia cerca nel sistema di archiviazione una partizione etichettata con un particolare Globally Unique Identifier (identificatore univoco globale -
GUID), che la contrassegna come
EFI System Partition (
ESP), dove si trovano i bootloader, boot manager, shell UEFI, ecc., ed esegue il bootloader desiderato. Se è abilitato il Secure Boot, il processo di boot verificherà l'autenticità dei binari EFI qui contenuti tramite la firma (in questo caso è necessario
grub-efi-arch-signed). La specifica UEFI definisce e supporta anche l'avvio nella vecchia modalità BIOS. Quest'ultima è chiamata
Compatibility Support Module (
CSM). Se CSM è abilitato, cercherà di effettuare l'avvio dall'MBR dell'unità. Purtroppo molti dei nuovi sistemi non supportano più tale modalità.
In entrambi i casi il bootloader presente subentra, trova la catena di bootloader o il kernel sul disco, lo carica e lo esegue. Il kernel è quindi inizializzato, e comincia a cercare e montare la partizione contenente il filesystem root, infine esegue il primo programma — init
. Spesso, questa "partizione root" e questo init
sono, di fatto, presenti in un filesystem virtuale che esiste solo nella RAM (da qui il suo nome, "initramfs", precedentemente chiamato "initrd" che sta per "disco RAM di inizializzazione"). Questo filesystem è caricato in memoria dal bootloader, spesso da un file su disco rigido o dalla rete. Contiene il minimo indispensabile richiesto dal kernel per caricare il "vero" filesystem root: possono essere moduli driver per l'hard disk, o altri dispositivi senza i quali il sistema non si avvia, o, più frequentemente, gli script di inizializzazione ed i moduli per il montaggio degli array RAID, l'apertura di partizioni cifrate, l'attivazione di volumi LVM, ecc. Una volta che la partizione di root è montata, initramfs passa il controllo all'init reale, e la macchina torna al processo di avvio standard.
9.1.1. Il sistema di init systemd
Il "vero init" è attualmente fornito da systemd e questa sezione documenta questo sistema di init.
Systemd esegue diversi processi, responsabili della configurazione del sistema: tastiera, drivers, filesystem, rete, servizi. Lo fa mantenendo una vsione globale del sistema nel suo complesso, ed i requisiti dei componenti. Ciascun componente è descritto da un "file unit" (a volte più); la sintassi generale deriva dalla sintassi ampiamente usata nei "file *.ini", con coppie chiave = valore
raggruppate tra le intestazioni [section]
. I file unit vengono memorizzati in /lib/systemd/system/
e /etc/systemd/system/
; sono disponibili in vari gusti, ma qui ci si concentrerà su "service" e "target".
Un "file .service
" di systemd descrive un processo gestito da systemd. Contiene più o meno le stesse informazioni degli script-init vecchio stile, ma espresse in modo dichiarativo (e molto più conciso). Systemd gestisce la maggior parte dei compiti ripetitivi (avviare e arrestare il processo, controllare il suo stato, salvare i log, togliere privilegi, e così via), ed il service file ha bisogno solo di compilare le specifiche del processo. Per esempio, questo è un service file per SSH:
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.service
La sezione [Unit]
contiene informazioni generiche relative al servizio, come le risorse per la pagina del manuale e la descrizione, ma anche relazioni (dipendenza e ordine) verso gli altri servizi. La parte [Service]
contiene le dichiarazioni relative all'esecuzione del servizio (avvio, arresto, azione di kill, riavvio), directory e file di configurazione usati. L'ultima sezione, [Install]
, riporta ancora informazioni generiche con l'obiettivo di installare il servizio e, in questo caso, l'alias che può essere utilizzato al posto del nome del servizio. Come si può vedere, c'è veramente poco codice qui, solo dichiarazioni. Systemd si prende cura di visualizzare i rapporti di progresso, tenere traccia dei processi e anche riavviarli quando necessario. La sintassi di questi file è completamente descritta in diverse pagine del manuale (ad esempio: systemd.service(5), systemd.unit(5), systemd.exec(5), ecc.).
Un "file .target
" di systemd descrive uno stato del sistema, dove un insieme di servizi sono noti per essere operativi. Può essere pensato come un equivalente del runlevel vecchio-stile. Uno dei target predefiniti è local-fs.target
; quando è raggiunto, il resto del sistema può ritenere tutti i filesystem locali montati ed accessibili. Altri target includono network-online.target
e sound.target
(per un elenco di terget speciali vedere systemd.special(7)). Le dipendenze di un target possono essere elencate sia nel file di destinazione (alla riga Requires=
), oppure usando un collegamento simbolico al file del servizio nella directory /lib/systemd/system/targetname.target.wants/
. Per esempio, /etc/systemd/system/printer.target.wants/
contiene un collegamento a /lib/systemd/system/cups.service
; systemd si assicurerà quindi che CUPS sia in esecuzione in modo da raggiungere il target printer.target
.
Dal momento che gli unit file sono dichiararativi e non script o programmi, non possono essere eseguiti direttamente, e sono solo interpretati da systemd; diverese utility consentono quindi all'amministratore di interagire con systemd e controllare lo stato del sistema e di ogni componente.
La prima di queste utility è systemctl
. Quando viene eseguita senza argomenti, elenca tutti gli unit file noti a systemd (eccetto quelli che sono stati disabilitati), coì come il loro stato. systemctl status
dà una migliore visione dei servizi, nonchè dei relativi processi. Se viene passato il nome di un servizio (come in systemctl status ntp.service
), restituisce ancora più dettagli, così come le ultime righe dei log relativi al servizio (ne parleremo più avanti).
L'avvio manuale del servizio è una cosa semplice eseguendo systemctl start nomedelservizio.service
. Come si può intuire, l'arresto di un servizio è fatto con systemctl stop nomedelservizio.service
; altri comandi includono reload
e restart
.
Per controllare se un servizio è attivo (es. se partirà automaticamente all'avvio), usa systemctl enable nomedelservizio.service
(oppure disable
). is-enabled
permette il controllo dello stato del servizio.
Una caratteristica interessante di systemd è che include un componente di registrazione chiamato journald
. Si presenta come un complemento a più sistemi di registrazione tradizionali come syslogd
, ma aggiunge delle caratteristiche interessanti come un collegamento formale tra un servizio ed i messaggi che genera, e la capacità di cattuare i messaggi generati dalla sua sequenza di avvio. I messaggi possono essere visualizzati in seguito, con un piccolo aiuto da parte del comando journalctl
. Senza argomenti, sputa fuori semplicemente tutti i messaggi di log che si sono verificati dall'avvio del sistema; raramente è usato in questo modo. La maggior parte delle volte sarà utilizzato con un identificatore del servizio:
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 10:08:49 CEST, end at Tue 2015-03-31 17:06:02 CEST. --
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Received SIGHUP; restarting.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:32 mirtuel sshd[1151]: Accepted password for roland from 192.168.1.129 port 53394 ssh2
Mar 31 10:09:32 mirtuel sshd[1151]: pam_unix(sshd:session): session opened for user roland by (uid=0)
Un'altro utile flag da riga di comando è -f
, che indica a journalctl
di mantenere la visualizzazione di nuovi messaggi quando sono emessi (più di quanto faccia tail -f file
).
Se un servizio sembra non funzionare come previsto, la prima cosa da fare per risolvere il problema è quella di verifica se il servizio sia effettivamente in esecuzione con systemctl status
; se non lo è, ed i messaggi dati dal primo comando non sono sufficienti a diagnosticare il problema, controllare i log raccolti da journald su quel servizio. Ad esempio, si supponga che il server SSH non funzioni:
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: failed (Result: start-limit) since Tue 2015-03-31 17:30:36 CEST; 1s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 1188 ExecStart=/usr/sbin/sshd -D $SSHD_OPTS (code=exited, status=255)
Main PID: 1188 (code=exited, status=255)
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 17:29:27 CEST, end at Tue 2015-03-31 17:30:36 CEST. --
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Received SIGHUP; restarting.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:30:10 mirtuel sshd[1147]: Accepted password for roland from 192.168.1.129 port 38742 ssh2
Mar 31 17:30:10 mirtuel sshd[1147]: pam_unix(sshd:session): session opened for user roland by (uid=0)
Mar 31 17:30:35 mirtuel sshd[1180]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1182]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1184]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1186]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1188]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
vi /etc/ssh/sshd_config
#
systemctl start ssh.service
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: active (running) since Tue 2015-03-31 17:31:09 CEST; 2s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Main PID: 1222 (sshd)
CGroup: /system.slice/ssh.service
└─1222 /usr/sbin/sshd -D
#
Dopo aver controllato lo stato del servizio (fallito), siamo andati a controllare i registri; indicano un errore nel file di configurazione. Dopo aver modificato il file di configurazione e sistemato l'errore, riavviamo il servizio, quindi verifichiamo che sia effettivamente in funzione.
9.1.2. Il sistema di init System V
Il sistema di init di System V (che per brevità chiameremo init) esegue diversi processi, seguendo le istruzioni del file /etc/inittab
. Il primo programma che viene eseguito (che corrisponde allo step sysinit ) è /etc/init.d/rcS
, uno script che esegue tutti i programmi contenuti nella directory /etc/rcS.d/
.
Tra questi, si trovano successivamente i programmi incaricati di:
configurare la tastiera della console;
caricare i driver: la maggior parte dei moduli del kernel vengono caricati dal kernel stesso, al rilevamento dell'hardware, altri driver aggiuntivi vengono caricati in seguito automaticamente se i moduli corrispondenti sono elencati nel file /etc/modules
;
verificare l'integrità dei file system;
montare partizioni locali;
configurare la rete;
montare file system di rete (NFS).
In seguito a questa fase, subentra init
e avvia quei programmi attivati nel runlevel predefinito (che di solito è il runlevel 2). Viene eseguito /etc/init.d/rc 2
, uno script che lancia tutti i servizi che sono elencati in /etc/rc2.d/
ed i cui nomi iniziano con la lettera "S". Il numero a due cifre che segue era storicamente utilizzato per definire l'ordine in cui i servizi dovevano essere avviati, ma al giorno d'oggi il sistema di avvio predefinito utilizza insserv
, che pianifica tutto automaticamente in base alle dipendenze degli script. Ogni script di avvio dichiara in tal modo le condizioni che devono essere soddisfatte per avviare o arrestare il servizio (per esempio, se si deve avviare prima o dopo un altro servizio); init
poi li esegue nell'ordine che soddisfa queste condizioni. La numerazione statica degli script quindi non è più presa in considerazione (ma devono sempre avere un nome che inizia con una "S" seguita da due cifre ed il nome effettivo dello script usato per le dipendenze). In generale, i servizi di base (come la registrazione con rsyslog
, o l'assegnazione di porte con portmap
) vengono avviati per primi, seguiti dai servizi standard e dall'interfaccia grafica (gdm3
).
Questo sistema di avvio basato su dipendenze consente di automatizzare la rinumerazione, che potrebbe risultare piuttosto noiosa se dovesse essere effettuata manualmente, e limita i rischi di errore umano, poiché la pianificazione viene effettuata secondo i parametri indicati. Un altro vantaggio è che i servizi possono essere avviati in parallelo quando sono indipendenti l'uno dall'altro, e quindi è possibile accelerare il processo di avvio.
init
distingue tra diversi runlevel, in modo da poter passare da uno all'altro con il comando telinit nuovo-livello
. Immediatamente, init
esegue ancora una volta /etc/init.d/rc
con il nuovo runlevel. Questo script quindi avvia i servizi mancanti e ferma quelli che non sono più desiderati. Per fare ciò, fa riferimento al contenuto di /etc/rcX.d
(dove X rappresenta il nuovo runlevel). Gli script che iniziano con "S" (come in "Start") sono i servizi da avviare, quelli che iniziano con "K" (come in "Kill") sono i servizi che devono essere arrestati. Lo script non avvia alcun servizio che era già attivo nel runlevel precedente.
Per impostazione predefinita, System V init in Debian utilizza quattro diversi runlevel:
Il livello 0 è utilizzato solo temporaneamente, mentre il computer si sta spegnendo. Come tale, esso contiene solo molti script "K".
Il livello 1, noto anche come modalità utente singolo, corrisponde al sistema in modalità degradata; include solo i servizi basilari, ed è destinato ad operazioni di manutenzione in cui le interazioni con gli utenti ordinari non sono desiderate.
Il livello 2 è il livello per il normale funzionamento, che include servizi di rete, un'interfaccia utente grafica, accesso utenti, ecc.
Il livello 6 è simile al livello 0, tranne che è utilizzato durante la fase di arresto che precede un riavvio.
Esistono altri livelli, in particolare da 3 a 5. In modo predefinito sono configurati per operare allo stesso modo del livello 2, ma l'amministratore può modificarli (aggiungendo o eliminando script nella corrispondente directory /etc/rcX.d
) per adattarli a particolari esigenze.
Tutti gli script contenuti nelle varie directory /etc/rcX.d
sono in realtà solo collegamenti simbolici, creati dall'installazione del pacchetto per il programma update-rc.d
, che puntano agli script reali che vengono memorizzati in /etc/init.d/
. L'amministratore può regolare i servizi disponibili in ogni runlevel attraverso la riesecuzione del comando update-rc.d
con i parametri corretti. La pagina di manuale di update-rc.d(1) descrive la sintassi in dettaglio. Notare che la rimozione di tutti i collegamenti simbolici (con il parametro remove
) non è un buon metodo per disabilitare un servizio. Si dovrebbe invece semplicemente configurare quel servizio per non essere lanciato in quel particolare runlevel (pur conservando le chiamate corrispondenti a fermarlo nel caso in cui il servizio viene eseguito nel runlevel precedente). Dal momento che update-rc.d
ha un'interfaccia un po' complicata, può essere preferibile usare rcconf
(presente nel pacchetto rcconf) che fornisce un'interfaccia più intuitiva.
Infine, init
avvia i programmi di controllo per le varie console virtuali (getty
). Visualizza un prompt, in attesa di un nome utente, poi esegue login utente
per iniziare una sessione.