DOKK / manpages / debian 12 / debconf-doc / debconf-devel.7.fr
DEBCONF-DEVEL(7) Miscellaneous Information Manual DEBCONF-DEVEL(7)

debconf - guide du développeur

C'est un guide pour créer des paquets qui utilisent debconf.

Ce manuel suppose que vous connaissez bien debconf en tant qu'utilisateur et que vous êtes familier avec les bases de la construction des paquets Debian.

Ce manuel commence par expliquer les deux nouveaux fichiers ajoutés aux paquets Debian qui utilisent debconf. Puis il explique comment le protocole debconf fonctionne et vous indique les bibliothèques qui permettent de communiquer avec le protocole. Il traite les autres scripts d'administration où debconf est typiquement utilisé : les scripts postinst et postrm. Ensuite, il passe à des sujets plus pointus comme le partage des questionnaires debconf, le débogage, d'autres techniques courantes et les pièges de la programmation avec debconf. Il se termine par une discussion sur les défauts actuels de debconf.

Debconf ajoute un script d'administration, le script config, au jeu de scripts pouvant être présents dans un paquet Debian (les scripts postinst, preinst, postrm, prerm). Le script config doit poser toutes les questions nécessaires à la configuration du paquet.

Remarque : il est un peu ennuyeux que dpkg considère le script postinst du paquet comme un script de « configuration » du paquet ; en effet, un paquet utilisant debconf est souvent pré-configuré, par son script config, avant que le script postinst soit lancé. Mais, bon !

Lorsque le script config est lancé, deux paramètres lui sont passés ; il en va de même pour le script postinst. Le premier est l'action à effectuer et le second est la version du paquet actuellement installé. Donc, comme pour un script postinst, vous pouvez utiliser dpkg --compare-versions sur $2 pour effectuer une action seulement en cas de mise à niveau d'une version particulière du paquet ou d'autres actions de ce type.

Le script config peut être lancé de l'une de ces trois façons :

1
Si un paquet est pré-configuré, avec dpkg-preconfigure, son script config est lancé avec les paramètres « configure » et « installed-version ».
2
Lorsque le script postinst d'un paquet est lancé, debconf essaiera aussi de lancer le script config, avec les mêmes paramètres que ceux qui sont passés lorsqu'il est pré-configuré. C'est nécessaire car le paquet n'a peut-être pas été pré-configuré, et donc le script config doit alors être lancé. Veuillez consulter la section BIDOUILLES pour plus de précisions.
3
Si un paquet est reconfiguré, avec dpkg-reconfigure, son script config est lancé et les paramètres « reconfigure » et le numéro de la version installée lui sont passés.

Notez que puisqu'une installation ou une mise à niveau typique utilisant apt exécute les étapes 1 et 2, le script config sera lancé deux fois. La seconde fois, il ne devrait rien faire (poser les questions deux fois de suite est ennuyeux) et il devrait être nettement idempotent. Par chance, debconf évite par défaut de répéter les questions, donc c'est relativement facile à effectuer.

Notez que le script config est lancé avant que le paquet ne soit dépaqueté. Il ne devrait utiliser que des commandes disponibles dans les paquets essentiels. La seule dépendance qui est sûre d'être satisfaite lorsque son script config est lancé est une dépendance (éventuellement sur une version spécifique) sur debconf lui-même.

Le script config ne devrait pas avoir besoin de modifier le système de fichiers. Il vérifie seulement l'état du système et pose des questions. Debconf conserve alors les réponses, qui pourront être utilisées par le script postinst. Et inversement, le script postinst ne devrait presque jamais utiliser debconf pour poser des questions, mais devrait à la place utiliser les réponses aux questions posées par le script config.

Un paquet qui utilise debconf voudra probablement poser quelques questions. Ces questions sont conservées sous une forme standard dans le fichier templates.

Comme le script config, le fichier templates est inclus dans la section control.tar.gz d'un paquet. Son format est semblable à celui du fichier control Debian ; un ensemble de paragraphes séparés par des lignes vides, où chaque paragraphe possède une forme du type RFC 822 :


Template: toto/tata
Type: string
Default: toto
Description: Il s'agit d'un exemple de question
Ceci est la description longue
.
Veuillez noter que :
- comme dans une description de paquet Debian, un point seul définit un
nouveau paragraphe ;
- la plupart du texte est reformaté, mais le texte avec une indentation
double est gardé tel quel, ainsi vous pouvez l'utiliser pour des listes,
comme celle-ci. Soyez prudent, comme il n'est pas reformaté, s'il est
trop large, il s'affichera mal. Il vaut mieux l'utiliser pour des
éléments courts (donc ceci est un mauvais exemple).


Template: toto/titi
Type: boolean
Description: C'est clair n'est-ce pas ?
Ceci est une autre question, de type booléenne.

Pour des exemples concrets de fichiers templates, regardez /var/lib/dpkg/info/debconf.templates, ainsi que les autres fichiers .templates de ce répertoire.

Examinons chaque champ successivement :

Le nom du message, dans le champ « Template », est généralement préfixé avec le nom du paquet. Ensuite, l'espace de nommage est très libre ; vous pouvez utiliser une simple disposition plane, ou définir des « sous-répertoires » contenant ces questions.
Le type du message détermine le type d'objet devant être présenté à l'utilisateur. Les types actuellement reconnus sont :
C'est un champ libre où l'utilisateur peut taper n'importe quelle chaîne de caractères.
Demande à l'utilisateur un mot de passe. Utilisez-le avec précaution ; soyez conscient que le mot de passe que l'utilisateur indique sera écrit dans la base de données de debconf. Vous devriez probablement effacer cette valeur de la base de données dès que possible.
Un choix du type « vrai ou faux ».
Un choix entre des valeurs. Les choix doivent être spécifiés dans un champ nommé « Choices ». Les valeurs sont séparées par des virgules et des espaces, comme ceci :

