domenica 30 gennaio 2011

Dati legacy: un piccolo dumper Perl per database PostgreSQL

Capita spesso di dover far dialogare la propria base dati PostgreSQL con un qualche tipo di applicazione legacy e/o proprietaria che non accetta nessuna forma di IPC o di data source configurabile. Solitamente in questi casi si è costretti a procedere attraverso l'import/export di file di testo in un qualche formato (CSV, tabulati, line-delimited, ecc.). Ebbene ultimamente ho dovuto anche io esportare dei dati "live" da un database PostgreSQL in modo che venissero riversati in un file di testo on-demand. Invece che scrivere un semplice script legato al contesto applicativo ho deciso di creare un mini driver piu' generale che permette un minimo di configurazione delle query e dei risultati. Ovviamente lo script Perl che propongo qui non è assolutamente un programma rock-solid, ma svolge abbastanza bene il suo compito; invito comunque chi abbia bisogno di estrazioni particolarmente complesse di costruire un qualche tipo di wrapper appositamente progettato.

Anzitutto ho deciso che ogni utente avrebbe potuto aver bisogno di query leggermente differenti (viste sui dati diverse), e quindi il componente dovrebbe poter caricare dinamicamente la query usando una qualche proprietà di ambiente, quale ad esempio la home dell'utente. Inoltre le query non possono avere i dati di selezione staticamente impostati, ma ci deve essere un qualche meccanismo di templating delle query: si deve poter specificare il tipo di query che l'utente vuole svolgere e questa deve essere effettivamente svolta con parametri passati dinamicamente. Insomma, una sorta di "prepared statement" del poveraccio!
Si immagini quindi di avere un file di testo, nella home utente (o in un qualche altro percorso parametrizzabile) che contenga il template di query:

SELECT pk, description
FROM   test
WHERE description like '{0}'
ORDER BY {1}



E' abbastanza ovvio che quello che si vuole ottenere è la sostituzione dei parametri posizionali {0} e {1} con valori reali e ottenuti dinamicamente all'atto dell'invocazione dell'estrattore. Segue lo script Perl che realizza questa funzione:


#!/usr/bin/perl

# variabili da utilizzare
$DATABASE = "mydb";
$HOST =  "localhost";
$MYSELF = "sqldump";
$QUERY_FILE =  $ENV{"HOME"} . "/$MYSELF" . "-" . "$DATABASE" . ".query";
$OUTPUT_FILE =  "/tmp/$MYSELF" . "-" . "$DATABASE" . ".txt";
$INTERNAL_FIELD_SEPARATOR = ";";
$FINAL_FIELD_SEPARATOR="\n";
$FINAL_RECORD_SEPARATOR=$FINAL_FIELD_SEPARATOR;



# lettura della query
# La query puo' essere parametrizzata con dei marcaposto {0} {1} ecc.
# Per ogni marcaposto ci deve essere un corrispondente parametro da linea di comando.
open( QUERY_FILE, "<" . "$QUERY_FILE" ) || die("\nImpossibile leggere il file di query\n$!\n");
while( $queryLine = ){
    chomp($queryLine);
    if( $queryLine =~ /\{(\d)\}/ ){

    $currentParam = $1;
    $queryLine =~ s/\{$currentParam\}/$ARGV[$currentParam]/g;
    }

   
    $query .= $queryLine . " ";

}


# Opzioni per l'esecuzione della query
$PSQL_OPTIONS = " -c \"$query\" -A -d $DATABASE  -F \'$INTERNAL_FIELD_SEPARATOR\' -h $HOST -t ";



# esecuzione del comando
open( OUTPUT, "psql $PSQL_OPTIONS |" ) || die("\nImpossibile eseguire il comando\n$!\n");

open( OUTPUT_FILE, ">" . $OUTPUT_FILE ) || die("\nImpossibile aprire il file in scrittura\n$!\n");

