Administration Unix - CM séance 16
16. Temporisation et démarrage du système
Les systèmes Unix disposent de mécanismes qui leur permet de lancer des commandes automatiquement à des dates choisies.
16.1. Exécution différée de tâches avec at
Les commandes de la famille at
permettent à un utilisateur de demander un
lancement différé d'une série de commandes, appelée un job.
La commande at
L'usage général est :
at [options] timespec
où timespec
indique l'heure et la date, qui peuvent être relative.
Dans cette forme, at
est interactive, et demande de rentrer les commandes du job,
la saisie se terminant par Ctrl+D :
$ date
lundi 15 février 2021, 13:30:50 (UTC+0100)
$ at 13:36 feb 15
warning: commands will be executed using /bin/sh
at> touch Riri # commandes saisies par l'utilisateur
at> mv -f Riri Fifi
at> <EOT> # l'utilisateur a tapé Ctrl-D
job 35 at Mon Feb 15 13:36:00 2021
On peut aussi spécifier la date relative par rapport à now
ou today
:
at now + 5 minutes
at 13:00 today
at 13:30 tomorrow
at 14:00 + 1 days
at now + 2 days
(voir /usr/share/doc/at/timespec
)
La commande atq
La commande atq
affiche la queue (liste numéro a
) des jobs en attente :
$ atq
36 Mon Feb 15 13:36:00 2021 a thiel
35 Mon Feb 15 13:36:00 2021 a thiel
37 Tue Feb 16 14:00:00 2021 a thiel
Les jobs disparaissent dès qu'ils sont terminés.
On peut supprimer un job :
$ atrm 37
$ atq
36 Mon Feb 15 13:36:00 2021 a thiel
35 Mon Feb 15 13:36:00 2021 a thiel
Regroupement
On peut appeler at
avec un here-document, ce qui est utile dans les scripts :
$ at now + 4 hours << FIN
touch Pluto
FIN
ou encore
$ at now + 4 hours <<< "touch Pluto"
Il est également possible de passer un fichier de commandes à at
:
$ cat todo.txt
touch Mickey
$ at -f todo.txt now + 10 minutes
warning: commands will be executed using /bin/sh
job 39 at Mon Feb 15 13:51:00 2021
Comme l'indique le warning, les commandes seront interprétées pas sh
; on ne peut
donc pas utiliser des construction bash telles que $()
, (())
, [[]]
, ...
Par contre on peut appeler dans ces commandes tout type de script.
Interrogation
On peut demander à at
les commandes qu'il va lancer pour un job :
$ at -c 39
#!/bin/sh
# atrun uid=1000 gid=1000
# mail thiel 0
...
umask 2
PATH=/home/thiel/bin:/usr/bin:/usr/local/bin; export PATH
OLDPWD=/home/thiel; export OLDPWD
cd /home/thiel/Bureau || {
echo 'Execution directory inaccessible' >&2
exit 1
}
touch Mickey
La commande at
possède le setuid
; elle choisira l'uid
et le gid
effectifs
d'après le commentaire # atrun uid=... gid=...
À la suite, at
enregistre toutes les variables d'environnement, puis une
commande cd
pour aller dans le répertoire courant au moment du at
, et enfin la
ou les commandes données par l'utilisateur.
Au niveau système, l'administrateur peut autoriser ou interdire aux utilisateurs
l'emploi de at
via les fichiers de configuration /etc/at.allow
et /etc/at.deny
(man at.allow
).
Envoi d'un mail
À la fin du job, les affichages produits sont envoyés automatiquement par mail
à l'utilisateur (voir options -m
ou -M
de at
).
Pour que cela fonctionne, il faut qu'un service MTA (Mail Transport Agent) soit
configuré sur la machine, sinon les affichages sont perdus.
S'il n'y a pas de MTA (situation par défaut), on peut rediriger explicitement les affichages dans un fichier :
$ at now + 2 minutes
warning: commands will be executed using /bin/sh
at> {
at> echo "L'espace disponible est"
at> df
at> } >> trace.txt
at> <EOT>
job 43 at Mon Feb 15 18:02:00 2021
Pour installer un MTA dans une VM xubuntu, il suffit de faire
$ sudo apt install mailutils
configuration :
- choisir envoi local uniquement, tout le reste par défaut
- pour reconfigurer :
sudo dpkg-reconfigure postfix
- pour supprimer :
sudo apt purge mailutils postfix ; sudo apt autoremove
Test :
$ at now + 2 minutes
warning: commands will be executed using /bin/sh
at> echo "Gloubi boulga"
at> <EOT>
job 2 at Mon Feb 15 18:50:00 2021
$ mail
"/var/mail/thiel": 1 message 1 nouveau
>N 1 Edouard Thiel lun. févr. 15 1 12/371 Output from your job 2
? 1
Subject: Output from your job 2
To: thiel@xubu1
From: Edouard Thiel <thiel@xubu1>
Gloubi boulga
? q
1 message sauvegardé dans /home/thiel/mbox
0 message conservé dans /var/mail/thiel
$ mail
Pas de courrier pour thiel
$ mail -f ~/mbox
"/home/thiel/mbox": 1 message 1 nouveau
>N 1 Edouard Thiel lun. févr. 15 1 12/371 Output from your job 2
? q
Compléments
Dans le man de at
, il est aussi question de
- choix parmi plusieurs queues (
a
,b
, ...) ; la queuea
par défaut est prioritaire sur les autres ; - une commande
batch
qui attend que le système ne soit pas surchargé avant de lancer un job.
16.2. Exécution périodique avec cron
Les commandes de la famille cron
permettent de programmer des tâches qui seront
exécutées périodiquement. Par exemple :
- sauvegardes
- test si un service est actif
- nettoyage
En réalité, cron
est le nom du démon qui lance les tâches, et la commande
pour les gérer est crontab
; les tâches programmées sont stockées dans
/var/spool/cron/crontabs/<user>
.
L'usage de crontab
est :
crontab -l
affiche la liste des tâches programmées ;crontab -e
édite la liste (avec le choix d'un éditeur).
Le fichier édité par crontab
est sous la forme :
# Edit this file to introduce tasks to be run by cron.
# ...
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
<tâche>
...
chaque ligne représente une tâche périodique sous la forme (man 5 crontab
) :
m h dom mon dow command
avec :
m minutes 0..59
h heures 0-23
dom day of the month 1..31
mon month 1..12
dow day of week 0..7 (0 = 7 = dimanche)
command une commande avec de préférence un chemin absolu
Les informations de date (m
, h
, dom
, mon
, dow
) peuvent être données sous la
forme de :
a,b liste de valeurs
a-b intervalle, équivalent à a,a+1,a+2,...,b
* n'importe quelle occurrence
*/n une occurrence sur n
Par exemple :
# m h dom mon dow command
15 8,12,16 * * 1-5 /home/thiel/monscript.sh
exécutera monscript.sh
du lundi au vendredi à 8h15, 12h15 et 16h15.
Pour annuler une tâche il suffit de rappeler crontab -e
et de commenter ou
supprimer la ligne correspondante.
L'administrateur peut autoriser ou interdire aux utilisateurs l'emploi de
crontab
via les fichiers de configuration /etc/cron.allow
et /etc/cron.deny
(man crontab
).
Au niveau du système, il y a de nombreuses tâches programmées pour cron
, qui
n'apparaissent pas dans des crontab
; elles sont stockées dans :
/etc/crontab fichier crontab avec en plus un champ user
/etc/cron.d/ fichiers au même format
/etc/cron.hourly/ contient des scripts à exécuter ; ils sont lancés
/etc/cron.daily/ par anacron, lui-même appelé dans /etc/crontab
/etc/cron.weekly/
/etc/cron.monthly/
Ainsi, lorsqu'on installe un paquet, son script postinst
pourra éventuellement
rajouter des scripts dans /etc/cron.{d,hourly,daily,weekly,monthly}
.
16.3. Démarrage traditionnel du système
Lors du démarrage, le chargeur d'amorçage exécute le noyau, qui à son tour
créé le premier processus du système : /sbin/init
, de PID 1.
Si le noyau n'y arrive pas, il fait un kernel panic et s'arrête.
init
est un processus démon, qui continue à tourner jusqu'à l'arrêt du système.
C'est l'ancêtre de tous les processus, et l'un de ses rôle est d'adopter les
orphelins (puis de supprimer les zombies de la table de processus).
Dès qu'il est lancé, init
va lancer une série de scripts pour démarrer les
différents services (réseau, impression, serveur web, ...), et éventuellement
un système de fenêtrage graphique (serveur X11, Wayland, ...).
Il existe différentes familles de démons init
et scripts de démarrage :
- BSD init, inspiré des Unix BSD, depuis 1986
- SysV init, inspiré de Unix System V (lire 5) de AT&T, depuis 1988
- launchd sur MacOS, depuis 2005
- upstart, sur Ubuntu, 2006 à 2014 (<= 14.10)
- systemd, depuis 2010 (sur ubuntu >= 15.04)
- ...
Ces familles sont apparues car il y a eu de plus en plus de services à démarrer, et on voulait à la fois en faciliter la gestion, et accélérer le démarrage.
BSD init
Inspiré du init
historique, capable en plus de démarrer le mode graphique.
init
exécute les scripts d'initialisation /etc/rc
puis /etc/rc.local
.
Ces scripts sont souvent longs, "monolithiques", et doivent être édités à la main.
Par la suite ont été rajoutés des scripts dans /etc/rc.d/
, mais leur ordre
d'appel devait être "codé en dur".
SysV init
Ce système introduit un état, appelé le runlevel (un chiffre entre 0
et 9
ou une lettre, par exemple S
ou K
).
Sa signification varie selon le système ; par exemple pour Linux Standard Base :
0 halt éteint le système
1 mode single-user pour administration
2 multi-user sans réseau
3 multi-user avec réseau mode console
4 réservé
5 3 + display manager mode graphique, par défaut
6 reboot
Voir aussi : https://en.wikipedia.org/wiki/Runlevel
Le fichier de configuration des runlevels est /etc/inittab
;
le runlevel par défaut y est indiqué dans la ligne
id:<runlevel>:initdefault:
Voir :
- https://manpages.debian.org/jessie/manpages-fr-extra/inittab.5.fr.html
- https://wiki.archlinux.org/index.php/SysVinit
L'administrateur peut changer immédiatement le runlevel avec la commande :
$ telinit <runlevel>
Pour connaître le runlevel précédent et courant :
$ runlevel
N 5 (N : inconnu)
Historiquement, les scripts de démarrages étaient répartis selon le runlevel
dans les fichiers /etc/rc.<runlevel>
.
Par la suite est apparue une organisation plus fine des scripts :
- les scripts sont placés dans
/etc/init.d/
; ils peuvent recevoir en argument :start
,stop
,restart
,reload
,status
,probe
, ... ; - pour chaque runlevel il y a un répertoire
/etc/rc<runlevel>.d
:/etc/rc1.d/
,/etc/rc2.d/
, ... - pour activer un script de
/etc/init.d/
lors d'un runlevel, il suffit de placer dans/etc/rc<runlevel>.d/
un lien vers le script. -
ces liens ont le format de nom suivant :
<K ou S><numéro sur 2 chiffres><nom du script>
Les numéros servent à fixer un ordre d'exécution (ordre lexicographique).
Les liens commençant par S
(tart) sont pour les services activés (enabled),
ceux commençant par K
(ill) sont pour les services désactivés (disabled).
Lors qu'on passe d'un runlevel <a>
à un runlevel <b>
, init
appelle
- tous les scripts de
/etc/rc<a>.d/S*
avec l'argumentstop
, puis - tous les scripts de
/etc/rc<b>.d/S*
avec l'argumentstart
.
Il est également possible de faire exécuter par init
un script de init.d/
avec la commande :
$ service <nom script> <ordre> [options]
où <ordre>
est start
, stop
, etc.
Extension LSB
Le projet Linux Standard Base a travaillé sur une méthode pour gérer les
dépendances entre les scripts de démarrage dans /etc/init.d
;
cela consiste à rajouter un entête dans chaque script :
### BEGIN INIT INFO
# Provides: fonctionnalité apportée par ce script
# Required-Start: éléments devant être lancés avant le démarrage
# Required-Stop: éléments devant être arrêtés avant l'arrêt
# Default-Start: runlevels de démarrage par défaut
# Default-Stop: runlevels d'arrêt par défaut
# Short-Description: description courte
# Description: description
### END INIT INFO
La commande update-rc.d
gère ensuite automatiquement les liens pour les
différents runlevels, en les renumérotant selon les dépendances
(man update-rc.d
).
Upstart
Spécifique à Ubuntu, et abandonné depuis 2015 au profit de systemd
;
compatible avec SysV init et l'extension LSB.
- Remarque :
-
le démon
init
(de PID 1) crée pour chaque user connecté un fils parinit --user
.
Ce fils adoptera tous les orphelins du user.
Avantage : répartit les orphelins sur plusieurs processus ;
Inconvénient : un processus ne peut plus savoir s'il est orphelin en testant si PPID est 1.
16.4. Démarrage moderne du système avec systemd
systemd
a été créé en 2010 par Lennart Poettering (employé par Redhat, auteur
également de pulseaudio
et avahi
).
Il permet un démarrage très rapide des services en parallèle, avec un système de dépendances, et plein d'autres choses.
De nombreux Unix sont passés à systemd
. Pour savoir si votre système l'utilise :
$ ls -gG /sbin/init
lrwxrwxrwx 1 20 sept. 4 11:31 /sbin/init -> /lib/systemd/systemd*
systemd
est compatible avec SysV init et l'extension LSB :
init
de PID 1,init --user
pour chaque user,runlevel
ettelinit
,- scripts dans
/etc/inid.d
et/etc/rc<runlevel>.d/
- pour exécuter les scripts : commande
service
- pour gérer/renuméroter les liens
/etc/rc*.d/
: commandeupdate-rc.d
systemd
remplace non seulement le init SysV, mais offre tout un écosystème
de commandes : man -k systemd
systemd systemd-detect-virt systemd-resolve
systemd-analyze systemd-escape systemd-run
systemd-ask-password systemd-hwdb systemd-stdio-bridge
systemd-cat systemd-inhibit systemd-machine-id-setup
systemd-cgls systemd-tmpfiles systemd-tty-ask-password-agent
systemctl ...
On peut par exemple rejouer le démarrage :
$ systemd-analyze
afficher les durées triées :
$ systemd-analyze blame
ou afficher les durées dans le navigateur :
$ systemd-analyze plot > durees.svg
$ firefox durees.svg &
Principe de fonctionnement
la gestion du système, pour systemd
, ne se réduit pas à démarrer ou arrêter des
démons ou changer d'état ; elle comprend aussi la gestion des périphériques,
et de différents éléments comme la date, les locales, et un journal des logs.
La philosophie est basée sur des unités, qui ont un nom et un type. Par exemple :
target
ce sont des cibles, l'équivalent des runlevels (dépréciés)service
ce sont les démonssocket
points de communication entre programmes (sockets)timer
actions temporelles (peuvent remplacer lescron
)
Les unités sont configurés dans des fichiers, sous la forme name.type
, dans
les répertoires :
/lib/systemd/system/ tous les fichiers de configuration
/etc/systemd/system/ liens vers les fichiers pour dire leur état
dans le target courant
L'état des unités peut être :
enabled actif dans le target courant : il y a un lien
/etc/systemd/system/unit.type -> /lib/systemd/system/unit.type
disabled inactif dans le target courant : pas de lien
/etc/systemd/system/unit.type
masked impossible à démarrer dans le target courant : lien
/etc/systemd/system/unit.type -> /dev/null
static impossible à démarrer par lui-même, c'est une dépendance
(on ne peut peut pas le faire passer à enable ou disable).
Exemple :
$ ls -l /lib/systemd/system/ssh.service # serveur ssh installé ?
Aucun fichier ou dossier de ce type
$ sudo apt install openssh-server # installation + enable
...
Created symlink /etc/systemd/system/sshd.service
-> /lib/systemd/system/ssh.service
Created symlink /etc/systemd/system/multi-user.target.wants/ssh.service
-> /lib/systemd/system/ssh.service
...
$ sudo systemctl disable ssh.service
Synchronizing state of ssh.service with SysV service script with
/lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install disable ssh
Removed /etc/systemd/system/sshd.service.
Removed /etc/systemd/system/multi-user.target.wants/ssh.service.
$ sudo systemctl mask ssh.service
Created symlink /etc/systemd/system/ssh.service -> /dev/null.
$ sudo systemctl enable ssh.service # essai enable
Synchronizing state of ssh.service with SysV service script with
/lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable ssh
Failed to enable unit: Unit file /etc/systemd/system/ssh.service is masked.
$ sudo systemctl unmask ssh.service # enlève le mask
Removed /etc/systemd/system/ssh.service.
$ sudo systemctl enable ssh.service # nouvel essai enable
Synchronizing state of ssh.service with SysV service script with
/lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable ssh
Created symlink /etc/systemd/system/sshd.service
-> /lib/systemd/system/ssh.service.
Created symlink /etc/systemd/system/multi-user.target.wants/ssh.service
-> /lib/systemd/system/ssh.service.
Fichiers de description
-
Fichier de description d'un target :
$ more /usr/lib/systemd/system/multi-user.target # This file is part of systemd. (+ licence) [Unit] Description=Multi-User System Documentation=man:systemd.special(7) Requires=basic.target Conflicts=rescue.service rescue.target After=basic.target rescue.service rescue.target AllowIsolate=yes $ more /usr/lib/systemd/system/graphical.target # This file is part of systemd. (+ licence) [Unit] Description=Graphical Interface Documentation=man:systemd.special(7) Requires=multi-user.target # dépend du target précédent Wants=display-manager.service Conflicts=rescue.service rescue.target After=multi-user.target rescue.service rescue.target display-manager.service AllowIsolate=yes
-
Fichier de description d'un service :
$ more /usr/lib/systemd/system/ssh.service [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
Manipulations
-
Manipulation des états (équivalent runlevels) :
systemctl list-unit-files --type=target affiche l'état des targets systemctl get-default affiche le target du boot (graphical.target) systemctl set-default level[.target] change le target pour le boot (équivalent inittab:initdefault) systemctl isolate level[.target] change immédiatement le target (équivalent telinit) systemctl halt|poweroff systemctl reboot systemctl suspend|hibernate
-
Manipulation des services :
systemctl list-unit-files --type=service affiche l'état des services systemctl enable|disable service change l'état du service au boot systemctl start|stop|status service change l'état actuel du service systemctl mask|unmask service empêche démarrage, même en manuel systemctl is-enabled service activé dans le target courant ? systemctl is-active service actif en ce moment ? systemctl is-failed service a démarré sur une erreur ?
Exemple :
```
$ systemctl list-unit-files --type=service
UNIT FILE STATE VENDOR PRESET
...
bluetooth.service enabled enabled
...
$ sudo systemctl stop bluetooth
$ sudo systemctl status bluetooth
bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled;
vendor preset: enabled)
Active: inactive (dead)
Docs: man:bluetoothd(8)
$ systemctl is-enabled bluetooth # au boot
enabled
$ systemctl is-active bluetooth # état actuel
inactive
```
Compléments
-
Interrogation des services :
systemctl cat service affiche le fichier du service systemctl show service affiche les propriétés du service systemctl list-dependencies service liste les dépendances du service ...
-
Confinement des processus :
Les processus sont étroitement confinés par l'utilisation massive de cgroups (control groups). Par exemple, tous les processus qui dépendront d'un démon
ssh
seront dans le même cgroup. On pourra lui allouer un quota d'espace mémoire ou le limiter en nombre de cœurs (de CPU) utilisés.Voir la hiérarchie des cgroups, ou les cgroups racines triés par ressource :
systemd-cgls systemd-cgtop
Voir :
-
Autres commandes relative à systemd :
localectl lister et manipuler les locales, le layout clavier, etc hostnamectl afficher ou manipuler le hostname timedatectl afficher ou manipuler la date et l'heure journalctl afficher ou manipuler le journal maintenu par systemd
-
Documentation :