Choices: oui, non, peut-être
Comme le type de données select, excepté que l'utilisateur peut choisir plusieurs éléments de la liste (ou n'en choisir aucun).
À la place d'une question, ce type de données indique une note qui peut être affichée à l'utilisateur. Elle ne devrait être utilisée que pour des notes importantes que l'utilisateur doit absolument lire, car debconf va déployer les grands moyens pour s'assurer que l'utilisateur la lira ; en arrêtant l'installation et en attendant qu'il presse une touche. Il est préférable de n'utiliser ceci que pour signaler des problèmes très sérieux et le type de données « error » est souvent plus approprié.
Ce type de données est utilisé pour les messages d'erreur, comme des erreurs d'entrée invalide. Debconf posera une question de ce genre même si la priorité est trop haute, ou si l'utilisateur l'a déjà vue.
Ce type de données est utilisé pour les titres, afin qu'ils soient positionnés avec la commande SETTITLE.
Ce type de données peut être utilisé pour des portions de texte, comme des étiquettes, qui peuvent être utilisées pour des raisons cosmétiques dans l'affichage de certaines interfaces. D'autres interfaces ne l'utiliseront pas. Il n'y a pas de raison de l'utiliser pour l'instant, puisqu'aucune interface ne le gère correctement. Il risque même d'être supprimé dans le futur.
Le champ « Default » indique à debconf la valeur par défaut. Pour multiselect, elle peut être une liste de choix séparés par des virgules semblable au champ « Choices ». Pour select, elle devrait être l'un des choix possibles. Pour Boolean, c'est « true » ou « false », alors que cela peut être n'importe quoi pour une chaîne de caractères et est ignoré pour les mots de passe.

Ne faites pas l'erreur de penser que le champ default contient la « valeur » de la question, ou qu'il puisse être utilisé pour changer la valeur de la question. Il ne le fait pas, et ne le peut pas, il fournit seulement une valeur par défaut lorsqu'une question est affichée pour la première fois. Pour fournir une valeur par défaut qui change à la volée, vous devriez utiliser la commande SET pour changer la valeur de la question.

Le champ « Description », comme la description d'un paquet Debian, est constitué de deux parties : une description courte et une description longue. Notez que certaines interfaces debconf n'affichent pas la description longue, ou ne l'affichent que si l'utilisateur demande de l'aide. La description courte doit donc suffire à la compréhension du message.

Si vous n'arrivez pas à trouver une description longue, premièrement, réfléchissez-y plus longuement. Postez sur debian-devel. Demandez de l'aide. Prenez votre plus belle plume ! Car la description longue est importante. Si après tout ça vous n'arrivez toujours pas à proposer quelque chose, laissez-la vide. Cela n'apporte rien de recopier la description courte.

Le texte dans la description longue sera reformaté, à moins qu'il ne soit préfixé avec une espace supplémentaire (après l'espace requise). Vous pouvez le découper en plusieurs paragraphes en les séparant par " ." sur une ligne.

Une question est une instance d'un message. En demandant à debconf d'afficher une question, votre script config peut interagir avec l'utilisateur. Quand debconf charge un questionnaire (cela arrive à chaque fois qu'un script postinst ou config est lancé), il crée automatiquement la question à partir du message. Il est possible de créer plusieurs questions indépendantes à partir d'un même message (en utilisant la commande REGISTER), mais c'est rarement nécessaire. Les messages sont des données statiques qui proviennent du fichier templates, alors que les questions sont utilisées pour conserver des données dynamiques, comme la valeur actuelle d'une question, ou si un utilisateur a déjà vu une question, etc. Gardez bien à l'esprit la différence entre un message et une question, mais ne vous inquiétez pas trop.

Il est tout à fait possible que des paquets partagent un message et une question. Tous les paquets doivent fournir une copie identique du message dans leur fichier templates. Cela peut être utile si un groupe de paquets a besoin de poser les mêmes questions et que vous avez envie de ne déranger les utilisateurs qu'une seule fois. Les messages partagés sont généralement placés dans le pseudo-répertoire shared/ dans l'espace de nommage des questionnaires debconf.

Les scripts config communiquent avec debconf en utilisant le protocole debconf. C'est un protocole simple orienté ligne, semblable aux protocoles internet courants comme SMTP. Le script config envoie à debconf une commande en l'écrivant sur la sortie standard. Il peut alors lire la réponse de debconf depuis l'entrée standard.

Les réponses de debconf peuvent être scindées en deux parties : un code de retour numérique (le premier mot de la réponse) et un code de retour étendu facultatif (le reste de la réponse). Le code numérique utilise 0 pour indiquer un succès et d'autres nombres pour indiquer divers types d'erreur. Pour plus de précisions, veuillez consulter le tableau dans les spécifications debconf de la charte Debian.

Le code de retour étendu n'a généralement aucune forme particulière, vous pourrez donc, dans la plupart des cas, l'ignorer et vous ne devriez pas essayer de l'analyser dans un programme pour savoir ce que debconf est en train de faire. Les commandes comme GET sont des exceptions car elles retournent une valeur dans le code de retour étendu.