while( $sqlLine = ){

    chomp( $sqlLine );
    $sqlLine =~ s/$INTERNAL_FIELD_SEPARATOR/$FINAL_FIELD_SEPARATOR/g;
    $sqlLine .= $FINAL_RECORD_SEPARATOR;
    print OUTPUT_FILE $sqlLine;
}


Lo script è piuttosto semplice (come la maggior parte del codice Perl che scrivo!). La prima parte si preoccupa di impostare alcune variabili per puntare correttamente al database, usare un determinato tipo di delimitatore di record e campo, e cercare il file template della query nonché impostare il file dump testuale che verrà prodotto.
La prima parte di codice si occupa di ricostruire un'unica stringa di query SQL analizzando riga per riga l'eventuale presenza di parametri posizionali ({0},{1}, ecc.) e sostituendoli con il relativo parametro da riga di comando nella medesima posizione. Se ad esempio lo script viene invocato con:

sqldump.pl ciao description


la query SQL risultante sarà:

SELECT pk, description FROM   test WHERE description like 'ciao' ORDER BY description


Ancora una volta vale la pena ribadire la semplicità di questo approccio, che ben si presta a tecniche di SQL injection e ad altri problemi di sicurezza; ne consiglio ancora una volta il solo utilizzo in ambienti sicuri e comunque controllati.
Una volta ottenuta la query on-demand si puo' procedere all'interrogazione del database mediante le proprieta' di comunicazione con la shell del comando psql. In particolare al comando viene chiesto di eseguire la query in modalità di output non allineato, con un separatore di campo temporaneo. L'output della query viene passato mediante pipe al processo Perl (quindi si ha una fork del processo stesso) cosi' da poter gestire anche grosse moli di dati in uscita e senza avere ritardi dovuti alla produzione di file temporanei. Ogni riga di output viene poi ripulita e il separatore di campo e record corretti vengono applicati per la produzione dell'output testuale finale.
L'esecuzione batch di questo semplice script richiede la configurazione del file .pgpass per l'accesso ai database senza richiesta di username e password.
Questo script, ampiamente migliorabile, mostra come sia possibile gestire elasticamente l'input/output di un database PostgreSQL anche quando si sia forzati a usare soluzioni legacy come i file di testo. Ovviamente è possibile migliorare lo script stesso con una serie pressoché infinita di opzioni di configurazione.

sabato 29 gennaio 2011

JSF: popolare un menu' programmaticamente

La tag library JSF contiene una serie di elementi, quali ad esempio selectOneMenu, che lavorano su una serie di item da visualizzare in formato lista o menu' a discesa. Il problema di questi item è che non possono essere generati automaticamente nella pagina stessa, come si può invece fare con un foreach della libreria JSP. Per poter generare una lista di item occorre definire in un backing bean un metodo che ritorni un iterable di tipo SelectItem (quindi ad esempio List oppure SelectItem[]) e associare, tramite il tag speciale selectItems, questo elenco al componente stesso.
Il seguente codice mostra un esempio di generazione di un elenco di mesi:

      public final SelectItem[] getMesi(){
        SelectItem months[] = new SelectItem[ 12 ];


        months[ 0 ]     = new SelectItem( 0, "Gennaio" );
        months[ 1 ]     = new SelectItem( 1, "Febbraio" );
        months[ 2 ]     = new SelectItem( 2, "Marzo" );
        months[ 3 ]     = new SelectItem( 3, "Aprile" );
        months[ 4 ]     = new SelectItem( 4, "Maggio" );
        months[ 5 ]     = new SelectItem( 5, "Giugno" );
        months[ 6 ]     = new SelectItem( 6, "Luglio" );
        months[ 7 ]     = new SelectItem( 7, "Agosto" );
        months[ 8 ]     = new SelectItem( 8, "Settembre" );
        months[ 9 ]     = new SelectItem( 9, "Ottobre" );
        months[ 10 ]     = new SelectItem( 10, "Novembre" );
        months[ 11 ]     = new SelectItem( 11, "Dicembre" );

        return months;

    }

e l'associazione con il relativo componente JSF avviene come segue:

<h:selectOneMenu id="selezioneMese" value="#{bean.mesi }" >
  <f:selectItems value="#{bean.mesi }"/>
</h:selectOneMenu>


Con "bean" che rappresenta il nome (id) del backing bean associato alla applicazione JSF.

JSF: propagare le proprieta' di un bean ad un altro bean

Mi sono trovato nella curiosa situazione di avere un bean gestito da una applicazione JSF e di dover usare tale bean per l'inizializzazione di diversi valori di un altro bean gestito dalla stessa applicazione. Non ho trovato nessun modo esplicito di poter collegare in cascata i bean a run-time, e sono quindi ricorso al costruttore del secondo bean per ottenere il primo e usarlo come parametro di inizializzazione per se stesso.
Si ipotizzi di avere il bean A registrato con id beanA e il bean B registrato con id beanB e di dover eseguire su B il metodo B.initFromA( A a ). E' possibile inserire nel costruttore di B il seguente codice:

       public B(){
        super();
       try{
        FacesContext facesContext = FacesContext.getCurrentInstance();

        if( facesContext == null )
            // non sono in contesto JFS?
            return;

        Application webApplication = facesContext.getApplication();
        ValueBinding valueBinding = webApplication.createValueBinding( "#{beanA}" );
        A beanAFromWebApplication = (A) valueBinding.getValue( facesContext );
        initFromA( beanAFromWebApplication );
   
          }catch( Exception e ){
            // esecuzione non in contesto JFS
            // ...
       }
    }


Da notare che vengono gestite eventuali eccezioni di cast (se il bean non viene trovato oppure non e' del tipo corretto) che consentono l'esecuzione di B anche fuori dal contesto JFS (ossia come bean normale).

venerdì 28 gennaio 2011

JSF: istruzioni ECL condizionali

Il tag outputTest della tag lig JSF consente l'utilizzo di istruzioni condizionali, e quindi e' possibile scrivere qualcosa del genere:

<h:outputLabel value="#{ (user.administrator) ?
     'admin' :
     'utente misero' }  " >



Ove si controlla se l'utente e' amministratore e a seconda del relativo livello si stampa un messaggio informativo opportuno.

Dipendenze dei moduli Web in Eclipse Helios

Quando si sviluppa un progetto Web dinamico (Dynamic Web Project) in Eclipse, una funzionalita' molto comoda e' quella che permette di esportare automaticamente assieme alla propria web application una o piu' librerie utente. Questo veniva svolto dal J2EE Module Dependencies, che nelle ultime versioni di Eclipse e' stato sostituito dal piu' scarno Deployment Assembly. Sostanzialmente si tratta di andare nelle proprieta' di progetto e aggiungere, nella finestra di Deployment Assembly, le librerie utente che servono all'esecuzione del progetto.

JSF: associazione programmatica di un managed bean

E' possibile registrare un bean in un contesto JSF programmaticamente (e quindi non solo usando faces-config.xml) con semplici righe di codice:

// ottengo la applicazione web JSF
FacesContext facesContext     = FacesContext.getCurrentInstance();
Application webApplication     = facesContext.getApplication();

String beanValueBindingID =  "#{myBean}";

// creo il binding
ValueBinding valueBinding = w
    ebApplication.createValueBinding( beanValueBindingID );
valueBinding.setValue(facesContext, beanToManage );


Nell'esempio di cui sopra l'oggetto beanToManage viene associato all'applicazione Web con nome mybean e quindi puo' essere usato nelle taglib delle pagine JSP.

venerdì 21 gennaio 2011

Alfresco & PostgreSQL

