Tutti gli articoli di Andrea Tassotti

diversamenteArte

Diversamente Arte (prima parte)

Arte ed Informatica si sono incontrate piú volte dalla nascita di quest’ultima.

L’Informatica ha creato nel tempo strumenti a supporto dell’Arte parlando la lingua dell’Arte (dalla conservazione per via digitale di capolavori alla creazione di nuovi strumenti); ma ha saputo dare all’Arte (o meglio agli artisti) anche nuovi linguaggi (e i conseguenti strumenti).

Molti strumenti sono divenuti di uso quotidiano in molte realtà ove l’Arte é anche industria (si pensi agli strumenti di fotoritocco, di modellazione 3D o per la produzione audio-video); altri sono rimasti pura sperimentazione o passatempo per informatici estrosi (e con molto tempo libero).

Di questa “diversa Arte” o “Arte diversa” vogliamo parlare in questo post.

Avrete certo visto forme di ASCII Art, ossia immagini create in documenti testo da caratteri la cui forma e posizione fanno credere a differenti livelli di tono tanto da illudere a sufficienza l’osservatore. Per lo più realizzate con carattere tipografo a corpo fisso (come nella “old school” in voga nel mondo Amiga) o nelle forme piú estreme con corpo variabile e colori (addirittura video con la AAlib di Linux), queste forme d’arte hanno abbellito, decorato o commentato manuali, programmi e quant’altro.

Da notare che anche gli emoticons noti ai piú nascono come forma minimalista di Ascii Art:

:-)

Su YouTube potreste trovare “composizioni per aghi e carta” (avete letto bene!): c’é chi ha “accordato” la propria stampante a matrice di aghi per eseguire composizioni musicali.

Ci sono artisti che amano creare Photographic Mosaic (o Photomosaic), ossia usare immagini come punti per una immagine piú grande frutto di questa composizione; oppure ce ne sono altri un po’ retro’ che si cimentano nella Pixel Art (puntillismo), ossia disegnano un pixel alla volta, con pazienza infinita.

