# Some Basic Utilities

## Analysis

In the previous chapter it might seem like we did not accomplish very much. A lot of energy was expended redesigning the root disk, but the functionality is basically the same as in the initial prototype phase. The root disk still does not do very much. But we did make significant improvements when it comes to space savings. In this chapter we will put that extra space to good use and start cramming the root disk with as many utilities as it can hold.

The first two root disks we built only had shell built-in commands like `echo` and `pwd`. This time it would be nice to have some of the commonly used external commands like `cat`, `ls`, `mkdir`, `rm` and such on the root disk. Keeping this in mind we can define the goals for this phase as follows:

- Retain all of the functionality from the previous root disk.

- Add some of the commonly used external commands.

## Design

### Determining Required Commands

The first question that might come to mind is, "How do we know which commands are needed?" It is possible to just start with `cat` and `ls` then install other commands as we discover a need for them. But this is terribly inefficient. We need a plan or a blueprint to work from. For this we can turn to the Filesystem Hierarchy Standard (FHS) available from http://www.pathname.com/fhs/. The FHS dictates which commands should be present on a Linux system and where they should be placed in the directory structure.

### Locating Source Code

The next logical question is, "Now that we know what we need, where do we get the source code?" One way to find the answer to this question is to check the manpages. We can either search the manpages included with one of the popular GNU/Linux distributions or use one of the manpage search engines listed at http://www.tldp.org/docs.html#man. One thing that should tip us off as to where to find the source code for a particular command is the email address listed for reporting bugs. For example the cat manpage lists bug-textutils@gnu.org. From this email address we can deduce that `cat` is part of the textutils package from GNU.

### Leveraging FHS

So let's look at the FHS requirements for the `/bin` directory. The first few commands in the list are `cat`, `chgrp`, `chmod`, `chown` and `cp`. We already know that `cat` is part of GNU's textutils. Using the next few commands as keywords in a manpage search we discover that we need GNU's fileutils package for `chmod`, `chgrp`, `chown` and `cp`. In fact quite a few of the commands in `/bin` come from GNU's fileutils. The `date` command also comes from a GNU package called sh-utils. So a good way to tackle the problem of finding source code might be to group the commands together by package as shown below.

- The BASH shell -- `echo`, `false`, `pwd`, `sh`, `true`

- GNU textutils -- `cat`

- GNU fileutils -- `chgrp`, `chmod`, `chown`, `cp`, `dd`, `df`, `ln`, `ls`, `mkdir`, `mknod`, `mv`, `rm`, `rmdir`, `sync`

- GNU sh-utils -- `date`, `hostname`, `stty`, `su`, `uname`

These four packages do not contain all of the commands in the `/bin` directory, but they do represent of over 70% of them. That should be enough to accomplish our goal of adding some of the commonly used external commands. We can worry about the other commands in later phases of the project.

### Downloading Source Code

To fetch the source code we simply need to connect to GNU's FTP site and navigate to the appropriate package directory.

When we get to the directory for textutils there are several versions available. There is also a note informing us that the package has been renamed to coreutils. The same message about coreutils appears in the fileutils and sh-utils directories as well. So instead of downloading three separate packages we can get everything in one convenient bundle in the coreutils directory.

## Construction

Rather than copying files directly to the ramdisk, we can make things easier by setting up a staging area. The staging area will give us room to work without worrying about the space constraints of the ramdisk. It will also provide a way to save our work and make it easier to enhance the rootdisk in later phases of the project.

The staging procedure will work like this:

1. Create a directory structure as defined in the FHS.

2. Copy in the files from phase 2's root disk.

3. Build the new package from source code.

4. Install files into the correct FHS directories.

5. Strip the binaries to save space.

6. Check library dependencies.

7. Copy to the whole directory structure to the ramdisk.

8. Compress the ramdisk and write it out to floppy.

### Create a staging area

    bash# mkdir ~/staging
    bash# cd ~/staging
    bash# mkdir bin boot dev etc home lib mnt opt proc root sbin tmp usr var
    bash# mkdir var/log var/run

