El cubo III

octubre 12th, 2016

Cuando actualicé mi PC hace unos años, opté por deshacerme del viejo Cubo II y utilizar una caja comercial cúbica. Luego volví a actualizar el ordenador y pasé a una caja clásica. Finalmente, hace unos meses, decidí que era hora de darle un poco a la carpintería, así que decidí construir una caja nueva.

Tras darle varias vueltas, sopesando posibles materiales, de pronto caí en la cuenta de que podía aprovechar la vieja caja cúbica. Esta caja era bastante grande, así que podía intentar reducirla a un tamaño más manejable (pulsar sobre las imágenes para verlas más grandes).

caja_pequena_2 caja_pequena_1

Esta es la caja original. No es realmente muy grande, pero aún así, dado que está pensada para contener placas MicroATX, dos unidades de 5 1/4 y una de 3 1/2 externas, y hasta dos discos duros internos, es obvio que sobra mucho espacio. Sobre todo cuando yo ya no uso el DVD para nada.

Lo primero que hice fue desmontarla completamente y dejar sólo el armazón.

caja_pequena_3

Vemos que hay dos piezas que van desde el frontal hasta la parte posterior y que, además de dar más solidez a la caja, permiten sostener las unidades externas; sobre una de ellas, además, se apoyan los discos duros y la fuente de alimentación. También hay una barra metálica en el otro lado. Las piezas están sujetas con remaches, que quité reventándolos con el taladro. La barra está atornillada, así que no tuvo ninguna dificultad.

caja_pequena_4

El siguiente paso consistió en cortar el chasis para acortarlo. Decidí reducirlo a lo mínimo que me permitiese la base donde se atornilla la placa madre (que es una pieza independiente). También recorté las dos mitades en altura, teniendo en cuenta el tamaño de la fuente de alimentación y la mínima altura a la que la podría poner.caja_pequena_5 caja_pequena_6

Una vez hecho esto, uní ambas mitades con remaches, consiguiendo así el chasis del tamaño que quería.

caja_pequena_7

A continuación, recorté la barra para ajustarla al tamaño, la limé bien para quitarle asperezas, y le hice un agujero en el lado recortado para poder fijarla con un tornillo en los mismos agujeros del chasis donde estaba originalmente.

caja_pequena_8 caja_pequena_9

Ahora tenía que añadir la pieza que va desde el frontal hasta la parte posterior, para poder apoyar la fuente de alimentación. Para ello eliminé primero los soportes de las unidades externas (usando el taladro, como en la primera foto), y la fijé boca arriba para poder recortar cómodamente los laterales con la dremel.

caja_pequena_11

Hecho esto, la fijé boca abajo sobre un listón y recorté la parte central, dejando unos dos centímetros extra de manera que dispusiese de una lengüeta, necesaria para fijar la pieza en el otro lado de la caja.

caja_pequena_12 caja_pequena_13

Doblé la lengüeta de manera que quedase perpendicular a la parte superior. Para que quedase lo mejor posible puse la pieza boca abajo de nuevo y la fijé con un listón, cuyo borde estaba situado justo en el punto de doblez. Luego sólo tuve que ir golpeando poco a poco con el martillo hasta conseguir dejarla exactamente como deseaba. El listón garantizó que sólo se doblará por donde me interesaba.

caja_pequena_14

Hecho esto, hice dos agujeros en los puntos adecuados y sujeté la pieza con unos remaches. Con esto terminé el chasis.

caja_pequena_15

Ahora le tocaba el turno a la fuente de alimentación. La coloqué sobre la pieza y recorté la zona de la rejilla de la parte posterior para dejar libres los conectores, además de hacer algún agujero extra para los tornillos.

caja_pequena_16

Llegó el turno de construir el frontal. Al principio había probado a recortar y recomponer el original, pero quedaba fatal, así que decidí hacer yo mismo uno nuevo. Y ya puestos, decidí darle algo de estilo, así que lo hice directamente en madera. Lo primero fue conseguir un trozo de tablero del tamaño deseado para el frontal y marcar en él qué zonas se van a recortar para colocar los conectores delanteros. En este caso dejé hueco para un lector de tarjetas, los conectores de audio, dos USB 2.0, dos USB 3.0 y dos eSATA.

caja_pequena_17

Procuré que la veta de la madera quedase en la misma dirección que los cortes más largos, pues simplifica el trabajo. A continuación fijé un listón de madera bien recto en cada una de las líneas a recortar, y usé un cincel de madera para recortar hasta una profundidad de unos cinco milímetros. El cincel lo iba apoyando en el listón para asegurarme de que el corte fuese lo más recto posible.

caja_pequena_18 caja_pequena_19

Una vez hechos todos los cortes, le di la vuelta a la tabla y usé una fresa para ir rebajando (poco a poco, unos milímetros de cada vez) por el lado opuesto las zonas de cada conector, hasta quedar a unos cuatro milímetros del frontal. Al hacerlo, los cortes que hice antes con el cincel hicieron que la pieza interna de los agujeros se soltase, quedando así libres.

caja_pequena_20

El siguiente paso fue lijar a conciencia la superficie, limar los bordes de los agujeros para dejarlos regulares y lisos, recortar los bordes del tablero con una fresa para darles un acabado redondeado, y barnizarla con tres capas de barniz. Este es el resultado definitivo, junto con los tres intentos fallidos anteriores.

caja_pequena_21

Luego ya sólo hubo que añadir el botón de encendido, los LEDs y los conectores por el lado trasero.

caja_pequena_22 caja_pequena_23

El siguiente paso fue el soporte del disco duro. Partí del mismo que tenía la caja vieja, pero lo recorté (el original tenía espacio para dos discos duros apilados, pero quedaba muy pegado a la tarjeta gráfica).

caja_pequena_28 caja_pequena_29

A continuación enderecé y volví a doblar la lengüeta superior para “colgar” el soporte de la barra que cruza la caja. Para ello utilicé un destornillador con el mismo diámetro que la barra, y el martillo. También recorté el lateral izquierdo para hacer una pestaña que encajase en la parte izquierda, para así sujetar el soporte a la caja con un remache (en la segunda foto, es la pestaña situada encima de las aberturas de las tarjetas de expansión, a la izquierda).

caja_pequena_30 caja_pequena_31

Por último, enderecé la pestaña de la derecha y le fijé un soporte para discos de 2 1/2 pulgadas, porque quería añadir un disco SSD a mi equipo.

