Añadiendo funciones LSB a un script de inicio …

debian-logo… de un servicio aprendo varias cosas sobre cómo está organizado.

En uno de los programas que ya he mencionado en otra entrada, un servidor de trabajos de impresión, tengo un script para utilizarlo como servicio del sistema mediante el programa init y notaba que le faltaba algo de integración. No tenía buen aspecto cuando se lanzaba con el resto y he creído conveniente incluirle algunas funciones que he visto disponibles en el paquete lsb-base.

Primero he mirado la documentación presente en el wiki de Debian pero no mencionan directamente nada sobre las funciones a emplear en los scripts. Sí que enlazan con la propia normativa y con la página de la Fundación Linux Base de donde voy extrayendo fragmentos hasta encontrar  lo que quiero: Init Script Functions. Sin embargo no está completamente actualizada y tengo que recurrir al archivo /usr/share/doc/lsb-base/README-Debian.gz donde sí, ahí están todas. La leche, sí que ha costado.

El  texto ya avisa de que emplear las funciones LSB puras no cumple la normativa Debian y no es aconsejable excepto si se pretende portar el script a otras distribuciones o en paquetes LSB estrictos. Así pues voy a anotar primero las funciones LSB y luego hablaré de la versión Debian.

Para que el script de control del servicio presente los mensajes cumpliendo con la normativa LSB debemos emplear las siguientes funciones cargadas directamente de /lib/lsb/init-functions:

  • log_success_msg: para notificar buenas noticias o informaciones sin trascendencia en el proceso.
  • log_failure_msg: para dar las malas noticias, especialmente fallos críticos.
  • log_warning_msg: para avisar de sucesos o resultados no críticos.

El mensaje que aceptan como parámetro no tiene un formato definido. Todas ellas, además, pueden estar enviando los textos a archivos de registro previamente establecidos pero que la norma no define exactamente.

Así que un fragmento de arranque del servicio puede quedar más o menos así:

case $1 in
"start")
log_success_msg "Starting daemon impresor"
start_daemon -p $PIDFILE $DAEMON $DAEMON_ARGS
if [ $? -eq 0 ]; then
log_success_msg " done"
else
log_failure_msg " fail"
fi
;;

Mencionar también las otras tres funciones básicas que ya de por sí encaminan la disposición del resto de los elementos que empleará el servidor:

  • start_daemon: para arrancar el servicio
  • kill_proc: para detenerlo
  • pidofproc: para obtener el PID del servicio

Las funciones Debian

La gente de Debian, intentando dar coherencia al arranque del sistema, han ampliado estas funciones y las han dotado de algunas características interesantes tales como

  • Texto coloreado: disponible mediante la variable de entorno FANCYTTY.
  • Puntos de anclaje para añadir o cambiar características al mecanismo de registro:
    • Bien empleando la carga de archivos desde el directorio /lib/lsb/init-functions.d.
    • Bien cargando el archivo /etc/lsb-base-logging.sh donde se pueden redefinir también funciones.

Las funciones de registro son entonces:

  • Arranque y parada de servicios:
    • log_daemon_msg message daemon_name : para notificar el inicio de un proceso demonio.
    • log_progress_msg daemon2_name : para notificar el inicio de un segundo proceso.
    • log_end_msg 0/1 : para notificar el éxito o el fallo de lo anterior.
    • status_of_proc [-p pidfile] pathname daemon_name : Registra el estado de un proceso y puede emplearse para realizar la acción status del script.
  • Acciones que no toman tiempo como la asignación de valores:
    • log_action_msg message
  • Acciones que sí toman tiempo y constan de varias fases:
    • log_action_begin_msg message
    • log_action_cont_msg message
    • log_action_end_msg 0|1 [message] : que indica que la acción anterior ha terminado correctamente (0) o ha fallado de alguna manera (1).

Con toda esta información he mejorado el script de inicio del servicio empleando estas funciones, tras añadir a lsb-base como dependencia del paquete:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          impresor
# Required-Start:    $network $local_fs $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Trabajos de impresión de Multibase
# Description:       Servidor de trabajos de impresión de 
#                    aplicaciones Multibase
### END INIT INFO

# Author: Victor Moral 

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Trabajos de impresión de Multibase"
NAME=impresor    
DAEMON=/usr/sbin/impresor 
DAEMON_ARGS=""            
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x $DAEMON ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Lock the start process
[ "$START" != "yes" ] && exit 0

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

case "$1" in
  start)
    log_daemon_msg "Starting $DESC" "$NAME"
    start_daemon -p $PIDFILE $DAEMON $DAEMON_ARGS
    log_end_msg $?
  ;;

  stop)
	log_daemon_msg "Stopping $DESC" "$NAME"
	killproc -p $PIDFILE $DAEMON
    log_end_msg $?
    ;;

  status)
       status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
       ;;

  restart|force-reload)
	log_daemon_msg "Restarting $DESC" "$NAME"
    killproc -p $PIDFILE $DAEMON
    start_daemon -p $PIDFILE $DAEMON $DAEMON_ARGS
    log_end_msg $?
	;;

  *)
	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
	exit 3
	;;
esac

:

A pesar de estar utilizando el módulo Perl Net::Server -con sus excentricidades- la interacción de mi proceso demonio está ahora plenamente integrada en el arranque Debian.

Reflexionando

Y ahora viene donde la matan … ¿ es que Debian no incluye nada como base ? Sí, obviamente lo hace. El archivo /etc/init.d/skeleton está prácticamente listo para usarse con sólo cambiar las variables que nombran, sitúan y describen el demonio.

Es fácil de usar y está documentado y lo único que echo en falta -y es algo a lo que me he acostumbrado- es a verificar el valor de la variable START o START_DAEMON para lanzar o no el proceso. Supongo, y digo supongo porque bucear en las listas de Debian se me hace eterno, que si han decidido eso es porque puede que se intente detener el proceso cuando borras el paquete (lo normal, vamos) y que te encuentres con que falta la variable o el archivo y el servicio se quede ahí, sólo y pelado sin nada debajo.

De todas maneras yo prefiero emplearlo. Aunque no lo he escrito así en el ejemplo de arriba la versión final de mi script incorpora la siguiente estrofa:

if [ "$START" != "yes" ]; then 
log_warning_msg "Cancel process of $NAME edit /etc/default/$NAME"
exit 0
fi

de manera que el administrador del sistema (conocido como yo mismo) pueda entender mejor por qué no está funcionando el servicio.