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.
In primo luogo, il BIOS prende il controllo del computer, rileva i dischi, carica il Master Boot Record, ed esegue il bootloader. Il bootloader subentra, trova il kernel sul disco, lo carica e lo esegue. Il kernel è quindi inizializzato, e comincia a cercare e montare la partizione contenente il file system 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 executes several processes, in charge of setting up the system: keyboard, drivers, filesystems, network, services. It does this while keeping a global view of the system as a whole, and the requirements of the components. Each component is described by a “unit file” (sometimes more); the general syntax is derived from the widely-used “*.ini files“ syntax, with key = value
pairs grouped between [section]
headers. Unit files are stored under /lib/systemd/system/
and /etc/systemd/system/
; they come in several flavours, but we will focus on “services” and “targets” here.
A systemd “service file” describes a process managed by systemd. It contains roughly the same information as old-style init-scripts, but expressed in a declaratory (and much more concise) way. Systemd handles the bulk of the repetitive tasks (starting and stopping the process, checking its status, logging, dropping privileges, and so on), and the service file only needs to fill in the specifics of the process. For instance, here is the service file for SSH:
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=sshd.service
Come si può vedere, c'è poco codice lì dentro, solo dichiarazioni. Systemd si occupa di visualizzare i report, tenendo tracci dei processi, ed anche riavviandoli quando necessario.
A systemd “target file” describes a state of the system, where a set of services are known to be operational. It can be thought of as an equivalent of the old-style runlevel. One of the targets is local-fs.target
; when it is reached, the rest of the system can assume that all local filesystems are mounted and accessible. Other targets include network-online.target
and sound.target
. The dependencies of a target can be listed either within the target file (in the Requires=
line), or using a symbolic link to a service file in the /lib/systemd/system/targetname.target.wants/
directory. For instance, /etc/systemd/system/printer.target.wants/
contains a link to /lib/systemd/system/cups.service
; systemd will therefore ensure CUPS is running in order to reach printer.target
.
Since unit files are declarative rather than scripts or programs, they cannot be run directly, and they are only interpreted by systemd; several utilities therefore allow the administrator to interact with systemd and control the state of the system and of each component.
The first such utility is systemctl
. When run without any arguments, it lists all the unit files known to systemd (except those that have been disabled), as well as their status. systemctl status
gives a better view of the services, as well as the related processes. If given the name of a service (as in systemctl status ntp.service
), it returns even more details, as well as the last few log lines related to the service (more on that later).
Starting a service by hand is a simple matter of running systemctl start servicename.service
. As one can guess, stopping the service is done with systemctl stop servicename.service
; other subcommands include reload
and restart
.
To control whether a service is active (i.e. whether it will get started automatically on boot), use systemctl enable servicename.service
(or disable
). is-enabled
allows checking the status of the service.
An interesting feature of systemd is that it includes a logging component named journald
. It comes as a complement to more traditional logging systems such as syslogd
, but it adds interesting features such as a formal link between a service and the messages it generates, and the ability to capture error messages generated by its initialisation sequence. The messages can be displayed later on, with a little help from the journalctl
command. Without any arguments, it simply spews all log messages that occurred since system boot; it will rarely be used in such a manner. Most of the time, it will be used with a service identifier:
#
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)
Another useful command-line flag is -f
, which instructs journalctl
to keep displaying new messages as they are emitted (much in the manner of tail -f file
).
If a service doesn't seem to be working as expected, the first step to solve the problem is to check that the service is actually running with systemctl status
; if it is not, and the messages given by the first command are not enough to diagnose the problem, check the logs gathered by journald about that service. For instance, assume the SSH server doesn't work:
#
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. The System V init system
The System V init system (which we'll call init for brevity) executes several processes, following instructions from the
/etc/inittab
file. The first program that is executed (which corresponds to the
sysinit step) is
/etc/init.d/rcS
, a script that executes all of the programs in the
/etc/rcS.d/
directory.
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.
By default, System V init in Debian uses four different runlevels:
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 solo collegamenti simbolici, creati con l'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 il 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.