caja_pequena_32

Y así quedaron ambos discos, ya montados y conectados.

caja_pequena_33

El último paso fue preparar la tapa y las paredes laterales. La tapa la recorté un centímetro más larga de lo necesario, y luego coloqué una tabla para doblarla en ángulo con el martillo. Esa zona quedó hacia atrás.

caja_pequena_24

Por último, las paredes. Comencé colocando las láminas originales en su sitio para marcar los dos cortes necesarios.

caja_pequena_25

Luego corté dejando un centímetro de más tanto por arriba como por detrás. La esquina común a ambos cortes la corté en chaflán. A continuación, usando una vez más un listón sujeto con dos sargentas y el martillo, procedí a doblar 180 grados el trozo que sobra. Al haber cortado en chaflán la esquina común, las dos lengüetas no se solaparon (como se puede ver en la tercera fotografía).

caja_pequena_26 caja_pequena_27

caja_pequena_35

Y este es el resultado final, una vez ensamblado todo y, por supuesto, darle una mano de pintura negra en spray: una caja que ocupa, aproximadamente, 2/3 del volumen de la caja original.

caja_pequena_34

Cargando fácilmente mis cascos

julio 25th, 2016

Hace tiempo acabé cansado de mis cascos con cable: se enredaban con todo, y a menudo me olvidaba que los llevaba puestos, con lo que, al levantarme, me daban un tirón. Es por esto que decidí comprar unos cascos inalámbricos. Además, para evitar interferencias o que los vecinos puedan escuchar desde otro equipo lo que estoy viendo en ese momento (cof, cof-porno-cof, cof), decidí usar unos cascos bluetooth. Compré unos bastante baratos, pero eran incómodos, así que trasplanté la circuitería a mis viejos cascos de cable.

Todo funcionaba bien, excepto por un detalle: cada dos por tres me encontraba sin batería porque me los quitaba y no los enchufaba al cargador, con lo que tenía que usarlos durante un buen rato con el cable enchufado, perdiendo así las ventajas. Ante esto empecé a pensar en maneras de recargar la batería de una manera más automática. La primera idea fue utilizar carga inalámbrica: montaría un soporte para los cascos con una bobina sobre él; a su vez, una segunda bobina montada en la diadema de los cascos estaría conectada a los pines de recarga. Así, al colocarlos en el soporte, ambas bobinas quedarían alineadas formando un transformador, igual que se hace en los sistemas de carga inalámbrica de los móviles. Por desgracia, aunque la teoría era muy sencilla, la práctica no lo era tanto: hacía falta un oscilador para alimentar la primera bobina, ajustar la impedancia del conjunto para que estuviese en resonancia… un cristo.

Decidí entonces probar una aproximación más pedestre: colocaría un par de contactos metálicos en la diadema que coincidiesen con otros situados en un soporte, de manera que cuando colgase los cascos éstos recibirían alimentación directamente. La primera idea fue colocar dos contactos en los laterales de la diadema para uno de los polos, y otro en el centro, por debajo, para el otro. De esta manera no habría riesgo de invertir la polaridad si colgaba los cascos al revés. Sin embargo, enseguida deseché esta opción por dos motivos: para empezar, no era sencillo construir un soporte con los tres contactos; y por otro, no me gustaba la idea de tener un contacto metálico apoyado en mi cuero cabelludo (con el sudor acabaría estropeándose). Tras darle varias vueltas, decidí que la mejor opción era usar sólo dos contactos, situados en cada lado de la diadema. Para evitar estropear los cascos por recibir la polaridad al revés si los colgaba mal, decidí añadir un puente de diodos entre los contactos y el circuito de carga. Por desgracia, a la salida tendría 1,4 voltios menos que en la entrada por culpa de las pérdidas de los diodos, y, por otro lado, no quería correr el riesgo de alimentar un dispositivo con una batería de Li-ion con una tensión excesiva(y menos si está situada junto a mi oreja) .

Dado que el diseño original estaba pensado para cargarse desde un USB y, por tanto, la tensión tenía que ser de 5V estabilizados, decidí añadir un regulador justo entre el puente de diodos y el circuito bluetooth. La primera idea que me vino a la cabeza fue el clásico 7805, pero dado que es un regulador lineal, podría tener problemas de sobrecalentamiento (y, una vez más, hay que recordar que eso va a ir pegado a nuestra oreja…), así que lo sustituí por un regulador conmutado, en concreto un TSRN 1-2450 de la marca TRACO. Gracias a él, podría usar cualquier tensión superior a 8 voltios en la entrada del circuito sin riesgo de quemar nada orgánico.

El sistema, finalmente, quedó así:

esquematico

Vemos que la tensión (que no es necesario que esté estabilizada) disponible en el soporte llega a los cascos mediante dos contactos metálicos situados a cada lado de la diadema. Esta tensión (que puede ir con cualquiera de las dos posibles polaridades) llega al puente de diodos, que nos garantiza tener el positivo y el negativo donde nos interesa, pero con una pérdida de 1,4 voltios (con lo que tendremos entre 7,6 y 10,6 voltios). A continuación viene el regulador de tensión, que nos proporciona los 5 voltios estabilizados que necesitamos para el circuito bluetooth.

Una vez diseñado llegó el momento de construirlo, y aquí fue donde empezaron los problemas, en concreto el problema de como hacer los contactos. La primera idea fue utilizar lámina de cobre, pero no fui capaz de encontrarla en ningún lugar. Decidí probar con papel de aluminio: supuse que, si iba bien pegado, no debería haber riesgo de rotura, y sería muy sencillo de fabricar al poder aplicar una capa de loctite primero, pegar encima el papel, y luego recortar con un cutter. Por desgracia el invento no aguantó ni dos días (no sólo el papel de aluminio es demasiado frágil, sino que el loctite no se adhiere nada bien a él). Empecé a buscar alternativas, llegando incluso a intentar hacer una especie de trenza con cable de cobre, pero no encontraba nada que fuese adecuado.

Y de pronto, la solución apareció por pura casualidad: malla de desoldar.

desoldar

Para los que no la conozcan, se trata de cable fino de cobre trenzado formando una tira plana, utilizado para desoldar componentes: básicamente se coloca encima del estaño que se quiere retirar y se calienta el conjunto con el soldador. Cuando el estaño se funde, la malla lo absorbe por capilaridad, quedando el punto completamente libre.