### Copy contents of phase 2 rootdisk

    bash# dd if=~/phase2-image.gz | gunzip -c > /dev/ram7
    bash# mount /dev/ram7 /mnt
    bash# cp -dpR /mnt/* ~/staging
    bash# umount /dev/ram7
    bash# rmdir ~/staging/lost+found

### Install binaries from GNU coreutils

Download a recent version of coreutils from ftp://ftp.gnu.org/gnu/coreutils/

    bash# cd /usr/src/coreutils-5.2.1
    bash# export CC="gcc -mcpu=i386"
    bash# ./configure --host=i386-pc-linux-gnu
    bash# make
    bash# cd src
    bash# cp cat chgrp chmod chown cp date dd df ~/staging/bin
    bash# cp hostname ln ls mkdir mkfifo mknod ~/staging/bin
    bash# cp mv rm rmdir stty su sync uname ~/staging/bin

### Copy additional libraries

Check library requirements by using `ldd` on some of the new binaries.

    bash# ldd ~/staging/bin/cat
    bash# ldd ~/staging/bin/ls
    bash# ldd ~/staging/bin/su
    bash# ls ~/staging/lib

Note the differences in the required libraries, as shown by the `ldd` command, and the libraries present in the staging area, as shown by the `ls` command, then copy any missing libraries to the staging area.

    bash# cp /lib/librt.so.1 ~/staging/lib
    bash# cp /lib/libpthread.so.0 ~/staging/lib
    bash# cp /lib/libcrypt.so.1 ~/staging/lib

### Strip binaries and libraries

    bash# strip ~/staging/bin/*
    bash# strip --strip-unneeded ~/staging/lib/*

### Create a compressed root disk image

    bash# cd /
    bash# dd if=/dev/zero of=/dev/ram7 bs=1k count=4096
    bash# mke2fs -m0 /dev/ram7 4096
    bash# mount /dev/ram7 /mnt
    bash# cp -dpR ~/staging/* /mnt
    bash# umount /dev/ram7
    bash# dd if=/dev/ram7 of=~/phase3-image bs=1k count=4096
    bash# gzip -9 ~/phase3-image

The process for creating the compressed root disk image will change very little throughout the remaining chapters. Writing a small script to handle this function can be a great time saver.

### Write the root disk image to floppy

Insert the diskette labeled "root disk" into drive fd0.

    bash# dd if=~/phase3-image.gz of=/dev/fd0 bs=1k

## Implementation

We will need to have a read-write filesystem in order for some of the commands to work. The kernel's normal behavior is to mount root as read-only, but we can change this using a kernel option. By passing the kernel the `rw` option before `init=/bin/sh` we will get a read-write root filesystem.

### System startup

Follow these steps to get the system running.

- Boot the PC from using the GRUB boot disk.

- At the grub> prompt, type `kernel (fd0)/boot/vmlinuz rw init=/bin/sh root=/dev/fd0 load_ramdisk=1 prompt_ramdisk=1`.

- Verify that you remembered to add the `rw` parameter and press Enter.

- Type boot and press Enter.

- Insert the recently created root disk when prompted.

The terminal display should look similar to the example below.

    GNU GRUB version 0.95

    grub> kernel (fd0)/boot/vmlinuz rw init=/bin/sh root=/dev/fd0 load_ramdisk=1 prompt_ramdisk=1
       [Linux-bzImage, setup=0xc00, size=0xce29b]

    grub> boot

    Linux version 2.4.26
    ..
    .. [various kernel messages]
    ..
    VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER
    RAMDISK: Compressed image found at block 0
    VFS: Mounted root (ext2 filesystem) read-write.
    Freeing unused kernel memory: 178k freed
    # _

### Testing new commands

Now that the system is up and running, try using some of the new commands.

    bash# uname -a
    bash# ls /etc
    bash# echo "PocketLinux" > /etc/hostname
    bash# hostname $(cat /etc/hostname)
    bash# uname -n
    bash# mkdir /home/stuff
    bash# cd /home/stuff

If everything goes well the commands like `cat`, `ls` and `hostname` should work now. Even `mkdir` should work since the root filesystem is mounted read-write. Of course since we are using a ramdisk, any changes will be lost once the PC is reset.

### System shutdown

Remove the diskette from fd0 and restart the system using CTRL-ALT-DELETE.