Raccolta di howto, informazioni, consigli, trucchi e documentazione di varia utilità per ricordare tante cose utili

martedì 24 novembre 2009

Usare cURL in in programma multithread

cURL è una libreria quasi del tutto thread-safe. Tuttavia è indispensabile prestare attenzione ad alcune cose nello svilupo pena frequenti ed inspiegabili crash dell'applicazione. Le indicazione seguenti sono date nell'ipotesi di lavorare con le curl_easy, tuttavia sono più generalmente valide.
  1. Prima di tutto è necessario inizializzare la libreria con la funzione curl_global_init(CURL_GLOBAL_ALL) prima della creazione di qualsiasi thread del programma, indipendentemente dal fatto che in uno specifico thread vengano o non vengano utilizzate funzioni cURL.
  2. Gli handle alla libreria CURL *cl = curl_easy_init(); vanno tenuti locali al singolo thread, non vanno mai dichiarati globali o utilizzati da più thread.
  3. Prima di eseguire ogni transazione (con curl_easy_perform) e necessario impostare l'opzione CURLOPT_NOSIGNAL ad 1 con la funzione curl_easy_setopt(cl, CURLOPT_NOSIGNAL, 1). Questa operazione comporta una piccola controindicazione: potrebbero non venire più gestiti i timeout di connessione al DNS nella fase di risoluzione dei nomi. Sono possibili due soluzioni a questo problema: la prima e risolvere i nomi con una chiamata esterna alla libreria ed inserire nell'url esclusivamente indirizzi numerici; la seconda e compilare la libreria con il supporto c-ares che assicura una corretta gestione dei timeout.
  4. Altri problemi possono insorgere nell'uso di comunicazioni cifrate con SSL/TLS (e.g. https). Le librerie openSSL e GnuTLS richiedeno delle specifiche attenzioni per una corretta gestione del multithreading mentre la libreria NSS dovrebbe essere nativamente thread-safe. Per verificare le richieste delle due librerie usate per la cifrartura fare riferimento a questi link: OpenSSL, GnuTLS

Un bel tacer non fu mai scritto

Chi ha detto/scritto per primo questa frase?
Sembra che sia stato Claudio Monteverdi, compositore italiano vissuto a cavallo del XVI e XVII secolo ne "Il ritorno di Ulisse in patria" composta dall'autore nel 1640 poco prima della sua morte.

mercoledì 18 novembre 2009

Introduzione a cURL

La breria cURL ci permette di effettuare in modo semplice connessioni HTTP, FTP ed altro da programmi scritti in C. La libreria insieme alla documentazione completa è possibile trovarla sul sito ufficiale: http://curl.haxx.se/. Il modo più semplice per iniziare ad utilizzare la libreria è di usale la cosiddetta "easy interface" un set ridotto di API per un uso semplice e veloce. Tutto l'esempio descritto di seguito fa riferimento a un sistema Linux, sotto Windows possono essere richieste modifiche più o meno grandi a seconda del compilatore e dell'ambiente di sviluppo utilizzato.
Primo passo è di inserire nel sorgente l'opportuno include:
#include
si deve definire un buffer per la ricezione dei dati di ritorno
char buf[1024];
si deve poi inizializzare la libreria con la funzione
CURL *cl = curl_easy_init();
if(!cl) {
    cout<<"Fallito l'uso di CURL"<
    return 0;

}
Si devono poi impostare le opzioni che descrivono il tipo di trasferimento
Impostiamo l'opzione verbose per avere nello stdout il log di quello che sta facendo la libreria, utile sostanzialmente in fase di debug
curl_easy_setopt(cl, CURLOPT_VERBOSE, 1);
Impostiamo l'url al quale effettuare la connessione
curl_easy_setopt(cl, CURLOPT_URL, "http://192.168.0.10/");
La parte più complessa e come farci restituire il risultato della pagina. Dobbiamo definire una funzione di callback che la libreria chiama quando riceve dei dati di ritorno
curl_easy_setopt(cl, CURLOPT_WRITEFUNCTION, curlwf);
e dobbiamo indicare alla libreria dove posizionare i dati ricevuti in attesa di elaborazione
curl_easy_setopt(cl, CURLOPT_WRITEDATA, buf);
Dopo aver definito tutte le impostazioni si effettua la chiamata voluta
curl_easy_perform(cl);
ed al termine si liberano le risorse allocate chiudendo la connessione
curl_easy_cleanup(cl);