Vous voudrez généralement utiliser une bibliothèque spécifique à un langage qui gère l'aspect pratique des connexions et des communications avec debconf.

Maintenant, voici les commandes de ce protocole. Ce n'est pas une définition complète, veuillez consulter les spécifications debconf de la charte Debian pour plus d'informations.

Vous n'aurez, en général, pas besoin d'utiliser cette commande. Elle échange avec le protocole debconf le numéro de la version utilisée. La version actuelle du protocole est 2.0 et les versions de la série 2.x assureront la compatibilité ascendante. Vous pouvez spécifier le numéro de version du protocole que vous parlez et debconf retournera la version du protocole qu'il parle dans le code de retour étendu. Si la version que vous spécifiez est trop faible, debconf renverra un code de retour numérique égal à 30.
Vous n'aurez, en général, pas besoin d'utiliser cette commande. Elle échange avec debconf une liste des fonctionnalités reconnues (séparées par des espaces). Des fonctionnalités supportées par vous et debconf seront utilisées et debconf répondra avec toutes les fonctionnalités qu'il accepte.

Si « escape » ne fait pas partie de vos fonctionnalités, debconf va attendre que les commandes que vous lui passez comportent des antislashes et des caractères de retour à la ligne échappés (comme \\ et \n respectivement) et va faire de même pour ses réponses. Ceci peut être utilisé par exemple pour changer des chaînes de caractères multilignes en modèles, ou pour récupérer des descriptions étendues multilignes de manière fiable en utilisant METAGET.

Utilisez la description courte du message comme titre de l'écran debconf pour la question indiquée. Le message devrait être de type titre. Vous n'aurez que rarement besoin de cette commande puisque debconf peut automatiquement générer un titre basé sur le nom de votre paquet.

Définir le titre depuis un message signifie que les titres seront stockés à la même place que les autres questions posées par debconf. Elle permet aussi de traduire ces titres.

Utiliser la chaîne de caractères comme titre de l'écran debconf. L'utilisation de la commande SETTITLE est préférable car elle permet de traduire les titres affichés par debconf.
Demande à debconf de préparer l'affichage d'une question à l'utilisateur. La question n'est pas affichée jusqu'à ce que la commande GO soit lancée ; cela permet de lancer plusieurs commandes INPUT en série, pour construire un jeu de questions qui pourraient toutes être posées sur un seul écran.

Le champ priorité indique à debconf s'il est important que la question soit posée à l'utilisateur ou non. Les priorités sont :

Éléments peu importants dont la valeur par défaut convient dans la majorité des cas ; seuls ceux qui veulent tout contrôler les voient ;
Éléments normaux qui ont une valeur par défaut raisonnable ;
Éléments qui n'ont pas de valeur par défaut raisonnable ;
Éléments qui peuvent probablement casser le système sans l'intervention de l'utilisateur.

Pour décider si la question doit être affichée ou non, debconf se base sur sa priorité, si l'utilisateur l'a déjà vue et l'interface qui va être utilisée. Si la question n'est pas à afficher, debconf retournera un code de retour égal à 30.

Cette commande demande à debconf d'afficher les questions accumulées (depuis les commandes INPUT).

Si la fonctionnalité de sauvegarde est supportée et si l'utilisateur indique qu'il veut revenir à une étape précédente, debconf répondra avec un code de retour égal à 30.

