Dans la séquence précédente, nous avons configuré le réseau à notre convenance. Toutefois, un problème persiste avec le Raspberry Pi…
Mon Raspberry Pi a démarré depuis quelques minutes, comme l'indique la
commande «uptime
» :
root@mybox:/tmp# uptime 12:39:57 up 0:05, 1 user, load average: 0.00, 0.06, 0.03
Je consulte l'heure du système avec la commande
«date
» :
root@mybox:/tmp# date Fri Mar 9 12:40:17 UTC 2018 root@mybox:/tmp#
L'heure est totalement fausse ! Tout est erroné, même l'année…
Le Raspberry Pi n'a pas de composant RTC (Real Time Clock) pour maintenir l'heure lorsqu'il est éteint. Aussi au démarrage le système est-il à l'heure zéro d'Unix (aussi appelée l'«epoch») : le premier janvier 1970 à 00:00:00 UTC.
Outre le fait qu'un système évoluant en janvier 1970 paraît un peu
ridicule à l'utilisateur, des problèmes peuvent se poser avec certains
protocoles réseaux par exemple qui ne veulent pas accepter de
certificats générés dans le lointain futur. Aussi, Yocto s'arrange-t-il
pour que l'heure système soit initialisée avec une valeur
réaliste : le vendredi 9 mars 2018 à 12:34:56 UTC. Si je comprends
assez bien le choix de l'heure, en revanche je ne vois pas ce qui conduit
à utiliser cette date. Elle est stockée «en dur» dans
«poky/meta/conf/bitbake.conf
».
Yocto l'inscrit au moment de la génération de l'image dans
le fichier /etc/timestamp
sous forme «année, mois,
jour, heure, minute, seconde» juxtaposés :
root@mybox:~# cat /etc/timestamp 20180309123456
L'inconvénient est qu'à chaque redémarrage le système reprend à la même
heure, et qu'au fil du temps on s'éloigne de plus en plus de l'heure
réelle. On préfèrerait une mise à l'heure automatique à partir du
réseau. Pour cela il existe des serveurs NTP (Network Time
Protocol) disponibles librement, il nous faut simplement un
programme client ntpd
pour les interroger.
root@mybox:~# ntpd -sh: ntpd: command not found
Sur les systèmes embarqué, le client ntpd
fait partie du
package busybox
, mais il n'a pas été intégré dans
sa configuration. À nous de l'ajouter…
Busybox est un projet assez incontournable dans les systèmes restreints. Il est d'ailleurs surnommés «le couteau suisse de l'embarqué»…
Il s'agit d'un unique exécutable qui adapte son comportement en
fonction du nom sous lequel il a été appelé. Cet exécutable implémente
plus de 400 commandes Linux courantes (on choisit la liste des
commandes à la configuration avant compilation), et des liens
symboliques permettent de l'invoquer avec des noms différents.
Lorsqu'on appelle busybox
sans aucun argument il nous
affiche la liste des commandes qu'il implémente :
root@mybox:~# busybox BusyBox v1.36.1 () multi-call binary. BusyBox is copyrighted by many authors between 1998-2015. Licensed under GPLv2. See source distribution for detailed copyright notices. Usage: busybox [function [arguments]...] or: busybox --list or: function [arguments]... BusyBox is a multi-call binary that combines many common Unix utilities into a single executable. Most people will create a link to busybox for each function they wish to use and BusyBox will act like whatever it was invoked as. Currently defined functions: [, [[, addgroup, adduser, ascii, ash, awk, base32, basename, blkid, bunzip2, bzcat, bzip2, cat, chattr, chgrp, chmod, chown, chroot, chvt, clear, cmp, cp, cpio, crc32, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, df, diff, dirname, dmesg, dnsdomainname, du, dumpkmap, dumpleases, echo, egrep, env, expr, false, fbset, fdisk, fgrep, find, flock, free, fsck, fstrim, fuser, getopt, getty, grep, groups, gunzip, gzip, head, hexdump, hostname, hwclock, id, ifconfig, ifdown, ifup, insmod, ip, kill, killall, klogd, less, ln, loadfont, loadkmap, logger, logname, logread, losetup, ls, lsmod, lzcat, md5sum, mesg, microcom, mkdir, mkfifo, mknod, mkswap, mktemp, modprobe, more, mount, mountpoint, mv, nc, netstat, nohup, nproc, nslookup, od, openvt, patch, pgrep, pidof, pivot_root, printf, ps, pwd, rdate, readlink, realpath, reboot, renice, reset, resize, rev, rfkill, rm, rmdir, rmmod, route, run-parts, sed, seq, setconsole, setsid, sh, sha1sum, sha256sum, shuf, sleep, sort, start-stop-daemon, stat, strings, stty, sulogin, swapoff, swapon, switch_root, sync, sysctl, syslogd, tail, tar, tee, telnet, test, tftp, time, top, touch, tr, true, ts, tty, udhcpc, udhcpd, umount, uname, uniq, unlink, unzip, uptime, users, usleep, vi, watch, wc, wget, which, who, whoami, xargs, xzcat, yes, zcat
Notre système est basé sur l'image
«core-image-base
» de Poky qui est assez
complète, avec de nombreux packages, et le rôle de Busybox est
relativement réduit. Si toutefois nous retournons sur l'image
«core-image-minimal
» que nous avons compilée
dans la
séquence I.3, nous voyons que Busybox est invoqué — par
l'intermédiaire de liens symboliques — pour presque chaque appel
de commande :
root@raspberrypi4:~# ls -l /bin/ lrwxrwxrwx 1 root root 19 Mar 9 12:34 ash -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 base32 -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 14 Mar 9 12:34 busybox -> busybox.nosuid -rwxr-xr-x 1 root root 489208 Mar 9 12:34 busybox.nosuid -rwsr-xr-x 1 root root 42440 Mar 9 12:34 busybox.suid lrwxrwxrwx 1 root root 19 Mar 9 12:34 cat -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 chattr -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 chgrp -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 chmod -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 chown -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 cp -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 cpio -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 date -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 dd -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 df -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 dmesg -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 dnsdomainname -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 dumpkmap -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 echo -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 egrep -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 false -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 fgrep -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 getopt -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 grep -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 gunzip -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 gzip -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 hostname -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 kill -> /bin/busybox.nosuid -rwxr-xr-x 1 root root 95740 Mar 9 12:34 kmod lrwxrwxrwx 1 root root 19 Mar 9 12:34 ln -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 17 Mar 9 12:34 login -> /bin/login.shadow -rwxr-xr-x 1 root root 47256 Mar 9 12:34 login.shadow lrwxrwxrwx 1 root root 19 Mar 9 12:34 ls -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 15 Mar 9 12:34 lsmod -> /bin/lsmod.kmod lrwxrwxrwx 1 root root 4 Mar 9 12:34 lsmod.kmod -> kmod lrwxrwxrwx 1 root root 19 Mar 9 12:34 mkdir -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 mknod -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 mktemp -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 more -> /bin/busybox.nosuid lrwxrwxrwx 1 root root 19 Mar 9 12:34 mount -> /bin/busybox.nosuid [...]
Le suffixe «.nosuid
» signifie simplement que
c'est une commande ne nécessitant pas de privilège particulier pour son
exécution.
Busybox nous propose une implémentation du client ntpd
qui
n'avait pas été sélectionnée dans la configuration par défaut. Pour
accéder à la configuration de Busybox,
nous pouvons exécuter la commande suivante
dans notre répertoire de build :
[build-rpi]$ bitbake -c menuconfig busybox
Cette commande demande à bitbake
d'exécuter l'étape
menuconfig
de la recette correspondant à
busybox
. Il s'agit d'un menu de configuration proposé par
le Makefile
de Busybox. En principe une nouvelle fenêtre
s'ouvre (ou un nouvel onglet de terminal) avec le menu de configuration
de la figure IV.2-1
Dans certaines circonstances (connexion à distance, etc.) l’écran de
configuration peut refuser de s’afficher. Dans ce cas, on peut
contourner le problème en remplissant dans le fichier
conf/local.conf
la variable OE_TERMINAL
avec
la valeur «screen
».
Nous accédons au menu «Networking Utilities»
pour activer l'utilitaire ntpd
comme sur
la figure IV.2-2.
Avant de quitter la configuration nous validons la sauvegarde de la configuration modifiée comme sur la figure IV.2-3.
La configuration de Busybox a été modifiée uniquement dans le
sous-répertoire que Yocto Project utilise pour son build. Mais
nous souhaitons pérenniser cette modification dans notre image. Nous
devons appeler bitbake
pour réaliser une étape
particulière de la recette :
[build-rpi]$ bitbake -c diffconfig busybox [...] Config fragment has been dumped into: /home/Builds/Lab/builds/build-rpi/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/busybox/1.36.1/fragment.cfg
Cette étape a priori un peu obscure demande à
bitbake
de créer un fragment de fichier de
configuration qui regroupe les modifications par rapport à la
configuration précédente (celle par défaut pour nous). Le fragment se trouve dans le
chemin indiqué en fin de commande.
Nous devons intégrer ce fragment dans une extension de recette pour
Busybox dans notre layer. Pour nous aider
l'outil recipetool
va prendre le relais.
Il faut lui fournir pas mal d'arguments :
[build-rpi]$ recipetool appendsrcfile -w ../../layers/meta-my-layer/ busybox tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/busybox/1.36.1/fragment.cfg NOTE: Starting bitbake server... Loading cache: 100% |######################################################################| Time: 0:00:00 Loaded 3259 entries from dependency cache. NOTE: Writing append file /home/Builds/Lab/layers/meta-my-layer/recipes-core/busybox/busybox_%.bbappend NOTE: Copying tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/busybox/1.36.1/fragment.cfg to /home/Builds/Lab/layers/meta-my-layer/recipes-core/busybox/busybox/fragment.cfg [build-rpi]$
On copie sur la ligne de commande de recipetool
le chemin
vers le fragment obtenu précédemment. Puis recipetool
nous
indique qu'il crée un fichier .bbappend
et qu'il copie
le fragment à l'endroit attendu par cette extension de recette.
Il nous reste à recompiler notre image. Nous effaçons la précédente compilation de Busybox d'abord pour éviter un warning de Yocto («do_compile is tainted from a forced run»).
[build-rpi]$ bitbake -c clean busybox [...] [build-rpi]$ bitbake my-image
Après avoir recompilé et redémarré notre image, nous pouvons appeler
ntpd
implémenté par Busybox pour interroger le serveur de
temps pool.ntp.org
.
root@mybox:~# which ntpd /usr/sbin/ntpd root@mybox:~# ls -l /usr/sbin/ntpd lrwxrwxrwx 1 root root 19 Mar 9 12:34 /usr/sbin/ntpd -> /bin/busybox.nosuid root@mybox:~# date Fri Mar 9 12:36:01 UTC 2018 root@mybox:~# ntpd -d -n -q -p pool.ntp.org ntpd: 'pool.ntp.org' is 212.83.158.83 ntpd: sending query to 212.83.158.83 ntpd: reply from 212.83.158.83: offset:+201462484.356525 delay:0.003368 status:0x24 strat:2 refid:0x0463581d rootdela y:0.000687 reach:0x01 ntpd: sending query to 212.83.158.83 ntpd: reply from 212.83.158.83: offset:+201462484.356534 delay:0.003247 status:0x24 strat:2 refid:0x0463581d rootdela y:0.000687 reach:0x03 ntpd: setting time to 2024-07-27 06:24:17.342819 (offset +201462484.356534s) root@mybox:~# date Sat Jul 27 06:24:19 UTC 2024 root@mybox:~#
Le système est maintenant à l'heure. Mais bien évidemment, au redémarrage…
root@mybox:~# reboot [...] The system is going down for reboot NOW! [...] My experimental distro 1.0 mybox ttyS0 mybox login: root Password: (linux) root@mybox:~# date Fri Mar 9 12:35:14 UTC 2018 root@mybox:~#
Il faudrait que la mise à l'heure soit automatiquement réalisée au démarrage du système. C'est ce que nous allons écrire à présent.
Il existe sous Linux plusieurs mécanismes pour lancer une tâche au
démarrage. Les plus connues sont «sysvinit
»
(«System V Init») et
«systemd
». De nos jours la plupart des PC et
des serveurs utilisent «systemd
».
Les systèmes embarqués quant à eux utilisent encore majoritairement
«System V Init».
Yocto Project choisit par défaut «System V Init»
mais nous offre la possibilité d'utiliser
«systemd
» si on le préfère en configurant une
recette de distribution spécifique. Ceci est un peu compliqué pour le
cadre de ce cours en ligne, aussi allons-nous conserver l'environnement
«sysvinit
».
Le processus «init
»
lancé automatiquement par le noyau au boot consulte
le fichier /etc/inittab
pour savoir comment agir.
Ce fichier configure le runlevel du système (son niveau de
fonctionnement) à la valeur 5. Puis lance successivement le script
shell /etc/init.d/rcS
suivi de tous les scripts se
trouvant dans le répertoire /etc/rc5.d/
(le 5
correspondant au runlevel). Dans ce répertoire, on trouve :
root@mybox:~# ls /etc/rc5.d/ S01networking S18btuart S22ofono S02dbus-1 S20apmd S64neard S10dropbear S20bluetooth S99rmnologin.sh S12rpcbind S20syslog S99stop-bootlogd S15mountnfs.sh S21avahi-daemon
Les scripts présents commencent tous par la lettre
«S
» comme Start pour indiquer qu'ils
sont lancés au démarrage du système, suivie d'un numéro d'ordre de
démarrage. En réalité tous ces fichiers sont des liens symboliques vers
des scripts se trouvant de /etc/init.d
:
root@mybox:~# ls -l /etc/rc5.d/ total 0 lrwxrwxrwx 1 root root 20 Mar 9 12:34 S01networking -> ../init.d/networking lrwxrwxrwx 1 root root 16 Mar 9 12:34 S02dbus-1 -> ../init.d/dbus-1 lrwxrwxrwx 1 root root 18 Mar 9 12:34 S10dropbear -> ../init.d/dropbear lrwxrwxrwx 1 root root 17 Mar 9 12:34 S12rpcbind -> ../init.d/rpcbind lrwxrwxrwx 1 root root 21 Mar 9 12:34 S15mountnfs.sh -> ../init.d/mountnfs.sh lrwxrwxrwx 1 root root 16 Mar 9 12:34 S18btuart -> ../init.d/btuart lrwxrwxrwx 1 root root 14 Mar 9 12:34 S20apmd -> ../init.d/apmd lrwxrwxrwx 1 root root 19 Mar 9 12:34 S20bluetooth -> ../init.d/bluetooth lrwxrwxrwx 1 root root 16 Mar 9 12:34 S20syslog -> ../init.d/syslog lrwxrwxrwx 1 root root 22 Mar 9 12:34 S21avahi-daemon -> ../init.d/avahi-daemon lrwxrwxrwx 1 root root 15 Mar 9 12:34 S22ofono -> ../init.d/ofono lrwxrwxrwx 1 root root 15 Mar 9 12:34 S64neard -> ../init.d/neard lrwxrwxrwx 1 root root 22 Mar 9 12:34 S99rmnologin.sh -> ../init.d/rmnologin.sh lrwxrwxrwx 1 root root 23 Mar 9 12:34 S99stop-bootlogd -> ../init.d/stop-bootlogd
Les liens symboliques sont créés au moment de la préparation du système
de fichiers en prenant en compte les runlevels auxquels les
services doivent démarrer et les dépendances entre ces services.
Le scripts sont appelés dans l'ordre et reçoivent
l'argument «start
»
au démarrage du système et «stop
» au moment du
shutdown.
Nous pouvons créer un script qui lance automatiquement
ntpd
avec le nom du serveur NTP en argument.
Préparons un répertoire dans notre layer pour accueillir la
recette et le script :
[build-rpi]$ mkdir -p ../../layers/meta-my-layer/recipes-custom/ntpd-start/files/
Je crée ensuite le petit script suivant :
[build-rpi]$ nano ../../layers/meta-my-layer/recipes-custom/ntpd-start/files/ntpd-start #!/bin/sh if [ "$1" = "start" ] then ntpd -d -n -q -p pool.ntp.org fi exit 0
C'est la classe «update-rc.d
»
fournie par Poky qui crée les liens en fonction des dépendances. Notre recette devra
donc en hériter. Elle s'appuie sur deux variables :
INITSCRIPT_NAME
»
contient le nom du script à lancer.
INITSCRIPT_PARAMS
»
contient une chaîne de caractères qui décrit les liens à créer. Cette chaîne contient
une ou plusieurs séquences, qui commencent par les mots-clés «start
»
ou «stop
» suivant que l'action doit être déclenchée au démarrage ou
à l'arrêt du système, suivis du numéro d'ordre (entre 1 et 99) et du runlevel
concerné. Un point termine chaque séquence. Par exemple on pourrait avoir la chaîne
«start 99 5 . stop 1 0 .
» pour un service qui démarre en dernier (99)
lorsque le système démarre au runlevel 5 et s'arrête en premier (1) lors du shutdown
(0).
Je rédige la recette suivante qui hérite de la classe update-rc.d
,
remplit les deux variables de configuration et installe le script dans le répertoire
«/etc/init.d/
» de la cible. La commande «ntpd
»
étant seulement active quelques instants au démarrage, je n'ai pas besoin de prévoir de
clause «stop
» pour l'arrêter.
[build-rpi]$ nano ../../layers/meta-my-layer/recipes-custom/ntpd-start/ntpd-start_1.0.bb SUMMARY = "Script to start ntpd client service" SECTION = "custom" LICENSE = "MIT" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" SRC_URI = "file://ntpd-start" inherit update-rc.d INITSCRIPT_NAME = "ntpd-start" INITSCRIPT_PARAMS = "start 90 5 ." do_install() { install -d ${D}${sysconfdir}/init.d install -m 755 ${WORKDIR}/ntpd-start ${D}${sysconfdir}/init.d/ }
Après inscription de ntpd-start
dans la recette d'image,
recompilation et installation de l'image, le service est lancé dès le
démarrage et le système est à l'heure :
My experimental distro 1.0 mybox ttyS0 mybox login: root Password: (linux) root@mybox:~# date Sat Jul 27 06:52:47 UTC 2024 root@mybox:~# ls -l /etc/rc5.d/ total 0 [...] lrwxrwxrwx 1 root root 20 Mar 9 2018 S90ntpd-start -> ../init.d/ntpd-start [...]
Cette séquence nous a permis de voir comment modifier la configuration de Busybox et lancer un service au démarrage par l'intermédiaire d'un script de lancement.
Dans la séquence suivante, nous allons continuer l'adaptation de notre image pour une cible personnalisée, en s'intéressant cette fois aux interactions avec le matériel.
Si vous préférez une session de cours interactif, en mode présentiel ou distanciel, n'hésitez pas à vous inscrire à mes formations "Développeur Linux embarqué avec Yocto Project" ou "Yocto Project avancé" .
Ce document est placé sous licence Creative Commons CC BY-NC. Vous pouvez copier son contenu et le réemployer à votre gré pour une utilisation non-commerciale. Vous devez en outre mentionner sa provenance.
Le nom Yocto Project est une marque déposée par la Linux Foundation. Le présent document n'est en aucune façon approuvé par Yocto Project ou la Linux Foundation.