📜 ⬆️ ⬇️

Making Linux From Scratch our universal distribution

It so happened that a couple of years ago, on duty, the development team to which I belong fell in an unexpected task - developing a hardware management system (this is something unexpected, for the direction of development is) with a management PC for Linux.
The development of the Linux part was conducted (and conducted) under Ubuntu, in the environment of Code :: Blocks. But, as practice has shown, for quality work you need something much lighter with a guaranteed response time. A console was enough to work, since the tasks of organizing the user interface were solved on a remote computer connected via TCP / IP.
Then the idea came to use the Linux distribution of its own assembly, which (in fact, in the distribution of the distribution kit), I actually did in my spare time. The choice fell on the LFS. About the fact that such LFS has already been repeatedly written even on Habré, I will describe the solution of several additional (except for simple Linux) tasks that confronted me in our particular case.
At first there was one such task - to use the real-time core.
However, further, when the idea of ​​a USB flash drive with a distribution kit came to everyone’s liking, the tasks of reproducing flash drives and launching the system on various computers appeared (there are many test booths, having your flash drive stuck in your pocket and go to any one). This is where the problems appeared - LFS is not 100% portable from one computer to another. To adapt it to a specific computer, you need to edit some of the scripts, which is problematic under the conditions of yesterday’s Windows coders' commands (some were transferred to the virtual machine from Ubuntu, but the console and scripts are a disaster). The reproduction of the system also requires the repetition of some manipulations performed during the assembly process (install the same GRUB).

Naturally, the solution of all problems is on the Internet, but I think that collecting some information in one place will not hurt anyone.
So, the specific tasks were the following ...

1. Using a Linux kernel with a real-time patch

It was one of the easiest tasks. The build process went through the LFS book with the only exception - instead of the standard kernel book, 2.6.33.9 was taken and an RT patch for it. Everywhere, where there were manipulations with a kernel (installation of Linux Headers and kernels directly), we work with our patched version.
Also, it would not be superfluous to say that the distribution was built without connecting a swap partition (2GB of RAM is in our case above the roof, the presence of a swap is not desirable because of its negative effect on the guaranteed response time, and it is extremely destructive for a flash drive) one single ext2fs partition.

2. Automatic login (under development conditions, security is not important to us, and we let the system under the root for several reasons)

The idea of ​​an automatic login was taken from here .
An autologin.c file was created with the following content:
int main() { execlp("login", "login", "-f", "root", 0); } 

Then the file was compiled with the command:
')
gcc autologin.c -o /usr/local/sbin/autologin

Further, it was decided that two consoles with automatic input would be enough (one to start the system, the other for everything else, if necessary).
In the / etc / inittab file of the line:

1:2345:respawn:/sbin/agetty tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600


were replaced by:

1:2345:respawn:/sbin/agetty -n -l /usr/local/sbin/autologin tty1 9600
2:2345:respawn:/sbin/agetty -n -l /usr/local/sbin/autologin tty2 9600


After that, there were no problems with the automatic login, so there was no additional manipulation of the scripts.

3. Unlinking the system from the order of connecting disks


Different BIOS has its own troubles. Some, for example, believe that the first is the disk from which we boot. In this case, our flash drive will be sda. Others believe that you must first go hard drives, and then other devices. In this case, our flash drive will have the name sdb, sdc, and so on.
As a result, the system cannot boot from a disk that does not exist, the root directory cannot be mounted for the reason that the wrong disk is specified in / etc / fstab.
Everything is solved either by fixing /boot/grub/grub.cfg and / etc / fstab for a specific machine, or using to load and mount not the drive name (sda, sdb, etc.), but the UUID of the file system, which for this file system the flash drive will be unique and, most importantly, permanent.
The problem is that GRUB is able to work with a UUID, but the kernel does not, that is, it is impossible to directly mount the root system using a UUID (without knowing the name of the device). This is not a bug, but a consequence of the ideological considerations of Linus Torvalds, so you should not hope for such a possibility in the future. However, the workaround is - this is the initramfs .
Initramfs is a temporary file system that helps in loading and mounting file systems of this system.
The standard LFS assembly initramfs is not included, so to build it we will use the recommendations from the Gentoo Wiki and some of our own considerations (the option from the Gentoo Wiki did not solve the problem with the drive names without changes, and did not really work).
To create the simplest system initramfs that mounts our main UUID, we need a simple shell and init script. The full set of command line utilities is cumbersome enough for initramfs, so busybox is often used for this purpose, which, with its modest size and requirements, implements some of the most frequently used utilities.
We are taking the latest version of busybox:

 cd sources 
