Linux From Scratch XII: Empaquetando y compilando

Como ya he indicado en mi artículo anterior, en éste os voy a contar la solución que he decidido adoptar para tener cierto grado de gestión de paquetes mientras compilo Linux From Scratch. La idea es usar un sistema que no interfiera con las instrucciones de instalación del capítulo 6 del libro y que no requiera modificar el código fuente de los paquetes.

De todas las técnicas de detección de cambios que os he explicado en mi post anterior, me ha gustado especialmente la de usar unionfs. Se trata de una técnica muy poco intrusiva que sólo requiere montar el sistema de ficheros uniendo el directorio en el que estamos instalando LFS con el directorio en el que vamos a dejar los ficheros del paquete. Si los unimos los dos de forma que queden en /, todo funciona de forma transparente. Se puede hacer la instalación tal cual indican las instrucciones y, cuando desmontemos, tendremos los ficheros en un directorio separado que se puede comprimir e instalar en nuestro LFS. Esto se puede conseguir haciendo un chroot al punto de montaje.

Esta técnica tiene un par de inconvenientes, pero ninguno de ellos es importante. En primer lugar, si tenemos otas particiones montadas en algún directorio que descienda de /, como puede ser /home o /usr, estas particiones no serán visibles desde unionfs, lo que hará que no funcione la instalación. Sin embargo, de momento no importa porque no tenemos más particiones montadas. Si queremos instalar nuestro sistema con varios puntos de montaje, siempre podemos usar una máquina virtual para generar los paquetes.

El otro problema es que si hay otros procesos escribiendo además de la instalación, los cambios que hagan estos procesos también se empaquetarán. Tampoco nos importa de momento, porque no vamos a tener ningún otro proceso escribiendo en el directorio en el que hemos montado nuestra partición de LFS.

Preparando nuestro sistema

En realidad no voy a usar el sistema de ficheros unionfs, ya que está obsoleto. Voy a usar su sucesor, aufs, que funciona prácticamente igual y tiene algunas ventajas, aunque no necesito aprovecharlas. En ArchLinux, el kernel no viene compilado con soporte para aufs, pero hay un kernel en el AUR que sí que lo tiene. Podemos instalarlo con yaourt y lo compilará automáticamente. Basta con instalar aufs3 y se instalará también el kernel al resolver las dependencias.

[~]$ yaourt -S aufs3 aufs3-util
[~]$ sudo pacman -Rsc linux-aufs_friendly-docs linux-aufs_friendly-headers

Una vez instalado, hay que actualizar el gestor de arranque para que lo reconozca. Si usamos grub se hace así:

[~]$ sudo grub-mkconfig -o /boot/grub/grub.cfg

En caso de estar usando syslinux, hay que editar el fichero /boot/syslinux/syslinux.cfg y añadir las siguientes lineas:

LABEL arch-aufs
        MENU LABEL Arch Linux aufs
        LINUX ../vmlinuz-linux-aufs_friendly
        APPEND root=/dev/sda2 ro quiet
        INITRD ../initramfs-linux-aufs_friendly.img

Y luego lo actualizamos.

[~]$ sudo syslinux-install_update -u

Por supuesto, si usáis una distro distinta de arch, las instrucciones para hacer esto serán diferentes. Supongo que ya las sabréis y, si no, os toca investigar. 🙂

Lo siguiente es arrancar con el nuevo kernel para tener disponible el sistema de ficheros aufs.

Probando el sistema

Una vez instalado el kernel y reiniciada la máquina, vamos a hacer algunas pruebas para ver cómo funciona.

[~]$ sudo -s
[~]# mkdir /mnt/{pkg,union,lfstest}
[~]# touch /mnt/lfstest/prueba
[~]# mount -t aufs none /mnt/union -o dirs=/mnt/pkg:/mnt/lfstest
[~]# ls /mnt/union
prueba
[~]# echo "Introducimos contenido" >> /mnt/union/prueba
[~]# touch /mnt/union/prueba2
[~]# ls /mnt/union
prueba  prueba2
[~]# cat /mnt/union/prueba
Introducimos contenido
[~]# umount /mnt/union
[~]# ls /mnt/union
[~]# ls /mnt/lfstest
prueba
[~]# ls /mnt/pkg/
prueba  prueba2
[~]# cat /mnt/lfstest/prueba
[~]# cat /mnt/pkg/prueba
Introducimos contenido
[~]# rm -rf /mnt/{pkg,lfstest,union}
[~]# exit

Lo que hemos hecho aquí es, en primer lugar, entrar como usuario root y crear tres directorios para hacer pruebas, pkg, union y lfstest, y un fichero en lfstest. Luego montamos un sistema de ficheros aufs en union uniendo pkg y lfstest. Al poner los directorios en ese orden, las escrituras se harán en pkg, pero union mostrará tanto los ficheros de pkg como los de lfstest, como podemos ver en el ls, que de momento nos muestra el fichero que habíamos creado en lfstest.

