Creating configuration files in /etc in Debian packages

I am currently working on an updated picockpit-client version, which should add additional configuration files in /etc/picockpit-client for the individual modules (PiDoctor & PiControl).

picockpit-client is a Raspbian package for picockpit.com, linking the Web platform with your individual Raspberry Pi.

As Raspbian is based on Debian, the following information holds true for general packaging for Debian and derivatives (e.g. Ubuntu).

dh-virtualenv

I highly recommend my other article about Debian packaging as an introduction and additional material, in case you are interested to package Python applications.

In this article I will discuss general concepts related to packaging, and specifically installing files in /etc/

One note up ahead: The “entrypoint” to build a package is the makefile debian/rules.

And you can build packages using the following command:

dpkg-buildpackage -uc -us –b

This command is run from the top directory of your package, not the debian/ directory.

Basic Overview

image

You can see a folder structure required for a Debian package (contained in the folder picockpit-client-package).

Most important, it contains a folder debian, which contains several configuration files used to build the package.

This file layout is used to build the package using debhelper, which most packages use because it automates many tasks.

changelog

This file contains changelog information (for the debian package). It is used to set the version of the package.

Example contents:

image

Note that indentation by spaces is necessary (two spaces before the * symbol, one space before the – symbol)

In my example the newest package version (entry at the top) is 0.18.5

compat

This file contains a number followed by a newline:

image

This is the version of debhelper your application is compatible with. (As of version 12 this can be specified in debian/control as well, but will still be supported in debian/compat for backwards compatibility – see this manpage)

control

image

This file is used to provide dependencies, package name, and other relevant package meta data.

copyright

image

This file provides copyright information for your package.

<packagename>.install

image

In my case, the file is called picockpit-client.install since my package is the picockpit-client package.

This file is evaluated by dh_install (which is part of debhelper). It copies a file or several files into a directory in the package (to be installed into this absolute system location later on).

In this case, I want the file etc/dinosaur/dinosaur.cfg to be installed in /etc/dinosaur, as /etc/dinosaur/dinosaur.cfg

One problem I came across when researching this topic is to understand, where to place etc/dinosaur/dinosaur.cfg, relative to picockpit-client.install.

It is important to know in which search locations dh_install looks for these files.

Here is the answer:

By default it looks in “.” and “debian/tmp”.

“.” is the directory in which the debian folder is contained.

Therefore, observe the folder structure:

image

So, as you see, “etc” is at the same folder level as “debian”. Please note that I could have named this arbitrarily, since I specify the location in picockpit-client.install:

etc/dinosaur/dinosaur.cfg etc/dinosaur

I could modify this to be simply dinosaur.cfg, then dinosaur.cfg has to be in the same parent directory as the debian folder.

Please see further below for more information about the special role of files which go into /etc, and an important dh_install debugging tip.

<packagename>.links

This will set up symlinks, e.g. in order to add your application to the system path in which binaries are being looked for:

image

<packagename>.postinst

A script which is run after the installation. set –e is important in this script, it must also return 0 to indicate success (so that the package install can be finalized).

I have the default from a template:

image

<packagename>.service

This is a systemd service entry to be added for your application. I have, for example:

image

<packagename>.triggers

I am not entirely sure what this is for; It looks like this for me (and works):

image

rules

rules is a makefile used to build the package. This will vary, depending on what kind of package you build. I am reproducing my rules file here for your convenience.

As you can see, it uses dh_virtualenv, which is a solution to create virtual environments for Python code (enabling incompatible packages to coexist on the same system, and you to be able to control the environment you deploy to).

If you are interested in dh_virtualenv, read my article on how to package Python packages for Raspbian with dh_virtualenv.

#!/usr/bin/make -f
#
# Build Debian package using https://github.com/spotify/dh-virtualenv
#
# The below targets create a clean copy of the workdir via
# using “sdist”, else “pip” goes haywire when installing from
# sourcedir “.”, because that includes the debian build stage,
# and a recursive explosion ensues when symlinks are followed.
#
# It also ensures your MANIFEST is complete and at least covers
# all files needed for a release build.

# Increase trace logging, see debhelper(7) (uncomment to enable)
#DH_VERBOSE=1

