«« sommaire »»

III.1 – Ajouter des scripts ou des fichiers de données

Christophe BLAESS - juillet 2023

Le premier type de fichiers que nous allons ajouter au BSP que nous avons produit jusqu’à présent va être assez simple : il s’agira de scripts. L’avantage de ces fichiers est qu’ils ne nécessitent pas de compilation, il suffit de les copier au bon endroit, et avec les bonnes permissions, pour qu’ils soient utilisables sur la cible.

Bien sûr il faut qu’un interpréteur adéquat soit disponible pour les interpréter. Pour les scripts shell cela fait partie de l'environnement de base installé par Yocto, et pour les scripts Python, nous devrons nous assurer de la présence de l'interpréteur et de certains modules supplémentaires.

Système de fichiers en lecture seule

Avant cela, nous allons commencer par sélectionner une feature d’image importante : la mise en lecture seule du système de fichiers. Dans les environnements embarqués, une préoccupation constante est en effet la corruption des données lors d’une coupure impromptue de l’alimentation électrique du système. Lorsqu’elle se produit, cette coupure provoque bien évidemment la perte des données qui étaient en cours d’enregistrement, mais peut engendrer une corruption du système de fichiers lui-même. Il devient alors nécessaire d'exécuter un utilitaire de vérification et de répartion du système de fichiers (par exemple «e2fsck»). Cette action peut être entreprise par des scripts de démarrage qui montent, vérifient, et réparent si besoin le système de fichiers concerné.

Le problème devient critique lorsque c'est le système de fichiers principal (root filesystem, ou rootfs) qui est atteint, car celui-ci est monté directement par le noyau à la fin de la phase de boot, juste avant de lancer le processus «init».

Le noyau n'a pas accès aux outils de vérification et réparation du système de fichiers, puisque justement ils se trouvent sur ce système de fichiers principal.

Ainsi une corruption, même mineure du système de fichiers principal peut conduire à une impossibilité de démarrer, à un système briqué (i.e.: aussi utile qu'une brique pour ce qui concerne le logiciel)

Il faut bien être conscient qu'une partition accessible en lecture-écriture peut toujours être sujette à ce type de corrution en cas de coupure intempestive de l'alimentation électrique. Même si nous ne faisons pas d'écriture intentionnelle sur la partition, il y a des méta-données (dernière heure de consultation d'un fichier par exemple) qui sont inscrites en permanence.

Une manière simple de corriger ce défaut est de demander à Yocto de monter le système de fichiers contenu sur la partition principale en lecture-seulement. Bien sûr on pourra ajouter des partitions supplémentaires en lecture-écriture pour y stocker des données si besoin, en s’arrangeant pour qu’une détection d’incohérence ne soit pas bloquante, nous en reparlerons. L’important est que la partition principale du système soit protégée.

J’édite donc mon fichier d’image pour ajouter la feature adéquate :

[build-qemu]$ nano  ../../layers/meta-my-layer/recipes-custom/images/my-image.bb 
   [...]
   IMAGE_FEATURES += "read-only-rootfs"

Je relance tranquillement le build, et... patatra !

[build-qemu]$ bitbake my-image
  [...]
ERROR: my-image-1.0-r0 do_rootfs: The following packages could not be configured offline and rootfs is read-only: ['100-sysvinit-inittab']
ERROR: Logfile of failure stored in: /home/Builds/Lab/Yocto-lab/builds/build-qemu/tmp/work/qemuarm-poky-linux-gnueabi/my-image/1.0-r0/temp/log.do_rootfs.1085283
ERROR: Task (/home/Builds/Lab/Yocto-lab/layers/meta-my-layer/recipes-custom/images/my-image.bb:do_rootfs) failed with exit code '1'
NOTE: Tasks Summary: Attempted 4668 tasks of which 4667 didn't need to be rerun and 1 failed.

Summary: 1 task failed:
  /home/Builds/Lab/Yocto-lab/layers/meta-my-layer/recipes-custom/images/my-image.bb:do_rootfs
Summary: There was 1 ERROR message, returning a non-zero exit code.

La compilation a échoué et affiché des lignes en rouge. Les messages d'erreur ne sont pas évidents à comprendre, mais il est clair que le problème vient du fait qu'un package donné a besoin de se configurer dynamiquement au démarage, en modifiant son fichier de démarrage.

