En la entrada anterior expliqué como hice para arrancar una Debian en mi dispositivo AndroidTV. Hoy estuve retocando el código para hacerlo más flexible y corregir algunos problemas de tipo práctico que he encontrado al empezar a usarlo de manera regular.
Cambios en launch_debian
El primer cambio ha sido en el propio launch_debian. Este binario es el encargado de detectar cuando se enchufa un disco externo y de lanzar la aplicación runlinux.sh que esté en su directorio raíz. El primer problema que resolví en ella es permitir pasarle los directorios en donde montar la unidad externa, en donde buscar los dispositivos conectados, y el tipo de sistema de archivos que se quiere montar. Todo esto se puede especificar ahora desde la línea de comandos si se desea (si no se indica nada, se utilizan los valores por defecto que comenté en la anterior entrada).
El segundo cambio es añadir una FIFO para poder ejecutar comandos desde el entorno externo al chroot. El principal motivo es para poder ejecutar un apagado ordenado, pues desde dentro del entorno no se puede ejecutar, por ejemplo, un shutdown -h now. Para ello se crea la FIFO /dev/chroot_ext_control, que esperará a recibir algún comando. De momento admite dos:
- halt mata todos los procesos que estén accediendo a algún fichero en la unidad, y una vez hecho esto, la vuelve a montar como sólo-lectura, para que no se corrompan los datos al apagar.
- reboot lo mismo que halt, pero luego reinicia la máquina
Estos comandos se pueden emitir simplemente con echo reboot > /dev/chroot_ext_control o echo halt > /dev/chroot_ext_control.
El nuevo código se puede descargar con este enlace.
Los parámetros que acepta la nueva versión desde la línea de comandos son:
- -o opciones de montaje. La cadena que sigue se añade tal cual al comando mount, junto con -o. Esto permite añadir opciones como noatime.
- -m ruta de montaje. La cadena que sigue indica la ruta donde se montará la unidad externa. Por defecto es /system/debian.
- -t tipo de sistema de ficheros. La cadena que sigue indica el tipo de sistema de ficheros que se pasará a mount. Por defecto es ext4.
- -c ruta de dispositivos. La cadena que sigue indica la ruta donde aparecen los ficheros de dispositivo, como sda1. Por defecto es /dev/block.
- -f ruta y fichero para la FIFO. La cadena que sigue indica la ruta y el fichero para la FIFO de control. Por defecto es /dev/chroot_ext_control.
El motivo de poner la FIFO de control en /dev es que, al ser un sistema de ficheros en RAM, no habrá interferencias con otros posibles ficheros, y además es accesible con el mismo nombre desde dentro y fuera del entorno chroot.
Cambios en el lanzador principal
El script situado en /system/etc/install-recovery.sh también ha cambiado ligeramente. Ahora es así:
#!/system/bin/sh cp /system/bin/launch_debian /dev /dev/launch_debian &
El motivo de hacer esto es que ahora launch_debian ya no muere al lanzar el entorno Debian, sino que sigue en marcha para responder a las peticiones de comandos emitidas a través de la FIFO. Eso significa que si queremos actualizarlo no lo podemos hacer directamente, porque el fichero estará bloqueado. Por eso primero copiamos el ejecutable a /dev (que tiene la ventaja de ser un disco RAM, y dado que el ejecutable es muy pequeño, no supone un desperdicio de memoria) y lo ejecutamos desde ahí: de esa forma el ejecutable original nunca se bloquea, y podemos reemplazarlo en caliente y luego reiniciar para que se utilice la nueva versión.
Cambios en el lanzador de la jaula chroot
El script situado en el disco duro, runlinux.sh, también ha cambiado un poco. Ahora es así:
#!/system/bin/sh # stop the Android system stop sleep 1 # stop the daemons to ensure that # they don't disturb the debian system # (can't kill them because INIT would # relaunch them) # Also allows to send them to SWAP busybox killall -SIGSTOP netd busybox killall -SIGSTOP vold busybox killall -SIGSTOP displayd busybox killall -SIGSTOP ueventd busybox killall -SIGSTOP debuggerd busybox killall -SIGSTOP rild busybox killall -SIGSTOP drmserver busybox killall -SIGSTOP mediaserver busybox killall -SIGSTOP installd busybox killall -SIGSTOP servicemanager # undo changes to kernel variables echo 0 > /proc/sys/kernel/panic_on_oops echo 18000000 > /proc/sys/kernel/sched_latency_ns echo 3000000 > /proc/sys/kernel/sched_wakeup_granularity_ns echo 0 > /proc/sys/vm/overcommit_memory echo 3000 > /proc/sys/vm/dirty_expire_centisecs echo 1024 > /dev/cpuctl/apps/bg_non_interactive/cpu.shares echo 900000 > /dev/cpuctl/apps/cpu.rt_runtime_us echo 900000 > /dev/cpuctl/apps/bg_non_interactive/cpu.rt_runtime_us # mount proc, sys, dev, dev/pts, dev/cpuctl and /system mount -o bind /proc $1/proc mount -o bind /sys $1/sys mount -o bind /dev $1/dev mount -o bind /dev/pts $1/dev/pts mount -o bind /dev/cpuctl $1/dev/cpuctl mount -o bind /system $1/android export HOME=/root export LD_LIBRARY_PATH= export PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin # set the Framebuffer devices where standard apps expect them cp -a /dev/graphics/* /dev # launch our Debian system /system/bin/busybox chroot $1 /bin/system.sh
El primer cambio está en los distintos comandos echo XXXXXX >…. Estos comandos pretenden restaurar los valores originales en diversas variables del núcleo. Esto es porque Android está orientado hacia aplicaciones de usuario, por lo que, por ejemplo, le quita prioridad a las aplicaciones en segundo plano. Con estas opciones intento deshacer lo que se cambia en el fichero init.rc. Sin embargo es importante indicar que probablemente lo que cambie dependa de cada fabricante, así que cada uno debe mirar qué es lo que se modifica.
El siguiente cambio es que monto también /dev/cpuctl, algo que se me había pasado.
Por último, monto el directorio /system dentro del directorio android de la unidad montada. De esta manera se tiene acceso al sistema Android desde dentro de la jaula chroot, lo que permite, por ejemplo, actualizar el fichero launch_debian.