wget http://busybox.net/downloads/busybox-1.18.4.tar.bz2


Unpack and configure:

tar jxf busybox-1.18.4.tar.bz2
cd busybox-1.18.4
make menuconfig


Configuration is done using a menu (like the Linux kernel). In principle, the standard configuration is enough for our needs, but, just in case, it is worth checking that the following features are connected:
Support for devfs - devfs support for working with / dev.
Build Busybox as a static library (no shared libraries) - static layout, so as not to pull a bunch of so-libraries.
Support version 2.6.x Linux kernels - support for 2.6 kernels.
As well as support for the functionality of the utilities: sh, cat, cut, findfs, mount, umount, sleep, echo, switch_root.

Compile:

make

Now we collect the directory tree of our file system:

mkdir /usr/src/initramfs
cd /usr/src/initramfs
mkdir -p bin lib dev etc mnt/root proc root sbin sys
cp -a /dev/{null,console} /usr/src/initramfs/dev/


Copy busybox and create links to utilities:

cp /sources/busybox-1.18.4/busybox ./bin/
cd bin
ln -s busybox sh
ln -s busybox cat
ln -s busybox cut
ln -s busybox findfs
ln -s busybox mount
ln -s busybox umount
ln -s busybox sleep
ln -s busybox switch_root
cd ..


It remains to write the init script:
 #!/bin/sh #    proc  sysfs mount -t proc none /proc mount -t sysfs none /sys # USB-     #     10 ,       sleep 10 #     mount -t devtmpfs none /dev #   UUID       for cmd in $(cat /proc/cmdline) ; do case $cmd in root=*) uuid=$(echo $cmd | cut -d= -f3) mount -o ro $(findfs UUID="$uuid") /mnt/root ;; esac done #      umount /dev umount /proc umount /sys #    exec switch_root /mnt/root /sbin/init 


Making our script executable:

chmod +x /usr/src/initramfs/init

Putting our temporary file system into archive:

cd /usr/src/initramfs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > /boot/initrd.img-2.6.33-rt31


Pay attention to the file name - it must match the name of the kernel (this is necessary for GRUB to pick it up correctly). That is, if the kernel is named vmlinux- 2.6.33-rt31 , then initramfs should be named initrd.img- 2.6.33-rt31 .

Now, when grub-mkconfig is executed, GRUB will detect the initramfs and also include the root system UUID in the configuration. For verification, you can fix /boot/grub/grub.cfg by hand. For example, the configuration:

menuentry "Linux 2.6.33-rt31" --class gnu-linux --class gnu --class os {
insmod ext2
set root='(hd0,1)'
search --no-floppy --fs-uuid --set 47029df8-8567-417d-b813-eedfe1ff8b0f
linux /boot/vmlinux-2.6.33-rt31 root=/dev/sda1 ro
}


fix on:

menuentry "Linux 2.6.33-rt31" --class gnu-linux --class gnu --class os {
insmod ext2
set root='(hd0,1)'
search --no-floppy --fs-uuid --set 47029df8-8567-417d-b813-eedfe1ff8b0f
linux /boot/vmlinux-2.6.33-rt31 root=UUID=47029df8-8567-417d-b813-eedfe1ff8b0f ro
initrd /boot/initrd.img-2.6.33-rt31
}


The file system UUID can be found as follows (for example, for / dev / sdb1):

blkid -p -o udev /dev/sdb1

It remains to correct / etc / fstab, replacing the line:

/dev/sda1 / ext2 defaults 1 1

on

UUID= 47029df8-8567-417d-b813-eedfe1ff8b0f / ext2 defaults 1 1

It should also be noted that for all the manipulations above, it is necessary that the devtmpfs (CONFIG_DEVTMPFS = y) and initramfs (CONFIG_BLK_DEV_INITRD = y) support be enabled in the kernel.

4. Detaching the system from the network card

If more than one network card is installed in the computer, then parallel loading of the kernel modules does not guarantee that the boards will always be assigned names. For example, there are two boards. Board_1 has the name of the interface in the system eth0, Board_2 is eth1. At the next reboot, it may happen that the Board_1 becomes eth1, and the Board_2 becomes eth0.
To this end, the LFS binds the name to a specific board. When booting on another computer there is a very high probability that the network will not rise.
In my particular case, the board on all computers is the same and the IP is static (communication only with the terminal computer directly).
The network interface in LFS is raised by the /etc/rc.d/init.d/network script. We will add a script so that each time the system loads the configuration file /etc/udev/rules.d/70-persistent-net.rules is generated and this file is deleted upon completion of the work. I suspect that there is a simpler method, but the method found has earned, and there was no time and special desire to delve into the principles of Udev work.
We add to the beginning of the start section of the case command:

 for NIC in /sys/class/net/* do INTERFACE=${NIC##*/} udevadm test --action=add $NIC done 