Élimine les questions accumulées (avec les commandes INPUT) sans les afficher.
Certaines interfaces peuvent afficher plusieurs questions à l'utilisateur en même temps. Peut-être qu'à l'avenir une interface pourra regrouper ces questions en blocs sur l'écran. BEGINBLOCK et ENDBLOCK peuvent être placées autour de plusieurs commandes INPUT pour indiquer des blocs de questions (les blocs peuvent même être emboîtés). Étant donné qu'aucune interface n'est encore aussi sophistiquée, ces commandes sont pour l'instant ignorées.
Cette commande dit à debconf que vous avez fini de communiquer avec lui. En général, debconf peut détecter la fin de votre programme, cette commande n'est donc pas nécessaire.
Après avoir utilisé INPUT et GO pour afficher une question, vous pouvez utiliser cette commande pour récupérer la valeur indiquée par l'utilisateur. Cette valeur est renvoyée dans le code de retour étendu.
Cette commande positionne la valeur d'une question et peut être utilisée pour remplacer la valeur par défaut avec une valeur que votre programme calcule à la volée.
Cela remet la question à sa valeur par défaut (comme il est spécifié dans le champ « Default » de son message).
Des questions peuvent avoir des substitutions incluses dans leurs champs « Description » et « Choices » (l'utilisation de substitutions dans les champs « Choices » fait un peu bidouillage, un meilleur mécanisme sera développé). Ces substitutions ressemblent à « ${key} ». Quand les questions sont affichées, les substitutions sont remplacées par leurs valeurs. Cette commande peut être utilisée pour fixer la valeur d'une substitution. Cela peut être utile si vous avez besoin d'afficher à l'utilisateur des messages que vous ne pouvez pas mettre dans le fichier templates.
N'essayez pas d'utiliser SUBST pour changer la valeur par défaut d'une question ; cela ne fonctionnera pas puisqu'il y a la commande SET prévue à cet effet.
Une marque peut être associée à une question. Les marques peuvent avoir une valeur « true » ou « false ». Cette commande renvoie la valeur de la marque.
Cela fixe la valeur de la marque d'une question. La valeur est soit « true » soit « false ».

La marque « seen » est courante. Elle n'est normalement positionnée que si un utilisateur a déjà vu la question. Habituellement, debconf affiche à l'utilisateur seulement les questions dont la marque « seen » est positionnée à « false » (ou si vous reconfigurez un paquet). Quelquefois vous voulez que l'utilisateur revoie une question -- dans ce cas vous pouvez positionner la marque « seen » à false pour forcer debconf à l'afficher à nouveau.

Cela renvoie la valeur d'un champ d'une question associée à un message (le champ Description, par exemple).
Cela crée une nouvelle question qui est liée à un message. Par défaut, chaque message possède une question du même nom qui lui est associée. Toutefois, on peut associer autant de questions que l'on veut à un message et cela permet de créer beaucoup de questions.
Cela retire une question de la base de données.
Appelez cette commande dans votre script postrm lorsque votre paquet est purgé. Il retire toutes les questions concernant votre paquet de la base de données de debconf.
Cette extensions charge le fichier de modèle spécifié dans la base de données de debconf. Le propriétaire assume que le paquet est en cours de configuration avec debconf.

Voici un exemple simple du protocole debconf en action.


INPUT medium debconf/frontend
30 question skipped
FSET debconf/frontend seen false
0 false
INPUT high debconf/frontend
0 question will be asked
GO
[ debconf affiche ici une question à l'utilisateur. ]
0 ok
GET no/such/question
10 no/such/question doesn't exist
GET debconf/frontend
0 Dialog

Comme parler directement à debconf via son protocole représente trop de travail, il existe quelques bibliothèques pour vous épargner cette tâche ingrate.

Pour la programmation shell, il y a la bibliothèque /usr/share/debconf/confmodule que vous pouvez inclure en début d'un script shell ; vous pourrez communiquer avec debconf de façon presque naturelle en utilisant la version en minuscules des commandes du protocole debconf qui sont préfixées de « db_ » (par ex. « db_input » et « db_go »). Pour plus de précisions veuillez consulter confmodule(3).

Les programmeurs Perl peuvent utiliser le module perl Debconf::Client::ConfModule(3pm) et les programmeurs Python peuvent utiliser le module python debconf.

Le reste de ce manuel utilisera la bibliothèque /usr/share/debconf/confmodule dans des scripts shell à titre d'exemple. Voici un exemple de script config utilisant cette bibliothèque, il pose seulement une question :


#!/bin/sh
set -e
. /usr/share/debconf/confmodule
db_set mypackage/reboot-now false
db_input high mypackage/reboot-now || true
db_go || true

Remarquez l'utilisation de « || true » pour éviter que le script ne meurt si debconf décide qu'il ne peut pas afficher une question, ou si l'utilisateur essaie de revenir en arrière. Dans ces situations, debconf renvoie un code de retour non nul et puisque set -e est positionné dans ce script shell, un code de retour non intercepté le fera abandonner.

Et voici le script postinst correspondant, qui utilise la réponse à la question de l'utilisateur pour voir si le système doit être redémarré (un exemple plutôt stupide) :


#!/bin/sh
set -e
. /usr/share/debconf/confmodule
db_get mypackage/reboot-now
if [ "$RET" = true ]; then
shutdown -r now
fi

Remarquez l'utilisation de la variable $RET pour récupérer le code de retour étendu de la commande GET, qui contient la réponse de l'utilisateur à la question.

La dernière section avait un exemple de script postinst qui utilise debconf pour récupérer la valeur d'une question et agir selon elle. Voici quelques remarques à garder à l'esprit lors de l'écriture des scripts postinst qui utilisent debconf :

*
évitez de poser des questions dans le script postinst. Le script config devrait poser ces questions à sa place en utilisant debconf, pour que la pré-configuration fonctionne par la suite ;
*
incluez toujours /usr/share/debconf/confmodule au début de votre script postinst, même si vous ne lancez aucune des commandes db_*. C'est nécessaire pour s'assurer que le script config sera bien lancé (veuillez consulter la section BIDOUILLES pour plus de détails) ;
*
évitez d'afficher quelque chose sur la sortie standard dans votre script postinst, puisque cela peut perturber debconf ; le script postinst ne devrait de toute façon pas être bavard. L'affichage sur la sortie d'erreur est autorisé, si vous le devez ;
*
si votre script postinst lance un démon, soyez sûr de dire à debconf de STOPper à la fin, car debconf peut avoir du mal à détecter la fin du script postinst ;
*
faites accepter à votre script postinst un premier paramètre « reconfigure ». Il peut le traiter comme « configure ». Cela sera utilisé dans une version ultérieure de debconf pour permettre aux scripts postinst de savoir quand ils sont reconfigurés.

En plus des scripts config et postinst, vous pouvez utiliser debconf dans tout autre script d'administration. En général, vous utiliserez debconf dans votre script postrm, pour appeler la commande PURGE quand votre paquet est purgé, pour vider ses entrées dans la base de données de debconf. En fait, cela est automatiquement configuré pour vous par dh_installdebconf(1).

Un emploi plus sophistiqué de debconf serait de l'utiliser dans le script postrm lors de la purge de votre paquet, pour poser une question concernant la suppression de quelque chose. Ou peut-être avez-vous besoin de l'utiliser dans les scripts preinst ou prerm pour quelque raison que ce soit. Toutes ces utilisations fonctionneront, bien qu'elles impliqueront probablement de poser une question et d'agir en fonction de la réponse dans le même programme, plutôt que de séparer les deux actions comme dans les scripts config et postinst.

Notez que si votre paquet n'utilise debconf que dans le script postrm, vous devriez faire en sorte que le script postinst de votre paquet inclue /usr/share/debconf/confmodule, pour que debconf puisse charger votre fichier templates dans sa base de données. Le questionnaire sera alors disponible lorsque votre paquet sera purgé.

Vous pouvez aussi utiliser debconf dans d'autres programmes indépendants. Le seul problème est que debconf n'est pas conçu pour être un système d'enregistrement et ne peut pas être utilisé comme tel. La philosophie d'Unix est préservée, les programmes sont configurés à l'aide de fichiers dans /etc, et non pas par une sombre base de données debconf (ce n'est qu'un cache et il peut se volatiliser). Réfléchissez donc longuement avant d'utiliser debconf dans un programme indépendant.

Cela peut prendre un sens dans certains cas, comme dans le programme apt-setup qui utilise debconf pour interroger l'utilisateur de manière cohérente avec le reste de la procédure d'installation de Debian et qui agit immédiatement avec les réponses pour configurer le fichier sources.list.

Debconf accepte la localisation des fichiers templates. Cela est accompli en ajoutant d'autres champs contenant les traductions. Tous les champs peuvent être traduits. Par exemple, vous pourriez avoir envie de traduire la description en espagnol. Créez simplement un champ nommé « Description-es » contenant la traduction. Si un champ traduit n'est pas disponible, debconf utilise le champ anglais.

En plus du champ « Description », vous devriez traduire le champ « Choices » d'un message de type select ou multiselect. Il faut lister les choix traduits dans l'ordre dans lequel ils apparaissent dans le champ « Choices » principal. Vous ne devriez pas avoir besoin de traduire le champ « Default » d'une question de type select ou multiselect et la valeur de la question sera automatiquement retournée en anglais.

Vous trouverez sûrement plus facile de gérer les traductions si vous les conservez dans des fichiers séparés ; un fichier par traduction. Par le passé, les programmes debconf-getlang(1) et debconf-mergetemplate(1) étaient utilisés pour gérer les fichiers debian/templates.ll. Cela a été rendu obsolète par le paquet po-debconf(7), qui permet de traiter les traductions des questionnaires debconf avec des fichiers .po comme les autres traductions. Vos traducteurs vous remercieront pour l'utilisation de ce nouveau mécanisme performant.

Pour plus de précisions sur po-debconf, consultez sa page de manuel. Si vous utilisez debhelper, la conversion vers po-debconf est aussi simple que de lancer la commande debconf-gettextize(1) une fois et d'ajouter une dépendance de construction (« Build-Depends ») sur po-debconf et sur debhelper (>= 4.1.13).

Donc vous avez un script config, un fichier templates, un script postinst qui utilisent debconf, etc. Réunir tous ces scripts dans un paquet Debian n'est pas difficile. Vous pouvez le faire à la main ou utiliser dh_installdebconf(1) qui fusionne vos questions-modèles traduites, copie les fichiers à la bonne place pour vous et peut même générer l'appel à PURGE qui devrait être placé dans votre script postrm. Assurez-vous que votre paquet dépende de debconf (>= 0.5), puisque les anciennes versions n'étaient pas compatibles avec tout ce qui est décrit dans ce manuel. Et c'est terminé !

Mais vous ne pouvez pas encore tester, déboguer et utiliser debconf pour des choses plus intéressantes que de poser de simples questions. Pour cela, veuillez continuer à lire.

Vous avez donc un paquet qui est supposé utiliser debconf, mais il ne fonctionne pas très bien. Peut-être que debconf ne pose pas la question que vous avez configurée. Ou peut-être que quelque chose d'étrange arrive ; il entre dans une boucle infinie, ou pire encore. Heureusement, debconf possède beaucoup de possibilités de débogage.

La première chose à portée de main est la variable d'environnement DEBCONF_DEBUG. Si vous positionnez et exportez DEBCONF_DEBUG=developer, debconf affichera sur la sortie d'erreur standard (« stderr ») une copie du protocole debconf lorsque votre programme s'exécute. Elle ressemblera à quelque chose comme ceci -- la faute est flagrante :


debconf (developer): <-- input high debconf/frontand
debconf (developer): --> 10 "debconf/frontand" doesn't exist
debconf (developer): <-- go
debconf (developer): --> 0 ok

Lors d'un débogage, l'interface readline de debconf est très utile (d'après l'auteur), car les questions ne masquent pas cet affichage, toute la sortie du débogage est facilement préservée et enregistrée.

Si cette variable d'environnement est définie à « true », l'interface graphique affichera les valeurs dans les champs « Choices-C » (s'ils sont présents) des modèles select et multi-select au lieu des valeurs compréhensibles.
debconf-communicate(1) est un autre programme utile. Lancez-le et vous pourrez parler le protocole debconf brut à debconf. C'est une bonne manière d'essayer des choses à la volée.
Si un utilisateur rapporte un problème, debconf-show(1) peut être utilisé pour lister les questions de votre paquet, en affichant leurs valeurs et en indiquant si l'utilisateur les a déjà vues.
.debconfrc
Pour éviter le cycle ennuyeux construction/installation/débogage, il peut être utile de charger vos questionnaires avec debconf-loadtemplate(1) et de lancer votre script config à la main avec la commande debconf(1). Néanmoins, vous devez toujours le faire en tant que superutilisateur, d'accord ? Pas terrible ! Et idéalement vous souhaiteriez être en mesure de voir à quoi ressemble une installation toute fraîche de votre paquet avec une base de données debconf propre.

Il s'avère que si vous configurez un fichier ~/.debconfrc pour un utilisateur normal, pointant vers un config.dat et un template.dat propres à l'utilisateur, vous pouvez charger les questionnaires et lancer tous les scripts config que vous voulez, sans avoir besoin d'un accès super-utilisateur. Si vous voulez commencer avec une base de données propre, supprimez simplement les fichiers *.dat.

Pour plus de détails pour mettre cela en place, voyez debconf.conf(5), et remarquez que /etc/debconf.conf fait un bon modèle pour un fichier ~/.debconfrc personnel.

Beaucoup d'entre vous ont l'air de vouloir utiliser debconf pour aider à la gestion des fichiers de configuration contenus dans vos paquets. Peut-être qu'il n'y a pas de bonne valeur par défaut à inclure dans votre paquet, vous voulez donc utiliser debconf pour interroger l'utilisateur et écrire un fichier de configuration basé sur ses réponses. Cela semble assez facile à faire, mais lorsque vous considérez les mises à niveau, que faire lorsque quelqu'un modifie le fichier de configuration que vous générez, et dpkg-reconfigure, et...

Il y a beaucoup de manières de le faire, la plupart d'entre-elles ne sont pas correctes et vous serez souvent ennuyé par des rapports de bogue. Voici une manière correcte de le faire. Cela suppose que votre fichier config n'est composé que d'une série de variables de shell positionnées, avec des commentaires entre elles, vous pouvez simplement inclure le fichier pour le « charger ». Si vous avez un format plus compliqué, sa lecture (et son écriture) devient un peu plus délicate.

Votre script config ressemblera à quelque chose comme ça :


#!/bin/sh
CONFIGFILE=/etc/foo.conf
set -e
. /usr/share/debconf/confmodule


# charge le fichier de configuration, s'il existe.
if [ -e $CONFIGFILE ]; then
. $CONFIGFILE || true

# Enregistrer les valeurs du fichier de configuration
# dans la base de données de debconf
db_set mypackage/toto "$FOO"
db_set mypackage/titi "$BAR"
fi


# Poser les questions.
db_input medium mypackage/toto || true
db_input medium mypackage/titi || true
db_go || true

Et le script postinst ressemblera à quelque chose comme ceci :


#!/bin/sh
CONFIGFILE=/etc/foo.conf
set -e
. /usr/share/debconf/confmodule


# Générer un fichier de configuration, s'il n'en existe pas.
# Une alternative est d'effectuer une copie dans un fichier
# modèle depuis un autre endroit.
if [ ! -e $CONFIGFILE ]; then
echo "# Fichier de configuration pour mon paquet" > $CONFIGFILE
echo "TOTO=" >> $CONFIGFILE
echo "TITI=" >> $CONFIGFILE
fi


# Substituer les valeurs par celles de la base
# de données de debconf. Des optimisations
# évidentes sont possibles ici. Le cp avant
# le sed permet de s'assurer que l'on ne détruit
# pas le système des droits du fichier config.
db_get mypackage/foo
TOTO="$RET"
db_get mypackage/bar
TITI="$RET"
cp -a -f $CONFIGFILE $CONFIGFILE.tmp


# Si l'administrateur a supprimé ou commenté des variables mais
# les a ensuite définies via debconf, ajouter (à nouveau) au
# fichier de configuration (conffile).
test -z "$TOTO" || grep -Eq '^ *TOTO=' $CONFIGFILE || \
echo "TOTO=" >> $CONFIGFILE
test -z "$TITI" || grep -Eq '^ *TITI=' $CONFIGFILE || \
echo "TITI=" >> $CONFIGFILE


sed -e "s/^ *TOTO=.*/TOTO=\"$TOTO\"/" \
-e "s/^ *TITI=.*/TITI=\"$TITI\"/" \
< $CONFIGFILE > $CONFIGFILE.tmp
mv -f $CONFIGFILE.tmp $CONFIGFILE

Examinez comment ces deux scripts gèrent tous les cas. Sur une nouvelle installation, les questions sont posées par le script config et un nouveau fichier de configuration est généré par le script postinst. Pendant les mises à niveau et les reconfigurations, le fichier config est lu, et ses valeurs sont utilisées pour modifier les valeurs dans la base de données de debconf : les modifications manuelles de l'administrateur ne sont donc pas perdues. Les questions sont posées à nouveau (et peuvent être affichées ou non). Puis le script postinst remplace les valeurs dans le fichier config, en laissant le reste du fichier inchangé.

Peu de choses sont plus frustrantes, quand vous utilisez un système comme debconf, que de répondre à une question posée, puis de passer à un autre écran avec une nouvelle question, de réaliser alors que vous avez fait une erreur dans la dernière question et que vous voulez y retourner mais vous découvrez que vous ne le pouvez pas.

Puisque debconf est conduit par votre script config, il ne peut revenir seul à une question précédente, mais avec un petit coup de pouce de votre part, il peut le faire. La première étape est que votre script config fasse savoir à debconf qu'il est capable de gérer le fait que l'utilisateur presse un bouton de retour en arrière. Vous utiliserez la commande CAPB pour le faire en passant backup en paramètre.

Puis, après chaque commande GO, vous devez essayer de voir si l'utilisateur a demandé à revenir en arrière (debconf renvoie un code de retour 30) et si c'est le cas, revenir à la question précédente.

Il existe plusieurs manières d'écrire des structures de contrôle pour que votre programme puisse revenir aux questions antérieures lorsque c'est nécessaire. Vous pouvez écrire du code spaghetti plein de goto. Ou vous pouvez créer plusieurs fonctions et les utiliser de manière récursive. Mais peut-être que la façon la plus correcte et la plus simple est de construire une machine à états. Voici le squelette d'une machine à états que vous pouvez remplir et améliorer.


#!/bin/sh
set -e
. /usr/share/debconf/confmodule
db_capb backup


STATE=1
while true; do
case "$STATE" in
1)
# Deux questions sans rapport.
db_input medium ma/question || true
db_input medium mon/autre_question || true
;;
2)
# Ne poser cette question que si la
# réponse à la première question était oui.
db_get ma/question
if [ "$RET" = "true" ]; then
db_input medium ma/question_dependante || true
fi
;;
*)
# Le cas par défaut est atteint quand $STATE est plus
# grand que le dernier état implémenté, et provoque la
# sortie de la boucle. Ceci requiert que les état soient
# numérotés à partir de 1, successivement, et sans trou,
# puisque l'on entrera dans le cas par défaut s'il y a un
# trou dans la numérotation
break # quitte la boucle "while"
;;
esac


