sábado, 7 de noviembre de 2009

Metodo Cesar y analisis de frecuencias

Hace poco compré el libro "Redes Privadas Virtuales" de la editorial Ra-Ma, escrito y publicado por mi buen amigo Javi (aprovecho para hacerte un poco de publicidad jejeje). Leyendo el principio del capitulo 3 sobre criptografía, recordé mis inicios en la criptografía cuando cursé en la UJI allá por 2º de carrera la asignatura de "Seguridad y Protección de la información" bajo la supervisión del Dr. Manuel Mollar, un crack en temas de seguridad donde los haya, del que aprendí todo lo que sé hoy en día sobre criptografía.

Entre unos y otros he terminado por animarme a escribir este post sobre criptosistemas y criptoanalisis que resultará bastante entretenido para la gente curiosa que guste de estos temas.

Primero unas formalidades para centrar el asunto sin enrollarnos demasiado: definiremos criptologia como la suma de dos ciencias que se complementan: criptografia y criptoanálisis.

La criptografia diseña algoritmos que permiten comunicaciones secretas entre un emisor y un destinatario. En la criptografia clasica intervienen dos factores: un algoritmo de cifrado y una clave secreta, K.

                     ----cifrado(K)---->
Mensaje en claro Mensaje cifrado
<--descifrado(K)---


El criptoanálisis se encarga de elaborar tecnicas que permiten descubrir tanto el algoritmo de cifrado utilizado como la clave secreta involucrada en el proceso de encriptado.

    Mensaje cifrado --- ¿algoritmo + K? ---> Mensaje en claro


Vamos a explicar como funciona un criptosistema muy sencillo, conocido como método de Cesar (usado hace unos cuantos años en las campañas militares romanas), y tambien vamos a ver lo facil que resulta atacarlo mediante una sencilla técnica de criptoanálisis conocida como el análisis de frecuencias.



El algoritmo de cifrado es muy sencillo. Cada caracter del mensaje en claro es sustituido por el situado K > 0 posiciones mas adelante en el alfabeto. Por ejemplo, asumiendo K=3, el criptosistema reemplaza la A por la D, la B por la E, y así sucesivamente. El descifrado es igual de facil, ya que consiste en sustituir cada caracter del mensaje cifrado por el situado K posiciones atras en el alfabeto.

Ejemplo de cifrado de Cesar con K = 4

    Mensaje: E L P E R R O D E S A N R O Q U E T I E N E R A B O
Clave: 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
Cifrado: I P T I V V S H I W E R V S U Y I X M I R I V E F S


Aqui teneis un codigo escrito en C cesar.c que os permitira encriptar y desencriptar cualquier documento con la clave K que paseis como argumento de entrada:

/* Algoritmo de Cesar con clave k */

int main (int argc, char **argv)
{
/* argumentos de entrada */
if (argc != 3) {
printf("syntax: %s -[e|d] key\n", argv[0]);
printf("example: %s -e 3\n", argv[0]);
exit(0);
}

if (argv[1][1] != 'e' && argv[1][1] != 'd') {
printf("syntax: %s -[e|d] key\n", argv[0]);
exit(0);
}

/* inicializaciones */
char ed = argv[1][1]; /* encode o decode */
int k = atoi(argv[2]); /* key */
if (k > 255)
k = k % 256;

char c;

/* encoding ... */
if (ed == 'e') {
while (read(0, &c, sizeof(unsigned char)) > 0)
printf("%c", (c + k) % 256);
}

/* decoding ... */
if (ed == 'd') {
while (read(0, &c, sizeof(unsigned char)) > 0)
printf("%c", (c - k) % 256);
}

return 0;
}


El criptosistema de Cesar es facilmente atacable usando una tecnica de criptoanalisis conocida como analisis de frecuencias, ya que la frecuencia de aparicion de cada letra en el mensaje en claro se refleja exactamente en el texto cifrado. Conociendo la letra de mayor frecuencia en el alfabeto utilizada, queda automaticamente establecida la correspondencia.

Vamos a atacar un mensaje cifrado con Cesar usando analisis de frecuencias. Sea por ejemplo el siguiente texto en castellano cifrado con una clave K, de momento desconocida. Nuestro objetivo es averiguar la clave K para descifrar el contenido del siguiente mensaje:

hqxqoxjdughodpdqfkdghfx|rqrpeuhqrtxlhurdfrugduphylyldxqorfrpdfduudtxhwrfdedodjxlwduud


Saber que el texto está en castellano es una pista muy clara de que debemos usar una tabla de frecuencias en castellano. En esta direccion teneis la tabla completa de la frecuencia de aparición de cada carácter en el idioma que mas os interese. En la tabla de castellano podremos encontrar que el caracter mas usado en nuestro idioma es el caracter 'e'.

Ahora necesitamos un programa que calcule la frecuencia de aparicion de cada caracter ASCII en el texto cifrado. Para ello hemos desarrollado este pequeño programa freq.c en C.

/* Frecuencia de cada caracter ASCII */

int main(int argc, char **argv)
{
/* inicializaciones */
unsigned long int total = 0;
float freq = 0;

int i;
int ascii[256]; /* numero de apariciones de cada ascii[i] */
for (i = 0; i < 256; i++)
ascii[i] = 0;

unsigned char c;

/* actualizacion de la tabla ascii y de la variable total */
while (read(0, &c, sizeof(unsigned char)) > 0 ) {
ascii[c]++;
total++;
}
close(0);

/* visualizacion resultados tabla ASCII */
for (i = 0; i < 256; i++) {
if (ascii[i]) {
freq = ((float) ascii[i]) / ((float) total);
printf("ASCII[%d] = %d\tfreq = %f\n", i, ascii[i], freq);
}
}

/* visualizacion total */
printf("\nTotal of characters: %lu", total);
printf("\n");

return(0);
}


El resultado aplicado sobre el texto cifrado es el siguiente:

