Incron: monitorare directory e rispondere ad eventi specifici

inotify

Con questo articolo vedremo come, utilizzando un tool che dialoga direttamente con il kernel Linux, monitorare un particolare path e rispondere ad alcuni eventi lanciando diversi comandi.

Come al solito mi piace analizzare una problematica reale per far comprendere meglio il problema e la soluzione che andremo ad implementare.

Immaginiamo di avere una macchina Linux (in questo caso specifico andremo ad utilizzare un server CentOS) attestata sulla rete perimetrale/pubblica della nostra azienda.

Questa macchina ha delle directory utente che vengono utilizzate, da procedure automatiche presenti su server remoti di altre aziende, per caricare dei file che la nostra azienda ha necessità di elaborare.

La soluzione “quick & dirty” sarebbe quella di accordarsi con l’azienda che caricherà il file sul nostro sistema sull’ora e definire una seguente procedura:

  • Alle ore 00:00 viene avviato il trasferimento del file (di dimensione variabile) tramite scp
  • Alle ore 01:00 viene avviato tramite cron un job che prende il file e lo elabora

Questa soluzione tendenzialmente può funzionare, ma possono verificarsi diversi problemi che possono bloccare la nostra procedura. Per esempio:

  • Un problema sulle procedure dell’azienda remota tardano o falliscono, facendo slittare l’upload del file di più di un’ora. In questo caso il nostro job in partenza alle 01:00 fallirà poiché non troverà il file caricato.
  • Sempre un problema sulle procedure dell’azienda remota fa tardare l’avvio dell’upload del file. In questo caso, quando il nostro job parte, potrebbe tentare di elaborare un file non ancora completo
  • Le procedure partono correttamente ma, un lag di rete dato da N possibili motivi, fa allungare il tempo di trasferimento del file. Anche qui il nostro job potrebbe tentare di elaborare un file non ancora completamente trasferito.
  • Delle modifiche alle procedure remote fanno variare il nome del file (che era stato precedentemente accordato). In questo caso, anche se il trasferimento è completo, il nostro script (se non correttamente ingegnerizzato) potrebbe non trovare il file semplicemente perché non si chiama come ci si aspetterebbe
  • Sempre per modifiche alle procedure remote, il file potrebbe arrivare non normalizzato (quindi con permission sballate, etc.). In questo caso, magari, il nostro script potrebbe non essere in grado di leggere/scrivere o, più in generale, di elaborare il file

Come appare evidente, le problematiche possono essere molteplici!!!
Fortunatamente stiamo lavorando su linux e, il più delle volte, ci viene data la possibilità di risolvere i nostri problemi con un po’ di ingegno!

inotify

inotify è un sottosistema del kernel linux che lavora a livello di filesystem e ci permette, a fronte di alcuni eventi catturati, di notificare le variazioni all’applicazione.
E’ stato incluso nel mainstream del kernel linux con la versione 2.6.13 (rilasciata a Giugno 2005), ma può essere compilato con la versione 2.6.12 (e precedenti), tramite l’utilizzo di una patch.

In poche parole, dato un path, il sottosistema ne controlla gli inode puntati, e reagisce ad uno o più eventi su essi. A fronte dello scatenarsi di un evento (definito mask – maschera), possono essere eseguite delle operazioni.

Alcuni eventi che possono essere monitorati sono i seguenti:

  • IN_ACCESS – E’ stato fatto un’accesso in lettura ad un file
  • IN_MODIFY – E’ stata eseguita una modifica ad un file
  • IN_ATTRIB – E’ stata eseguita una modifica agli attributi di un file
  • IN_OPEN – Un file è stato aperto
  • IN_CLOSE_WRITE – Un file, aperto in scrittura, è stato chiuso
  • IN_CLOSE_NOWRITE – Un file, aperto NON in scrittura, è stato chiuso
  • IN_MOVED_FROM e IN_MOVED_TO – Un file è stato spostato o copiato
  • IN_DELETE – Un file/directory è stato cancellato
  • IN_CREATE – Un file/directory è stato creato
  • IN_DELETE_SELF – Il file/directory monitorato è stato cancellato

incrond

incrond è un demone che funziona in maniera molto simile a quello che è l’ormai conosciutissimo demone cron; la differenza sta nel fatto che, se cron lavora con giorni ed orari, incrond lavora invece con inotify.

Quello che possiamo fare per rendere la nostra procedura “intelligente” è andare a monitorare il path in cui l’azienda remota esegue l’upload del file e, al termine dell’upload, lanciare il job che elabora il file.
Questo ci permette di evitare di legarsi a gli orari, e di lanciare il nostro job ogni volta che un file viene caricato, indipendentemente dall’ora o dal nome del file. Esattamente quello che ci interessa.

Intanto andiamo ad installare il demone, operazione estremamente semplice:

# yum install incrond

A questo punto, la prima cosa da fare è creare il file /etc/incrond.allow contenente la lista degli utenti abilitati all’uso di incrond. Nel nostro caso abilitiamo l’utente root e l’utente matteo:

# cat > /etc/incrond.allow <<EOF
>root
>matteo
>EOF
# cat /etc/incrond.allow
root
matteo

Avviamo il demone ed assicuriamoci che sia abilitato per l’avvio automatico al boot della macchina:

# service incrond start
Starting incrond:                                          [  OK  ]
# chkconfig --list incrond
incrond        	0:off	1:off	2:off	3:off	4:off	5:off	6:off
# chkconfig incrond on

Ok, adesso siamo pronti per il definire il nostro monitoraggio.

Poniamo, nel nostro esempio, che l’utente matteo sia l’utente che carica il file, e che lo carica nel path /home/matteo/uploads.

Iniziamo a definire la incrontab (vedrete che l’assonanza con cron non è solo nelle definizioni, ma anche nei comandi) per l’utente matteo. Il comando è semplicissimo:

# su - matteo
$ incrontab -e

Questo apre il nostro editor di default e ci permette di compilare la incrontab.
Questa incrontab è composta da 3 campi (che vedremo di seguito) separati da spazio

ATTENZIONE – I campi DEVONO essere separati dal carattere di spaziatura. Separare i campi con il consueto TAB porta a problemi di interpretazione della incrontab da parte del demone incrond (dai, è tutto molto bello, un piccolo difetto lo possiamo anche concedere 🙂 ).

I tre campi sono, nell’ordine:

  • path – Il path ASSOLUTO che si desidera monitorare
  • mask – L’evento che si vuole monitorare. Sono supportati tutte le maschere che sono state descritte prima, più la maschera IN_ALL_EVENTS che, semplicemente, cattura tutti gli eventi (ottima per il debugging)
  • command– Il comando da eseguire quando viene catturato l’evento. Da notare che in questo comando possiamo utilizzare delle variabili di inotify che ci vengono passate direttamente dal demone. Queste variabili sono:
    • $@ – Il path monitorato
    • $# – Il file sul quale si è scatenato l’evento
    • $% – L’evento, in forma testuale, che si è verificato
    • $& – L’evento, in forma numerica, che si è verificato
    • $$ – Il carattere $

Semplice, non trovate?

Allora, torniamo all’esempio. Il nostro job si trova nella directory /home/matteo/scripts/, si chiama elaborazione.sh ed accetta come parametro il nome del file da elaborare.

Facciamo un’attimo il punto della situazione. In un certo momento (non sappiamo quale), la procedura remota andrà a caricare, con scp, il nostro file.
Una volta effettuata la connessione con successo, il file verrà in primo luogo creato (IN_CREATE), aperto in scrittura (IN_OPEN), scritto (una sequenza di IN_MODIFY) ed infine chiuso (IN_CLOSE_WRITE, visto che era stato aperto in scrittura).

Quindi, l’evento che andrà a determinare la fine del trasferimento è IN_CLOSE_WRITE. Andiamo quindi a compilare la incrontab dell’utente matteo:

/home/matteo/uploads IN_CLOSE_WRITE /home/matteo/scripts/elaborazione.sh $@/$#

Salviamo usciamo ed assicuriamoci che la nuova incrontab sia stata letta ed interpretata dal sistema:

table updated
$ exit
# tail -1 /var/log/cron
Oct 27 10:30:39 myserver incrond[889]: table for user matteo changed, reloading

Possiamo testare se funziona caricando un dummy file (supponiamo che i file contenti il testo “dummy” vengano ignorati dal nostro script) da una macchina remota:

client$ cat > dummyfile <<EOF
>dummy
>EOF
client$ cat dummyfile
dummy
client$ scp dummyfile matteo@myserver:uploads/
Password:

Torniamo sul server e, dal log di cron, vediamo che incrond ha catturato l’evento ed ha eseguito il nostro elaboratore:

# tail -1 /var/log/cron
Oct 27 10:35:43 myserver incrond[889]: (matteo) CMD (/home/matteo/scripts/elaborazione.sh /home/matteo/uploads/dummyfile)

 

Conclusioni

Abbiamo visto come installare e configurare incrond per monitorare il cambio di stato di un file in una directory.
Questa è una possibile soluzione pratica ad un problema che, tendenzialmente, si verifica abbastanza di frequente in ambienti enterprise, in cui ci sono flussi di lavoro locali/remoti che devono lavorare in relazione gli uni con gli altri.

In realtà, gli utilizzi di inotify sono estremamente ampi; per esempio, è il sistema che viene normalmente utilizzato dagli indicizzatori di filesystem su linux (per esempio, Beagle di SUSE).
Andando a monitorare l’intero filesystem (/) ed avendo le notifiche sulle modifiche di qualsiasi file al suo interno, l’indicizzatore può aggiornare i suoi database senza doversi rileggere l’intero filesystem.

Un’altro utilizzo può essere quello di monitorare il file di log di un’applicazione. Quando questa applicazione andrà a loggare qualcosa, possiamo mandare una mail contenente il file di configurazione a qualcuno.

Il limite è il vostro ingegno 😉