if db_go; then
STATE=$(($STATE + 1))
else
STATE=$(($STATE - 1))
fi
done


if [ $STATE -eq 0 ]; then
# L'utilisateur a demandé à revenir à la première
# question. Ce cas est problématique. L'installation
# normale des paquets avec dpkg et apt n'est pas
# capable de revenir en arrière vers les questions
# d'autres paquets, à l'heure où ceci est écrit, donc
# cela va provoquer la sortie, laissant les paquets non
# configurés - ce qui est probablement la meilleure
# façon de gérer la situation.
exit 10
fi

Notez que si tout ce que fait votre script config est de poser quelques questions sans rapport les unes avec les autres, il n'y a pas besoin d'une machine à états. Posez simplement toutes les questions et GO ; debconf fera de son mieux pour les présenter toutes sur un écran et l'utilisateur n'aura pas besoin de revenir en arrière.

Éviter les boucles infinies

Une chose très agaçante peut arriver avec debconf si vous avez une boucle dans votre script. Supposez que vous demandiez une entrée et que vous la validiez, ou que vous effectuiez une boucle si elle n'est pas valable :


ok=''
do while [ ! "$ok" ];
db_input low toto/titi || true
db_go || true
db_get toto/titi
if [ "$RET" ]; then
ok=1
fi
done