ASCII[100] = 15 freq = 0.176471
ASCII[117] = 9 freq = 0.105882
ASCII[114] = 8 freq = 0.094118
ASCII[104] = 7 freq = 0.082353
ASCII[120] = 7 freq = 0.082353
ASCII[102] = 6 freq = 0.070588
ASCII[113] = 6 freq = 0.070588
ASCII[108] = 4 freq = 0.047059
ASCII[111] = 4 freq = 0.047059
ASCII[112] = 4 freq = 0.047059
ASCII[103] = 3 freq = 0.035294
ASCII[101] = 2 freq = 0.023529
ASCII[106] = 2 freq = 0.023529
ASCII[116] = 2 freq = 0.023529
ASCII[119] = 2 freq = 0.023529
ASCII[121] = 2 freq = 0.023529
ASCII[107] = 1 freq = 0.011765
ASCII[124] = 1 freq = 0.011765

Total of characters: 85


Si el caracter mas frecuente en español es la letra 'e' (ascii 101), sabemos que alguno de los caracteres mas frecuentes del texto cifrado corresponde con la letra 'e'. Solo nos queda ir probando en secuencia las posibles claves hasta dar con una que decodifique el texto cifrado y nos deje ver con claridad el contenido del texto cifrado. Por tanto, debemos ir probando con esta secuencia de claves K:

    MAX   'e'
------------------
K = 100 - 101 = -1
K = 117 - 101 = 16
K = 114 - 101 = 13
K = 104 - 101 = 3
K = 120 - 101 = 19
...


Ningun texto cumple al pie de la letra con las frecuencias características del lenguaje (es un tema estadístico), aunque podeis estar seguros que la probabilidad de que el caracter 'e' sea uno de los caracteres mas frecuentes, es muy alta.

¿Quien será el primero en dejarme un comentario con el resultado del texto en claro?

Existen varios concursos planteados por distintos organismos oficiales como la CIA y conocidísimas empresas todopoderosas como Google con el proposito de cazar talentos dispersos por esos mundos...



En los exteriores de las oficinas de la CIA en Langley (Virgina) se encuentra una escultura enorme de bronce, de unos cuatro metros de altura, conocida como kryptos, en la que se esconden cuatro mensajes cifrados escritos en ingles. La primera persona que anunció publicamente haber resuelto los tres primeros fue James Gillogly, y por lo que se sabe, la cuarta parte continua sin resolver.



En las paredes del MIT aparecieron carteles de Google con este críptico mensaje acompañado del texto "si puedes averiguar esto, puedes tener futuro en Google". El mensaje encriptado contiene un número de teléfono al que llamar en caso de acertarlo.

Para resolver estos criptosistemas, necesitareis investigar por vuestra cuenta sobre otros criptosistemas por trasposicion como el Vigenere, una variante del metodo de Cesar que usa cadenas de texto como clave. Y tambien los criptosistemas por sustitucion, que unicamente reordenan la posicion de los mensajes en el texto. Y sobre variantes de unos y otros, incluso combinadas entre si. Asi que manos a la obra... ¿Os veis con ganas de intentar trabajar... en Google ... o en la CIA? Yes you can! Why not! :-)

miércoles, 7 de octubre de 2009

La ciencia española no necesita tijeras...

El Gobierno baraja una reducción del 37% del dinero destinado a financiar los proyectos de I+D, las becas y los contratos de investigadores, para caer a los niveles de 2006.

Muy bonito señores gobernantes de este país, es fabuloso. Los bancos piden ayudas millonarias al gobierno para afrontar una crisis que ellos mismos inician (por pura avaricia), nuestros señores gobernantes se las condecen para que puedan auto-regalarse pensiones vitalicias millonarias (son empresas privadas, cierto, pero reflotadas con dinero público... con el dinero de nuestros impuestos).

La industria del automovil pide ayudas millonarias porque se reducen las ventas y no alcanzan sus previsiones de crecimiento, (pobrecitos, que no han ganado bastante con los millones de vehiculos que colapsan las carreteras españolas a cualquier hora punta del día), ayudas que les llegan.

Que los paletas de este pais se quedan sin trabajo despues de estallar la burbuja inmoviliaria en la que ganaban mensualmente el sueldo de dos ingenieros, y les daba para construirse su propia casas a precio de coste en un par de años... Pues ayudas para que los paletas arreglen aceras... Mientras otros españoles (todos ellos con los mismos derechos según la Constitución española) se ven obligados a hipotecar su vida con algún banco, durante 30, 40 y hasta 50 años...

¿Y quien va a pagar las consecuencias de los excesos cometidos? Desde luego, todos los españoles, con el anunciado incremento del iva, que cuando interesa... "todos somos iguales" y que "hacienda somos todos", etc. "Todos debemos ser solidarios", eso nos dicen ahora. Supongo que esos que manejan millones diariamente deberían ser igual de solidarios que todos, igualdad en terminos relativos... a su salario neto, señores gobernantes, eso es lo que queremos los ciudadanos, y que no nos tomen mas el pelo.

"Todos los ciudadanos españoles tienen derecho a una vivienda digna". Pero por lo visto la vivienda no es un bien de primera necesidad, pues está grabada con un iva del 7%. Y que a un mileurista le digan que tiene que ser solidario y pagar 1000 euros mas de iva al comprar una vivienda de 40m2 para vivir con su familia... (que en realidad no son 1000 sino 2000 euros, porque de esos 1000 euros de iva tambien hay que pagar los intereses al banco durante los 30 años de la hipoteca), eso, eso es fomentar la solidaridad, eso es.

A esos millonarios (de euros, no de pesetas), "no les podemos subir mas los impuestos, no sea cosa que se lleven su dinero a otro pais". Y me pregunto yo, qué problema habrá con que ese dinero se vaya a otro país, ¡si el dinero seguirá siendo suyo vayan donde vayan! ¿Quizás sus "regalos" son mas atractivos que los mios?.

Pero especialmente afectados se verán los científicos e investigadores, que parece ser que para lo inteligentes que son, aprovechan muy poco sus cualidades intelectuales para sacar beneficio propio (gracias a dios, y por el bien de la ciencia, mejor que mantengan ocupados sus cerebros en cosas mas trascendentales), porque por lo visto no sacan tajada nunca, ni cuando las cosas van bien, ni menos aún cuando van mal.