Plus particulièrement, le problème vient de la détection au boot des consoles séries accessibles et la modification du fichier /etc/inittab pour les lister.

Une manière simple (à appliquer mais pas à comprendre) de régler le problème est d'ajouter les deux lignes suivantes dans le fichier conf/local.conf :

SERIAL_CONSOLES_CHECK:forcevariable = ""
SERIAL_CONSOLES="115200;ttyAMA0"

C'est la première ligne qui est la plus importante, la seconde sert seulement à éviter des messages d'erreurs au boot pour les consoles série non disponibles.

À noter : on peut observer la présence d'un opérateur rare : :forcevariable. À n'utiliser qu'en dernier ressort, il s'agit d'une affectation avec la priorité la plus élevée possible.

Après re-compilation et démarrage, vérifions le comportement :

mybox login: root
Password: (linux)
root@mybox:~# pwd
/home/root
root@mybox:~# echo  HELLO  > my-file.txt 
-sh: my-file.txt: Read-only file system

Notre système de fichiers principal est désormais en lecture seule et il n'y a plus de riques de perte de données sur cette partition en cas de coupure intempestive de l'alimentation.

Pendant la phase de mise au point du système embarqué, il peut être pratique de tester des modifications directement dans le système de fichiers avant de les intégrer dans une recette.

Pour cela, il est possible de remonter temporairement la partition principale en lecture-écriture ainsi :

root@mybox:~# mount  /  -o  remount,rw
 [188.917856] EXT4-fs (vda): re-mounted. Opts: (null)
root@mybox:~# echo  HELLO  > my-file.txt
root@mybox:~# ls
my-file.txt

Puis de revenir en lecture-seule :

root@mybox:~# mount  /  -o  remount,ro
 [260.995398] EXT4-fs (vda): re-mounted. Opts: (null)
root@mybox:~# rm  my-file.txt
rm: cannot remove 'my-file.txt': Read-only file system

Néanmoins ces lignes de commandes sont un peu fastidieuses, aussi je vous propose de réaliser deux petits scripts, que j’appelle traditionnellement «rw» et «ro» pour réaliser ces tâches. Ceci nous donnera l’occasion d’écrire une recette pour les intégrer dans notre image.

Intégration de scripts shells

Commençons par écrire les deux scripts shell dans un répertoire dédié de notre layer. Vous pouvez les télécharger ici : rw et ro.

[build-qemu]$ mkdir  -p  ../../layers/meta-my-layer/recipes-custom/my-scripts/files 

[build-qemu]$ nano  ../../layers/meta-my-layer/recipes-custom/my-scripts/files/rw
#! /bin/sh
mount / -o remount,rw

[build-qemu]$ nano  ../../layers/meta-my-layer/recipes-custom/my-scripts/files/ro 
#! /bin/sh
mount / -o remount,ro

Inutile de les rendre exécutables, nous ne les lancerons pas sur le système de développement, et leurs permissions sur la cible seront indiquées directement dans notre recette. Nous allons créer manuellement le fichier de recette (sans passer par recipetool) afin d'en analyser la structure. Créons le fichier de suivant :

[build-qemu]$ nano  ../meta-my-layer/recipes-custom/my-scripts/my-scripts.bb
SUMMARY = "Custom scripts for Yocto training"
SECTION = "custom"

LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI  = "file://ro"
SRC_URI += "file://rw"

do_install() {
     install -d ${D}${sbindir}
     install -m 0755 ${WORKDIR}/ro ${D}${sbindir}
     install -m 0755 ${WORKDIR}/rw ${D}${sbindir}
}

FILES:${PN} += "${sbindir}/ro"
FILES:${PN} += "${sbindir}/rw"

Quelques explications sur la syntaxe de notre première recette complète :

Nous avons utilisé la variable sbindir, il existe toute une liste de variables prédéfinies avec des chemins standards. Voici ci-dessous les plus courantes, pour en savoir plus, se reporter au début du fichier poky/meta/conf/bitbake.conf.

VariableValeur par défaut
bindir/usr/bin
datadir/usr/share
docdir/usr/share/doc
includedir/usr/include
infodir/usr/share/info
libdir/usr/lib
libexecdir/usr/libexec
localedir/usr/lib/locale
localstatedir/var
sbindir/usr/sbin
servicedir/srv
sysconfdir/etc
systemd_unitdir/lib/systemd
systemd_system_unitdir/lib/systemd/system
systemd_user_unitdir/usr/lib/systemd/user

