web-dev-qa-db-fra.com

Comment créez-vous un démon en Python?

Recherche sur Google révèle des extraits de code x2. Le premier résultat est cette recette de code qui a beaucoup de documentation et d’explications, ainsi que des discussions utiles en dessous.

Cependant, n autre exemple de code , bien que ne contenant pas tant de documentation, inclut un exemple de code permettant de passer des commandes telles que démarrer, arrêter et redémarrer. Il crée également un fichier PID qui peut être utile pour vérifier si le démon est déjà en cours d'exécution, etc.

Ces exemples expliquent comment créer le démon. Y a-t-il d'autres éléments à prendre en compte? Un échantillon est-il meilleur que l'autre et pourquoi?

234
DavidM

Solution actuelle

Une implémentation de référence de PEP 3143 (bibliothèque de processus de démon standard)] est maintenant disponible sous la forme python-daemon .

Réponse historique

Le exemple de code de Sander Marechal est supérieur à l'original, qui avait été posté en 2004. J'ai déjà contribué à un démoniseur pour Pyro, mais j'utiliserais probablement le code de Sander si je devais le refaire.

155
Jeff Bauer

Il y a beaucoup de choses délicates à prendre en charge pour devenir un processus démon bien comporté :

  • empêcher les vidages de base (de nombreux démons sont exécutés en tant que root et les vidages de base peuvent contenir des informations sensibles)

  • se comporter correctement à l'intérieur d'une prison chroot

  • définir l'UID, le GID, le répertoire de travail, l'umask et d'autres paramètres de processus de manière appropriée pour le cas d'utilisation

  • abandonne les privilèges suid, sgid élevés

  • ferme tous les descripteurs de fichiers ouverts, avec des exclusions selon le cas d'utilisation

  • se comporte correctement s'il est démarré dans un contexte déjà détaché, tel que init, inetd, etc.

  • configurer des gestionnaires de signaux pour le comportement sensible des démons, mais aussi avec des gestionnaires spécifiques déterminés par le cas d'utilisation

  • redirige les flux standard stdin, stdout, stderr car un processus démon n'a plus de terminal de contrôle

  • gérer un fichier PID comme un verrou consultatif coopératif, qui est un ne boîte de Pandore en soi avec de nombreuses façons contradictoires mais valables de se comporter

  • permettre un nettoyage correct lorsque le processus est terminé

  • devenir réellement un processus de démon sans conduire à zombies

Certains d'entre eux sont standard , comme décrit dans la littérature canonique Unix ( Programmation avancée dans l'environnement UNIX , par le regretté W. Richard Stevens, Addison-Wesley, 1992). D'autres, telles que la redirection de flux et traitement du fichier PID , ont un comportement classique auquel s'attendent la plupart des utilisateurs de démons, mais qui sont moins normalisées.

Tous ces éléments sont couverts par la spécification PEP 314 "Bibliothèque de processus standard démon" . L'implémentation de référence python-daemon fonctionne sur Python 2.7 ou version ultérieure, et sur Python 3.2 ou version ultérieure.

162
bignose

Voici le démon de base 'Howdy World' Python de base par lequel je commence lorsque je développe une nouvelle application démon.

#!/usr/bin/python
import time
from daemon import runner

class App():
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
    def run(self):
        while True:
            print("Howdy!  Gig'em!  Whoop!")
            time.sleep(10)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

Notez que vous aurez besoin de la bibliothèque python-daemon. Vous pouvez l'installer par:

pip install python-daemon

Ensuite, lancez-le simplement avec ./howdy.py start et arrêtez-le avec ./howdy.py stop.

94
Dustin Kirkland

Notez le paquet python-daemon qui résout beaucoup de problèmes derrière les démons.

Parmi d'autres fonctionnalités, il permet (à partir de la description du paquet Debian):

  • Détachez le processus dans son propre groupe de processus.
  • Définissez l'environnement de processus approprié pour une exécution dans un chroot.
  • Renoncez aux privilèges suid et sgid.
  • Fermez tous les descripteurs de fichiers ouverts.
  • Changez le répertoire de travail, uid, gid et umask.
  • Définissez les gestionnaires de signaux appropriés.
  • Ouvrez de nouveaux descripteurs de fichier pour stdin, stdout et stderr.
  • Gérer un fichier de verrouillage PID spécifié.
  • Enregistrez les fonctions de nettoyage pour le traitement à la sortie.
