venerdì 3 ottobre 2008

Database: anteporre un prefisso ai nomi delle tabelle

E' una sana abitudine che può evitare fastidiosi mal di testa da debug.
Usando un ORM per gestire il database (come DBIx::Class in perl), il codice SQL viene generato in modo automatico. Se una delle tabelle ha come nome una parola riservata (ad esempio "group" per indicare un gruppo di oggetti o un gruppo musicale), l'SQL prodotto da una chiamata all'ORM può generare degli errori di sintassi, a causa della presenza di una parola riservata ("GROUP", appunto) in una posizione in cui il parser si aspetta un identificativo di tabella.
Molto probabilmente si sarebbe indotti a cercare un bug nel proprio codice applicativo (ad esempio nella definizione delle relazioni tra le tabelle) oppure nel codice di generazione SQL dell'ORM, in sessioni di debug interminabili e, naturalmente, del tutto infruttuose.
Utilizzando un prefisso per il nome di ogni tabella ci si pone al riparo da questo tipo di problemi e si ha la possibilità di scegliere liberamente i nomi ritenuti più intuitivi.

Riferimenti:

http://lists.scsys.co.uk/pipermail/dbix-class/2008-October/006854.html

martedì 2 settembre 2008

Subversion vs Samba vs UTF-8 (opzione iocharset)

Situazione
Salvataggio dei dati di un pc su un server Gentoo Linux tramite subversion.

Sul server il charset di default è UTF-8:

# cat /etc/env.d/02locale
LC_ALL="it_IT.utf8"
LANG="it_IT.utf8"


# set|grep -i LC_ALL
LC_ALL=it_IT.utf8

Per accedere ai dati, monto lo share nascosto "C$" in modalità "cifs":

mount -t cifs //pc-utente/C$ /mnt

Dato che il repository subversion è ancora da inizializzare, eseguo un "import":

svn import /mnt/Documents\ and\ Settings/utente file:///dati/backup/SVNRepos

Problema
Ottengo però un messaggio di errore:

Aggiungo mnt/Documents and Settings/utente/Recent
svn: Dati UTF-8 validi
(hex: 43 68 65 63 6b 20 69 6e 20 57 69 6e 64 6f 77 73 20 55 70 64 61 74 65 20)
seguito da una sequence UTF-8 non valida
(hex: bb 20 4d 79)

Soluzione
In fase di mount si deve indicare in modo esplicito la codifica da utilizzare:

mount -t cifs -o iocharset=utf8 //pc-utente/C$ /mnt

venerdì 29 agosto 2008

Recupero dati con dd_rescue

Capita che un hard disk presenti dei settori danneggiati. Se non si tiene d'occhio la situazione e non si sostituisce il disco per tempo, il numero di errori cresce, fino a quando i meccanismi di auto-riparazione del disco esauriscono le proprie risorse, e gli errori di lettura divengono evidenti.

Uno strumento che può risultare utile in questi casi è dd_rescue. Dal punto di vista della funzionalità è del tutto simile a dd: permette di copiare blocchi di dati da un file o dispositivo ad un altro.
Realizzare una "copia-specchio" di un hard disk o di una partizione è molto semplice con dd:

dd if=/dev/hda of=/dev/hdc

(supponendo che hda sia il disco da copiare e hdc sia il disco di destinazione di capacità pari o superiore ad hda).

Il limite maggiore di dd è che interrompe l'operazione di copia al primo errore di lettura che incontra nel dispositivo sorgente.

Questo limite è superato da dd_rescue grazie ad alcune potenti opzioni che permettono di gestire gli errori di lettura in modo molto più "intelligente".

Dalla man page:

-b softbs
block size for copy operation (def=16384)

-B hardbs
fallback block size in case of errs (def=512)

-A
Always write blocks, zeroed if err (def=no)


