lunes, 18 de agosto de 2014

De la tecla al glifo (I: el camino de ida en la consola)

En esta entrada y en las siguientes de esta misma saga voy a intentar explicar, hasta donde llegan mis conocimientos y experiencia, qué ocurre en Linux desde que se pulsa una tecla hasta que aparece el carácter correspondiente en el terminal.

Esta primera entrada versará sobre lo que ocurre en el entorno de consola de Linux desde que se pulsa la tecla hasta que el carácter llega a la aplicación, posponiendo a otras entradas la explicación del camino de vuelta, lo que ocurre en X-Window y en una consola remota conectada mediante SSH.

El entorno en el que voy a desarrollar mi explicación es el siguiente:

  • GNU/Linux Kernel 3.13.11 (LFS 7.5) de 32 bits
  • Teclado IBM AT original conectado al puerto PS/2

El software responsable de toda la gestión de la consola es kbd. En mi caso, kbd-2.0.1, descargable desde la web del proyecto: https://www.kernel.org/pub/linux/utils/kbd

Primer nivel: hardware y scancodes

El microcontrolador del teclado se encuentra continuamente escaneando la matriz de las conexiones de las teclas a la búsqueda de una pulsación o de una liberación de tecla. Cuando se produce en evento de este tipo, la información viaja hasta nuestro sistema atravesando diferentes etapas: microcontrolador del teclado, controlador "virtual" 8042, driver del núcleo atkbd, dispositivo del directorio /dev, ...

Nota: por si algún lector no lo recuerda, mi sistema LFS tiene una personalidad un tanto rebelde con las modas: he suprimido a propósito udev y systemd. Me gusta lo clásico. Ya se sabe: "Your distro, your rules"

La información "en crudo" que llega al sistema se puede ver fácilmente con la orden:

hexdump -C /dev/input/event2

(la ruta exacta al dispositivo puede cambiar en el sistema del lector dependiendo de si está gestionando los dispositivos con udev, si el teclado es usb, etc.)

A continuación se muestra la salida de hexdump correspondiente a la pulsación y liberación de la secuencia de teclas: May. Izq., May. Dch., Ñ, Intro, Intro (teclado numérico) (designo por "tecla Ñ" a la tecla que en un teclado español convencional lleva impresa la letra "Ñ"). Para una mejor interpretación, he separado por líneas en blanco las pulsaciones y liberaciones de cada una de las teclas:

00000030  8f 23 f3 53 e7 f2 03 00  04 00 04 00 2a 00 00 00  |.#óSçò......*...|
00000040  8f 23 f3 53 e7 f2 03 00  01 00 2a 00 01 00 00 00  |.#óSçò....*.....|
00000050  8f 23 f3 53 e7 f2 03 00  00 00 00 00 00 00 00 00  |.#óSçò..........|

00000060  8f 23 f3 53 7b ad 05 00  04 00 04 00 2a 00 00 00  |.#óS{­......*...|
00000070  8f 23 f3 53 7b ad 05 00  01 00 2a 00 00 00 00 00  |.#óS{­....*.....|
00000080  8f 23 f3 53 7b ad 05 00  00 00 00 00 00 00 00 00  |.#óS{­..........|

00000090  8f 23 f3 53 75 a0 0d 00  04 00 04 00 36 00 00 00  |.#óSu ......6...|
000000a0  8f 23 f3 53 75 a0 0d 00  01 00 36 00 01 00 00 00  |.#óSu ....6.....|
000000b0  8f 23 f3 53 75 a0 0d 00  00 00 00 00 00 00 00 00  |.#óSu ..........|

000000c0  90 23 f3 53 00 8b 00 00  04 00 04 00 36 00 00 00  |.#óS........6...|
000000d0  90 23 f3 53 00 8b 00 00  01 00 36 00 00 00 00 00  |.#óS......6.....|
000000e0  90 23 f3 53 00 8b 00 00  00 00 00 00 00 00 00 00  |.#óS............|