42
Viliam

Une alternative - créer un programme Python normal et non démonisé, puis le démoniser en externe à l'aide de supervisord . Cela peut économiser beaucoup de maux de tête et est * portable-nix et langue.

30
Chris Johnson

Probablement pas une réponse directe à la question, mais systemd peut être utilisé pour exécuter votre application en tant que démon. Voici un exemple:

[Unit]
Description=Python daemon
After=syslog.target
After=network.target

[Service]
Type=simple
User=<run as user>
Group=<run as group group>
ExecStart=/usr/bin/python <python script home>/script.py

# Give the script some time to startup
TimeoutSec=300

[Install]
WantedBy=multi-user.target

Je préfère cette méthode car beaucoup de travail est fait pour vous et votre script démon se comporte de la même manière que le reste de votre système.

-Orby

12
Luke Dupin

YapDi est un module relativement nouveau python qui est apparu dans Hacker News. A l'air très utile, peut être utilisé pour convertir un script python en mode démon à partir de l'intérieur du script.

6
Sergey R

comme python-daemon n’a pas encore supporté python 3.x, et à partir de ce qui peut être lu sur la liste de diffusion, il est possible que cela ne se produise jamais, j’ai écrit une nouvelle implémentation de PEP 3143: pep3143daemon

pep3143daemon devrait supporter au moins python 2.6, 2.7 et 3.x

Il contient également une classe PidFile.

La bibliothèque ne dépend que de la bibliothèque standard et du module six.

Il peut être utilisé en remplacement immédiat de python-daemon.

Voici le documentation .

6
stephan schultchen

Cette fonction transformera une application en démon:

import sys
import os

def daemonize():
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #1 failed: {0}\n'.format(err))
        sys.exit(1)
    # decouple from parent environment
    os.chdir('/')
    os.setsid()
    os.umask(0)
    # do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # exit from second parent
            sys.exit(0)
    except OSError as err:
        sys.stderr.write('_Fork #2 failed: {0}\n'.format(err))
        sys.exit(1)
    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = open(os.devnull, 'r')
    so = open(os.devnull, 'w')
    se = open(os.devnull, 'w')
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())
4
Ivan Kolesnikov

J'ai bien peur que le module démon mentionné par @Dustin ne fonctionne pas pour moi. Au lieu de cela, j'ai installé python-daemon et utilisé le code suivant:

# filename myDaemon.py
import sys
import daemon
sys.path.append('/home/ubuntu/samplemodule') # till __init__.py
from samplemodule import moduleclass 

with daemon.DaemonContext():
    moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.

Courir, c'est facile

> python myDaemon.py

juste pour compléter, voici le contenu du répertoire samplemodule

>ls samplemodule
__init__.py __init__.pyc moduleclass.py

Le contenu de moduleclass.py peut être

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.
3
Somum

Une dernière chose à laquelle penser lors de la démonisation en python:

Si vous utilisez python journalisation et que vous souhaitez continuer à l'utiliser après la démonisation, veillez à appeler close() sur les gestionnaires (en particulier les gestionnaires de fichiers).

Si vous ne le faites pas, le gestionnaire peut toujours penser qu'il a des fichiers ouverts et vos messages disparaîtront tout simplement. En d'autres termes, assurez-vous que l'enregistreur sait que ses fichiers sont fermés!

Cela suppose que lorsque vous démonisez, vous fermez TOUS les descripteurs de fichiers ouverts sans distinction - vous pouvez essayer de fermer tous les fichiers sauf les fichiers journaux (mais il est généralement plus simple de tout fermer puis de rouvrir ceux que vous voulez).

2
Matthew Wilcoxson

J'ai modifié quelques lignes dans l'exemple de code de Sander Marechal (mentionné par @JeffBauer dans la réponse acceptée ) pour ajouter une méthode quit() à exécuter préalablement à l'arrêt du démon. C'est parfois très utile.

le voici.

Note: Je n'utilise pas le module "python-daemon" car la documentation est toujours manquante (voir aussi beaucoup d'autres questions SO) et est assez obscur (comment démarrer/arrêter correctement un démon en ligne de commande avec ce module?)

1
Basj