Avant de recompiler l’image, il convient d’inclure notre package dans le fichier d’image :

[build-qemu]$ nano  ../../layers/meta-my-layer/recipes-custom/images/my-image.bb 
  [...]
  IMAGE_INSTALL:append = " my-scripts"

Après compilation et démarrage de la cible, nous vérifions que nos scripts sont bien accessibles et se comportent comme voulu :

Poky (Yocto Project Reference Distro) 4.0.11 mybox ttyAMA0 

mybox login: root
Password: (linux)
root@mybox:~# ls
root@mybox:~# echo  HELLO  > my-file.txt
-sh: my-file.txt: Read-only file system
root@mybox:~# rw
[   68.291024] EXT4-fs (vda): re-mounted. Opts: (null)
root@mybox:~# echo  HELLO  > my-file.txt
root@mybox:~# ls
my-file.txt
root@mybox:~# ro
[   89.067793] EXT4-fs (vda): re-mounted. Opts: (null)
root@mybox:~# ls
my-file.txt
root@mybox:~# rm -f my-file.txt 
rm: cannot remove 'my-file.txt': Read-only file system
root@mybox:~#

NB: les messages "EXT4-fs (vda): re-mounted. Opts: (null)" ne sont pas des erreurs mais des traces du noyau (nous sommes connectés sur la console principale) lorsqu'une partition est montée ou démontée.

Scripts et modules Python

Nous pouvons facilement ajouter des scripts Python sur notre cible, un interpréteur Python 3 étant déjà présent dans notre image, mais le nombre de modules installés par défaut est assez limité. Il est donc possible d’ajouter la ligne suivante dans notre fichier my-image.bb pour enrichir l’installation.

IMAGE_INSTALL:append = " python3-modules"

Il n'existe pas de package python3-modules à proprement parler, il s'agit d'une liste de modules déclarée dans la recette de python3 fournie par Poky.

root@mybox:~# python3 -V
Python 3.10.4 (main, Mar 23 2022, 20:25:24) [GCC 11.3.0]
root@mybox:~#

Je rajoute donc, dans un répertoire meta-my-layer/recipes-custom/python-hello/files/ le petit script python-hello.py suivant :

#!/usr/bin/python3
#
# Christophe BLAESS 2020-2023.
#
# Licence MIT.
#
import socket
import sys
print("Python {} says 'Hello' from {}".format(sys.version[0:3],socket.gethostname()))

Ainsi que la recette python-hello_1.1.bb ci-dessous, dans le répertoire meta-my-layer/recipes-custom/python-hello.

SUMMARY = "Python Hello World for Yocto tutotial"
SECTION = "custom"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI  = "file://python-hello.py"

RDEPENDS:python-hello += "python3-core"
RDEPENDS:python-hello += "python3-modules"

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${WORKDIR}/python-hello.py ${D}${bindir}

}

FILES:${PN} += "${bindir}/python-hello.py"

On peut noter la présence des deux lignes centrales, qui renseignent la variable RDEPENDS pour cette recette. Cette variable, abréviation de Runtime Dependencies contient la liste des packages nécessaires pour le fonctionnement de la recette sur la cible. Il faut la distinguer de la variable DEPENDS qui liste les packages nécessaires pour compiler une recette sur la machine de développement.

La dépendance de python3-code permet d'avoir l'interpréteur sur la cible.

Le package python3-modules doit être installé pour disposer du module socket qui nous fournit la fonction gethostname().

En ajoutant dans notre image la ligne :

IMAGE_INSTALL:append = " python-hello"

et en recompilant notre système, nous pouvons observer après démarrage :

Poky (Yocto Project Reference Distro) 4.0.11 mybox ttyAMA0

mybox login: root
Password: (linux)
root@mybox:~# python-hello.py 
Python 3.1 says 'Hello' from mybox
root@mybox:~# 

Conclusion

Nous avons vu dans cette séquence comment ajouter dans une image des fichiers fournis par une recette. Dans le cas d’un script, il faut simplement penser à lui ajouter les permissions d’exécution (ce qui se fait facilement avec la commande install) et éventuellement l’interpréteur nécessaire. Dans la séquence suivante, nous écrirons une recette nécessitant une compilation de code source.

Ce document est placé sous licence Creative Common 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.

«« sommaire »»