000000f0  90 23 f3 53 2f 9f 09 00  04 00 04 00 27 00 00 00  |.#óS/.......'...|
00000100  90 23 f3 53 2f 9f 09 00  01 00 27 00 01 00 00 00  |.#óS/.....'.....|
00000110  90 23 f3 53 2f 9f 09 00  00 00 00 00 00 00 00 00  |.#óS/...........|

00000120  90 23 f3 53 4c 33 0b 00  04 00 04 00 27 00 00 00  |.#óSL3......'...|
00000130  90 23 f3 53 4c 33 0b 00  01 00 27 00 00 00 00 00  |.#óSL3....'.....|
00000140  90 23 f3 53 4c 33 0b 00  00 00 00 00 00 00 00 00  |.#óSL3..........|

00000150  90 23 f3 53 c5 3b 0f 00  04 00 04 00 1c 00 00 00  |.#óSÅ;..........|
00000160  90 23 f3 53 c5 3b 0f 00  01 00 1c 00 01 00 00 00  |.#óSÅ;..........|
00000170  90 23 f3 53 c5 3b 0f 00  00 00 00 00 00 00 00 00  |.#óSÅ;..........|

00000180  91 23 f3 53 72 41 01 00  04 00 04 00 1c 00 00 00  |.#óSrA..........|
00000190  91 23 f3 53 72 41 01 00  01 00 1c 00 00 00 00 00  |.#óSrA..........|
000001a0  91 23 f3 53 72 41 01 00  00 00 00 00 00 00 00 00  |.#óSrA..........|

000001b0  91 23 f3 53 70 85 07 00  04 00 04 00 9c 00 00 00  |.#óSp...........|
000001c0  91 23 f3 53 70 85 07 00  01 00 60 00 01 00 00 00  |.#óSp.....`.....|
000001d0  91 23 f3 53 70 85 07 00  00 00 00 00 00 00 00 00  |.#óSp...........|

000001e0  91 23 f3 53 d5 d3 08 00  04 00 04 00 9c 00 00 00  |.#óSÕÓ..........|
000001f0  91 23 f3 53 d5 d3 08 00  01 00 60 00 00 00 00 00  |.#óSÕÓ....`.....|
00000200  91 23 f3 53 d5 d3 08 00  00 00 00 00 00 00 00 00  |.#óSÕÓ..........|

Podemos apreciar que al sistema llega bastante información referente a las pulsaciones de las teclas. Sin embargo, el driver del teclado y las utilidades de kbd nos van a permitir procesar más fácilmente esta información. Gracias a ellos una pulsación del teclado se convierte en una secuencia de bytes llamada scancode. Podemos ver los scancode generados por las pulsaciones de tecla con la utilidad showkey -s ejecutada como root en una consola:

# showkey -s
kb mode was XLATE

press any key (program terminates 10s after last keypress)...
0x9c
0x2a 0xaa
0x36 0xb6
0x27 0xa7
0x1c 0x9c
0xe0 0x1c 0xe0 0x9c
#

El primer código 0x9c corresponde a la liberación de la tecla Intro tras la pulsación que ha lanzado el programa. Los códigos 0x2a y 0xaa corresponden a la pulsación y liberación de la tecla Mayusc. Izq. Los códigos 0x36 y 0xb6 corresponden a la tecla Mayusc. Dcha. Los códigos 0x27 y 0xa7 a la tecla "Ñ". Los códigos 0x1c y 0x9c son de la tecla Intro principal y los códigos 0xe0 0x1c junto con los 0xe0 0x9c corresponden a la pulsación y liberación de la tecla Intro del teclado numérico.

Una ayuda del driver: keycodes

En el camino desde la pulsación de tecla hasta el programa del usuario el driver del teclado nos facilita las cosas en la etapa siguiente traduciendo los scancodes a códigos de tecla llamados keycodes. De esta forma la pulsación y liberación de una tecla quedan unidas en un sólo código. Podemos acceder a los keycodes con la orden showkey -k que ahora nos ofrece los siguientes resultados:

# showkey -k
kb mode was XLATE