Esistono programmi che consentono a musicisti di generare suoni o musiche a partire da una immagine, o consentono di imprimere il  volto dell’autore nello spettro audio di una composizione (in modo cioé che uno spettrogramma riproduca una immagine di tale volto: è un vezzo questo che si è concesso I’artista Aphex Twin (al secolo Richard David James).

Molti piú dettagli e molte altri modi di fare arte con i computer possono essere approfonditi cercando il termine “demoscene” su Wikipedia.

E veniamo all’esempio di “diversa arte” di cui vogliamo ampliare la conoscenza.

Non tutti sanno che le motherboard di PC e Server sono dotate di beeper (emettitore acustico tipicamente piezoelettrico). Spesso la sua presenza è rilevabile solo nelle fasi pre-boot, nell’accesso al BIOS, e altre “fasi hardware”.

Per sistemisti (o utenti) UNIX il “beep” è una presenza immanente, sin dai tempi dei terminali “telescriventi”: ma anche tra questi sono pochi a conoscere che il beeper può essere “accordato” ad emettere suono di altezza e lunghezza differenti dal classico “beep” (tipicamente 750Hz per 200msec).

Pochi non vuol dire nessuno: utilizzando il comando beep, sia su sistemi FreeBSD, sia su sistemi Linux (con differenti implementazioni), “diversamente artisti” hanno potuto sequenziare l’emissione sonora e temporale del beeper per eseguire musica, benché monofonica (una sola voce) e monotimbrica (un solo colore).

Tra questi artisti, molti lavorano direttamente sui parametri del comando beep (frequenza in Hz e tempo in msec (o cent sec nel caso FreeBSD).
I piú raffinati tentano un approccio con convertitori da MIDI SMF a sequenza di comandi beep (o sequenza di suoi parametri nella implementazione Linux).

Entrambi gli approcci hanno dei problemi: il primo è certamente un metodo “innaturale” e “complicato”; il secondo si deve scontrare con il limite monofonico (innaturale nelle trascrizioni MIDI di musica) e con i problemi della quantizzazione del tempo che può tradursi in una caotica quantità di esecuzioni di comandi beep (o suoi parametri) per mimare il timing degli eventi MIDI.

Il sistema MIDI si basa infatti sulla successione di eventi note on e note off, nell’ottica di una meccanica key pressed (on) e key released (off): questo metodo é perfetto per “registrare” esattamente l’esecuzione di un brano da parte di un musicista su uno strumento di input MIDI (come una keyboard): la riproduzione sarà esattamente uguale (con precisione molto, molto elevata): in questo é cruciale il riferimento temporale (relativo tra due eventi o assoluto).

Il comando beep non ha un sistema di riferimento temporale: il suono viene emesso nel momento di esecuzione del comando e per il tempo indicato (durata): nella versione Linux in cui si può istruire un solo comando ad eseguire piú note e si può indicare anche una latenza temporale tra due note (pausa), ma sempre in termini di durata.

Per i piú ferrati in musica si riconoscerà in questo un approccio più simile al metodo musicale della divisione del tempo, in cui è fondamentale il concetto di durata (e altezza) della nota.
Vogliamo quindi fornire un nuovo strumento per questa Beep Art (o meglio Beep Music), che sia semplice e piú”musicale” possibile.
Proponiamo di scrivere in un semplice file testo una nota per riga (come un “rullo musicale”), espressa con altezza, durata naturale e modificatori, tre campi separati da carattere colon (:). Questo file testo sarà la nostra partitura che un software (che andremo a costruire) provvederà a convertire in una sequenze di comandi beep.

Esistono sistemi di Music Engraving che si basano su file testo molto semplice (vedi abc),
quindi non si consideri banale questo metodo: solo prototipale.

L’altezza sarà espressa in notazione anglosassone e con estensione come dettata anche dallo standard MIDI dalla MMA (da C-2 a G8, ossia dal Do ben 2 ottave sotto la nota piú bassa del pianoforte, fino a 127 semitoni sopra).
La durata sarà espressa in termini di divisione musicale, esprimendo la componente a denominatore. Come modificatori saranno supportati il punto (singolo, doppio e triplo, e i gruppi (irregolari) espressi da un semplice numero.
Le pause saranno espresse da note con altezza “P”, con durata e modificatori come per le note.

E’ evidente che in questo modo la durata e posizione delle note nel tempo musicale é garantita dalla divisione del tempo tra note e pause (come é nelle notazione classica e come é pure per il comando beep.

A completare la sintassi introduciamo indicazioni sulla divisione con il metadato tsig (indicata in modo canonico con una frazione), ed indicazione sul tempo (velocità di esecuzione), con un numero che esprima i bpm (beat for measure) ossia il numero di pulsazioni (note di lunghezza indicate dal denominatore delle tsig: per un 3/4 una nota di 1/4) al minuto.
Questi parametri altro non fanno che produrre il rapporto tra le durate di note e pause in termini di tempo assoluto (secondi o frazionamenti).

Se volessimo realizzare un metronomo per una divisione 4/4 avremo:

A5:4:
A4:4:
A4:4:
A4:4:

Ma questo andrebbe bene se il generatore di toni fosse di natura impulsiva e le note poste all’inizio dei quarti della misura decadessero in ampiezza prima di esaurire la loro durata (caso tipico di una partitura MIDI per un drum kick).

Questo non é quasi mai valido in generale, e non é assolutamete vero nel caso di beep ché si comporta come un generatore di tono ideale (potendo assumere altezza e durata arbitrari).

Per questo la giusta partitura potrebbe essere:

A5:16:
A4:16:
A4:16:
A4:16:

Dato che questo è poco intuitivo per un musicista, introduciamo un meccanismo di “umanizzazione” indicando nella “partitura” i portamenti “legato” (comportamanto naturale) e “staccato” (meccanismo per emulare suoni di natura impulsiva). Questo sintatticamente verrà collocato all’interno di una riga di commento. La riga di commento potrà essere inoltre utilizzata per indicare l’inizio della “misura” (o battuta). Questo potrà essere d’ausilio nella verifica (anche automatica) della trascrizione.

Come si può intuire trascrivere musica da spartito originale con questa notazione risulta assai facile.

Vedremo nel prossimo post come anche la trasformazione in Beep Music sia altrettanto facile.

(Fine prima parte)

videogame80

Amarcord e nuove sfide

Correvano gli anni ’80 (secolo XX!) e nelle case c’erano i cosí detti “home computers”.
Imperava la sfida tra possessori di ZX Spectrum e Commodore 64.
Era l’epoca in cui ci si approcciava alla programmazione con il linguaggio BASIC; i piú arditi si spingevano verso l’assembler (io tra quelli) a colpi di PEEK e POKE (mitiche istruzioni BASIC per accedere direttamente alla memoria di quelle semplici architetture, perciò semplici da comprendere).

Era il tempo in cui eravamo capaci di attendere decine di minuti per caricare da nastro magnetico (una semplice cassetta audio analogica!) in memoria i pochi kbytes (il 64 ne aveva appunto 64, di cui solo 38 per programmi BASIC) dell’agognato video gioco: il tutto da fruire sul TV di casa.
I piú fortunati poterono godere dell’accesso diretto (e della relativa velocitá) dei floppy disk da 5″1/2 per la bellezza di 176k (unità 1541), o di un monitor a colori (1701/1702)!

Molti giochi erano proprio in BASIC, trascritti da pagine di riviste di computer (cosa sono?), frutto di scambi e baratto. Spesso si ignoravano origine e autore.

Vedere un super calcolatore giocare a Tic TacToc (Tris) nel film “War Games” nello stesso modo (e sostanzialmente qualità grafica ) del nostro home computer era parte di una forma di orgoglio di essere nel futuro, nella modernità, al passo con la tecnologia.

The-Only-Winning-Move-is-NOT-to-Play-320x177

Se aggiungiamo il fatto che il gioco lo avevamo “scritto di nostro pugno”, é facile immaginare quali emozioni potevano correre nei nostri cuori.

Di quei giochi semplici si é perso un po’ ricordo, e non sono stati piú riproposti (con qualche eccezione come componente ludica nei primi cellulari Nokia: ricordate Snakes?).

Snakec64
E quindi lanciamo qui una sfida.

Cominciamo una “operazione amarcord”, ossia cominciamo a recuperare al ricordo e alla fruibilità questi vecchi e gloriosi giochi.
Noi inizieremo da uno: cerchiamo volenterosi che proseguano la sfida.

La mia scelta cade sul ricordo di un gioco il cui nome era noto a me come Strike & Ball, gioco che mi ha appassionato sia nella sua implementazione (ne avevo esteso le caratteristiche di base e realizzato una interfaccia molto accattivante per i mezzi disponibili), sia nel suo utilizzo.

Grazie ad Internet (e Wikipedia), inesistente negli anni ’80, scopro che questo glorioso gioco aveva un padre illustre (e non si tratta dell’ovvio Mastermind che deve suoi natali allo stesso padre).
Si tratta di Bulls and Cows un gioco semplice che può essere giocato tra amici anche semplicemente con carta e penna.
La cosa per me piú affascinante é che il gioco é da ritenersi all’origine del primo “videogioco” della storia: nel 1970 infatti J.M.Grochow al MIT scrisse in PL/1 su Multics (cavolo! il padre di Unix! … quasi una riunione di famiglia!) un programma dal nome moo (esplicativo, no?!) per giocare a Bulls and Cows.

Le regole di gioco (nella versione numerica, ossia quella da cui é derivata la versione del 1970) sono molto semplici.
Si tratta di indovinare in un numero finito di tentativi un numero segreto di 4 cifre; ad ogni tentativo l’avversario (nel nostro caso il computer) indicherà quante cifre sono state individuate nella posizione corretta con il simbolo X (Strike)  e quante invece sono state indicate giuste ma nella posizione errata con il simbolo O (Ball).

Si vince indovinando in numero segreto; si perde esaurendo i tentativi concessi (tipicamente 10).

Sembra facile, ma vi assicuro che consente di impegnare un po’ di “materia grigia”!

Nella versione originale un progamma al calcolatore impiega in media circa 5 tentativi per indovinare il numero segreto! Voi?

Ovviamente si può “alzare l’asticella” elevando il numero di cifre per il numero segreto.
Alla prossima puntata con due proposte di implementazione per questo gioco.

P.S. Forse ne troverete in giro, anche Open Source: ma noi cercheremo di stupirvi!

IMG_3049.PNG

Estendere le capacitˆà di stampa di Cocoa

Avete realizzato l’interfaccia per cdlabelgen come descritto nel precedente post?

Beh, io si e ho affrontato anche il problema della stampa: voi?

La stampa in programmi Cocoa deriva dall’essenziale derivazione del motore grafico Quartz dal PDF. Quindi in Cocoa la stampa nasce dalla costruzione di un suo oggetto NSView da cui discendano come contenuto tutte le componenti grafiche necessarie a strutturare il layout finale.

Quello che otteniamo è dunque una relazione 1:1 tra widget Cocoa, testo e immagini e le componenti PDF che porteremo alla stampa.

Ma con cdlabelgen abbiamo a che fare con postscript: questo è ciò che viene generato dallo script. Che PDF e postscript siano parenti non aiuta affatto!

Cercando in Internet non ho trovato soluzioni, nemmeno lontanamente. Che la cosa non fosse mai stata affrontata o fosse stata risolta con librerie proprietarie dai software di grafica con tanto di blasone mi sembrava alquanto strana.

Poi (la disperazione a volte aiuta!) mi sono ricordato che nella cartella esempi di XCode qualcosa riguardo la stampa era presente: ho cominciato a cercare e studiare, tornando sull’indispensabile (e preziosa) documentazione ADC.

Ed ecco a voi la soluzione (Apple, non mia: io ho solo ritrovato e ora pubblicizzato tale soluzione).

Dicevamo che i frameworks che compongono l’universo Cocoa (in particolare AppKit) supportano una limitata visione del processo di stampa, segnatamente una versione NSView-centrica gestita attraverso le seguenti astrazioni:

  • NSPageLayout
  • NSPrintInfo
  • NSPrintOperation
  • NSPrintPanel
  • NSPrinter

Un supporto completo alla stampa deriva (da indicazione esplicita in documentazione) dall’utilizzo diretto del framework Core Printing, una API in C di cui AppKit crea una interfaccia limitata.

La soluzione da noi cercata per stampare dati postscript (file, in particolare) è la funzione PMPrinterPrintWithFile presente in questo framework; il suo prototipo è:

OSStatus PMPrinterPrintWithFile (

 PMPrinter printer,

 PMPrintSettings settings,

 PMPageFormat format,

 CFStringRef mimeType,

 CFURLRef fileURL

);

Come possiamo vedere mediante il parametro fileURL si fornisce un percorso al file da stampare, file di cui mediante parametro mimeType si indica anche il tipo. La specifica di tipo è importante in quanto deve coincidere con uno dei tipi supportati dal framework; i tipi supportati (ricavati dalla funzione per la validazione del tipo passato) sono:

  •     “application/octet-stream”
  •     “application/pc-eps”
  •     “application/pdf”
  •     “application/pictwps”
  •     “application/postscript”
  •     “application/vnd.apple-postscript”
  •     “application/vnd.cups-banner”
  •     “application/vnd.cups-command”
  •     “application/vnd.cups-postscript”
  •     “application/vnd.cups-raster”
  •     “application/vnd.cups-raw”
  •     “application/vnd.hp-hpgl”
  •     “application/x-cshell”
  •     “application/x-csource”
  •     “application/x-perl”
  •     “application/x-shell”
  •     “application/xhtml+xml”
  •     “image/fuji-raw”
  •     “image/gif”
  •     “image/imageio”
  •     “image/jp2”
  •     “image/jpeg”
  •     “image/minolta-raw”
  •     “image/openexr”
  •     “image/pict”
  •     “image/png”
  •     “image/rad”
  •     “image/tiff”
  •     “image/x-alias”
  •     “image/x-bitmap”
  •     “image/x-bmp”
  •     “image/x-ico”
  •     “image/x-icon”
  •     “image/x-photocd”
  •     “image/x-portable-anymap”
  •     “image/x-portable-bitmap”
  •     “image/x-portable-graymap”
  •     “image/x-portable-pixmap”
  •     “image/x-psd”
  •     “image/x-qtif”
  •     “image/x-sgi-rgb”
  •     “image/x-sun-raster”
  •     “image/x-tga”
  •     “image/x-xbitmap”
  •     “image/x-xpixmap”
  •     “text/html”
  •     “text/plain”
  •     “text/rtf”

Non male, vero?

È ovvio che per invocare questa funzione occorre completarne la lista argomenti: stampante, formato e settaggio per il processo di stampa.
Questi ultimi due parametri li ricaviamo fortunatamente direttamente in AppKit, in particolare da una istanza condivisa di NSPrintInfo 

 [NSPrintInfo sharedPrintInfo]

dopo averla popolata utilizzando NSPrintPanel e NSPageLayout (le componenti Cocoa che realizzano le finestre di dialogo utente per le rispettive finalitˆà utilizzando una istanza di NSPrintInfo). Per Il parametro stampante invocheremo PMSessionGetCurrentPrinter() passando la sessione ottenuta sempre da NSPrintInfo dopo averle collegato settaggio e formato mediante PMSessionDefaultPrintSettings() e PMSessionValidatePageFormat() (funzioni di Core Printing).

Mettendo assieme il tutto avrete la vostra stampa di file postscript.
Questo un rapido quadro della soluzione: per i dettagli c’e’ la documentazione: replicarla qui mi sembra troppo!

Alla prossima!

IMG_2826.JPG

Cocoa & Perl: come ti faccio diventare uno script una applicazione Cocoa

Torniamo a parlare di programmazione Cocoa.

Questa volta parliamo di come integrare una interfaccia Cocoa ad una preesistente applicazione a linea di comando, e perché no, persino un’applicazione interpretata.

Avete preferenze?

Io una si.

Ho scelto di integrare uno script perl che ho sempre utilizzato in Linux e che volevo anche su Mac, ovviamente in una veste più “consona”, passatemi il termine.

Si tratta di cdlabelgen (http://www.aczoom.com/tools/cdinsert/), un utilissimo strumento per generare rapidamente copertine e buste per CDROM e DVD.

Scaricare e provare lo script è stata la prima cosa, anche per verificare quali potevano essere i requisiti per il suo funzionamento su OSX. Tutto tranquillo: funziona senza ulteriori installazioni.
La veste più consona è ovviamente la veste grafica: scartati Automator e AppleScript quali potenziali strumenti, la soluzione è ovviamente una applicazione Cocoa. Come in molti porting di software open source l’applicazione essenzialmente avrà il compito di generare la lista argomenti conseguente alle scelte utente (frontend) e attivare il programma di backend (nel nostro caso cdlabelgen): per non essere dipendente da fattori esterni l’applicazione conterrà lo script nel proprio bundle.

Volendo essere precisi, trattandosi di uno script interpretato, l’applicazione attivata sarà l’interprete perl del sistema a cui verrà passato il nome dello script con i suoi argomenti.
Il risultato mi è sembrato soddisfacente, quindi ve lo propongo. 
Vediamo come si realizza.
Per prima cosa creiamo un nuovo progetto Cocoa Application nel nostro XCode e copiamo lo script cdlabelgen nel gruppo Resources del nostro progetto; parimenti facciamo anche della cartella “postscript” fornita assieme allo script (ci sono i modelli grafici di riferimento).
Poi in un metodo di delega (applicationDidFinishLaunching) dell’applicazione Cocoa leggiamo il percorso alle risorse del bundle: ci serviranno per avviare lo script, in particolare per fornire il percorso delle risorse come program working directory a cdlabelgen in cui cercherà la cartella postScript.

NSString * programWorkingDirectory = [[[NSBundle mainBundle] resourcePath] retain];

NSString * commandPath = [[programWorkingDirectory stringByAppendingPathComponent: @"cdlabelgen"] retain];

(Ovviamente creiamole come variabili dell’istanza di NSObject <NSApplicationDelegate> di cui scegliamo un metodo in cui inserire il precedente codice).
Creiamo quindi una interfaccia per raccogliere le opzioni per la linea di comando ed un pulsante “Vai”, “Disegna”, “Go”, “Salva” o qualsiasi cosa gradiate. Colleghiamo il selettore di questo pulsante al metodo – (IBAction)save:(id)sender che avrete creato nel delegato dell’applicazione.

In questo metodo eseguiremo quanto necessario all’esecuzione di cdlabelgen e alla raccolta eventuale dei suoi frutti.

Un piccolo accenno su come costruire rapidamente una interfaccia per gli argomenti.
Create una istanza di oggetto NSUserDefaultController nello XIB; otterrete un istanza “Shared”; collegate i value dei widget al controller key “value” dell’istanza condivisa di User Defaults: non solo avrete un oggetto per rendere “attive” e visibili al programma le scelte effettuate, ma avrete anche una persistenza delle ultime configurazioni scelte, tutto in uno!

Solo per i tipi derivati da NSPopupMenu c’é una piccola eccezione: occorre in questo caso collegare il “Selected Index”: il value contenuto è per noi solo una label.
Per la costruzione della linea di comando, comprensiva delle opzioni, sarà ora sufficiente decodificare tutte le chiavi negli userDefaults elencandole in un array (è quanto vuole l’API di esecuzione di programmi esterni); ad esempio in questo modo:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];

if ( [[[NSUserDefaults standardUserDefaults] valueForKey:@"noTrayPlaque"] boolValue] == YES )

[args addObject:@"--no-tray-plaque"];

Forse non è elegante, ma questo metodo è il più rapido da realizzare senza dover progettare un complesso modello che tenga conto delle varie necessità o scomodare Core Data. Ricorsarsi di applicare prime il metodo synchronize sui defaults prima di iniziare:

[[NSUserDefaults standardUserDefaults] synchronize];

Per la parte operativa utilizziamo una istanza di NSTask per eseguire il comando esterno.
Per configurare l’esecuzione occorre il percorso del programma interprete.
Su Terminale quindi eseguiamo questo comando:

iMac:~ andrea$ which perl

/usr/bin/perl

Portiamo questo a conoscenza di una istanza NSTask:

NSTask * task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/perl";

task.currentDirectoryPath = programWorkingDirectory;

oppure (variante senza property):

[task setLaunchPath: @"/usr/bin/perl"];

[task setCurrentDirectoryPath: programWorkingDirectory];

così come per gli argomenti:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];
[args addObject: @"-o"];
[args addObject: filename];

Per ovviare i problemi di bufferizzazione di NSPipe, invece di “leggere” lo standard output di cdlabelgen gli passiamo sempre l’opzione “–output-file” con un nome di file precedentemente fornito dall’utente con un NSSavePanel.
Non rimane che lanciare il nostro programma:

task.arguments = args;
[task launch];

avremo il nostro file generato nel percorso e nome dati.
È opportuno dare un riscontro all’utente con una finestra di dialogo.

Per questo è sufficiente porre un risponditore alla notifica NSTaskDidTerminateNotification che possa presentare un semplice NSAlert. La notifica è lanciata da NSTask al termine del processo, e questo completa l’interfaccia utente.
Potrete aggiungere a piacimento tutto quello che ritenete utile ad una applicazione compiuta, ivi compresa una icona accattivante.

Ovviamente , se decidete di realizzare questo progetto e lo distribuite (anche tra amici), ricordatevi di inserire le credenziale degli sviluppatori di cdlabelgen in qualche parte dell’interfaccia, ossia Avinash Chopde <avinash@aczoom.com> www.aczoom.com e B. W. Fitzpatrick <fitz@red-bean.com>

Al prossimo esperimento.

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 3)

Fatto il vostro backup?

Allora siamo pronti a partire con lo sviluppo del cuore del nostro progetto: una procedura di creazione di una libreria indipendente e condivisa.

Da questo momento consideriamo la libreria del nostro utente (che è amministratore per ragioni che vedremo in seguito) la libreria master nella nostra architettura, che evidentemente è stata scelta in quanto ragionevolmente é quella che per ragioni anche storiche ha il peso maggiore di media condivisibili nel sistema.

Creiamo una nuova libreria nella Cartella condivisa (con procedura ormai nota se avete letto la documentazione consigliata).SegliLibreria

Apriamola. Configuriamola in modo che non esegua copia degli oggetti importanti (consolidamento) e non mantenga ordinata la libreria: non è il suo ruolo: questa sarà una libreria slave, non dovrebbe avere diritto di gestione (anche se vedremo possibile, entro certi limiti, utilizzando uno strumento di gestione che proporremo alla fine della trattazione).configurazione

Il problema (rilevato ma non risolto dal supporto Apple) sarà trovare il modo di rendere sincrone le due librerie. Vedremo di risolverlo.

Rimuoviamo ora la cartella Music nella nuova libreria (in iTunes Media); al suo posto ci poniamo (muovendola) la cartella Music della libreria master (è la cartella che contiene tutti i media da condividere).

Nella libreria master spoglia della cartella Music (in iTunes Media) creiamo un collegamento (semplicemente tenendo premuto alt+cmd mentre fate un drag&drop) della cartella Music ora mossa nella nuova posizione. Questo garantirà alla libreria master di mantenere la visibilità dei media a lei noti.stato_master

Come amministratore possiamo ora modificare i diritti della cartella Musica appena spostata consentendo la lettura a tutti gli utenti con cui volete condividere il suo contenuto; non autorizziamo la scrittura se vogliamo rendere la libreria puramente slave e non abbia quindi diritto di modificare i media.

Ora è il momento di riaprire la libreria master: tutto è al suo posto, giusto? Se qualcosa non è al posto giusto possiamo verificare la posizione della cartella Music nelle Preferenze ed eventualmente reimpostarla. Se iTunes pensa di dove iniziare la copia dei media potrebbe avviare tale procedura: bloccatela! Non è necessaria. Abbiamo giocato con spostamento e collegamento, andando oltre la procedura Apple che prevede necessita di spazio disco libero pari alla dimensione dei media da spostare. Se tutto è dunque a posto esportiamo la libreria master in formato xml.export

Ora riaprite la nuova libreria costituita in precedenza (slave): questa è vuota pur avendo collocati una cartella Music piena di materiale. Abbiamo detto già che la libreria è tale in quanto raccoglitore di metadati. Ora importate il file xml esportato precedentemente con la funzione “Importa playlist”.import

Chiaro no? L’importazione parla di playlist; Music è una playlist, e iTunes ci sta dicendo che l’importazione riguarderà solo musica.

Finita l’operazione ecco dunque che avrete due librerie identiche (solo nel contenuto migrato) ma che condividono anche fisicamente gli stessi media, ma solo per ciò che attiene alla musica. Per gli altri media la gestione risulterà indipendente. Potrete aggiungere libri, applicazioni o quanto altro alla nuova libreria nei modi di sempre: questo è dunque possibile. Ciò trova anche una soluzione alla gestione ad esempio delle app “legacy” per vecchi dispositivi (basta creare una libreria slave per gestire questi, pur condividendo altri media).


Ma adesso arriva il difficile: aggiungendo nuovi media musicali nella libreria master (che consolida) avremo nuovi oggetti nella cartella media condivisa, ma non li avremo come metadati nella libreria (o nelle librerie) slave (nel file .itl, per intenderci).

Ogni volta che si modifica la libreria master andrebbe aggiornata la slave aprendo iTunes su questa e aggiungendo o rimuovendo oggetti (alterare playlist, ecc).

È evidente come sia necessario automatizzare questa procedura, almeno per quanto riguarda l’aggiunta di nuovi media condivisi (le pllaylist sono raccolte soggettive che lasciamo ai singoli utenti).

Per risolvere questo problema ancora una colta ho scritto un po’ di codice AppleScript (ci ho preso gusto!).

Purtroppo iTunes non espone metodi per salvare tutte le informazioni in un colpo solo, ma solo un metodo add per aggiungere file a partire da un loro alias: questo mi sono fatto comunque bastare. Un unico script che esporti ed importi i metadati attraverso un file di interscambio (un semplice file contenente la lista dei nuovi file) tra le due istanze di libreria.

Andrà attivato prima sulla libreria sorgente (usando la playlist “Aggiunti di recente” quale fonte dell’elenco dei brani da migrare), poi su quella destinazione: questa procedura ci offre la soluzione cercata.

Possiamo eseguire esportazioni successive dalla libreria master ed importare il tutto nella libreria slave in un unico momento.  L’importazione finisce con la rimozione del file di interscambio, iniziando una nuova accumulazione.

Ma non dilunghiamoci in descrizioni ulteriori di implementazione; ecco di seguito il sorgente.

 

[codesyntax lang=”applescript”]

-- 
-- @author Andrea Tassotti


--
-- differenza tra due insiemi
--
on difference(set1, set2)
set differ to {}
repeat with o in set2
if set1 does not contain o then
set differ to differ & o
end if
end repeat
return differ
end difference

--
-- Esistenza file
--
on existsFile(migrationFile)
tell application "Finder"
if not (exists migrationFile) then
return false
else
return true
end if
end tell
end existsFile

--
-- Lettura lista migrazione
--
on readMigrationFile(migrationFile)
-- read
if existsFile(migrationFile) is true then
tell current application
set fileRef to open for access alias migrationFile
set lista to read alias migrationFile as list
close access fileRef
return lista
end tell
else
return false
end if
end readMigrationFile

--
-- Scrittura (overwrite) lista migrazione
--
on writeMigrationFile(migrationFile, lista)
-- write
try
tell current application
set fileRef to open for access alias migrationFile with write permission
write lista to fileRef as list
close access fileRef
end tell
on error
display alert "Errore scrittura:" & (migrationFile as text)
end try
end writeMigrationFile

--
-- Preleva alias da voci di playlist selezionata (no Libreria intera)
-- 
to itemsToMigrate()
tell application "iTunes"
set currentPlayList to view of window 1
set currentPlayListName to name of currentPlayList
if special kind of currentPlayList is none then
return location of every file track of currentPlayList
else
display alert "Possono essere esportate solo playlist effettive" message "Le playlist speciali (Music, Books, Podcasts, ecc) non sono contemplate" giving up after 2
return {}
end if
end tell
return {}
end itemsToMigrate

--
-- Corpo principale del programma
--
on run
set migrati to {}
set migrationFile to ((path to shared documents) as text) & "migration.itpl"
set migrati to readMigrationFile(migrationFile)

if migrati is not false then
-- - -- Verifica se master o slave
display alert "Controllo esistenza elementi in libreria. Questa operazione può durare molto. Attendere" giving up after 1
set primo to first item of migrati
tell application "iTunes"

set libreria to location of every file track of library playlist 1

if libreria does not contain primo then
-- Libreria Slave: importiamo
repeat with aFile in migrati
add aFile
end repeat
-- Rimuoviamo il file consumato
tell application "Finder"
if exists migrationFile then
delete migrationFile
end if
end tell
display alert "Fine importazione"
return
end if
end tell
end if
 

if migrati is false then
set migrati to {}
end if


-- Esportazione controllata
set daMigrare to itemsToMigrate()
if daMigrare is not {} then
-- Calcoliamo la differenza per evitare doppioni
-- poi aggiungiamo comunque quelli migrati per fare append del file
set lista to difference(migrati, daMigrare)
if lista is {} then
display alert "Contenuto migrato in precedenza"
else
writeMigrationFile(migrationFile, migrati & lista)
display alert "Esportazione lista migrazione terminata"
end if
end if
end run

[/codesyntax]

 

In quanto risultato delle nostre analisi, lo script lo rilasciamo liberamente, ma gradiremmo almeno un commento su questo blog se sarete tra quelli che lo copieranno ed utilizzeranno.

Buona condivisione!

Riferimenti

iTunes: How to share music between different user accounts on a single computer

iTunes for Mac: Moving your iTunes Media folder

How to move your iTunes library to a new computer

iTunes: How to re-create your iTunes library and playlists

2000px-Cisco_logo.svg

Uno sguardo a CDP

Ho recentemente ricercato soluzioni per reperire informazioni sulla topologia di una rete e tra le varie tecnologie che ho analizzato mi sono soffermato sulle possibilità offerte dal protocollo CDP di Cisco.

CDP è un protocollo proprietario Cisco che lavora a livello 2. Ogni dispositivo di rete conforme a questo protocollo (ossia quasi tutti i dispositivi Cisco) invia a tempo (di base 60 secondi) pacchetti conformi a questo protocollo ad un indirizzo di multicast definito. Tutti gli altri apparati nel medesimo dominio di broadcast raccolgono questi pacchetto/annuncio e memorizzano le informazioni ivi contenute in una memoria cache (cdp cache).

Le informazioni indicano versione di IOS, ruolo, porte, hostname, e altro ancora, costruendo per ciascun dispositivo una conoscenza su quale altro apparato ha nelle vicinanze (neighbours).

La cache è interrogabile via comandi IOS (show cdp…) oppure remotamente via SNMP.

Per costruire una topologia della rete (che vada anche oltre l’analisi di livello 2) occorre quindi interrogare un qualche oggetto (noto) capace di CDP e da questo ricorsivamente tutti gli oggetti confinanti rilevabili (non noti a priori). In questo modo una volta rilevato un router potremo anche superare la barriera del rilevamento a livello 2 (tipico di un nmap, ad esempio). [Cfr Rif. 1 pag 4]

Per eseguire queste interrogazioni da remoto occorre disporre di un altro protocollo, lo SNMP.

In particolare per interrogare un dispositivo Cisco con CDP attivo occorre compilare i seguenti MIBs:

  1. SNMPv2-SMI
  2. SNMPv2-TC
  3. SNMPv2-CONF
  4. SNMPv2-MIB
  5. CISCO-SMI
  6. IANAifType-MIB
  7. IF-MIB
  8. CISCO-TC
  9. INET-ADDRESS-MIB
  10. SNMP-FRAMEWORK-MIB
  11. RMON-MIB
  12. CISCO-VTP-MIB
  13. RFC1155-SMI
  14. RFC-1212
  15. SNMPv2-TC-v1
  16. CISCO-CDP-MIB

Disponibili sul sito Cisco.

Nello specifico potremmo interrogare i seguenti oggetti:

  • cdpGlobal
  • cdpInterface
  • cdpCache

Pur potendo eseguire il tutto a mano (o in maniera batch) mediante il comando snmpwalk (o altro strumento di navigazione tra rami SNMP) ho cercato una soluzione integrabile in una interfaccia web.

Così ho cercato e trovato un framework in PHP molto interessante che offre una soluzione per interrogare ricorsivamente dispositivi CDP (oltre a tutta un’altra serie di capacità che meritano di essere approfondite).

Si tratta del Framework OpenSolutions/OSS_SNMP. E’ estremamente estensibile e è già compatibile con tecnologie Cisco, HP, Foundry/Brocade, Asterisk, Extreme, MRV.

Richiede PHP >5.4.

Senza entrare in dettagli vi fornisco un programma demo che utilizza tale framework (è necessario che tutti i dispositivi di rete rispondano in SNMPv2 per la stessa community) .

Fornendo a linea di comando (PHP-CLI) l’indirizzo di un dispositivi noto interrogabile (switch, router, ecc) e la community restituisce (ovvero stampa a console) un array con la topologia individuata.

#! /usr/bin/php

<?php
// This is an example script for OSS_SNMP Cisco CDP MIBs
if( count( $argv ) != 3 )
{
echo "Missing argument\n";
echo "USAGE: <hostname/IP Address> <community>\n";

exit( 1 );
}

require_once( dirname( __FILE__ ) . '/../OSS_SNMP/SNMP.php' );

$router = new \OSS_SNMP\SNMP( $argv[1], $argv[2] );

// Neighbours del route dato
print_r( $router->useCisco_CDP()->neighbours(true) );

// Interroga ricorsivamente i router periferici
print_r( $router->useCisco_CDP()->crawl() );

echo "\n\n";
exit( 0 );

 

Vedremo in un prossimo articolo come consentire ad un server Linux di diventare un oggetto di rete che si annuncia via CDP e sia pertanto visibile come oggetto confinante ai dispositivi Cisco.

 

Riferimenti

1 “Cisco Discovery Protocol Configuration Guide, Cisco IOS Release 15M&T” 2014 (cdp-15-mt-book.pdf)

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 2)

Continuiamo l’analisi di iTunes per raggiungere il nostro obiettivo di condivisione.

Ripartiamo con alcune caratteristiche “fisiche” della libreria iTunes.

La libreria è composta di un file “.itl” e un insieme ordinato di cartelle.
Lo schema della cartella della libreria è il seguente:

  • Album Artwork
  • iTunes Library.xml
  • iTunes Library Extras.itdb
  • iTunes Library Genius.itdb
  • iTunes Library.itl
  • iTunes Media
  • Previous iTunes Libraries

In particolare la cartella iTunes Media contiene le seguenti cartelle (molte create dinamicamente all’aggiunta in libreria dei media per cui sono predisposte, così come verranno rimosse quando dalla libreria verrà eliminata l’ultima istanza di in certo media):

  • Automatically Add to iTunes.localized
  • Books
  • Music
  • Home Videos
  • iTunes U
  • Mobile Applications
  • Podcasts

Se la vostra libreria è vecchia come la mia, ha probabilmente ereditato la struttura gerarchica precedente: potete liberamente trasferire le cartelle nella posizione corretta (troverete probabilmente Books, Podcast e Home Videos in una cartella iTunes Music).

Mentre le finalità di ciascuna cartella sono abbastanza ovvie, meno noto è l’uso della cartella “Automatically Add to iTunes.localized”.

La cartella “Automatically Add to iTunes.localized” serve ad aggiungere alla libreria (all’avvio di iTunes) gli oggetti ivi eventualmente contenuti: ha due difetti: accetta solo file di media del tipo gestito  (no alias, no playlist in formato xml, testo, m3u, ecc) e copia l’oggetto nel ramo di iTunes Media anche se opzione consolidamento non è attiva. Questo impedisce a questa cartella di essere usata come luogo di scambio (o migrazione) tra librerie da sincronizzare.

ITunes consente di esportare la libreria in formato xml: è una operazione da menu, e si chiama appunto “Esporta Libreria”. Come sottolineato dalla documentazione Apple questa esportazione non è completa; aggiungiamo noi quanto non detto: il file non contempla ad esempio le app mobile, ma per noi è sufficiente riguardi la playlist Musica e le playlist utente derivanti.export

L’importazione sarebbe un mistero se non si tenesse in considerazione quanto abbiamo sottolineato in precedenza e quanto è osservabile interrogando iTunes via AppleScript: la Musica nella libreria è una playlist (particolare, ma pur sempre una playlist). Quindi la funzione “Importa Playlist” è la nostra funzione di importazione della libreria di musica. Per quanto accennato la funzione importerà anche tutte le playlist utente esportate col precedente metodo.import

Non considerate questa esportazione/importazione un sistema di backup completo per la vostra libreria: abbiamo già detto che non contiene tutte le informazioni.

Eppure Apple stessa la considera utile per un disaster recovery della vostra libreria: perché ?

La nascita stessa di una libreria in iTunes fonda le sue ragioni nella necessità di preservare quelle informazioni che non possono risiedere nel formato file del media (è ancora vero per alcuni formati). Tutto quanto è parte del formato file è recuperabile aggiungendo nuovamente il file alla libreria, ma il resto delle informazioni aggiunte grazie alla capacità della libreria non lo è: queste informazioni sono oggetto di esportazione nel formato xml della funzione Esporta Libreria, ecco perché è utile ancora oggi a recuperare una libreria corrotta.

Noi sfrutteremo questa funzione con ben altro scopo.


Quindi, prima di iniziare, affidatevi ad una copia aggiornata della vostra libreria in Time Machine, o (se proprio non potete) almeno affidatevi ad una copia su altro percorso del file Library.itl.

Ci leggiamo quando siete pronti !

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 1)

Torniamo a parlare di iTunes.

Uno degli aspetti più trascurati in molti software è la possibilità di condividere il proprio archivio tra gli utenti del sistema quando questo archivio abbia una naturale vocazione alla condivisione, anche e non solo per la necessità di tenere sotto controllo il peso degli oggetti di archivio che possono ripetersi tra più utenti.
Forse questa tendenza a trascurare la multi-utenza è dovuta alla ridotta necessità di questa in un mercato dominato da computer portatili utilizzati da un solo proprietario (personal computer, dunque). Il predominare poi dei dispositivi mobile (per loro natura “personal”) ha ulteriormente allontanato la multi-utenza tra le priorità degli sviluppatori software.

Peccano di mancanza di strumenti per la multi-utenza iTunes come gli altri software della famiglia i* (tutto il pacchetto iLife, per intenderci).

Ma stiamo parlando di OSX, un sistema che a differenza del MacOS nasce multiutente, e questa caratteristica ha una naturale collocazione nell’uso domestico e familiare di un iMac, ove ciascun membro può avere un profilo proprio ma che (per naturale appartenenza al medesimo gruppo umano) condividerà molto con gli altri utenti (pensiamo solo ai video o le foto delle gite familiari).

Eppure tutti questi programmi ignorano la loro possibile collocazione in ambito multiutente, lasciando a noi l’onere di individuare una soluzione.

