En los títulos y los textos vais a encontrar unas cuantas citaciones cinematográficas (y si, soy un cinéfilo). Si no os interesan podéis fingir no verlas, ya que no son fundamentales para la comprensión de los post...

Este blog es la versión en Español de mi blog en Italiano L'arte della programmazione in C. Espero que mis traducciones sean comprensibles...

martes, 11 de octubre de 2016

El gran lighttpd
cómo escribir un módulo lighttpd en C - pt.2

El Nota: Sabes, esto... esto es un caso muy, muy complicado, Maude. Un montón de input y de output. Afortunadamente estoy siguiendo un régimen de drogas bastante estricto para mantener la mente..., ya sabes...: ágil.
Entonces: la buena noticia es que para escribir un módulo lighttpd no estáis obligados a seguir el mismo régimen de El Nota... la mala noticia es que se necesita un poco de esfuerzo, pero será mucho más fácil después de leer este post (y el anterior, por supuesto... ¿que no lo habéis leído? ¡Pero esta es la segunda parte!).
cara de "¡Ahora se cómo se escribe un módulo lighttpd!"
Entonces, ¿dónde estábamos? Hemos instalado lighttpd en nuestro sistema, hemos creado un entorno de desarrollo descargando y descomprimiendo el tar de instalación encontrado en la web oficial de lighttpd (acordarse de crear el entorno utilizando la misma versión de la que ya está instalada en el sistema). La compilación y la instalación de la versión descargada ha dado (espero) buenos resultados, así que estamos listos para escribir el nuestro módulo.

En primer lugar elegimos un nombre y una acción... va a ser un clásico: le llamaremos mod_helloworld y el módulo escribirá en el browser la frase "Hello, world!" (¡Muy, pero muy, original!).

Abrimos un terminal de Linux, entramos en la
root-directory del entorno de desarrollo y ejecutamos los siguientes comandos (que he enumerado para describirlos uno por 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
Entonces: con 1. editamos (con gedit, pluma, vim, geany... o con lo que os gusta más), el file de configuración lighttpd para añadir a la lista de módulos instalados el nuestro nuevo módulo: buscad la lista "server.modules" y insertad después de el último módulo, el nuevo. Una vez echo esto el aspecto de la lista será así
server.modules = (
    "mod_access",
    "mod_alias",
    "mod_compress",
    "mod_redirect",
    "mod_helloworld",
)
Con 2. editamos el file Makefile.am del package. En comparación con el package original estamos añadiendo un módulo, por lo que hay que buscar el último módulo estándar presente (normalmente es mod_accesslog). Nos encontraremos con cuatro líneas de este tipo:
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) 
Copiamos estas líneas y las pegamos inmediatamente a continuación, sustituiendo en las NUEVAS cuatro lineas mod_accesslog con mod_helloworld. Guardamos y salimos: ahora el nuestro Makefile.am está listo para manejar también el nuevo módulo. Las líneas añadidas serán estas:
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. copiamos el file mod_skeleton.c en un nuevo file che llamaremos mod_helloworld.c: mod_skeleton.c es untemplate file que contiene el esqueleto básico de un módulo: este file nos lo proporcionan amablemente los excelentes desarrolladores de lighttpd para facilitarnos el desarrollo de nuevos módulos.

Con 4. editamos mod_helloworld.c y, con un comando de sustitución global,  cambiamos todas las recurrencias de la palabra skeleton en helloworld. Por el momento guardamos el file así como está y salimos del editor.

Con la secuencia 5.6.7.8. (¿ya vista en el post anterior, os acordáis?) compilamos e instalamos el nuevo módulo. Es posible que en el paso 6. (la configuración) el proceso os diga dice que no encontra algunas librerias: normalmente el error se resuelve usando:
sudo apt-get install libpcre3-dev libbz2-dev
y repitiendo, luego, el punto 6.

Con 9. copiamo el nuevo módulo generado (que tiene la forma de una librería dinámica, una .so) en la directory de sistema que contiene los módulos lighttpd.
Con 10., finalmente, reiniciamos lighttpd y, si no aparece ningún error, el nuestro Web Server ya está listo para utilizar el nuevo módulo (que, por el momento, está medio vacío: como se ha mencionado anteriormente es sólo el esqueleto de un módulo).

¿Que nos falta en este punto? Bueno, obviamente, tenemos que añadir un poco de código al nuestro módulo para que él haga algo (en este caso, mostrarnos un bonito
"Hello, world!"). Pero esto lo veremos en el próximo episodio, y, como siempre, no contengáis la respiración esperando...