Quiero recalcar lo duro que resulta cursar estudios universitarios de ciencias, esfuerzo que por meritos propios debería garantizar al individuo que los afronta con éxito un futuro próspero y mas estable. Los científicos deben costearse unos estudios universitarios e hipotecar su vida personal por un periodo mínimo de 5 años de carrera universitaria (y muchos saben, sobre todo los implicados, que resulta muy pero que muy complicado completarlos en ese tiempo). Otros, como los médicos, incluso hasta 10 años pagandose los estudios y sin cobrar ni un misero céntimo. ¿Y todo ese esfuerzo economico y personal para llegar a donde?

La compensación que les ofrece el gobierno parece humillante... Lo que les tocará es reducir sus salarios o matar proyectos de investigación en los que llevaban años trabajando, para poder pagar impuestos necesarios para rescatar a bancos o a industrias del automovil, y solidarizarse con esa gente parada con escasa cualificación que estan perdiendo sus puestos de trabajo diariamente (miles). Y que mientras esos parados (muchos de ellos felices de su condición) viven en sus casitas a cuerpo de rey, cobrando el subsidio de desempleo y dándose unas buenas vacaciones, incluso rechazando ofertas de trabajo que "no les gustan", (hablo con conocimiento de causa) mientras los cientificos y sus proyectos pagan los excesos cometidos por unos y otros.

Señores politicos, con este apoyo a los cientificos, y viendo los beneficios obtenidos despues de tanto sacrificio, lo que van a conseguir es que en pocos años, este pais se convierta en un pais de analfabetos incultos esperando nuevas ayudas para cobrar la prestación por desempleo. Cualquiera diría que esta situación les interesa incluso más que tener un pais de gente competente, no sea que les pongan las cosas dificiles en su labor de gobernar.

Si investigan un poco podrán ver la tendencia decreciente en matriculaciones en carreras de ciencias en los últimos años, lo que demuestra que estamos en el buen camino. Que alguien me explique como piensan sostener este pais haciendo lo que estan haciendo. Sin investigación no hay productos nuevos. Sin productos nuevos no hay produccion. Sin produccion la gran masa social se queda desempleada. Sin trabajadores, el estado reduce sus ingresos... Poco a poco la piramide se desmorona, y a este paso caeremos todos, desde el primero hasta el último.

No se olviden de una cosa, sus hijos son los que pagarán las consecuencias mas graves. Aunque posiblemente eso también les debe importar muy poco... porque el largo plazo que mas les preocupa seguramente no llegue mucho mas allá de la fecha marcada por las próximas elecciones.

Esta es la realidad del pais en el que vivimos muchos científicos.

sábado, 3 de octubre de 2009

Kernel boot process para novatos

El usuario de un PC pulsa el boton de alimentación del sistema (power) y espera su arranque hasta que se muestre en pantalla el interfaz de usuario gráfico donde el sistema solicita usuario/password para iniciar la sesion. Este post profundiza en ese proceso de arranque, con la intención de que cualquier lector con los conocimientos suficientes, entienda mejor todos los pasos involucrados, sin entrar en detalles que dificulten su comprensión.

En resumen, desde el power-on hasta el login del usuario, se pasa por 4 etapas:
1.- Programa en ROM carga el boot-loader
2.- El boot-loader carga el kernel
3.- El kernel inicializa el sistema y pasa el control al user-space
4.- El user-space finaliza la inicializacion.


Etapa 1. La BIOS

Tras el power-on, un pequeño programa cargado en ROM toma el control del sistema (la BIOS en x86).

Este pequeño programa localiza el "boot device" con el que arrancar el sistema, y para ello elige el primer dispositivo bootable disponible en la secuencia "boot order" (por ejemplo, primero con CD/DVD, despues con USB y despues con HD). Una vez conocido el dispositivo bootable, lee su MBR (sector 0). Este MBR almacena 3 cosas: la rutina bootstrap loader (en .asm), la tabla de particiones y la firma de dispositivo bootable (0x55AAh) como dos ultimos bytes de la partición. Por último, el pequeño programa cargado en ROM pasa el control de ejecución a la rutina bootstrap loader, hecha única y exclusivamente para pasar el control al boot-loader.


Etapa 2. El boot-loader

Los ejemplos mas comunes de boot-loader son grub y lilo (x86, amd) y u-boot (arm). Lo típico es que el boot-loader presente al usuario un menu con las distintas opciones de arranque (diferentes sistemas operativos, distintos kernels del mismo sistema operativo, o programas que no necesiten de sistema operativo como memtest86+). Normalmente tienen una opcion seleccionada por defecto mas un timeout. El usuario elige una de las opciones o el timeout elige la opcion por defecto.

El boot-loader carga el kernel seleccionado en RAM, y opcionalmente tambien la imagen initramfs. La pregunta es, ¿y como accede el boot-loader a la imagen del kernel guardada en el filesystem de una de las particiones...? ¡Sin soporte del kernel!. Cada boot-loader resuelve este problema de forma diferente. El lilo por ejemplo necesita conocer la localizacion exacta del sector del disco en el que esta la imagen del kernel. Grub, en cambio, entiende muchos sistemas de fichero (ffs, fat16, fat32, minic, ext2, reiserfs, jfs, xfs y vst), y puede leer ficheros en dichas particiones sin ayuda de nadie.

Finalmente, el boot-loader pasa el control de ejecución al kernel cargado en RAM, pasandole tambien su lista de argumentos argv (como se hace con cualquier otro programa escrito en C).

Arranque directo vs arranque encadenado

El grub boot-loader permite dos tipos de arranque: directo y encadenado. El arranque directo permite arrancar directamente un sistema operativo (Linux). En arranque encadenado (chain-loading), permite arrancar otro boot-loader distinto (el que usa Windows).

Para arrancar un sistema operativo, el grub necesita conocer el dispositivo de almacenamiento y la partición de ese dispositivo en el que se encuentra almacenado su kernel. Además, en modo directo tambien necesita el path al binario del kernel al que transfiere el control. Mientras que en modo encadenado, transfiere el control al PBR (Partition Boot Record) de la partición indicada; se trata de un bloque similar al del MBR pero almacenado en la partición. Esta dualidad posibilita a grub el arranque del cualquier sistema operativo, sea libre o propietario.

El fichero de configuración del grub

El fichero de configuracion del grub se llama menu.lst y generalmente se encuentra en el directorio /boot/grub/. Cuando grub arranca, lee su contenido y presenta al usuario un menu con las opciones de arranque disponibles en su interior.

Algunas etiquetas para definir parametros generales de su configuración:
- default - Establece la entrada (title) seleccionada por defecto
- timeout - Tiempo en segundos para arrancar la configuracion default
- splashimage - Imagen de fondo (14 colores, 640x480, .xpm.gz)

Por ejemplo:
default 1
timeout 3
splashimage=(hd0,0)/boot/grub/splash.xpm.gz

Por cada sistema operativo que grub puede arrancar, se define en menu.lst un grupo de etiquetas que especifican toda la información que grub necesita para poder arrancarlo. Cada grupo de etiquetas se inicia con la palabra clave "title", que especifica el texto mostrado para el sistema operativo arrancado, y a continuación una serie de etiquetas que dependen del modo de arranque (directo o encadenado). Veamos dos ejemplos para entenderlo mejor:

Ejemplo tipico Linux (carga directa)
title       Ubuntu, kernel 2.6.22.5-custom
root (hd0,0)
kernel /boot/vmlinuz-2.6.22.5-custom root=/dev/hda1 ro quiet splash
initrd /boot/initrd.img-2.6.22.5-custom
boot

Ejemplo tipico Windows (carga encadenada)
title       Windows Vista
root (hd0,1)
chainloader +1

Las etiquetas tipicas de estas entradas son:
- title - Texto mostrado para el sistema operativo arrancado
- root - Dispositivo de almacenamiento y particion donde esta disponible el kernel.
- kernel - (Carga directa) Path en root a la imagen del kernel arrancado
- initrd - (Carga directa) Path en root al fichero initramfs
- boot - (Carga directa) Arranca el sistema operativo seleccionado
- chainloader - (Carga encadenada) Pasa control al sector del PBR indicado

La etiqueta "root" confunde a mucha gente (y no es para menos). La sintaxis es propia de grub, no la confundais con la sintaxis de los ficheros dispositivo situados en /dev para los dispositivos de almacenamiento, que diferencia los dispositivos de almacenamiento IDE (hda) de los SCSI (sda). En cambio, grub usa siempre el prefijo hd. Por tanto, (hd0,0) significa primer bootable-device (0) de la primera partición (0), mientras que (hd1,3) significa segundo bootable-device (1) de la cuarta partición (3). Tampoco olvideis que el bootable-device depende del boot-order asignado en la BIOS.


Etapa 3. El kernel de Linux

El kernel toma el control del sistema: se descomprime a si mismo, inicializa el hardware (CPU, cache y configuración específica de cada board), los distintos subsistemas del kernel (el scheduler, el gestor de memoria, y los sistemas de ficheros virtuales /proc y /sys, sin llegar a montarlos). A continuación lee su lista de argumentos argv. Algunos parámetros tipicos de argv son estos:

- root=/dev/sda1 - Fichero dispositivo de la particion raiz ("/")
- rootfstype=ext2 - Sistema de ficheros de la particion raiz
- init=/sbin/init - Primer programa del user-space ejecutado
- Muchos mas en Documentation/kernel-parameters.txt

A continuación inicializa la consola (/dev/console) para poder mostrar los logs del arranque por pantalla. Y por fin monta su partición raiz (root). Este es el paso mas importante para los usuarios, y se explica despues con mayor detalle.

El kernel intenta ejecutar el primer programa del user-space (init). Para ello, crea un kernel-thread que se reemplaza a si mismo (syscall execve) con la imagen del binario del userspace apuntado por init (normalmente apuntando a /sbin/init). Si el programa indicado por init no se encuentra en la particion raiz, el kernel muestra un panic error indicando el error. Si lo encuentra, pasa el control a dicho programa.

Recordemos que este binario reside en la particion raiz (root). Se trata del primer programa del user-space ejecutado, y el primer programa compilado con la libreria de C estandar (libc).

Montaje de la partición raiz

Para montar la partición raiz (root), el kernel necesita el device driver del dispositivo que aloja la partición raiz y el driver del sistema de ficheros de esa partición (ext2, ext3, etc.).

Si esos 2 drivers fueron compilados built-in (no como modulos), el kernel dispone de todo lo necesario para montar la partición raiz. Por tanto la monta y completa su trabajo.

Si los drivers fueron compilados como modulos (no built-in), el kernel necesita extraer esos modulos de algun sitio. Para ello se usa la imagen initramfs de la que ya hemos hecho algun comentario en este post (profundizaremos sobre ella despues). El kernel lee la imagen initramfs cargada en RAM por el bootloader, la descomprime y monta el sistema de ficheros almacenado en su interior. Continua ejecutando el script /init almacenado en el initramfs. Desde ese script podemos lanzar el código que mas nos interese:

- Una opcion es que /init monte la partición raiz indicada en (root). Para ello lee de initramfs los modulos necesarios para montar el dispositivo indicado en root, carga dichos modulos y reemplaza el montaje del initramfs por el de la partición raiz.
- Otra opcion, mas empleada en sistemas embedded con sistemas de ficheros muy pequeños, es que el initramfs se convierta en la partición raiz del kernel y el su interior se aloje la aplicación del user-space que va a correr el sistema embedded.

La imagen initramfs

Introducido en el kernel 2.6 para conseguir un proceso de inicialización del kernel mucho mas limpio, ya que parte del código de inicialización del kernel se mueve al user-space. Solo esta soportado en kernels >= 2.6.17 configurados adecuadamente.

Como ya hemos visto, puede usarse en sistemas embedded como sistema de ficheros root del kernel, y en sistemas mas grandes, como sistema de ficheros intermedio para montar la partición raiz.

Para construir una imagen initramfs para el boot-loader brug, se hace esto:
$ mkinitramfs -o /boot/initrd.img-2.6.22.5-custom 2.6.22.5-custom

Para crear una imagen initramfs custom (casi nadie hace esto):
$ find  -print -depth | cpio -ov | gzip  -c  > initramfs.img

Para extraer el contenido de una imagen initramfs una vez generada:
$ gzip -dc initramfs.img | cpio -id



Etapa 4. El user-space

