Transformer un script python en service (daemon)

python-logoVoici la méthode pour lancer un script python en tant que service sur le Raspberry mais c’est valable aussi sur Debian.

 

EDIT : cette astuce n’est valable que sur les versions wheezy de raspbian , elle ne fonctionne plus avec la nouvelle version Jessie

 1 -rendre votre script executable

Pour commencer , assurez vous que votre script soit bien exécutable :

chmod +x monscript.py

testez l’execution de votre script :

./monscript.py

n’utilisez pas la méthode « python monscript.py » car pour la suite du tuto votre script doit être exécutable directement

si votre script ne fonctionne pas , vous avez certainement un problème avec votre shebang ( vous savez la première ligne d’un script )

Normalement sur le Raspberry c’est celle ci :

 #!/usr/bin/python 
2 – créer un lien vers votre script :

un service doit être lancé depuis /usr/bin ou /usr/sbin ,

nous allons donc créer un lien dans un de ces répertoires pointant vers notre script

sudo  ln  -s   /chemin_de_mon_script/monscript.py   /usr/sbin/monscript

notez bien qu’on a enlever l’extension .py

3 – paramétrage du service :

c’est assez simple , il y a un modèle déjà fourni : le fichier skeleton

cd /etc/init.d

sudo cp skeleton monscript

chmod +x monscript

éditez le fichier monscript avec votre éditeur préféré et repérez les lignes suivantes:

PATH=/usr/sbin:/usr/bin:/sbin:/bin
DESC= »Description of the service »
NAME=daemonexecutablename
DAEMON=/usr/sbin/$NAME
DAEMON_ARGS= »–options args »
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

ne touchez pas aux lignes en rouge et remplacez les lignes vertes par vos valeurs

DESC : ce que vous voulez
NAME=monscript
DAEMON_ARGS= » »

(si votre script n’a pas besoin de paramètre)

ensuite repérez les lignes suivantes:

# Function that starts the daemon/service
#
do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon –start –quiet  –pidfile $PIDFILE –exec $DAEMON –test > /dev/null \
                || return 1
        start-stop-daemon –start –quiet  –pidfile $PIDFILE –exec $DAEMON — \
                $DAEMON_ARGS \
                || return 2

Pour pouvoir arrêter un service, il faut connaître son PID (identifiant du processus). Soit c’est votre programme qui crée le fichier .pid , soit c’est le service. En général c’est plus simple de laisser le service s’occuper de ce pid, il faut dans ce cas ajoutez l’option –make-pidfile (voir ci-dessous).

si votre script ne rends pas la main ( il boucle en permanence ) alors il faut le lancer en mode background. Il faut dans ce cas ajouter l’option –background

voici les lignes finales:

 

start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
     || return 1
start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- \  
        $DAEMON_ARGS \
     || return 2

 

4 – voila le service est prêt il faut maintenant le tester :

sudo service monscript start
sudo service monscript status
sudo service monscript stop

si tout va bien nous passons a la suite : le lancement automatique au boot :

sudo update-rc.d monscript defaults 99

update-rc.d se charge de créer tout ce qu’il faut pour lancer un service , le chiffre 99 corresponds a l’ordre de lancement au boot , ici en dernier après tous les autres