Probé a pegarlo con loctite y el resultado fue excelente… hasta que intenté alimentar el circuito: la malla había absorbido el loctite por capilaridad, por lo que había quedado completamente cubierta y ya no era conductora. Probé también con pistola de pegamento térmico, pero no agarraba en el cobre y se soltaba.

Entonces decidí hacer una prueba a la desesperada, así que fui a la papelería de enfrente de mi casa y compré cinta adhesiva de doble cara, la apliqué en ambos lados de la diadema, pegué la malla poco a poco pasando la uña fuertemente por encima para asegurarme de que quedase bien pegada, y recorté los trozos sobrantes con un cutter. El resultado fue perfecto, y es capaz de aguantar muy bien el rozamiento diario de colgarlo y quitarlo del soporte:

contactos

La cinta aislante negra colocada en los dos extremos es para evitar que la malla se suelte: aunque a lo largo queda muy bien fijada, en los extremos es muy fácil que se acabe levantando, lo que acaba en un “efecto cremallera”. Es por esto que es fundamental protegerla de esta manera si queremos que nos dure mucho tiempo.

Para el soporte utilicé una alcayata grande en forma de L, y para los contactos aproveché las láminas de una pila de 4,5 voltios (la clásica “pila de petaca”):

pila_de_petaca

Con ellos hice dos contactos, uno en cada lado, y los conecté a un alimentador de 12 voltios:

soporte

El resultado no es muy elegante y puede mejorarse, lo reconozco, pero será cuando tenga algo de tiempo.

Y este es el resultado final: ahora sólo tengo que asegurarme de colgar los cascos en el soporte y siempre estarán cargados y listos para su uso.

completo

Multipackager más rápido

mayo 22nd, 2016

He lanzado una nueva versión de Multipackager con una interesante novedad: ahora es muchísimo más rápido. Para ello utilizo OverlayFS.

Para entender por qué es así, cabe explicar que cada vez que se utiliza por primera vez un sistema operativo concreto (por ejemplo: debian jessie i386), multipackager genera dos copias: una para construir paquetes, y otra para lanzar shells.

Hasta aquí nada especial. Sin embargo, la copia para construir paquetes no se utiliza nunca directamente. Cuando se va a construir uno, lo primero que se hace es sacar una copia de dicha copia, y sólo entonces se instalan los paquetes necesarios para crear el paquete. Si todo va bien se borra la copia vieja, y ya tenemos la máquina original con nuevos paquetes; si algo falla, se borra la copia nueva y se restaura la vieja. Así nunca se queda un sistema “a medias”. Por último se hace una nueva copia para crear, esta vez sí, dentro de ella el nuevo paquete. Esta copia se destruye una vez que se ha terminado la generación.

Este sistema permite garantizar que las copias locales de cada sistema operativo están siempre “sanas”, lo que evita tener que generar una nueva completa cada vez que queramos crear un paquete. El inconveniente es que sacar cada copia lleva MUCHO tiempo. Y lo que es peor: a medida que se instalan más y más paquetes, la copia tarda más y más.

La solución consistió en no hacer copias, sino utilizar OverlayFS para poder realizar las operaciones sin modificar la máquina original. Esta operativa es inmediata en el segundo caso, cuando queremos construir el paquete en una máquina que se borrará cuando se termine. Sin embargo, para el primer caso tuve que rascarme un poco la cabeza, pues si la operación de instalar paquetes termina correctamente es necesario “aplicar los cambios” sobre la máquina virtual original. Esta operación, afortunadamente, no es muy complicada.

El resultado es que los tiempos para generar los distintos paquetes se han reducido en un orden de magnitud, lo que no está nada mal, teniendo en cuenta que actualmente estoy generando entre seis y ocho paquetes por programa (debian, ubuntu, fedora y arch, cada una en 32 y 64 bits).

Más sobre Debian en Android

febrero 20th, 2016

A raíz de la aparición de MaruOS, decidí intentar hacer algo similar en mi móvil Android, así que me puse a preparar una instalación de Debian para Android como ya había hecho otras veces. Por desgracia las cosas se torcieron ya al principio, así que voy a comentar los pasos que di para corregir los problemas que encontré, que no han sido pocos:

Para empezar, mi móvil tiene Android 6 (en concreto la distribución PureNexus para Nexus 4 que encontré en la página de El tendero digital). Parece que algunos cambios de seguridad hechos en él, junto con otros en APT fueron los que acabaron dando guerra. Pero no adelantemos acontecimientos…

Empecé creando el entorno básico con debootstrap, con el comando

sudo debootstrap --arch=armhf --variant=minbase --foreign sid  /home/raster/tmp  http://ftp.debian.org/debian

El resultado lo comprimí con TAR, lo pasé al móvil, y me conecté a través de USB con un shell ADB (para disponer de ADB hay que bajarse el SDK de Android):

sudo ./platform-tools/adb shell

Una vez hecho esto, lo primero es ejecutar el comando resize para ajustar el tamaño lógico de la ventana a la que tenemos físicamente, y que aplicaciones como los editores de texto y demás se vean correctamente.

Tras ello, fui hasta /data/media/0, que es donde se encuentra la zona de datos de usuario, y allí descomprimí el fichero .tar con el sistema Debian básico. Luego usé un sencillo script para entrar dentro de él para seguir la instalación:

resize
export SDCARD=/data
export ROOT=$SDCARD/media/0/debian
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
export HOME=/root
mount -o remount,exec,dev,suid $SDCARD
for f in dev dev/pts proc sys ; do mount -o bind /$f $ROOT/$f ; done
chroot $ROOT /bin/bash -l
for f in sys proc dev/pts dev ; do umount $ROOT/$f ; done

Tras lanzarlo, procedí a terminar la instalación del sistema, pero me encontré con que al ejecutar debootstrap/debootstrap –second-stage, fallaba y no continuaba la instalación. Tras varias pruebas, descubrí que el problema era que intentaba crear varias veces una serie de nodos en /dev.

Se me ocurrió probar a usar QEMU para realizar la operación en mi propio PC, así que instalé qemu-static, copié qemu-arm-static al directorio /usr/bin del sistema Debian sin configurar, y lancé una sesión de shell dentro de ella con CHROOT en mi propio PC. Ahí el comando debootstrap/debootstrap –second-stage sí funcionó, así que procedí a comprimir el sistema ya listo y subirlo al móvil.