La funzione di callback da passare alla cURL deve seguire il seguente prototipo
size_t curlwf (void *ptr, size_t size, size_t nmemb, void *stream);
i dati ricevuti sono all'indirizzo puntato da *ptr ed hanno una dimensione di size*nmemb; stream contiene il puntatore definito con l'opzione CURLOPT_WRITEDATA (nell'esempio specifico buf). Da notare che i dati ricevuti e posti in *ptr non sono null terminated, se si vogliono utilizzare come stringa è necessario aggiungere lo zero al fondo. Un esempio di funzione di callback può essere il seguente:
size_t curlwf (void *ptr, size_t size, size_t nmemb, void *stream) {
    size_t fulllen = size * nmemb;
    if (fulllen > 1000) fulllen=1000;
    memcpy(stream, ptr, fulllen);
    char *x=(char *)stream;
    x[fulllen]=0;
    return size*nmemb;
}
Infine bisogna ricordarsi di aggiungere per il linking la libreria libcurl (opzione -lcurl del compilatore/linker)

Il sorgente completo del programma di esempio è
#include

size_t curlwf (void *ptr, size_t size, size_t nmemb, void *stream);
int main()
{

    char buf[1024];
    CURL *cl = curl_easy_init();
    if(!cl) {
        cout<<"Fallito l'uso di CURL"<
        return 0;
    }
    curl_easy_setopt(cl, CURLOPT_VERBOSE, 1);
    curl_easy_setopt(cl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(cl, CURLOPT_URL, "http://10.39.125.75/");
    curl_easy_setopt(cl, CURLOPT_WRITEFUNCTION, curlwf);
    curl_easy_setopt(cl, CURLOPT_WRITEDATA, buf);

    curl_easy_perform(cl);

    curl_easy_cleanup(cl);

    cout<<

    cout << "Finito con CURL!" << endl;
}


size_t curlwf (void *ptr, size_t size, size_t nmemb, void *stream) {
    size_t fulllen = size * nmemb;
    if (fulllen > 1000) fulllen=1000;
    memcpy(stream, ptr, fulllen);
    char *x=(char *)stream;
    x[fulllen]=0;
    return size*nmemb;
}

martedì 17 novembre 2009

Eseguire un thread POSIX da C++

Per eseguire un thread POSIX dobbiamo chiamare la funzione
pthread_create(pthread_t *thread, const pthread_attr *attr, void *(*start_routine)(void *), void *arg)
Il problema nell'utilizzare questa cfunzione C in un programma C++ e che la funzione che deve essere eseguita dal nuovo thread
void * start_routine(void *arg)
deve essere una funzione C standard e non può dunque essere un metodo di una classe.
Per poter eseguire un metodo di uno specifico oggetto come nuovo thread, cosa che molto presumibilmente si vuole fare se si è deciso di utilizzare il C++, si deve utilizzare un piccolo trucco: si deve utilizzare una funzione C che richiama il metodo voluto.
In particolare supponiamo di voler richiamare il metodo Start dell'oggetto obj appartenete alla classe myClass ; si deve prima di tutto definire la funzione incaricata di avviare il thread
void * start_routine(void *p)
{
    myClass *pObj = (myClass *)p;
    pObj->Start();
    delete pObj;

    pthread_exit(NULL);
}
Nella funzione principale del programma (o dovunque si voglia far partire il nuovo thread) si deve inserire il seguente codice:
pthread_t pTh;
myClass *obj = new myClass();
obj->setParam(a, b, c);
pthread_create(&pTh, NULL, start_routine, (void *)obj);
Il metodo setParam può ovviamente essere un qualsiasi metodo utile per inizializzare lo stato dell'oggetto. La chiamata a delete dentro la start_routine è del tutto opzionale e serve esclusivamente se si vuole distruggere l'oggetto al terminare del thread.

lunedì 16 novembre 2009

Checksum Offloading

Grazie all'uso delle moderne schede di rete i sistemi operativi moderni possono delegare il calcolo delle checksum UDP/TCP direttamente al chipset della scheda sgravando parzialmente il lavoro della CPU. Tuttavia almeno due inconvenienti possono capitare: il primo (che può capitare solo alle persone più tecnice e che quindi dovrebbero conoscere problema e risposta) e che andando a effettuare uno sniff dei pacchetti di rete in uscita si rileva una "cheksum error", errore per altro non più presente sulla macchina di destinazione.
Il secondo e che in taluni casi kernel e driver possono essere ingannati dalle schede e cercare di utilizzare il "checksum offloading" anche con schede di rete non in grado di supportarlo con conseguenti rallentamenti e malfunzionamenti di rete

Sotto Linux il "checksum offloading" è presente in tutti i kernel 2.6 a seconda del driver della scheda di rete utilizzato, è tuttavia possibile disabilitarlo con il comando
ethtool -K eth0 rx on tx off

Sotto Windows questa caratteristica esiste a partire da XP/server 2003. Il "checksum offloading" è abilitato/disabilitato a livello di driver, per tanto in caso di malfunzionamenti è opportuno cercare un driver più aggiornato o verificare se il diver supporta l'abilitazione/disabilitazione della caratteristica.
Per fare questo occorre andare nelle proprietà della scheda di rete, premere il pulsante "Configura" accanto al nome della scheda, andare nel tab "Avanzate" e cercare un proprietà con un nome del tipo del tipo "UDP/TCP checksum offload" oppure del tipo "TCP offload".

E opportuno disabilitare queste opzioni solo in caso di effettivi problemi, altrimenti il risultato è esclusivamente un appesantimento del carico della CPU con conseguente rallentamento del sistema

mercoledì 4 novembre 2009

Ripristino del MBR e del Boot Sector di Windows

Il ripristino del Master Boot Record e del Boot Sector di un'installazione di Windows può essere effettuato mediante il comando bootrec.exe.
Tale comando è presente nel DVD di installazione originale di Windows (nella cartella \windows\system32\) ed è raggiungibile effettuando il boot dal DVD scegliendo "Ripristina il computer" e nella finestra delle opzioni di ripristino del sistema scegliendo "Prompt dei comandi".

Una spiegazione dell'uso di questo comando la s può trovare nella knowledge base di Microsoft Qui

Avviare Linux dal boot manager di Windows Vista

Solitamente quando sullo stesso PC si vuole installare sia Windows che Linux si usa GRUB per decidere al boot quale sistema avviare sulla macchina. Può tuttavia in alcune situazioni risultare utile utilizzare il boot manager di Windows per avviare entrambi i sistemi operativi (per esempio in presenza di alcuni software di sicurezza che hanno il brutto vizio di riscrivere il MBR se non trovano quello originale di Windows). Per raggiungere questo risultato bisogna procedere comedescritto di seguito.
L'esempio si basa sull'ipotesi di un di un disco sata con una partizione primaria dove è installato Windows e tre estese per Linux e lo swap e per condividere i dati tra i duo S.O. Ovvero le partizioni seguono il seguente schema
/dev/sda1     Windows Vista NTFS
/dev/sda5     Linux ext3
/dev/sda6     swap linux
/dev/sda7     FAT32
Per prima cosa dobbiamo installare il GRUB non più nel MBR (in quanto windows lo sovrascrive con il suo) ma direttamente nella partizione dove è installato Linux. Dopo aver avviato Linux (eventualmente tramite un liveCD se nessun boot manager è funzionante) avviare grub e dare i seguenti comandi
grub> root(hd0,4)
grub> setup(hd0,4)
grub> quit
Successivamente per poter istruire correttamente il Windows Boot Manager dobbiamo effettuare una copia del boot sector di Linux (sempre rimanendo in Linux) con il comando
dd if=/dev/sda5 of=/tmp/linux.bin bs=512 count=1
dove /tmp è una cartella che dovrà essere accessibile anche a Windows, nell'esempio è il mount point di /dev/sda7 ma potrebbe essere un qualunque dispositivo rimovibile formattato con FAT.

Aquesto punto possiamo passare a Windows, eventualmente installandolo se non lo abbiamo ancora fatto, e comunque ripristinando nel MBR il codice del boot manager di Vista. Tutte le successive operazioni elencate dovranno essere eseguite da una finestra di comandi con i privilegi amministrativi.
Per prima cosa copiare il file linux.bin precedentemente creato nella radice della partizione attiva; solitamente è il disco C: ma se non si è sicuri utilizzare il comando diskmgmt.msc per verificarlo.
Usare il comando bcdedit.exe per aggiungere la "entry" di GRUB nel boot manager di Vista con la seguente sintassi
bcdedit /create /d "GRUB" /application BOOTSECTOR
questo comando ritornerà un id del tipo {25ac1879-54cf-04bd-dfc73e60be72} che di seguito chiamerò esclusivamente {ID}. Si dovrà quindi specificare quale device contiene la copia del boot sector ed in quale file con i comandi:
bcdedit /set {ID} device boot
bcdedit /set {ID} PATH \linux.bin
Alla fine dobbiamo dire al boot manager in quale ordine proporci le scelte e per quanto tempo con i comandi
bcdedit /displayorder {ID} /addlast
bcdedit /timeout 10
Al reboot successivvo apparirà la schermata del Windows Boot Manager dando la scelta se avviare Windows o GRUB (dal quale sarà poi possibile avviare Linux)

P.S.
Avendone la possibilità è tuttavia più indicato e più semplice installare EasyBCD che permette di configurare il boot manager di Vista in maniera più semplice e intuitiva

martedì 3 novembre 2009

Il comando qlist di Gentoo

Il comando qlist delle distribuzioni Linux Gentoo di base permette di visualizzare i files appartenenti ad un determinato package, tuttavia ha motenzialità molto maggiori. Per utilizzare qlist è necessario installare il package app-portage/portage-utils.
Come accennato l'uso più banale di qlist è ottenere la lista dei files presenti in un package, ad esempio con il comando:
qlist app-portage/portage-utils 
o semplicemente
qlist portage-utils

Per conoscere tutti i packages installati nella categoria app-admin si può dare il comando:
qlist -I app-admin/*

Se invece voglio sapere tutti i pacchetti installati che hanno a che fare con il gcc si può dare il comando
qlist -I gcc

La lista dei pacchetti ottenuta da qlist può essere utilizzata come input di altri comandi; ad esempio volendo effettuare il re-emerge di tutti i pacchetti della categoria media-video posso usare il comando:
emerge -va `qlist -C -I media-video/*`