17 agosto, 2006

Cambios en mi vida

Es oficial, llevo 2 semanas viviendo en San Vicente del Raspeig. Y de momento todo bien, salvo que tenemos la casa patas arriba (lo malo de irse con las obras a mitad).

Como las obras las estamos realizando en ratos libres van bastante lentas y no me dejan mucho tiempo para escribir en el blog (aunque anteriormente tampoco lo hacía muy a menudo). Además, de momento, no dispongo de conexión a internet en mi piso.

He escrito un nuevo post que espero le guste a la gente (si alguien lo lee). Aunque este blog parece ya un monográfico de los lectores de tarjetas Omnikey, esa no es mi intención y espero escribir sobre otras cosas pronto.

Por si el cambio de residencia no fuese bastante (con cambio de pueblo incluído), nos hemos traído con nosotros un compañero peludo: un gatito de 2 meses. Es muy revoltoso, muy nervioso, y tiene las uñas muy afiladas (de momento, jeje, quien ríe el último ríe mejor). Además nos ha ocurrido una cosa curiosa (que parece ser muy normal con los gatos y las personas que no los conocen bien). Nos trajimos a San Vicente una gata (hembra) blanca con ojos azules y cuando salimos del veterinario de ponerle su primera vacuna era un gato (macho) gris con ojos grises... así, en cuestión de 20 minutos. Sí notábamos que el pelo se le oscurecía, pero a nosotros nos parecían azules sus ojos, mientras que para el veterinario eran grises. Pero que fuese macho... ¿y las pelotillas? ¿Y el aparato reproductor? !Pues vaya machote está hecho, si encima la esconde¡ ¿Es que tiene vergüenza?

En fin, estamos muy contentos con nuestro gato y nos pasamos casi todo el tiempo libre jugando con él. Incluso pienso que lo mimamos demasiado, ya tiene más trastos en casa que nosotros mismos.

Saludos a toda la gente de San Vicente. Ya os pediré que me hagáis una pequeña guía de sitios a dónde ir por este pueblo.

P.D. ¿Cuál es el gentilicio de San Vicente? Se lo he preguntado a gente de San Vicente y no han sabido responderme, ni siquiera he sabido encontrarlo en la página del ayuntamiento... ¿Es secreto de estado?. El gentilicio de San Vicente es sanvicentero, que suena bastante lógico.

Cómo modificar un driver sin conocer el API del kernel Linux

En este artículo voy a detallar los pasos que seguí para actualizar el módulo del lector de tarjetas Omnikey Cardman 2020. Nunca antes había modificado un driver del kernel Linux, no me considero un buen programador y haciendo esto no gano ni gané nada.

En primer lugar, en la página de Omnikey tenemos disponible el código fuente del driver de Linux para este lector. Lamentablemente, a partir del kernel 2.6.15 no compila. Así que me puse en contacto con el soporte de Omnikey y me dijeron que ese producto ya no se fabricaba, por lo que no le daban más soporte. Pero, ya que dejan en su página el código fuente, me dije: ¿Y por qué no puedo arreglarlo yo?