press any key (program terminates 10s after last keypress)...
keycode  28 release
keycode  42 press
keycode  42 release
keycode  54 press
keycode  54 release
keycode  39 press
keycode  39 release
keycode  28 press
keycode  28 release
keycode  96 press
keycode  96 release
#

Como podemos apreciar, los códigos asociados a las primeras teclas son los mismos que los scancodes de las mismas sólo que en notación decimal. Sin embargo, el correspondiente a la tecla Intro del teclado numérico ha cambiado de 0xe0 0x1c a 96. Para saber qué correspondencias existen entre scancodes y keycodes, podemos utilizar la orden getkeycodes:

# getkeycodes
Plain scancodes xx (hex) versus keycodes (dec)
for 1-83 (0x01-0x53) scancode equals keycode

 0x50:   80  81  82  83  99   0  86  87
 0x58:   88 117   0   0  95 183 184 185
 0x60:    0   0   0   0   0   0   0   0
 0x68:    0   0   0   0   0   0   0   0
 0x70:   93   0   0  89   0   0  85  91
 0x78:   90  92   0  94   0 124 121   0

Escaped scancodes e0 xx (hex)

e0 00:    0   0   0   0   0   0   0   0
e0 08:    0   0   0   0   0   0   0   0
e0 10:  165   0   0   0   0   0   0   0
e0 18:    0 163   0   0  96  97   0   0
e0 20:  113 140 164   0 166   0   0   0
e0 28:    0   0 255   0   0   0 114   0
e0 30:  115   0 172   0   0  98 255  99
e0 38:  100   0   0   0   0   0   0   0
e0 40:    0   0   0   0   0 119 119 102
e0 48:  103 104   0 105 112 106 118 107
e0 50:  108 109 110 111   0   0   0   0
e0 58:    0   0   0 125 126 127 116 142
e0 60:    0   0   0 143   0 217 156 173
e0 68:  128 159 158 157 155 226   0 112
e0 70:    0   0   0   0   0   0   0   0
e0 78:    0   0   0   0   0   0   0   0
#

Tercer nivel: del código de tecla al carácter

En el siguiente paso el driver del teclado lleva a cabo una traducción de keycodes (códigos de tecla) a keysyms (símbolos de tecla). Cada keysym es un secuencia de dos bytes. Las secuencias que empiezan por el byte 0x00 corresponden a caracteres. Cada keysym tiene un nombre simbólico que describe de forma aproximada el símbolo impreso en una tecla (p.e.: "a" o "Esc") o una acción determinada (p.e.: "Show_Memory"). Cada combinación de una tecla con las teclas modificadoras (Mayusc., AltrGr., Ctrl., Alt., Mayusc. Izq., Mayusc. Dch., Ctrl Izq., Ctrl. Dcha., Bloq. Mayusc.) puede tener asignado un keysym diferente.

La lista de nombres simbólicos reconocidos por el driver del teclado pueden obtenerse con la orden:

dumpkeys -l

La lista de asociaciones entre keycodes y keysyms activas en un momento determinado puede obtenerse con la orden dumpkeys.

La lista de asociaciones activas puede cambiarse con la orden loadkeys. Esta orden puede tomar como argumento un nombre de fichero con las asociaciones deseadas. Este fichero suele denominarse keymap (cf. man keymaps) . El diseño de loadkeys permite que las definiciones sean acumulativas, por lo que puede jugarse, hasta cierto punto, incluyendo nuevas definiciones a lo largo de una sesión que amplíen o modifiquen las funcionalidades del teclado.

Como ejemplo, vamos a asignar a la tecla F11 el carácter 0xF1:

# loadkeys <<FIN
keycode 87 = 0x00F1
FIN
#

Esta forma de asignar keysyms mediante el código hexadecimal del carácter (o el código Unicode al estilo U+6566) no es lo más cómodo ni lo más habitual. Lo más frecuente es asignarlos mediante un nombre simbólico (de ahí la designación keysym):

# loadkeys <<FIN
keycode 87 = euro
FIN
#

Puede comprobarse el efecto de esta modificación de la forma siguiente (designo entre corchetes la tecla que es pulsada en cada ocasión):