Cela parait correct au premier coup d'œil. Mais pensez à ce qui va arriver si la valeur de toto/titi est "" lorsque l'on entre dans cette boucle et que l'utilisateur a fixé la priorité à high, ou s'il utilise une interface non interactive et qu'on ne lui demande donc aucune entrée. La valeur de toto/titi n'est pas changée par db_input et donc le test échoue et boucle. Et boucle...

Une solution à ce problème est de s'assurer qu'avant l'entrée dans la boucle, la valeur de toto/titi est positionnée à quelque chose qui permettra de passer le test de la boucle. Donc par exemple si la valeur par défaut de toto/titi est « 1 », vous pourrez passer la commande RESET toto/titi juste avant d'entrer dans la boucle.

Une autre solution serait de vérifier la valeur du code de retour de la commande INPUT. Si c'est 30, l'utilisateur ne verra alors pas la question qui lui est posée et vous devriez sortir de la boucle.

Parfois, un ensemble de paquets liés peuvent être installés et vous voulez demander à l'utilisateur lequel doit être utilisé par défaut. Les dictionnaires, ispell ou les gestionnaires de fenêtres sont des exemples de tels jeux de paquets.

Bien qu'il pourrait être possible de simplement demander « Ce paquet doit-il être celui par défaut ? » pour chaque paquet de l'ensemble, cela aboutit à un grand nombre de questions répétitives si plusieurs de ces paquets sont installés. Avec debconf, il est possible d'afficher une liste de tous les paquets de l'ensemble et d'autoriser l'utilisateur à choisir l'un d'entre eux. Et voici comment.