export DH_VIRTUALENV_INSTALL_ROOT=/usr/share
SNAKE=/usr/bin/python3
EXTRA_REQUIREMENTS=–preinstall “setuptools>=17.1,<34” –preinstall “pip>=7” –preinstall “wheel” –preinstall “no-manylinux1”
DH_VENV_ARGS=–with python-virtualenv –setuptools –python $(SNAKE) $(EXTRA_REQUIREMENTS) #-v
PACKAGE=$(shell dh_listpackages)
VERSION=$(shell $(SNAKE) setup.py –version)
SDIST_DIR=debian/$(PACKAGE)-$(VERSION)

clean:
     test ! -d dist || rm -rf dist
     test ! -d $(SDIST_DIR) || rm -rf $(SDIST_DIR)
     dh $@ $(DH_VENV_ARGS)

build-arch:
     $(SNAKE) setup.py sdist –formats tar
     mkdir -p $(SDIST_DIR)
     tar -x -C $(SDIST_DIR) –strip-components=1 –exclude ‘*.egg-info’ -f dist/*.tar
     #dh $@ $(DH_VENV_ARGS) –sourcedir $(SDIST_DIR) –sourcedir .
     dh $@ $(DH_VENV_ARGS) –sourcedir .

%:
     #dh $@ $(DH_VENV_ARGS) –sourcedir $(SDIST_DIR) –sourcedir .
     dh $@ $(DH_VENV_ARGS) –sourcedir .

Please note that WordPress tends to mess up the syntax of code, so only use this as a rough guide, not to copy & paste!

More information about /etc, and dh_install

Important sidenote / bugfix

I spent a lot of time debugging the following problem:

dh_install: Cannot find (any matches for) “etc/dinosaur/dinosaur.cfg” (tried in debian/picockpit-client-0.18.5, debian/tmp)

dh_install: picockpit-client missing files: etc/dinosaur/dinosaur.cfg
dh_install: missing files, aborting

This problem is related to a “wrong” entry in debian/rules

image

as you can see here, in this (for beginners) rather overwhelming and cryptic syntax, a parameter called –sourcedir is being set for the dh command on the build-arch and % entries:

image

In the dh_install documentation, we are able to read that this parameter configures in which source directory files should be searched for for the installation.

image

Since this had been set in my debian/rules dh_install was looking in debian/picockpit-client-0.18.5 and debian/tmp – but not in the top level directory.

The fix is simple:

image

dh $@ $(DH_VENV_ARGS) –sourcedir .

I simply pass “.” as directory name to –sourcedir

Please note that passing two sourcedir entries will not work as expected:

image

in this case, the first entry is going to be overwritten:

image

So only “.” is going to be checked, and the directory passed in $(SDIST_DIR) not.

etc is special

dh_install treats files to be installed in etc as so-called conffiles.

This is automatically determined, you do not need to add any additional configuration for this (simply add the configuration file as a line in your <package>.install file).

To say it again: if you use debhelper for your packaging, you do not need to create the conffiles file in the debian directory manually. debhelper will do it for you.

dpkg manages these configuration files, and ensures that user configuration is saved between package updates, and on simple package removal.

Read this wonderful overview about what conffiles mean for users by Raphael Hertzog.

Let’s say I have installed my package (with a fictituous /etc/dinosaur/dinosaur.cfg ).

I have then added an additional entry “New Dino adventures” into /etc/dinosaur/dinosaur.cfg

image

If I simply remove the package:

apt-get remove picockpit-client

Then the package will be removed, but the configuration file will stay behind:

image

To get rid of the modified configuration file as well, we need to purge the package:

apt-get purge picockpit-client

image

It will inform you about what it is doing: “Purging configuration files for picockpit-client (0.18.5) …”

Note: you do not have to modify the configuration file for it to stay behind if you remove the package. An unmodified file will also stay behind.

OK, now let’s say you have modified the file and are about to upgrade or reinstall the package:

image

In this case, your modified version will stay (or you will be prompted):

image

The versions of the config files, and which version your modifications are based on stem from MD5 sums.

You can inspect these md5sums by:

dpkg –status picockpit-client

image

As you can see, in the Conffiles section the file /etc/dinosaur/dinosaur.cfg is show with it’s original MD5 sum.

dpkg can compare the MD5 sum between upgrades of the package, and see if the maintainer’s version changes.

In case you do not modify the original configuration file (as system user), the maintainer’s new version will be installed.

If both have modified their files, then you will be prompted to make a choice.

References

For dh_virtualenv: