Alessio Treglia

everybody lies
  • Quality Assurance
  • EN blog
  • rss
  • Inizio
  • Chi sono
  • pyRisk
  • Roma3 WiFi Authenticator
  • Installation Report Generator
  • Contattami

Un Python demoniaco alla fermata del bus!

quadrispro | 12 Febbraio 2008

I miei studi sul Python proseguono a gonfie vele e oggi vi presento un piccolo HOWTO su come scrivere un demone nel linguaggio di programmazione più portabile del mondo (e a chi, in questo momento, viene in mente solo la parola Java dico: “Seee! Te piacerebbe!“).

Andiamo per gradi, iniziamo a rispondere alla prima domanda: cos’è un demone? Cito Wikipedia:

Nei sistemi Unix, e più in generale nei sistemi operativi multitasking, un demone (daemon in inglese) è un programma eseguito in background, senza che sia sotto il controllo diretto dell’utente. Di solito i demoni hanno nomi che finiscono per “d”: per esempio, syslogd è il demone che gestisce i log di sistema.

In ambiente Windows un tale programma è noto con il nome di Servizio.

Angeli e diavoli

Insomma, un demone è un applicazione residente in memoria che può avere, come compiti principali, quello di rispondere a delle richieste da parte di altri programmi e/o eseguire periodicamente delle procedure; questo tipo di soluzione è molto utile nel caso si intenda sviluppare un’applicazione distribuita secondo un modello client/server e nel seguente esempio verrà implementato un piccolo demone, il quale avrà come unico scopo quello di rispondere alle richieste ricevute tramite DBus, il bus di sistema di GNOME. Se pensate che possa essere un compito difficile, ve ne farò ricredere!

Ok, siamo pronti, cominciamo: innanzitutto entriamo nella nostra Home e creiamoci una cartella dove andremo a posizionare i sorgenti:

cd ~
mkdir test

Dunque creiamo il file che conterrà il codice del nostro infernale componente software:

gedit ~/test/daemon.py

Ecco fatto! Ora non resta che copiare e incollare all’interno dell’editor il seguente codice, al cui interno ho già inserito numerosi commenti, i quali, grazie anche all’estrema semplicità del linguaggio, saranno sicuramente più che sufficienti per comprendere il funzionamento del demone:


#!/usr/bin/env python

# File: daemon.py

# Spostiamoci nella directory corrente
import os,sys
if __name__ == '__main__':
        os.chdir(os.path.dirname(os.path.normpath(os.path.join(os.getcwd(),sys.argv[0]))))
# Importiamo i moduli necessari
import gobject
import dbus
import dbus.service
if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
        import dbus.glib

LOG_DIR = '/tmp'    # Questa ci serve per memorizzare il file .pid

# Il nostro oggetto condiviso
class Demone(dbus.service.Object):
    def __init__(self, bus_name, object_path='/org/test/daemon'):
    # è necessario richiamare il costruttore della classe base
        dbus.service.Object.__init__(self, bus_name, object_path)
    ## Eccoci ad un punto saliente finalmente!
    ## Questo "stupido" metodo, che verrà richiama dalla nostra
    ## applicazione, fa solo una cosa: stampa a schermo un
    ## simpatico saluto (strano per un *demone*, vi pare?).
    @dbus.service.method('org.test.daemon.prova')
    def Hello(self):
        sys.stdout.write('Hello world from a dbus daemon!\n')
        sys.stdout.flush()
        return 0
    #end function Hello

############## SIGNAL HANDLERS #################
## Qui raccogliamo tutti i metodi che gestiranno i segnali ricevuti.
## In questo caso ci interessa gestire solo SIGTERM.
def terminate(signal, param):
	try:
		# Esegue tutte le operazioni necessarie alla corretta
                # chiusura dell'applicazione

		# Cancella il file .pid
		os.remove(os.path.join(LOG_DIR, 'run.pid'))
	except:
		pass                            # Ignora tutti gli errori...
        ## ...e stampa un messaggio di addio!
	sys.stdout.write("........terminating\n")
	sys.exit(0)
############## END SIGNAL HANDLERS ##############

## Analizza e restituisce lo stato attuale del demone (se è attivo
## o meno). Restituisce il process id del demone se è attualmente
## presente in memoria, -1 in caso contrario.
def status():
    if os.path.isfile(os.path.join(LOG_DIR, 'run.pid')):
        f = open(os.path.join(LOG_DIR, 'run.pid'), 'r')
        pid = string.atoi(string.strip(f.readline()))
        f.close()
        try:
            os.kill(pid, 0)
        except os.error, args:
            if args[0] != errno.ESRCH: # Nessun processo
                raise os.error, args
        else:
            # il demone è avviato
            return pid
    # il demone non è presente in memoria
    return -1

## Quelle che seguono sono le istruzioni relative alla gestione
## dei processi del demone: verranno creati due fork, in questo modo
## sarà possibile "staccare" il programma dal terminale e farne
## continuare l'esecuzione in background.
def start():
    try:
        # procediamo con il primo fork...
        pid = os.fork()
        if pid > 0:
            # chiudiamo il processo padre...
            sys.exit(0)
    except OSError, e:
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
        sys.exit(1)

	# "stacchiamolo" dal processo principale...
    os.setsid()
    os.umask(0) # ...e diamogli i giusti permessi!

    # Ora procediamo con il secondo fork...
    try:
        pid = os.fork()
        if pid > 0:
            print "test daemon: pid " + str(pid)
            sys.exit(0)
    except OSError, e:
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
        sys.exit(1)

    # Controlla l'esistenza della directory del file di log. Se non esiste, la crea.
    if not os.path.isdir(LOG_DIR):
        os.mkdir(LOG_DIR, 0755)

    # Infine scrive l'id del processo nel file .pid.
    f = open(os.path.join(LOG_DIR, 'run.pid'), 'w')
    f.write("%d" % os.getpid())
    f.close()

    return 0

## Se start() serve per avviarlo, immaginate un po' a che serve
## questo... Se il demone è attivo lo termina e restituisce 0, altrimenti
## stampa un avviso e restituisce -1.
def stop():
    ## Controlliamo la presenza del file .pid...
    if os.path.isfile(os.path.join(LOG_DIR, 'run.pid')):
        ## apre il file e legge l'id del processo...
        f = open(os.path.join(LOG_DIR, 'run.pid'), 'r')
        pid = string.atoi(string.strip(f.readline()))
        f.close()
        try:
            os.kill(pid, 0)  ## ...dunque prova a killarlo...
        except os.error, args:
            if args[0] != errno.ESRCH: ## ...se non trova il processo
                raise os.error, args ## lancia un'eccezione...
        else:
            os.kill(pid, signal.SIGTERM) ##....altrimenti invia un SIGTERM
            return 0                          ## al demone
    return -1

# Memorizza il nome del programma.
call_name = os.path.split(sys.argv[0])[1]

## Riceve gli argomenti passati sulla riga di comando. Stabiliamo di
## processarne 3, "start", "status" e "stop", uno per ogni metodo di
## quelli definiti in precedenza.
if len(sys.argv) != 2:
    sys.stderr.write("Usage: %s [start|stop|status]\n" % call_name)
    sys.exit(1)
if len(sys.argv) == 2:
    if sys.argv[1] == 'stop':
        # "Stoppa" il demone
        s = stop()
        if s == 1:     ## se il demone non è attivo stampa un avviso...
            sys.stdout.write("%s: daemon is not running\n" % call_name)
            sys.exit(1) ## ...e esci.
        sys.stdout.write("%s: stopped.\n" % call_name)
        sys.exit(0)
    elif sys.argv[1] == 'status':
        # Stampa lo stato del demone.... beh, questa è facile, no?
        s = status()
        if s == -1:
            sys.stdout.write("%s: daemon is not running\n" % call_name)
        else:
            sys.stdout.write("%s: daemon [%d] is running\n" % (call_name, s))
            sys.exit(0)
    elif sys.argv[1] == 'start':
        # Oh ecco, qui avviamo il demone, verificando prima che non sia già
        # già stato caricato in memoria.
        s = status()
        if s != -1:
            sys.stdout.write("%s: daemon [%d] is running\n" % (call_name, s))
            sys.exit(1)
        start()
else:
    ## e qui ricordiamo all'utente come funziona il nostro demone....
    sys.stderr.write("Usage: %s [stop|status]\n" % call_name)
    sys.exit(1)

## Terminata la parte "infernale", ovvero quella relativa al demone,
## passiamo ad occuparci del lato server.
# Apriamo la connessione con DBus e "attacchiamoci" al bus della sessione.
session_bus = dbus.SessionBus() # Per un demone di sistema va sostituito con dbus.SystemBus()
try:
# Creiamo, inizializziamo e "attacchiamo" l'oggetto a DBus
    bus_name = dbus.service.BusName('org.test.daemon', bus=session_bus)
    object = Demone(bus_name)
except Exception, e:
    print "Errore ", e
# Ora non resta che avviare il loop principale
mainloop = gobject.MainLoop()
mainloop.run()
# Il demone è pronto e già freme nell'attesa di
# rispondere alle nostre richieste!

Ok, salvato il file apriamo un terminale e diamogli i giusti permessi con

chmod +x ~/test/daemon.py

Finito! Per avviarlo basta digitare il nome del comando seguito dall’opzione start:

~/test/daemon.py start

Successivamente, per stopparlo, basterà sostituire start con stop.

Se si vuole essere sicuri che tutto sia andato a buon fine, su Ubuntu è possibile andare su Sistema -> Amministrazione -> Monitor di sistema e verificare con i propri occhi la presenza del demone fra i processi residenti in memoria:

Monitor di sistema

 