Utilisez un message partagé par tous les paquets de cet ensemble. Quelque chose comme ceci :


Template: shared/window-manager
Type: select
Choices: ${choices}
Description: Choisissez une gestionnaire de fenêtres par défaut
Veuillez choisir le gestionnaire de fenêtres qui sera démarré par défaut
lors du lancement de X.
Description: Select the default window manager.
Select the window manager that will be started by
default when X starts.

Chaque paquet devrait inclure une copie de ce message. Puis il devrait inclure du code comme ceci dans son script config :


db_metaget shared/window-manager owners
OWNERS=$RET
db_metaget shared/window-manager choices
CHOICES=$RET


if [ "$OWNERS" != "$CHOICES" ]; then
db_subst shared/window-manager choices $OWNERS
db_fset shared/window-manager seen false
fi


db_input medium shared/window-manager || true
db_go || true

Une petite explication est nécessaire. Pour l'instant votre script est lancé, debconf a déjà lu tous les questionnaires des paquets qui vont être installés. Puisque tous ces paquets ont une question en commun, debconf enregistre ce fait dans le champ owners. Par une étrange coïncidence, le format du champ owners est le même que celui du champ choices (une liste de valeurs séparées par virgule et espace).

La commande METAGET peut être utilisée pour récupérer la liste des propriétaires (« owners ») et la liste des choix. S'ils sont différents, un nouveau paquet est alors installé. Utilisez alors la commande SUBST pour modifier la liste des choix afin qu'elle soit identique à celle des propriétaires et posez la question.

