Capítulo 6 - Scripts del encargado de paquete y procedimiento de instalación

6.1 Introducción a los scripts del encargado de paquete

Es posible suministrar scripts como parte de un paquete, los cuales el sistema de gestión de paquetes ejecutará en tu nombre cuando tu paquete sea instalado, actualizado o eliminado.

Estos scripts son los ficheros preinst, postinst, prerm y postrm en el área de control del paquete. Deben ser ficheros ejecutables correctos; si son scripts (que es lo recomendado), deben comenzar con la convención usual #!. Deberían tener permisos de lectura y ejecución para cualquiera pero, cualquiera no debería tener permiso para su modificación.

El sistema de gestión de paquetes utiliza el valor del estado de terminación de estos scripts. Es importante que terminen con un valor de estado distinto de cero si hay un error, de tal forma que el sistema de gestión de paquetes pueda parar su procesamiento. Para shell scripts esto significa que casi siempre necesites utilizar set -e (esto es de hecho normalmente cierto cuando se escriben shell scripts). Es también importante, naturalmente, que no terminen con un valor de estado distinto de cero si todo ha ido bien.

Cuando un paquete es actualizado, se invoca durante el procedimiento de actualización a una combinación de scripts del antiguo y nuevo paquete. Si tu scripts van a ser muy complicados debes tener cuidado de ésto y, puedes necesitar comprobar los argumentos de tu script.

A groso modo preinst es invocado antes (una versión particular) de que un paquete sea instalado y, postinst es invocado después; prerm antes (una versión de) que un paquete sea eliminado y postrm después.

Los programas invocados por los scripts del encargado no deberían tener un camino de búsqueda antepuesto. Antes de que comience la instalación, el sistema de gestión de paquetes comprueba si los programas ldconfig, start-stop-daemon, install-info y update-rc.d pueden ser encontrados a través de la variable de entorno PATH. Aquellos programas y cualquier otro programa, que uno esperaría encontrar a través de la variable PATH, deberían ser invocados por lo tanto sin un camino de búsqueda absoluto. Los scripts del encargado tampoco deberían reinicializar la variable PATH aunque, podrían elegir modificarla anteponiendo o añadiendo directorios específicos del paquete. Estas consideraciones realmente se aplican a todos los shell scripts.

6.2 Idempotencia de los scripts del encargado

Es necesario para los procedimientos de recuperación a errores que los scripts sean idempotentes. Esto significa que si es ejecutado satisfactoriamente, y después es invocado de nuevo, no explote o cause ningún daño, sino sólo asegure que todo está como debería ser. Si la primera invocación falló, o fue abortada en la mitad por alguna razón, la segunda invocación debería simplemente hacer las cosas que fueron dejadas sin hacer la primera vez, si las hubiera, y terminar con un valor de estado de éxito si todo fue correcto.1

6.3 Terminal de control para los scripts del encargado

Se garantiza que los scripts del encargado se ejecutan con una terminal de control y que puedan interactuar con el usuario. Si necesitan solicitar una password, realizar interacción a toda pantalla o cosas similares, deberían hacer estas cosas escribiendo o leyendo de /dev/tty, dado que dpkg en algún punto redirigirá la entrada y salida estándar de los scripts para que pueda registrar el proceso de instalación. Asimismo, dado que estos scripts pueden ser ejecutados con la salida estándar redirigida a una tubería por motivos de registro, los perl scripts deberían inicializar la salida para no utilizar memoria intermedia por medio de inicializar $|=1, así que, la salida se imprime inmediatamente en vez de quedar almacenada en la memoria intermedia.

Cada script debería retornar un valor cero de estado de terminación en caso de éxito, o un valor distinto de cero en caso de fallo.

6.4 Resumen de las maneras en que son invocados los scripts del encargado

6.5 Detalles de la fase de desempaquetado en la instalación o actualización