Alfresco e' un sistema documentale di fascia enterprise, che nella sua versione community viene distribuito in bundle con MySQL. Inutile dire che sulla mia macchina viene installato su PostgreSQL (dopotutto se Alfresco e' un sistema Enterprise perche' non fornirgli uno storage Enterprise?), cosa relativamente banale ma poco documentata.
Anzitutto occorre creare un database sul cluster PostgreSQL all'interno del quale Alfresco possa memorizzare i propri dati; con grande slancio di fantasia si puo' creare un database "alfrescodb" con username "alfresco-pg" e password "alfresco-pg-pwd". Fatto questo occorre editare il file alfresco-global.properties che si trova nella directory /tomcat/shared/classes dell'installazione community di Alfresco. All'interno di tale file occorre commentare i parametri che puntano al database MySQL e inserire i seguenti per un corretto funzionamento di PostgreSQL:


db.driver=org.postgresql.Driver
db.username=alfresco-pg
db.password=alfresco-pg-pwd
db.name=alfrescodb
db.url=jdbc:postgresql://localhost/alfrescodb
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.query.substitutions=true TRUE, false FALSE
db.port=5432
db.host=localhost


Infine occorre copiare il jar del driver JDBC di PostgreSQL nella cartella /tomcat/lib (ossia nella directory delle librerie di Tomcat) affinche' il driver sopra citato sia caricabile dal servlet engine. Non resta quindi che avviare Alfresco, che inizialmente impieghera' alcuni minuti per la creazione del database e delle relative relazioni.

if( boolean.isEasyToUnderstand() )

Oggi riflettevo su come migliorare la leggibilita' del codice Java/C/C# e sono giunto alla conclusione che un buon metodo per aumentare la leggibilita' del codice e' quello di dare un nome esplicito a tutti i parametri dei metodi (ove possibile). E questo significa che, ad esempio, i valori booleani devono essere rimossi dalle chiamate di metodo e sostituiti con enumerazioni o costanti "parlanti".
Per meglio comprendere si consideri un esempio di codice come segue:

File myFile = new File("/tmp/test.txt");
byte[] mycontent = "TEST".getBytes();
MyFileManager.writeContent( myFile, mycontent, true );

Ora cosa significa quel "true" come ultimo parametro del metodo? Ad esempio potrebbe essere un indicatore che dice di lavorare in sostituzione invece che in append sul file, e quindi potrebbe essere migliorato il codice come segue:

File myFile = new File("/tmp/test.txt");
byte[] mycontent = "TEST".getBytes();
MyFileManager.writeContent( myFile, mycontent, MyFileManager.OVERWRITE );

e se si volesse lavorare in append si potrebbe scrivere:

File myFile = new File("/tmp/test.txt");
byte[] mycontent = "TEST".getBytes();
MyFileManager.writeContent( myFile, mycontent, MyFileManager.APPEND );


Inutile dire che a prima lettura queste scritture sono molto piu' chiare rispetto a quella che utilizza un valore booleano.
Colgo l'occasione per fare una riflessione sulle enumerazioni, che ben si prestano allo scopo appena descritto: uno degli svantaggi delle enum e' che per avere dei valori "stampabili" o da usare come chiavi per hash e indici occorre sovrascrivere il toString() per ogni elemento della enumerazione. In altre parole i singoli valori di una enum sono visti come una inner class, e come tale possono avere un valore stringa che per default e' il valore della enum stessa, ma che puo' essere sovrascritto. Ad esempio:

public enum EMPLOYEE_STATUS{
       HIRED {
                public String toString(){
                 return "assunto";
         }
       },


       FIRED {
                public String toString(){
                 return "licenziato";
         }
       },

}

Questo make up si rende necessario per limitare i valori che vengono usati nel software e rendere il tutto piu' consistente. Ma in questo caso, l'utilizzo di stringhe "secche" risulta piu' semplice che una enumerazione:

public class EMPLOYEE_STATUS{
       public static final String HIRED = "assunto";
       public static final String FIRED = "licenziato";
}

Inutile dire che lo svantaggio piu' evidente di questo sistema e' che e' bene vincolare un parametro ad una serie limitata di possibilita' e non ad un generico "int" o "String", che potrebbe assumere qualunque valore.

Il triste cammino di OpenSolaris (e degli altri...)