Il parametro -b (b _minuscola_) indica quanti byte alla volta vengono copiati dalla sorgente alla destinazione. Usando un valore "elevato", ad esempio 65536, la copia procede più spedita, perché si riduce il numero di system call effettuate dal programma, spostando in questo modo il collo di bottiglia dalla CPU al sottosistema di I/O (in altre parole si sfrutta tutta la banda passante messa a disposizione dall'insieme discon sorgente, disco destinazione, chipset).

Il parametro -B (b _maiuscola_) entra in gioco quando si verificano degli errori di lettura dal dispositivo sorgente. Quando un settore di dimensione "softbs" risulta illeggibile, dd_rescue tenta la rilettura con settori di dimensione "hardbs".
Se al posto del valore di default pari a 512 byte si utilizza il valore di 1 byte, si evita che per ogni byte illeggibile ne vengano scartati altri 511. Si tenta cioè di recuperare il maggior numero di byte possibile, verificando per ciascuno la possibilità o meno di effettuare con successo la sua lettura.

Infine, il parametro -A indica al programma di sostituire i blocchi illeggibili del dispositvo sorgente con blocchi costituiti da byte nulli. Se si adotta l'opzione -B 1 come suggerito prima, significa che tutti i byte leggibili verranno copiati, salvo quelli non leggibili che verranno scritti come zeri.

Con queste impostazioni si ottiene un duplice beneficio: grazie al valore elevato di -b la copia procederà spedita nelle zone del disco non danneggiate, mentre grazie al valore unitario di -B in caso di errore si procederà un byte alla volta, riducendo al minimo la corruzione dei dati nell'immagine di destinazione.

Esempio completo:

- supponiamo che /dev/hda sia il disco sorgente, danneggiato;
- supponiamo che /dev/hdc sia il disco destinazione, privo di difetti;

dd_rescue -b 65535 -B 1 -A -v -l copia_specchio.log /dev/hda /dev/hdc

con l'opzione -v si ottiene una indicazione chiara dello stato di avanzamento della copia; con l'opzione -l si salvano in un file di testo i problemi incontrati durante l'operazione, utile per un'analisi a posteriori del problema.

giovedì 28 agosto 2008

Postgresql: server status senza PgAdmin

Per conoscere lo stato del server postgresql tramite SQL (ad esempio con psql o da programma), basta usare questo comando:

SELECT * FROM pg_stat_activity;

giovedì 21 agosto 2008

testdisk mi ha salvato la vita (o quasi...)

Situazione: hard disk esterno USB LaCie da 500 GB collegato al computer. Avrei dovuto scollegarlo, ma me ne sono dimenticato. Lancio l'installazione di Windows XP, e nella schermata di scelta delle partizioni vedo un insolito "affollamento": partizioni di varie dimensioni miste a spazi non partizionati... Non mi interessano i dati presenti sull'hard disk del PC, e sono molto, troppo di fretta, quindi cancello tutte le partizioni che vedo... per poi accorgermi che due di quelle appena cancellate appartengono all'hard disk esterno!!

Dopo un attimo di painco, mi ricordo che proprio il giorno prima avevo letto su una rivista dedicata a GNU/Linux una breve recensione dell'utility TestDisk.

Decido di provarla.
Scarico l'eseguibile precompilato per GNU/Linux, lo lancio come root ed eseguo una scansione del disco usb. In un istante vedo materializzarsi sullo schermo le due parzioni appena cancellate. Leggo attentamente le opzioni che il programma mette a disposizione per non fare altri danni, poi finalmente premo su "write", per salvare la tabella delle partizioni. Un messaggio mi avvisa che è necessario un reboot... in realtà è sufficiente scollegare e ricollegare l'hard disk per permettere al sistema operativo di rileggere la tabella delle partizioni del dispositivo.

Collego il cavo USB e incrocio le dita... i due volumi compaiono! Li apro con il file manager e i file sono ancora tutti al loro posto!

La morale è: mai interessarsi a utility di recupero dati trovate per caso... è segno che presto vi serviranno!

mercoledì 26 marzo 2008

Configurare Xorg per il monitor HP w2007v

La chiave della configurazione è la riga seguente:

ModeLine "1680x1050" 146.2 1680 1960 2136 2240 1050 1053 1059 1089

La sezione "Monitor" completa è la seguente:

Section "Monitor"
Identifier "Monitor0"
VendorName "HWP"
ModelName "HP w2007"
HorizSync 24.0 - 83.0
VertRefresh 60
Option "DPMS"
ModeLine "1680x1050" 146.2 1680 1960 2136 2240 1050 1053 1059 1089
#h_active: 1680 h_sync: 1960 h_sync_end 2136 h_blank_end 2240 h_border: 0
#v_active: 1050 v_sync: 1053 v_sync_end 1059 v_blanking: 1089 v_border: 0
EndSection

La corrispondente sezione "Screen", che utilizza la modalità "1680x1050" appena definita è infine:

ection "Screen"
Identifier "Screen0"
Device "Card0"
Monitor "Monitor0"
DefaultColorDepth 24
SubSection "Display"
Depth 24
Modes "1680x1050"
EndSubSection
EndSection

venerdì 4 gennaio 2008

Linux: creare un modulo del kernel

Introduzione
Lo scopo di questo post è descrivere l'infrastruttura di base per lo sviluppo di un modulo del kernel. Realizzeremo a questo proposito il classico "hallo-world", cioè un modulo la cui unica funzionalità sarà quella di annunciare, tramite un messaggio nei log di sistema, il suo caricamento o la sua rimozione dal kernel.
Non ho ovviamente la pretesa di scrivere una guida esaustiva, ma intendo riportare quanto ho appreso durante la lettura dei documenti del kernel e i risultati delle mie prove "sul campo".

Prerequisiti
L'ambiente su cui ho lavorato è una OpenSuse 10.2 con kernel 2.6.23.9 compilato manualmente. Credo però che la compilazione a partire dai sorgenti non sia strettamente necessaria, e che basti disporre del pacchetti di sviluppo del kernel, ed eventualmente del pacchetto sorgenti se si vuole consultare la documentazione ufficiale (oltre ovviamente agli strumenti di sviluppo come make, gcc, ecc.).

Preparazione
Per prima cosa decidiamo il nome del modulo (con molta fantasia ho scelto "modulo_prova"), e creiamo una cartella di lavoro (nel mio caso è Sviluppo/kernel/modulo_prova).
All'interno di questa cartella creeremo tutti i file necessari alla realizzazione del modulo.

La compilazione di un modulo del kernel non può prescindere dalle opzioni con cui è stato compilato il kernel stesso, pena l'impossibilità di caricamento o, peggio, l'introduzione di gravi instabilità nel sistema.
Per questo non è sufficiente dotarsi di un normale Makefile, ma è necessario sfruttare il sistema di compilazione del kernel stesso.
Come spiegato nei documenti citati in fodo all'articolo, questo sistema si basa sia sui comuni Makefile sia sui file Kbuild.

Nel nostro caso il file Kbuild è molto semplice. Il suo contenuto è costituito dalla seguente riga:

obj-m += modulo_prova.o

La direttiva obj-m sta ad indicare che vogliamo produrre un modulo. Un'alternativa è utilizzare obj-y, per indicare che vogliamo integrare il codice oggetto direttamente nel kernel. Le lettere "m" e "y" non sono ovviamente casuali: derivano infatti dai valori delle direttive CONFIG_* presenti nel file di configurazione del kernel (/usr/src/linux/.config).
Il valore della direttiva, "modulo_prova.o", indicha al sistema di build qual è il file oggetto da includere nel modulo. Per produrre tale file oggetto il sistema cercherà automaticamente il file sorgente corrispondente, sostituendo l'estensione ".o" con ".c".

Il Makefile contiene il comando da impartire per avviare la compilazione. Dato che stiamo parlando del kernel, è necessario fornire a "make" due informazioni importanti: la prima è la build del kernel nel quale il modulo verrà inserito, la seconda è la cartella di lavoro in cui si trovano i sorgenti del modulo (stiamo infatti operando al di fuori dell'albero dei sorgenti del kernel).
Il contenuto del makefile è dunque il seguente:

KERNELDIR := /lib/modules/`uname -r`/build
all::
make -C $(KERNELDIR) M=`pwd`


L'opzione "-C" indica a make di spostarsi nella cartella indicata prima di effettuare qualsiasi operazione. In questo modo viene avviata un'operazione ricorsiva del tutto simile a quella che avviene durante la costruzione del kernel vero e proprio. Usando "uname -r" produrremo un modulo per il kernel attualmente in esecuzione.
La direttiva "M=`pwd`" indica che il codice del modulo si trova nella cartella corrente.

Il codice
Veniamo ora al codice vero e propio.
Il contenuto del file modulo_prova.c è il seguente:

#include <linux/module.h>
#include <linux/kernel.h>

static int __init prova_init(void)
{
printk(KERN_INFO "Modulo 'prova' inizializzato\n");
return 0;
}

static void __exit prova_exit(void)
{
printk(KERN_INFO "Modulo 'prova' terminato\n");
}

module_init(prova_init);
module_exit(prova_exit);
MODULE_LICENSE("GPL");


Le due funzioni di inizializzazione e di uscita non fanno altro che emettere una stringa sul log di sistema. In questo modo possiamo verificare l'avvenuta esecuzione del codice del modulo.
Le funzioni module_init() e module_exit() indicano al kernel quali sono le funzioni da chiamare rispettivamente al caricamento e alla rimozione del modulo.

Compilazione
Grazie alla preparazione svolta in precedenza la compilazione si risolve nel semplice comando

make

impartito dalla cartella di lavoro.
Dovrebbero essere prodotti alcuni file, tra i quali il più importante è ovviamente modulo_prova.ko, che rappresenta il modulo nella sua forma compilata adatta al caricamento all'interno del kernel.

Utilizzo
Per vedere il risultato delle nostre fatiche è sufficiente inserire e rimuovere il modulo e verificare che compaiano i messaggi nel log di sistema, impartendo i seguenti comandi come utente root:

insmod modulo_prova.ko
dmesg
rmmod modulo_prova
dmesg


Riferimenti
La documentazione presente nei sorgenti di Linux è esaustiva ma piuttosto complessa. Per lo sviluppo dei moduli due documenti fondamentali sono Documentation/kbuild/modules.txt e Documentation/kbuild/makefiles.txt.