Ahora ya tenía un sistema Debian. O casi, porque cualquier intento de usar APT para instalar paquetes o actualizar la lista de programas fallaba con un error muy raro: no era capaz de resolver la dirección del repositorio:

root@localhost:/# apt-get update
Err:1 http://ftp.debian.org/debian sid InRelease
  Temporary failure resolving 'ftp.debian.org'
Reading package lists... Done
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/InRelease  Temporary failure resolving 'ftp.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.

Sin embargo, un PING o un WGET a dicha dirección sí funcionaba perfectamente, por lo que no parecía ser un problema de la red en sí. Probé a poner manualmente la IP en el fichero /etc/hosts y entonces el error pasó a ser otro:

root@localhost:/# apt-get update
Ign:1 http://ftp.debian.org/debian sid InRelease
Ign:2 http://ftp.debian.org/debian sid Release
Ign:3 http://ftp.debian.org/debian sid/main armhf Packages.diff/Index
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:5 http://ftp.debian.org/debian sid/main Translation-en.diff/Index
Ign:6 http://ftp.debian.org/debian sid/contrib armhf Packages.diff/Index
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:8 http://ftp.debian.org/debian sid/contrib Translation-en.diff/Index
Ign:9 http://ftp.debian.org/debian sid/non-free armhf Packages.diff/Index
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:11 http://ftp.debian.org/debian sid/non-free Translation-en.diff/Index
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Ign:12 http://ftp.debian.org/debian sid/main armhf Packages
Ign:4 http://ftp.debian.org/debian sid/main all Packages
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Ign:14 http://ftp.debian.org/debian sid/contrib armhf Packages
Ign:7 http://ftp.debian.org/debian sid/contrib all Packages
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Ign:16 http://ftp.debian.org/debian sid/non-free armhf Packages
Ign:10 http://ftp.debian.org/debian sid/non-free all Packages
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Err:12 http://ftp.debian.org/debian sid/main armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:13 http://ftp.debian.org/debian sid/main Translation-en
Err:14 http://ftp.debian.org/debian sid/contrib armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:15 http://ftp.debian.org/debian sid/contrib Translation-en
Err:16 http://ftp.debian.org/debian sid/non-free armhf Packages
  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
Ign:17 http://ftp.debian.org/debian sid/non-free Translation-en
Reading package lists... Done
W: The repository 'http://ftp.debian.org/debian sid Release' does not have a Release file.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/main/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/contrib/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
W: Failed to fetch http://ftp.debian.org/debian/dists/sid/non-free/binary-armhf/Packages  Could not create a socket for 130.89.148.12 (f=2 t=1 p=6) - socket (13: Permission denied)
E: Some index files failed to download. They have been ignored, or old ones used instead.

Esto era otra cosa, claramente: por algún motivo, APT no conseguía permisos para acceder a la red. Parecía un problema de SELINUX, pero realmente estaba en modo permisivo, por lo que no debería estar fallando.

Tras rebuscar por todas partes, descubrí una entrada donde se comentaba el mismo problema para Kali-Rolling en Android: la clave parece estar en que el usuario _apt está en el grupo nogroup, y eso en Android supone no tener acceso a nada, ni siquiera a la red. Para solucionarlo, sólo tuve que editar el fichero /etc/passwd y cambiar el grupo de _apt de 65534 (nogroup) a 3004, que aunque es un grupo que no existe en mi sistema, es suficiente para que todo vuelva a funcionar como debe.

Usando DBus desde lenguaje C y JavaScript

diciembre 11th, 2015

Trabajar con DBus desde Python o Vala es muy sencillo: esconden la complejidad del protocolo de manera que uno tiene la ilusión de estar haciendo una llamada local. Sin embargo, históricamente, trabajar con DBus desde C ha sido considerado un peñazo de dimensiones colosales. Sin embargo, desde la llegada de la biblioteca GDBus, que integra DBus dentro de GIO la cosa se ha simplificado bastante, sobre todo en aquellos casos en los que simplemente queremos llamar de manera síncrona a un método remoto.

Debido a algunos cambios que estuve haciendo en Panther Launcher, necesité poder hacer precisamente eso: llamar a un método remoto mediante DBus desde C, para implementarlo dentro del applet para Gnome Flashback. Dado que no encontré documentación sencilla para este detalle concreto (esto es, sin usar entre medias un bucle de eventos), voy a comentar como lo hice, por si le es útil a alguien más.

Lo primero que necesitamos para poder llamar a un método remoto mediante DBus es la interfaz correspondiente donde se define dicho método. Esta interfaz se puede obtener de manera muy sencilla mediante dbus-send y las capacidades de introspección de DBus:

dbus-send --session --type=method_call --print-reply --dest=com.rastersoft.panther.remotecontrol /com/rastersoft/panther/remotecontrol org.freedesktop.DBus.Introspectable.Introspect

En este ejemplo obtenemos las interfaces disponibles en el objeto  /com/rastersoft/panther/remotecontrol, del servicio  com.rastersoft.panther.remotecontrol. El resultado es éste:

freedesktop.DBus.Introspectable.Introspect
method return time=1449871353.404951 sender=:1.1222 -> destination=:1.1246 serial=104 reply_serial=2
 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.46.2 -->
<node>
 <interface name="org.freedesktop.DBus.Properties">
   <method name="Get">
     <arg type="s" name="interface_name" direction="in"/>
     <arg type="s" name="property_name" direction="in"/>
     <arg type="v" name="value" direction="out"/>
   </method>
   <method name="GetAll">
     <arg type="s" name="interface_name" direction="in"/>
     <arg type="a{sv}" name="properties" direction="out"/>
   </method>
   <method name="Set">
     <arg type="s" name="interface_name" direction="in"/>
     <arg type="s" name="property_name" direction="in"/>
     <arg type="v" name="value" direction="in"/>
   </method>
   <signal name="PropertiesChanged">
     <arg type="s" name="interface_name"/>
     <arg type="a{sv}" name="changed_properties"/>
     <arg type="as" name="invalidated_properties"/>
   </signal>
 </interface>
 <interface name="org.freedesktop.DBus.Introspectable">
   <method name="Introspect">
     <arg type="s" name="xml_data" direction="out"/>
   </method>
 </interface>
 <interface name="org.freedesktop.DBus.Peer">
   <method name="Ping"/>
   <method name="GetMachineId">
     <arg type="s" name="machine_uuid" direction="out"/>
   </method>
 </interface>
 <interface name="com.rastersoft.panther.remotecontrol">
   <method name="DoPing">
     <arg type="i" name="v" direction="in"/>
     <arg type="i" name="result" direction="out"/>
   </method>
   <method name="DoShow">
   </method>
 </interface>
