vracbazar.free.fr v2

VracBazar

Créer une version "safe" d'un service

Besoin de lancer un service de façon "safe", c'est-à-dire avec une capacité de redémarrage automatique en cas de crash ?

Exemple d'implémentation

La fonction "safe" proposée ici est greffée sur un script init.d assez standard. Elle a cependant un prérequis : qu'un fichier "pid file" soit créé par le service lancé (ou par le script de lancement).

Pour les besoins de l'exemple, le script créé un simili-service, en l'occurrence un script bash /usr/local/sbin/aserviced dont la seule capacité consiste à tenir à jour un fichier /tmp/aserviced.log.

Le code ajouté au titre de la fonction "safe" est mis en évidence en bleu.
Le code spécifique de création du simili-service est en gris.



Fichier /etc/rc.d/init.d/aservice

#!/bin/bash
#
# aservice    Init script for a service
#
# chkconfig: - 99 01
#
# description:    A service
# processname: aservice
# config: /etc/aservice.conf
# pidfile: /var/run/aservice.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Service name
SERVICE=$(basename $0)
# Path to the actual executable
BINARY=/usr/local/sbin/aserviced

# Process name
PROCESS=$(basename $BINARY)
# Lock file name
LOCKFILE=/var/lock/subsys/$SERVICE
# Process pid file name. Should be created by the executable, otherwise by start() function
PIDFILE=/var/run/$SERVICE.pid

# Safe process pid file name. Created by safe() function
PIDFILE_SAFE=/var/run/$SERVICE-safe.pid

RETVAL=0

# Functions

safe() {
    # One safe process is enough
    [ -f $PIDFILE_SAFE ] && [ -d /proc/$(cat $PIDFILE_SAFE) ] && exit
    safe_eye &
    # Store safe process pid
    echo $! >$PIDFILE_SAFE
    RETVAL=0
}

safe_eye() {
    # Safe process. Simple enough not to crash. Never ends unless killed.
    SAFE_PERIOD=5
    SAFE_RESTART=10
    # Keep an eye on service and restart when dead
    while true; do
        sleep $SAFE_PERIOD
        if [ -f $PIDFILE ] && [ ! -d /proc/$(cat $PIDFILE) ]; then
            LOG="Crash detected! re-start $SERVICE"
            echo $LOG >&2
            logger -p daemon.warn -t $SERVICE $LOG
            service $SERVICE start
            sleep $SAFE_RESTART
        fi
    done
}

start() {
    # Clean obsolete pid file
    [ -f $PIDFILE ] && [ ! -d /proc/$(cat $PIDFILE) ] && rm -f $PIDFILE

    echo -n "Starting $SERVICE: "
    daemon --pidfile $PIDFILE $BINARY
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch $LOCKFILE

    # Start safe service
    service aservice safe
}

stop() {
    # Stop safe service
    [ -f $PIDFILE_SAFE ] && PID_SAFE=$(cat $PIDFILE_SAFE) && rm -f $PIDFILE_SAFE && kill $PID_SAFE

    echo -n "Stopping $SERVICE: "
    killproc -p $PIDFILE $PROCESS
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f $LOCKFILE
}

restart() {
    stop
    start
}

########################################
# For demo only: create a dummy service
if [ ! -x $BINARY ]; then
    cat >$BINARY <<EOT
#! /bin/bash
# Mini dummy service
# args : -p pidfile
# This demo 'service' forks, creates a pid file and
# continuously maintains an up-to-date /tmp/aservice.log file.
# Additionally, it intercepts SIGHUP for a 'reload'-like feature.

do_work() {
    trap "echo \$0: SIGHUP caught!" SIGHUP
    while true; do
        sleep 1
        date >/tmp/$PROCESS.log
    done
}

# Get arguments
pidfile=/var/run/aservice.pid # Defaut pid file
while [ -n "\$1" ]; do
    case \$1 in
    -p)
        pidfile=\$2
        shift 2
        ;;
    esac
done
do_work &
echo \$! >\$pidfile
echo -e "\n$0: try to kill process \$! and see what happens."
exit 0
EOT
    chmod +x $BINARY
fi
########################################

# Check that binary is available
[ -x $BINARY ] || exit 5 # LSB (except for status): 5 - program is not installed

# See how we were called
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    restart
    ;;
  try-restart|condrestart)
    [ -f $LOCKFILE ] && restart
    ;;
  reload|force-reload)
    killproc -p $PIDFILE $BINARY -HUP
    RETVAL=$?
    echo
    ;;
  status)
    status $PROCESS -p $PIDFILE
    RETVAL=$?
    ;;
  safe)
    # 'safe' is reserved for internal use.
    # Keep using 'start' to start the service
    safe
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|try-restart|condrestart|reload|force-reload|status}"
    RETVAL=1 # LSB (except for status): 1 - generic or unspecified error (current practice)
esac

exit $RETVAL



Références :
http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html