A continuación, introducimos una linea en el fichero que habíamos creado, pero lo hacemos accediendo a él a través de union, no de lfstest. Luego creamos un fichero prueba2 en union y comprobamos que en union aparecen los dos ficheros que hemos creado y que el fichero prueba tiene el contenido que hemos introducido. Luego desmontamos union.

Ahora viene lo interesante. Después de desmontar, en union no hay ningún fichero, al fin y al cabo sólo era un punto de montaje. Sin embargo vemos que en lfstest sigue estando el fichero que habíamos creado inicialmente y en pkg están los dos ficheros: el inicial y el nuevo. Es más, el fichero prueba de lfstest está vacío, tal cual lo habíamos creado. En cambio, el de pkg tiene el contenido que habíamos introducido.

Lo que ha ocurrido es que, no sólo los nuevos ficheros escritos en union se han escrito realmente en pkg, sino que los ficheros de lfstest que hemos modificado, se han copiado a pkg y se han modificado allí, dejando la copia original intacta. Ideal para lo que queremos hacer.

Creando los scripts necesarios

Y ahora viene el plato fuente. El script que he preparado para hacer la instalación es este:

#!/bin/bash

LFSROOT=sdb3
LFSSWAP=sdb2

LFS=/mnt/lfs
PKGMNT=/mnt/pkg

PKGNAME="$1"

mkdir "$LFS"
mount /dev/$LFSROOT "$LFS" || exit
swapon /dev/$LFSSWAP
umask 022

if [ -n "$PKGNAME" ]; then
    mkdir "$PKGMNT"
    mount -t tmpfs tmpfs "$PKGMNT"
    mount -t aufs none "$LFS" -o dirs="$PKGMNT":"$LFS" || exit
fi

mount --bind /dev "$LFS/dev"
mount -t devpts devpts "$LFS/dev/pts"
mount -t proc proc "$LFS/proc"
mount -t sysfs sysfs "$LFS/sys"
mount -t tmpfs shm "$LFS/dev/shm"

ln -sf "$LFS" /tools

chroot "$LFS" /tools/bin/env -i \
    HOME=/root                  \
    TERM="$TERM"                \
    PS1='\u:\w\$ '              \
    PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin \
    MAKEFLAGS=-j5               \
    HISTFILE=                   \
    /tools/bin/bash --login +h

rm /tools

umount "$LFS/dev/shm"
umount "$LFS/sys"
umount "$LFS/proc"
umount "$LFS/dev/pts"
umount "$LFS/dev"

shopt -s extglob

if [ -n "$PKGNAME" ]; then
    umount "$LFS"
    OLDDIR="$PWD"
    cd "$PKGMNT"
    tar cJf "$OLDDIR/$PKGNAME.txz" !(tools|sources|tmp)
    cd "$OLDDIR"
    umount "$PKGMNT"
    rmdir "$PKGMNT"
fi

swapoff /dev/$LFSSWAP
umount "$LFS"
rmdir "$LFS"

Copiadlo en un directorio que esté en el PATH poniendo como nombre “lfs” y dazle permisos. Es importante colocar en las variables LFSROOT y LFSSWAP que hay al principio del script los nombres de los dispositivos correspondientes a las particiones root y swap de nuestro LFS. El script se encarga de crear los puntos de montaje que necesita, así como el enlace en /tools, así que si ya los teníamos creados de antes podemos borrarlos.

[~]# rmdir /mnt/lfs
[~]# rm /tools

La forma de usar el script es muy sencilla. Hay que lanzarlo siempre con sudo, ya que necesita permisos de root para hacer casi todas las operaciones. Además, el libro indica que los comandos de este capítulo necesitan lanzarse como root, o sea que es un requisito que tendríamos de todos modos. Se puede usar de dos maneras. Si lo lanzáis sin parámetros, montará las particiones de LFS y os dejará en un chroot dentro de ellas. Todos los cambios que hagáis serán permanentes. Por ejemplo:

[~]# sudo lfs

La otra forma es indicando como parámetro el nombre del paquete que vamos a construir. En este caso montará tanto las particiones como el sistema aufs y nos dejará en un chroot en el que podremos hacer todos los cambios que queramos, pero no serán permanentes, sino que, al salir del chroot, todos los ficheros creados o modificados se empaquetarán con el nombre del paquete que le hemos indicado y nuestro LFS quedará tal y como estaba antes. El paquete queda en el directorio en el que estabamos cuando lanzamos el script. Esto sería algo así:

[~]# sudo lfs linux-headers-3.5.2

Esto nos dejaría hacer los cambios que queramos y luego los empaquetaría en el fichero linux-headers-3.5.2.txz. Como el sistema LFS queda sin cambios, nos queda un último toque, que es un script para instalar el paquete binario generado en nuestro LFS. El script es el siguiente:

#!/bin/bash

LFSROOT=sdb3
LFS=/mnt/lfs

PKGNAME="$1"