¡Hasta el próximo post!

domingo, 18 de septiembre de 2016

El gran lighttpd
cómo escribir un módulo lighttpd en C - pt.1

Policía: ¿Y qué había en su maletín?
El Nota: Eh, papeles, solo papeles. Ya saben, solo mis papeles. Papeles del trabajo.
Policía: ¿Y a qué se dedica, señor?
El Nota: Estoy desempleado.
Vale, a menos que no os interese hacer el mismo trabajo del legendario El Nota (The Dude), es una buena idea tener un know-how valioso. Este es un post de alto valor añadido, y lo es simplemente considerando la ley de la oferta y la demanda: vamos a hablar de un tema que no dispone de una base de conocimientos amplia, entonces esto es un post valioso.
...me gustaría escribir un módulo, pero estoy un poco cansado...
Aquí ya habíamos visto hace tiempo (y os invito a releerlo) cómo escribir un módulo para Apache. Apache es un Web Server un súper popular (el más popular) y tiene una gran base de desarrolladores internos (Apache Software Foundation) y externos (profesionales y aficionados que escriben los más variados tipos de módulos). Gracias a esto se encuentra en la red una gran cantidad de documentación y guías, tanto oficiales (de nuevo la Apache Software Foundation) que no (sitios técnicos, bloggers como yo, etc.). Escribir un módulo Apache no es fácil, pero, con todo el apoyo disponible, no es una misión imposible.

Y llegamos a lighttpd: es un Web Server popular (pero mucho menos que Apache) y con características técnicas de primera clase: tiene un rendimiento comparable al de Apache, pero es mucho más ligero: poca carga de la CPU, bajo uso de memoria, etc. Prácticamente es un must para sistemas embeddeded o, más en general, para sistemas que necesitan alto rendimiento utilizando pocos recursos. Desafortunadamente lighttpd no tiene detrás una grande organización de desarrollo, y, en proporción a la menor popularidad, ni siquiera tiene un gran ejército de profesionales y aficionados que trabajan en ello. Gracias a esto NO se encuentra en la red una gran cantidad de documentación y guías, tanto oficiales que no. Escribir un módulo lighttpd no es fácil, y, con el escaso apoyo disponible, es una misión complicada.

En concreto: lighttpd es un gran producto escrito y mantenido por una pequeña comunidad de excelentes programadores, pero, al no tener detrás de una gran organización, se pierde un poco en los detalles: con más documentación y guías oficiales aumentaría la popularidad, lo que aumentaría también la comunidad de usuarios/desarrolladores (y por lo tanto la documentación no oficial), y, en definitiva, aumentaría el éxito del producto. Pero esto no pasa y entonces nos enfrentamos a un caso clásico de síndrome del perro que se muerde la cola.

lighttpd está en constante evolución, con rendimiento y fiabilidad en aumento, pero, por la falta de detalles mencionada antes, puede pasar que, entre una release y la otra, se olvide la retrocompatibilidad y se introduzcan cambios en la API de programación que invalidan muchos de los módulos (laboriosamente) escritos por desarrolladores externos (¡me pasó a mí con un módulo que escribí para la rel.1.4.33 y que con la rel.1.4.36 dejó de funcionar!).

Bueno, sin hablar más: vamos a escribir un módulo elemental para lighttpd, que será una buena base para escribir módulos más complejos (sobre los cuales os daré algunos consejos en futuros post). La primera actividad es, por supuesto, instalar lighttpd en el PC (y, si estaba instalado, quitar primero Apache). Buscando, con nuestro amigo Google, una de las muchas guías (como esta) para convertir un PC en un server LLMP (o WLMP) (no voy a describir el procedimiento para no alargar demasiado el discurso, pero, os lo garantizo, es bastante simple). Obviamente, al final del procedimiento hay que comprobar si el servidor Web funciona correctamente (por lo general las guías te dicen cómo hacerlo).

A continuación, hay que instalar lo necesario para desarrollar nuestro módulo. Las siguientes instrucciones son (por supuesto) para Linux. Para otros sistemas de la familia UNIX (BSD, OS X, etc.) el procedimiento se adapta de forma intuitiva, mientras que, por ese otro sistema que ni siquiera quiero mencionar (comienza en W...), lo siento pero tendréis que valeros vosotros mismos (si lo conoces lo evitas, y yo, os lo aseguro, lo conozco bien).

