The Debconf Programmer's Tutorial


The Debconf Programmer's Tutorial


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


  Table of Contents

  Introduction

  Getting_started

  The_Templates_file

  The_Config_Script

  Modifying_Existing_Maintainer_Scripts

  Finishing_Up

  Testing

  Troubleshooting

  Advanced_Topics

  A. Commands

  B. Question_hierarchy


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"._________________________________________|
|           |Holds one of a finite number of possible values. These values    |
|select     |must be specified in a field named Choices:. Separate the        |
|           |possible values with commas and spaces, like this: Choices: yes, |
|___________|no,_maybe________________________________________________________|
|           |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.          |
|multiselect|                                                                 |
|           |     Note: For compatability with old versions of Debconf,       |
|           |     if you use this data type, please make your package         |
|           |     depend on debconf (>= 0.2.26)                               |
|___________|_________________________________________________________________|
|           |This template is a note that can be displayed to the user. As    |
|note       |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.____________|
|           |This template is a scrap of text that can be displayed to the    |
|           |user. It's intended to be used for mostly cosmetic reasons,      |
|text       |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.____________________________________|
|           |Holds a password. Use with caution. Be aware that the password   |
|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:

  o Redirect fd's to /dev/null before running the daemon
  o Or fix the daemon.
  o 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:

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