mkdir "$LFS"
mount "/dev/$LFSROOT" "$LFS" || exit
tar xJf "$PKGNAME" -C "$LFS"
umount "$LFS"
rmdir "$LFS"

Este lo he llamado “lfsinst“. Se lanza con sudo indicando como parámetro el nombre completo de un paquete generado con el anterior (incluída la extensión txz) y lo instala en nuestro LFS. Acordáos de cambiar las variables LFSROOT y LFS con los valores adecuados a vuestro sistema. El uso es algo similar a esto:

[~]# sudo lfsinst linux-headers-3.5.2.txz

Y os instalará el paquete generado anteriormente. Esto es lo que vamos a hacer a continuación. La mejor manera de probar los scripts es usarlos para generar e instalar el primer paquete del capitulo 6 del libro: los headers del kernel.

Instalando los headers del kernel

Ha llegado el momento de seguir con el capítulo 6 del libro, pero lo voy a hacer utilizando los scripts para generar e instalar los paquetes binarios correspondientes a cada paquete de código fuente empezando por los headers del kernel. A partir de ahora no voy a poner la salida de todos los comandos a no ser que me parezca importante. Así no se alargarán tanto los posts.

Primero lanzamos el script lfs indicando el nombre del paquete que vamos a generar.

[~/lfs]$ sudo lfs linux-headers-3.5.2

Ahora entramos en el directorio de código fuente y descomprimimos el paquete correspondiente a los headers.

root:/# cd sources/
root:/sources# tar xvJf linux-3.5.2.tar.xz
root:/sources# cd linux-3.5.2

Ahora podemos seguir las instrucciones del libro paso por paso sin cambiar ni una coma. La única diferencia es que los cambios no van a ser permanentes.

root:/sources/linux-3.5.2# make mrproper
root:/sources/linux-3.5.2# make headers_check
root:/sources/linux-3.5.2# make INSTALL_HDR_PATH=dest headers_install
root:/sources/linux-3.5.2# find dest/include \( -name .install -o -name ..install.cmd \) -delete
root:/sources/linux-3.5.2# cp -rv dest/include/* /usr/include

Ya podemos salir del chroot y comprobar que nos ha generado un paquete. El fichero lfs-tools-base.txz es una copia de seguridad que me he hecho antes de meterme en todo esto. No os lo había dicho antes, pero si queréis hacerla, en este punto todavía estáis a tiempo.

root:/sources/linux-3.5.2# exit
[~/lfs]$ ls
lfs-tools-base.txz  linux-headers-3.5.2.txz

Podemos incluso ver los ficheros que se han incluido en el paquete para comprobar que, efectivamente, son los headers del kernel y nada más.

[~/lfs]$ tar tvJf linux-headers-3.5.2.txz
drwxr-xr-x root/root         0 2013-01-07 18:22 usr/
drwxr-xr-x root/root         0 2013-01-14 19:00 usr/include/
drwxr-xr-x root/root         0 2013-01-14 19:00 usr/include/xen/
-rw-r--r-- root/root      2967 2013-01-14 19:00 usr/include/xen/evtchn.h

...

-rw-r--r-- root/root       375 2013-01-14 19:00 usr/include/asm/vsyscall.h
-rw-r--r-- root/root      9198 2013-01-14 19:00 usr/include/asm/unistd_32.h
-rw-r--r-- root/root      8338 2013-01-14 19:00 usr/include/asm/unistd_64.h
-rw-r--r-- root/root     14669 2013-01-14 19:00 usr/include/asm/unistd_x32.h

Como hasta ahora no hemos hecho realmente ningún cambio, queda un último paso: instalar el paquete. Para ello usamos el segundo script.

[~/lfs]$ sudo lfsinst linux-headers-3.5.2.txz

Y, si queremos, podemos entrar en nuestro LFS para comprobar que los headers se han instalado realmente.

[~/lfs]$ sudo lfs
root:/# cd /usr/include/linux/
root:/usr/include/linux# ls
a.out.h           coda_psdev.h         hdreg.h      ipmi_msgdefs.h       ncp_mount.h       ppp_defs.h          taskstats.h
acct.h            coff.h               hid.h        ipsec.h              ncp_no.h          pps.h               tc_act

...

cn_proc.h         hdlc.h               ipc.h        ncp.h                ppp-comp.h        sysctl.h            xfrm.h
coda.h            hdlcdrv.h            ipmi.h       ncp_fs.h             ppp-ioctl.h       sysinfo.h
root:/usr/include/linux# exit

Por supuesto, no tenéis por qué hacer todo esto si no queréis. También podéis compilarlo todo sin generar paquetes. En ese caso basta con seguir las instrucciones tal cual sin usar los scripts. Mi intención es tener paquetes binarios de todo. De esa manera, cuando hayamos terminado con el capítulo 6, podremos instalar un gestor de paquetes real y convertir todos estos paquetes a un formato más adecuado para hacer gestión de paquetes de verdad.

EOF

Anuncios