Un paquete Debian no es sólo un compendio de archivos a instalar. Forma parte de un todo más grande y describe su relación con otros paquetes Debian (requisitos, dependencias, conflictos, sugerencias). También provee scripts que permiten la ejecución de órdenes en diferentes etapas del ciclo de vida del paquete (instalación, actualización, eliminación). Estos datos utilizados por las herramientas de gestión de paquetes no son parte del software empaquetado, son lo que se denomina «metainformación» (información sobre otra información) dentro del paquete.
5.2.1. Descripción: el archivo control
Este archivo usa una estructura similar a las cabeceras de correo electrónico (según define
RFC 2822) y se describe con detalle en la Normativa de Debian y en las páginas de manual
deb-control(5) y
deb822(5).
Por ejemplo el archivo control
de apt se ve de la siguiente forma:
$
apt-cache show apt
Package: apt
Architecture: amd64
Version: 2.6.1
Priority: required
Section: admin
Maintainer: APT Development Team <deity@lists.debian.org>
Installed-Size: 4232
Provides: apt-transport-https (= 2.6.1)
Depends: adduser, gpgv | gpgv2 | gpgv1, libapt-pkg6.0 (>= 2.6.1), debian-archive-keyring, libc6 (>= 2.34), libgcc-s1 (>= 3.0), libgnutls30 (>= 3.7.5), libseccomp2 (>= 2.4.2), libstdc++6 (>= 11), libsystemd0
Recommends: ca-certificates
Suggests: apt-doc, aptitude | synaptic | wajig, dpkg-dev (>= 1.17.2), gnupg | gnupg2 | gnupg1, powermgmt-base
Breaks: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~), aptitude (<< 0.8.10)
Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~)
Filename: pool/main/a/apt/apt_2.6.1_amd64.deb
Size: 1372852
MD5sum: 2a077296b9b9f4f8b027f86d9500192b
SHA256: 6ea03cbbc7a7bfcee601c9fb08d4e026fd522ede5350561f06867ad9c0a0fa6b
Description-en: commandline package manager
This package provides commandline tools for searching and
managing as well as querying information about packages
as a low-level access to all features of the libapt-pkg library.
.
These include:
* apt-get for retrieval of packages and information about them
from authenticated sources and for installation, upgrade and
removal of packages together with their dependencies
* apt-cache for querying available information about installed
as well as installable packages
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys
Description-md5: 9fb97a88cb7383934ef963352b53b4a7
Tag: admin::package-management, devel::lang:ruby, hardware::storage,
hardware::storage:cd, implemented-in::c++, implemented-in::perl,
implemented-in::ruby, interface::commandline, network::client,
protocol::ftp, protocol::http, protocol::ipv6, role::program,
scope::application, scope::utility, suite::debian, use::downloading,
use::organizing, use::playing, use::searching, works-with-format::html,
works-with::audio, works-with::software:package, works-with::text
Echemos una cuidadosa mirada al propósito de algunos de los campos enumerados en el comando anterior.
5.2.1.1. Dependencias: el campo Depends
Las dependencias están definidas en el campo Depends
en la cabecera del paquete. Es una lista de condiciones a cumplir para que el paquete funcione correctamente. Las herramientas como apt
utilizan esta información para instalar las bibliotecas, herramientas, controladores, etc. necesarios, en versiones apropiadas para satisfacer las dependencias del paquete a instalar. Para cada dependencia es posible restringir el rango de versiones que cumplen dicha condición. En otras palabras, es posible expresar el hecho de que necesitamos el paquete libc6 en una versión igual o mayor a «2.15» (escrito como «libc6 (>= 2.15)
». Los operadores de comparación de versiones son los siguientes:
En una lista de condiciones a cumplir, la coma sirve como separador. Debe interpretarse como un «and» lógico. En las condiciones una barra vertical («|») expresa un «or» lógica (es un «or» inclusivo, no un exclusivo “either/or”). Tiene más prioridad que «and» y se puede usar tantas veces como sea necesario. Por lo tanto, la dependencia «(A o B) y C» se escribe
A | B, C
. Por otro lado, la expresión «A o (B y C)» debe escribirse «(A o B) y (A o C)» ya que el campo
Depends
no permite paréntesis que cambien el orden de las prioridades entre los operadores lógicos «or» e «and». Por lo tanto, se escribiría
A | B, A | C
.
El sistema de dependencias es un buen mecanismo para garantizar el funcionamiento de un programa, pero tiene otro uso con los «metapaquetes». Éstos son paquetes vacíos que sólo describen dependencias. Facilitan la instalación de un grupo consistente de programas preseleccionados por el desarrollador del metapaquete; como tal apt install meta-package
instalará automáticamente todos estos programas utilizando las dependencias del metapaquete. Los paquetes gnome, kde-full y linux-image-amd64, por ejemplo, son metapaquetes.
5.2.1.2. Conflictos: el campo Conflicts
El campo Conflicts
indica que un paquete no puede instalarse simultáneamente con otro. La razón más común es que ambos paquetes contienen un archivo con el mismo nombre y ruta, proveen el mismo servicio en el mismo puerto TCP o estorban el funcionamiento del otro.
dpkg
se negará a instalar un paquete si genera un conflicto con un paquete ya instalado, excepto si el nuevo paquete especifica que «reemplazará» al paquete instalado en cuyo caso dpkg
elegirá reemplazar el paquete existente con el nuevo. apt
siempre seguirá sus instrucciones: si desea instalar un nuevo paquete ofrecerá automáticamente desinstalar el paquete que genera problemas.
5.2.1.3. Incompatibilidades: el campo Breaks
El campo Breaks
tiene un efecto similar al del campo Conflicts
pero con un significado especial. Indica que la instalación de un paquete «romperá» otro paquete (o versiones particulares del mismo). En general, esta incompatibilidad entre dos paquetes es temporal y la relación Breaks
se refiere específicamente a las versiones incompatibles.
dpkg
se negará a instalar un paquete que rompe un paquete ya instalado y apt
intentará resolver el problema actualizando a una nueva versión el paquete que se rompería (que se asume estaría arreglado y, por lo tanto, sería compatible nuevamente).
Este tipo de situaciones pueden ocurrir en casos de actualizaciones que no sean compatibles con versiones anteriores: este es el caso si una nueva versión ya no funciona con la versión anterior y causa un mal funcionamiento en otros programas si no se toman medidas especiales. El campo Breaks
previene que el usuario se tope con estos problemas.
5.2.1.4. Elementos provistos: el campo Provides
Este campo introduce el concepto interesante de un «paquete virtual». Tiene muchos roles pero hay dos particularmente importantes. El primero consiste en utilizar un paquete virtual para asociar un servicio genérico con él (el paquete «provee» el servicio). El segundo indica que un paquete reemplaza completamente a otro y, para esos propósitos, también puede satisfacer las dependencias que otros satisfacen. Es posible, entonces, crear un paquete substituto sin tener que utilizar el mismo nombre de paquete.
5.2.1.4.1. Proveyendo un «servicio»
Discutamos con más detalles el primer caso con un ejemplo: se dice que todos los servicios de correo, como postfix o sendmail «proveen» el paquete virtual mail-transport-agent. Por lo tanto, cualquier paquete que necesite este servicio para funcionar (por ejemplo, un gestor de listas de correo como smartlist o sympa) simplemente indican en sus dependencias que requieren de mail-transport-agent en lugar de especificar una lista larga y aún incompleta de posibles soluciones (por ejemplo postfix | sendmail | exim4 | ...
). Lo que es más, es inútil instalar dos servidores de correo en el mismo equipo, por lo que cada uno de estos paquetes declara un conflicto con el paquete virtual mail-transport-agent. Un conflicto de un paquete con sí mismo es ignorado por el sistema, pero esta técnica prohibirá la instalación de dos servidores de correo simultáneamente.
5.2.1.4.2. Intercambio con otro paquete
The Provides
field is also interesting when the content of a package is included in a larger package. For example, the libdigest-md5-perl Perl module was an optional module in Perl 5.6, and has been integrated as standard in Perl 5.8 (and later versions, such as 5.36.0 present in Bookworm). As such, the package perl has since version 5.8 declared Provides: libdigest-md5-perl
so that the dependencies on this package are met if the user has Perl 5.8 (or newer). The libdigest-md5-perl package itself has eventually been deleted, since it no longer had any purpose when old Perl versions were removed.
Esta funcionalidad es muy útil ya que nunca es posible anticipar los caprichos del desarrollo y es necesario que sea posible adaptarse a cambios de nombre y otros reemplazos automáticos de software obsoleto.
5.2.1.4.3. Limitaciones anteriores
Los paquetes virtuales solían sufrir algunas limitaciones, la más importante de ellas era la ausencia de un número de versión. Volviendo al ejemplo anterior, una dependencia como Depends: libdigest-md5-perl (>= 1.6)
nunca se considerará como satisfecha por el sistema de empaquetado aún en presencia de Perl 5.10 — aunque de hecho es más que probable que esté satisfecha. Desconociendo esto el sistema de paquetes selecciona la opción menos arriesgada, asumiendo que las versiones no coinciden.
This limitation has been lifted in dpkg 1.17.11, and is no longer relevant. Packages, like perl 5.36.0, can assign a version to the virtual packages they provide, such as Provides: libdigest-md5-perl (= 2.58)
, and thus allow other packages to use versioned dependencies.
5.2.1.5. Reemplazo de archivos: el campo Replaces
El campo Replaces
indica que el paquete contiene archivos que también están presentes en otro paquete, pero que el paquete tiene derecho legítimo a reemplazarlos. Sin esta especificación, dpkg
falla al instalar el paquete, indicando que no puede sobrescribir los archivos de otro paquete (técnicamente, es posible forzarlo a hacerlo con la opción --force-overwrite
, pero no se considera una operación estándar). Esto permite identificar problemas potenciales y requiere que el mantenedor estudie el asunto antes de elegir si añadir o no dicho campo.
El uso de este campo está justificado cuando cambian los nombres de los paquetes o cuando un paquete está incluido en otro. Esto sucede cuando el desarrollador decide distribuir los archivos de otra forma entre los varios paquetes binarios producidos del mismo paquete fuente: un archivo reemplazado no le corresponde al paquete antiguo, sólo al nuevo.
Si todos los archivos de un paquete instalado fueron reemplazados, se considera que se eliminó el paquete. Finalmente, este campo incita que dpkg
elimie los paquetes reemplazados en casos de conflictos.
5.2.2. Scripts de configuración
Además del archivo
control
, el compendio
control.tar.gz
de cada paquete Debian puede contener una cantidad de scripts que serán ejecutados por
dpkg
en diferentes etapas del procesamiento de un paquete. La Normativa Debian describe los casos posibles
en detalle, especificando los scripts que serán llamados y los argumentos que recibirán. Estas secuencias pueden ser complicadas ya que si falla uno de los scripts
dpkg
intentará volver a un estado satisfactorio cancelando la instalación o eliminación en curso (siempre que sea posible).
En general, se ejecuta el script preinst
antes de la instalación del paquete, y postinst
luego. De la misma forma, se invoca prerm
antes de la eliminación de un paquete y postrm
luego. Actualizar un paquete es equivalente a eliminar la versión anterior e instalar la nueva. No es posible describir en detalle todos los escenarios posibles aquí, pero discutiremos los dos más comunes: instalación/actualización y eliminación.
5.2.2.1. Instalación y actualización
Durante la instalación inicial y para cada actualización de un paquete, dpkg
llama a los llamados scripts de mantenimiento, como prerm
o scripts preinst
. Estos scripts pueden realizar acciones adicionales durante las diferentes etapas del ciclo de vida de un paquete. Los nombres de los scripts precedidos por new-
son los scripts de la nueva versión de un paquete que se está instalando o actualizando. Los nombres de los scripts precedidos por old-
son los scripts de la versión antigua de un paquete desde el que se está actualizando.
Durante cada invocación, dpkg
pasará ciertos argumentos a cada script, como upgrade nueva-versión
. El script invocado puede entonces manejar los argumentos y realizar una acción particular, o ignorar los argumentos y regresar con un código de salida de 0
, si no es necesario hacer nada durante ese paso. En la práctica, muchos paquetes no necesitarán realizar una acción durante cada paso del ciclo de vida. Por lo tanto, un script de configuración típico buscará un argumento en particular e ignorará todos los demás, regresando implícitamente con el código de salida 0
.
Esto es lo que sucede durante una instalación (o actualización). Los argumentos old-version, new-version y last-version-configured son marcadores de posición para los números de versión reales (antiguos y nuevos) del paquete:
Para una actualización, dpkg
llama al script old-prerm
y pasa upgrade new-version
como argumentos.
Aún para una actualización, dpkg
luego ejecuta el script new-preinst
con los argumentos upgrade old-version
; para la instalación inicial, ejecuta el script new-preinst
y pasa install
como argumento. Se puede agregar la versión anterior en el último parámetro, si el paquete ya se instaló y eliminó desde entonces (pero no se eliminó y, por lo tanto, se conservaron los archivos de configuración).
Se descomprimen los archivos del nuevo paquete. Si un archivo ya existe, es reemplazado pero se guarda una copia de respaldo de forma temporal.
Para una actualización, dpkg
ejecutar el script old-postrm
y pasar upgrade new-version
como argumentos.
dpkg
actualiza toda su información interna (lista de archivos, scripts de configuración, etc.) y elimina los respaldos de los archivos reemplazados. Este es el punto sin retorno: dpkg
ya no tiene acceso a todos los elementos necesarios para volver al estado anterior.
Finalmente, dpkg
configura el paquete ejecutando el script new-postinst
con los argumentos configure last-version-configured
.
5.2.2.2. Eliminación de un paquete
Los pasos para eliminar un paquete son análogos a los pasos de instalación. La principal diferencia es que los scripts de eliminación del paquete se llaman:
dpkg
llama al script prerm
y pasa el argumento remove
.
dpkg
elimina todos los archivos del paquete, con la excepción de los archivos de configuración y scripts de mantenimiento.
dpkg
ejecuta el script postrm
y pasa remove
como argumento. Después, se eliminan todos los scripts de mantenimiento, excepto el script postrm
. Si el usuario no ha utilizado la opción “purgar”, el proceso se detiene aquí.
Para eliminar completamente un paquete (con la orden dpkg --purge
o dpkg -P
), los archivos de configuración también son eliminados junto con una cantidad de copias (*.dpkg-tmp
, *.dpkg-old
, *.dpkg-new
) y archivos temporales; dpkg
luego ejecuta el script postrm
y pasa purge
como argumento.
Los cuatro scripts que aparecen detallados anteriormente se complementan con un script config
provisto por los paquetes que utilizan debconf
para adquirir información de configuración del usuario. Durante la instalación este script define en detalle las preguntas realizadas por debconf
. Se graban las respuestas en la base de datos de debconf
para futuras referencias. Generalmente apt
ejecuta el script antes de instalar los paquetes uno por uno para agrupar las preguntas y realizarlas todas al usuario al comienzo del proceso. Los scripts de pre y postinstalación pueden utilizar esta información para operar según los deseos del usuario.
5.2.3. Sumas de comprobación, lista de archivos de configuración y más.
Además de los scripts de mantenimiento y los datos de control ya mencionados en las secciones anteriores, el archivo control.tar.gz
de un paquete Debian puede contener otros archivos interesantes.
El primer,
md5sums
contiene una lista sumas de verificación MD5 de todos los archivos del paquete. Su principal ventaja es que permite que
dpkg-verify
(que estudiaremos en
Sección 14.3.4.1, “Auditoría de paquetes mediante dpkg --verify
”) y
debsums
(del paquete homónimo; ver
Sección 14.3.4.2, “Auditoría de paquetes: debsums
y sus límites”) chequear si estos archivos fueron modificados desde su instalación. Tener en cuenta que cuando este archivo no existe, lo que podría ser el caso de algunos paquetes más antiguos,
dpkg
lo generará dinámicamente en el momento de la instalación (y lo almacenará en la base de datos dpkg como otros archivos de control).
conffiles
enumera los archivos del paquete que tienen que administrarse como archivos de configuración (ver también deb-conffiles(5)). El administrador puede modificar los archivos de configuración y dpkg
intentará preservar estos cambios durante la actualización de un paquete.
De hecho, en esta situación, dpkg
se comporta tan inteligentemente como le es posible: si el archivo de configuración estándar no fue modificado entre dos versiones, no hace nada. Si, sin embargo, el archivo cambió intentará actualizar este archivo. Son posibles dos casos: o bien el administrador no modificó el archivo, en cuyo caso dpkg
automáticamente instalará la nueva versión; o el archivo fue modificado, en cuyo caso dpkg
le preguntará al administrador qué versión desea utilizar (la antigua con modificaciones o la nueva provista con el paquete). Para asistirlo en esta decisión dpkg
ofrece mostrar las diferencias entre las dos versiones («diff
»). Si el usuario decide mantener la versión anterior, la nueva será almacenada en la misma ubicación con el sufijo .dpkg-dist
. Si el usuario selecciona la nueva versión, se mantiene la versión anterior en la misma ubicación con el sufijo .dpkg-old
. Otra acción posible consiste en interrumpir momentáneamente dpkg
para editar el archivo e intentar rehacer las modificaciones relevantes (identificadas previamente con diff
).
Con frecuencia, el archivo de control también contiene otros archivos, como triggers
, shlibs
o symbols
. Estos ficheros están bien descritos en deb-triggers(5), deb-shlibs(5) y deb-symbols(5).
Triggers were introduced to reduce the amount of duplicated events during package installation, such as file registration or catalog/database update tasks. Packages can define their own or activate defined triggers. A more comprehensive documentation can be found in
/usr/share/doc/dpkg/triggers.txt.gz
.
El sistema shlibs
es una alternativa más antigua y sencilla al sistema symbols
para declarar dependencias para bibliotecas compartidas. Define el nombre del paquete y la versión en la que encontrar una versión SONAME específica de una biblioteca compartida. El sistema symbols
más nuevo permite definir la dependencia mediante el seguimiento de los símbolos y cuándo se han introducido o modificado en la biblioteca.