5. E finalmente, il come fare.
Ora, voglio produrre una initramfs professionale, partendo dal nulla e arricchendola progressivamente, pezzo per pezzo, a mano.
In tutto, sono sette passi:
a. Il baselayout.
b. File di init.
c. Il chroot.
d. lvm2.
e. Raid + file di configurazione
f. I dispositivi.
g. Conclusione, con l'assemblaggio finale.
Ho testato la mia initramfs con un tradizionale gentoo-sources e con un kernel xen. Quest'ultimo ha manifestato la necessità di alcune regole restrittive addizionali, di cui ho tenuto conto nella stesura di quanto segue. Esse sono:
- 1. L'initramfs deve essere costruito a cipolla. Queste rende obsolete le initrd vecchie maniera, che producono un laconico messaggio di /init o /linuxrc not found.
2. Il primo strato deve essere sufficientemente leggero - in pratica, solo il baselayout.
3. Il primo strato deve contenere il file di init.
a. Il baselayout.
Prima di tutto, serve uno spazio temporaneo per il lavoro sporco e di una struttura elementare di filesystem:
Code: Select all
# mkdir -p /tmp/initramfs/base_dir /tmp/initramfs/cpio_data
# cd /tmp/initramfs/base_dir
# mkdir bin dev etc lib proc sys sysroot tmp
# ln -s bin sbin
# ln -s lib lib64 # solo se necessario
b. File di init.
Xen impone di aggiungere subito il fine init.
Si tratta di uno script di bash studiato per acquisire in input una cmdline come la seguente:
Code: Select all
# cat /proc/cmdline
real_root=/dev/mapper/raid0-root root=/dev/ram0 rootdelay=5 rw init=/linuxrc splash=verbose,theme:emergence video=vesa:ywrap,mtrr,vga=0x31B
E questo è il taglia incolla del mio `cat init`:
Code: Select all
#!/bin/bash
echo "lunga attesa"
sleep 3
echo "attesa finita"
echo "Caricamento del filesystem /proc"
mount -t proc none /proc
CMDLINE=`cat /proc/cmdline`
echo "Creazione dei dispositivi"
sed -e '1,2d;s/\(.*\) \(.*\) \(.*\) \(.*\)/mknod \/dev\/\4 b \2 \1/' /proc/partitions|bash
# attivazione delle partizioni raid. Utilizza le impostazioni di /etc/mdadm
mdadm --assemble /dev/md2 /dev/hda5 /dev/sda5
echo "attivazione delle partizioni lvm"
vgscan --mknodes --ignorelockingfailure
vgchange -a y
sleep 5
mount
echo "Caricamento della partizione di root"
sed 's/real_root[[:graph:]]*[[:blank:]]/&xxx/g' /proc/cmdline |sed 's/ xxx.*//'|sed 's/real_root=/mount -v -o noatime --rw /'|sed 's|.*|& /sysroot|'|bash
mount
#if [ ! -d /sysroot/dev/mapper ]; then cp -a /dev/mapper /sysroot/dev ;fi
echo "Pivot root e avvio del processo di init reale"
umount /proc
cd sysroot
pivot_root . initrd
exec chroot . /bin/sh <<- EOF >dev/console 2>&1
exec /sbin/init ${CMDLINE}
EOF
Il primo strato della cipolla è pronto:
Code: Select all
# pwd
/tmp/initramfs/base_dir
# find . -depth -print|cpio -o -H newc>../cpio_data/base.cpio
c. Il chroot.
Qui scelgo di non usare le klibc (anche perché ho già disinstallato genkernel).
La procedura di costruzione manuale di un chroot è lunga e noiosa, descritta in cento salse su internet.
Tuttavia, qui, mi sento obbligato a mostrarla dettagliatamente lo stesso.
Code: Select all
# cp -a ../base_dir ../chroot_dir
# cd ../chroot_dir/bin
# cp `which bash` `which cat` `which chroot` `which cp` `which echo` `which insmod` `which less` `which ls` `which mkdir` \
`which mknod` `which mount` `which pivot_root` `which sed` `which sleep` `which umount` .
Qui per la verità ho aggiunto qualcosa di troppo, ma è meglio abbondare.
Come dicevo, poi, non sono le klibc. Per ogni programma servono le rispettive librerie.
Code: Select all
# ldd bash
linux-gate.so.1 => (0xffffe000)
libdl.so.2 => /lib/libdl.so.2 (0x48ab9000)
libc.so.6 => /lib/libc.so.6 (0x48978000)
/lib/ld-linux.so.2 (0x4895f000)
# cp /lib/libdl.so.2 /lib/libc.so.6 ../lib
e iterativamente, con pazienza, per ogni programma. Queste sono le librerie che sono risultate necessarie nel mio caso:
Code: Select all
# ls ../lib
ld-linux-x86-64.so.2 libblkid.so.1 libc.so.6 libdl.so.2 libncurses.so.5 libncursesw.so.5 libpthread.so.0 librt.so.1 libuuid.so.1 libz.so.1
Alla fine, testare il chroot:
Code: Select all
# chroot ..
bash-3.1#
bash-3.1# # alleluia!
bash-3.1# exit
# cd ..
pwd
/tmp/initramfs/chroot_dir
# find -depth -print |cpio -o -H newc>../cpio_data/chroot.cpio
d. Terzo passo. Aggiungere lvm2.
Code: Select all
# cp -a ../base_dir ../lvm_dir
# cd ../lvm_dir
# cp -a `equery f lvm2|grep bin/` bin
# cp -a /etc/lvm etc/ # mai dimenticare i file di configurazione essenziali.
Già fatto? Sì. lvm è un eseguibile statico. Nessuna libreria richiesta.
Code: Select all
# pwd
/tmp/initramfs/lvm_dir
# find -depth -print |cpio -o -H newc>../cpio_data/lvm.cpio
e. Raid + file di configurazione
Per il raid, ho risolto usando le raidtools ed mdadm. Genkernel, invece, usa dmraid, del quale dispone attualemente di una versione più avanzata di quella distribuita in portage. Per sys-fs/mdadm, non utilizzare la use ssl senza la use static.
Code: Select all
# cp -a ../base_dir ../raid_dir
# cd ../raid_dir
# cp `which mdadm` bin
# ldd bin/mdadm
libc.so.6 => /lib/libc.so.6 (0x00002ad9b6cad000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad9b6b90000)
# #librerie già presenti. Non serve nulla di nuovo.
# >etc/fstab
# >etc/mtab
# cp /etc/mdadm.conf etc
f. I dispositivi.
Non dimenticare i dispositivi essenziali per l'avvio. Riporto quelli che uso io.
I numeri specifici possono variare a seconda delle necessità locali,a dattando di conseguenza il successivo file di init.
Code: Select all
# mknod dev/console c 5 1
# mknod dev/loop0 c 7 0
# mknod dev/md0 b 9 0
# mknod dev/md1 b 9 1
# mknod dev/md2 b 9 2
# mknod dev/null c 1 3
# mknod dev/tty c 4 0
Conclusione.
Ci siamo. Adesso basta aggregare il tutto.
(rullo di tamburi

)
Code: Select all
# find -depth -print |cpio -o -H newc>../cpio_data/raid.cpio
# cd ../cpio_data
# cat base.cpio chroot.cpio lvm.cpio raid.cpio |gzip --best >/boot/cpio_data/init.gz
L'ultimo è certamente il passo più bello. Adesso si può aggiungere ancora udev, o configurare il caricamento di un modulo esterno o quant'altro piaccia.
Basta procedere a oltranza, modificando l'essenziale.