Paquetes de configuración Debian: …

debian-logo… no son tan complicados y es un tema ya resuelto. Menos mal.

En los paquetes Debian que creo en mi lugar de trabajo me he ido encontrando con un problema menor que se está desmadrando: los archivos de configuración de otros paquetes.

Resulta que construyo un paquete para configurar, pongamos, las estadísticas web en la intranet de la empresa. Necesito que se instale awstats con una configuración muy concreta y varios otros archivos auxiliares más. Lo primero es muy sencillo: declaro awstats como dependencia de mi paquete y listo. Lo segundo es más complicado. Si defino en mi paquete el archivo de configuración de awstats como propio, para incluir mi versión, dpkg no me dejará instalarlo porque el archivo en cuestión, /etc/awstats/awstats.conf.local, pertenece a awstats y no hay más que negociar. Sí, se puede forzar la instalación hasta cierto punto, pero no es eso precisamente lo que quiero.

La primera vez que me encontré con este problema lo vi fácil. Añado mi versión del archivo a un directorio de datos del paquete como /usr/share/empresa/config/awstats.conf.local y en la configuración lo sobrescribo por las bravas. Problema resuelto y problema añadido: cada vez que mi paquete se actualice instalará la versión incluída por las bravas. ¿ Y las posibles modificaciones locales ? Ignoradas y arrasadas. Debian no me preguntará nunca si quiero conservar la versión modificada localmente porque no sabrá que mi paquete realiza tal modificación.

No, así tampoco. Ingenié y añadí un mecanismo rápido y sucio para paliar ésto en lo posible: en la construcción del paquete calcularía una suma MD5 del archivo de configuración que incluiría en el paquete y que serviría para que en la configuración del mismo ignorase su instalación si coincidía con ella. Y bueno, está funcionando pero no resulta muy cómodo, ni de mantener ni de usar.

Estaba seguro de que tenía que existir otra forma de hacerlo en Debian. No podía ser el único que se encontrase con un problema similar. Hace unos días me puse en serio a buscarlo y terminé por encontrar en el paquete config-package-dev la solución que creo más civilizada.

Paquetes de configuración en Debian

config-package-dev es una extensión de Debhelper y un juego de módulos de CDBS. Está diseñado para indicar formalmente en un paquete que operaciones realiza con archivos de configuración de terceros, de manera que sea cómodo instalar tu propia versión, modificarla localmente y eliminarla -recuperando la original- cuando el paquete sea borrado. Emplea, como no podía ser otra cosa, el sistema de desvío de componentes de Debian llamado dpkg-diversion.

Define tres operaciones a realizar con los archivos de configuración de terceros:

  1. displace (desplazar) para mover un archivo y emplear un reemplazo.
  2. transform (transformar) que consiste en reutilizar el contenido tras modificarlo.
  3. hide (ocultar) que desvía el archivo de manera que desaparece del sistema.

Para utilizarlo es necesario seguir estos pasos:

  1. Declarar el paquete config-package-dev como dependencia en la construcción de nuestro paquete.
  2. Añadir las directivas Provides y Conflicts correspondientes (se pueden ver más abajo)
  3. Indicar al programa dh que lo tenga en cuenta.
  4. Declarar los archivos y las operaciones dentro del propio directorio debian/ .

El primer punto se resuelve con la siguiente estrofa en el archivo debian/control:

Source: empresa-sigfrido
Section: admin
Priority: extra
Maintainer: Victor Moral <victor@empresa.net>
Build-Depends: debhelper (>= 8.0.0), config-package-dev (>= 5.0)
Standards-Version: 3.9.4

Package: empresa-sigfrido
Architecture: all
Provides: ${diverted-files}
Conflicts: ${diverted-files}
Pre-Depends: empresa-common, rsync
Depends: ${shlibs:Depends}, ${misc:Depends}, awstats, impresor

El segundo consiste en añadir ésto a debian/rules:

#!/usr/bin/make -f

# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1

%:
    dh $@ --with=config-package

y es el tercer punto el que merece alguna explicación más detallada.

Declarando archivos de configuración

Para declarar los archivos de configuración y su manipulación final en el paquete que estamos construyendo necesitamos trabajar sobre el directorio debian/ . En él vamos a inscribir los archivos con la nomenclatura paquete.operacion de tal manera que los archivos que vayan a ser desplazados estarán declarados en paquete.displace, los que sufrirán algún cambio en paquete.transform y los que serán ocultados en paquete.hide. Hay otras variantes y para eso es mejor consultar la documentación directamente.

El funcionamiento de config-package-dev consiste en añadir unas cuantas funciones bash al final de nuestros scripts postinstpostrm y demás que realizarán las operaciones con estos archivos en la parte final de la instalación. Ojo, estas funciones se añadirán a los scripts en la línea que reza #: no hay línea, no hay funciones.