And at the end of the stop section (just before ;;) we add:

 rm /etc/udev/rules.d/70-persistent-net.rules 


Now, when booting on any system, the name of the network interface will be eth0 (except for the most exotic cases) and the network will go up. Of course, the /etc/sysconfig/network-devices/ifconfig.eth0 directory with the ipv4 file must exist. The contents of this file are described in the LFS book.

5. Writing a script that produces an LFS installation on any USB flash drive

The last thing left is to make an archive of the system and a script that will install it on an arbitrary medium.
Boot into another system (not from a USB flash drive), mount a USB flash drive, for example in / mnt / usb-os. Archiving content:

cd /mnt/usb-os
tar -cvjf ~/pack.tar.bz2 *


We write a script to install install_usb-os.sh. As a parameter, the script takes the name of the device on which the system is to be deployed (for example, / dev / sdb). The script itself will create the necessary partition and file system (/ dev / sdb1 if the name / dev / sdb is specified), unpack the archive and install GRUB.

It should run as root and, in fact, very dangerous.
In case of an incorrect indication of the device name, all data on the working disk can be destroyed!

 #!/bin/sh # ,       if [ "x${1}" = "x" ] ; then echo "Usage: install_usb-os device_name" exit fi #  ,     umount ${1}1 #      if [ ! -e /mnt/USBOSTmp ]; then mkdir /mnt/USBOSTmp fi #  MBR,      echo "Building partitions..." parted -s ${1} mklabel msdos parted -s ${1} unit % mkpart primary ext2 0 100 #          echo "Preparing filesystem..." mkfs -t ext2 ${1}1 mount -t ext2 ${1}1 /mnt/USBOSTmp #   echo "Unpacking distributive..." tar -xvf ./pack.tar.bz2 -C /mnt/USBOSTmp #    console  null # (,    , -  ) mknod -m 600 /mnt/USBOSTmp/dev/console c 5 1 mknod -m 666 /mnt/USBOSTmp/dev/null c 1 3 #     (  ..) echo "Mounting necessary file systems..." mount -v --bind /dev /mnt/USBOSTmp/dev mount -vt devpts devpts /mnt/USBOSTmp/dev/pts mount -vt tmpfs shm /mnt/USBOSTmp/dev/shm mount -vt proc proc /mnt/USBOSTmp/proc mount -vt sysfs sysfs /mnt/USBOSTmp/sys #  UUID         #  fstab uu=`blkid -p -o value ${1}1 | grep -` echo "Writing fstab for uuid=$uu..." cat > /mnt/USBOSTmp/tmp/fstab << "EOF" proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 devpts /dev/pts devpts gid=4,mode=620 0 0 tmpfs /dev/shm tmpfs defaults 0 0 EOF echo "UUID=$uu / ext2 defaults 1 1" >> /mnt/USBOSTmp/tmp/fstab #    ,     #       #        GRUB cat > /mnt/USBOSTmp/tmp/update.sh << "EOF" echo "Starting internal update script for $H_DEV ..." mv -v /tmp/fstab /etc/fstab grub-mkconfig -o /boot/grub/grub.cfg echo "Updating mbr $H_DEV2..." grub-setup $H_DEV2 EOF #    chmod +x /mnt/USBOSTmp/tmp/update.sh #     root-, #       echo "Running update..." chroot /mnt/AxiOMATmp/ /usr/bin/env -i HOME=/root TERM="$TERM" PS1='\u:\w\$ ' PATH=/bin:/usr/bin:/sbin:/usr/sbin H_DEV=${1}1 H_DEV2=${1} /tmp/update.sh #   ,    rm -v /mnt/USBOSTmp/tmp/update.sh umount -v /mnt/USBOSTmp/dev/pts umount -v /mnt/USBOSTmp/dev/shm umount -v /mnt/USBOSTmp/dev umount -v /mnt/USBOSTmp/proc umount -v /mnt/USBOSTmp/sys umount ${1}1 rm -rvf /mnt/USBOSTmp echo "Installation finished!" 


Now we will make our script executable:

chmod +x install_usb-os.sh

We place the archive pack.tar.bz2 in the directory with the script and the installer is ready!

Source: https://habr.com/ru/post/122179/


All Articles