</node>

Vemos que aparecen varias interfaces, dentro de cada una varios métodos, y dentro de cada método puede haber cero o más parámetros, y cero o más valores devueltos. Para nuestros propósitos no vamos a necesitar todo, sino sólo la interfaz com.rastersoft.panther.remotecontrol, así que eliminaremos el resto de entradas (y las líneas superiores descriptivas, sólo queremos el XML), y nos quedará esto:

<node>
 <interface name="com.rastersoft.panther.remotecontrol">
   <method name="DoPing">
     <arg type="i" name="v" direction="in"/>
     <arg type="i" name="result" direction="out"/>
   </method>
   <method name="DoShow">
   </method>
 </interface>
</node>

Este fichero XML es el que describe las llamadas que vamos a implementar.

Si queremos llamar a alguno de estos métodos desde JavaScript (por ejemplo, para hacer una llamada desde una extensión de Gnome Shell), sólo necesitaremos hacer lo siguiente:

const Gio = imports.gi.Gio;

const MyIface = '<node>\
 <interface name="com.rastersoft.panther.remotecontrol">\
  <method name="DoShow" />\
  <method name="DoPing" >\
   <arg name="n" direction="in" type="i"/>\
   <arg name="response" direction="out" type="i"/>\
  </method>\
 </interface>\
</node>';

const MyProxy = Gio.DBusProxy.makeProxyWrapper(MyIface);

let instance = new MyProxy(Gio.DBus.session, 'com.rastersoft.panther.remotecontrol','/com/rastersoft/panther/remotecontrol');
instance.DoShowSync();
instance.DoPingSync(0);

Primero importamos Gio para tener acceso a GDBus. Después insertamos el XML con la interfaz en una variable, y creamos un Proxy DBus con ella. Finalmente, cada vez que queramos acceder a un objeto (en este caso /com/rastersoft/panther/remotecontrol) de un servicio DBus (en este caso com.rastersoft.panther.remotecontrol), sólo tenemos que crear una instancia del proxy anterior, el cual nos permitirá llamar a los métodos definidos en la interfaz.

Cabe recalcar que aquí estoy llamando a los métodos con su nombre terminado en Sync. Eso significa que la llamada será bloqueante, y no retornará hasta que se reciba la respuesta del otro extremo. Es posible hacer llamadas asíncronas, pero no lo he investigado y no voy a entrar ahí.

Hasta aquí JavaScript, que es la parte más sencilla. Ahora llega el turno de como hacer esto mismo desde C.

GDbus tiene un generador de código que nos simplifica el trabajo. Para usarlo basta con grabar en un fichero el XML anterior (por ejemplo, remotecontrol.xml) y llamar a gdbus-codegen para que construya el código necesario para poder llamar a dichos métodos:

gdbus-codegen --c-generate-object-manager --generate-c-code dbus remotecontrol.xml

Este comando creará dos ficheros: dbus.c y dbus.h (se utiliza el nombre indicado en –generate-c-code) a partir del fichero remotecontrol.xml. Existen algunos parámetros extra, como –c-namespace, que permite especificar un prefijo para todas las funciones que se generen, y así evitar choques de nombres.

En dichos ficheros tendremos un montón de código ya escrito, y es precisamente este código el que nos simplifica el proceso, pues no tendremos que escribirlo nosotros. Una vez que lo tenemos, sólo hemos de llamarlo así:

#include "dbus.h"

GError *error = NULL;

ComRastersoftPantherRemotecontrol *proxy;

proxy = com_rastersoft_panther_remotecontrol_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                  G_DBUS_PROXY_FLAGS_NONE,
                                                  "com.rastersoft.panther.remotecontrol",
                                                  "/com/rastersoft/panther/remotecontrol",
                                                  NULL, /* GCancellable */
                                                  &error);
if (proxy != NULL) {
    error = NULL;
    retval = com_rastersoft_panther_remotecontrol_call_do_show_sync(proxy,NULL,&error);

    ...

    error = NULL;
    gint value;
    retval = com_rastersoft_panther_remotecontrol_call_do_ping_sync(proxy,0,&value,NULL,&error);
}

Tras incluir el fichero dbus.h para disponer de las llamadas, lo primero que hacemos es crear un puntero de tipo GError para recibir las excepciones que se produzcan.

A continuación creamos de manera síncrona un proxy de la interfaz concreta que queremos utilizar. Vemos que lo estamos haciendo para el bus de sesión, para el objeto /com/rastersoft/panther/remotecontrol del servicio com.rastersoft.panther.remotecontrol. Es fundamental que el puntero GError esté inicializado a NULL, pues de no hacerlo la llamada fallará.

Y una vez que tenemos dicho proxy ya podemos llamar a los métodos remotos. Vemos que cada llamada está formada por el nombre de la interfaz, más _call_, más el nombre del método, y en este caso, como la llamada queremos que se bloquee hasta que llegue la respuesta, termina en _sync. El primer parámetro es el proxy, y los dos últimos un puntero a la función para cancelar la llamada (que ponemos a NULL para no complicarnos la vida) y un puntero a GError, que también debe estar inicializado a NULL antes de llamar a la función.

Entre medias se introducen, en el mismo orden en que están definidos en el XML, los parámetros de entrada (que se pasan por valor) y punteros para los parámetros de salida.

Para compilarlo hay que utilizar pkg-config gio-2.0 gio-unix-2.0 para que se añadan las cabeceras y bibliotecas necesarias.

Y ya está, con esto podemos por fin llamar de manera sencilla un método DBus desde lenguaje C.

Nueva version del entorno Gentoo para WebTV

julio 30th, 2015

Acabo de subir a mi web la versión 2 del entorno de desarrollo Gentoo para el WebTV. Con ella ya debería ser posible instalar una versión decente de transmission. Ahora voy a empezar a trabajar en aplicaciones extra.

Peleandome con Python 3.4

julio 30th, 2015

Dado que quiero tener soporte para gnutls, tuve que cambiar el USE de mi distribución a:

USE="${ARCH} -pam -fortran -sanitize -iptables -static -systemd -mdev gnutls internal-glib -caps -filecaps -X -gtk -qt -tk"