Certo una qualche soluzione a volte è fornita dal supporto Apple (vedi https://support.apple.com/it-it/HT204488), ma come si può leggere non tutto è agevole, e nemmeno tutto è esplicitato.
Focalizzandoci su iTunes (dovremmo affrontare analoghe questioni anche per iPhoto o iMovie) vedremo di trovare alcune soluzioni più “complete”.
iTunes nasce in epoca MacOS ed era dedicato alla sola riproduzione audio. Si è evoluto gradualmente: video, podcast e app mobile sono arrivare col tempo e ben dopo la migrazione a OSX. Librerie ereditate da un’epoca pre iOS (come nel caso della mia) contengono i file multimediali in una cartella iTunes Music. Abbastanza significativo, no?
Gli aggiornamenti dell’applicativo hanno modificato la base dati, ma non la struttura di directory della mia libreria.
Ma il tempo è passato, e la struttura prevista dalle nuove versioni prevede ora tutta un’altra gerarchia, come si può osservare in una libreria creata ex-novo con una nuova versione (vedi procedura https://support.apple.com/it-it/HT201596).

In questa serie di articoli affronteremo principalmente il problema della condivisione della libreria iTunes a vari livelli, ma forniremo anche alcune esperienze sul significato di alcune componenti e sulle possibili configurazioni della libreria, nonché qualche suggerimento sparso. Sia ben chiaro: la “Condivisione in famiglia” non ha nulla a che vedere con il problema affrontato: questa condivisione è via rete locale, quindi l’iPod di vostra moglie non potrà essere sincronizzato con i brani che voi avete acquistato sullo store, pertanto non potrà sentirli durante la sua sessione di palestra (a meno che non abbiate la palestra in casa).
Purtroppo quello che vorremmo raggiungere, ossia la a piena automazione di tutte le procedure, non è ottenibile (come evidente dal documento del supporto Apple https://support.apple.com/it-it/HT204488) anche e non solo in quanto iTunes (come altri software) può aprire librerie differenti solo al suo avvio (vedi procedura https://support.apple.com/it-it/HT201596) e non è capace di fare questo nemmeno via Apple Script (esistono delle possibilità alterando un file di configurazione attraverso il comando defaults, ma non è una scelta condivisibile: troppo suscettibile di variazioni con gli aggiornamenti software). Comunque e una possibilità che completerebbe l’automazione cercata.
Cercheremo comunque di avvicinarci offrendo delle soluzioni di automazione che almeno supportino i processi eventualmente necessari a raggiungere lo scopo principale di realizzare una condivisione multiutente.

Chiariamo subito: una totale condivisione della libreria è possibile in un modo semplice ed immediato ma non utile. Si può infatti completamente condividere una libreria ponendola in area condivisa (Shared folder.localized) tra più utenti e dando i giusti permessi (fatto questo abbastanza ovvio).
Questa però non è (dicevamo) una soluzione utile, in quanto la libreria è contenitore di tutti i media gestibili con iTunes e ciascun utente vorrebbe una qualche autonomia di configurazione, lasciando che solo quanto è realmente condivisibile lo sia mentre il resto possa restare autonomo e privato. Se consideriamo che il file “.itl” gestisce il parco musica, così come le app mobile, libri e playlist e altro ancora è di tutta evidenza come ciascun utente voglia poter mantenere una certa autonomia di alcuni di questi media.

Cercheremo di mostrare come consentire una condivisione completa di musica e al contempo una piena indipendenza di app o quanto altro.
Come si evince da documenti Apple, si possono avere due librerie distinte che condividono gli stessi media, ma non si ha un modo per sincronizzarle.

Questo in quanto le due librerie hanno file “.itl” indipendenti: in questi è presente l’insieme delle informazioni che costituiscono la libreria, e non esistono strumenti di esportazione o importazione parziali.

Cercheremo di superare questo ostacolo. Inoltre daremo una soluzione alla individualità di gestione di altre risorse come le app mobile (la frattura tra versioni di iOS e dispositivi di vecchia generazione impone di avere un parco software non aggiornatissimo in una libreria indipendente diciamo “legacy”).

Iniziamo dunque con una panoramica degli strumenti disponibili, ma questo lo faremo in un altro articolo: ci siamo dilungati troppo.

20150212-215729.jpg

Salvataggio e ripristino di una VM libvirt (parte III)

Concludiamo questa serie di articoli fornendo il codice per il ripristino e qualche suggerimento che renda la nostra implementazione di un sistema di backup pratica e portabile.

Iniziamo con la procedura di ripristino.
Dato il nome del file di archivio, iniziamo ad espanderlo (con utenza amministrativa): questo collocherà i files dei dischi virtuali nel percorso originale (ripristiniamo il percorso assoluto collocando la radice delle operazioni di tar con l’opzione -C) ed il file XML di definizione della macchina virtuale in /tmp (per costruzione del backup).

tar xz -C / -f "$1"

Il nome del file di descrizione è il nome della macchina virtuale (ancora per costruzione del backup) ed è parte del nome dell’archivio: quindi estraiamo da questo il nome della macchina virtuale che stiamo ripristinando e con questo ricaviamo il nome del file di descrizione:

fname="$(basename $1)"
vmname=${fname%%.tar.gz}

Quindi se confermiamo l’esistenza del file di descrizione possiamo importare la macchina inviando l’XML allo stdout della console amministrativa di libvirt (virsh):

	if [ -f /tmp/${fname}.xml ]
	then
		echo define /tmp/${name%%.tar.gz}.xml | virsh
        fi

Questo rende nuovamente disponibile la macchina virtuale.

Come vi avevo promesso, ora qualche suggerimento per rendere il tutto un prodotto.

Dapprima racchiudiamo in funzioni la procedura di salvataggio e ripristino: chiamiamole backup() e restore().
Il codice presentato in queste puntate non necessita di modifiche, ed i parametri che dovevano essere per un ipotetico comando saranno ora per le funzioni: nome di macchina per backup(), nome file archivio per restore().

A questo punto creiamo un file per la nostra applicazione; diciamo libvirt-utils.sh.
Poniamo in questo le nostre funzioni e aggiungiamo il seguente codice:

case $(basename $0) in
virbackup)
  backup "$1"
  ;;
virestore)
  restore "$1"
  ;;
*)
  [ ! -f /sbin/virbackup ] && cp $0 /sbin/virbackup
  [ ! -f /sbin/virestore ] && ln /sbin/virbackup /sbin/virestore 
  ;;