11 réflexions sur “Transformer un script python en service (daemon)

  1. Merci aussi : cela m’a permis de mettre en place mon premier service Python en quelques minutes ! J’ai perdu un peu de temps avec un truc tout bête : à la fin de l’étape 3, il ne faut pas oublier de rendre le fichier de paramétrage du service executable lui aussi (un chmod +x monscript). Sans cela, le service n’est pas trouvé…

    J’aime

  2. Oui, et je l’avais bien fait pour monscript.py….mais je parle du fichier de paramètrage (le clone du skeleton) qui doit aussi subir ce sort. Comme tu ne l’avais pas dit, je ne l’avais pas fait…et j’ai bien dû perdre 15 min à chercher d’où le problème venait. Encore merci en tout cas.

    J’aime

  3. bonjour,
    je suis sur Raspberry Pi 3 avec Jessie
    j’ai suivi ce tuto , mais j’ai un problème:
    le service démarre bien mais le programme n’est pas actif…
    resultat du journalctl -xn
    /* **************** */
    root@raspberrypi:/etc/init.d# journalctl -xn
    — Logs begin at ven. 2016-10-28 14:03:16 CEST, end at ven. 2016-10-28 16:42:08 CEST. —
    oct. 28 16:41:51 raspberrypi systemd[1]: Started LSB: Example initscript.
    — Subject: L’unité (unit) rdm880.service a terminé son démarrage
    — Defined-By: systemd
    — Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel

    — L’unité (unit) rdm880.service a terminé son démarrage, avec le résultat done.
    oct. 28 16:41:51 raspberrypi sudo[1330]: pam_unix(sudo:session): session closed for user root
    oct. 28 16:41:53 raspberrypi sudo[1362]: pi : TTY=pts/0 ; PWD=/home/pi ; USER=root ; COMMAND=/usr/sbin/service rdm880 status
    oct. 28 16:41:53 raspberrypi sudo[1362]: pam_unix(sudo:session): session opened for user root by pi(uid=0)
    oct. 28 16:41:53 raspberrypi sudo[1362]: pam_unix(sudo:session): session closed for user root
    oct. 28 16:42:08 raspberrypi sudo[1372]: pi : TTY=pts/0 ; PWD=/home/pi ; USER=root ; COMMAND=/bin/su
    oct. 28 16:42:08 raspberrypi sudo[1372]: pam_unix(sudo:session): session opened for user root by pi(uid=0)
    oct. 28 16:42:08 raspberrypi su[1376]: Successful su for root by root
    oct. 28 16:42:08 raspberrypi su[1376]: + /dev/pts/0 root:root
    oct. 28 16:42:08 raspberrypi su[1376]: pam_unix(su:session): session opened for user root by pi(uid=0)
    /* ************* */
    mon script a base de skeleton :
    /* *************** */
    #!/bin/sh
    # kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
    if [ true != « $INIT_D_SCRIPT_SOURCED » ] ; then
    set « $0 » « $@ »; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
    fi
    ### BEGIN INIT INFO
    # Provides: skeleton
    # Required-Start: $remote_fs $syslog
    # Required-Stop: $remote_fs $syslog
    # Default-Start: 2 3 4 5
    # Default-Stop: 0 1 6
    # Short-Description: Example initscript
    # Description: This file should be used to construct scripts to be
    # placed in /etc/init.d. This example start a
    # single forking daemon capable of writing a pid
    # file. To get other behavoirs, implemend
    # do_start(), do_stop() or other functions to
    # override the defaults in /lib/init/init-d-script.
    ### END INIT INFO

    # Author: Foo Bar
    #
    # Please remove the « Author » lines above and replace them
    # with your own name if you copy and modify this script.
    PATH=/usr/sbin:/usr/bin:/sbin:/bin
    DESC= »RFID rdm880 service »
    NAME=rdm880
    DAEMON=/usr/sbin/$NAME
    DAEMON_ARGS= » »
    PIDFILE=/var/run/$NAME.pid
    SCRIPTNAME=/etc/init.d/$NAME
    # Function that starts the daemon/service
    #
    do_start()
    {
    # Return
    # 0 if daemon has been started
    # 1 if daemon was already running
    # 2 if daemon could not be started
    start-stop-daemon –start –quiet –background –make-pidfile –pidfile $PIDFILE –exec $DAEMON –test > /dev/null \
    || return 1
    start-stop-daemon –start –quiet –background –make-pidfile –pidfile $PIDFILE –exec $DAEMON — \ $DAEMON_ARGS \
    || return 2
    }
    /* ************** */
    Mon script python
    /* ********************* */
    #!/usr/bin/python
    # -*-coding:Utf-8 -*
    import serial
    import time
    import wiringpi
    import MySQLdb
    import RPi.GPIO as GPIO
    from datetime import datetime
    from binascii import hexlify

    serial=serial.Serial(« /dev/ttyAMA0″,
    baudrate=9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=0.1)

    wiringpi.wiringPiSetupGpio()
    wiringpi.pinMode(18, 1)

    db = MySQLdb.connect(host= »192.168.1.90″, user= »user », passwd= »QY7R97H », db= »porte »)
    cur = db.cursor()
    query = (« SELECT * FROM droit_acces WHERE Code = %s LIMIT 1 »)

    ID_rong = 128187 # reader respone if no card
    chuoi= « \xAA\x00\x03\x25\x26\x00\x00\xBB »
    def RFID(str): #function read RFID via uart
    serial.write(chuoi)
    data = serial.readline()
    tach_5 = data[5]
    tach_6 = data[6]
    hex_5 = hexlify(tach_5)
    hex_6= hexlify(tach_6)
    num_5 = int(hex_5,16)
    num_6 = int(hex_6,16)
    num_a = num_5 * 1000 + num_6
    if(num_a != ID_rong):
    tach_7 = data[7]
    tach_8 = data[7]
    hex_7 = hexlify(tach_7)
    hex_8= hexlify(tach_8)
    num_7 = int(hex_7,16)
    num_8 = int(hex_8,16)
    num = num_8 + num_7 * 1000 + num_6 * 1000000 + num_5 * 1000000000
    else:
    num = num_5 * 1000 + num_6
    return num

    while 1:
    num = RFID(chuoi)
    if num != ID_rong:
    # print « no card …. »
    # else:
    # print « Card Serial:%d » % num
    cur.execute(query, « %d » % num )
    row = cur.fetchone()
    if row is not None:
    # print « %s » % row[2]
    # print « %s » % row[0]
    wiringpi.digitalWrite(18, 1)
    time.sleep(2)
    wiringpi.digitalWrite(18, 0)
    # else:
    # print « BAD card …. »

    time.sleep(1)
    /* ********************* */
    doit-on le voir avec la commande ps -ef | grep monscript ??
    qu’ai-je oublié ?
    d’avance merci de voter aide …

    J’aime

  4. Bonjour,
    quelqu’un a-t-il la solution pour démarrer un programme python au démarrage de jessie?
    J’écume le web depuis une semaine, j’essaie plein de trucs, mais au final, rien ne fonctionne.
    Donc si une âme charitable pouvait nous faire profiter de sa solution éprouvée…

    J’aime

      • j’ai solutionné le problème en mettant une ligne de commande dans rc.local…
        je creer un programme python exemple rdm880.py
        je le rends executable
        je créé un lien dans /usr/sbin/

        et dans le rc.local j’ajoute la ligne suivante :

        sudo /usr/sbin/rdm880 &

        J’aime

      • @jahislove, merci pour cette réponse.

        C’est cette solution qui fonctionne le mieux pour moi, sauf que je suis obligé de rajouter dans le crontab la ligne @reboot sudo systemctl restart MonService.service pour qu’il redémarre vraiment.

        Dès que j’ai que j’ai du temps (il faut bien que je passe à autre chose), je reprendrai tout à zéro, y compris la solution de @Delamarre jean-paul, car j’ai fait tellement d’essais que je ne sais plus très bien ce que j’ai réellement fait.
        Ça me permettra de nettoyer un peu par la même occasion.

        Merci à vous deux.

        J’aime

Laisser un commentaire