# hexdump -C
[F11][barra espaciadora][barra espaciadora]...[barra espaciadora][Intro]
00000000  a4 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |.               |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
#

Es posible que la salida de hexdump -C sea ligeramente diferente:

# hexdump -C
[F11][barra espaciadora][barra espaciadora]...[barra espaciadora][Intro]
00000000  c2 a4 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |..              |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
#

En breve explicaré a qué es debida esta diferencia. Por el momento centrémonos en que hemos asignado el código hexadecimal 0x00A4 a la tecla F11 utilizando el nombre simbólico euro. Esto no supone ninguna ambigüedad porque el carácter euro tiene asignado el código hexadecimal 0xA4 de forma inequívoca, pero ¿cómo debería interpretarse la asignación siguiente?:

# loadkeys <<FIN
keycode 87 = mu
FIN
#

El carácter mu corresponde al código hexadecimal 0xB5 en los conjuntos de caracteres ISO-8859-1,3,8,9,13,y 15 y al código hexadecimal 0xEC en el ISO-8859-7. ¿Qué código queremos que genere la tecla F11?

Si no especificamos nada, la orden loadkeys asumirá que todos los nombres simbólicos que utilicemos deben interpretarse siguiendo la norma ISO-8859-1. En otro caso, deberemos especificarlo:

# loadkeys <<FIN
charset "iso-8859-7"
keycode 87 = mu
FIN
#

Ahora la pulsación de la tecla F11 nos generárá el código 0xEC y no el código 0xB5.

Obsérvese que esta especificación de la codificación de caracteres no tiene otra consecuencia que la forma de interpretar los keysyms que adoptará la orden loadkeys en una ejecución determinada y no tendrá mayores consecuencias en relación con la totalidad del sistema.

El último paso: entregando a la aplicación el carácter.

En este camino desde la pulsación de tecla al momento en que el carácter llega a la aplicación del usuario, hemos llegado a la última etapa. El driver del teclado ya sabe qué debe entregar, bien un carácter si el código asignado a la tecla comienza por 0x00, bien una cadena de caracteres si empieza por 0x01, etc. Si el código del carácter se encuentra entre 0x0000 y 0x007F, no hay ningún problema, pero ¿cómo debe entregar los caracteres con un código superior? ¿como un sólo carácter o como varios caracteres UTF?

Para contestar a esta pregunta debemos utilizar la orden kbd_mode. Con el modificador -a, entregará los caracteres 0x0080 a 0x00FF como un sólo byte. Con el modificador -u, los entregará codificados en UTF. Si suponemos que hemos asignado el código 0x00F1 a la tecla F11, podemos ver el efecto de kbd_mode de la siguiente forma:

# kdb_mode -a
# hexdump -C
[F11][barra espaciadora][barra espaciadora]...[barra espaciadora][Intro]
00000000  f1 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |.               |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |

# kdb_mode -u
# hexdump -C
[F11][barra espaciadora][barra espaciadora]...[barra espaciadora][Intro]
00000000  c3 b1 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |..              |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |

#

Lo que ocurra a partir de este momento es responsabilidad de la aplicación. Nosotros hemos utilizado para nuestros ejemplos la aplicación hexdump, pero el comportamiento de otras aplicaciones, como el ubicuo bash, puede verse afectado por variables de entorno como LANG, etc.

viernes, 15 de agosto de 2014

Torrents en torrente con rtorrent

Elección del cliente rtorrent

La elección de rtorrent como cliente BitTorrent estuvo basada en los siguientes motivos:

  • Poseía una interfaz en línea de comandos con ciertas concesiones a la interfaz gráfica gracias a ncurses
  • Prometía un bajo consumo de recursos.
  • Permitía su ejecución con screen, de forma que podría gestionarse de forma remota.

Descarga

La descarga puede efectuarse desde la web del autor: Jari Sundell (alias Rakshasa). Como el propio autor indica en el fichero README del paquete rtorrent:

Note that rtorrent follows the development of libtorrent closely, and thus the versions must be in sync. This should not be nessesary in the future, when the library API stabilizes.