No vamos a entrar en muchos detalles, ya que el arranque del user-space es dependiente de cada distribución de Linux. Para no extendernos demasiado, explicaremos aqui el modelo genérico de linux, asumiendo que init=/sbin/init. Por tanto, el primer programa del user-space que tomará el control será /sbin/init, quien leerá el script /etc/inittab y lo ejecuta en secuencia.

Cada linea del fichero /etc/inittab tiene este formato:
id:runlevels:action:process

- id - Secuencia de 4 caracteres. Nosotros lo vamos a ignorar
- runlevels - Niveles de ejecucion. Nosotros lo vamos a ignorar
- action - Accion llevada a cabo cuando se ejecute la entrada
--> sysinit: proceso ejecutado durante el boot del sistema
--> restart: proceso se reinicia cuando termina
--> shutdown: proceso ejecutado con el shutdown del sistema
- process - Especifica linea de comandos del proceso ejecutado

Un ejemplo de fichero /etc/inittab típico de cualquier sistema Linux:
# Script de inicializacion del arranque del sistema
::sysinit:/etc/init.d/rcS
# El proceso /sbin/init se reinicia cada vez que termina
::restart:/sbin/init
# Proceso ejecutado cuando hacemos un shutdown
::shutdown:/bin/umount -a -r

Como vemos, la inicialización del user-space la completa el script apuntado por sysinit en inittab (/etc/init.d/rcS). Desde este script se configuran los dispositivos de red (ifconfig), se montan los virtual filesystems (/proc, /sys), se montan las particiones de datos (recordemos que la partición root ya esta montada), se inicializan los distintos servicios del sistema (http, smtp, ...).

Finalmente se muestra en pantalla el interfaz de usuario gráfico donde el sistema solicita usuario/password para iniciar la sesion.

miércoles, 2 de septiembre de 2009

Crónica de un urban runner

Hace años que practico mi afición de salir a correr por las calles de la ciudad en la que resido. Recorriendo la ciudad de esta forma uno aprende el callejero, se mantiene en forma y se relaja del stress de vivir en una gran ciudad. Como veis, el urban runner tiene muchos alicientes para practicar su deporte favorito.


Pero que nadie se olvide: ninguna practica deportiva esta exenta de riesgos. El urban runner esta continuamente expuesto al peligro de los atropellos. Obviamente, al de los coches y las motos cuando cruza la calle. Si. Pero tambien a los atropellos producidos por otros peatones cuando el urban runner transita por parques, aceras, paseos, calles... todos los sitios teóricamente "habilitados" para su práctica deportiva. Estos peatones "suicidas" presentan características muy peculiares que quiero compartir con vosotros:

- Jovencitas locas que ven a lo lejos su tienda favorita y dan un aceleron, enfilando en linea recta la entrada por su puerta principal, sin importarles lo mas minimo quien se cruce en su camino.
- Madres que se aproximan de cara con el carrito de su bebe haciendo "eses" hacia el mismo lado en el que tu intentas esquivarlas, hasta que finalmente te toca hacer una gran curva para evitarlas.
- Niños de 4 o 5 años que van cogidos de la mano de sus papis y que cuando estas a punto de adelantarles, sueltan de la mano al niño que empieza a purular por el unico espacio libre por donde podías pasar.
- Señoras mayores que van con su gayatito haciendo zig zag en la misma direccion en la que tu intentas esquivarlas, mientras te preguntas por qué no siguen una linea recta.
- Jovencitas locas que salen corriendo de su tienda de ropa favorita con dos bolsas en cada mano mientras por su cabeza solo ronda la idea de entrar en su otra tienda de ropa favorita, y se lanzan a cruzar la calle sin reparar quien se aproxima por su lado.
- Señoras mayores que ven una moneda de 2 centimos brillando en el suelo y que a pesar de que te han visto venir, deciden recogerla justo cuando estas a punto de pasar por ese mismo punto, agachandose al suelo cortandote la trayectoria.
- El típico chulito "killo", que se encara hacia ti mientras te aproximas a el y a pesar de ir cambiando poco a poco tu trayectoria para evitar colisionar, insiste en invadir tu trayectoria recta hasta que consigue desviarte completamente.
- Chiquillas adolescentes de 15 años con acné en la cara que se quedan mirandote mientras te acercas a ellas con cara de que les duele una muela, directas hacia la colisión te apartes hacia el lado que te apartes.
- Grupito de señoras mayores, muy animadas, que justo terminan la conversacion cuando estas proximo a pasar por su lado y se dispersan en todas direcciones sin preocuparse mas que por seguir despidiendose de sus amigas una y otra vez a voces.
- Divorciada maquillada y muy perfumada que tras tomarse su cafecito en el bar y pagar en la barra, sale a la calle despreocupada guardando el monedero en su bolso mientras cruza la calle sin percatarse que has estado a punto de atropellarla.
- Personas paseando su perro con 6 metros de correa extensible cortando el paso de extremo a extremo de la calle.
- Parejita romántica de las que andan cogiditos de la mano, a la que te acercas por su espalda e intentas adelantar por el trozo de acera que dejan libre y cuando estas a punto de hacerlo empiezan a separarse sin soltarse las manos cortardote inconscientemente el paso.
- Señoras mayores subiditas de peso que una vez hacen la intencion de bajar un bordillo no pueden detener esa intención ni aunque se queden mirandote mientras te acercas a ellas, con lo que finalmente se cruzan involuntariamente por donde tu querías pasar.
- Conductores que aparcan el coche a su izquierda y salen del coche, abriendo brúscamente la puerta, sin antes comprobar si alguien se acercaba por la acera.
- Jovencitas locas que salen corriendo de su otra tienda de ropa favorita con 4 bolsas en cada mano y mientras por su cabeza ronda el remordimiento de que su tarjeta de crédito está echando chispas, se lanzan a cruzar la calle sin reparar que vienes corriendo.

Esta es la lista de los atropellos mas curisosos que me han ocurrido en los últimos meses. Que nadie se preocupe: por el momento he salido ileso en todos ellos. Seguiré actualizando este post mientras la gravedad de los atropellos así me lo permita :-)

sábado, 22 de agosto de 2009

Qt4, QComboBox y el foco del teclado

