The Debconf Tutorial

Joey Hess

The Debian Project

joeyh@debian.org

This text is distributed according to the General Public License.


Table of Contents
Introduction
Getting started
The Templates file
The Config Script
Modifying Existing Maintainer Scripts
Finishing Up
Testing
Troubleshooting
Advanced Topics
A. Commands

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 spec , 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 of the user. 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 part of the base system.

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

TypeDescription
stringHolds 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 definitly see.
passwordHolds a password. Use with caution.

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. If you still can't come up with anything, leave it blank. There is no point in duplicating the short description.

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, and the answer returned when you display the question will always be in English.


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 seperate 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:

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 variable" asks the frontend to make sure it has asked the user for the value of a variable, specifying how important it is the user be asked this. The variable 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 variable" asks the frontend to return to you what value a variable is set to.

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. [3].

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 the base system. 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 Debian::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" [4]. 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. [5]

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:


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 dependant on each other. [6] 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 scructures 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: [7] 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" != stop ]; do
	case "$STATE" in
	1)
		# 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
			# Proceed to second question.
			STATE=2
		else
			# All done.
			STATE=stop
	        fi
	;;
	
	2)
 		# Poor misguided one..
		db_input high foo/why_debian_is_great || true
		if ! db_go; then
			# Back button -- go back to first question.
			STATE=1
			# But first, ensure that all questions asked
			# so far will be asked again.
			db_fset foo/like_debian isdefault true
			db_fset foo/why_debian_is_great isdefault true
		else
			# All done.
			STATE=stop
		fi
	;;
	esac
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.

Now, consider what happens if the user does see the question, but answers with something that fails the test. It loops and shows the question again, right? Wrong! Debconf doesn't display a question every time you ask it to be input; it only displays questions once, ever, unless you reset the is_default flag to true.


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 repetative 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 isdefault true
	db_input medium shared/window-manager || true
	db_go || true
fi
	

A bit of an explination 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 cooincidence, 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 variable.
	db_unregister shared/window-manager
		
	# See if the shared variable 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 isdefault true
			db_input critical shared/window-manager || true
			db_go || true
		fi
	fi
fi
	


A. Commands

The commands may return a textual value, but they always return a numeric status code, which is in the following ranges:

Table A-1. Numeric status codes

RangeDescription
0success
1-9reserved
10-19invalid question
20-29syntax errors
30-99command-specific return codes
100-255reserved

Here is a complete list of the the commands you can use to communicate with the frontend. [8]

Notes

[1]

Since policy frowns on pre-dependancies 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]

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

[4]

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

[5]

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

[6]

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

[7]

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.

[8]

Remember these are prefixed with "db_" if you are using the shell library.