Entonces: descargaros de la Web oficial de lighttpd el tar de desarrollo correspondiente a la versión que acabais de instalar en el sistema (están disponibles también los tar de versiones anteriores a la última). Este tar sirve para crear en el sistema un entorno adecuado para el desarrollo y la instalación de nuevos módulos. Descomprimid (donde quieran) el tar, entren en la directory creada y intentad compilar e instalar la release (lo sé, es la misma versión que acabáis de instalar el sistema, pero este paso sirve para determinar si el sistema de desarrollo funciona correctamente). Suponiendo de utilizar la versión 1.4.33 escribiremos:
    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
Comprueben si el Web Server sigue funcionando (de la misma manera utilizada en la primera instalación) y, si todo está bien, estamos listos para escribir/compilar/instalar el nuestro nuevo módulo. Pero esto lo veremos en el próximo episodio...

¡Hasta el próximo post!

martes, 23 de agosto de 2016

UDPhunter
cómo escribir un UDP Client en C - pt.2

Estamos al final de Agosto y la vuelta al cole es siempre dura. Entonces, por ahorrar tiempo y fatiga, intentaré reciclar parte del texto de un mi viejo post bien conectado con este. Hay que considerar que esta es la segunda parte (el Client) de los post sobre UDP y, entonces, la segunda parte de mis viejos post sobre TCP cae perfecta. El código es, obviamente, todo nuevo.
hoy vamos a hablar de los UDP client...
Como se intuye por el título esta vez vamos a hablar de UDP Client. Espero que todos sepan lo que es, de lo contrario os recomiendo una util lectura con ejemplo incluido (¡pero mi ejemplo es mejor!), así que no pierdo tiempo y puedo ir directamente al código. ¡Ahí está!
#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 argumentos
    if (argc != 3) {
        // error args
        printf("%s: numero argumentos equivocado\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) {
        // error socket()
        printf("%s: no puedo crear el socket (%s)\n", argv[0], strerror(errno));
        return EXIT_FAILURE;
    }

    // prepara la estructura sockaddr_in para el 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 comunicación con el server remoto
    for (;;) {
        // compone mensaje para el server remoto
        char my_msg[MYBUFSIZE];
        printf("Escribe un mensaje para el Server remoto: ");
        scanf("%s", my_msg);

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

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

        // enseña la respuesta
        printf("%s: respuesta Server: %s\n", argv[0], my_msg);
    }

    // sale con Ok
    return EXIT_SUCCESS;
}
Ok, como se nota es ampliamente comentado y así se auto-explica, por lo cual no voy a detenerme sobre las instrucciónes y/o grupos de instrucciones (¡leer los comentarios! ¡Están ahí para eso!), pero voy a añadir, solamente, algunos detalles estructurales.

La estructura es la clásica y básica de un UDP Client:
  1. socket() - crea un socket
  2. prepara la estructura sockaddr_in para el server remoto
  3. sendto() + recvfrom() - loop de comunicación col server remoto
por supuesto, hay variaciones de esta estructura, pero esta es la clásica. En este ejemplo, escrito y probado específicamente para el blog (bueno, en realidad he adaptado/modificado un poco de código que escribí para trabajo: ¡no es el primer UDP Client que escribo!), en el bucle de comunicación hay, también, la lectura de la respuesta del Server, por lo que cerramos el círculo con el post precedente y podemos probar en serio una conversación Client/Server.

En cuanto al flujo y el estilo del main() son válidas las notas que figuran en el post sobre el Server. Para probarlo, basta con abrir dos terminales (UNIX o Linux, por supuesto), y iniciar el Server en uno y el Client en el otro; si usamos un solo ordenador el Client debe, como es lógico, conectarse con el Server en localhost. Para el argumento port se puede utilizar cualquier número elegido entre los no reservados (¡y hay que utilizar el mismo puerto para Client y Server!) El resultado es el siguiente:

terminal 1 (Server):
aldo@ao-linux-nb:~/blogtest$ ./udpserver 8888
./udpserver: recibido mensaje del sock 3: pippo
./udpserver: recibido mensaje del sock 3: pluto
terminal 2 (Client):
aldo@ao-linux-nb:~/blogtest$ ./udpclient 127.0.0.1 8888
Escribe un mensaje para el Server remoto: pippo
./udpclient: respuesta Server: me has escrito: pippo
Escribe un mensaje para el Server remoto: pluto
./udpclient: respuesta Server: me has escrito: pluto
Escribe un mensaje para el Server remoto: ^C
Como se nota el Client y el Server se hablan y cuando el Client se desconecta (con un brutal Ctrl-C) el Server no se da cuenta (es una comunicación connectionless). ¡Mission cumplida!

¡Hasta el próximo post!