Esta vez vamos a escribir un post que será útil para desarrolladores de la libreria Qt (version 4) que tengan interes en profundizar en la gestión del foco de teclado del widget QComboBox. Vamos a modificar el comportamiento standard de este widget, y supongo que por ese motivo la documentación de la libreria es poco clara en este sentido. Por ello creo que este post os puede servir para entender mejor este asunto.


La idea es implementar un widget custom haciendo uso de dos de los widgets predefinidos por la libreria: QLabel y QComboBox. Nuestra widget custom (ComboLabel) compondrá el QLabel y el QComboBox en un solo widget, de forma que cuando reciba el foco del teclado, desplegará automáticamente la lista del QComboBox, y cuando pierda el foco, la esconderá automaticamente. Vamos a usar la tecla "Enter" (Qt::Key_Enter) para hacer la transición del foco.

Este problema no plantea complicaciones en entornos donde existe un raton o una pantalla tactil, ya que en estos casos, el puntero aisla de los problemas de foco. Sin embargo, en los sistemas donde el único dispositivo de entrada es un teclado, la gestion del foco se convierte en uno de los problemas clave a gestionar y resolver por el desarrollador. Bueno, en uno mas :-)

Os planteo una primera propuesta para resolver el problema que en principio debe parecer obvia (aunque veremos que no funciona). Primero crear una subclase de QWidget (ComboLabel) que componga una instancia de la clase QLabel y de QComboBox en un mismo layout. Despues reimplementar los filtros de entrada (focusInEvent) y salida (focusOutEvent) del foco, para mostrar y ocultar automáticamente la lista. Por ultimo reimplementar el filtro de eventos de teclado (keyPressEvent) para hacer la transicion del foco cuando el usuario pulse la tecla Qt::Key_Enter.

ComboLabel::ComboLabel(QString title, QWidget *parent) : QWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);

m_label = new QLabel(title);
m_combo = new QComboBox;

QHBoxLayout * mainLayout = new QHBoxLayout;
mainLayout->addWidget(m_label);
mainLayout->addWidget(m_combo);
setLayout(mainLayout);
}

void ComboLabel::focusInEvent(QFocusEvent * event)
{
m_combo->showPopup();
}

void ComboLabel::focusOutEvent(QFocusEvent * event)
{
m_combo->hidePopup();
}

void ComboLabel::keyPressEvent(QKeyEvent * event)
{
switch(event->key()) {
case Qt::Key_Enter:
focusNextChild();
break;
default:
QWidget::keyPressEvent(event);
}
}

Esta propuesta parece obvia pero no funciona como queremos. El problema viene dado por la implementación del QComboBox, ya que este widget esta compuesto internamente por dos widgets, un QLineEdit y un QAbstractItemView, y cuando el QAbstractItemView del QComboBox toma el foco del teclado, nunca mas lo devuelve al padre. ¿Como resolvemos entonces este asunto?

La solución del problema (bastante tricky como vereis) consiste en crear una clase ComboBox (derivada de QComboBox), e instalar un filtro de eventos (installEventFilter()) en la vista desplegada (view()). Con este filtro se detectan los eventos de pulsacion de tecla sobre el QAbstractItemView, y se hace la transicion del foco cuando el usuario pulsa sobre dicha vista la tecla Qt::Key_Enter. Para terminar, cuando el widget recibe el foco debe desplegar la vista (showPopup()) y cuando abandona el foco, debe esconderla (hidePopup()). Ambos métodos deben ser reimplementados tal y como veremos, ya que la implementación de la clase padre (QComboBox) no es suficiente para nuestro objetivo.


Veamos los detalles:

ComboLabel::ComboLabel(QString title, QWidget *parent) : QWidget(parent)
{
m_label = new QLabel(title);
m_combo = new ComboBox;

QHBoxLayout * mainLayout = new QHBoxLayout;
mainLayout->addWidget(m_label);
mainLayout->addWidget(m_combo);
setLayout(mainLayout);

setFocusProxy(m_combo);
}

ComboBox::ComboBox(QWidget * parent) : QComboBox(parent)
{
view()->installEventFilter(this);
}

bool ComboBox::eventFilter(QObject * obj, QEvent * event)
{
if (obj == view() && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast(event);
switch (keyEvent->key()) {
case Qt::Key_Enter:
hidePopup();
focusNextChild();
return QComboBox::eventFilter(obj, event);
}
}
return QObject::eventFilter(obj, event);
}

void ComboBox::showPopup()
{
if (!(view()->isVisible())) {
setRootModelIndex(QModelIndex());
QComboBox::showPopup();
}
}

void ComboBox::hidePopup()
{
if (view()->isVisible()) {
setRootModelIndex(view()->currentIndex().parent());
setCurrentIndex(view()->currentIndex().row());
QComboBox::hidePopup();
}
}

void ComboBox::focusInEvent(QFocusEvent * event)
{
if (count() > 0) showPopup(); else focusNextChild();
}

Para que podais probar el ejemplo en vuestro ordenador y analizarlo con mas calma, os dejo este tarball aqui. Insistid porque parece que el servidor web también esta de vacaciones :-). En él encontrareis todos los fuentes listos para ser compilados.

Espero que este pequeño ejemplo os sirva en el futuro para resolver mejor vuestros problemas de foco con QComboBox'es y toda clase de widgets custom que implementeis de ahora en adelante. La librería Qt4 es una librería muy extensa, asi que si alguien tiene interés en tratar algun tema en particular, podeis mandarme un correo sin ningun compromiso e intentaré hacer lo que pueda para ayudaros.

Por cierto... ¿que tal lo habeis pasado estas vacaciones? Las mías cortitas, este verano se han terminado pronto. Y terminadas las vacaciones, como veis, volvemos con energias renovadas y mas ganas de transmitir nuevos conocimientos a los lectores de este blog. Estamos en epoca de crisis, pero ilusion que no falte, que mientras nadie diga lo contrario... ¡sigue siendo gratis! :-)

sábado, 1 de agosto de 2009

Organizar un proyecto en C con librerias

Los programadores newbies en Linux podeis aprovechar este post para aprender a compilar y a usar vuestras librerías en C. Encontrareis muchos manuales en Internet mucho mas extensos y elaborados que este, pero yo lo que quiero transmitiros son conocimientos de forma rapida, para que lo pilleis a la primera sin necesidad de que perdais mucho tiempo. Así que, sin anestesia, nos ponemos manos a la obra:



Información de background

Segun programamos, encontramos funciones que se usan en muchas partes de nuestro codigo. Nos interesa tenerlas en un directorio separado, compiladas y listas para poder usarlas siempre que queramos. Las ventajas de esto son enormes:
- Evitamos los continuos copy/paste (código mas compacto)
- Reducimos tiempo de compilacion (solo se compilan una vez)
- Aumentamos fiabilidad con el tiempo de uso (mas probadas, menos bugs)

La librería estara compuesta por uno o mas ficheros .c fuentes y uno o mas ficheros .h de cabeceras. Veamos un ejemplo:
$ cat milib.h
#ifndef _MI_LIB_H
#define _MI_LIB_H
int suma(int a, int b);
int resta(int a, int b);
#endif

$ cat milib.c
int suma(int a, int b) { return a+b; }
int resta(int a, int b) { return a-b; }


Librerías estáticas vs librerías dinámicas

En linux podemos hacer dos tipos de librerías:
- Librerias estáticas
- Librerias dinámicas

Las librerías estaticas se añaden a la imagen de nuestro ejecutable. Una vez hecha esta operación, la librería puede borrarse y el código sigue funcionando, ya que nuestro binario resultante contiene todo lo que necesita de la librería.

Las librerías dinámicas no se añaden a la imagen de nuestro ejecutable. El ejecutable debe buscar la librería siempre que la necesite, y por tanto, la librería dinámica no puede borrarse, o nuestro programa dejaría de funcionar.

La elección del tipo de librería que debeis usar depende de la naturaleza del proyecto desarrollado. Es una eleccion en la que debeis valorar ventajas e inconvenientes, y llegar a un compromiso entre ambas:
- Ejecutable estatico ocupa mas espacio
- Ejecutable estatico es mas facil de instalar en otro PC
- Ejecutable estatico es mas rapido
- Ejecutable dinamico aprovecha mejoras de la librería compatible con el código del programa
- Ejecutable dinamico requiere refactorización con cambios incompatibles

En Linux (en general Unix) las librerías estáticas se nombran con la extension .a (libmia.a) y las librerías dinámicas se nombran con la extension .so (libmia.so).

Receta para construir y usar una librería estática

Partimos de nuestro programa miprograma.c y de los fuentes de nuestra libreria milib.c.
$ cat miprograma.c
#include
int main(void) {
printf("%d\n", suma(10,10));
printf("%d\n", resta(10,10));
}


Veamos los pasos necesarios para construir y hacer uso de una librería estática:
- Obtener los ficheros objeto .o de todos los fuentes .c de nuestra libreria
$ gcc -c milib.c -o milib.o

- Crear la libreria .a
$ ar -rv libmia.a milib.o

- Compilar nuestro programa, linkando con la libreria estatica
$ gcc -o miprograma miprograma.c -I. -L. -Bstatic -lmia

Observar que -Bstatic -lmia indica que se debe coger la libreria libmia.a. El prefijo lib y la extension .a ya las pone automaticamente el compilador gcc. La opción -Bstatic afecta a todas las librerías que van detrán en la línea de compilación.

Receta para compilar y usar una librería dinámica

Partimos de nuestro programa miprograma.c y de los fuentes de nuestra libreria milib.c. Veamos los pasos necesarios para construir y hacer uso de una librería dinámica:
- Obtener los ficheros objeto .o de todos los fuentes .c de nuestra libreria
$ gcc -c milib.c -o milib.o

- Crear la libreria .so
$ ld -o libmia.so milib.o -shared

- Compilar nuestro programa, linkando con la libreria dinámica
$ gcc -o miprograma miprograma.c -I. -L. -Bdynamic -lmia

Observar que -Bdynamic -lmia indica que se debe coger la libreria libmia.so El prefijo lib y la extension .so ya las pone automaticamente el compilador gcc. La opción -Bdynamic afecta a todas las librerías que van detrán en la línea de compilación.

El comando ldd sirve para ver las librerias dinamicas con las que se ha linkado un programa. Por ejemplo:
$ ldd miprograma
linux-gate.so.1 => (0xbfffe000)
libmia.so => not found
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e6f000)
/lib/ld-linux.so.2 (0xb7faa000)

Como vemos en el ejemplo, el programa no encuentra la libreria libmia.so. Hay que decirle al programa donde estan las librerías dinámicas. Para ello definimos la variable de entorno LD_LIBRARY_PATH de esta manera:
$ export LD_LIBRARY_PATH=/path/a/miprograma
$ ldd miprograma
linux-gate.so.1 => (0xbfffe000)
libmia.so => /path/a/miprograma/libmia.so (0xb7f2f000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7df7000)
/lib/ld-linux.so.2 (0xb7f34000)


Cierre del post

Es hora de que practiqueis vosotros, os tomeis un tiempo para probar este ejemplo y asimilar los conceptos, y empeceis a aplicar estos conocimientos en vuestros futuros proyectos.

Yo me despido del blog hasta despues del verano. Hoy cumplo 33 años y quiero aprovechar la ocasión para dedicarle este post a todos mis lectores, y en especial a aquellos que se acuerden de felicitarme. Os agradezco a todos las felicitaciones, pero yo lo que quiero son regalos little bit frikies! ;-) Que paseis un buen verano y nos leemos en Septiembre! :-)

viernes, 10 de julio de 2009

Estiramientos para calentar y enfriar

Me gustaría dedicar este post a todos los deportistas newbies, que por el motivo que sea deciden cambiar sus habitos de vida sedentarios por otros mas saludables. Esa energia que os animó en su momento a dar el primer paso puede aportaros mucha satisfacción a lo largo de vuestras vidas, pero debeis encauzarla adecuadamente para que no se os acabe pronto la mecha.

Y es que mucha gente abandona en pocas semanas la practica de deporte por falta de motivación, o peor aún, por alguna lesión producida por alguna sobrecarga durante sus primeras semanas de entrenamiento.

No podemos luchar contra la falta de motivación. Es un tema muy personal que depende de los gustos y la capacidad de sacrificio de cada individuo. Pero si podemos dar algunos consejos para reducir la aparición de las temidas lesiones, y evitar así culparlas de un temprano abandono de la actividad fisica.