Ho trovato una coppia di blog post veramente interessanti sul caso Oracle-Sun e i progetti Open Source. Il primo post mostra dove siano finiti i principali sviluppatori e membri del core Sun ad un anno (circa) dalla acquisizione di Oracle; il secondo post mostra invece lo stato attuale dei progetti Open Source iniziati e/o sostenuti da Sun. Ci sono diverse cose interessanti da osservare: la prima e' che OpenSolaris viene considerato un progetto strategico per Oracle, ma non per questo viene sviluppato con i canoni OpenSource caratteristici di tale progetto. In altre parole, Oracle ha "divorato" il buono prodotto da Sun e dalla comunita' per creare un prodotto strategico nel mercato commerciale. L'altra cosa interessante da notare e' che il supporto MySQL, seppur non modificato negli intenti, ha subito una profonda variazione. Se e' pur vero che ora MySQL puo' contare sul supporto nativo InnoDB (che e' il motore di default per la versione 5.5), molti degli sviluppatori principali sono "scappati" verso altri progetti come MariaDB.

Record Counting ^ 2

Mi sono trovato a dover mettere la mani sui sorgenti di una applicazione PHP che accede ad un database PostgreSQL mediante ADODB, scovando un errore a prima vista di difficile individuazione anche se molto banale. Questo errore oltretutto non e' strettamente correlato ne' a PostgreSQL ne' a PHP, ma e' un semplice esempio di "cattiva programmazione" (o meglio di programmazione "distratta").
Il codice in questione doveva inserire un record in una tabella, senza sapere a priori se il record fosse da modificare o da inserire from-scratch; per discriminare le possibilita' il codice effettuava prima una ricerca fra i dati della tabella per vedere quante righe associate ai dati in esame esistevano. Da notare che la selezione implicitamente faceva un conteggio del numero di righe esistenti. Il risultato veniva poi passato ad un metodo di ADODB per il conteggio delle tuple ritornate, quindi qualcosa del tipo:

$query = "SELECT count(id) FROM myTable";
$resultSet = $driver->execute( $query );
$rowCount = $resultset->RecordCount();


Notato l'errore? Ebbene inizialmente io non me ne sono accorto, ma il problema del pezzo di codice qui sopra e' che la query che viene richiesta al database e' una "count", e su questa si fa il record count nuovamente. Ora il count di count non ritorna un count al quadrato (eh eh) ma sempre 1: la "SELECT count(id)" ritorna una sola tupla monodimensionale con il numero di record, e quindi il record count di una sola tupla ritorna sempre 1, anche se il valore della tupla (e quindi il conteggio effettivo del count) e' 0! Il codice va quindi corretto con qualcosa del tipo:

$query = "SELECT id FROM myTable";
$resultSet = $driver->execute( $query );
$rowCount = $resultset->RecordCount();


oppure con qualcosa del tipo:

$query = "SELECT count(id) AS conteggio FROM myTable";
$resultSet = $driver->execute( $query );
$tuple = $resultset->GetArray();
$rowCount = $tuple[0][0];



L'errore e' subdolo poiche' anche leggendo il codice e' chiaro quali siano le intenzioni del programmatore: contare i record presenti. Tuttavia e' il "doppio conteggio" a mascherare l'errore di esecuzione!

Ubuntu & Qt

Come si puo' leggere da un post di Mark Shuttleworth la prossima release di Ubuntu verra' distribuita con applicazioni basate su Qt all'interno del cd di installazione. L'idea di fondo e' buona: si apre la strada a configurazioni Qt-based e binding per far convivere una applicazione Qt all'interno del desktop Gnome come fosse una applicazione nativa. Va notato che si parla di applicazioni Qt, non KDE, che sono a loro volta uno "strato" sopra quello normale delle applicazioni Qt. Cio' significa anche che, nel breve termine, e' piu' facile vedere integrata una applicazione Qt pura che una KDE, anche se lo stesso Mark non esclude che in futuro le applicazioni KDE, forti dei nuovi binding Qt, possano integrarsi meglio con il desktop Ubuntu.
La notizia e' sicuramente positiva, poiche' una maggiore adozione di Qt non puo' che incidere positivamente su KDE, che potrebbe essere brutalmente considerato il "Qt-desktop". Tuttavia penso che le ragioni dietro a questa scelta da parte di Canonical non siano cosi' Open Source come le si vuole vendere. Anzitutto non capisco perche' spingere per questa integrazione Qt quando KDE ha sempre aderito agli standard OpenDesktop; sarebbe sicuramente meglio che entrambi i desktop aderissero appieno agli standard per una maggiore interoperabilita'. Analoga considerazione puo' essere fatta nei confronti delle impostazioni utente e della configurazione di entrambi i desktop. Ma ancora non mi convince il fatto che questo processo di integrazione sia rivolto a Qt e non a KDE. Ubuntu vuole avvicinarsi al mondo Nokia, grande sostenitore di Qt? Non posso poi fare a meno di notare che questa apertura verso il mondo Qt arriva proprio nel momento "vivo" della migrazione a GTK+3 e al relativo desktop Gnome 3, e anche se Mark specifica nel suo post che non si tratta di una critica a Gnome/GTK+, penso che questa cosa non sia completamente scorrelata.
Infine non bisogna dimenticare che, mentre KDE ha sempre cercato di lavorare in modo compatibile con GTK+, lo stesso non vale per Gnome. Da un certo punto di vista KDE e' sempre risultato piu' aperto alle altre realta', basti pensare che molti widget di Plasma sono stati portati verso altre piattaforme, o che esiste la possibilita' di applicare temi specifici alle applicazioni GTK+ sotto KDE (non sono a conoscenza di un'analogo sistema sotto Gnome); Gnome dall'altra parte e' sempre stato molto GTK+ centrico, e anche per motivi ideologici (si vedano i commenti di Stallman e l'idea dietro all'intero progetto) e' sempre stato riluttante ad accettare lavori "esterni".
Francamente non riesco a giudicare pienamente positiva questa notizia, ma devo anche riconoscere che Ubuntu ha la potenza, la diffusione e il bacino di utenti tale per smuovere le comunita' verso una vera integrazione, obbiettivo fallito miseramente in passato. Vedremo in futuro se questi sforzi saranno a vantaggio esclusivo di Canonical/Ubuntu o se forniranno qualcosa di utile anche agli sviluppatori.

Firefox Sync: sincronizzare le preferenze fra istanze differenti del browser

E' indubbio che lista dei bookmark di un browser rappresenta oggi giorno un bagaglio di conoscenza indispensabile, soprattutto per svolgere con efficienza un lavoro tecnico. Nel tempo io ho accumulato un elevato numero di bookmarks, rigorosamente organizzati e ordinati per soggetto e tematica, che mi consentono di cercare rapidamente la soluzione a problemi quotidiani. Il problema con i bookmark e' che questi devono essere trasferiti da un computer all'altro per essere realmente utilizzabili ovunque. Se da un lato Firefox semplifica molto questo aspetto, consentendo una rapida esportazione/importazione dei preferiti (racchiusi in un file XML), servizi di storage on-line dei bookmark consentono di spingersi oltre permettendo la sincronizzazione bidirezionale dei preferiti.
Uno di questi servizi e' Firefox-Sync che, tramite registrazione gratuita, consente agli utenti di memorizzare su uno storage remoto le impostazioni del proprio Firefox da sincronizzare poi su un'altra installazione. Consiglio vivamente di non memorizzare password e history, poiche' occorre sempre tenere presente che si stanno affidando i dati ad uno storage nel cloud sul quale non si ha controllo. In ogni caso il sistema funziona molto bene, e' veloce e semplice da usare, e quindi lo consiglio caldamente a tutti gli utenti Firefox.

Una cosa che mi ha insegnato l'arco...

Questa mattina, mentre salivo le scale per arrivare al mio ufficio, riflettevo su una frase letta sul sito di Bruce Momjian, che inizialmente aveva creato scalpore fra gli studenti alla quale l'avevo mostrata:
In a school setting, children spend all their time with people their own age.  However, once you finish school, this is rarely the case.  Our children interact with people of all ages, and have many outside activities to foster socialization.  They have a music, gym, and art classes at church once a week with other home-school children, and are involved in gymnastics, marble competitions, musical instrument instruction, choir, drama, and professional theater.  This is easier to do because they don't spend eight hours in school every day.  In fact, studies have shown that home-schooled children are more involved in community activities and better at socializing with people of different ages than traditionally schooled children.

Scelta sicuramente coraggiosa, che a molti potrebbe far storcere il naso. In effetti e' stata la mia stessa esperienza, anche se non cosi' esasperata: quando praticavo tiro con l'arco a livello agonistico mi sono spesso trovato a dover stare in mezzo a persone di differenti eta' e sesso. Basti pensare che l'assegnazione dei bersagli in una gara e' totalmente casuale (eccezion fatta per i Campionati) e quindi di fatto un ragazzo puo' capitare sullo stesso bersaglio con un senior, una donna e un ragazzo di eta' diversa. E questo significa passare l'intero tempo della competizione, siano poche ore che due giorni, assieme a loro, fraternizzando e confrontandosi con eta' e stili differenti. Cosa ancora piu' accentuata se si pensa che i giovani sono spesso accompagnati alle competizioni e/o agli allenamenti da altri arcieri della stessa societa' sportiva ma, ovviamente, adulti. Ne consegue quindi che l'arco, fra i tanti pregi, ha la caratteristica di spingere i giovani a confrontarsi non solo per quanto riguarda i risultati sportivi, ma anche per quanto riguarda gli aspetti sociologici, con persone di eta', culture e sesso differenti.

giovedì 13 gennaio 2011

Mi sono trovato nella strana e interessante situazione di dover collegare la mia macchina Linux in una rete ove e' presente un server WINS. Wins e' un (assurdo) protocollo di comunicazione fra postazioni Microsoft Windows, sostanzialmente fa le veci di un DNS per la risoluzione dei nomi NetBios. Inutile dire che Unix non necessita di una simile complicazione, ma per l'interoperabilita' e' necessario che Samba consenta la connessione al server WINS. A complicare ancora di piu' la cosa vi era il fatto che il server di fatto si trovava in una sottorete distinta da quella della mia macchina e che le macchine Windows che dovevo in realta' raggiungere non erano nemmeno "dominate" dal server WINS in questione. In altre parole un semplice nmblookup contro il server WINS non risolveva i nomi NetBios che cercavo di ottenere, mentre una query ricorsiva come

nmblookup -U -R 1.2.3.4 nome-netbios

(con 1.2.3.4 indirizzo IP del server WINS)
funzionava correttamente. Due sono le opzioni da configurare in Samba affinche' la risoluzione dei nomi corrispondente al sopra citato invio ricorsivo funzioni:

 wins server = 1.2.3.4
 wins proxy = yes
 name resolve order = wins lmhosts host bcast


che rispettivamente abilitano il server WINS da interrogare in prima istanza, istrumentano i comandi Samba per inviare query ricorsive e forzano il processo di risoluzione dei nomi NetBios a partire prima dal server WINS, passando poi per i classici file degli host e infine via broadcast sulla rete.

Se poi si vuole anche abilitare la risoluzione dei nomi NetBios come nomi host normali (e quindi si vuole ad esempio pingare un nome host NetBios) occorre modificare il file nsswitch.conf inserendo nella riga hosts anche la direttiva wins:

hosts:          files mdns4_minimal [NOTFOUND=return] dns wins mdns4

resolv.conf: errore sintattico

Il file /etc/resolv.conf istruisce il resolver di un sistema Unix su quali DNS contattare per la risoluzione dei nomi di host. Una direttiva di tale file, "search", indica quale dominio appendere in default per un nome host semplice (o meglio per un nome che non viene risolto correttamente). Per un errore di battitura, ho inserito una direttiva search con un nome di dominio racchiuso fra doppi apici. I doppi apici non vengono interpolati, e quindi il dominio inserito comprendeva gli apici come carattere valido da risolvere, con conseguente impossibilita' da parte del resolver di risolvere qualunque nome di host non qualificato.

Eclipse (Equinox): specificare quale JVM usare per eseguire l'ambiente

La configurazione di Eclipse/OSGi avviene tramite un file di proprieta' di avvio, solitamente denominato eclipse.ini (da notare l'orrendo retaggio .ini tipico della configurazione Microsoft Windows!) che contiene una lista di proprieta' lette dal launcher nativo OSGi /Equinoxusato dalla piattaforma Eclipse stessa.
Una opzione molto importante per l'avvio di Eclipse (IDE) e' la virtual machine da utilizzare per eseguire l'IDE stesso. Sembra banale, ma nell'epoca degli aggiornamenti software automatici, e' molto facile perdere il controllo su quale JVM/JDK viene usata per eseguire diverse applicazioni, specialmente se si considera che Eclipse non tiene conto di variabili di ambiente (es. la classica JAVA_HOME). Specificare la JVM da usare per l'avvio di Eclipse (IDE) e' molto semplice: si deve inserire una riga con l'opzione -vm e sulla riga seguente il path completo all'eseguibile java da usare, come ad esempio:

-vm
/sviluppo/java/jdk1.6.0_23/bin/java


Da notare che questa opzione deve essere specificata prima della riga -vmargs, poiche' tutte le righe che seguono l'opzione -vmargs verranno trattate come parametri da passare alla virtual machine stessa. E' anche possibile specificare l'opzione su riga di comando, nel qual caso occorre inserire il path dell'eseguibile fra apici doppi.

domenica 2 gennaio 2011

sfdumper e iterazioni multiple

sfdumper è un ottimo programma scritto interamente come script shell per il recupero selettivo di file da una immagine (anche raw) di un disco o di una partizione, e fa parte della toolbox per l'analisi forense dei sistemi digitali. Durante il suo utilizzo ho notato che, nel caso di iterazioni ripetute sulla stessa immagine, i file eliminati non sono relativi al tipo della ricerca in corso, ma contengono l'intera lista dei file già trovati nei passaggi precedenti. Sembra quasi che i file eliminati siano tenuti in una lista in solo append. Per evitare il problema, il seguente semplice script shell lanciato con un solo parametro che è la directory contenitore dei dati recuperati permette di eliminare i file cancellati ed erroneamenti recuperati multiple volte dentro a directory che dovrebbe contenere file di altro tipo.

#!/bin/bash

# controllo parametri
if [ $# -le 0 ]
then
    echo "Usage: $0 "
    exit 1
fi

# si entra nella directory di partenza per il recupero
cd $1

ERASED=0

for d in *
do

    # salto tutto cio' che non e' una directory
    if [ ! -d "$d" ]
 then
 echo "Salto la non directory $d"
 continue
    fi

    DIR_NAME="$d"

    # salto le directory speciali TMP e report
    if [ "$DIR_NAME" == "tmp" -o "$DIR_NAME" == "report" ]
 then
 echo "Salto le directory speciali ($DIR_NAME)"
 continue
    fi

    FILE_TYPE=`echo $DIR_NAME | awk -F '_' '{print $2;}' `
    echo "Entro in $DIR_NAME per eliminare i file che non sono $FILE_TYPE"
    cd "$DIR_NAME"

    for d2 in *
    do
 if [ -d "$d2" ]
 then
     cd $d2

     for f in *
     do
  if [ -f $f ] 
  then
      
      case $f in
   *${FILE_TYPE}) ;;
   *)  echo "Cancellazione di $f da $DIR_NAME"
       rm $f
       ERASED=$(( ERASED + 1 ))
       ;;
      esac
  fi
     done
     
     cd ..
 fi
    done
    cd ..

done

echo "File eliminati $ERASED"