The right way to remove an obsolete conffile in a Debian package

A conffile is a configuration file managed by dpkg, I’m sure you remember the introductory article about conffiles. When your package stops providing a conffile, the file stays on disk and it’s recorded as obsolete by the package manager. It’s only removed during purge. If you want the file to go away, you have to remove it yourself within your package’s configuration scripts. You will now learn how to do this right.

When is that needed?

dpkg errs on the side of safety by not removing the file until purge but in most cases it’s best to remove it sooner so as to not confuse the user. In some cases, it’s even required because keeping the file could break the software (for example if the file is in a .d configuration directory, and if it contains directives that are either no longer supported by the new version or in conflict with other new configuration files).

What’s complicated in “rm”?

So you want to remove the conffile. Adding an “rm” command in debian/postinst sounds easy. Except it’s not the right thing to do. The conffile might contain customizations made by the administrator and you don’t want to wipe those. Instead you want to keep the file around so that he can get his changes back and do whatever is required with those.

The correct action is thus to move the file away in the prerm, to ensure it doesn’t disturb the new version. At the same time, you need to verify whether the conffile has been modified by the administrator and remember it for later. In the postinst, you need to remove the file if it’s unmodified, or keep it under a different name that doesn’t interfere with the software. In many cases adding a simple .dpkg-bak suffix is enough. For instance, run-parts ignore files that contain a dot, and many other software are configured to only include files with a certain extension—say *.conf. In the postrm, you have to remove the obsolete conffiles that were kept due to local changes and you should also restore the original conffile in case the upgrade obsoleting the conffile is aborted.

Automating everything with dpkg-maintscript-helper

Phewww… that’s a lot of things to do for a seemingly simple task. Fortunately everything can be automated with dpkg-maintscript-helper. Let’s assume you want to remove /etc/foo/conf.d/bar because it’s obsolete and you’re going to prepare a new version 1.2-1 with the appropriate code to remove the file on upgrade. You just have to put this snippet in the 3 relevant scripts (preinst, postinst, postrm):

if dpkg-maintscript-helper supports rm_conffile 2>/dev/null; then
    dpkg-maintscript-helper rm_conffile /etc/foo/conf.d/bar 1.2-1 -- "$@"
fi

You can avoid the preliminary test if you pre-depend on “dpkg (>= 1.15.7.2)” or if enough time has passed to assume that everybody has a newer version anyway. You can learn all the details in dpkg-maintscript-helper’s manual page.

Debhelper integration

debhelper makes it easy to inject those commands for you. You can provide debian/*.maintscript files. See dh_installdeb’s manual page for details.

I hope you found this article helpful. You can follow me on Identi.ca, Twitter and Facebook.

Additional Resources

Get the Debian Administrator's Handbook

After a successful liberation campaign, the Debian Administrator's Handbook is now freely available. If you appreciate my articles and what I do for Debian, check out the book and grab a copy.

Comments

  1. Why doesn’t dpkg Just Do the Right Thing for you and make this happen without maintainer script changes?

    • I’m not sure what’s the correct answer.

      On one side, dpkg is always on the safe side and let the maintainer do the more risky operation of dropping it when the maintainer knows that it the right thing to do. My title is a bit misleading in that regard, it’s not always the right way, but more often that not, it is.

      On the other side, it would probably make sense to optimize for the more common case. And when we introduced this dpkg-maintscript-helper interface, we were aware that it would let us make it a no-op once/if dpkg does the work by itself.

  2. When faced with this issue I found myself very annoyed by the fact that it has to be replicated 3 times. For the maintainance of a “data-configuration” package (usb-modeswitch-data for instance), maintaining three “osbolete files repositories” is really too error-prone. Hence #574443 . :-)

  3. http://wiki.debian.org/DpkgConffileHandling also explains the “old” way, for backporters or people otherwise concerned with supporting anything other than unstable. (Not a criticism, just an addition.)

    • In fact, dpkg-maintscript-helper was inspired by the snippets in this wiki page. dpkg-maintscript-helper has been recently introduced so it’s not suited for packages that must work in lenny (without a dpkg backport) but you can use it in squeeze (so it’s not limited to unstable either).

      $ grep -B 1 http://wiki /usr/bin/dpkg-maintscript-helper 
      # The conffile related functions are inspired by
      # http://wiki.debian.org/DpkgConffileHandling
      
  4. Teresa e Junior says:

    Hello, Raphaël!

    I couldn’t understand the implementation of this for the following: I have a package with data built from a set of scripts that goes to /etc/skel. There goes hundreds of files which change constantly. The user is bothered each upgrade about installing the maintainer version, but I want this to be silent only for this package.

    When using this code, the files are renamed to .dpkg-remove in the preinst, and then removed in the postinst. But the new files are not installed. They are kind of blacklisted so dpkg won’t install them..?

    I have implemented a simple loop which does the same: moves all old files to .dpkg-remove in the preinst, and then removes them in the postinst. The result is the same… Am I doing anything wrong? A `rm *’ doesn’t play well in this case.

    How could I remove the old files, and then install them without user prompting? Or make dpkg silent (like –force-confnew), but only for this package?

    Best regards!
    Thank you

    • If the user did not modify any file in /etc/skel, he should not be bothered.

      But as long as you keep those files as conffiles, then there’s no way to avoid dpkg doing its work. You might want to drop those files from debian/package/DEBIAN/conffiles so that they are no longer conffiles at all if that’s what you really want (beware that customizations made by users will be lost on each upgrade though). Tweaking that file should be done after the dh_installdeb call since that’s the program that will automatically mark files from /etc/ as conffiles.

      Or you can implement everything manually: provide the files in /usr/share/package/skel/ and copy what you want in the postinst when you want it. It’s up to you to ensure that you don’t loose any modification made by the user.

      • Teresa e Junior says:

        OK, you are right, considering the changes made by the user is a good point! I’ll probably have to change my scripts, since the files were autogenerated, and many obsolete files were being left also, it is good to implement your script for that.

        I’ve been bothered sometimes with the new conffile question even without changing anything in /etc/skel, but thinking better it’s probably because I build the package a few times before uploading it and (erroneusly?) don’t modify the version number until the upload. So the checksums are checked against the same version and found to be different.

        Thank you and best regards!