# Filling in the Gaps

## Analysis

The root disk has come a long way since its humble beginnings as a statically-linked shell. It now shares many features with the popular, ready-made distributions. For example it has:

- Several common utilities like `cat`, `ls` and so on.

- Startup scripts that automatically check and mount filesystems.

- Graceful shutdown capability.

- Support for multiple users and virtual terminals.

As a final test, we can put the root disk up against the Filesystem Hierarchy Standard (FHS) requirements for the root filesystem. (We will ignore anything in the `/usr` hierarchy because of space constraints.) Compared to FHS requirement, the only files missing are a few commands in the `/bin` directory. Specifically, the root disk lacks the following commands:

- `more`

- `ps`

- `sed`

In addition to the required commands, it might be nice to include the "ed" editor listed as an option by the FHS. It is not as robust as vi or emacs, but it works and it should fit onto the tiny root filesystem.

So in order to finish up this phase of the project, we need to accomplish the following goals:

- Add the `more`, `ps` and `sed` commands.

- Install the optional `ed` editor.

## Design

### more

There is a `more` command that comes with util-linux, but it will not work for this project. The reason is because of library dependencies and space constraints. The util-linux supplied `more` needs either the libncurses or libtermcap to work and there just is not enough space on the root disk floppy to fit everything in. So, in order to have a `more` command we will have to get creative.

The `more` command is used to display a file page by page. It's a little like having a `cat` command that pauses every twenty-five lines. The basic logic is outlined below.

- Read one line of the file.

- Display the line on the screen.

- If 25 lines have been displayed, pause.

- Loop and do it again.

Of course there are some details left out like what to do if the screen dimensions are not what we anticipated, but overall it is a fair representation of what `more` does. Given this simple program logic, it should not be hard to put together a short shell script that emulates the basic functionality of `more`. The BASH(1) manpage and Adv-BASH-Scripting-Guide will serve as references.

### More device files

The `more` script will need access to device files that are not on the root disk yet. Specifically `more` needs to have `stdin`, `stdout` and `stderr`, but while we are at it we should check for any other missing `/dev` files. The Linux Standard Base requires `null`, `zero` and `tty` to be present in the `/dev` directory. Files for `null` and `tty` already exist from previous phases of the project, but we still need `/dev/zero`. We can refer to `devices.txt` in the Linux source code `Documentation` directory for major and minor numbers.

### ps, sed & ed

These three packages can be found by using the Internet resources we have used before plus one new site. The "sed" and "ed" packages can be found at the same place we found BASH, on the GNU FTP server. The procps package shows up in an Ibiblio LSM search, but it is an old version. In order to find the latest version we can go to the Freshmeat website at http://freshmeat.net and search for "procps" in projects.

Both "sed" and "ed" packages feature GNU's familiar `configure` script and are therefore very easy to build. There is no `configure` script for "procps" but this does not make things too difficult. We can just read the package's `README` file to find out about how to set various configuration options. We can use one of these options to avoid the complexity of using and installing libproc. Setting `SHARED=0` makes `libproc` an integrated part of `ps` rather than a separate, shared library.

## Construction

### Write a "more" script

Create the following script with a text editor and save it as `~/staging/bin/more.sh`

    #!/bin/sh
    #
    # more.sh - emulates the basic functions of the "more" binary without
    #           requiring ncurses or termcap libraries.
    #
    # Assume input is coming from STDIN unless a valid file is given as
    # a command-line argument.
    if [ -f $1 ]; then
      INPUT="$1"
    else
      INPUT="/dev/stdin"
    fi
    #
    # Set IFS to newline only. See BASH(1) manpage for details on IFS.
    IFS=$'\n'
    #
    # If terminal dimensions are not already set as shell variables, take
    # a guess of 80x25.
    if [ "$COLUMNS" = "" ]; then
      let COLUMNS=80;
    fi
    if [ "$LINES" = "" ]; then
      let LINES=25;
    fi
    #
    # Initialize line counter variable
    let LINE_COUNTER=$LINES
    #
    # Read the input file one line at a time and display on STDOUT until
    # the page fills up. Display "Press <Enter>" message on STDERR and wait
    # for keypress from STDERR.  Continue until the end of the input file.
    # Any input line greater than $COLUMNS characters in length is wrapped
    # and counts as multiple lines.
    #
    while read -n $COLUMNS LINE_BUFFER; do
      echo "$LINE_BUFFER"
      let LINE_COUNTER=$LINE_COUNTER-1
      if [ $LINE_COUNTER -le 1 ]; then
        echo "Press <ENTER> for next page or <CTRL>+C to quit.">/dev/stderr
        read</dev/stderr
        let LINE_COUNTER=$LINES
      fi
    done<$INPUT
    #
    # end of more.sh

Create a symbolic link for `more`

    bash# ln -s more.sh ~/staging/bin/more

### Create additional device files

    bash# ln -s /proc/self/fd ~/staging/dev/fd
    bash# ln -s fd/0 ~/staging/dev/stdin
    bash# ln -s fd/1 ~/staging/dev/stdout
    bash# ln -s fd/2 ~/staging/dev/stderr
    bash# mknod -m644 ~/staging/dev/zero c 1 5

### Install ps

Get the latest procps source package from http://procps.sourceforge.net/

    bash# cd /usr/src/procps-3.2.3
    bash# make SHARED=0 CC="gcc -mcpu=i386"
    bash# cd ps
    bash# cp ps ~/staging/bin

### Install sed

Download GNU's sed from ftp://ftp.gnu.org/gnu/sed/

    bash# cd /usr/src/sed-4.1.2
    bash# export CC="gcc -mcpu=i386"
    bash# ./configure --host=i386-pc-linux-gnu
    bash# make
    bash# cd sed
    bash# cp sed ~/staging/bin

### Install ed

The ed package also comes from GNU at ftp://ftp.gnu.org/gnu/ed/

    bash# cd /usr/src/ed-0.2
    bash# ./configure --host=i386-pc-linux-gnu
    bash# make
    bash# cp ed ~/staging/bin

### Strip binaries to save space

    bash# strip ~/staging/bin/*

### Ensure proper permissions

    bash# chown 0:0 ~/staging/bin/*
    bash# chmod -R 755 ~/staging/bin
    bash# chmod 4750 ~/staging/bin/su

### Create the 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=~/phase7-image bs=1k
    bash# gzip -9 ~/phase7-image

### Copy the image to diskette

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

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

## Implementation

### System startup

Boot from the diskset in the usual way and log in as root.

### Test the "more" script

Display kernel messages by piping the output of `dmesg` to `more`.

    bash# dmesg | more

Examine the `local_fs` script by using `more` with a command-line argument.

    bash# more /etc/init.d/local_fs

### Use ps to show running processes

Display processes for the user currently logged in.

    bash# ps

Display all available information about all running processes.

    bash# ps -ef

### Run a simple sed script

Use `sed` to display an alternate version of `/etc/passwd`.

    bash# sed -e "s/Legacy/Old School/" /etc/passwd

Verify that sed did not make the changes permanent.

    bash# cat /etc/passwd

### Test the "ed" editor

Use `ed` to change properties on the "daemon" user.

    bash# ed -p*
    ed* r /etc/passwd
    ed* %p
    ed* /daemon/s/Legacy/Old School/
    ed* %p
    ed* w
    ed* q

Verify that the changes are permanent (at least until the system is restarted.)

    bash# cat /etc/passwd

### System shutdown

Bring the system down gracefully with the `shutdown` command.