Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

domenica 9 ottobre 2016

Il grande lighttpd
come scrivere un modulo lighttpd in C - pt.2

Drugo: Sai, questo... questo è un caso molto, molto complicato, Maude. Un sacco di input e di output. Sai, fortunatamente io rispetto un regime di droghe piuttosto rigido per mantenere la mente, diciamo, flessibile.
Allora: la buona notizia è che per scrivere un modulo lighttpd non è necessario seguire lo stesso regime del Drugo... quella cattiva è che ci vuole un certo impegno, ma sarà molto più semplice dopo aver letto questo post (e il precedente, ovviamente... non l'avete ancora letto? Ma questa è la seconda parte!).
faccia da "Ora so come si scrive un modulo lighttpd!"
Allora, dove eravamo rimasti? Abbiamo installato lighttpd sul nostro sistema e abbiamo creato un ambiente di sviluppo scaricando e decomprimendo il tar di installazione trovato sul sito ufficiale di lighttpd (ricordarsi di creare l'ambiente usando la stessa versione di quella già installata sul sistema). La compilazione e installazione della versione scaricata ha dato (spero) buon esito, quindi siamo pronti per scrivere il nostro modulo.

Per prima cosa scegliamo un nome e un azione... sarà un classico: lo chiameremo mod_helloworld e il modulo scriverà nel browser la frase "Hello, world!" (molto, ma molto, originale!).

Apriamo un terminale Linux, entriamo nella root-directory dell'ambiente di sviluppo ed eseguiamo i seguenti comandi (che ho numerato per descriverli uno a uno):
 1. sudo gedit /etc/lighttpd/lighttpd.conf
 2. gedit src/Makefile.am     
 3. cp src/mod_skeleton.c src/mod_helloworld.c 
 4. gedit src/mod_helloworld.c    
 5. ./autogen.sh
 6. ./configure
 7. make
 8. sudo make install
 9. sudo cp /usr/local/lib/mod_helloworld.so /usr/lib/lighttpd
10. sudo /etc/init.d/lighttpd restart
Allora: con 1. editiamo (con gedit, pluma, vim, geany... o quello che preferite) il file di configurazione di lighttpd per aggiungere alla lista dei moduli installati il nostro nuovo modulo: cercate la lista "server.modules" e inserite, dopo l'ultimo modulo listato, il nuovo. Fatto ciò l'aspetto della lista sarà del tipo:
server.modules = (
    "mod_access",
    "mod_alias",
    "mod_compress",
    "mod_redirect",
    "mod_helloworld",
)
Con 2. editiamo il file Makefile.am del package. Rispetto al package originale stiamo aggiungendo un modulo, quindi bisogna cercare l'ultimo modulo standard presente (normalmente è mod_accesslog). Troveremo quattro linee così:
lib_LTLIBRARIES += mod_accesslog.la
mod_accesslog_la_SOURCES = mod_accesslog.c
mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_accesslog_la_LIBADD = $(common_libadd) 
Copiamo queste linee e le replichiamo immediatamente sotto, sostituendo nelle NUOVE quattro linee mod_accesslog con mod_helloworld. Salviamo e usciamo: ora il nostro Makefile.am è pronto per gestire anche il nuovo modulo. Le linee aggiunte saranno queste:
lib_LTLIBRARIES += mod_helloworld.la
mod_helloworld_la_SOURCES = mod_helloworld.c
mod_helloworld_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_helloworld_la_LIBADD = $(common_libadd)
Con 3. copiamo il file mod_skeleton.c in un nuovo file che chiameremo mod_helloworld.c: mod_skeleton.c è un template file contenente lo scheletro base di ogni modulo: questo file ce lo mettono gentilmente a disposizione gli ottimi sviluppatori di lighttpd per facilitarci la scrittura di nuovi moduli.

Con 4. editiamo mod_helloworld.c e, con un comando di sostituzione globale, cambiamo tutte le ricorrenze della parola skeleton in helloworld. Per il momento salviamo così il file e usciamo dall'editor.

Con la sequenza 5.6.7.8. (vista già nel post precedente, ricordate?) compiliamo e installiamo il nuovo modulo. È possibile che nella fase 6. (quella di configurazione) il processo vi dica che non trova alcune librerie: normalmente l'errore si risolve usando:
sudo apt-get install libpcre3-dev libbz2-dev
e ripetendo, poi, il punto 6.

Con 9. copiamo il nuovo modulo generato (che ha la forma di una libreria dinamica, una .so) nella directory di sistema che contiene i moduli lighttpd.

Con 10., finalmente, riavviamo lighttpd e, se non ci appare nessun errore il nostro Web Server è già pronto per usare il nuovo modulo (che però, per il momento, è semivuoto: come detto sopra è solo lo scheletro di un modulo).

Cosa ci manca a questo punto? Beh, ovviamente, dobbiamo aggiungere un po' di codice nel nostro modulo per fargli fare qualcosa (nel nostro caso mostrarci un bel "Hello, world!"). Ma questo lo vedremo nella prossima puntata, e, come sempre, non trattenete il respiro nell'attesa...

Ciao e al prossimo post!

sabato 17 settembre 2016

Il grande lighttpd
come scrivere un modulo lighttpd in C - pt.1

Poliziotto: E nella valigetta?
Drugo: Oh, beh, documenti, solo documenti. Già, solo i miei documenti. Documenti di lavoro.
Poliziotto: Che lavoro fa?
Drugo: Sono disoccupato.
Ecco, a meno che non vi interessi fare lo stesso lavoro del mitico Drugo (The Dude), è una buona idea disporre di un know-how di valore. Questo è un post ad alto valore aggiunto, e lo è semplicemente considerando la legge della domanda e dell'offerta: tratteremo un argomento di cui non è disponibile una grande base di conoscenza, quindi è un post di valore.
...vorrei scrivere un modulo, ma sono un po' stanco...
Qui avevamo già visto tempo fa (e vi invito a rileggerlo) come scrivere un modulo elementare per Apache. Apache è un Web Server super diffuso (largamente il più diffuso) e ha una grande base di sviluppatori interni (la Apache Software Foundation) ed esterni (professionisti e appassionati che scrivono i più svariati tipi di moduli). Grazie a ciò si trova in rete una grande quantità di documentazione e guide, sia ufficiali (di nuovo la Apache Software Foundation) che non (siti tecnici, blogger come il sottoscritto, ecc.). Scrivere un modulo Apache non è facilissimo, ma, con tutto il supporto disponibile, non è una missione impossibile.

E veniamo a lighttpd: è un Web Server diffuso (ma molto meno di Apache) e con caratteristiche tecniche di prima classe: ha prestazioni paragonabili ad Apache ma è enormemente più leggero: poco carico di CPU, poca memoria, ecc. Praticamente è un must per sistemi embedded o, più in generale, per sistemi dove sono necessarie alte prestazioni usando poche risorse. Sfortunatamente lighttpd non ha dietro una grande organizzazione di sviluppo, e, proporzionalmente alla minore diffusione, non ha neanche un grande esercito di professionisti e appassionati che ci lavorano. Grazie a ciò NON si trova in rete una grande quantità di documentazione e guide, sia ufficiali che non. Scrivere un modulo lighttpd non è facilissimo, e, con il poco supporto disponibile, è una missione complicata.

Nello specifico: lighttpd è un gran prodotto scritto e mantenuto da una ristretta comunità di eccellenti programmatori, ma, non avendo dietro una grande organizzazione, si perde un po' nei dettagli: con più documentazione e guide ufficiali si aumenterebbe la diffusione, il che aumenterebbe anche la comunità di utilizzatori/sviluppatori (e quindi la documentazione non ufficiale), e, alla fin fine, aumenterebbe il successo del prodotto. Ma questo non succede e quindi siamo davanti a un caso classico di sindrome del cane che si morde la coda.  

lighttpd è in continua evoluzione, con prestazioni e affidabilità in crescendo, ma, per la carenza nei dettagli citata prima, può anche succedere che, tra una release e l'altra, ci si dimentichi della retro-compatibilità e si introducano delle modifiche alla API di programmazione che invalidano molti dei moduli (faticosamente) scritti dagli sviluppatori esterni (è successo anche a me con un modulo che ho scritto per la rel.1.4.33 e che con la rel.1.4.36 ha smesso di funzionare!).

Va bene, bando alle ciance: scriveremo un modulo elementare per lighttpd, che sarà una buona base per la scrittura di moduli più complessi (su cui vi fornirò alcune dritte in post futuri). La prima attività è, ovviamente, installare lighttpd sul PC (e, se anteriormente installato, rimuovere prima Apache). Cercate, con il nostro amico Google, una delle molte guide (ad esempio questa) per trasformare un PC in un server LLMP (o WLMP) (non vi descrivo io la procedura per non dilungarmi troppo, ma, vi garantisco, è abbastanza semplice). Ovviamente a fine procedura verificate se il Web Server funziona correttamente (di solito le guide vi dicono come farlo). 

Poi bisogna installare il necessario per sviluppare il nostro modulo. Le istruzioni che seguono sono (ovviamente) per Linux. Per altri sistemi della famiglia UNIX (BSD, OS X, ecc.) il procedimento si adatta intuitivamente, mentre, per quell'altro sistema che non voglio neanche nominare (comincia per W...), mi dispiace ma vi dovrete arrangiare da soli (se lo conosci lo eviti, ed io, vi assicuro, lo conosco bene).

Allora: scaricatevi dal sito ufficiale di lighttpd il tar di sviluppo corrispondente alla versione che avete appena installato sul sistema (sono disponibili anche i tar di versioni precedenti all'ultima). Questo tar serve per creare sul sistema il giusto ambiente di sviluppo e installazione di nuovi moduli. Decomprimete (dove volete) il tar, entrate nella directory creata e provate a compilare e installare la release (lo so, la stessa versione l'avete appena installata sul sistema, ma questo passo serve a verificare se il sistema di sviluppo funziona correttamente). Supponendo di usare la versione 1.4.33 scriveremo:
    tar zxvf lighttpd-1.4.33.tar.gz
    cd lighttpd-1.4.33
    ./autogen.sh
    ./configure
    make
    sudo make install
    sudo /etc/init.d/lighttpd restart
Verificate se il Web Server funziona ancora (nella stessa maniera usata per la prima installazione) e, se tutto è OK, siamo pronti per scrivere/compilare/installare il nostro nuovo modulo. Ma questo lo vedremo nella prossima puntata...

Ciao e al prossimo post!

martedì 23 agosto 2016

UDPhunter
come scrivere un UDP Client in C - pt.2

Siamo alla fine di Agosto e il ritorno dalle vacanze è sempre duro. Quindi, per risparmiare tempo e fatica cercherò di riciclare parte del testo di un mio vecchio post ben collegato a questo. Del resto questa è la seconda parte (il Client) dei post su UDP e, quindi, la seconda parte dei miei vecchi post su TCP cade a fagiolo. Il codice è, ovviamente, tutto nuovo.
oggi parleremo degli UDP client...
Come si intuisce dal titolo questa volta è il turno del UDP Client. Spero che tutti sappiate cos'è, se no vi consiglio una utile lettura con tanto di esempio (ma il mio esempio è meglio!), così non perdo tempo e posso passare direttamente al codice. Eccolo!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>

#define MYBUFSIZE 1024

int main(int argc, char *argv[])
{
    // test argomenti
    if (argc != 3) {
        // errore args
        printf("%s: numero argomenti errato\n", argv[0]);
        printf("uso: %s host port [i.e.: %s 127.0.0.1 8888]\n", argv[0], argv[0]);
        return EXIT_FAILURE;
    }

    // crea un socket
    int my_socket;
    if ((my_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        // errore socket()
        printf("%s: non posso creare il socket (%s)\n", argv[0], strerror(errno));
        return EXIT_FAILURE;
    }

    // prepara la struttura sockaddr_in per il server remoto
    struct sockaddr_in server;                      // (remote) server socket info
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;                    // set address family
    server.sin_addr.s_addr = inet_addr(argv[1]);    // set server address
    server.sin_port = htons(atoi(argv[2]));         // set server port number

    // loop di comunicazione col server remoto
    for (;;) {
        // compone messaggio per il server remoto
        char my_msg[MYBUFSIZE];
        printf("Scrivi un messaggio per il Server remoto: ");
        scanf("%s", my_msg);

        // send messaggio al server remoto
        if (sendto(my_socket, my_msg, strlen(my_msg), 0, (struct sockaddr *)&server, sizeof(server)) < 0) {
            // errore send()
            printf("%s: errore send (%s)\n", argv[0], strerror(errno));
            return EXIT_FAILURE;
        }

        // riceve una risposta dal server remoto
        memset(my_msg, 0, MYBUFSIZE);
        if (recvfrom(my_socket, my_msg, MYBUFSIZE, 0, NULL, NULL) < 0) {
            // errore recv()
            printf("%s: errore recv (%s)\n", argv[0], strerror(errno));
            return EXIT_FAILURE;
        }

        // mostra la risposta
        printf("%s: risposta Server: %s\n", argv[0], my_msg);
    }

    // esco con Ok
    return EXIT_SUCCESS;
}
Ok, come vedete è ampiamente commentato e quindi è auto-esplicativo, per cui non mi dilungherò sulle singole istruzioni e/o gruppi di istruzioni (leggete i commenti! sono li per quello!), ma aggiungerò, solo, qualche dettaglio strutturale.

La struttura è quella classica e basica di un UDP Client:
  1. socket() - crea un socket
  2. prepara la struttura sockaddr_in per il server remoto
  3. sendto() + recvfrom() - loop di comunicazione col server remoto
ovviamente esistono varianti di questa struttura, ma questa è quella classica. In quest'esempio, che ho scritto e testato appositamente per il blog (beh, in realtà ho adattato/modificato ad uso blog un po' di codice che ho scritto per lavoro, non è certo il primo UDP Client che scrivo!), nel loop di comunicazione c'è anche la lettura della risposta del Server, così chiudiamo il cerchio con il post precedente e possiamo testare sul serio una conversazione Client/Server.

Per quanto riguarda il flusso e lo stile del main() valgono le note elencate nel post sul Server. Per testarlo è sufficiente aprire due terminali (UNIX o Linux, ovviamente), e avviare in uno il Server e nell'altro il Client; se proviamo in una macchina sola il Client deve, come è logico, collegarsi al Server su localhost. Per l'argomento port si può usare un numero qualsiasi scelto tra quelli non riservati (e bisogna usare lo stesso port per Client e Server!) Il risultato sarà il seguente:

terminale 1 (Server):
aldo@ao-linux-nb:~/blogtest$ ./udpserver 8888
./udpserver: ricevuto messaggio dal sock 3: pippo
./udpserver: ricevuto messaggio dal sock 3: pluto
terminale 2 (Client):
aldo@ao-linux-nb:~/blogtest$ ./udpclient 127.0.0.1 8888
Scrivi un messaggio per il Server remoto: pippo
./udpclient: risposta Server: mi hai scritto: pippo
Scrivi un messaggio per il Server remoto: pluto
./udpclient: risposta Server: mi hai scritto: pluto
Scrivi un messaggio per il Server remoto: ^C
Come si nota il Client e il Server si parlano e quando il Client si scollega (con un brutale Ctrl-C) il Server non se ne accorge (è una comunicazione connectionless). Missione compiuta!

Ciao e al prossimo post!