Las siete últimas adiciones fueron para no añadir nada de entorno gráfico (pues no tiene sentido en el WebTV) y para asegurar de que las nuevas bibliotecas necesarias para incluir gnutls se puedan compilar. Hubo varios problemillas, pero uno a uno los fui resolviendo.

Por desgracia, el último escollo estaba en python. Cuando compilé el sistema la versión estable era la 3.3, pero ahora ya salió la 3.4. El problema es que se negaba a compilar, dando un error raro:

Python build finished successfully!
The necessary bits to build these optional modules were not found:
    _tkinter
To find the necessary bits, look in setup.py in detect_modules() for the module's name.
Failed to build these modules:
    _socket               _ssl

Decía que se había compilado correctamente, pero emerge devolvía un error. Al principio pensaba que el problema estaba en tkinter, el módulo gráfico de python, pero no tenía sentido porque había especificado que no quería ni tk, ni X ni nada relacionado con un entorno gráfico. Entonces, revisando el log, vi que en medio de la compilación había este error:

/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.o
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c: In function 'makesockaddr':
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1175:14: error: dereferencing pointer to incomplete type
         if (a->can_ifindex) {
              ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1176:32: error: dereferencing pointer to incomplete type
             ifr.ifr_ifindex = a->can_ifindex;
                                ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1183:38: error: dereferencing pointer to incomplete type
                                     a->can_family);
                                      ^
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c: In function 'getsockaddrlen':
/tmp/portage/dev-lang/python-3.4.3/work/Python-3.4.3/Modules/socketmodule.c:1802:28: error: invalid application of 'sizeof' to incomplete type 'struct sockaddr_can'
         *len_ret = sizeof (struct sockaddr_can);
                            ^
building '_ssl' extension

¿CAN? ¿En el módulo de sockets? Bastante raro. Rebuscando encontré que, efectivamente, desde el núcleo 2.6.25 hay soporte para el bus CAN; sin embargo, mi núcleo (y sus cabeceras) es el 2.6.22. ¿Por qué se empeñaba en incluir soporte? Por otro lado, python 3.3 también trae de serie soporte para bus CAN, pero esa versión sí compilaba bien. ¿Qué estaba pasando?

Al final descubrí que el núcleo 2.6.22 tiene algo de soporte del bus CAN, pero parece que no el suficiente, y eso lía a python 3.4.

La solución que encontré fue editar el fichero /usr/include/bits/socket.h, y comentar la línea donde se define AF_CAN:

//#define AF_CAN PF_CAN

Y con eso, por fin, pude compilar absolutamente todo, listo para empezar a preparar el sistema que va a llevar definitivamente.

No puede caber aqui

julio 29th, 2015

Llevo un par de días incapaz de actualizar el paquete binutils usando la emulación de mipsel sobre mi PC. Es una cosa misteriosa, pues daba un error al compilar el linker gold. Tras intentar hacerlo a mano, me devolvió como mensaje de error:

(for i in `seq 1 70000`; do 
  echo "int var_$i __attribute__((section("section_$i"))) = $i;"; 
done) > many_sections_define.h.tmp
make: execvp: /bin/sh: Argument list too long

¿Argument list too long? Un error bastante extraño, sin duda. Y más en el propio make. Encima, si eliminaba todo ese código y metía un simple echo, el error persistía. ¿Qué estaba pasando?

Tras probar de todo y rebuscar por todas partes, por fin encontré el problema: qemu define un tamaño máximo para la línea de comandos (MAX_ARG_PAGES) demasiado pequeño para compilar binutils, y por eso casca. Encima, dicho valor se define a piñón en el código fuente, por lo que la única solución consiste en bajarse los fuentes de qemu, modificar el fichero linux-user/qemu.h para aumentar a 64 o más las páginas reservadas para la línea de comandos (yo puse 129), y compilarlo todo con:

./configure --static --target-list=mipsel-linux-user
make

Con esto ya tendremos en mipsel-linux-user/qemu-mipsel el ejecutable estático, el cual podemos copiar dentro de la carpeta de nuestra máquina virtual como usr/bin/qemu-mipsel-static. Y con esto deberíamos ser capaces de compilar cualquier cosa.

(Si, el título es por esta escena 🙂 )

Actualizando la Gentoo del WebTV desde el PC

julio 27th, 2015

Siguiendo con lo que hice el otro día, ahora quería empezar a instalar cosas en el sistema Gentoo del WebTV. Por desgracia la cosa no es tan sencilla porque enseguida pide actualizar algunos paquetes, lo cual tarda mucho tiempo al hacer la compilación en el propio dispositivo. Y por si fuera poco, con alguno necesita tanta memoria que, directamente, casca a la mitad de la compilación.

Afortunadamente hay una forma de hacer todo esto directamente en un PC, pero haciendo creer al sistema Gentoo que está corriendo de forma nativa en un sistema Mipsel. Para ello sólo necesitamos QEMU.

Para empezar necesitamos el binario /usr/bin/qemu-mipsel-static, así que buscamos en qué paquete está disponible y lo instalamos en nuestro sistema. En el caso de Debian, el paquete es qemu-user-static. Este binario nos permite ejecutar binarios de la arquitectura deseada, pero (y esto es lo interesante) encaminando las llamadas al núcleo directamente al de la máquina física, con lo que no necesitamos compilar otro núcleo.

Ahora descomprimimos el fichero entorno_gentoo_mipsel.tar.bz2 en un directorio (por ejemplo, en /tmp), y descomprimimos en lugar adecuado (en nuestro ejemplo, en /tmp/bg_apps/usr) también el fichero de portage, tras bajarlo. Por último, copiamos /usr/bin/qemu-mipsel-static dentro de nuestro sistema mipsel (en nuestro caso, en /tmp/bg_apps/usr/bin/). Con esto hemos terminado los preparativos.

Ahora lanzamos nuestra sesión mediante:

sudo systemd-nspawn -u 1000 -D /tmp/bg_apps /bin/bash

De esta manera lanzamos nuestra sesión como usuario 1000 (que es el que usa el WebTV cuando se arranca una sesión en segundo plano). Además, gracias a que copiamos el binario de qemu, los binarios de mipsel se ejecutarán directamente, sin ningún problema, como si fuesen nativos de nuestro sistema (por increíble que parezca).

Una vez hecho esto ya podemos actualizar el sistema y demás, sin temor a quedarnos sin memoria y a mucha más velocidad. Pero echad un vistazo también a esta entrada posterior: http://blog.rastersoft.com/?p=1645.