El procedimiento en la instalación/actualización/sobre escritura/desaparición (esto es, cuando se ejecuta dpkg --unpack, o la etapa de desempaquetado de =dpkg --install=) es como sigue. En cada caso, si un error importante ocurre (a menos que sea relacionado a continuación) las acciones son, en general, ejecutar hacia atrás - esto significa que se ejecutan en orden inverso los scripts del encargado con otros argumentos. Estas son las invocaciones a "recuperación de errores" relacionadas a continuación.

    1. Si una versión del paquete está ya instalada, invocar
      • prerm-antiguo upgrade versión-nueva
    2. Si el script se ejecuta pero termina con un valor de estado distinto de cero, dpkg intentará
      • prerm-nuevo failed-upgrade versión-antigua
      • Recuperación de errores, para los dos casos anteriores:
        • postinst-antiguo abort-upgrade versión-nueva
  1. Si un paquete "conflictivo" se está eliminando al mismo tiempo:
    1. Si hay paquetes dependientes del paquete conflictivo y se ha especificado --auto-deconfigure, invocar, para cada uno de estos paquetes:
      • prerm-del-des-configurado deconfigure in-favour paquete-siendo-instalado versión removing versión-del-paquete-conflictivo
        • Recuperación de errores:
          • postinst-del-des-configurado abort-deconfigure in-favour paquete-siendo-instalado-pero-fallado versión removing versión-paquete-conflictivo Los paquetes des configurados son marcados como que requieren configuración, así que si se usa --install serán configurados de nuevo si es posible.
    2. Para preparar la eliminación del paquete conflictivo, invocar:
      • prerm-del-conflictivo remove in-favour paquete versión-nueva
        • Recuperación de errores:
          • prerm-del-conflictivo abort-remove in-favour paquete versión-nueva
    1. 1 Si el paquete esta siendo actualizado, invocar:
      • preinst-nuevo upgrade versión-antigua
    2. En otro caso, si el paquete tuvo algunos ficheros de configuración de una versión previa instalada (es decir, esta en el estado "solamente ficheros de configuración"):
      • preinst-nuevo install versión-antigua
    3. En otro caso (es decir, el paquete fue completamente purgado):
      • preinst-nuevo install
      • Recuperación de errores, respectivamente:
        • postrm-nuevo abort-upgrade versión-antigua postrm-nuevo abort-install versión-antigua postrm-nuevo abort-install
  2. Los ficheros del nuevo paquete están desempaquetados, sobre escribiendo cualquiera que pudiera estar ya en el sistema, por ejemplo cualquiera procedente de una versión antigua del mismo paquete o de otro. Se mantienen temporalmente copias de respaldo de los ficheros antiguos y, si algo va mal, el sistema de gestión de paquetes intentará restaurarlos como parte de la recuperación a errores.
    • Es un error para un paquete contener ficheros que estén en el sistema en otro paquete, a menos que sea utilizado Replaces (ver 'Sobre escribiendo ficheros y reemplazando paquetes - =Replaces' en la página XX). Es un error más serio para un paquete contener un fichero plano o otro clase distinta de un directorio donde otro paquete tiene un directorio (de nuevo, a menos que sea utilizado Replaces). Este error puede ser anulado, si se desea, utilizando --force-overwrite-dir, pero esto no es advertido. Los paquetes que se sobre escriben entre ellos ficheros provocan un comportamiento que, aunque determinístico, es difícil para el administrador del sistema comprender. Esto puede dar lugar fácilmente programas "perdidos" si, por ejemplo, un paquete es instalado sobre escribiendo un fichero de otro paquete, y es a continuación eliminado.2 Un directorio nunca será reemplazado por un enlace simbólico a un directorio o vice versa; en lugar de esto, será solamente mantenido el estado existente (enlace o no) y dpkg seguirá el enlace si lo hay.
    1. Si el paquete está siendo actualizado, invocar
      • postrm-antiguo upgrade versión-nueva
    2. Si falla esto, dpkg intentará:
      • postrm-nuevo failed-upgrade versión-antigua
      • Recuperación de errores, para ambos casos:
        • preinst-antiguo abort-upgrade versión-nueva Esto es un punto sin retorno- si dpkg llega hasta aquí, no retrocederá pasado este punto si ocurre un error. Esto dejará al paquete en un estado bastante malo, que requerirá una re-instalación satisfactoria para solucionarlo, aunque es cuando dpkg comienza ha hacer cosas que son irreversibles.
  3. Son eliminados los ficheros que estuvieran en la versión antigua de un paquete pero no en la nueva.
  4. La nueva lista de ficheros reemplaza a la antigua.
  5. Los nuevos scripts del encargado reemplazan a los antiguos.
  6. Todos los paquetes cuyos ficheros ha sido sobre escritos durante la instalación, y que no son requeridos por las dependencias, son considerados como si hubieran sido eliminados. Para cada uno estos paquetes
    1. dpkg invoca:
      • postrm-del-a-ser-eliminado disappear el-que-sobre-escribe versión-del-que-sobre-escribe
    2. Son eliminados los scripts del encargado del paquete.
    3. Se anota en la base de datos de estado como que está en un estado sano, esto es no instalado (los ficheros de configuración que pudiera tener son ignorados, mas que eliminados por dpkg). Nótese que a los paquetes a desaparecer no se les ha invocado su prerm, porque dpkg no conoce a priori que va a desparecer el paquete.
  1. Los ficheros en el paquete que estamos desempaquetando que también estén relacionados en las listas de ficheros de otros paquetes son eliminados de aquellas listas. (esto lobo tomiza la lista de ficheros del paquete en conflicto si lo hubiera).
  2. Las anteriormente mencionadas copias de respaldo de ficheros realizadas durante la instalación son borradas.
  3. El estado del nuevo paquete es ahora sano, y grabado como "desempaquetado".
    • Aquí hay otro punto sin retorno - si la eliminación del paquete en conflicto falla no haremos la recuperación del resto de la instalación; el paquete en conflicto es dejado en el limbo del eliminado a medias.
  4. Si hubiera un paquete en conflicto proseguimos y hacemos las acciones de eliminación (descritas anteriormente), comenzando con la eliminación de los ficheros del paquete en conflicto (cualquier fichero que también esté en el paquete siendo instalado ya ha sido eliminado de la lista de ficheros del paquete en conflicto y, así, no es eliminado ahora).

6.6 Detalles de la configuración

Cuando configuramos un paquete (esto ocurre con dpkg --install y dpkg --confiugure), primero actualizamos cualquiera de los conffiles y después invocamos:

postinst configure versión-configurada-más-recientemente

No se realiza ningún intento de recuperación después de errores durante la configuración.

dpkg pasará un argumento nulo si no hay una versión configurada más recientemente.3

6.7 Detalles de la eliminación y/o configuración del purgado

  1. prerm remove
  2. Son eliminados los ficheros del paquete (excepto los conffiles).
  3. postrm remove
  4. Son eliminados todos los scripts del encargado excepto postrm.
    • Si no estamos purgando el paquete paramos aquí. Nótese que los paquetes que no tienen postrm y ningún conffiles son automáticamente purgados cuando son eliminados, sin ninguna diferencia a excepción del estado de dpkg.
  5. Son eliminados los conffiles y cualquier fichero de respaldo (ficheros: ~*, #*#, %*, .dpkg-{old,new,tmp}, etc.).
  6. postrm purge
  7. Es eliminada la lista de ficheros del paquete.
    • Si hubiera problemas durante este proceso, invocamos postinst abort-remove No es realizado ningún otro intento de recuperación de errores durante la eliminación.