Antes de iniciar cualquier actividad deportiva, sea del tipo que sea, es necesario realizar un buen calentamiento. Y lo voy a recalcar para que quede bien claro: hay que hacerlo siempre. Se trata de poner en funcionamiento el aparato locomotor, lo que yo llamo "arranque de motores". Lo mismo debe repetirse despues de terminar la actividad física, pero a la inversa, es decir, realizar un buen enfriamiento, o "parada de motores". Es como si cogemos un coche y lo ponemos de 0 a 200Km/h sin hacerle el rodaje. Lo mismo si apagamos el contacto del motor cuando el coche va a 200Km/h. (Bueno, lo dejaré en 120Km/h que no quiero incitaros a sobrepasar los limites de velocidad legales). Como podeis imaginar, el motor sufrirá mucho y correreis el riesgo de griparlo. Nuestro cuerpo funciona como el motor de ese coche. Por tanto debemos tratarlo con el mismo cuidado.

Para la fase de calentamiento os recomiendo realizar un trote suave "borriquero" de 5 minutos y tal como lo termineis, realizar la siguiente tabla de estiramientos (desde la cabeza hasta los pies). Debe llevaros unos 10/12 minutos en total.

- 10rep mirar nuestro hombro izquierdo y despues mirar nuestro hombro derecho.
- 10rep apoyar la oreja sobre el hombro izquierdo y despues sobre el derecho
- 10rep mirar al techo y al suelo
- 10rep elevación hombros con los brazos pegados al cuerpo (hacia arriba)
- 10rep rotación hombros con los brazos pegados al cuerpo (rotación delantera)
- 10rep rotación hombros con los brazos pegados al cuerpo (rotación trasera)
- 10sec con mano izquierda, coger codo derecho y llevarlo al hombro izquierdo
- 10sec con mano derecha, coger codo izquierdo y llevarlo al hombro derecho
- 10sec codo derecho en la nuca estirando hacia la izq. con mano izquierda
- 10sec codo izquierdo en la nuca estirando hacia la der. con mano derecha
- 10sec mano der. por el cuello y izq. por debajo espalda, coger manos y hacer un 8
- 10sec mano izq. por el cuello y der. por debajo espalda, coger manos y hacer un 8
- 10sec mano der. a la cintura y mano izq. por encima de la cabeza, doblar cintura
- 10sec mano izq. a la cintura y mano der. por encima de la cabeza, doblar cintura
- 10sec separar piernas, dejar caer cintura, palmas tocan suelo
- 10sec separar piernas, dejar caer cintura a la izq. manos tocando tobillo izq.
- 10sec separar piernas, dejar caer cintura a la der. manos tocando tobillo der.
- 10sec separar piernas, dejar caer culo sobre tobillo der. y estirada pierna izquierda
- 10sec separar piernas, dejar caer culo sobre tobillo izq. y estirada pierna derecha
- 10sec mirando al frente, estirar pierna izq. hacia delante sin mover la pierna der.
- 10sec mirando al frente, estirar pierna der. hacia delante sin mover la pierna izq.
- 10sec fuerza para tumbar una pared, manteniendo recta pierna izq. sin doblar rodilla
- 10sec fuerza para tumbar una pared, manteniendo recta pierna der. sin doblar rodilla
- 10sec de pie, cruzar pie izq. sobre pie der. y tocar tobillos con las manos
- 10sec de pie, cruzar pie der. sobre pie izq. y tocar tobillos con las manos
- 10sec de pie, subir talon izq. sobre un bordillo y mantener flexionado
- 10sec de pie, subir talon der. sobre un bordillo y mantener flexionado
- 10rep de pie, rotacion tobillo izq. apagando colilla sentido horario
- 10rep de pie, rotacion tobillo izq. apagando colilla sentido antihorario
- 10rep de pie, rotacion tobillo der. apagando colilla sentido horario
- 10rep de pie, rotacion tobillo der. apagando colilla sentido antihorario
- 10rep de pie, flexionar rodillas, rotacion de ambas en sentido horario
- 10rep de pie, flexionar rodillas, rotacion de ambas en sentido antihorario


Para la fase de enfriamiento, nunca pareis de golpe y os senteis en un sofá, debeis andar durante 5 minutos mientras os bajan las pulsaciones. Despues os recomiendo realizar la siguiente tabla de estiramientos en la que todos los ejercicios se hacen en el suelo. En total son 6/8 minutos mas.

- 10sec juntar las plantas de los pies y tirar las rodillas al suelo
- 10sec pierna izq. estirada al frente y tocar punta del pie.
- 10sec pierna der. estirada al frente y tocar punta del pie.
- 10sec abrir piernas y estirar brazos intentando tocar el suelo con el pecho
- 10sec abrir piernas y estirar brazos y coger pie izq. con las dos manos
- 10sec abrir piernas y estirar brazos y coger pie der. con las dos manos
- 10sec acostado boca arriba, llevar rodilla izq. sobre el pecho
- 10sec acostado boca arriba, llevar rodilla der. sobre el pecho
- 10sec acostado boca arriba, llevar rodilla izq. y rodilla der. sobre el pecho
- 10sec acostado boca abajo, presionar talon del pie izq. sobre el culo
- 10sec acostado boca abajo, presionar talon del pie der. sobre el culo
- 10sec acostado boca abajo, presionar talones del pie izq. y der. sobre el culo

Estos ejercicios mejorarán la elasticidad de vuestros musculos. Con ello evitareis lesiones musculares, sobrecargas, contracturas, distensiones. Tambien son buenos como remedio contra las agujetas.

Si haceis bien estos ejercicios podreis practicar deporte por muchísimo tiempo sin tener prácticamente lesiones tontas, y os será mas dificil abandonar el deporte con la excusa de que a vosotros os ha tratado mal, que el deporte es malo para salud, o frases por el estilo que seguro habeis escuchado en mas de una ocasión. Vosotros disponeis de la información necesaria para hacer mejor las cosas y no abandonar a la primera. Es hora de ponerse manos a la obra, chicos. ¡A estirar se ha dicho!

Visitas:

Seguidores