Generando Gentoo para el WebTV

julio 19th, 2015

Nota: actualizado el parche para BusyBox.

Estos días estoy bastante liado con el trabajo-que-paga-las-facturas, pero por suerte he podido sacar un rato para cacharrear. Me he puesto con el WebTV (que tengo bastante abandonado desde que me compré la Raspberry Pi) y he decidido intentar meter un sistema “decente”. ¿A qué me refiero? Pues a que, por defecto, la máxima versión de Debian que puede correr es wheezy, que ya es old-stable. La estable actual (jessie) necesita un núcleo más reciente, y se niega a trabajar con el que trae el WebTV (2.6.22).

Ante esto decidí probar con Gentoo, a ver si conseguía compilarlo todo. A continuación indicaré como lo hice.

NOTA: para los que no quieran leerse este tocho, en mi web está disponible este entorno Gentoo completo para WebTV, ya compilado y listo para usar.

Para empezar, me bajé la stage 3 de Gentoo para X86_64, bajé también la última lista de paquetes de portage, y descomprimí ésta última en /usr de la stage 3. Con ello ya pude lanzar un contenedor de gentoo con

sudo systemd-nspawn -D /directorio/con/la/stage3 /bin/bash

Aquí toca primero preparar el sistema para hacer crossdev. Esto se puede repasar en una entrada anterior: Emergiendo.

Una vez dentro intenté hacer un crossdev para compilar una gentoo para mipsel usando

crossdev --kernel 2.6.22 -t mipsel -v

Por desgracia fallaba: se empeñaba en utilizar las cabeceras de la versión 2.4.36. ¿Qué pasaba? Pues que aunque en los repositorios de Gentoo sí existen las cabeceras del kernel 2.6.22, éstas no están disponibles en la lista de ebuilds de portage.

Ante esto empecé a buscar y probar, y finalmente con la ayuda de la gente de IRC del canal #Gentoo-kernel conseguí el ebuild de las cabeceras para la versión 2.6.22-r2. Sin embargo tuve que grabarlo en /usr/portage/sys-kernel/linux-headers con el nombre linux-headers-2.6.22-r3, pues la R3 es la versión disponible en el repositorio.

Tras ello actualicé el fichero Manifest para que encontrase el nuevo ebuild con

ebuild /usr/portage/sys-kernel/linux-headers/linux-headers-2.6.22-r3.ebuild manifest

Probé de nuevo a generar el crossdev pero seguía intentando usar la versión 2.4.36… porque la versión 2.6.22-r3 es posterior a la 2.6.22 a secas. Cambiando el parámetro en crossdev solucionó el problema.

crossdev --kernel 2.6.22-r3 -t mipsel -v

Por desgracia, ahora me encontraba con otro: ocurría un error durante la instalación de las cabeceras:

HOSTCC  scripts/unifdef
scripts/unifdef.c:209:25: error: conflicting types for 'getline'
 static Linetype         getline(void);
                         ^
In file included from scripts/unifdef.c:70:0:
/usr/include/stdio.h:678:20: note: previous declaration of 'getline' was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^
scripts/Makefile.host:118: recipe for target 'scripts/unifdef' failed
make[1]: *** [scripts/unifdef] Error 1
Makefile:927: recipe for target 'headers_install' failed
make: *** [headers_install] Error 2
emake failed

Tocaba buscar más soluciones. Afortunadamente esta era sencilla: bastaba con editar el fichero unifdef.c y sustituir todas las ocurrencias de getline por otra cosa, como por ejemplo get_line. Preparé el siguiente parche para ello:

--- a/scripts/unifdef.c
+++ b/scripts/unifdef.c
@@ -206,7 +206,7 @@ static void             done(void);
 static void             error(const char *);
 static int              findsym(const char *);
 static void             flushline(bool);
-static Linetype         getline(void);
+static Linetype         get_line(void);
 static Linetype         ifeval(const char **);
 static void             ignoreoff(void);
 static void             ignoreon(void);
