Whole document tree
    

Whole document tree

The Debconf Programmer's Tutorial

The Debconf Programmer's Tutorial

Joey Hess

The Debian Project

joeyh@debian.org

This text is copyright by the author under the terms of the BSD license, without the advertising clause.


Introduction

This 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 started

First, 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.)

Note: If your preinst uses debconf, you must make your package Pre-Depend on debconf (>= 0.2.17).



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 file

Start 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

Type Description
string Holds any arbitrary string of data.
boolean Holds "true" or "false".
select Holds one of a finite number of possible values. These values must be specified in a field named Choices:. Separate the possible values with commas and spaces, like this: Choices: yes, no, maybe
multiselect Just like the select data type, except the user can choose any number of items from the list. This means that the Default: field and the actual value of the question may be a comma and space delimited list of values, just like the Choices: field.

Note: For compatability with old versions of Debconf, if you use this data type, please make your package depend on debconf (>= 0.2.26)

note This template is a note that can be displayed to the user. As opposed to text, it is something important, that the user really should see. If debconf is not running interactively, it might be saved to a log file or mailbox for them to see later.
text This template is a scrap of text that can be displayed to the user. It's intended to be used for mostly cosmetic reasons, touching up around other questions that are asked at the same time. Unlike a note, it isn't treated as something the user should definitely see. Less complex frontends may refuse to ever display this type of element.
password Holds a password. Use with caution. Be aware that the password the user enters will be written to debconf's database. You should consider clearing that value out of the database as soon as is possible.


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 file

Later, 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.it
   
and 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.it
   
Fuzzy 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 Script

Next, 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.

Note: These questions are asked by a separate config script, not by the postinst, so the package can be configured before it is installed, or reconfigured after it is installed. Do not make your postinst use debconf to ask questions.

Depending on what language you choose to write debian/config in, you have some choices about how to communicate with the frontend:
  • shell script

    You can source /usr/share/debconf/confmodule, which will make a number of shell functions available to you. Each shell function corresponds to a command in the protocol (lowercased, and with "db_" prefixed to its name). You pass parameters to it and get a result back in the $RET variable.

    Note: Some commands also return a number exitcode, to indicate failure and other unusual occurances, so you need to trap that to prevent set -e shell scripts from dying.

    For details, see confmodule.3 and the debconf protocol specification in Debian policy.

  • perl

    You can use the Debconf::Client::ConfModule perl module, which makes a number of functions available to you. Each function corresponds to a command in the protocol -- you pass parameters into it and it returns the result. For details, see the Debconf::Client::ConfModule (3pm) man page, and the debconf protocol specification in Debian policy.

  • other

    You'll have to communicate with the frontend directly via standard input and standard output. This isn't hard; read the protocol specification for details (the specification is in Debian policy).



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

     


Note: Note that the config script is run before the package is unpacked. It should only use commands that are in essential packages Also, it shouldn't actually edit files on the system, or affect it in any other way.


Modifying Existing Maintainer Scripts

Once 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
     


Note: Debhelper will do this for you if you use the dh_installdebconf command.

Note: Even if your postinst doesn't do anything with debconf, you currently need to make sure it loads one of the debconf libraries. This is because the debconf libraries do deep magic to make the config scripts work. This will be changed in the future.


Finishing Up

Now 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!


Testing

Before 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.

Note: While this is a little ugly, it means you can name your config script debian/config, or debian/package.config, and name your templates file likewise, and just run them, and things will work fine, automatically.




Troubleshooting

A few things can commonly go wrong when you convert something over to debconf:

  • Your postinst uses debconf and starts a daemon that doesn't close all inherited file descriptors (all such daemons are buggy, really). This makes debconf hang, because the debconf frontend waits for the daemon to close the fd's before continuing. Note that if you use confmodule, the program probably needs to close fd's 0, 1, 2, and 3.

    To fix:

    • Redirect fd's to /dev/null before running the daemon

    • Or fix the daemon.

    • Or call the "stop" command at the end of your postinst, to let the frontend know you're done.



  • Something weird is happening, and you don't understand what's going on.

    Try setting DEBCONF_DEBUG to 'developer' in the environment. This makes debconf output debugging information, including the commands your scripts send to it and its responses. See the User's Manual for more information on the DEBCONF_DEBUG environment variable.




Advanced Topics

Now I'll move on to some more complicated areas of debconf.


Blocks

This 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 Up

It'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 Prevention

One 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 packages

Sometimes 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 debconf

It'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.

Note: Remember, debconf is not intended to be a registry.

It might occasionally make sense to write a standalone program that uses debconf to prompt the user for questions. Standalone programs that use debconf work just like a debian config script: load up the debconf library for your language and proceed as usual.

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. Commands

This 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 hierarchy

Templates 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.

  • package/*

    This is the property of a single package, and can be subdivided however that package wants to.

  • shared/foo/*

    This holds items that are shared between several packages. "foo" is replaced by a name that relates to all of them. For example, news grabbers and readers all can use shared/news/server to store the news server they use.

    Currently the following shared templates and questions exist:

    • shared/news/server

      Hostname of the news server.





Notes

[1]

Since policy frowns on pre-dependencies that haven't been approved by debian-devel, you could also make your package detect if debconf isn't installed, and use a sane fallback that doesn't involve debconf.

[2]

Actually, Description-es_ES (or es_MX, or whatever) will be checked first.

[3]

Unless the default value is locale dependent.

[4]

Debhelper will do this for you if you use the dh_installdebconf command.

[5]

This is very similar to the arguments that are passed to the postinst script.

[6]

Though in reality they can use any commands, including "input", you are strongly encouraged not to do so.

[7]

If you use debhelper, this will all be done automatically.

[8]

For example, if question b should only be asked if question a is true, you obviously can't display them at the same time.

[9]

Actually, there are two. The other time a back button might be useful is at the first question asked, when it might jump back from configuring this package to configuring the previous package. However, the details of how this would work are not worked out yet.