Pues bien, los pasos que seguí fueron los siguientes (suponiendo que ya tengamos los kernel-headers de nuestro kernel instalados):

  1. Bajarse el código fuente.
  2. En la página web de Omnikey, sección Soporte [Support], opción Drivers, podemos descargarnos el código fuente del driver.

    El archivo que descargamos se llama cm2020_installer_v2_4_1_src.tar.gz.

  3. Descomprimir.
  4. Cambiamos al directorio donde hayamos descargado el driver en el paso anterior y lo descomprimimos:

    ~$ tar xvzf cm2020_installer_v2_4_1_src.tar.gz
  5. Compilar por primera vez.
  6. Antes de compilar vamos a realizar un pequeño cambio en el archivo "install". Este archivo tiene un error y si no realizamos este cambio nos sobreescribirá el script de inicio de pcscd, y con este nuevo archivo a mí no me ha funcionado. Como supongo que pcscd viene empaquetado para la mayoría (si no todas) de las distribuciones, no veo necesario que se compile e instale a partir del código fuente que viene con este driver que acabamos de descargar.

    ~$ cp install install.old
    ~$ vi install
    ~$ diff -u install.old install
    --- install.old 2006-08-16 10:14:29.000000000 +0200
    +++ install 2006-08-16 10:10:20.000000000 +0200
    @@ -231,7 +231,7 @@
     echo OK
     fi
     mkdir -p /usr/local/pcsc/drivers
    -fi #if [ $nopcscd = 0 ];
    +#fi #if [ $nopcscd = 0 ];
     #
     # make initscript
     #
    @@ -565,7 +565,7 @@
    
     fi
    
    -#fi #if [ $nopcscd = 0 ];
    +fi #if [ $nopcscd = 0 ];
    
     #
     # copy the pcsc-shared library to its proper place
    

    Como vemos el cambio consiste en comentar la línea 234 y descomentar la línea 568.

    Hecho este cambio procedemos a compilar. Para ello nos advierte que debemos ejecutar el script como root, por lo que deberemos cambiar a root o ejecutar el script mediante sudo:

    ~$ sudo sh ./install --nopcscd
    Password:
    
    Installing OMNIKEY Cardman USB Smartcard reader...
    
    Distribution is debian
    Found PCSC version 1.3.1 in /usr/sbin/pcscd
    /usr/lib/pcsc/drivers/
    The kernel module for 2.6.16-2-686 isn't prebuilt!
    Try to compile it for you!
    rm -f *.o 2>/dev/null
    rm -f *.ko 2>/dev/null
    rm -f cardman.mod* 2>/dev/null
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    Couldn't build kernel module.
    Compile it by yourself or
    contact OMNIKEY for further details.
    (e-mail: support.linux@omnikey.com)
    

    Primer intento fallido, pero nos dice que tratemos de compilarlo por nosotros mismos (y es lo que vamos a hacer, ya que no nos indica el error por el cuál ha fallado la compilación).

    ~$ cd src/cm2020
    ~$ make
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:168: error: unknown field 'mode' specified in initializer
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: error: unknown field 'owner' specified in initializer
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: warning: initialization from incompatible pointer type
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_read':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1442: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_write':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1540: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_ioctl':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1778: warning: implicit declaration of function 'verify_area'
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1829: warning: passing argument 1 of 'copy_to_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1953: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2008: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1829: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1842: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1942: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1943: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1944: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1945: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1953: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2008: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: At top level:
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2565: fatal error: opening dependency file /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/.cardman.o.d: Permiso denegado
    compilation terminated.
    make[2]: *** [/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o] Error 1
    make[1]: *** [_module_/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020] Error 2
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    make: *** [default] Error 2
    

    Buf! Muchos errores, así que vamos a pasar al siguiente punto.

  7. ¿Dónde buscar?
  8. Cabe aclarar que este mismo driver lo estaba utilizando en el kernel 2.6.12, funcionando correctamente. Así que al cambiar a un kernel posterior y no poder compilar el driver supuse que sería por cambios en el kernel (cuando modifiqué el driver yo utilizaba un kernel 2.6.15). Solamente tenía que buscar una página donde explicasen los cambios que había sufrido el kernel en sucesivas versiones. La solución se encuentra en dos páginas principalmente: Kernel Newbies y en LWN.net. En la primera (Kernel Newbies) explican los cambios de una manera más comprensible, aunque solamente de la última release del kernel. Es un buen punto de partida si utilizas la última versión del kernel. Como no es mi caso busqué en la segunda página (LWN).

    Además, podemos echarle un vistazo al código fuente del kernel (o a sus cabeceras).

  9. Arreglando errores.
  10. El primer error que aparece es el siguiente:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:168: error: unknown field 'mode' specified in initializer

    Este error no pude localizarlo en los cambios del API de la páginas anteriormente citadas. Así que hice una búsqueda con la herramienta de la página con las siguientes palabras: "usb_class_driver mode field". Solamente apareció un resultado: 2.6.15-rc1 short log, y buscando "mode field" en la página encontramos lo siguiente:

    devfs: Remove the mode field from usb_class_driver as it's no longer needed

    Como dice el mensaje, a partir del kernel 2.6.15 ya no se utilizará este campo. Así que lo comentamos de la siguiente manera:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 10:56:07.000000000 +0200
    @@ -165,7 +165,9 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    

    Las líneas añadidas son para que solamente añada ese campo si la version del kernel es menor que la versión 2.6.15. Esto tampoco sabía cómo se hacía, pero en el mismo archivo cardman.c se utiliza esta sentencia, así que simplemente lo copié y modifiqué.

    Si compilamos veremos que el error anterior desaparece (¡PERFECTO!).

    Pasamos al siguiente error:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: error: unknown field 'owner' specified in initializer

    Buscamos la palabra owner en la página de cambios en el kernel (LWN). En ella encontramos lo siguiente en la sección correspondiente al kernel 2.6.16:

     The usb_driver structure has a new field (no_dynamic_id) which lets a driver disable the addition of dynamic device IDs. The owner field has also been removed from this structure.

    Así que editamos el archivo cardman.c de la siguiente forma:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 11:06:14.000000000 +0200
    @@ -165,13 +165,15 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    
     STATIC struct usb_driver cmu_driver = {
    -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
    +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
     .owner= THIS_MODULE,
     #endif
     .name= "cardman",
    

    En esta ocasión añadimos una segunda comprobación a la que ya se tenía (versión del kernel 2.4.20), por lo que deduzco que este campo se añadió a partir de esa versión y ahora se ha eliminado.

    Si compilamos, podemos comprobar que en esta ocasión la compilación termina, obteniendo el archivo "cardman.ko":

    ~$ make
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_read':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1444: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_write':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1542: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_ioctl':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1780: warning: implicit declaration of function 'verify_area'
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1831: warning: passing argument 1 of 'copy_to_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1955: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2010: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1831: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1844: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1944: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1945: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1946: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1947: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1955: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2010: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
     Building modules, stage 2.
     MODPOST
    *** Warning: "verify_area" [/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko] undefined!
     CC /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.mod.o
     LD [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    

    Pero este módulo no funciona, y nos da el error de que verify_area no está definido. Indagando en los warning que nos da la compilación (que no errores) vemos el siguiente mensaje:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1780: warning: implicit declaration of function 'verify_area'

    Es decir, que estamos utilizando una función que no está definida en las cabeceras del kernel. Nuevamente buscamos los cambios efectuados sobre esta función, encontrando el siguiente artículo: Patch: remove verify_area(), donde dice que está deprecado y que se sustituye por access_ok(). Si buscamos referencias a los valores devueltos por verify_area() en los headers del kernel:

    ~$ grep -r "int verify_area(" /usr/src/kernel-headers-2.6.xx/
    /usr/src/kernel-headers-2.6.xx/include/asm-i386/uaccess.h:static inline int verify_area(int type, const void __user * addr, unsigned long size)
    /usr/src/kernel-headers-2.6.xx/include/asm/uaccess.h:static inline int verify_area(int type, const void __user * addr, unsigned long size)
    

    En cualquiera de estos archivos encontramos el siguiente comentario en la declaración de la función verify_area():

    /**
     * verify_area: - Obsolete, use access_ok()
     * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE
     * @addr: User space pointer to start of block to check
     * @size: Size of block to check
     *
     * Context: User context only.  This function may sleep.
     *
     * This function has been replaced by access_ok().
     *
     * Checks if a pointer to a block of memory in user space is valid.
     *
     * Returns zero if the memory block may be valid, -EFAULT
     * if it is definitely invalid.
     *
     * See access_ok() for more details.
     */
    

    Como nos dice, vamos a mirar la función access_ok() para más detalles (se encuentra en el mismo archivo que verify_access():

    /**
     * access_ok: - Checks if a user space pointer is valid
     * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
     *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
     *        to write to a block, it is always safe to read from it.
     * @addr: User space pointer to start of block to check
     * @size: Size of block to check
     *
     * Context: User context only.  This function may sleep.
     *
     * Checks if a pointer to a block of memory in user space is valid.
     *
     * Returns true (nonzero) if the memory block may be valid, false (zero)
     * if it is definitely invalid.
     *
     * Note that, depending on architecture, this function probably just
     * checks that the pointer is in the user space range - after calling
     * this function, memory access functions may still return -EFAULT.
     */
    

    Resumiendo, verify_access() devuelve 0 o -EFAULT, mientras que access_ok() devuelve true o false. Buscamos referencias a esta función y encontramos que en distintos parches se utiliza de la siguiente forma:

    if (!access_ok(VERIFY_WRITE, buf, space))
     return -EFAULT;
    

    Así que buscamos dónde utilizamos la función verify_area() y la modificamos. En este caso no hace falta hacer comprobaciones de la versión del kernel, ya que sobre la función access_ok(), la primera referencia que he encontrado es en el kernel 2.4.17:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 11:44:40.000000000 +0200
    @@ -165,13 +165,15 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    
     STATIC struct usb_driver cmu_driver = {
    -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
    +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
     .owner= THIS_MODULE,
     #endif
     .name= "cardman",
    @@ -1775,12 +1777,12 @@
     if (_IOC_DIR(cmd)&_IOC_READ) {
     DEBUG(5,"ioctl: _ioc_dir=read\n");
     if (arg==0) rc=-EFAULT;
    - else rc=verify_area(VERIFY_WRITE,(void *)arg,size);
    + else rc=(access_ok(VERIFY_WRITE,(void *)arg,size))?0:-EFAULT;
     }
     if (_IOC_DIR(cmd)&_IOC_WRITE) {
     DEBUG(5,"ioctl: _ioc_dir=write\n");
     if (arg==0) rc=-EFAULT;
    - else rc=verify_area(VERIFY_READ,(void *)arg,size);
    + else rc=(access_ok(VERIFY_READ,(void *)arg,size))?0:-EFAULT;
     }
     if (rc) {
     DEBUG(4, "<- ioctl (unknown comamnd)\n");
    

    Como en esta función devolvemos el valor de rc, utilizamos una única sentencia para darle el valor 0 (todo OK) o el valor -EFAULT (error). Ahora ya podemos compilarlo correctamente.

  11. Compilar e instalar.
  12. Volvemos al directorio donde hayamos descomprimido el driver del lector e instalamos:

    ~$ sh ./install --nopcscd
    
    Installing OMNIKEY Cardman USB Smartcard reader...
    
    Distribution is debian
    Found PCSC version 1.3.1 in /usr/sbin/pcscd
    /usr/lib/pcsc/drivers/
    The kernel module for 2.6.16-2-686 isn't prebuilt!
    Try to compile it for you!
    rm -f *.o 2>/dev/null
    rm -f *.ko 2>/dev/null
    rm -f cardman.mod* 2>/dev/null
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
     Building modules, stage 2.
     MODPOST
     CC /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.mod.o
     LD [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    Copy cardman.ko to /lib/modules/2.6.16-2-686/kernel/drivers/usb/misc
    Create init-script and symlink into runlevels
    ./install: 840: declare: not found
    [: 840: 6: unexpected operator
    Attempting to start the cm_usb kernel module
    Starting cm_usb
    Installation finished
    

    Da un error al ejecutar el script de instalación en la línea 840:

    declare -i i=2

    Pero no es importante, ya que es para instalar en los distintos runlevels un script que cargue el driver al arrancar, aunque yo no he tenido problemas para que el kernel cargue el driver cuando enchufo la tarjeta al equipo (hotplug o udev), así que podemos eliminar esas líneas o escribir antes de la comprobación una declaración de la variable initdir:

    initdir=""
  13. Cambiar reglas de udev.
  14. En udev ha habido una serie de cambios, por lo que el archivo que viene junto al driver tampoco funciona correctamente. Para que funcione debemos realizar los siguientes cambios:

    ~$ vi 10-cardmanusb.rules
    ~$ diff -u 10-cardmanusb.rules.old 10-cardmanusb.rules
    --- 10-cardmanusb.rules.old 2006-01-31 16:36:41.000000000 +0100
    +++ 010_cardmanusb.rules 2006-08-16 12:18:01.000000000 +0200
    @@ -1,3 +1,3 @@
    -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="0596", NAME="cm%n", SYMLINK="usb/cm%n"
    -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="1784", NAME="cm%n", SYMLINK="usb/cm%n"
    -BUS="usb", SYSFS{idVendor}="08d4", SYSFS{idProduct}="0009", NAME="cm%n", SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="0596",SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="1784",SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="08d4",SYSFS{idProduct}=="0009",SYMLINK="usb/cm%n"
    
  15. Resumen.
  16. Hemos averiguado los errores que nos daba nuestro driver al compilarlo, hemos buscado soluciones en el API del kernel y lo hemos solucionado. En definitiva, tenemos funcionando un periférico al que la compañía que lo fabrica ya no le brinda soporte, pero que es perfectamente funcional y las únicas diferencias que le he hallado con respecto a versiones más modernas de lectores es la velocidad de lectura de las tarjetas (aunque la diferencia no es abismal) y el que no utilice estándares (las nuevas versiones de lector son enchufar y funcionar, sin compilar nada, incluso más fácilmente que en Window$) para lo que tenemos este driver.

    Ha quedado un poco largo, pero espero que se entienda el proceso que he seguido y lo fácil que resulta realizar cambios sobre un driver que funciona. Entiendo que se podría estudiar el código de dicho driver y mejorarlo, pero eso no entra dentro de mis planes más inmediatos.

  17. Parches conseguidos.
  18. Aquí tenemos los parches que hemos conseguido con nuestros cambios. Para aplicarlos solamente tenéis que copiarlos en un archivo, en el mismo directorio donde se encuentra el original y ejecutar el siguiente comando, en ese directorio :

    ~$ patch -p1 < archivo.patch
    • Archivo: install.patch
    • --- install.old 2006-08-16 10:14:29.000000000 +0200
      +++ install 2006-08-16 12:14:18.000000000 +0200
      @@ -231,7 +231,7 @@
       echo OK
       fi
       mkdir -p /usr/local/pcsc/drivers
      -fi #if [ $nopcscd = 0 ];
      +#fi #if [ $nopcscd = 0 ];
       #
       # make initscript
       #
      @@ -565,7 +565,7 @@
      
       fi
      
      -#fi #if [ $nopcscd = 0 ];
      +fi #if [ $nopcscd = 0 ];
      
       #
       # copy the pcsc-shared library to its proper place
      @@ -609,9 +609,9 @@
       # copy cardman kernel module
       #
      
      -if [ $min == 2 ]; then
      +if [ $min -eq 2 ]; then
       mod_min=misc
      -elif [ $min == 4 ]; then
      +elif [ $min -eq 4 ]; then
       mod_min=kernel/drivers/usb
       elif [ $min -ge 5 ]; then
       mod_min=kernel/drivers/usb/misc
      @@ -622,7 +622,7 @@
       echo Copy cardman.$kext to $moddir/$rel/$mod_min
       cp ./modules/cardman.$rel.$kext $moddir/$rel/$mod_min/cardman.$kext
       chmod 644 $moddir/$rel/$mod_min/cardman.$kext
      -if [ $? == 1 ]; then
      +if [ $? -eq 1 ]; then
       echo Could not copy cardman-module, exiting
       exit 0
       fi
      @@ -834,6 +834,7 @@
       echo "Couldn't make any runlevel entries (Don't mind if you are using Hotplugging!)"
       fi
      
      +initdir=""
       if [ -n "$initdir" ]; then
       chmod a+x $initdir/$iname
      
    • Archivo: cardman.patch
    • --- src/cm2020/cardman.c.old 2006-08-16 10:55:03.000000000 +0200
      +++ src/cm2020/cardman.c 2006-08-16 11:44:40.000000000 +0200
      @@ -165,13 +165,15 @@
       static struct usb_class_driver cmu_class = {
       .name= "usb/cm%d",
       .fops= &cmu_fops,
      + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
       .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
      + #endif
       .minor_base= CM2020_MINOR,
       };
       #endif
      
       STATIC struct usb_driver cmu_driver = {
      -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
      +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
       .owner= THIS_MODULE,
       #endif
       .name= "cardman",
      @@ -1775,12 +1777,12 @@
       if (_IOC_DIR(cmd)&_IOC_READ) {
       DEBUG(5,"ioctl: _ioc_dir=read\n");
       if (arg==0) rc=-EFAULT;
      - else rc=verify_area(VERIFY_WRITE,(void *)arg,size);
      + else rc=(access_ok(VERIFY_WRITE,(void *)arg,size))?0:-EFAULT;
       }
       if (_IOC_DIR(cmd)&_IOC_WRITE) {
       DEBUG(5,"ioctl: _ioc_dir=write\n");
       if (arg==0) rc=-EFAULT;
      - else rc=verify_area(VERIFY_READ,(void *)arg,size);
      + else rc=(access_ok(VERIFY_READ,(void *)arg,size))?0:-EFAULT;
       }
       if (rc) {
       DEBUG(4, "<- ioctl (unknown comamnd)\n");
      
    • Archivo: 10-cardmanusb.rules.patch
    • --- 10-cardmanusb.rules.old 2006-08-16 12:48:21.000000000 +0200
      +++ 10-cardmanusb.rules 2006-08-16 12:48:37.000000000 +0200
      @@ -1,3 +1,3 @@
      -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="0596", NAME="cm%n", SYMLINK="usb/cm%n"
      -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="1784", NAME="cm%n", SYMLINK="usb/cm%n"
      -BUS="usb", SYSFS{idVendor}="08d4", SYSFS{idProduct}="0009", NAME="cm%n", SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="0596",SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="1784",SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="08d4",SYSFS{idProduct}=="0009",SYMLINK="usb/cm%n"
      

05 abril, 2006

Uso práctico de certificado digital

En anteriores entradas expliqué cómo instalar el driver del lector de tarjetas Omnikey Cardman 2020 y cómo utilizar la tarjeta criptográfica en Gnu/Linux.

Ahora voy a comentar un uso práctico del certificado digital de ACCV.

Para hacer que funcionase el lector y la tarjeta criptográfica tuve que pelearme(1) bastante (y todavía me estoy peleando) con los respectivos fabricantes, o mejor dicho, con sus representantes. Pero hoy me he visto recompensado, al menos en parte.

Hoy he realizado la declaración de la renta a través de la página de la AEAT al efecto: Campaña Renta y Patrimonio 2005. El proceso ha sido sencillo, al menos en la parte técnica, para la parte teórica de rellenar el formulario he necesitado la ayuda de mi compañera de curro (¡Gracias Mª José!). Simplemente se accede a la página, se introduce el NIF y se elige identificarse con Certificado de Usuario. El navegador te pregunta (si lo tienes configurado así) por el certificado que deseas utilizar para firmar y accedes a tus datos. Así de simple.

En la página de la campaña tienes acceso, mediante el mismo certificado, a tus datos fiscales, a la solicitud/modificación del borrador de tu declaración, a la posterior presentación... Es decir, cuando sabes qué y dónde ponerlo apenas tardas 5 minutos en hacer la declaración. Y sin moverte de casa.

Ahora solamente me queda aclarar, a quien no haya leído los posts anteriores, que el certificado se puede obtener en formato disquete de forma gratuita. Solamente hay que personarse en un PRU con el DNI y una fotocopia de éste, y te lo entregarán en el momento.

Para quien tenga curiosidad por las aplicaciones del certificado digital emitido por la ACCV, en su página oficial tiene una Lista de Aplicaciones y Servicios con Firma Electrónica.

(1)Pelearme en el buen sentido de la palabra, ya que se mostraron muy interesados en ayudarme desde un primer momento. El problema viene con versiones (kernel, driver, librerías, ...) en sucesivas actualizaciones. De momento no consigo que funcione bien el lector en el kernel 2.6.15, aunque con el 2.6.12 funciona de maravilla. Y con diálogo con los desarrolladores del driver incluído. Volver.

24 enero, 2006

Utilizar tarjeta criptográfica en GNU/Linux

En mi anterior post veíamos cómo Instalar driver Omnikey CardMan 2020 usb. En éste vamos a darle utilidad a éste lector de tarjetas.

Esta parte me costó más de conseguir, y no fue por ganas. Fue por el software privativo. Me explico. La tarjeta que expide la ACCV utiliza el estándar PKCS#11 (hasta ahí bien). Pero accede a ése estándar a través de un API propietario (Siemens CardOS API). Cuando adquieres el lector, éste viene con un cd donde se encuentra este API (intentad buscarlo por internet, es complicadísimo encontrar un enlace de descarga), aunque solamente está para Window$.

Por suerte, en mi trabajo tenemos (aunque yo no lo sabía) contacto con gente de Siemens, que están bastante dispuestos a ayudar. Pues bien, perfecto, Siemens tiene un driver de la tarjeta para Linux, pero con un pequeño problema. No se puede distribuir, salvo que tengas una licencia del CardOS API (en el cd que viene con la tarjeta tienes una licencia, ¡Yuju!).

Una vez conseguido el API para Linux la instalación es muy sencilla:

  • Se copia el archivo .tgz en la raiz del sistema (/)
  • Se descomprime el archivo: tar xvzf archivo.tgz
Y a funcionar...

Con esto ya podemos utilizar la tarjeta criptográfica en Linux. Los pasos para utilizar cualquier tarjeta criptográfica (soportada en Linux) serían prácticamente los mismos. Si tenéis suerte vuestra tarjeta puede estar soportada por el proyecto OpenSC (aunque parece que tiene problemas con su dominio...). Soporta muchos tipos de tarjetas y trata de ser un API estándar para acceder a tarjetas criptográficas.

Preparar las aplicaciones

Ahora detallaré los pasos a seguir para utilizar la tarjeta criptográfica con los programas que más utilizamos (en mi caso Firefox, Evolution).

En primer lugar vamos a hacer un pequeño hack (si es correcto llamarlo así). Estas aplicaciones que he comentado tienen un punto en común a la hora de utilizar los certificados de la tarjeta; utilizan la librería NSS (Network Security Services) de Mozilla. Esto es importante porque en un principio ni Evolution, ni OpenOffice.org tienen un mecanismo para cargar certificados desde un lector de tarjetas. Así vamos a realizar los pasos siguientes:

  • Creamos un directorio en nuestro home:
    # mkdir .certs-db
    
  • Copiamos en esa carpeta los siguientes archivos del directorio de Firefox: cert8.db, key3.db, secmod.db
    # cp .mozilla/firefox/.default/cert8.db .certs-db
    # cp .mozilla/firefox/.default/key3.db .certs-db
    # cp .mozilla/firefox/.default/secmod.db .certs-db
    
  • Creamos enlaces simbólicos en Firefox y en Evolution:
    # ln -sf /home//.certs-db/cert8.db /home//.evolution/cert8.db
    # ln -sf /home//.certs-db/key3.db /home//.evolution/key3.db
    # ln -sf /home//.certs-db/secmod.db /home//.evolution/secmod.db
    # ln -sf /home//.certs-db/cert8.db /home//.mozilla/firefox/.default/cert8.db
    # ln -sf /home//.certs-db/key3.db /home//.mozilla/firefox/.default/key3.db
    # ln -sf /home//.certs-db/cert8.db \ 
    /home//.mozilla/firefox/.default/secmod.db
    

Así, los certificados que añadamos a Firefox nos servirán para Evolution. Igual que con las tarjetas criptográficas. Los módulos de tarjetas que añadamos a Firefox los podremos utilizar en Evolution.

Instalar el módulo de la tarjeta en Mozilla Firefox

Para instalar el módulo de la tarjeta en Firefox seguiremos los siguientes pasos:

  • Arrancamos Firefox.
  • Elegimos: Editar->Preferencias
  • Seleccinamos: Avanzadas
  • Desplegamos: Certificados
  • Presionamos sobre Administrar dispositivos de seguridad...
  • Elegimos Cargar, le damos un nombre descriptivo (en mi caso Siemens CardOS API) y elegimos el módulo que queremos cargar. Si utilizásemos OpenSC, por ejemplo, elegiríamos /usr/lib/pkcs11/opensc-pkcs11.so.
  • Aceptamos y ya está. Si accedemos a Administrar certificados nos pedirá la clave de nuestra tarjeta.

Ahora, si ejecutamos Evolution y mostramos los certificados, igualmente, nos pedirá el pin de nuestra tarjeta y podremos utilizarla con éste programa.

17 enero, 2006

Instalar driver Omnikey CardMan 2020 usb

En primer lugar podemos preguntarnos para qué sirve un lector de tarjetas. Estos lectores sirven para leer una tarjeta de firma electrónica.

En mi caso se trata de la firma que expide la ACCV. Esta firma se puede conseguir de manera gratuita en cualquier punto de registro autorizado. Y se puede solicitar en soporte software o en dispositivo seguro. En soporte software se le entrega al usuario en formato de disquete y es gratuito. En dispositivo seguro se entrega en tarjeta criptográfica y cuesta dinero (podéis consultar precios en la misma página del ACCV).

Por razones que no vienen al caso yo soy operador autorizado, y puedo expedir estas firmas digitales. Para ello dispongo de la firma electrónica en soporte de tarjeta y con un lector usb. Si el formato fuese en disquete no habría problemas para hacerla funcionar en linux, pero en formato tarjeta es un poco más laborioso.

En este primer post voy a explicar qué requerimientos hay para hacer funcionar el lector y que se reconozca la tarjeta insertada. En posteriores envíos trataré el tema de utilizar estos certificados desde linux.

Requerimientos

  • Driver Omnikey CardMan 2020: Módulo del kernel para el lector de tarjetas Omnikey CardMan 2020. La última versión es la 2.4.0 y tiene bastante tiempo ya, así que no creo que la actualicen.
  • - PC/SC Smart Card Daemon: Es un middleware que actúa de interfaz entre la tarjeta y las aplicaciones de manejo de los certificados.

Instalación

En primer lugar vamos a instalar pcsc-lite. En mi caso, al trabajar en Debian, se hace con el siguiente comando, si utilizáis otra distribución buscad cómo se instalaría en ella:

# apt-get install pcscd pcsc-tools

Después descomprimimos el archivo descargado del driver del lector y entramos al directorio creado:

# tar xzvf cm2020_installer_v2_4_0_src.tar.gz
# cd cm2020_installer_v2_4_0

Para instalar el módulo hace falta que tengamos los headers de nuestro kernel instalados y ejecutar el script de instalación que trae:

# sh ./install

Installing OMNIKEY Cardman USB Smartcard reader...

Checking, if pcsc-lite (>=1.2.0.) is installed ... found version 1.2.9-beta7. in /usr/sbin/pcscd
./install: line 113: [: 9-beta7: integer expression expected
Version too old!
Installing pcsc-lite to /usr
Preparing build of pcsc-lite ... OK
Building pcsc-lite ...

En mi caso ahí se queda. No quiero que continúe, ya que intenta compilar la versión 1.2.0 y yo tengo instalada la versión 1.2.9-beta7. Y ése el problema, no reconoce 9-beta7 como un número entero (yo tampoco, pero bueno). Mi solución, ha sido la siguiente; como compara con la versión 1.2.0 cualquier cosa que sea 1.2.X cumplirá la condición, así que elimino las referencias posteriores a 1.2:

# vi install

Localizamos la línea 113:

if [ \( \( $pcscd_maj -eq $pcscd_refmaj \) -a \( $pcscd_min -eq $pcscd_refmin \) -a \( $pcscd_fine -ge $pcscd_reffine \) \) -o \( \( $pcscd_maj -eq $pcscd_refmaj \) -a \( $pcscd_min -gt $pcscd_refmin \) \) -o \( $pcscd_maj -gt $pcscd_refmaj \) ]; then

Y eliminamos las referencias a $pcscd_reffine (que vale 0), quedando como resultado:

if [ \( \( $pcscd_maj -eq $pcscd_refmaj \) -a \( $pcscd_min -eq $pcscd_refmin \) \) -o \( \( $pcscd_maj -eq $pcscd_refmaj \) -a \( $pcscd_min -gt $pcscd_refmin \) \) -o \( $pcscd_maj -gt $pcscd_refmaj \) ]; then

Volvemos a ejecutar el script de instalación:

# sh ./install

Installing OMNIKEY Cardman USB Smartcard reader...

Checking, if pcsc-lite (>=1.2.0.) is installed ... found version 1.2.9-beta7. in /usr/sbin/pcscd
OK!
The kernel module for 2.6.12-10-686 isn't prebuilt!
Try to compile it for you!
make -C /lib/modules/2.6.12-10-686/build SUBDIRS=/home/asanchez/Downloads/cm2020_installer_v2_4_0_src/src/cm2020 modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.12-10-686'
  CC [M]  /home/asanchez/Downloads/cm2020_installer_v2_4_0_src/src/cm2020/cardman.o
  Building modules, stage 2.
  MODPOST
  CC      /home/asanchez/Downloads/cm2020_installer_v2_4_0_src/src/cm2020/cardman.mod.o
  LD [M]  /home/asanchez/Downloads/cm2020_installer_v2_4_0_src/src/cm2020/cardman.ko
make[1]: Leaving directory `/usr/src/linux-headers-2.6.12-10-686'
Copy cardman.ko to /lib/modules/2.6.12-10-686/kernel/drivers/usb/misc
Attempting to start the cm_usb kernel module

Installation finished

Con esto tenemos el módulo instalado, si no lo ha cargado podemos cargarlo nosotros mismos:

# modprobe cardman
# lsmod | grep cardman
cardman                39460  0
usbcore               118044  3 cardman,ohci_hcd

Si insertamos el lector de tarjetas comprobaremos que se carga el driver dinámicamente. Ahora solamente queda reiniciar pcscd y comprobar que coge correctamente el lector:

# /etc/init.d/pcscd restart
Stopping PCSC Lite resource manager: pcscd.
Starting PCSC Lite resource manager: pcscd.
# pcsc_scan
PC/SC device scanner
V 1.4.1 (c) 2001-2004, Ludovic Rousseau 
Compiled with PC/SC lite version: 1.2.9-beta7
Scanning present readers
0: Omnikey CardMan 2020 00 00

Tue Jan 17 17:50:32 2006
 Reader 0 (Omnikey CardMan 2020 00 00)
        Card state: Card removed,

Tue Jan 17 17:50:45 2006
 Reader 0 (Omnikey CardMan 2020 00 00)
        Card state: Card inserted,
        ATR: 3B F2 98 00 FF C1 10 31 FE 55 C8 04 12

ATR: 3B F2 98 00 FF C1 10 31 FE 55 C8 04 12
+ TS = 3B --> Direct Convention
+ T0 = F2, Y(1): 1111, K: 2 (historical bytes)
  TA(1) = 98 --> Fi=512, Di=12, 42.667 cycles/ETU
  TB(1) = 00 --> Programming Param P: 0 Volts, I: 0 milli-Amp\uffffres
  TC(1) = FF --> Extra guard time: 255
  TD(1) = C1 --> Y(i+1) = 1100, Protocol T = 1
-----
  TC(2) = 10 --> Work waiting time: 960 x 16 x (Fi/F)
  TD(2) = 31 --> Y(i+1) = 0011, Protocol T = 1
-----
  TA(3) = FE --> IFSC: 254
  TB(3) = 55 --> Block Waiting Integer: 5 - Character Waiting Integer: 5
+ Historical bytes: C8 04 12

Possibly identified card (using /usr/lib/pcsc/smartcard_list.txt):
3B F2 98 00 FF C1 10 31 FE 55 C8 04 12
        CardOS M4.01a (SLE66CX322P)

Con esto ya tenemos el lector de tarjetas instalado en Linux, próximamente veremos cómo utilizar la tarjeta (y para qué sirve, si no lo sabéis).

Un saludo.

09 enero, 2006

Devil's Pie 0.16 y s-expressions

Me he decidido a probar la nueva versión de Devil's Pie (a pesar de que estaba funcionando con la versión 0.11). La última versión (al escribir estas líneas) es la 0.16. Hasta ahora no había cambiado la versión porque a partir de la versión 0.13 se cambió el archivo de configuración xml por archivos de configuración con formato "s-expression" (luego comentaré ese formato para quien no lo conozca).

¿Por qué el cambio de formato xml (cada vez más utilizado) a s-expressions? Para entender ese cambio habría que conocer un poco el formato s-expression (como yo no lo conocía eché un vistazo a la Wikipedia):

Básicamente, las s-expressions sirven para representar estructuras de datos en forma de texto. Se utilizan en los lenguajes de programación Lisp y Scheme principalmente.

Así, viendo las novedades de la versión 0.13 de Devil's Pie parece ser que se realizó el cambio porque con las s-expressions se pueden combinar diferentes "matchers" (¿alguna traducción candidata?) con operadores lógicos (or, and, not) de forma sencilla, multiplicando las posibilidades de configuración.

Hay una buena documentación en este wiki de Devil's Pie. Está bastante bien explicado y no se necesitan grandes conocimientos de inglés para seguir los ejemplos.

Después de probar la nueva forma de configuración solamente puedo decir que me gusta más. Ahora, en vez de tener un único archivo .devilspie.xml tenemos varios archivos .devislpie/archivo[s].ds. Y una vez escitos nuestros archivos y comprendiendo cómo funcionan, su lectura es muy rápida. Mi .devilspie/terminal-desktop.ds:

(if
        (is (window_name) "terminal-desktop")
        (begin
                (pin)
                (geometry "750x700+5+25")
                (skip_pager)
                (skip_tasklist)
                (wintype "desktop")
        )
)

Con este archivo tengo una terminal como fondo de pantalla, con las siguientes opciones:

  • pin: Para que aparezca en todos los escritorios.
  • geometry: Marca la geometría de la ventana en el formato (anchura)x(altura)+-(xoffset)+-(yoffset).
  • skip_pager: No mostrar en las áreas de trabajo.
  • skip_tasklist: No mostrar en la lista de tareas.
  • wintype: Especifica el tipo de ventana.

¿Complicado? Si tenéis alguna duda contactad conmigo e intentaré resolverlas.

17 noviembre, 2005

Herramientas para la consola (I)

Hoy en día lo que más utilizo es la consola (terminal, línea de comandos,... o como queráis llamarlo). Por eso se me hace imprescindible tener herramientas que me puedan resolver ciertos problemas en un momento dado.

En esta ocasión voy a explicar cómo utilizar una serie de herramientas para poder leer documentos en consola. Los documentos que vamos a poder leer son los que tengan formato Doc (.doc, formato cerrado propiedad de Microsoft) y formato Pdf (.pdf, formato abierto propiedad de Adobe). Hay muchas herramientas que convierten distintos tipos de archivos a texto plano (catdoc, pdftotext, ...). Pero la navegación por ellos se hace compleja (pierden la estructura) e incluso se pierden los enlaces que pueda contener el documento. Por estas razones nosotros vamos a convertir estos archivos a Html, formato con el que, en la mayoría de los casos, mantienen la estructura y los enlaces.

Para conseguir nuestro propósito necesitaremos las siguientes herramientas:

  • wvWare: Biblioteca para convertir documentos de Word. Nos facilita una serie de herramientas para convertir dicho formato en otros más accesibles: Html, Latex, Ps, Pdf, Dvi, Text, Abiword, Rtf, ... Sus librerías las utilizan entre otros AbiWord para abrir documentos de Word. Para instalarlo en Gnu/Debian:
    $ apt-get install wv
  • pdftohtml: Utilidad que convierte archivos Pdf en html. Para instalarlo en Gnu/Debian:
    $ apt-get install pdftohtml
  • w3m: Navegador web en modo texto. Este navegador tiene la particularidad de que sirve también como paginador, y tiene extensiones para mostrar las imágenes en framebuffer (para lo que tienes que configurar la consola para utilizar framebuffer). Para instalarlo en Gnu/Debian:
    $ apt-get install w3m
    Para poder ver imágenes en consola:
    $ apt-get install w3m-img
Como casi siempre en Linux tenemos varias alternativas para realizar cualquier tarea. Así, para poder visualizar los documentos serviría cualquier navegador en modo texto: lynx, links, ...

wvWare muestra el resultado de convertir el documento en formato Doc por la salida estándar. Los diferentes sabores de wv (wvPdf, wvHtml, ...) no lo hacen así, por lo que habría que pasarles el archivo donde queremos escribir el resultado. Lo único que le tenemos que decir a wvWare es la plantilla a utilizar para realizar la conversión, que se encuentran (en Gnu/Debian) en '/usr/share/wv/', y no son más que archivos xml. Para nuestros propósitos utilizaremos la plantilla que convierte a Html: '/usr/share/wv/wvHtml.xml' (leyendo el man nos damos cuenta de que es la plantilla por defecto). Para conseguir el resultado de la conversión por la salida estándar el comando sería el siguiente:
$ wvWare -x /usr/share/wv/wvHtml.xml nombreArchivo.doc

pdftohtml, si no se le dice lo contrario guarda el resultado en un fichero, pero con la opción '-stdout' le diremos que lo muestre por la salida estándar. Además le pasaremos la opción '-p' para que cambie los enlaces Pdf a enlaces Html. Así, el comando a utilizar sería el siguiente:
$ pdftohtml -p -stdout nombreArchivo.pdf

Ahora que tenemos en la salida estándar un archivo Html lo único que hay que hacer es redirigir esa salida a la entrada estándar de nuestro navegador preferido. En el caso de ser w3m habría que decirle que la entrada que le va a llegar es un Html. Para ello le pasamos el parámetro '-T' diciéndole el tipo del que se trata: 'text/html':
w3m -T text/html
En el caso de ser links, el comando quedaría:
links -force-html

Juntando todo lo que hemos dicho quedarían unas líneas como las siguientes:

  • Para documentos Doc:
    $ wvWare -x /usr/share/wv/wvHtml.xml nombreArchivo.doc | w3m -T text/html
  • Para documentos Pdf:
    $ pdftohtml -p -stdout nombreArchivo.pdf | w3m -T text/html

Si queremos automatizar los procesos simplemente deberíamos crearnos unos scripts. Nada más fácil:

  • viewDoc.sh:
    #!/bin/bash
    wvWare -x /usr/share/wv/wvHtml.xml $1 2>/dev/null | w3m -T text/html
  • viewPdf.sh:
    #!/bin/bash
    pdftohtml -p -stdout $1 2>/dev/null | w3m -T text/html

TODO Hacer un único script que muestre un archivo a partir de su extensión.

Espero que este post os sirva de ayuda. En posteriores envíos explicaré otras herramientas de consola. Para la mentes inquietas es todo un reto realizar las tareas más cotidianas en consola y produce fuertes dosis de satisfacción. Además esto sirve para que se le pueda dar un uso, aunque sea didáctico, a nuestros viejos equipos que no pueden con las X.

Un saludo a todos

P.D. Las herramientas wvWare y w3m las descubrí un día navegando por las páginas de LinuxDevCenter de O'Reilly, exactamente en las páginas de ejemplo de uno de sus libros. Curiosamente es lo mismo que yo hacía con los Pdf (formato que utilizo más a menudo) pero que no encontraba cómo hacerlo con los Doc.

03 octubre, 2005

Vendí mi alma al diablo por los años de los años

Pues eso, que acabo de comprarme mi primera (y con una probabilidad del 99,999999% única) vivienda. Y además en un pueblo algo alejado del mío (emigrante/inmigrante?).

Para hacer memoria, actualmente vivo en Elda (Alicante). Me he comprado un piso en San Vicente (Alicante), bastante más caro de lo que están actualmente en mi pueblo (argh!!). Pero tras meditarlo mucho mi novia y yo lo decidimos así, ya que ambos trabajamos en Alicante. Para más señas se encuentra cerca de la iglesia "nosétodavíasunombre", al lado del ayuntamiento. Cerca de La Casa del Café, donde quedamos hace ya unos cuantos meses, y cerca de la cementera.

De momento no nos vamos a mudar, ya que primero realizaremos unas cuantas reformas. Pero si terminamos de limpiarlo pronto puede ser que nos quedemos a dormir más de un fin de semana en él. ¡Ya no hay excusas para no ir a las reuniones del GULA!.

P.D. Me parece inconcedible que uno tenga que hipotecar su vida por tantos años (30 en mi caso, snif!), a este paso, ¿dónde vamos a llegar?

16 septiembre, 2005

Devil's Pie 0.11

Ha salido la versión de Devil's Pie 0.11, que arregla un bug que afectaba al archivo de configuración del terminal como fondo de escritorio. En especial a la acción sobre el tipo de ventana, que causaba una 'Violación de segmento'.

Esta misma mañana le envié un correo a Ross Burton, el autor de la aplicación comentándole el bug (en un pésimo inglés). En apenas una hora me ha respondido, diciéndome que habíamos sido dos personas los que habíamos reportado el error en menos de dos horas y que ya estaba solucionado, indicándome de dónde podía descargar el parche. He abierto la página de Devil's Pie cuando he podido y ya había sacado una nueva versión, la 0.11, que arregla este fallo, aparte de algún otro.

Menos mal, ya que mi escritorio no era el mismo sin mi terminal tan a mano, sin tener que buscarlo por el panel de aplicaciones. Cuando te acostumbras a algo no lo echas en falta hasta que no lo tienes.

Gracias Ross Burton.

02 septiembre, 2005

Terminal como fondo de pantalla

Esta es una pequeña guía que escribí en la web anterior de Gula. La he modificado (más bien poco) y la publico aquí, ya que en estos momentos no está en ningún sitio (creo).

Un día me dije: "si lo que más uso es la consola, pero me gusta acceder a los menús de los escritorios para lanzar aplicaciones de X... ¿Por qué no tener las dos cosas?". Dicho y hecho, utilizo Gnome, pero mi fondo de pantalla es un terminal. Tanto si pulso en "mostrar escritorio", como si accedo al escritorio con combinación de teclas, tengo un terminal esperando que teclee comandos. Además no aparece en la barra de tareas ni tabulando entre ventanas. Podéis ver una muestra de un terminal como fondo de pantalla.

Probablemente habrá una forma más fácil (no la he encontrado, pero se aceptan ideas), y como me costó encontrar esta solución se me ocurrió hacer una guía.

¿Qué necesitamos?

  1. Gnu/Linux - Indudablemente.
  2. Escritorio Gnome- Es el que utilizo desde hace tiempo, no sé si funcionará en otros ya que depende del manejador de ventanas metacity; aunque creo que utiliza especificaciones de freedesktop, por lo que debería funcionar en todos los manejadores de ventanas que las cumplan.
  3. devilspie - Después de conocer esta aplicación mi vida cambió radicalmente. Está diseñada para metacity, actualmente va por la versión 0.10.
  4. Terminal x preferido, preferible que pueda tener fondo transparente. Teóricamente sirve cualquiera del tipo Eterm, aterm, ... aunque el elegido ha sido gnome-terminal (ya que estamos con Gnome).
  5. Un empujoncito brindado por el foro de Mandrake, que me guiaron por el buen camino, es el único sitio donde encontré información: Entrada en el foro de Mandrake.

En primer lugar creamos un perfil del gnome-terminal: Abrimos un terminal. En el menú Editar elegimos Perfiles... Creamos uno nuevo, por ejemplo de nombre "terminal-desktop". A este perfil le damos los valores que deseemos: tipo de letra, tamaño de fuente, color de fuente, color de fondo (en mi caso fondo transparente), etc. En el campo "Título inicial" le daremos el nombre por el que luego reconoceremos el terminal que ejecutemos con este perfil; en un alarde de ingenio yo lo llamé "terminal-desktop". Lo guardamos y vamos al siguiente paso.

El principal artífice de nuestro terminal como fondo es la aplicación devilspie. Esta herramienta sirve para manipular ventanas del gestor de ventanas metacity (en el que yo lo he probado). Así que básicamente, lo que hacemos es lanzar esta herramienta, previamente configurada. Devilspie detecta cuando se lanza una aplicación, examina los parámetros de la ventana según unas reglas que le damos y le cambia unos parámetros que le definimos.

La documentación sobre devilspie es un poco pobre, como ejemplo visualizad el man. Así que tuve que buscar bastante para encontrar cómo funciona el archivo de configuración. Si queréis una lista de las propiedades que se pueden cambiar con devilspie buscad en el README que viene con el código fuente, que fue el último sitio donde miré :'(. Leyendo esta documentación, que es suficiente una vez entiendes el funcionamiento, escribí mi archivo de configuración (basado también en el foro de Mandrake). Este archivo de configuración se llama .devilspie.xml y debe estar en vuestro $HOME:

Código:


<?xml version="1.0"?>
<!DOCTYPE devilspie SYSTEM "devilspie.dtd">
<!-- Archivo: .devilspie.xml -->
<!-- The root element is devilspie -->
<devilspie>
    <flurb name="Terminal in desktop">
        <matchers>
            <!-- Este matcher busca en el titulo de la ventana -->
            <matcher name="DevilsPieMatcherWindowName">
                <property name="window_title" value="terminal-desktop"/>
                <!-- mismo nombre que le hemos dado al titulo en el perfil -->
                <!-- para utilizar Eterm -->
                <!--
                    <property name="application_name" value="Eterm"/>
                -->
            </matcher>
        </matchers>
        <actions>
            <!-- Esta accion sirve para esconder la ventana -->
            <action name="DevilsPieActionHide">
                <!-- Esta propiedad esconde la ventana de la barra de tareas -->
                <property name="skip_tasklist" value="TRUE"/>

                <!-- Esta propiedad esconde la ventana de las 
                    areas de trabajo -->
                <property name="skip_pager" value="TRUE"/>
            </action>

            <!-- Esta accion mueve la ventana delante o detras, 
                solamente tiene la propiedad above -->
            <action name="DevilsPieActionLayer">
                <property name="above" value="FALSE"/>
            </action>

            <!-- Esta accion coloca la ventana en un espacio de trabajo -->
            <action name="DevilsPieActionSetWorkspace">
                <!-- Esta propiedad pone la ventana en 
                    todos los espacios de trabajo -->
                <property name="pinned" value="TRUE"/>
            </action>

            <!-- Esta accion cambia el tipo de ventana -->
            <action name="DevilsPieActionSetWintype">
                <!-- ESTA ES LA PROPIEDAD QUE PONE EL 
                    TERMINAL COMO FONDO DE ESCRITORIO -->
                <property name="wintype" value="DESKTOP"/>
            </action>

            <!-- Esta accion cambia la geometria de la ventana -->
            <action name="DevilsPieActionSetGeometry">
                <property name="width" value="xxx" />
                <property name="height" value="yyy" />
                <property name="xoffset" value="x0" />
                <property name="yoffset" value="y0" />
            </action>
        </actions>
    </flurb>
</devilspie>

Después de configurarlo simplemente debemos añadir devilspie al principio de la sesión. En Gnome se consigue buscando el menú 'Preferencias del escritorio -> Sesiones' (o ejecutáis el comando 'gnome-session-properties'). Allí escribimos devilspie en 'Programas al inicio'.

Ahora ponemos también en sesiones el terminal, que se ejecute después de devilspie, el comando es tan sencillo como: gnome-terminal --window-with-profile=terminal-desktop

Y ya está, tenemos nuestro terminal en el fondo de pantalla. Aunque cambiemos de espacio de trabajo tendremos los comandos que estemos ejecutando (esto se puede configurar). También podemos comprobar cómo, al pulsar en el icono de ver escritorio tendremos allí nuestro terminal, sin ventanas que estorben ni tener que buscarlo. Podéis probar distintas configuraciones, hasta encontrar la que más os agrade.

Alternativa a gnome-terminal: Eterm, terminal muy configurable que acepta temas (man Eterm para más señas).

Espero que esta guía le sirva a alguien. Esta es una pequeña demostración de la potencia de devilspie, se deja a cargo del lector buscar cualquier otro uso. No se explica cómo instalar cada uno de los programas, ya que para cada distribución es diferente y no es el objetivo de esta guía explicar cómo se instalan. Si tenéis cualquier duda intentad contactar conmigo y, si puedo, os la resolveré.