Ahora bien, ¿ qué es lo que tengo que hacer para que mis archivos de configuración reemplacen ordenadamente a los del paquete que voy a configurar ? Pues depende de lo que pretendamos que sea el resultado final, pero antes conviene aclarar qué es el término extensión que menciono más adelante.

Los scripts del paquete config-package-dev utilizan una extensión en los archivos con los que trabajan que se obtiene del nombre del paquete. De forma predeterminada se toma la primera palabra antes de un guión y puede cambiarse pero si lo hacemos no termina de funcionar en las posteriores actualizaciones. Por ejemplo en el paquete empresa-printers-pdf la extensión que se utiliza es empresa.

Ahora veamos los pasos a seguir:

  1. Para reemplazar el archivo de configuración con nuestra versión:
    1. Creamos nuestra copia del archivo con una extensión que hemos mencionado en el ejemplo para distinguirlos del original: /etc/awstats/awstats.local.empresa.
    2. Lo añadimos a debian/paquete.install porque config-package-dev no instala por su cuenta y es necesario hacerlo aparte.
    3. Registramos el archivo en debian/paquete.displace en una única línea y con la ruta final en el sistema instalado. Por ejemplo /etc/awstats/awstats.local.empresa. Si, lo declaramos también con la extensión, ya que config-package-dev creará un enlace con el nombre sin dicha extensión y todo quedará igual.
  2. Para parchear el archivo de configuración original bastará con declararlo en el archivo debian/paquete.transform junto con el procedimiento de modificación. Más abajo amplío ésto.
  3. Para ocultar el archivo de configuración original éste tendrá que ir en debian/paquete.hide y la instalación se encargará de situarlo en un directorio del sistema para que no se pierda su pista (esto es, ya no estará bajo /etc).

Sustituyendo la configuración original

Tomando como ejemplo el de /etc/awstats/awstats.local tras la instalación correcta de nuestro paquete tendremos lo siguiente:

/etc/awstats/awstats.local -> /etc/awstasts/awstats.local.empresa
/etc/awstats/awstats.local.empresa
/etc/awstats/awstats.local-orig

El archivo original se conservará en la versión con extensión -orig y se creará un enlace al nuestro empleando dpkg-divert por lo que se puede emplear también en archivos binarios sin preocuparse de posteriores actualizaciones del paquete original.

Parcheando archivos de configuración

En el caso de los archivos resultantes de una transformación de otros (debian/paquete.transform) la síntaxis del archivo es un poco diferente.

Además de indicar la ruta final como en el archivo debian/paquete.displace debemos añadir el método para obtener el resultado final en forma de una orden simple de un shell o haciendo referencia a un ejecutable que creamos también en el directorio debian/.

Esto significa que nuestro paquete no llevará consigo los archivos de configuración ya que su contenido se obtiene de la aplicación del filtro que le hemos indicado, con su entrada estándar redirigida desde el archivo original y su salida estándar hacia el archivo con el nombre que hemos indicado. Después, una vez que el filtro termina sin fallos, realiza las mismas operaciones que con los archivos desplazados de debian/paquete.displace.

Como ejemplo voy a poner el del paquete cups-pdf en el caso de que quisiéramos añadir un filtro de posprocesado tal y como se indica en la configuración. La directiva utilizada es PostProcessing por lo que la línea debería quedar así

PostProcessing /usr/bin/myfilter

y para ello podemos emplear lo siguiente en el archivo debian/package.transform

/etc/cups/cups-pdf.conf.empresa perl -0pe 's|PostProcessing|PostProcessing /usr/bin/myfilter|g or die '

Esto es, le indicamos que tomando como fuente nuestra versión de cups-pdf.conf  la procese mediante un filtro Perl y lo que se obtenga se use como contenido de cups-pdf.conf. Bueno, en realidad será el contenido de cups-pdf.conf.empresa que luego se utilizará para sustituir al original. Primero se transforma el archivo y luego se sustituye de forma segura.

Ocultando archivos

En ocasiones necesitamos que un archivo de configuración desaparezca tras la instalación. ¿ Por qué ? El ejemplo del directorio /etc/cron.d es adecuado porque se utilizan todos los archivos que se encuentren en él sin excluirlos por la extensión o por algún indicativo en el nombre.

Otra posibilidad son aquellos programas que sólo actúan si tienen configuración en un archivo; no son muchos pero algunos me he encontrado. Es más, he construído algunos basándose en eso como el programa de copias de seguridad con attic.

Así que dado que los archivos tienen que desaparecer config-package-dev lo mueve ordenadamente (con dpkg-divert) al directorio /usr/share/package (siendo package el nombre de tu paquete no uno genérico) sustituyendo los carácteres de separación de directorios (la barra) con una secuencia de signos más (++).