Whole document tree The Debconf Programmer's TutorialJoey Hess
The Debian Project
joeyh@debian.org Copyright © 1999 , 2000 by Joey Hess This text is copyright by the author under the terms of the BSD license, without the advertising clause. IntroductionThis is a guide to using debconf with your packages, aimed at a Debian developer. So, what is debconf? To save you reading the specification (which is in Debian policy if you're interested), debconf is a backend database, with a frontend that talks to it and presents an interface to the user. There can be many different types of frontends, from plain text to a web frontend. The frontend also talks to a special config script in the control section of a debian package, and it can talk to postinst scripts and other scripts as well, all using a special protocol. These scripts tell the frontend what values they need from the database, and the frontend asks the user questions to get those values if they aren't set. Debconf should be used whenever your package needs to output something to the user, or ask a question. I'll assume you already have a package that does this and you want to convert it to use debconf. Getting startedFirst, your package must depend on debconf (or pre-depend on it if it uses debconf in its preinst[1]). This is necessary since debconf isn't essential. The first thing to do is look at your postinst, plus any program your postinst calls (like a "packageconfig" program), plus your preinst, and even your prerm and postrm. Take note of all output they can generate and all input they prompt the user for. All this output and input must be eliminated for your package to use debconf. (Output to stderr can be left as is.)
For example, a hypothetical package "foo" has the following postinst: #!/bin/sh -e echo -n "Do you like debian? [yn] " read like case "$like" in n*|N*) echo "Poor misguided one. Why are you installing this package, then?" /etc/init.d/subliminal_messages start "I like debian." ;; esac It's clear that it asks a question and sometimes outputs a message. In this tutorial, we will make it use use debconf to do both. The Templates fileStart writing a debian/templates file. Each time you find a piece of output or a question, add it to the file as a new template. The format of this file is simple and quite similar to a Debian control file: Template: packagename/something Type: [select,multiselect,string,boolean,note,text,password] Default: [an optional default value] Description: Blah blah blah? Blah blah blah. Blah blah. Blah blah blah. Blah blah? Blah blah blah blah. Blah blah blah. Blah blah. . Blah blah blah. Blah blah. Blah blah blah. Blah blah. Blah blah blah. blah. Template: .... ....
Table 1. Available data types
Note that the Description field has two parts, a short description and a long description. Please note, that some frontends do not always display the long description, often only showing it if the user asks for additional help. So the short description should be entirely standalone. If you can't think up a long description, then first, think some more. Post to debian-devel. Ask for help. Take a writing class! That extended description is important. If after all that you still can't come up with anything, leave it blank. There is no point in duplicating the short description. Text in the long description will be word-wrapped, unless it is prefixed by additional whitespace (beyond the one required space). You can break it up into separate paragraphs by putting " ." on a line by itself between them. Following along in our example, we create a templates file with two templates in it: Template: foo/like_debian Type: boolean Description: Do you like Debian? We'd like to know if you like the Debian GNU/Linux system. Template: foo/why_debian_is_great Type: note Description: Poor misguided one. Why are you installing this package? Debian is great. As you continue using Debian, we hope you will discover the error in your ways. Localizing the templates fileLater, you might want to add translations to your templates file. This is accomplished by adding more fields, with translated text in them. Any of the fields can be translated. For example, you might want to translate the description into Spanish. Just make a field named Description-es [2] that holds the translation. Of course, if a translated field is not available, it falls back to the normal field names. Note that you can (and should) even translate the Choices field of a select or multiselect question. If you do, you should list the same choices, in the same order as they appear in the main Choices field. It is very important the order is the same. You do not need to translate the Default field of a select or a multiselect question [3], and the answer returned when you display the question will always be in English. As you won't certainly be able to translate your templates file into all the desired languages, you need some external help. Be kind with translators, they need to know when you update the templates file without having to manually check it all over the time, and the simplest way to help them is to keep translated templates in separate files, e.g. templates.it for Italian translation. Thus when an Italian translator wants to translate your template file for the first time, he runs debconf-getlang it templates > templates.itand change all translatable fields. All you have to do then is to put this file along with your templates file in the debian subdirectory. The debconf-mergetemplate program can merge several such templates files together to produce a combined file to put in your binary package. [4] You may check that all strings have been translated by running debconf-getlang --stats templates templates.it When you update English text, do not touch translated templates files. Translators can check by running the command above that their translation is outdated, and merge changes by running debconf-getlang it templates templates.it > new.itFuzzy fields are tagged with -fuzzy in their name, and original field without value is inserted on a single line just before. Translator updates such fields according to the English text, removes these marks and provides you with a newer version. Again, all you have to do is to place this translated templates file in the debian subdirectory with the right name. The Config ScriptNext, decide what order the questions should be asked and the messages to the user should be displayed, figure out what tests you'll make before asking the questions and displaying the messages, and start writing a debian/config file to ask and display them.
A list and description of all the commands you can use to talk to the frontend is in the Commands appendix. The most common commands you will use in the config script are "input", "go", and "get". Briefly, "input priority question" asks the frontend to make sure it has asked the user a question, specifying how important it is the user be asked. The question names normally correspond to the names of the templates in the template files. "go" tells the frontend to display all accumulated input commands to the user. And "get question" asks the frontend to return to you the answer to a question. Some other notes about config scripts: Just like other maintainer scripts, config scripts must be idempotent. The config script is passed 2 parameters. The first is either "configure" or "reconfigure". The latter occurs only if a package is being reconfigured by dpkg-reconfig. The second parameter is the last version of the package that was configured. [5]. Continuing the example, we need a config script to display the first question, and if the user says they do not like debian, it should display the message about that. #!/bin/sh -e # Source debconf library. . /usr/share/debconf/confmodule # Do you like debian? db_input medium foo/like_debian || true db_go # Check their answer. db_get foo/like_debian if [ "$RET" = "false" ]; then # Poor misguided one.. db_input high foo/why_debian_is_great || true db_go fi
Modifying Existing Maintainer ScriptsOnce you have a templates file and a config script, it's time to move on to using the data your config script collects in other maintainer scripts of your package, like your postinst. Just like the config script, the postinst and other maintainer scripts can use the confmodule or Debconf::Client::ConfModule libraries, or they can speak directly to the frontend on standard output. Anything your maintainer scripts output to standard output is passed into the frontend as a command, so you need to remove all extraneous noise, like the starting and stopping of daemons, etc. The only command postinsts normally use to communicate with the frontend is "get" [6]. Typically, the config script prompts the user for input, and the postinst then pulls that input out of the database via the get command. In the example, before debconf, the package's postinst did this: #!/bin/sh -e echo -n "Do you like debian? [yn] " read like case "$like" in n*|N*) echo "Poor misguided one. Why are you installing this package, then?" /etc/init.d/subliminal_messages start "I like debian." ;; esac Our config script already handles most of this. After debconf, the postinst becomes: #!/bin/sh -e # Source debconf library. . /usr/share/debconf/confmodule db_get foo/like_debian if [ "$RET" = "false" ]; then /etc/init.d/subliminal_messages start "I like debian." fi There is one other alteration you need to make to the postrm script. When your package is purged, it should get rid of all the questions and templates it was using in the database. Accomplishing this is simple; use the "purge" command (make sure you don't fail if debconf has already been removed, though): if [ "$1" = "purge" -a -e /usr/share/debconf/confmodule ]; then # Source debconf library. . /usr/share/debconf/confmodule # Remove my changes to the db. db_purge fi
Finishing UpNow you have a config script and a templates file. Install both into debian/tmp/DEBIAN/. Make sure to make the config script executable. [7] Your package now uses debconf! TestingBefore you go build your package, you probably want to test the config script you wrote. This is possible to do, without installing the package -- just run your config script. There is a problem though: the config script relies on your templates being loaded before it is run. When a package that uses debconf is installed, that is handled automatically. Luckily, in most cases it is also handled automatically when you run the config script by hand. Debconf uses two simple rules to try to figure out the templates file associated with the config script, and if it finds one, it loads it. First, if there is a file with a name that is ".templates" appended to the name of the config script that is being run, debconf assumes that is the templates file. If that fails, debconf looks to see if the config script that is being ran has a filename ending in "config". If so, and if there exists a file with the same name, except the "config" is instead "templates", debconf assumes that is the templates file.
TroubleshootingA few things can commonly go wrong when you convert something over to debconf:
Advanced TopicsNow I'll move on to some more complicated areas of debconf. BlocksThis is really rather easy to do. Some debconf frontends have the ability to display more than one question on screen at the same time. However, the questions can't be dependent on each other. [8] Just wrap the input commands inside beginblock and endblock commands. Letting the User Back UpIt's very useful, if you are asking a long series of questions, if the user can jump backward in the list and change an answer. Debconf supports this, but it takes a fair amount of work on your part to make your package support it. The first step is to make your config script let debconf know it is capable of handling the user pressing a back button. You use the capb command to do this, passing "backup" as a parameter. Then after each go command, you must test to see if the user hit the back button, and if so jump back to the previous question. If the user hit the back button, the go command will return 30. There are several ways to write the control structures of your program so it can jump back to previous questions when necessary. You can write goto-laden spaghetti code. Or you can create several functions and use recursion. But perhaps the cleanest and easiest way is to construct a state machine. So let's take the example config script developed earlier in this tutorial, and make it support backing up. There is only one place in that config script where the user might hit a back button: [9] when the second question is displayed to them, to return to the first question. Here is a new version of the script that handles the back button: #!/bin/sh -e # Source debconf library. . /usr/share/debconf/confmodule db_version 2.0 # This conf script is capable of backing up db_capb backup STATE=1 while [ "$STATE" != 0 -a "$STATE" != 3 ]; do case "$STATE" in 1) # Do you like debian? db_input medium foo/like_debian || true ;; 2) # Check to see if they like debian. db_get foo/like_debian if [ "$RET" = "false" ]; then # Poor misguided one.. db_input high foo/why_debian_is_great || true fi ;; esac if db_go; then STATE=$(($STATE + 1)) else STATE=$(($STATE - 1)) fi done Infinite Loop PreventionOne gotcha with debconf comes up if you have a loop in your config script. Suppose you're asking for input and validating it, and looping if it's not valid: ok='' do while [ ! "$ok" ]; db_input low foo/bar || true db_go || true db_get foo/bar if [ "$RET" ]; then ok=1 fi done This looks ok at first glance. But consider what happens if the value of foo/bar is "" when this loop is entered, and the user has their priority set high, or is using a non-interactive frontend, and so they are not really asked for input. The value of foo/bar is not changed by the db_input, and so it fails the test and loops. And loops ... The fix for this is to make sure that before the loop is entered, the value of foo/bar is set to something that will pass the test in the loop. So for example if the default value of foo/bar is "1", then the "reset" command can just be called before entering the loop. Another fix is to check the return code of the "input" command. If it is 30 then the user is not being shown the question you asked them, and you should break out of the loop. Choosing among related packagesSometimes a set of related packages can be installed, and you want to prompt the user which of the set should be used by default. Examples of such sets are window managers, or ispell dictionary files. While it would be possible for each package in the set to simply prompt "Should this package be default?", this leads to a lot of repetitive questions if several of the packages are installed. It's possible with debconf to present a list of all the packages in the set and allow the user to choose between them. Here's how. Make all the packages in the set use a shared template. Something like this: Template: shared/window-manager Type: select Choices: ${choices} Description: Select the default window manager. Select the window manager that will be started by default when X starts. Each package should include a copy of the template. Then it should include some code like this in its config script: 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 A bit of an explanation is called for. By the time your config script runs, debconf has already read in all the templates for the packages that are being installed. Since the set of packages share a question, debconf records that fact in the owners field. By a strange coincidence, the format of the owners field is the same as that of the choices field (a comma and space delimited list of values). The "metaget" command can be used to get the list of owners and the list of choices. If they are different, then a new package has been installed. So use the "subst" command to change the list of choices to be the same as the list of owners, and ask the question. When a package is removed, you probably want to see if that package is the currently selected choice, and if so, prompt the user to select a different package to replace it. This can be accomplished by adding something like this to the prerm scripts of all related packages (replacing <package> with the package name): if [ -e /usr/share/debconf/confmodule ]; then . /usr/share/debconf/confmodule # I no longer claim this question. db_unregister shared/window-manager # See if the shared question still exists. 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 [ "<package>" = "$RET" ] ; then db_fset shared/window-manage seen false db_input critical shared/window-manager || true db_go || true fi # Now do whatever the postinst script did tp upsate # the window manager symlink or whatever. fi fi Standalone programs that use debconfIt's actually possible to use debconf in standalone programs, rather just from Debian package scripts. Whether or not doing this is a good idea, is a whole different matter.
If you're doing this type of thing, you might find it useful to drop template files in /usr/share/debconf/$0.templates, where $0 is the name of the program that is running. Debconf will load such templates up when the program runs. A. CommandsThis appendix has been removed now that the debconf specification is in Debian policy and is no longer built from the same tree as this document. Please see policy for a full list of the commands. B. Question hierarchyTemplates and questions in the debconf database are arranged in a hierarchical namespace. It can be arbitrarily deep, and is separated by '/' characters, like a Unix directory hierarchy. Here are the conventions we're using so far to divide up the namespace.
Notes
|