@@ -512,7 +512,7 @@ process(void)
 
 	for (;;) {
 		linenum++;
-		lineval = getline();
+		lineval = get_line();
 		trans_table[ifstate[depth]][lineval]();
 		debug("process %s -> %s depth %d",
 		    linetype_name[lineval],
@@ -526,7 +526,7 @@ process(void)
  * help from skipcomment().
  */
 static Linetype
-getline(void)
+get_line(void)
 {
 	const char *cp;
 	int cursym;

Y entonces me encontré con el problema de como aplicarlo durante la generación del crossdev. La cosa no era sencilla, porque se empeña en comprobar los valores de sha256, sha512 y whirlpool de todo lo que baje. En teoría se pueden añadir parches manualmente en /etc/portage/patches, pero tras probar de todo no conseguí que funcionase, así que al final fui a la solución cazurra y metí el comando de parcheado directamente en el ebuild. Para ello edité el fichero /usr/portage/sys-kernel/linux-headers/linux-headers-2.6.22-r3.ebuild y lo dejé como sigue:

# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/sys-kernel/linux-headers/Attic/linux-headers-2.6.22-r2.ebuild,v 1.11 2008/04/12 22:24:36 vapier dead $

ETYPE="headers"
H_SUPPORTEDARCH="alpha amd64 arm cris hppa m68k mips ia64 ppc ppc64 s390 sh sparc x86"
inherit kernel-2
detect_version

echo ${PV}
echo ${PATCH_VER}

PATCH_VER="3"
SRC_URI="mirror://gentoo/gentoo-headers-base-${PV}.tar.bz2"
[[ -n ${PATCH_VER} ]] && SRC_URI="${SRC_URI} mirror://gentoo/gentoo-headers-${PV}-${PATCH_VER}.tar.bz2"

KEYWORDS="-* alpha amd64 arm hppa ia64 m68k mips ppc ppc64 s390 sh sparc x86"

DEPEND="dev-util/unifdef"
RDEPEND=""

S=${WORKDIR}/gentoo-headers-base-${PV}

src_unpack() {
        unpack ${A}
        cd "${S}"
        [[ -n ${PATCH_VER} ]] && EPATCH_SUFFIX="patch" epatch "${WORKDIR}"/${PV}
        patch -p1 < /getline.patch
}

src_install() {
        kernel-2_src_install
        cd "${D}"
        egrep -r '[[:space:]](asm|volatile|inline)[[:space:](]' .
        headers___fix $(find -type f)
}

src_test() {
        make ARCH=$(tc-arch-kernel) headers_check || die
}

Luego copié el texto del parche en el fichero /getline.patch y actualicé de nuevo el manifest. Ahora, siempre que se intente instalar el paquete, se parcheará correctamente.

A intentarlo otra vez… y otra vez falla, esta vez porque la versión 2.20 de glibc necesita, al menos, un kernel 2.6.32. Como el núcleo disponible es el que es, toca probar con una versión anterior. La 2.19r1 fue suficiente:

crossdev --kernel 2.6.22-r3 --l 2.19-r1 -t mipsel -v

Finalmente, con esto ya es capaz de compilar glibc. Ahora vienen las curvas, porque no es capaz de compilar el soporte de Fortran para el GCC, ni las pruebas sanity ni algunas cosas más, así que toca armarse de paciencia e ir probando opciones de USE hasta que todo compile. El resultado final es que hay que usar la siguiente línea (añadí el -X para reducir las dependencias, pues en el WebTV no es necesario):

USE="-fortran -sanitize -X" CFLAGS="-O2 -pipe" crossdev --kernel 2.6.22-r3 --l 2.19-r1 -s4 -t mipsel -v

Inicializamos los wrappers de compilación cruzada…

emerge-wrapper --target mipsel-unknown-linux-gnu --init

Y ya tenemos el sistema de compilación cruzada para MIPSel. Ahora toca configurar el entorno y preparar el sistema. Para ello lo primero es borrar el enlace /usr/mipsel-unknown-linux-gnu/etc/portage/make.profile (que, por defecto, apunta a /usr/portage/profiles/embedded) y sustituirlo por uno que apunte a /usr/portage/profiles/ default/linux/mips/13.0/mipsel.

Una vez hecho esto editamos /usr/mipsel-unknown-linux-gnu/etc/portage/make.conf, y ahí modificamos la línea donde se define el USE para añadir, al menos, -fortran -sanitize -X -iptables. También podemos añadir, opcionalmente, un MAKEOPTS=”-jX” (siendo X el número de núcleos de nuestro procesador más uno), para que la compilación sea más rápida.

También tenemos que editar el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/package.mask, y añadir estas líneas:

>sys-libs/glibc-2.19-r1
>sys-kernel/linux-headers-2.6.22-r3

Con ellas evitamos que instale versiones posteriores de ambos paquetes, que harían que el sistema dejase de funcionar en nuestro WebTV.

Con esto ya podemos intentar generar nuestro sistema base con

emerge-mipsel-unknown-linux-gnu system

Si falla al compilar Busybox, es probable que haya alguna opción que no le gusta. En ese caso hay que editar su fichero .ebuild en /usr/portage/sys-apps/busybox/busybox-X.Y.Z.ebuild, añadir las opciones de configuración que se quieren activar o desactivar, y luego ejecutar ebuild /usr/portage/sys-apps/busybox/busybox-X.Y.Z.ebuild manifest para actualizar el manifest. En mi caso, el problema es que se activan por defecto el soporte de UBIFS y de I2C, cosa que no parece gustarle, así que para eliminarlo tuve que añadir las siguientes líneas en el sitio adecuado del ebuild:

busybox_config_option n I2CGET
busybox_config_option n I2CSET
busybox_config_option n I2CDUMP
busybox_config_option n I2CDETECT
busybox_config_option n UBIATTACH
busybox_config_option n UBIDETACH
busybox_config_option n UBIMKVOL
busybox_config_option n UBIRMVOL
busybox_config_option n UBIRSVOL
busybox_config_option n UBIUPDATEVOL

Con suerte, en un par de días este parche ya estará incluido en los repositorios oficiales.

Otro problema, esta vez más grave, es con Perl: se trata de un paquete al que no le gusta que le hagan compilación cruzada. El resultado es que, simplemente, no podemos instalarlo así. La solución consiste en, de momento, hacer creer al sistema que sí está instalado, e instalarlo manualmente desde el sistema final una vez que ya estamos en el equipo. Para hacer esto basta con editar el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/profile/package.provided y poner, en cada línea, los paquetes que queremos marcar como instalados. Hice lo mismo con los paquetes de UDev, que tampoco los necesito. En mi caso su contenido fue:

dev-lang/perl-5.22
virtual/perl-Data-Dumper-2.158.0
perl-core/File-Temp-0.230.400-r1
virtual/perl-File-Temp-0.230.400-r3
dev-perl/Text-Unidecode-1.230.0
dev-perl/libintl-perl-1.240.0
virtual/perl-File-Spec-3.560.0
dev-perl/Unicode-EastAsianWidth-1.330.0-r1
sys-fs/udev-222
virtual/udev-217
sys-fs/udev-init-scripts-30
virtual/dev-manager-0

Pero, obviamente, depende de la versión de portage y de los paquetes disponibles.

Tras instalar todo esto, si el equipo es de 64 bits nos encontraremos con que nos ha metido varios elementos de python en /usr/lib64, cuando todo debería ir en /usr/lib. Es por esto que debemos mover todos los ficheros del primero al segundo.

Ahora ya podemos copiar el contenido de /usr/mipsel-unknown-linux-gnu/ a un disco duro externo (dentro de una carpeta llamada bg_apps), añadir un fichero init y otro vacío llamado no_base_system, y ya podemos arrancar nuestro sistema Gentoo en el WebTV.

Pero aún no hemos acabado. Para empezar, es necesario hacer el siguiente enlace cada vez que se encienda el equipo:

ln -s /proc/self/fd /dev/fd

para que emerge funcione correctamente. También es recomendable editar el fichero /etc/portage/make.conf y eliminar la opción de compilación -pipe, pues consume más memoria, y en un equipo relativamente limitado como el WebTV nos puede dar problemas con compilaciones muy tochas.

Por otro lado, tenemos que comentar las entradas de Perl que pusimos en el fichero /usr/mipsel-unknown-linux-gnu/etc/portage/profile/package.provided, y procer a instalarlos todos con emerge.

No hay que olvidar que, debido a la gran cantidad de ficheros que tiene el directorio /usr/portage, el arranque de la sesión en segundo plano del WebTV tardará bastante tiempo (en torno a un minuto), pues antes de lanzar la sesión, el sistema revisa todos y cada uno de los ficheros para asegurarse de que no hay “cosas raras”.


Utilizamos cookies para garantizar que tenga la mejor experiencia en nuestro sitio web.