Soddisfatti? Io molto e sì, sono uno che si diverte con davvero poco! Ma soprattutto mi diverte farmi stupire da Python, un linguaggio davvero sbalorditivo per come riesce a unire potenza, semplicità e grandi performance (e qui mi viene da pensare a Java: “oh! come rido!” :D)

Alla prossima puntata, dove scriveremo una piccola applicazione capace di interagire con il nostro piccolo diavolo!

Categorie
GNOME, Guide e howto, Java, Open source & free software, Python, Ubuntu
Commenti RSS
Commenti RSS
Trackback
Trackback

« Pep-pepepeeee! Pep-pepepeeee! Pep-pepepee-pepeeee!! La guida per il mio portatile sul wiki di Ubuntu-it »

2 risposte

hai proprio ragione, sono agli inizi, ma python è un

devil | 30 Maggio 2008 | 14:20

hai proprio ragione, sono agli inizi, ma python è un linguaggio con delle incredibili potenzialità, e si può unire anche ad altri linguaggi

[Reply]

ma la libreria gobject dove la prendi? =) Anche io vorrei

Michele | 25 Luglio 2008 | 00:58

ma la libreria gobject dove la prendi? =)
Anche io vorrei fare una specie di demone, o servizio in windows, un programma che resti in esecuzione e svolga il suo compito in background ogni 15 minuti.
mi saresti una mano?

Saluti

[Reply]

Lascia un commento

Puoi usare questi tag : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Click to cancel reply

Chi sono


Per contattarmi mandami una mail

Argomenti

Aggiornamento Ambienti di sviluppo Audio Bug e problemi vari C Cazzate varie Comunità Italiana Documentazione e wiki Feisty Firefox GNOME GTK+ GTK Rm3WiFi Authenticator Guide e howto Gutsy Hardy Installation Report Generator Installazione e problemi all'avvio Intrepid Jaunty Java Kernel e moduli Last.fm Musica Notizie Open source & free software Planet Ubuntu-it Programmazione e sviluppo Python Siti,servizi & blog Società e media Traduzioni Ubuntu Università e scienza Viaggi Video Windows Wordpress Xubuntu

Ultimi commenti

  • Roberto su Ubuntu 9.04, chiamiamola per nome
  • Flavio su Roma3, anche Ingegneria esprime il suo dissenso
  • iced su Si riparte!
  • Fabio su Si riparte!
  • l3on su Si riparte!

Tutti gli articoli

Ubuntu in Italiano

Versione a 32 bit

RSS Planet di Ubuntu-it

  • Flavia Weisghizzi: La Strega e l'Alchimista (Parte 2)
  • Leo Iannacone (l3on): Una piccola patch per Kaffeine…
  • Divilinux: Italia chiama Brasile, Brasile risponde
  • Ubuntu-it Newsletter: Newsletter italiana numero 27
  • Dario Cavedon: E' arrivato il freddo...

Blogroll

  • (LS) Lorenzo Sfarra
  • Bubuntu
  • crisis
  • Dagospia
  • Divilinux Lost Blog
  • Embrace’s Blog
  • Fradeve OpenBlog
  • Il blog di Totò
  • Il mondo di Paolettopn
  • Leo
  • Maurizio Moriconi
  • Milo Casagrande
  • Parzialmente scremato
  • TuxLinux
  • Ubuntu block notes

Licenza

I contenuti di questo sito sono coperti da licenza copyleft Creative Commons


Creative Commons Attribution-ShareAlike 2.5 Italy

Powered links

Il Bloggatore
BlogItalia.it - La directory italiana dei blog
Sfondi Desktop Classifica di siti - Iscrivete il vostro!
eXplicatum - Aggregatore online di Notizie / Blog / Feed RSS

Ascolti recenti

  • Joe Cocker – Feelin Alright
  • Band from TV – You Can't Always Get What You Want
  • Solomon Burke – 10 - None of Us Are Free
  • Jon Cleary – Got to Be More Careful
  • Three Dog Night – One is the Lonliest Number
  • Solomon Burke – 10 - None of Us Are Free
  • Jon Cleary – Got to Be More Careful
  • Band from TV – You Can't Always Get What You Want
  • Massive Attack – Teardrop
  • Joe Cocker – Feelin Alright

RSS alessio and friends on identi.ca

  • alessio: Workin on darcs package
  • alessio: packaging in progress, I think to leave Italian Doc Team seriously
  • milo: socket socket socket! Python socket!
  • alessio: is hungry
  • alessio: trying Simply RSS
  • alessio: feelin' so much tired, going to bed...
  • alessio: feeling s
  • alessio: 's waiting for Vale...
  • alessio: I have to study...
  • milo: fuck telepass.it website... it sucks!

Accedi o registrati

  • Registrati
  • Collegati
  • Voce RSS
  • RSS dei commenti
  • WordPress.org
rss Commenti RSS valid xhtml 1.1 design by jide powered by Wordpress get firefox