Linux as a serial terminal


Background

After some expansion in the number of servers at my old job, we were running out of old terminals to use on our Sun servers (most of which were headless and didn't even have a graphics board for a monitor). I'd set up a DOS machine as a terminal in the past, but most of the serial terminal emulators I'd found were shareware. In any case, as linux supports virtual terminals which can be switched between using Alt-Fn, I thought that there had to be a way to use linux to connect to multiple servers (provided the PC had multiple serial ports).

I did a little hunting on the web and discovered the Serial Terminal Linux homepage. While this didn't do what I needed exactly, it gave me enough pointers to look at /usr/src/linux/Documentation/ramdisk.txt and Minicom. However, it didn't support virtual terminals. I then found rungetty which allowed me to run arbitrary programs on virtual terminals (eg, minicom!).

NB: I couldn't find a home page for rungetty during an update of links. If anyone knows where it has a homepage, let me know! In any case, you should be able to find it as part of your distribution; a Google search on rungetty reveals a lot of places to download it, including RPMs for most distributions and it's part of Debian anyway.


Requirements

The requirements are pretty basic:

  • 386 or better x86 based CPU (may work on other architectures, but I haven't tested it)
  • keyboard
  • monitor
  • bootable floppy drive unit (using a CD to boot is also possible, I'd imagine)
  • one or more serial ports
  • at least 8MB of RAM
The last requirement is the most stringent. The system uses approximately 1.5MB of RAM for the kernel and 2MB for the unpacked RAM disk. This leaves only 4.5MB in an 8MB machine for minicom and init; I've tried 3 terminals on an 8MB machine and it just plain didn't work. 2 seems to be OK, though.

There may be methods to reduce the memory requirements; minicom is too fully-featured for what is really required. agetty may actually be better for the job, but I haven't tested it. In addition, you could set (part of) the harddrive as swap space.


Creating the custom kernel

Obviously, in a dumb terminal, you are not going to need advanced features such as TCP/IP stack, NFS or even sound. As a result, I configured the kernel with nothing except:

  • Math emulation (CONFIG_MATH_EMULATION) as old 386/486 PC's may not have an adequate coprocessor.
  • ELF format binaries (CONFIG_BINFMT_ELF) to run programs
  • Floppy support (CONFIG_BLK_FD) to read ramdisk image
  • Ramdisk support (CONFIG_BLK_RAM) and initrd support (CONFIG_BLK_INITRD) for single floppy booting
  • Virtual terminal support (CONFIG_VT)
  • Console on VT (CONFIG_VT_CONSOLE)
  • Serial support (CONFIG_SERIAL)
  • Sharing of IRQ's between serial ports (CONFIG_SERIAL_SHARE_IRQ) which should allow us to use COM1-4
  • I added some other serial stuff which probably wasn't necessary, but couldn't go amiss, such as CONFIG_SERIAL_MANY_PORTS. These may be required if you plan on using more than 4 serial ports.
  • ext2fs (CONFIG_EXT2_FS)
  • I set the codepage to ISO8859-1 (western Europe). Shouldn't really matter, but you certainly don't need all the NLS.
  • Console drivers for VGA (CONFIG_VGA_CONSOLE) and Video select (CONFIG_VIDEO_SELECT) to drive the monitor.
You can either create your own kernel or download mine. It's a custom kernel compiled from Redhat 6.2 (ie, kernel 2.2.14) and is 335739 bytes in size (MD5 sum of 286aeb41a78b3b9a888126112695746a).

For more information on compiling kernels, see the Kernel HOWTO at the Linux Documentation Project


Creating the file system

This is copied almost entirely from /usr/src/linux/Documentation/ramdisk.txt:

  1. # dd if=/dev/zero of=/dev/ram bs=1k count=2048
  2. # mke2fs -vm0 /dev/ram 2048
  3. # mkdir /mnt/ramdisk
  4. # mount /dev/ram /mnt/ramdisk
What this does is:
  1. Create a 2MB Ram disk
  2. Create a filesystem on the ramdisk
  3. Make a mount point
  4. Mount the ramdisk on the mount point.
You can now create the directories you need. At a minimum, you should create:
  • /usr/share/lib/terminfo/l
  • /bin
  • /etc
  • /dev
  • /lib
  • /sbin
There are certain files you have to copy from your linux distribution to this filesystem; these should be stripped before you copy them (most already are, but you have to strip libc first as it is normally 4MB in size!). The required files are:
  • /sbin/init
  • /sbin/mount
  • /usr/bin/minicom
  • /usr/share/terminfo/l/linux
  • /lib/libc.so.6
  • /lib/ld-linux.so.2
  • /lib/libncurses.so.4
  • /lib/libnss_files.so.2
In addition, other files have to be created:

/etc/inittab

inittab controls what programs init will start after boot. There are two things we have to do:

  • Remount / read-write and
  • start up minicom for each terminal
We have to remount / read-write so that we can open /dev/tty[1234] for writing. For this, we create an fstab entry (see below) and a remount command. This is done as a sysinit command which will be done before anything else. The inittab file now follows:
# Only to to run level 1:
id:1:initdefault:
# Remount / rw as we need to write to tty's
mnt::sysinit:/sbin/mount -n -o remount,rw /
# Run minicoms on tty's
com1:1:respawn:/sbin/rungetty tty1 /usr/bin/minicom ttyS0
com2:1:respawn:/sbin/rungetty tty2 /usr/bin/minicom ttyS1
#com3:1:respawn:/sbin/rungetty tty3 /usr/bin/minicom ttyS2
#com4:1:respawn:/sbin/rungetty tty4 /usr/bin/minicom ttyS3
Currently, com3 and com4 are commented out due to the memory restrictions we have, but it shows the syntax for the lines. If other serial ports are added, the syntax should follow the above.

/etc/minirc.ttyS[0123]

These files specify the device files required and the baud rate for communication. This is an example file for minirc.ttyS0 using 9600 baud and 8 bits:

pr port             /dev/ttyS0
pu baudrate         9600
pu bits             8
pu parity           N
Most terminals will probably only require you to change the port (for different COM ports) and the baudrate setting. For example, Sun machines use 9600 by default, while 3com switches use 19200.

/etc/passwd

A single line for root is all that is required here:

root:x:0:0:root:/root:/bin/bash
The fact the shell doesn't exist doesn't matter here as that is only run for a login, which we will never do.

/etc/fstab

In order to remount /, we want an entry here for the mount command:

 /dev/ram               /                       ext2    defaults        1 1

/etc/nsswitch.conf

minicom uses nsswitch for something, so here's the config required:

passwd:     files
shadow:     files
group:      files
hosts:      files
ethers:     files
netmasks:   files
networks:   files
protocols:  files
rpc:        files
services:   files
The passwd: entry may actually be sufficient.

/sbin/rungetty

rungetty is a program that allows arbitrary programs to be run on different virtual terminals. However, some of its features are not required on a floppy based distribution and actually cause problems.

Firstly, we don't have a syslog daemon running and rungetty will try to log problems there first of all. The simplest fix for this is to comment out or remove the line #define USE_SYSLOG which will stop the syslog stuff being compiled in.

Secondly, rungetty tries to setuid() to 'nobody' for security. For our floppy based distribution, we don't need this kind of security, so we uncomment the getpw* entries and the setuid() and setgid() calls.

This done, we recompile and strip the binary and copy to our distro file.

Device files

Finally, certain device files are required using mknod. These files, along with type (character/block) and major/minor numbers are:

devicechr/blkmajorminor
consolec51
nullc13
ramb11
ttyc50
tty1c41
tty2c42
tty3c43
tty4c44
ttyS0c464
ttyS1c465
ttyS2c466
ttyS3c467

Creating the floppy

With all the files created, you can now create the floppy.

First, put the kernel on the floppy by running:

 dd if=zImage of=/dev/fd0 bs=1k
Take a note of the number of blocks copied as you must put the fs image after this. If you use my kernel image, an offset of 400 should be adequate.

Next, unmount the filesystem and copy the ramdisk image to a file:

 umount /mnt/ramdisk
 dd if=/dev/ram bs=1k count=2048 | gzip -v9 > /tmp/ram_image.gz
and put the image onto the floppy:
 dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k seek=400
Finally, set the floppy boot settings:
 rdev /dev/fd0 /dev/fd0
 rdev -r /dev/fd0 16784
The 16784 denotes that a ramdisk should be loaded (2^14=16384) from offset 400 (+400=16784) without prompting. See /usr/src/linux/Documentation/ramdisk.txt for more information.


Downloads

  • serial.tar.gz, a tar.gz copy of the filesystem.
  • Image of my working floppy. You can make this into a floppy by running dd if=floppy.img of=/dev/fd0 or using rawrite.exe from a Redhat CD (or similar).

TODO

  • Add support for keyboard layouts (eg, .uk)
  • Use the HD for swap space
  • Automate the setup of files and floppy
These are unlikely to be done due to the fact I don't have a need to do this any more, but the work I've done is free to use by anyone who wants. It isn't rocket science, but it might be useful to someone (I hope!).

File last modified: Saturday, 06-Mar-2004 14:12:08 GMT