Por tanto es necesario descargar los paquetes libtorrent y rtorrent de forma sincronizada. En mi caso:

libtorrent-0.13.4.tar.gz
rtorrent-0.9.4.tar.gz

Compilación e instalación

libtorrent-0.13.4

La compilación e instalación de libtorrent-0.13.4 se llevó a cabo de la forma siguiente:

tar -xf libtorrent-0.13.4,tar.gz
cd libtorrent-0.13.4
./configure --prefix=/usr/local/libtorrent-0.13.4
make
make install
cd ..
rm -fr libtorrent-0.13.4
ln -s ../../libtorrent-0.13.4/lib/pkgconfig/libtorrent.pc /usr/local/lib/pkgconfig

Con el fin de mantener un inventario de los paquetes instalados, mantengo un fichero vacío con el nombre del paquete instalado:

touch INSTALADO-libtorrent-0.13.4

y, debido a que deben mantenerse en sincronía, prefiero mantener una copia local del paquete instalado, de ahí que no borre el fichero libtorrent-0.13.4.tar.gz

rtorrent-0.9.4

La compilación e instalación de rtorrent-0.9.4 es bastante inmediata. Sólo es necesario tener la precaución de asegurarse de que el fichero /usr/local/libtorrent-0.13.4/lib/pkgconfig/libtorrent.pc esté accesible a través de la variable $PKG_CONFIG_PATH. Aprovechamos el enlace simbólico creado en el último paso de la instalcón de libtorrent-0.13.4 para ello:

export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig
tar -xf rtorrent-0.9.4.tar.gz
cd rtorrent-0.9.4
./configure --prefix=/usr/local/rtorrent-0.9.4
make
make install
cd ..
ln - s ../rtorrent-0.9.4/bin/rtorrent /usr/local/bin
touch INSTALADO-rtorrent-0.9.4

Antes de borrar el directorio resultado de la extracción de rtorrent, debemos hacer una copia de seguridad del fichero rtorrent-0.9.4/doc/rtorrent.rc a un lugar seguro. Nos servirá como plantilla para crear el fichero $HOME/.rtorrent.rc con los parámetros adecuados de configuración del cliente rtorrent. Una vez copiado este fichero en lugar seguro, podemos borrar el directorio de extracción de rtorrent:

rm -fr rtorrent-0.9.4

Configuración

La estructura de almacenamiento que vamos a configurar es la siguiente:

  • Un directorio ~/rTorrent donde se almacenará todo lo relativo al cliente rtorrent.
  • Un directorio ~/rTorrent/Torrents donde se concentrarán los ficheros *.torrent con los torrents que deben ser descargados.
  • Un directorio ~/rTorrent/Descargas donde se localizarán los ficheros y directorios descargados.
  • Un directorio ~/rTorrent/Sesion donde permitiremos que rtorrent guarde los ficheros necesarios para continuar las descargas entre sesiones.

La configuración de rtorrent está basada en el fichero $HOME/.rtorrent.rc que es recomendable crear a partir del fichero doc/rtorrent.rc del paquete rtorrent-0.9.4 antes mencionado. Los valores del fichero plantilla son valores por defecto y orientativos y están todos comentados. Los valores modificados y activos para la configuración antes mencionada son los siguientes:

directory = ~/rTorrent/Descargas
session = ~/rTorrent/Sesion
schedule = watch_directory,5,5,load_start=~/rTorrent/Torrents/*.torrent
schedule = untied_directory,5,5,stop_untied=

Creando un fichero ~/.rtorrrent.rc sólo con los valores anteriores debería funcionar, aunque recomiendo mantener la integridad del fichero con los valores por defecto comentados para facilitar ulteriores modificaciones.

Uso

Suponiendo que el directorio /usr/local/bin forma parte de la variable $PATH, la ejecución de rtorrent es tan simple como ejecutar:

rtorrent

Para añadir un nuevo torrent a la lista de descargas, basta con añadir el fichero .torrent al directorio ~/rTorrent/Torrents y, con un retardo máximo de cinco segundos, comenzará a descargarse.

Y esto es todo