esac

Lanciate almeno una volta lo script senza parametri come amministratore: avrete installati due nuovi programmi: virbackup e virestore ! Lo script originale potrà anche essere rimosso o meglio collocato nella vostra “cassetta degli attrezzi”.

Buona virtualizzazione e buon salvataggio e ripristino a tutti !

20150116-221004.jpg

Salvataggio e ripristino di una VM libvirt (parte II)

Torniamo per completare quanto lasciato in sospeso nella precedente puntata.

Cominciamo con la definizione del metodo per eseguire il backup (seguirà quello per il ripristino) di una specifica VM individuata per id di dominio. Collocheremo questo (e quello per il ripristino) in una funzione bash che invocheremo nel programma principale che costruiremo attorno a questo nucleo applicando una qualche interfaccia per avere lista di domini su cui operare e così invocare ciclicamente la funzione.

Per operare il backup di una VM dobbiamo considerare la sua definizione e ogni immagine disco: nel caso più semplice avremo almeno due files: il file di definizione e una immagine disco. Ipotizzando di voler usare tar quale sistema di archiviazione è opportuno costruire un file con elencate tutte queste sorgenti (files) da sottoporre a l’opzione -T. Questo metodo è il più efficiente nell’ipotesi di utilizzo del comando tar, in quanto operazioni di aggiunta successiva di file in archivio su file compresso non sono consentite direttamente e comunque queste operazioni necessitano la rilettura dell’intero archivio per il posizionamento in coda dei nuovi elementi. Intollerabile.

Quindi cominciano con il produrre il file di definizione di un dominio:

virsh dumpxml "$1" > /tmp/${1}.xml

Nell’ipotesi di collocare questo in una funzione, $1 rappresenta l’unico argomento fornito a questa. Adottiamo /tmp come storage per pulizia.

Aggiungiamo questo file al costruendo file-elenco per tar:

echo "/tmp/${1}.xml" >/tmp/backup.files

Sfruttiamo il file di definizione del dominio per ritrovare i percorsi e nomi dei file di immagini disco appartenenti al dominio, e aggiungiamo questi nel file-elenco:

	grep 'source file=' /tmp/${1}.xml | egrep 'img|vdi|vmkd|qcow|qcow2' | cut -d\' -f2 >>/tmp/backup.files

A questo punto non ci resta che procedere ad archiviare il tutto sfruttando il nome del dominio come nome dell’archivio e soprattutto usando il file-elenco preparato prima:

	tar cz -f ${1}.tar.gz -T /tmp/backup.files

Non ci resta che pulire i files temporanei:

	rm /tmp/${1}.xml /tmp/backup.files

Ed il backup è concluso.

Nella prossima parte descriveremo il processo inverso e qualche suggerimento per costruire un buon programma principale per eseguire il tutto nel mondo reale.