2010-08-10 5 views
5

mio Python daemon funziona bene in primo piano del mio sistema Ubuntu utilizzare questo comando nel terminale:Python daemon non verrà eseguito in background su Ubuntu

python /opt/my-daemon.py foreground 

Tuttavia quando provo a chiamare il demone con il "start" comando fallisce, perché?

python /opt/my-daemon.py start 

Questo è come io chiamo il comando nel file /etc/rc.local:

python /opt/my-daemon.py start & 

Con la presente il codice:

1.daemon.py

#!/usr/bin/env python 
import sys, os, time, atexit 
from signal import SIGTERM 
class Daemon: 
""" 
A generic daemon class. 

Usage: subclass the Daemon class and override the run() method 
""" 
def __init__(self, pidfile, 
    stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'): 
    self.stdin = stdin 
    self.stdout = stdout 
    self.stderr = stderr 
    self.pidfile = pidfile 

def daemonize(self): 
    """ 
    Do the UNIX double-fork magic. See Richard Stevens' "Advanced 
    Programming in the UNIX Environment" for details (ISBN 0201563177) 
    http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 
    """ 
    try: 
     pid = os.fork() 
     if pid > 0: 
      # exit first parent 
      sys.exit(0) 
    except OSError, e: 
     sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, 
        e.strerror)) 
     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, e: 
     sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) 
     sys.exit(1) 

    # Redirect standard file descriptors 
    sys.stdout.flush() 
    sys.stderr.flush() 
    si = file(self.stdin, 'r') 
    so = file(self.stdout, 'a+') 
    se = file(self.stderr, 'a+', 0) 
    os.dup2(si.fileno(), sys.stdin.fileno()) 
    os.dup2(so.fileno(), sys.stdout.fileno()) 
    os.dup2(se.fileno(), sys.stderr.fileno()) 

    # Write pidfile 
    atexit.register(self.delpid) 
    pid = str(os.getpid()) 
    file(self.pidfile,'w+').write("%s\n" % pid) 

def delpid(self): 
    os.remove(self.pidfile) 

def start(self): 
    """ 
    Start the daemon 
    """ 
    # Check for a pidfile to see if the daemon already runs 
    try: 
     pf = file(self.pidfile,'r') 
     pid = int(pf.read().strip()) 
     pf.close() 
    except IOError: 
     pid = None 

    if pid: 
     message = "pidfile %s already exist. Daemon already running?\n" 
     sys.stderr.write(message % self.pidfile) 
     sys.exit(1) 

    # Start the daemon 
    self.daemonize() 
    self.run() 

def stop(self): 
    """ 
    Stop the daemon 
    """ 
    # Get the pid from the pidfile 
    try: 
     pf = file(self.pidfile,'r') 
     pid = int(pf.read().strip()) 
     pf.close() 
    except IOError: 
     pid = None 

    if not pid: 
     message = "pidfile %s does not exist. Daemon not running?\n" 
     sys.stderr.write(message % self.pidfile) 
     return # not an error in a restart 

    # Try killing the daemon process 
    try: 
     while 1: 
      os.kill(pid, SIGTERM) 
      time.sleep(0.1) 
    except OSError, err: 
     err = str(err) 
     if err.find("No such process") > 0: 
      if os.path.exists(self.pidfile): 
       os.remove(self.pidfile) 
     else: 
      print str(err) 
      sys.exit(1) 

def restart(self): 
    """ 
    Restart the daemon 
    """ 
    self.stop() 
    self.start() 

def run(self): 
    """ 
    You should override this method when you subclass Daemon. It will be called after the process has been 
    daemonized by start() or restart(). 
    """ 

2 .my-daemon.py

import sys, time 
from daemon import Daemon 
import MySQLdb #MySQL libraries 
#Database parameters 
config = {"host":"localhost",...} 
try: 
    conn = MySQLdb.connect(config['host'],... 
class MyDaemon(Daemon): 
def run(self): 
    while True: 
     time.sleep(2) 
        #{Do processes, connect to the database, etc....} 
        ... 
if __name__ == "__main__": 
daemon = MyDaemon('/tmp/daemon-example.pid') 
if len(sys.argv) == 2: 
    if 'start' == sys.argv[1]: 
     daemon.start() 
    elif 'stop' == sys.argv[1]: 
     daemon.stop() 
    elif 'restart' == sys.argv[1]: 
     daemon.restart() 
    elif 'foreground' == sys.argv[1]: #This runs the daemon in the foreground 
     daemon.run() 
    else: 
     print "Unknown command" 
     sys.exit(2) 
    sys.exit(0) 
else: 
    print "usage: %s start|foreground|stop|restart" % sys.argv[0] 
    sys.exit(2) 
+0

Cosa succede esattamente se si utilizza il comando 'start'? – chryss

+1

Cosa intendi con "fallisce"? –

+0

Errore come in non eseguito affatto. Funziona con il comando in primo piano. Quando scrivo il comando "start" nel terminale con il & alla fine vedo il PID ma poi uccide immediatamente il processo demone. – QCar

risposta

0

Invece di usare daemon.py, si può prendere in considerazione sfruttando di Upstart system che fornisce un modo semplice per configurare un demone respawning Ubuntu. Dal stesso link, è dotato di:

* Services may be respawned if they die unexpectedly 
* Supervision and respawning of daemons which separate from their parent process 

Se si utilizza Ubuntu9.10 o poi, dare un'occhiata a /etc/init/cron.conf come esempio. Per le versioni precedenti di Ubuntu, credo che gli script di upstart si trovino in /etc/event.d/.

Per una spiegazione delle parole chiave di Upstart, vedere here.

+0

Ciao, grazie per la tua risposta ma credo di aver trovato il problema ma controllerò il sistema upstart. Grazie! – QCar

1

RISOLTO. Avevo l'impressione che il parametro foreground e il parametro start fossero due cose diverse. Si scopre che ho solo bisogno di fare quanto segue.

def run(self): 
    while True: 
     time.sleep(2) 

a

def start(self): 
    while True: 
     time.sleep(2) 

Ho quindi rimosso il parametro foreground, perché posso eseguire lo script dal terminale con il comando start per visualizzare l'output in primo piano.

python /opt/my-daemon.py start 

Inoltre rc.local comincio lo script come segue:

python /opt/my-daemon.py start & 

Questo nasconde il processo demone ed esegue lo script all'avvio a prescindere dal utente che accede a :)

+0

Sono contento che tu abbia trovato un modo per far funzionare il tuo programma, ma ho pensato che dovresti essere consapevole che sovrascrivendo 'start' come descritto sopra, stai tagliando il codice in daemon.py che avvia il demone. – unutbu

+0

Questo "inizio" che ignora l'uscita anticipata del tuo script suggerisce che il problema originale si trova probabilmente in "Daemon.start". Forse il pidfile esiste già? o qualcosa in 'Daemon.daemonize' sta causando l'uscita anticipata? Potresti usare le dichiarazioni di stampa per indagare, o magari provare invece Upstart. – unutbu

+0

@Unutbu Grazie per tutte le vostre risposte, indagherò ulteriormente. – QCar