<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Fabio's Tech Blog</title>
	
	<link>http://www.codewithstyle.eu</link>
	<description>Idee e appunti per il mondo IT</description>
	<lastBuildDate>Wed, 17 Feb 2010 00:38:26 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/FabiosTechBlog" /><feedburner:info uri="fabiostechblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Creazione di un raid software livello 5 con Debian o Ubuntu</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/Awuw5pry6uc/</link>
		<comments>http://www.codewithstyle.eu/2010/02/17/creazione-di-un-raid-software-livello-5-con-debian-o-ubuntu/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 00:27:17 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[filesystem]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[mdadm]]></category>
		<category><![CDATA[raid]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=282</guid>
		<description><![CDATA[Nella gestione dei dati una delle tecniche migliori da utilizzare è sicuramente il RAID, in particolare tra tutti i livelli disponibili per questa tecnologia, quello che offre il miglior rapporto tra prestazioni, uso disco e ridondanza è sicuramente il livello 5. Esistono 3 tipologie di RAID a livello implementativo

RAID hardware: disponibile sui server di fascia [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-medium wp-image-289" style="margin: 5px;" title="RAID5" src="http://www.codewithstyle.eu/wp-content/uploads/2010/02/RAID5-300x245.png" alt="" width="210" height="172" />Nella gestione dei dati una delle tecniche migliori da utilizzare è sicuramente il <a href="http://it.wikipedia.org/wiki/RAID">RAID</a>, in particolare tra tutti i livelli disponibili per questa tecnologia, quello che offre il miglior rapporto tra prestazioni, uso disco e ridondanza è sicuramente il livello 5. Esistono 3 tipologie di RAID a livello implementativo</p>
<ul>
<li>RAID hardware: disponibile sui server di fascia alta, è quello che offre forse le migliori performance in quanto le operazioni vengono eseguite tutte dal controller in maniera completamente trasparente, ma ha sicuramente i costi più elevati. Inoltre utilizzando questa tipologia di raid i dati sono legati al controller utilizzato e non possono essere utilizzati ad esempio in un&#8217;altra macchina senza che si utilizzi lo stesso controlloer</li>
<li>FAKERAID: ovvero RAID hardware a basso costo. Questa è la tecnologia che viene usata sui controller di fascia bassa oppure implementata nelle motherboard dei computer desktop. Questa tecnologia viene chiamata fakeraid in quanto non è un RAID vero e proprio ma una parte dell&#8217;implementazione e della computazione viene lasciata alla CPU e l&#8217;interfacciamento con i dati avviene tramite un driver. Qui le performance sono decisamente più basse e rimane il problema del trasporto dei dati da un sistema all&#8217;altro. Inoltre non è detto che il driver di questi controller sia disponibile per tutti i sistemi operativi.</li>
<li>RAID software: quello di cui discuteremo qui. Il maggiore vantaggio di questa tecnologia è il controllo assoluto sui propri dati, si possono aggiungere e rimuovere dispositivi a piacere e soprattutto si possono trasferire da una macchina all&#8217;altra. Inoltre è possibile eseguire le operazioni di manutenzione a sistema attivo, cosa che non è possibile negli altri due casi in cui è richiesto un reboot.<span id="more-282"></span></li>
</ul>
<p>Per questo tutorial utilizzerò una macchina virtuale in modo da poter aggiungere dischi a piacimento e simulare la rottura di un disco rimuovendolo dalla macchina virtuale. Il sistema operativo di questa macchina virtuale è Ubuntu 9.10, ma i comandi ed i pacchetti utilizzati sono gli stessi che si utilizzerebbero su Debian. L&#8217;unico pacchetto che va installato per poter creare e manipolare array di dischi è il pacchetto mdadm che mette a disposizione l&#8217;omonimo comando.</p>
<p>Per creare un array di dischi a utilizzando il RAID5 sono necessari almeno 3 dischi/partizioni della stessa dimensione. Degli N dischi utilizzati uno di essi è utilizzato come codice di controllo in maniera circolare, per cui lo spazio effettivo a disposizione nell&#8217;array risultante sarà pari ad (n-1) * disksize. Il livello di fault tolerance offerto da questa soluzione è di un disco, ovvero il sistema rimane in funzione ed i dati rimangono integri se si danneggia fino ad un disco, in caso di rottura di più di un disco contemporaneamente i dati sono irrimediabilmente persi. Se si hanno più dischi a disposizione si può configurare uno di essi come spare disk, ovvero come disco di emergenza. Nel caso di rottura di un disco esso verrà rimpiazzato automaticamente dal disco di riserva ottenendo quindi una fault tolerance pari a 2 dischi.</p>
<p>Nella macchina virtuale di esempio verranno utilizzati 3 dischi da 3GB per la configurazione iniziale che porteranno quindi ad un array di dimensione 6GB.</p>
<h2>Creazione delle partizioni</h2>
<p>Una volta aggiunti i dischi alla macchina virtuale essi appaiono come dispositivi vuoti, quindi come prima cosa vanno partizionati per poter essere utilizzati come membri di array. Il tipo di partizione per il RAID è <code>fd</code>, per cui utilizzando fdisk la sequenza di comandi da utilizzare per ognuno dei 3 dischi è la seguente:</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/# fdisk /dev/sdb
Il dispositivo non contiene né una tabella delle partizioni DOS valida, né una disklabel Sun, SGI od OSF
Building a new DOS disklabel with disk identifier 0xd90f450c.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Attenzione: il flag 0x0000 non valido della tabella delle partizioni 4 verrà corretto con w(rite)

Comando (m per richiamare la guida): n
Azione comando
   e   estesa
   p   partizione primaria (1-4)
p
Numero della partizione (1-4): 1
Primo cilindro (1-391, predefinito 1):
Utilizzo del valore predefinito 1
Last cilindro, +cilindri or +size{K,M,G} (1-391, predefinito 391):
Utilizzo del valore predefinito 391

Comando (m per richiamare la guida): t
Partizione selezionata 1
Codice esadecimale (digitare L per elencare i codici): fd
Modificato il tipo di sistema della partizione 1 in fd (Autorilevamento raid di Linux)

Comando (m per richiamare la guida): w
La tabella delle partizioni è stata alterata!

Chiamata di ioctl() per rileggere la tabella delle partizioni.
Sincronizzazione dei dischi in corso.</pre>
<p>Il procedimento va ripetuto per tutti e tre i dischi in modo da prepararli per la costruzione dell&#8217;array. Una volta create le partizioni dopo aver creato le partizioni per tutti i dischi interessati si dovrebbe avere questo output digitando il comando <code>fdisk -l</code></p>
<pre class="brush: plain;">
Disco /dev/sdb: 3221 MB, 3221225472 byte
255 testine, 63 settori/tracce, 391 cilindri
Unità = cilindri di 16065 * 512 = 8225280 byte
Identificativo disco: 0x00000000

Dispositivo Boot      Start         End      Blocks   Id  System
/dev/sdb1               1         391     3140676   fd  Autorilevamento raid di Linux

Disco /dev/sdc: 3221 MB, 3221225472 byte
255 testine, 63 settori/tracce, 391 cilindri
Unità = cilindri di 16065 * 512 = 8225280 byte
Identificativo disco: 0xd90f450c

Dispositivo Boot      Start         End      Blocks   Id  System
/dev/sdc1               1         391     3140676   fd  Autorilevamento raid di Linux

Disco /dev/sdd: 3221 MB, 3221225472 byte
255 testine, 63 settori/tracce, 391 cilindri
Unità = cilindri di 16065 * 512 = 8225280 byte
Identificativo disco: 0x171338f5

Dispositivo Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         391     3140676   fd  Autorilevamento raid di Linux
</pre>
<h2>Creazione dell&#8217;array</h2>
<p>Come accennato in precedenza tutte le operazioni vengono eseguite con il comando mdadm. Per vedere le opzioni a disposizione vedere la relativa pagina di manuale, in ogni caso per creare il volume è sufficiente eseguire il comando</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/# mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/sdb1 /dev/sdc1 /dev/sdd1
mdadm: layout defaults to left-symmetric
mdadm: chunk size defaults to 64K
mdadm: size set to 3140608K
mdadm: array /dev/md0 started.</pre>
<p>In questo modo si è creato ed avviato il device /dev/md0 che può essere utilizzato come una normale partizione in modo del tutto trasparente. Per controllare che il device sia stato utilizzato correttamente basta eseguire</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/# cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid5 sdd1[2] sdc1[1] sdb1[0]
      6281216 blocks level 5, 64k chunk, algorithm 2 [3/3] [UUU]

unused devices: &lt;none&gt;
</pre>
<p>Se l&#8217;array è stato creato correttamente siamo a posto, ma dobbiamo fare in modo che esso venga avviato all&#8217;avvio del sistema operativo. Per fare ciò dobbiamo editare (o creare) il file mdadm.conf che normalmente dovrebbe trovarsi in <code>/etc</code>, ma in Ubuntu si trova in <code>/etc/mdadm/mdadm.conf</code>. Per i dettagli relativi a tale file consultare la pagina di manuale relativa (ovvero mdadm.conf). In ogni caso l&#8217;opzione che va inserita per caricare all&#8217;avvio l&#8217;array appena creato è ARRAY. Per ottenere automaticamente il contenuto di tale opzione è sufficiente eseguire il comando</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/# mdadm --examine --scan
ARRAY /dev/md0 level=raid5 num-devices=3 UUID=c5a20602:9985ece9:11a1047a:ce03a3ef</pre>
<p>L&#8217;output generato da tale comando è la linea che dobbiamo inserire nel file di configurazione. Un&#8217;altra opzione utile da considerare in tale file è MAILADDR ovvero l&#8217;indirizzo di posta a cui il demone mdadm invierà le email per segnalare eventuali anomalie nel sistema. Su Ubuntu/Debian il programma mdadm viene avviato automaticamente all&#8217;avvio del sistema, su altre distribuzioni potrebbe essere necessario aggiungere uno script in init.d a tale scopo. Infine un&#8217;ultima opzione che consiglio di lasciare al valore di default è DEVICE. Questa opzione indica a mdadm in quali device deve ricercare partizioni di tipo <code>fd</code>. Il valore di default è partitions, ovvero tutte le partizioni elencate in <code>/proc/partitions</code>. Utilizzando questo valore invece che la lista esplicita delle partizioni di tipo RAID non si rischia di dimenticare nessuna partizione che potrebbe contenere una parte di array.</p>
<p>Finito di editare il file mdadm.conf riavviamo il sistema e controlliamo al riavvio attraverso il file <code>/proc/mdstat</code> che l&#8217;array sia stato inizializzato correttamente (ovvero che si abbia lo stesso output avuto in precedenza).</p>
<p>Se tutto è andato a buon fine si può cominciare ad utilizzare il device creando un filesystem su di esso. Ad esempio per creare un filesystem di tipo ext3 con il seguente comando</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/home/fabio# mkfs.ext3 /dev/md0
mke2fs 1.41.9 (22-Aug-2009)
Etichetta del filesystem=
Tipo SO: Linux
Dimensione blocco=4096 (log=2)
Dimensione frammento=4096 (log=2)
393216 inode, 1570304 blocchi
78515 blocchi (5.00%) riservati per l'utente root
Primo blocco dati=0
Maximum filesystem blocks=1610612736
48 gruppi di blocchi
32768 blocchi per gruppo, 32768 frammenti per gruppo
8192 inode per gruppo
Backup del superblocco salvati nei blocchi:
	32768, 98304, 163840, 229376, 294912, 819200, 884736

Scrittura delle tavole degli inode: fatto
Creating journal (32768 blocks): fatto
Scrittura delle informazioni dei superblocchi e dell'accounting del filesystem: fatto

Questo filesystem verrà automaticamente controllato ogni 25 mount, o
180 giorni, a seconda di quale venga prima. Usare tune2fs -c o -i per cambiare.
</pre>
<h2>Fault tolerance in RAID5</h2>
<p>Vediamo ora cosa succede disconnettendo un disco dalla macchina virtuale, simulando così la rottura di un disco. Per verificare l&#8217;integrità dei dati nella partizione costruita sul RAID ho creato alcuni files dal contenuto casuale (con il comando <code>dd if=/dev/urandom of=testfile bs=1MB count=100</code>) e ne ho salvato l&#8217;md5 in un file di testo.</p>
<p>Spegnendo la macchina virtuale ed eliminando un disco al successivo riavvio l&#8217;array non verra&#8217; montato e, se configurata la variabile MAILADDR in mdadm.conf ci arriverà un messaggio di posta che ci avvisa che l&#8217;array è degradato. Sarà comunque possibile avviare l&#8217;array utilizzando il seguente comando ed i verificare che i dati sono integri e disponibili</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/mnt/raid# mdadm -As
mdadm: /dev/md0 has been started with 2 drives (out of 3).
root@fabio-laptop-ubuntu:/mnt/raid# mount /mnt/raid
root@fabio-laptop-ubuntu:/mnt/raid# md5sum -c md5
testfile: OK
testfile1: OK
testfile2: OK
testfile3: OK
</pre>
<p>Chiaramente questo è molto pericoloso in quanto se si dovesse rovinare un altro disco sarebbe tutto irrimediabilmente perso (ed inoltre le performance sono ridotte all&#8217;osso in quanto i dati del disco mancante sono calcolati a partire dagli altri 2). Per cui il passo successivo è quello di rimpiazzare al più presto il disco mancante e ricostruire l&#8217;array. Inserendo un nuovo disco per sostituire il precedente e partizionandolo come in precedenza (quindi sempre come <code>/dev/sdd1</code>), sarà sufficiente eseguire il seguente comando per ricostruire l&#8217;array e tornare operativi e coperti da eventuali ulteriori guasti:</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/mnt/raid# mdadm /dev/md0 --add /dev/sdd1
mdadm: added /dev/sdd1
</pre>
<p>A questo punto l&#8217;array verrà ricostruito automaticamente come si può verificare con il comando</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/mnt/raid# cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid5 sdd1[3] sdb1[0] sdc1[1]
      6281216 blocks level 5, 64k chunk, algorithm 2 [3/2] [UU_]
      [&gt;....................]  recovery =  4.9% (154624/3140608) finish=2.2min speed=22089K/sec

unused devices: &lt;none&gt;
</pre>
<h2>Aggiunta di un nuovo disco all&#8217;array</h2>
<p>Nello stesso modo in cui si sostituisce un disco/partizione che ha fallito è possibile aggiungere un nuovo device all&#8217;array nel caso in cui fossimo a corto di spazio. Per cui una volta inserito il nuovo disco nella macchina si può aggiungere all&#8217;array con il comando</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/mnt/raid# mdadm /dev/md0 --add /dev/sde1
mdadm: added /dev/sde1
</pre>
<p>In questo modo si è aggiunto il disco all&#8217;array ma allo stato attuale delle cose il nuovo disco viene considerato come disco di spare, ovvero come disco di emergenza da usare in caso di fallimento di un altro disco. Per aggiungere questo dispositivo allo spazio effettivamente disponibile bisogna utilizzare un altro comando con il quale si ingrandisce l&#8217;array.</p>
<pre class="brush: plain;">
root@fabio-laptop-ubuntu:/mnt/raid# mdadm --grow /dev/md0 --raid-devices=4
mdadm: Need to backup 384K of critical section..
mdadm: ... critical section passed.
</pre>
<p>Anche qui si può controllare lo stato della ricostruzione dell&#8217;array con il comando</p>
<pre class="brush: plain;">root@fabio-laptop-ubuntu:/mnt/raid# cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4] [linear] [multipath] [raid0] [raid1] [raid10]
md0 : active raid5 sde1[3] sdb1[0] sdd1[2] sdc1[1]
      6281216 blocks super 0.91 level 5, 64k chunk, algorithm 2 [4/4] [UUUU]
      [&gt;....................]  reshape =  2.5% (79680/3140608) finish=3.8min speed=13280K/sec

unused devices: &lt;none&gt;
</pre>
<p>Dopo che si è aggiunto il disco però è necessario un altro passo, ovvero l&#8217;estensione del filesystem, altrimenti non otterremmo nessun beneficio nell&#8217;aver aggiunto ulteriore spazio. Nel caso di ext3 questa operazione può essere eseguita al volo con il comando</p>
<pre class="brush: plain;">root@fabio-laptop-ubuntu:/mnt/raid# resize2fs /dev/md0
resize2fs 1.41.9 (22-Aug-2009)
Filesystem at /dev/md0 is mounted on /mnt/raid; on-line resizing required
old desc_blocks = 1, new_desc_blocks = 1
Performing an on-line resize of /dev/md0 to 2355456 (4k) blocks.
The filesystem on /dev/md0 is now 2355456 blocks long.
</pre>

<p><a href="http://feedads.g.doubleclick.net/~a/IyONSYIcF465lpY-o9pjJIahl4s/0/da"><img src="http://feedads.g.doubleclick.net/~a/IyONSYIcF465lpY-o9pjJIahl4s/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/IyONSYIcF465lpY-o9pjJIahl4s/1/da"><img src="http://feedads.g.doubleclick.net/~a/IyONSYIcF465lpY-o9pjJIahl4s/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/Awuw5pry6uc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2010/02/17/creazione-di-un-raid-software-livello-5-con-debian-o-ubuntu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2010/02/17/creazione-di-un-raid-software-livello-5-con-debian-o-ubuntu/</feedburner:origLink></item>
		<item>
		<title>Geotag delle foto con iPhone, Everytrail ed exiftool</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/-dj6Ev-y-EM/</link>
		<comments>http://www.codewithstyle.eu/2010/02/16/geotag-delle-foto-con-iphone-everytrail-ed-exiftool/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 02:04:22 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[everytrail]]></category>
		<category><![CDATA[exiftool]]></category>
		<category><![CDATA[geotag]]></category>
		<category><![CDATA[gps]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[iphoto]]></category>
		<category><![CDATA[osx]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=274</guid>
		<description><![CDATA[Una delle funzioni che ho apprezzato maggiormente di iPhoto 09 è l&#8217;aggiunta di &#8220;Places&#8221; (nella versione italiana &#8220;Luoghi&#8221;). Questa funzione si interfaccia con Google Maps per mostrare una mappa interattiva delle proprie fotografie e dei luoghi dove esse sono state scattate.
Per far si che le foto vengano mostrate sulla mappa è necessario però che le [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-medium wp-image-278" style="margin: 5px;" title="places" src="http://www.codewithstyle.eu/wp-content/uploads/2010/02/places-300x299.png" alt="" width="240" height="239" />Una delle funzioni che ho apprezzato maggiormente di iPhoto 09 è l&#8217;aggiunta di &#8220;Places&#8221; (nella versione italiana &#8220;Luoghi&#8221;). Questa funzione si interfaccia con Google Maps per mostrare una mappa interattiva delle proprie fotografie e dei luoghi dove esse sono state scattate.</p>
<p>Per far si che le foto vengano mostrate sulla mappa è necessario però che le immagini contengano i dati GPS negli appositi campi EXIF. Questo può essere ottenuto in vari modi, ad esempio iPhone, essendo dotato di un modulo GPS interno, inserisce automaticamente tali dati nelle proprie foto. Altre macchine fotografiche di fascia medio alta hanno integrato un sensore GPS oppure dispongono di un interfaccia hardware per collegare un ricevitore esterno, normalmente questo comporta una spesa aggiuntiva che con un iPhone ed un po di sforzo possiamo evitare.<span id="more-274"></span></p>
<p>Il metodo che descriverò ora funziona con qualsiasi macchina fotografica, anzi in realtà con qualsiasi immagine. È richiesto solamente un po di lavoro da eseguire tramite riga di comando per taggare correttamente le proprie foto. Basta utilizzare uno dei tanti software disponibili per iPhone per registrare i propri percorsi in formato <a href="http://en.wikipedia.org/wiki/GPS_eXchange_Format">GPX</a>. Io mi sono trovato bene con il software <a href="http://itunes.apple.com/it/app/everytrail/id342467041?mt=8">EveryTrail</a> che è un software gratuito, ma esistono moltissime alternative su AppStore.</p>
<p>Una volta scaricato il software e necessario creare un account (gratuito anch&#8217;esso) sul sito di EveryTrail per poter caricare i propri percorsi tramite iPhone. Una volta che si è creato l&#8217;account è possibile visualizzare i propri percorsi registrati con l&#8217;applicazione.</p>
<p>Per cui nel momento in cui si inizia a scattare ricordatevi di avviare l&#8217;applicazione e tenetela attiva sul telefono. In caso di arrivo di una chiamata l&#8217;applicazione va in pausa ed è sempre possibile riprendere in seguito il percorso iniziato in precedenza. Quando avete terminato di scattare eseguite l&#8217;upload del percorso, cliccate sul link che vi arriva per email e scaricate il file gpx con i dati GPS registrati.</p>
<h2>Sincronizzazione e geotag con exiftool</h2>
<p>Le operazioni da eseguire per effettuare il geotag possono essere eseguite utilizzando <a href="http://www.sno.phy.queensu.ca/~phil/exiftool/">exiftool</a>. Il programma, una volta installato, mette a disposizione da riga di comando l&#8217;eseguibile exiftool, la cui pagina di manuale si trova <a href="http://www.sno.phy.queensu.ca/~phil/exiftool/exiftool_pod.html">qui</a>. Il programma è uno strumento molto potente da utilizzare a riga di comando. Negli esempi seguenti utilizzerò le opzioni che vanno a sovrascrivere i file originali che sono</p>
<pre class="brush: plain;">
  -P          (-preserve)          Preserve date/time of original file
  -overwrite_original_in_place     Overwrite original by copying tmp file
</pre>
<p>Nel caso in cui si esegua qualche esperimento è il caso di non utilizzare tali opzioni,  in quanto con esse i file originali vengono sostituiti e non c&#8217;è modo di recuperarli.</p>
<p>Indipendentemente da come si è ottenuto il file con i dati gps la cosa più importante da verificare è la sincronizzazione tra gli orari delle immagini e quelli indicati nel file con le coordinate gps. La cosa migliore da fare è di sincronizzare il proprio iPhone e la propria camera prima di cominciare, possibilmente con una precisione al secondo. Se questo non è possibile o ce ne siamo semplicemente dimenticati (come è successo a me) è possibile cambiare la data di scatto nei dati EXIF.</p>
<p>La prima cosa da fare è quella di calcolare l&#8217;offset tra il tempo di scatto ed il tempo esatto, nel mio caso l&#8217;orario della macchinetta era completamente sballato (di circa una settimana) per cui ho dovuto cambiare completamente data ed ora. Per eseguire questa procedura ho eseguito una fotografia al display del telefono in modo da avere sul file la data di scatto memorizzata dalla macchinetta e nell&#8217;immagine il tempo registrato dall&#8217;iPhone. Confrontando i dati ho ottenuto una differenza di tempo pari ad 8 giorni, 13 ore 44 minuti e 24 secondi, per cui facendo riferimento a <a href="http://search.cpan.org/dist/Image-ExifTool/lib/Image/ExifTool/Shift.pl">questa pagina di manuale riguardante lo shift di tempi e date</a> la sintassi da utilizzare con exiftool è</p>
<pre class="brush: bash;">exiftool -P -overwrite_original_in_place -DateTimeOriginal+='8 13:44:24' -createdate+='8 13:44:24' *.JPG
   22 image files updated
</pre>
<p>Ora che la data del file è corretta è possibile utilizzare il file gpx per assegnare la localizzazione ad ogni foto, il comando per fare ciò è il seguente</p>
<pre class="brush: bash;">
exiftool -geotag geodata.gpx -P -overwrite_original_in_place *.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1263.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1264.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1265.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1266.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1267.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1268.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1269.JPG
Warning: Time is too far before track in File:Geotime (ValueConvInv) - DSCF1270.JPG
Warning: Time is too far beyond track in File:Geotime (ValueConvInv) - DSCF1281.JPG
Warning: Time is too far beyond track in File:Geotime (ValueConvInv) - DSCF1282.JPG
Warning: Time is too far beyond track in File:Geotime (ValueConvInv) - DSCF1283.JPG
Warning: Time is too far beyond track in File:Geotime (ValueConvInv) - DSCF1284.JPG
   10 image files updated
   12 image files unchanged
</pre>
<p>Come si vede nel caso in cui  alcune immagini non rientrino nell&#8217;intervallo temporale dei dati gps, exiftool ce lo segnala con un warning e lascia i file in questione invariati. Alla fine di questo processo potete importare le foto in iPhoto ed esse compariranno sulla mappa in &#8220;Luoghi&#8221;.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/98F2XNtydxbhlWqlq82qd06hInw/0/da"><img src="http://feedads.g.doubleclick.net/~a/98F2XNtydxbhlWqlq82qd06hInw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/98F2XNtydxbhlWqlq82qd06hInw/1/da"><img src="http://feedads.g.doubleclick.net/~a/98F2XNtydxbhlWqlq82qd06hInw/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/-dj6Ev-y-EM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2010/02/16/geotag-delle-foto-con-iphone-everytrail-ed-exiftool/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2010/02/16/geotag-delle-foto-con-iphone-everytrail-ed-exiftool/</feedburner:origLink></item>
		<item>
		<title>Creare files icns per applicazioni Mac</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/viJDeChQhv4/</link>
		<comments>http://www.codewithstyle.eu/2010/02/04/creare-files-icns-per-applicazioni-mac/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 02:25:42 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[icns]]></category>
		<category><![CDATA[icone]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[snow leopard]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=263</guid>
		<description><![CDATA[Un buon motivo per cui si può volere un file formato icns è quello di voler personalizzare le icone del Finder ed avere icone di eccellente qualità. Nel mio caso, mi serviva un icona per pacchettizzare un&#8217;applicazione Java che ho sviluppato, e volevo darle un aspetto migliore dell&#8217;icona degli archivi jar. Il risultato che ho [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.codewithstyle.eu/wp-content/uploads/2010/02/icona-jar-vs-icns.png"><img class="alignleft size-full wp-image-264" style="margin: 5px;" title="jar vs icns" src="http://www.codewithstyle.eu/wp-content/uploads/2010/02/icona-jar-vs-icns.png" alt="" width="266" height="137" /></a>Un buon motivo per cui si può volere un file formato icns è quello di voler personalizzare le icone del Finder ed avere icone di eccellente qualità. Nel mio caso, mi serviva un icona per pacchettizzare un&#8217;applicazione Java che ho sviluppato, e volevo darle un aspetto migliore dell&#8217;icona degli archivi jar. Il risultato che ho avuto nel mio caso è questo</p>
<p>Per ottenere le icone in questo formato ho trovato un paio di trucchetti che non sono niente male. Il primo metodo per creare icone in questo formato a partire da un file grafico esistente è quello di utilizzare il sito <a href="http://iconverticons.com/">IConvert</a>. Il sito è di utilizzo immediato, si carica un file grafico ed una volta terminato l&#8217;upload si ha la possibilità di scaricare il file convertito in numerosi formati tra cui icns.</p>
<p>Il secondo metodo consiste nell&#8217;utilizzare il programma img2icns scaricabile da <a href="http://www.img2icnsapp.com/">questo sito</a>. Anche questo metodo risulta di immediato utilizzo, si apre il programma, si trascina all&#8217;interno della finestra principale l&#8217;immagine da convertire e si salva il file tramite il pulsante apposito. Il programma offre anche la possibilità di esportare l&#8217;immagine come cartella, come icona per iPhone o come favicon per i propri siti, ma questi due ultimi formati richiedono la versione pro a pagamento.</p>
<p style="text-align: center;"><a href="http://www.codewithstyle.eu/wp-content/uploads/2010/02/img2icns.png"><img class="size-full wp-image-266 aligncenter" title="img2icns" src="http://www.codewithstyle.eu/wp-content/uploads/2010/02/img2icns.png" alt="" width="480" height="362" /></a></p>
<p>Infine un ultimo metodo che ho trovato molto utile per creare icone a partire da icone esistenti. Supponiamo che vogliate creare un file icns a partire da una cartella o da un&#8217;applicazione in vostro possesso. In tal caso ho trovato un metodo molto efficace per Snow Leopard in <a href="http://njr.sabi.net/2005/12/22/finder-icon-to-icns-file/">questo post</a>. Il metodo descritto nel post dovrebbe funzionare per chi ha ancora Leopard, ma in SL sembra che l&#8217;utility icnsBrowser non ci sia più. In ogni caso trovo molto più efficace il metodo descritto nell&#8217;ultimo commento, in pratica partendo dalla cartella (o applicazione) della quale si vuole l&#8217;icona si apre il pannello delle informazioni del finder (clic destro e Ottieni Informazioni oppure Comando (⌘)-I dopo aver selezionato l&#8217;elemento). Nel pannello fare clic sull&#8217;icona di interesse in alto a sinistra affinché questa venga evidenziata, dopo di che premere Comando (⌘)-C per copiare l&#8217;icona. A questo punto apriamo Anteprima (Preview) e premiamo Comando (⌘)-N per creare l&#8217;icona a partire dal contenuto degli appunti.</p>
<p><img class="aligncenter size-medium wp-image-267" title="Preview" src="http://www.codewithstyle.eu/wp-content/uploads/2010/02/Preview-300x262.png" alt="" width="300" height="262" /></p>
<p>Abbiamo la nostra icona in formato icns visualizzata in Anteprima, ma se proviamo a salvare non risulta possibile. Per avere il file però è sufficiente &#8220;chiudere&#8221; i vari elementi nella barra laterale di Anteprima utilizzando la freccia nera a destra e trascinare in una cartella qualsiasi del finder l&#8217;oggetto risultante ed esso verrà salvato come icona nel punto di rilascio, provare per credere.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/YrUUilNR-ltCTnDJ-b70MK19qAs/0/da"><img src="http://feedads.g.doubleclick.net/~a/YrUUilNR-ltCTnDJ-b70MK19qAs/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/YrUUilNR-ltCTnDJ-b70MK19qAs/1/da"><img src="http://feedads.g.doubleclick.net/~a/YrUUilNR-ltCTnDJ-b70MK19qAs/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/viJDeChQhv4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2010/02/04/creare-files-icns-per-applicazioni-mac/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2010/02/04/creare-files-icns-per-applicazioni-mac/</feedburner:origLink></item>
		<item>
		<title>Cambiare la posizione delle screenshot in Snow Leopard</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/ThvkVNieeb4/</link>
		<comments>http://www.codewithstyle.eu/2009/12/29/cambiare-la-posizione-delle-screenshot-in-snow-leopard/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 22:47:23 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[screenshot]]></category>
		<category><![CDATA[snow leopard]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=254</guid>
		<description><![CDATA[Una funzione molto comoda di OS X è quella di poter creare con semplicità screenshot di quello che si visualizza a schermo. Per utilizzare questa funzionalità si può usare il programma Istantanea presente nella directory Applicazioni &#8211; Utility oppure delle comode shortcut da tastiera. Personalmente preferisco il secondo metodo in quanto una volta imparate le [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-255" title="istantanea" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/istantanea.png" alt="" width="260" height="243" />Una funzione molto comoda di OS X è quella di poter creare con semplicità screenshot di quello che si visualizza a schermo. Per utilizzare questa funzionalità si può usare il programma Istantanea presente nella directory Applicazioni &#8211; Utility oppure delle comode shortcut da tastiera. Personalmente preferisco il secondo metodo in quanto una volta imparate le scorciatoie è sicuramente di più immediato richiamo.</p>
<p>A differenza del programma Istantanea, utilizzando le combinazioni di tasti non è possibile decidere dove saranno salvate le immagini prodotte, queste saranno registrate tutte sul Desktop. In alcuni casi può essere desiderabile cambiare la posizione di default per mantenere un pò di ordine sulla propria scrivania.<span id="more-254"></span></p>
<p>In questo caso ci viene incontro il terminale, infatti è sufficiente un semplice comando per cambiare la directory dove salvare le screenshot. In particolare è sufficiente aprire il terminale (sempre dalla cartella Utility in Applicazioni) e digitare il seguente comando</p>
<pre class="brush: bash;">defaults write com.apple.screencapture location /Users/fabio/Pictures</pre>
<p>In questo caso ho impostato come directory di salvataggio la cartella Immagini nella mia home, chiaramente l&#8217;ultimo argomento del comando è quello che determina la posizione dove saranno salvate le immagini d&#8217;ora in poi.</p>
<p>Per rendere effettiva la modifica riavviamo il server grafico (per evitare un logout o un riavvio) con il comando</p>
<pre class="brush: bash;">killall SystemUIServer</pre>
<p>A questo punto se eseguiamo una screenshot con una delle shortcut disponibili l&#8217;immagine sarà salvata nella directory scelta e non più nel Desktop.</p>
<p>Per completezza riporto quali sono le scorciatoie da tastiera disponibili per i vari tipi di screenshot che si possono eseguire:</p>

<table id="wp-table-reloaded-id-2-no-1" class="wp-table-reloaded wp-table-reloaded-id-2">
<thead>
	<tr class="row-1">
		<th class="column-1">Operazione</th><th class="column-2">Shortcut</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2">
		<td class="column-1">Scattare un'istantanea di tutto lo schermo.</td><td class="column-2">Comando (⌘)-Maiuscole-3</td>
	</tr>
	<tr class="row-3">
		<td class="column-1">Scattare un'istantanea di parte dello schermo.</td><td class="column-2">Comando (⌘)-Maiuscole-4, e quindi trascina il puntatore per selezionare l'area. Continua a premere il pulsante del mouse, rilascia i tasti, quindi premi Maiuscole, Opzione o la barra spaziatrice mentre trascini per ridimensionare l'area di selezione. Quando sei pronto a scattare un'istantanea, rilascia il pulsante del mouse.<br />
Per annullare, premi Esc.</td>
	</tr>
	<tr class="row-4">
		<td class="column-1">Eseguire un'istantanea di una finestra o della barra dei menu</td><td class="column-2">Comando (⌘)- Maiuscole-4, premi la barra spaziatrice, sposta il puntatore a forma di fotocamera sopra l'area da evidenziare e quindi fai clic.<br />
Per annullare, premi Esc.</td>
	</tr>
	<tr class="row-5">
		<td class="column-1">Eseguire un'istantanea di un menu, includendo il titolo</td><td class="column-2">Fai clic sul menu per visualizzare i comandi del menu, premi Comando (⌘)-Maiuscole-4, quindi trascina il puntatore sull'area.<br />
Per annullare, premi Esc.</td>
	</tr>
	<tr class="row-6">
		<td class="column-1">Eseguire un'istantanea del menu senza il titolo</td><td class="column-2">Fai clic sul menu per visualizzare i comandi del menu, premi Comando (⌘)-Maiuscole-4, premi la barra spaziatrice, sposta il puntatore a forma di fotocamera sul menu per evidenziarlo, quindi fai clic.<br />
Per annullare, premi Esc.</td>
	</tr>
</tbody>
</table>


<p><a href="http://feedads.g.doubleclick.net/~a/jvkvgOjj7b2ClQuUyx_klUzYcCA/0/da"><img src="http://feedads.g.doubleclick.net/~a/jvkvgOjj7b2ClQuUyx_klUzYcCA/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/jvkvgOjj7b2ClQuUyx_klUzYcCA/1/da"><img src="http://feedads.g.doubleclick.net/~a/jvkvgOjj7b2ClQuUyx_klUzYcCA/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/ThvkVNieeb4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/12/29/cambiare-la-posizione-delle-screenshot-in-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/12/29/cambiare-la-posizione-delle-screenshot-in-snow-leopard/</feedburner:origLink></item>
		<item>
		<title>Database profiling con Firebug in Zend Framework</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/Sl8u6zno8Xg/</link>
		<comments>http://www.codewithstyle.eu/2009/12/29/database-profiling-con-firebug-in-zend-framework/#comments</comments>
		<pubDate>Tue, 29 Dec 2009 21:36:12 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[application.ini]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[profiling]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=249</guid>
		<description><![CDATA[Un metodo rapido e veloce per tenere sott&#8217;occhio quello che succede nel vostro database durante l&#8217;esecuzione di applicazioni Zend Framework è quello di utilizzare la classe Zend_Db_Profiler_Firebug. A differenza del plugin ZFDebug, il setup di questa classe è immediato e non richiede l&#8217;installazione di files aggiuntivi, ma solamente un paio di direttive nel file application.ini [...]]]></description>
			<content:encoded><![CDATA[<p>Un metodo rapido e veloce per tenere sott&#8217;occhio quello che succede nel vostro database durante l&#8217;esecuzione di applicazioni Zend Framework è quello di utilizzare la classe <code>Zend_Db_Profiler_Firebug</code>. A differenza del plugin <a href="http://www.codewithstyle.eu/2009/11/09/debug-di-applicazioni-zend-framework-con-zfdebug/">ZFDebug</a>, il setup di questa classe è immediato e non richiede l&#8217;installazione di files aggiuntivi, ma solamente un paio di direttive nel file <code>application.ini</code> e delle due estensioni per Firefox <a href="http://getfirebug.com/">Firebug</a> e <a href="http://www.firephp.org/">Firephp</a> (che a prescindere da questo utilizzo sono indispensabili per chi sviluppa siti web di qualunque natura).<span id="more-249"></span></p>
<p>Per utilizzare questa comoda funzionalità messa a disposizione da Zend Framework, è sufficiente inserire queste righe nel file di configurazione dell&#8217;applicazione</p>
<pre class="brush: plain;">
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = Zend_Db_Profiler_Firebug
</pre>
<p>Per ovvi motivi queste direttive vanno abilitate solamente nella sezione development del file di configurazione, a meno che non vogliate far sapere a tutto il mondo quali sono le query che eseguite sul database.</p>
<p>Il risultato che si ottiene su Firefox è il seguente:</p>
<div id="attachment_250" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.codewithstyle.eu/wp-content/uploads/2009/12/zend_db_profiler_firebug.png"><img class="size-medium wp-image-250" title="zend_db_profiler_firebug" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/zend_db_profiler_firebug-300x213.png" alt="Zend_Db_Profiler_Firebug" width="300" height="213" /></a><p class="wp-caption-text">Dati restituiti dal profiler per Firebug</p></div>

<p><a href="http://feedads.g.doubleclick.net/~a/bJoLkl89X_lLpgkMY8WkRCc8Q6Y/0/da"><img src="http://feedads.g.doubleclick.net/~a/bJoLkl89X_lLpgkMY8WkRCc8Q6Y/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/bJoLkl89X_lLpgkMY8WkRCc8Q6Y/1/da"><img src="http://feedads.g.doubleclick.net/~a/bJoLkl89X_lLpgkMY8WkRCc8Q6Y/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/Sl8u6zno8Xg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/12/29/database-profiling-con-firebug-in-zend-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/12/29/database-profiling-con-firebug-in-zend-framework/</feedburner:origLink></item>
		<item>
		<title>Migliorare le performance del proprio sito PHP con l’estensione APC</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/ErA11tTM66g/</link>
		<comments>http://www.codewithstyle.eu/2009/12/14/migliorare-le-performance-del-proprio-sito-php-con-lestensione-apc/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 03:03:13 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[apc]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=225</guid>
		<description><![CDATA[Se avete un sito web realizzato in PHP e volete raddoppiarne le performance con un semplice ma significativo passo, allora la soluzione giusta è l&#8217;estensione PECL APC.
Come molti sapranno PHP è un linguaggio interpretato, questo fa si che ad ogni singola richiesta di una pagina web, l&#8217;interprete PHP debba leggere il codice nei files memorizzati [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-229" style="margin: 5px;" title="php-logo" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/php-logo.png" alt="php-logo" width="270" height="142" />Se avete un sito web realizzato in PHP e volete raddoppiarne le performance con un semplice ma significativo passo, allora la soluzione giusta è l&#8217;estensione <a href="http://pecl.php.net/package/APC">PECL APC</a>.</p>
<p>Come molti sapranno PHP è un <a href="http://it.wikipedia.org/wiki/Interprete_(informatica)">linguaggio interpretato</a>, questo fa si che ad ogni singola richiesta di una pagina web, l&#8217;interprete PHP debba leggere il codice nei files memorizzati su disco, valutarne la correttezza, trasformarlo in istruzioni eseguibili, trasformare i nomi di variabili e funzioni in indirizzi di memoria, ed eseguire le istruzioni specificate.</p>
<p>Inoltre per chi fa uso di framework o librerie particolarmente corpose, c&#8217;è l&#8217;overhead dovuto alle dozzine di files contenuti in queste librerie. Per esempio Zend Framework è composto da più di 2500 files, e spesso e volentieri per istanziare un singolo oggetto di una data classe è necessario che l&#8217;interprete legga decine di files.<span id="more-225"></span></p>
<div id="attachment_226" class="wp-caption alignright" style="width: 160px"><a href="http://www.codewithstyle.eu/wp-content/uploads/2009/12/zendframework-app-with-zfdebug.png"><img class="size-thumbnail wp-image-226" title="zendframework-app-with-zfdebug" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/zendframework-app-with-zfdebug-150x150.png" alt="Files caricati da Zend Framework" width="150" height="150" /></a><p class="wp-caption-text">Files caricati da Zend Framework</p></div>
<p>L&#8217;applicazione di prova dell&#8217;<a href="http://www.codewithstyle.eu/2009/11/09/debug-di-applicazioni-zend-framework-con-zfdebug/">articolo su ZFDebug</a> necessita di 110 files (circa 1MB) solamente per caricare l&#8217;home page.</p>
<p>Proprio in casi come questi ci viene incontro APC, infatti questa estensione crea un file mappato in memoria (praticamente una porzione di RAM salvata su filesystem) che viene condivisa tra tutte le richieste al webserver, dove mantiene una copia cache di tutti i files caricati durante l&#8217;esecuzione degli script.</p>
<p>In questo modo, fatta eccezione per la prima richiesta, i tempi di risposta avranno uno speedup notevole in quanto l&#8217;interprete non dovrà più leggere i files da disco ma se li ritroverà automaticamente caricati in memoria.</p>
<h3>Installazione e configurazione</h3>
<p>Per installare questa estensione su una macchina Debian/Ubuntu è sufficiente eseguire il comando</p>
<pre class="brush: bash;">aptitude install php-apc</pre>
<p>In alternativa è possibile installarla utilizzando <a href="http://www.php.net/manual/en/install.pecl.php">PECL</a> oppure compilando a mano l&#8217;estensione e caricandola con il file php.ini con la riga</p>
<pre class="brush: plain;">extension=apc.so</pre>
<p>Per quanto riguarda la configurazione, in linea di massima va bene quella di default che prevede 1 segmento di memoria condivisa di dimensione pari a 30MB, ed abilita la cache di tutti i files php ed inc di dimensione inferiore al Megabyte.</p>
<p>Su una macchina in produzione può essere molto producente l&#8217;opzione <a href="http://www.php.net/manual/en/apc.configuration.php#ini.apc.stat">apc.stat</a> con la quale si indica all&#8217;estensione di non controllare per i files in cache se sono cambiati su disco. Questa infatti è un&#8217;operazione abbastanza onerosa, in quanto richiede per ogni file un controllo sul filesystem. Chiaramente questo settaggio va usato con molta cautela, in quanto la cache non viene svuotata sino ad un riavvio del webserver, per cui eventuali modifiche effettuate ad uno script non saranno visibili se questo si trova in cache.</p>
<p>Un&#8217;altra opzione che può tornare utile cambiare è <a href="http://www.php.net/manual/en/apc.configuration.php#ini.apc.enable-cli">apc.enable_cli</a>, che rende disponibile la cache dei files anche negli script a riga di comando. Per default questa opzione è disattivata, quindi non si ha la cache attiva negli script a riga di comando.</p>
<p>Per una trattazione completa delle opzioni, del loro significato e dei valori di default si può vedere la <a href="http://www.php.net/manual/en/apc.configuration.php">pagina relativa</a>.</p>
<h3>Benchmark</h3>
<p>Per misurare l&#8217;aumento di prestazioni che si ha installando sul proprio webserver l&#8217;estensione APC, ho utilizzato il tool <a href="http://httpd.apache.org/docs/2.2/programs/ab.html">Apache Benchmark</a>. Questo strumento misura i tempi di risposta di un sito web e determina il numero massimo di richieste al secondo che la vostra installazione di Apache può gestire. Per ottenere un risultato indipendente da eventuali congestioni di rete le prove sono state eseguite in locale come spesso viene consigliato, per cui la singola sessione di benchmark è stata lanciata eseguendo il comando</p>
<pre class="brush: bash;">ab -c 30 -n 1000 http://localhost/zfqs/</pre>
<p>Ovvero ho detto ad ab di eseguire 1000 richieste lanciandone al più 30 in parallelo sulla home page del progetto dell&#8217;articolo di ZFDebug, che come detto in precedenza carica 110 files per mostrare la home page.</p>
<p>Il benchmark è stato eseguito in tre situazioni distinte:</p>
<ol>
<li>Estensione apc non caricata</li>
<li>Estensione apc caricata con opzione stat=1 (default)</li>
<li>Estensione apc caricata con opzione stat=0</li>
</ol>
<p>E questi sono i risultati ottenuti:</p>
<pre class="brush: plain; highlight: [22,24,43];">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache/2.2.9
Server Hostname:        localhost
Server Port:            80

Document Path:          /zfqs/
Document Length:        24269 bytes

Concurrency Level:      30
Time taken for tests:   67.103 seconds
Complete requests:      1000
Failed requests:        182
   (Connect: 0, Receive: 0, Length: 182, Exceptions: 0)
Write errors:           0
Total transferred:      24717294 bytes
HTML transferred:       24268294 bytes
Requests per second:    14.90 [#/sec] (mean)
Time per request:       2013.098 [ms] (mean)
Time per request:       67.103 [ms] (mean, across all concurrent requests)
Transfer rate:          359.71 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       2
Processing:   984 2004 228.3   2001    3384
Waiting:      957 1874 221.8   1865    3209
Total:        984 2004 228.3   2001    3386

Percentage of the requests served within a certain time (ms)
  50%   2001
  66%   2078
  75%   2125
  80%   2159
  90%   2239
  95%   2321
  98%   2412
  99%   2740
 100%   3386 (longest request)
</pre>
<pre class="brush: plain; highlight: [22,24,43];">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache/2.2.9
Server Hostname:        localhost
Server Port:            80

Document Path:          /zfqs/
Document Length:        24263 bytes

Concurrency Level:      30
Time taken for tests:   22.696 seconds
Complete requests:      1000
Failed requests:        753
   (Connect: 0, Receive: 0, Length: 753, Exceptions: 0)
Write errors:           0
Total transferred:      24714238 bytes
HTML transferred:       24265238 bytes
Requests per second:    44.06 [#/sec] (mean)
Time per request:       680.882 [ms] (mean)
Time per request:       22.696 [ms] (mean, across all concurrent requests)
Transfer rate:          1063.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       2
Processing:   198  678 144.2    673    1449
Waiting:      184  639 142.5    640    1444
Total:        198  678 144.1    673    1449

Percentage of the requests served within a certain time (ms)
  50%    673
  66%    722
  75%    752
  80%    778
  90%    846
  95%    916
  98%   1001
  99%   1101
 100%   1449 (longest request)
</pre>
<pre class="brush: plain; highlight: [22,24,43];">
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)

Server Software:        Apache/2.2.9
Server Hostname:        localhost
Server Port:            80

Document Path:          /zfqs/
Document Length:        24267 bytes

Concurrency Level:      30
Time taken for tests:   18.690 seconds
Complete requests:      1000
Failed requests:        866
   (Connect: 0, Receive: 0, Length: 866, Exceptions: 0)
Write errors:           0
Total transferred:      24713251 bytes
HTML transferred:       24264251 bytes
Requests per second:    53.50 [#/sec] (mean)
Time per request:       560.712 [ms] (mean)
Time per request:       18.690 [ms] (mean, across all concurrent requests)
Transfer rate:          1291.25 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2  11.5      0      77
Processing:    80  555 152.3    549    1247
Waiting:       75  519 150.7    516    1223
Total:         80  557 153.6    550    1247

Percentage of the requests served within a certain time (ms)
  50%    550
  66%    588
  75%    621
  80%    648
  90%    742
  95%    821
  98%    946
  99%   1091
 100%   1247 (longest request)
</pre>
<p>I risultati parlano da soli e sono veramente notevoli, in ogni caso ho evidenziato i valori più importanti</p>
<ul>
<li>Numero di richieste gestite al secondo da Apache</li>
<li>Tempo medio per l&#8217;esecuzione di una richiesta (in concorrenza)</li>
<li>Tempo massimo per servire una richiesta</li>
</ul>
<p>Questi tre valori cambiano nel seguente modo nei tre casi analizzati</p>

<table id="wp-table-reloaded-id-1-no-1" class="wp-table-reloaded wp-table-reloaded-id-1">
<thead>
	<tr class="row-1 odd">
		<th class="column-1"></th><th class="column-2">Richieste al secondo</th><th class="column-3">Tempo medio di richiesta</th><th class="column-4">Tempo richiesta più lunga</th>
	</tr>
</thead>
<tbody>
	<tr class="row-2 even">
		<td class="column-1">Caso 1</td><td class="column-2">14,90</td><td class="column-3">67,103ms</td><td class="column-4">3386ms</td>
	</tr>
	<tr class="row-3 odd">
		<td class="column-1">Caso 2</td><td class="column-2">44,06</td><td class="column-3">22,696ms</td><td class="column-4">1449ms</td>
	</tr>
	<tr class="row-4 even">
		<td class="column-1">Caso 3</td><td class="column-2">53,50</td><td class="column-3">18,690ms</td><td class="column-4">1247ms</td>
	</tr>
</tbody>
</table>

<h3>Uso di APC come cache a livello utente</h3>
<p>Un altro uso che può tornare molto utile di questa estensione è il caching dei dati applicazione. In alcune operazioni l&#8217;uso della cache può portare notevoli benefici in termini di velocità, ad esempio quando si eseguono query molto complesse su un database il cui risultato cambia molto raramente nel tempo, oppure quando si caricano dati da un webservice esterno. In tali casi si può memorizzare il dato calcolato in cache attraverso l&#8217;uso delle <a href="http://www.php.net/manual/en/ref.apc.php">funzioni apposite</a>, e, alla successiva esecuzione dello script verificare se il dato è presente in cache; in caso affermativo, si può restituire il dato preso dalla cache senza il bisogno di rieseguire il calcolo.</p>
<p>Apc può essere usata anche come  backend del componente Zend_Cache. Nei miei progetti in ZF utilizzo sempre il seguente codice in produzione, per evitare di ricaricare i metadati relativi alla struttura delle tabelle nelle classi che fanno uso/derivano da Zend_Db_Table</p>
<pre class="brush: php;">
$frontendOptions = array ('lifetime' =&gt; 7200, 'automatic_serialization' =&gt; true);
$backend = extension_loaded('apc') ? 'Apc' : 'File';
// getting a Zend_Cache_Core object
$cache = Zend_Cache::factory('Core', $backend, $frontendOptions);
Zend_Db_Table::setDefaultMetadataCache($cache);
</pre>
<p>In questo modo il componente Zend_Db_Table caricherà i metadati per la tabella che rappresenta (nomi delle colonne, tipi, etc) una sola volta ogni 2 ore per ogni tabella, invece che ad ogni esecuzione. L&#8217;incremento di prestazioni che si ottiene con queste poche righe è veramente considerevole.</p>
<p>Gli usi possibili di un&#8217;estensione del genere spaziano moltissimo, chiaramente per l&#8217;utilizzo all&#8217;interno di un applicazione è necessario studiare bene l&#8217;impatto che può avere il caching di dati a livello logico, ma se implementata correttamente nei punti chiave di un applicazione, il guadagno che se ne ottiene giustifica ampiamente l&#8217;impiego di questo meccanismo.</p>
<p>Un buon metodo per capire su quali operazioni implementare la cache è sicuramente il profiling del proprio codice. Quando avrò un po di tempo scriverò un articolo su come applicare questa strategia.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/zUnaod4ce5ImrlDUMo-V90q8h-w/0/da"><img src="http://feedads.g.doubleclick.net/~a/zUnaod4ce5ImrlDUMo-V90q8h-w/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/zUnaod4ce5ImrlDUMo-V90q8h-w/1/da"><img src="http://feedads.g.doubleclick.net/~a/zUnaod4ce5ImrlDUMo-V90q8h-w/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/ErA11tTM66g" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/12/14/migliorare-le-performance-del-proprio-sito-php-con-lestensione-apc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/12/14/migliorare-le-performance-del-proprio-sito-php-con-lestensione-apc/</feedburner:origLink></item>
		<item>
		<title>Usare Google CDN per caricare i framework javascript</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/Hcd0q0zQWiY/</link>
		<comments>http://www.codewithstyle.eu/2009/12/05/usare-google-cdn-per-caricare-i-framework-javascript/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 03:22:18 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cdn]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=215</guid>
		<description><![CDATA[Una Content Delivery Network (in breve CDN), è una rete distribuita per la distribuzione di contenuti al grande pubblico. Vediamo come è perché utilizzare Google come CDN per il caricamento dei più diffusi framework javascript nelle nostre applicazioni web.
Spesso capita di dover utilizzare un framework javascript per aggiungere funzionalità 2.0 al proprio sito, in tale [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft" style="margin: 25px 10px" src="http://code.google.com/images/code_logo.png" alt="" width="161" height="40" />Una Content Delivery Network (in breve CDN), è una rete distribuita per la distribuzione di contenuti al grande pubblico. Vediamo come è perché utilizzare Google come CDN per il caricamento dei più diffusi framework javascript nelle nostre applicazioni web.</p>
<p>Spesso capita di dover utilizzare un framework javascript per aggiungere funzionalità 2.0 al proprio sito, in tale contesto su siti sviluppati ad hoc è prassi comune includere i files javascript contenenti le librerie direttamente nel proprio dominio.</p>
<p>Questa procedura può e dovrebbe essere evitata andando ad utilizzare le versioni di tali librerie messe a disposizione da Google nella propria rete di distribuzione. Utilizzare tale approccio ha innumerevoli vantaggi tra i quali</p>
<ul>
<li>La rete di Google è distribuita e replicata attraverso innumerevoli host dislocati in tutto il mondo, nel risolvere l&#8217;hostname i dns di Google reindirizzeranno la richiesta al server geograficamente più vicino alla locazione del utente che fa richiesta della risorsa, garantendo tempi di download e di latenza migliori rispetto al vostro webserver.</li>
<li>Più siti utilizzano questa tecnica (e ci sono già innumerevoli siti e portali che lo fanno, incluse le principali piattaforme di blogging), maggiore è la probabilità che la stessa richiesta che il browser dell&#8217;utente effettua sia già stata eseguita in precedenza. Dal momento che Google indica al browser di mantenere i file scaricati in cache per un anno, nella stragrande maggioranza dei casi il download del file richiesto non sarà neppure necessario, trovandosi esso in cache.</li>
<li>Gli aggiornamenti dei framework sono gestiti da Google che mette a disposizione le ultime release stabili delle librerie disponibili.</li>
<li>Con alcuni contratti di hosting la banda può essere una risorsa preziosa, in questo modo si svincola il proprio server dal dover inviare i files javascript portando ad un risparmio di banda notevole. Ad esempio, la versione attualmente disponibile di prototype pesa 143KB, mentre la versione compressa di jQuery pesa 56KB. Moltiplicate per il numero di utenti ed la banda impiegata cresce a dismisura.</li>
</ul>
<h3><span id="more-215"></span>Come utilizzare Google CDN</h3>
<p>Ci sono due modalità d&#8217;uso per caricare le librerie dal CDN di google. La prima modalità è quella di utilizzare il loader javascript messo a disposizione da Google nella libreria Google Ajax API. Per usare questa modalità è sufficiente includere un singolo file javascript nel proprio sorgente che carica questa libreria con il seguente tag</p>
<pre class="brush: xml;">&lt;script src=&quot;http://www.google.com/jsapi&quot;&gt;&lt;/script&gt;</pre>
<p>Una volta incluso tale file si ha a disposizione il loader di Google richiamabile attraverso il metodo <code>google.load()</code> indicando come primo argomento il nome della libreria da caricare e come secondo argomento la versione richiesta.</p>
<p>Per quanto riguarda il numero di versione è possibile indicare un numero completo di versione (e quindi specificare la release precisa) oppure specificare un numero parziale, in tal caso sarà restituita la sottoversione più recente di tale ramo di sviluppo. Ad esempio se di un&#8217;ipotetica libreria fossero disponibili le versioni 1.8.2, 1.8.4 ed 1.9.1, si avrà la seguente corrispondenza tra parametro e versione effettivamente caricata</p>
<ul>
<li>1.8.2 =&gt; 1.8.2</li>
<li>1.8 =&gt; 1.8.4</li>
<li>1 =&gt; 1.9.1</li>
</ul>
<p>È disponibile anche un terzo argomento con il quale si possono passare parametri aggiuntivi opzionali, ad esempio se caricare o meno la versione compressa del file javascript. La documentazione completa del metodo load si trova in <a href="http://code.google.com/intl/it-IT/apis/ajax/documentation/">questa pagina</a>. Un esempio di utilizzo del loader è il seguente, dopo aver incluso il file javascript principale si può inserire nell&#8217;head del documento il seguente codice:</p>
<pre class="brush: xml;">
&lt;script type=&quot;text/javascript&quot;&gt;
      google.load(&quot;jquery&quot;, &quot;1.3.2&quot;);
      google.load(&quot;jqueryui&quot;, &quot;1.7.2&quot;);
      google.setOnLoadCallback(function () {$(&quot;#title&quot;).effect(&quot;pulsate&quot;);});
 &lt;/script&gt;
...
&lt;h1 id=&quot;title&quot;&gt;Example Title&lt;/h1&gt;
</pre>
<p>Utilizzare il loader ha anche un ulteriore vantaggio, si ha a disposizione un&#8217;interfaccia unificata per il callback onload, infatti i vari framework hanno differenti funzioni da utilizzare per eseguire codice al caricamento della pagina, quindi nel caso in cui si dovessero utilizzare più framework in una stessa pagina si eviterebbe di dover scrivere molteplici funzioni onload.</p>
<p>Il secondo metodo consiste nel caricamento diretto dei file javascript a cui siamo interessati usando il tag script. È sufficiente recuperare l&#8217;indirizzo del file, disponibile nella <a href="http://code.google.com/intl/it-IT/apis/ajaxlibs/documentation/">pagina apposita</a> e inserire nel tag head il codice che carica il file</p>
<pre class="brush: xml;">&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot;&gt; &lt;/script&gt;</pre>
<h3>Librerie attualmente disponibili</h3>
<p>La lista delle librerie è in continuo aggiornamento, ed al momento comprende:</p>
<ul>
<li>jQuery</li>
<li>jQueryUI</li>
<li>Prototype</li>
<li>script.aculo.us</li>
<li>MooTools</li>
<li>Dojo</li>
<li>SWFObject</li>
<li>Yahoo User Interface (YUI)</li>
<li>Ext Core</li>
<li>Chrome Frame</li>
</ul>

<p><a href="http://feedads.g.doubleclick.net/~a/P7YLNca3wDGGtlSb22ZdK1g6zbU/0/da"><img src="http://feedads.g.doubleclick.net/~a/P7YLNca3wDGGtlSb22ZdK1g6zbU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/P7YLNca3wDGGtlSb22ZdK1g6zbU/1/da"><img src="http://feedads.g.doubleclick.net/~a/P7YLNca3wDGGtlSb22ZdK1g6zbU/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/Hcd0q0zQWiY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/12/05/usare-google-cdn-per-caricare-i-framework-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/12/05/usare-google-cdn-per-caricare-i-framework-javascript/</feedburner:origLink></item>
		<item>
		<title>Recuperare i propri files da un disco danneggiato</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/vvao4TR3uOs/</link>
		<comments>http://www.codewithstyle.eu/2009/12/05/recuperare-i-propri-files-da-un-disco-danneggiato/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 01:41:49 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[data recovery]]></category>
		<category><![CDATA[ddrescue]]></category>
		<category><![CDATA[knoppix]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[photorec]]></category>
		<category><![CDATA[recupero dati]]></category>
		<category><![CDATA[testdisk]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=196</guid>
		<description><![CDATA[Mi è appena capitato di dovermi cimentare in questa impresa per fare un favore ad un amico. Come al solito ci si accorge di non aver fatto i backup dei propri files importanti sempre quando e troppo tardi per recuperarli, o quasi. Nella fattispecie si trattava di una pendrive (piuttosto attempata) che non risultava più [...]]]></description>
			<content:encoded><![CDATA[<p>Mi è appena capitato di dovermi cimentare in questa impresa per fare un favore ad un amico. Come al solito ci si accorge di non aver fatto i backup dei propri files importanti sempre quando e troppo tardi per recuperarli, o quasi. Nella fattispecie si trattava di una pendrive (piuttosto attempata) che non risultava più leggibile con nessun sistema operativo/computer.</p>
<p>Inserendola in un computer con Windowx XP compariva in esplora risorse il messaggio &#8220;L&#8217;unità F: non è formattata, formattare?&#8221;</p>
<p>Fortunatamente la chiavetta mi è capitata in mano prima che qualcuno rispondesse avventatamente si a tale domanda.</p>
<h4>Come recuperare i dati</h4>
<p>La procedura che ho usato in questo caso è abbastanza semplice, richiede solamente un paio di programmi per provare il recupero <a href="http://www.cgsecurity.org/wiki/TestDisk">testdisk</a> e <a href="http://www.cgsecurity.org/wiki/PhotoRec">photorec</a>. In più per lavorare con un buon margine di sicurezza e lasciarsi aperta la strada per tentare altri approcci di recupero conviene non lavorare direttamente sul dispositivo, ma su una sua immagine salvata su un altro harddisk. A questo scopo ci sono varie soluzioni, ma quella con cui mi sono trovato meglio è <a href="http://www.garloff.de/kurt/linux/ddrescue/">dd_rescue</a>.<span id="more-196"></span></p>
<p>Se non si ha a disposizione un sistema Linux sul quale installare i programmi citati, può tornare molto comodo un disco di <a href="http://www.knoppix.org/">Knoppix</a>, che contiene i tool necessari al recupero nelle loro ultime versioni disponibili. Nella fattispecie ho utilizzato la versione CD dove ho trovato tutto il necessario (tranne il compilatore c++ che servirà più avanti se si vuole installare la versione GNU di ddrescue).</p>
<p>Dopo aver masterizzato il disco con knoppix facciamolo partire in un qualsiasi computer in grado di fare boot da CD. In alternativa si può anche preparare una pendrive con knoppix se il vostro sistema supporta il boot da USB seguendo <a href="http://www.knoppix.net/wiki/USB_Based_FAQ">queste istruzioni</a>.</p>
<p>Una volta che il sistema è caricato apriamo una finestra di terminale e diventiamo root con il comando <code>sudo su</code>. Dopo di ciò inseriamo la pendrive danneggiata nella porta USB. Esaminando l&#8217;output del comando <code>dmesg</code> saremo in grado di identificare quale nome è stato assegnato al dispositivo:</p>
<pre class="brush: plain;">
[12866.896026] usb 1-2: new full speed USB device using uhci_hcd and address 3
[12867.039523] usb 1-2: configuration #1 chosen from 1 choice
[12867.044738] usb 1-2: New USB device found, idVendor=13fd, idProduct=1618
[12867.044747] usb 1-2: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[20771.265739] sd 4:0:0:0: [sdc] 234441648 512-byte hardware sectors (120034 MB)
[20771.269228] sd 4:0:0:0: [sdc] Write Protect is off
[20771.269233] sd 4:0:0:0: [sdc] Mode Sense: 00 38 00 00
[20771.269235] sd 4:0:0:0: [sdc] Assuming drive cache: write through
[20771.272999] sd 4:0:0:0: [sdc] 234441648 512-byte hardware sectors (120034 MB)
[20771.274137] sd 4:0:0:0: [sdc] Write Protect is off
[20771.274141] sd 4:0:0:0: [sdc] Mode Sense: 00 38 00 00
[20771.274144] sd 4:0:0:0: [sdc] Assuming drive cache: write through
[20771.274146]  sdc: sdc1
[20771.317017] sd 4:0:0:0: [sdc] Attached SCSI disk
</pre>
<p>In questo caso la pendrive viene riconosciuta come sdc ma anche qui non risulta possibile montarla in quanto non viene riconosciuto nessun filesystem al suo interno.</p>
<p>Una volta che si conosce il nome del dispositivo da utilizzare la prima cosa da fare è crearne un immagine sul disco fisso, quindi dopo aver montato uno dei dischi del computer &#8220;ospite&#8221;, proviamo prima con il comando <code>dd_rescue</code> presente in knoppix.</p>
<pre class="brush: bash;">
dd_rescue /dev/sdc /savepath/pendrive.image
</pre>
<p>A differenza del comando dd il software ddrescue evita di inchiodarsi sui settori non leggibili e completerà l&#8217;immagine anche in presenza di errori di lettura dal device.</p>
<p>Nel mio caso su 512MB ddrescue ha saltato per impossibilità di lettura circa 8MB, ma ha completato comunque l&#8217;immagine.</p>
<p>Ora se siete fortunati potreste anche riuscire a montare l&#8217;immagine creata e provare a vedere se risulta leggibile con l&#8217;opzione loop di mount in questo modo</p>
<pre class="brush: bash;">
mount -t vfat -o loop /savepath/pendrive.image /mnt/pendrive/
</pre>
<p>Se invece l&#8217;immagine non risulta montabile allora è il momento di passare a testdisk/photorec.</p>
<p>Per non lasciare nulla di intentato si può provare come dicevo prima ad utilizzare la <a href="http://www.gnu.org/software/ddrescue/ddrescue.html">versione GNU di ddrescue</a>, c&#8217;è <a href="http://www.cgsecurity.org/wiki/Damaged_Hard_Disk#The_best_method:_Antonio_Diaz.27s_GNU_.27ddrescue.27">chi dice che sia più veloce e abbia risultati migliori</a>. In tal caso serve un compilatore C++ che non è incluso nel CD di Knoppix (mentre dovrebbe essere incluso nel DVD). Per installarlo occorre essere collegati ad internet e digitare i seguenti comandi</p>
<pre class="brush: bash;">
aptitude update
aptitude install g++
</pre>
<p>Una volta installato va verificato che sia disponibile l&#8217;eseguibile c++, in caso contrario si può facilmente creare con il comando</p>
<pre class="brush: bash;">
ln -s /usr/bin/g++ /usr/bin/c++
</pre>
<p>A questo punto è sufficiente eseguire pochi comandi per compilare il programma ed ottenere infine l&#8217;immagine del device danneggiato</p>
<pre class="brush: bash;">
# download ddrescue
wget http://download.savannah.gnu.org/releases/ddrescue/ddrescue-1.8.tar.bz2
# extract the source code
tar xjf ddrescue-1.8.tar.bz2
# compile ddrescue
cd ddrescue-1.8
./configure &amp;&amp; make
# first, grab most of the error-free areas in a hurry:
./ddrescue -n /dev/old_disk /dev/new_disk rescued.log
# then try to recover as much of the dicy areas as possible:
./ddrescue -r 1 /dev/old_disk /dev/new_disk rescued.log
</pre>
<h4>Uso di testdisk</h4>
<p>Se non siamo riusciti a recuperare niente con ddrescue, allora si può provare con questi due utilissimi tool.</p>
<p>Il primo serve per cercare di riparare partizioni danneggiate, mentre il secondo recupera i dati da dispositivi raw.</p>
<p>Il programma testdisk è un programma interattivo che va avviato passandogli semplicemente il nome del dispositivo (o dell&#8217;immagine) sul quale lavorare.</p>
<p>Una volta avviato l&#8217;eseguibile il programma si presenta con una schermata nella quale possiamo muoverci tra le varie opzioni con i tasti cursore. L&#8217;utilizzo è abbastanza intuitivo, in ogni caso <a href="http://www.cgsecurity.org/wiki/Running_TestDisk">viene spiegato molto in dettaglio</a> sul sito del programma cosa si può fare con esso e come va usato.</p>
<div id="attachment_207" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.codewithstyle.eu/wp-content/uploads/2009/12/testdisk.png"><img class="size-medium wp-image-207" title="testdisk" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/testdisk-300x136.png" alt="Schermata principale di testdisk" width="300" height="136" /></a><p class="wp-caption-text">Schermata principale di testdisk</p></div>
<p>Un&#8217;altra feature molto interessante di questo strumento è che può essere utilizzato anche per <a href="http://www.cgsecurity.org/wiki/TestDisk:_undelete_file_for_NTFS">l&#8217;undelete dei files cancellati su partizioni NTFS</a>, <a href="http://www.cgsecurity.org/wiki/TestDisk:_undelete_file_for_FAT">FAT</a> ed <a href="http://www.cgsecurity.org/wiki/TestDisk:_undelete_file_for_ext2">ext2</a>.</p>
<h4>Utilizzo di photorec</h4>
<p>Chiaramente nel mio caso il recovery della partizione non è andato a buon fine per nessuna delle 2 immagini create con le differenti versioni di ddrescue. A questo punto non rimane che una speranza, photorec. Però si tratta di una speranza ben riposta, perché questo software fa veramente miracoli ed è quanto di più banale ci sia al mondo da utilizzare.</p>
<p>Anche in questo caso per avviare il recupero è sufficiente avviare l&#8217;eseguibile seguito dal nome dell&#8217;immagine creata in precedenza. Una volta avviato il programma si devono indicare pochi dati per iniziare la ricerca dei files perduti (<a href="http://www.cgsecurity.org/wiki/PhotoRec_Step_By_Step">guida illustrata passo per passo</a>), tra cui indicare la partizione da cui recuperare oppure il device raw da cui leggere, indicare tipo della tabella delle partizioni e tipo di filesystem di cui si tratta(va). Infine va selezionata la directory dove salvare il materiale recuperato, dare l&#8217;avvio ed aspettare incrociando le dita, quello che ci si presenterà davanti è questa schermata</p>
<div id="attachment_210" class="wp-caption aligncenter" style="width: 677px"><a href="http://www.codewithstyle.eu/wp-content/uploads/2009/12/PhotoRec_running.png"><img class="size-full wp-image-210" title="photorec" src="http://www.codewithstyle.eu/wp-content/uploads/2009/12/PhotoRec_running.png" alt="Photorec in azione" width="667" height="479" /></a><p class="wp-caption-text">Photorec in azione</p></div>
<p>Alla fine del recupero nella directory indicata troverete tutto il materiale recuperato, per cui se siete stati fortunati avrete nuovamente i file dati per persi.</p>
<p>Nel mio caso sono stato in grado di recuperare da una partizione completamente danneggiata (testdisk non è riuscito neanche a riconoscere il tipo di partizione) circa 441 files sui circa 500 presenti. Tra di essi alcuni documenti in formato excel sono risultati illeggibili mentre per quanto riguarda gli altri formati non ci sono stati problemi. Ovviamente mancava il documento più importante di tutti ma d&#8217;altronde si sa la <a href="http://it.wikipedia.org/wiki/Legge_di_Murphy">legge di Murphy</a> è sempre in agguato, per cui prima di essere costretti ad utilizzare questi programmi la parola d&#8217;ordine è sempre backup, backup, backup.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/775OTgh69OOmF7FolTR_WtYRpzQ/0/da"><img src="http://feedads.g.doubleclick.net/~a/775OTgh69OOmF7FolTR_WtYRpzQ/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/775OTgh69OOmF7FolTR_WtYRpzQ/1/da"><img src="http://feedads.g.doubleclick.net/~a/775OTgh69OOmF7FolTR_WtYRpzQ/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/vvao4TR3uOs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/12/05/recuperare-i-propri-files-da-un-disco-danneggiato/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/12/05/recuperare-i-propri-files-da-un-disco-danneggiato/</feedburner:origLink></item>
		<item>
		<title>Configurare Zend_Mail usando application.ini</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/djw_Q6eVyew/</link>
		<comments>http://www.codewithstyle.eu/2009/11/18/configurare-zend_mail-usando-application-ini/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 01:08:25 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[application resource]]></category>
		<category><![CDATA[application.ini]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Zend Mail]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=167</guid>
		<description><![CDATA[In un precedente articolo ho parlato di come scrivere un&#8217;application resource per Zend Framework per utilizzare il plugin ZFDebug. In questo articolo vedremo come utilizzare lo stesso meccanismo per un&#8217;operazione più semplice ma di utilizzo molto più frequente, ovvero configurare il default transport per l&#8217;invio di messaggi di posta elettronica tramite il componente Zend_Mail.
Normalmente per configurare l&#8217;invio [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-medium wp-image-174" title="zfmail" src="http://www.codewithstyle.eu/wp-content/uploads/2009/11/test-300x158.png" alt="zfmail" width="300" height="158" />In un <a href="http://www.codewithstyle.eu/2009/11/09/debug-di-applicazioni-zend-framework-con-zfdebug/">precedente articolo</a> ho parlato di come scrivere un&#8217;application resource per Zend Framework per utilizzare il plugin ZFDebug. In questo articolo vedremo come utilizzare lo stesso meccanismo per un&#8217;operazione più semplice ma di utilizzo molto più frequente, ovvero configurare il default transport per l&#8217;invio di messaggi di posta elettronica tramite il componente Zend_Mail.</p>
<p>Normalmente per configurare l&#8217;invio dei messaggi si utilizza il seguente codice (ad esempio nel bootstrap):</p>
<pre class="brush: php;">
$config = array('auth' =&gt; 'login',
                'username' =&gt; 'myusername',
                'password' =&gt; 'password');

$transport = new Zend_Mail_Transport_Smtp('mail.server.com', $config);
Zend_Mail::setDefaultTransport($transport);
</pre>
<p>Per liberarci di questo codice in ogni applicazione che utilizzi la classe Zend_Mail ho scritto una classe Application Resource da inserire nella propria libreria. Il codice della classe è piuttosto semplice<br />
<span id="more-167"></span></p>
<pre class="brush: php;">
/**
 * Resource for creating smtp transports
 *
 * @uses       Zend_Application_Resource_ResourceAbstract
 * @category   Zend
 * @package    Zend_Application
 * @subpackage Resource
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zenit_Application_Resource_Smtp extends Zend_Application_Resource_ResourceAbstract {
	/**
	 * Transport
	 *
	 * @var Zend_Mail_Transport_Abstract
	 */
	protected $_transport = null;

	/**
	 * Parameters to use
	 *
	 * @var array
	 */
	protected $_params = array ();

	/**
	 * Server name
	 *
	 * @var string
	 */
	protected $_server = '127.0.0.1';

	/**
	 * @var string
	 */
	protected $_transportClass;

	/**
	 * Set the adapter params
	 *
	 * @param $params array
	 */
	public function setParams(array $params) {
		$this-&gt;_params = $params;
		return $this;
	}

	/**
	 * Validates the hostname provided as server
	 *
	 * @param string $server
	 */
	public function setServer($server) {
		if (Zend_Validate::is($server, 'Hostname')) {
			$this-&gt;_server = $server;
		} else {
			throw new Zend_Mail_Exception(&quot;Invalid server name $server&quot;);
		}
	}

	/**
	 * Returns the hostname for the server
	 *
	 * @return string
	 */
	public function getServer() {
		return $this-&gt;_server;
	}

	/**
	 * Transport parameters
	 *
	 * @return array
	 */
	public function getParams() {
		return $this-&gt;_params;
	}

	/**
	 * Determine the type of transport to use
	 *
	 * @param string $class
	 * @throws Zend_Mail_Transport_Exception if the given class doesn't exists
	 */
	public function setTransport($class) {
		if (false === strpos($class, '_')) {
			// allow short names such as smtp
			$class = 'Zend_Mail_Transport_' . ucfirst($class);
		}
		if (!class_exists($class)) {
			throw new Zend_Mail_Transport_Exception(
					&quot;Unexistant transport class $class&quot;);
		}
		$this-&gt;_transportClass = $class;
	}

	/**
	 * Return the transport class to use
	 *
	 * @return null|Zend_Mail_Transport_Abstract
	 */
	public function getTransport() {
		if (null == $this-&gt;_transport &amp;&amp; $this-&gt;_transportClass) {
			$class = $this-&gt;_transportClass;
			// instantiate class
			switch ($class) {
				case 'Zend_Mail_Transport_Smtp':
				case 'Zenit_Mail_Transport_Smtp':
					$transport = new $class($this-&gt;getServer(),
							$this-&gt;getParams());
					break;
				case 'Zend_Mail_Transport_Sendmail':
				default:
					$transport = new $class();
					break;
			}
			// check transport type
			if (!$transport instanceof Zend_Mail_Transport_Abstract) {
				throw new Zend_Mail_Transport_Exception(
						&quot;Class $class is not a valid Zend_Mail_Transport_Abstract&quot;);
			}
			// set transport as default transport
			Zend_Mail::setDefaultTransport($transport);
			// assign it to local field
			$this-&gt;_transport = $transport;
		}
		return $this-&gt;_transport;
	}

	/**
	 * Defined by Zend_Application_Resource_Resource
	 *
	 * @return null|Zend_Mail_Transport_Abstract
	 */
	public function init() {
		return $this-&gt;getTransport();
	}
}
</pre>
<p>Come nel caso precedente, è sufficiente inserire questa classe nel proprio include_path e cambiare/registrare il namespace della libreria (nel mio caso Zenit).</p>
<p>Fatto ciò per configurare il trasporto da usare usando il file <code>application.ini</code> è sufficiente inserire queste righe:</p>
<pre class="brush: plain;">
; smtp stuff
resources.smtp.transport = &quot;smtp&quot;
resources.smtp.server = &quot;smtp.example.org&quot;
resources.smtp.params.auth = &quot;login&quot;
resources.smtp.params.username = &quot;myusername&quot;
resources.smtp.params.password = &quot;mypassword&quot;
</pre>
<p>Et voilà, il gioco è fatto. Ora si può usare in qualsiasi punto della propria applicazione la classe Zend_Mail per l&#8217;invio di messaggi ed il metodo di invio usato sarà quello configurato tramite il file <code>application.ini</code>.</p>
<p>È possibile usare anche classi personalizzate come transport, è sufficiente che tali classi estendano la classe <code>Zend_Mail_Transport_Abstract</code> e che prevedano un costruttore vuoto (in alternativa è necessario personalizzare il metodo <code>getTransport</code> per adattarlo alle proprie esigenze). Per utilizzare una classe personalizzata è sufficiente passare il nome completo della classe come valore dell&#8217;opzione <code>resources.smtp.transport</code>, mentre per le classi predefinite si può anche usare il nome breve della classe senza il namespace completo (nell&#8217;esempio &#8220;smtp&#8221;).</p>
<p>Se il server smtp non necessita di autenticazione per l&#8217;invio è sufficiente rimuovere le relative righe dal file di configurazione (4-6).</p>
<p>Per quanto mi riguarda attualmente sto utilizzando come transport l&#8217;estensione della classe <code>Zend_Mail_Transport_Smtp</code> consigliatami in <a href="http://old.nabble.com/Incredible-memory-usage-on-public-server%2C-normal-on-dev-server-ts26215767.html">questo thread</a> del forum di Zend_Framework, in quanto la classe originale soffre di un memory leak nel momento in cui si invia un numero consistente di email nello stesso script, a causa di un log interno che viene salvato in una stringa in memoria. Non appena verrà rilasciato l&#8217;aggiornamento che includa la correzione discussa <a href="http://framework.zend.com/issues/browse/ZF-8278">qui</a>, tornerò ad usare la classe ufficiale (di conseguenza la linea <code>case 'Zenit_Mail_Transport_Smtp':</code> nel metodo <code>getTransport</code> verrà rimossa dal codice).</p>

<p><a href="http://feedads.g.doubleclick.net/~a/L0GXOxxyw792Km7Jq0QKEfn2qJI/0/da"><img src="http://feedads.g.doubleclick.net/~a/L0GXOxxyw792Km7Jq0QKEfn2qJI/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/L0GXOxxyw792Km7Jq0QKEfn2qJI/1/da"><img src="http://feedads.g.doubleclick.net/~a/L0GXOxxyw792Km7Jq0QKEfn2qJI/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/djw_Q6eVyew" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/11/18/configurare-zend_mail-usando-application-ini/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/11/18/configurare-zend_mail-usando-application-ini/</feedburner:origLink></item>
		<item>
		<title>Backup di un repository svn</title>
		<link>http://feedproxy.google.com/~r/FabiosTechBlog/~3/l5EpFKQ3QzI/</link>
		<comments>http://www.codewithstyle.eu/2009/11/14/backup-di-un-repository-svn/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 03:28:34 +0000</pubDate>
		<dc:creator>fabio</dc:creator>
				<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[repository backup]]></category>
		<category><![CDATA[subversion]]></category>
		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://www.codewithstyle.eu/?p=155</guid>
		<description><![CDATA[Uno degli strumenti che utilizzo ed apprezzo di più nell&#8217;uso quotidiano è sicuramente subversion. Questo strumento eccezionale è insuperabile quando si lavora in gruppo, ma è utilissimo anche quando si è gli unici a lavorare su un singolo progetto.
Per questo ognuno dei progetti su cui lavoro è gestito tramite un repository subversion nel quale memorizzo [...]]]></description>
			<content:encoded><![CDATA[<p>Uno degli strumenti che utilizzo ed apprezzo di più nell&#8217;uso quotidiano è sicuramente <a href="http://subversion.tigris.org/">subversion</a>. Questo strumento eccezionale è insuperabile quando si lavora in gruppo, ma è utilissimo anche quando si è gli unici a lavorare su un singolo progetto.</p>
<p>Per questo ognuno dei progetti su cui lavoro è gestito tramite un repository subversion nel quale memorizzo tutti i files di interesse. In tal modo ho sempre la possibilità di ricostruire le modifiche ad un file nel tempo, di poterne recuperare le versioni precedenti, di poter trasferire le modifiche con estrema semplicità da una macchina all&#8217;altra, insomma una versione super di <a href="http://www.apple.com/macosx/what-is-macosx/time-machine.html">Time Machine</a> (chiaramente senza fronzoli grafici e non di così immediato utilizzo).</p>
<p>Quando si utilizza subversion, indipendentemente da come si accede al repository, tutto il contenuto (dati, revisioni e configurazione) è memorizzato nella directory del repository stesso, che è semplicemente una directory all&#8217;interno del filesystem. Per eseguire il backup di un repository si potrebbe pensare di salvare il contenuto di questa directory su supporto esterno ed amen. Sebbene questa soluzione potrebbe anche funzionare, di sicuro non è quella consigliata. Infatti una soluzione di questo tipo potrebbe corrompere lo stato interno del repository nel caso in cui venga effettuata una qualsiasi operazione sul repository stesso durante la copia, rendendo inservibile il backup creato.</p>
<p>Vediamo invece una delle strategie di backup possibili utilizzando gli strumenti messi a disposizione con la distribuzione standard di subversion.<span id="more-155"></span></p>
<h4>Lo script hot-backup.py</h4>
<p>Il primo strumento da utilizzare per creare un backup di un repository è lo script <code>hot-backup.py</code>. Questo script è progettato per eseguire una copia completa di un repository &#8220;a caldo&#8221;, ovvero anche se il repository è in uso crea una copia consistente di tutte le transazioni evitando di incorrere nei problemi descritti sopra. Per cui utilizzando tale script si otterrà una copia funzionante del repository pronta all&#8217;uso. L&#8217;utilizzo di questo comando è abbastanza immediato (su Debian lo script è installato di default in <code>/usr/bin/svn-hot-backup</code>):</p>
<pre class="brush: bash;">
# svn-hot-backup --help
USAGE: svn-hot-backup [OPTIONS] REPOS_PATH BACKUP_PATH

Create a backup of the repository at REPOS_PATH in a subdirectory of
the BACKUP_PATH location, named after the youngest revision.

Options:
  --archive-type=FMT Create an archive of the backup. FMT can be one of:
                       bz2 : Creates a bzip2 compressed tar file.
                       gz  : Creates a gzip compressed tar file.
                       zip : Creates a compressed zip file.
  --num-backups=N    Number of prior backups to keep around (0 to keep all).
  --help      -h     Print this help message and exit.
</pre>
<p>È sufficiente passargli il percorso del repository ed il percorso dove si vuole salvare il backup ed il gioco è fatto, copia creata:</p>
<pre class="brush: bash;">
# svn-hot-backup /home/svn/repositories/src backup/
Beginning hot backup of '/home2/svn/repositories/src'.
Youngest revision is 1663
Backing up repository to 'backup/src-1663'...
Done.
</pre>
<p>Il comando ha appena creato una copia perfettamente funzionante del repository in oggetto nella directory di backup.</p>
<p>Vediamo come utilizzare questo comando con le sue opzioni per programmare un backup a lungo termine. Le opzioni a disposizione sono solamente due, ma entrambe molto utili. La prima opzione è <code>--archive-type=FMT</code>, con questa opzione si può dire al comando di creare un archivio compresso in uno dei formati supportati dallo script, ovvero gz, bz2 e zip. La seconda opzione, <code>--num-backups=N</code>, invece consente di eseguire il rotate del log in maniera simile al comando <code>logrotate</code>, mantenendo solamente le <code>N</code> copie più recenti dei backup.</p>
<p>Combinando le due opzioni viste si può scrivere un cronjob per salvare il contenuto dei repository su un supporto di backup ad intervalli regolari. Nel mio caso ho inserito il backup nei cron settimanali della macchina, creando il file <code>/etc/cron.weekly/backup-svn</code> il cui contenuto è</p>
<pre class="brush: bash;">
#! /bin/sh

MAILTO='fabio@example.com'

# Backup svn repositories

test -x /usr/bin/svn-hot-backup || exit 0

# repositories list
REPOS_LIST=&quot;
/home/svn/repositories/src
/home/svn/repositories/src2&quot;

# rotate every n backups
ROTATE=4

# backup path
BACKUP_PATH=/home/svn/backups

for REPOS in $REPOS_LIST; do
        svn-hot-backup --num-backups=$ROTATE --archive-type=bz2 $REPOS $BACKUP_PATH
done
</pre>
<p>In questo modo ogni settimana viene creata una copia integrale dei repositories indicati nella variabile REPOS_LIST mantenendo solamente gli ultimi 4 nella directory per i backup. Chiaramente se la directory BACKUP_PATH risiede sulla stesso disco del repository, è quasi inutile questo tipo di backup. Nel mio caso la directory contenente i backups viene copiata giornalmente tramite <code>rsync</code> su un&#8217;altra macchina. In alternativa è sufficiente aggiungere un comando dopo il ciclo for che copi il contenuto della directory dove meglio si crede (disco esterno, disco di rete, nastro, etc).</p>
<p>Un consiglio importante, in precedenza avevo provato ad impostare la variabile BACKUP_PATH con il percorso di un disco montato con nfs, e le prestazioni erano veramente ridicole, per cui se volete utilizzare un disco di rete (smb o nfs che sia) conviene creare prima i backup sul disco locale e poi copiarli. Non eseguite direttamente lo script hot-backup sull&#8217;unità di rete perché è spaventosamente lento.</p>
<h4>Backup incrementale delle singole revisioni</h4>
<p>Il metodo precedente crea i backup settimanalmente (anche se basta impostare diversamente cron per eseguirlo con cadenza differente), ma che succede se c&#8217;è un imprevisto dopo 6 giorni dal backup. Potenzialmente si possono perdere fino a 6 giorni di lavoro, e sicuramente si perdono tutte le singole revisioni di cui si è fatto commit dall&#8217;ultimo backup.</p>
<p>Una soluzione molto elegante a questo problema (che può funzionare anche come soluzione stand-alone di backup) è quella di utilizzare il backup incrementale delle singole revisioni di cui si fa commit utilizzando il programma <code><a href="http://svnbook.red-bean.com/en/1.1/re31.html">svnadmin dump</a></code> e gli <a href="http://svnbook.red-bean.com/en/1.1/ch05s02.html#svn-ch-5-sect-2.1">hook scripts</a>. Utilizzando questi due strumenti è possibile salvare ogni singola revisione del repository per poi reimportarla in un repository creato nella sezione precedente. In tal modo in casi di disastri si perderà poco o niente del proprio repository.</p>
<p>Per salvare il contenuto di ogni singola revisione in una directory a piacimento è sufficiente editare (o creare) lo script <code>post-commit</code> nella directory hooks del repository ed aggiungere il seguente contenuto</p>
<pre class="brush: bash;">
REPOS=&quot;$1&quot;
REV=&quot;$2&quot;

# backup of current revision to revision dir
svnadmin dump &quot;$REPOS&quot; -q -r $REV --incremental --deltas | gzip &gt; /home/svn/backups/revisions/src.rev$REV.gz
</pre>
<p>In questo modo ogni volta che si fa commit il contenuto della revisione corrente viene salvato nel file src.rev&lt;num&gt;.gz e può essere utilizzato tramite il comando svnadmin load per ricreare la modifica su una copia del repository.</p>
<p>Una cosa estremamente importante a cui prestare attenzione è il flag <code>--incremental</code>, infatti senza di esso verrebbe salvato un dump completo del repository nel file di backup.</p>
<p>Per quanto riguarda invece il flag <code>--deltas</code> può essere anche omesso, esso serve per creare il dump in formato binario e in modo che contenga solo le differenze con la precedente revisione; questo fa si che il file che contiene la revisione sia sensibilmente più piccolo di quello ottenuto senza questo flag (che contiene una copia intera in formato testuale dei files modificati nella revisione corrente). Quindi usare o meno il flag <code>--deltas</code> è un compromesso tra spazio disco ed uso processore in fase di dump/load e di conseguenza in fase di commit. Questo è il raffronto in termini di costi tra un backup creato con e senza il flag <code>--deltas</code> su un repository di dimensioni medio-piccole. Da notare come, se compressi con <code>gzip</code>, i backup ottenuti abbiano più o meno la stessa dimensione finale.</p>
<pre class="brush: bash;">
# time svnadmin dump -q /home/svn/repositories/src --deltas &gt; deltas.dump

real	0m16.554s
user	0m12.181s
sys	0m4.128s
# time svnadmin dump -q /home/svn/repositories/src &gt; nodeltas.dump

real	0m9.240s
user	0m6.956s
sys	0m2.016s
# ll -h
total 86M
-rw-r--r-- 1 root root 28M 2009-11-13 22:12 deltas.dump
-rw-r--r-- 1 root root 58M 2009-11-13 22:13 nodeltas.dump
# ll -h
total 42M
-rw-r--r-- 1 root root 21M 2009-11-13 22:12 deltas.dump.gz
-rw-r--r-- 1 root root 22M 2009-11-13 22:13 nodeltas.dump.gz
</pre>
<p>I backup delle singole revisioni andrebbero memorizzati insieme ad i backup completi creati in precedenza, in modo tale da poterli utilizzare congiuntamente per ricreare il repository nello stato dell&#8217;ultima versione disponibile. Per eseguire un ripristino è sufficiente prendere l&#8217;ultima versione completa a disposizione del repository scompattarla e importarci tutte le successive revisioni di cui si ha il backup con il comando <a href="http://svnbook.red-bean.com/en/1.5/svn.ref.svnadmin.c.load.html"><code>svnadmin load</code></a>.</p>

<p><a href="http://feedads.g.doubleclick.net/~a/STyrFlgsjlACYeJdQPNMt0lbC7M/0/da"><img src="http://feedads.g.doubleclick.net/~a/STyrFlgsjlACYeJdQPNMt0lbC7M/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/STyrFlgsjlACYeJdQPNMt0lbC7M/1/da"><img src="http://feedads.g.doubleclick.net/~a/STyrFlgsjlACYeJdQPNMt0lbC7M/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds.feedburner.com/~r/FabiosTechBlog/~4/l5EpFKQ3QzI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.codewithstyle.eu/2009/11/14/backup-di-un-repository-svn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.codewithstyle.eu/2009/11/14/backup-di-un-repository-svn/</feedburner:origLink></item>
	</channel>
</rss>