Lorsqu'un paquet est supprimé, vous voudrez probablement voir si ce paquet est le choix actuellement sélectionné et s'il l'est, demander à l'utilisateur de sélectionner un autre paquet pour le remplacer.

Cela peut être accompli en ajoutant quelque chose comme ceci dans le script prerm de tous les paquets liés (en remplaçant <paquet> par le nom du paquet) :


if [ -e /usr/share/debconf/confmodule ]; then
. /usr/share/debconf/confmodule
# Je ne veux plus de cette question.
db_unregister shared/window-manager

# Regarde si la question partagée existe toujours.
if db_get shared/window-manager; then
db_metaget shared/window-manager owners
db_subst shared/window-manager choices $RET
db_metaget shared/window-manager value
if [ "<paquet>" = "$RET" ] ; then
db_fset shared/window-manager seen false
db_input high shared/window-manager || true
db_go || true
fi


# Maintenant faites ce que le script postinst faisait
# pour mettre à jour le lien symbolique du gestionnaire de
# fenêtre.
fi
fi

Debconf n'est pas encore entièrement intégré à dpkg (mais je veux changer ça), cela demande donc actuellement quelques bidouilles peu propres.

Le pire de ces bidouillages est le lancement du script config. Le fonctionnement actuel est de lancer le script config lorsque le paquet est pré-configuré. Puis, lorsque le script postinst s'exécute, il lance debconf. Debconf remarque qu'il va être utilisé par le script postinst, il s'arrête et lance le script config. Cela ne fonctionne que si votre script postinst charge l'une des bibliothèques debconf, les scripts postinst doivent toujours prendre soin de les charger. Nous espérons nous attaquer à cela plus tard en ajoutant un support explicite de debconf dans dpkg. Le programme debconf(1) est une étape dans ce sens.

Une bidouille similaire est de lancer debconf lorsqu'un script config, postinst, ou d'autres programmes qui l'utilisent commence. Après tout, ils espèrent avoir la possibilité de communiquer avec debconf d'une façon correcte. Pour l'instant, la manière dont cela est accompli est que lorsqu'un tel script charge une bibliothèque debconf (comme /usr/share/debconf/confmodule) et que debconf n'est pas déjà lancé, il est démarré et une nouvelle copie du script est ré-exécutée. Le seul résultat perceptible est que vous avez besoin de mettre la ligne qui charge une bibliothèque debconf au tout début du script, ou des choses étranges arriveront. Nous espérons examiner ça plus tard en changeant l'invocation de debconf et le changer en un démon provisoire.

La façon dont debconf trouve quels fichiers templates charger et quand les charger ressemble vraiment à une bidouille. Lorsque les scripts config, preinst et postinst invoquent debconf, il trouvera automatiquement l'emplacement du fichier templates et le chargera. Les programmes indépendants qui utilisent debconf forceront debconf à rechercher les fichiers templates dans /usr/share/debconf/templates/nomduprog.templates. Et si un le script postrm veut utiliser debconf au moment de la purge, les questionnaires ne seront pas disponibles à moins que debconf ait une opportunité de les charger dans son script postinst. Cela n'est pas très propre mais presque inévitable. Dans le futur, certains de ces programmes pourraient malgré tout avoir la possibilité d'utiliser debconf-loadtemplate à la main.

Le comportement historique de /usr/share/debconf/confmodule de jouer avec les descripteurs de fichier et de configurer un descripteur de fichier spécial (« fd #3 ») qui communique avec debconf, peut causer toutes sortes de trouble lorsqu'un démon est lancé par un script postinst, puisque le démon cesse de communiquer avec debconf et que debconf ne peut pas savoir quand le script se termine. La commande STOP peut être utilisée pour ceci. Nous envisagerons plus tard de faire passer la communication avec debconf à travers une socket ou un autre mécanisme que les entrées et sorties standard.

Debconf positionne DEBCONF_RECONFIGURE=1 avant de lancer les scripts postinst, donc un script postinst voulant éviter des opérations coûteuses lorsqu'il est reconfiguré peut regarder cette variable. C'est une bidouille car la meilleure chose à faire serait de lui passer $1 = "reconfigure", mais le faire sans casser tous les scripts postinsts qui utilisent debconf est difficile. Le projet de migration pour cette bidouille est d'encourager les gens à écrire des scripts postinst qui acceptent "reconfigure" et, une fois qu'ils le feront tous, commencer à passer ce paramètre.

debconf(7) est le guide de l'utilisateur de debconf.

La spécification debconf dans la charte Debian est une définition canonique du protocole debconf. /usr/share/doc/debian-policy/debconf_specification.txt.gz

debconf.conf(5) contient beaucoup d'informations, y compris des informations sur les pilotes de la base de données.

Joey Hess <joeyh@debian.org>

Julien Louis <ptitlouis@sysif.net>, 2005
Cyril Brulebois <kibi@debian.org>, 2006

Veuillez signaler toute erreur de traduction en écrivant à <debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le paquet debconf.