Uno script perl per monitorare l’aggiornamento di file su internet

Tavolta capita l’esigenza di dover monitorare dei file pubblicati su internet, nello specifico archivi zip, e sapere se questi son stati modificati dall’ultima volta che son stati scaricati.

Ricevere una notifica via email quando i file vengono aggiornati è una comodità non da poco.  Perciò ho preparato un piccolo script in linguaggio PERL che si occupa, nell’ordine:

  • recuperare l’elenco dei file da monitorare da un database.
  • di scaricare dal web i file da monitorare.
  • calcolare l’hash di ogni singolo file e confrontarlo con quello memorizzato in una base dati.
  • verificare se coincidano o meno; se son diversi significa che il file è stato modificato dall’ultima volta che è stato scaricato.
  • nel caso uno o più file siano stati modificati invii una notifica via email indicando quali sono i file modificati.

Predisposto lo script ho configurato il demone “crond“, un programma linux che si occupa di lanciare ad orari precisi o ad intervalli di tempo prefissati altri programmi, per lanciare automaticamente lo script ogni giorno. Grazie a ciò non ho più bisogno di verificare “a mano” se c’è stata qualche aggiornamento dei file e, nel caso ci siano state variazioni mi arriva una comoda notifica via email.

Questa è la query SQL che crea la tabella che contiene i dati sui file da controllare.

CREATE TABLE `CHECK_AGGIORNAMENTI` (
  -- la chiave primaria che identifica il dataset
  `ID_DATASET` int(11) NOT NULL,
  -- il nome del dataset, per facilità di lettura
  `NOME` varchar(45) NOT NULL,
  -- l'url del file da scaricare
  `URL` varchar(255) NOT NULL,
  -- l'ultimo hash del file
  `HASH` varchar(255) DEFAULT NULL,
  -- la data dell'ultimo aggiornamento
  `DATA_ULTIMO_AGGIORNAMENTO` date DEFAULT NULL,
  PRIMARY KEY (`ID_DATASET`)
);

E questo invece è lo script in perl.


#!/bin/perl
#questo script si occupa di monitorare i file pubblicati da diverse sorgenti e 
#di inviare una segnalazione all'utente quando rileva un qualche cambiamento in essi

#chiamata delle librerie
use strict;   #usa la sintassi rigida (ad esempio dai errore se vengono utilizzate variabili non precedentemente dichiarate)
use warnings; #scrivi anche i warnings
use DBI;
use File::Fetch;
use Digest::MD5 qw(md5 md5_hex md5_base64);
use Mail::Sender;

my $db_user = 'user';    #nome utente del database
my $db_pw = 'password';     #password
my $db_host = '127.0.0.1';  #indirizzo del db server
my $db_name = 'database'; #nome del database

my $mail_body = ""; #contiene il corpo dell'eventuale email
my $md5new;         
my $md5old;
my $file_content;
my $row; 
my $sth2;

# connettiti alla base dati
my $dbh = DBI->connect("DBI:mysql:database=".$db_name.";host=".$db_host,$db_user, $db_pw,{'RaiseError' => 1});

#preleva l'elenco dei file da monitorare
my $query = "SELECT * FROM CHECK_AGGIORNAMENTI";
my $sth = $dbh->prepare($query) or die "execution failed: ".$dbh->errstr();
$sth->execute();

#apri il file dove registrare il log dell'analisi
open(OUT,">", "/root/variazioni.log");

my $update_counter = 0; #conta quanti file son stati aggiornati

#per ogni riga prelevata dal DB
while($row = $sth->fetchrow_hashref())
{
	#scarica il file usando una subroutine definita sotto
	$file_content = fetch_file($row->{'URL'});
	$md5old = $row->{'HASH'};
	#calcola l'hash del contenuto del file
	$md5new = md5_hex($file_content);
	#se non e' variato comunica l'assenza di variazioni (usato per il debug)
	if($md5new eq $md5old) {
		print OUT "NESSUNA VARIAZIONE IN ".$row->{'NOME'}." VARIATO\n";
	}
	#altrimenti
	else
	{
		print OUT "DATASET ".$row->{'NOME'}." VARIATO\n";
		#aggiorna la base dati
		$query = "UPDATE CHECK_AGGIORNAMENTI SET HASH = '".$md5new."', DATA_ULTIMO_AGGIORNAMENTO = NOW() WHERE ID_DATASET = ".$row->{'ID_DATASET'}.";";
		$sth2 = $dbh->prepare($query);
		$sth2->execute();
                #aumenta di uno l'elenco dei file che son stati trovati aggiornati
		$update_counter++;
		#scrivi nel corpo della email il nome del dataset variato.
		$mail_body .= "variato il file dati di ".$row->{'NOME'}."\n\n";	
	}
}	
#se ci son stati cambiamenti invia anche un avviso via email usando una subroutine definita sotto.
if ($update_counter > 0)
{
	send_email( "COMUNICAZIONE VARIAZIONE FILE MONITORATI", $mail_body);
}
#chiudi la connessione alla base dati
$dbh->disconnect();
close(IN);

#subroutine di servizio; questa si occupa dello scaricamento del file dall'url che viene passato come parametro.
sub fetch_file
{
	my $url = shift @_;
	my $ff = File::Fetch->new(uri => $url);
	my $scalar;
	my $where = $ff->fetch(to => \$scalar) or die $ff->error;
	return $scalar;
}

#questa subroutine si occupa di inviare una email di avviso
sub send_email
{
	#prendi come oggetto della mail il primo parametro passato e come corpo il secondo.
	my $subject = shift @_;
	my $body = shift @_;
	#prepara l'email
	my  $sender = new Mail::Sender {smtp => 'posta@mioprovider', from => 'io@mioprovider' ,on_errors =>'code'} or die();

	ref ($sender = new Mail::Sender { from => 'io@mioprovider',
		   smtp => 'posta.mioprovider'})
	 or die "Error($sender) : $Mail::Sender::Error\n";

	 ref $sender->Open({to => 'destinatario_1@provider1,destinatario_2@provider2', subject => $subject})
			 or die "Error: $Mail::Sender::Error\n";
	 #scrivi il corpo della email
	 my $FH = $sender->GetHandle();
	 print $FH $body;
	 #spedisci l'email.
	 $sender->Close;
};


 

 

GianCarlo Massidda

Tappabuchi informatico; il mio motto è: "ut inpediunt, quae efficaciora sunt semper." (esiste sempre un modo più efficace per incasinare tutto).