Booting from Local Device
This page is for people to post links to articles about booting a thin client from a local device, such as a flash disk, cdrom or hard drive.
Boot from Flash
How it all works
In a normal linux system when an app needs a file the system checks to see if the
file is in cache.
If it is, good, it's used. If not drivers are tasked to read the file into
cache and the app continues.
The magic that Linus et al achieved, when starting to boot a system,
is to load the cache with a complete
file system. All cache requests will be satisfied. The code is
the basis of a normal running system, so is very well checked.
This is called an initramfs. It looks like a ram disk, can use all the memory
as it grows, so grow it carefully, but it uses no drivers to access the files.
Refer to Documentation/filesystems/initramfs.txt in a current kernel source directory.
This may also be informative
http://tigger.ws/wiki/index.php/InitRamFs
LTSP nfs mounts, as Read-Only, the shared file system bits and uses the
native ramfs for the /tmp /var etc bits in the initramfs.
The flash LTSP implementation uses the same strategy, mounting a local flash
file system into the ramfs instead of nfs mounts.
The root file system is mounted Read-Only so it can't wear or be damaged by
power-downs or by malicious code.
Introduction
I created a complete flash client around
ltsp. I installed my system on an ebox-2300 with 256M flash.
I did a few things to suit me, and have documented what I did, not what I
should have done.
I left out all the printer stuff, to save a tiny bit of space I left out the
modules and mod_tools as well as the zoneinfo data base and terminfo
(except for linux) database.
I expect that others using this howto will rectify my omissions. When I
started this document would have helped me lots. I hope others will find
it helps them.
The Recipe
See the listings at the end for the files used, changed etc.
Create a working Directory on a system that you have installed ltsp on.
LBE and make packages could just as well be used.
I called mine Black-Box because of the ebox-2300 appearence.
In Black-Box create a ltsp_kernel_kit directory.
http://ltsp.mirrors.tds.net/pub/ltsp/utils/ltsp_kernel_kit_4.2u3.tgz
Create a kernel source tree: I used 2.6.17.8.
Create
extra with a mirror of any files you want to change and include.
Create an executable
build_embedded_root
In the kernel directory (linux-2.6.17.8) copy
../ltsp_kernel_kit/config-2.6.17.8-ltsp-1 to .config
make oldconfig
Now configure your kernel to NOT use modules, to include the drivers for your
hardware and to include ide drivers.
make xconfig or
make menuconfig whatever
make
Copy arch/i386/boot/bzImage to ../extra/boot/bzImage
Change build_initramfs to not reference any modules.
Change initramfs/init to mount the flash. (listings)
Make a dev/hda1 entry in initramfs/dev (mknod hda1 b 3 1)
In ltsp_kernel_kit run
build_initramfs and copy /tmp/initramfs-bla.gz to
extra/boot/initramfs.gz
Populate extra with the things you want eg I put in menu.lst
ssh leys and changed etc/inittab.base to include ssh, rc.earlysysinit and
rc.sysinit to do what was required (listings)
I want to be able to backup the whole directory so I copied /opt/ltsp/pkg_cache
to my Black-Box directory as opt/ltsp/pkg_cache.
Run
build_embedded_root after modifying it to copy the timezone of your choice
to etc/localtime before zoneinfo is rtemoved. Note my
build_embedded_root does
not prune the terminfo database. I did that by hand.
Note that any files in extra that map to sym-links in the real root system
must be removed, else your copy end up in the sym-link and may not exist.
eg ensure etc/localtime is a file not a link to zoneinfo which gets removed.
When build_embedded_root completes i386 is an image of the flash
root file system that you want. It has also copied extra into place, and
made a tar image in /tmp/flash.tar
Boot the target using knoppix. I used 5.1.1 and because the ebox-2300 has
only 128M ram I booted in text mode (knoppix 2)
prepare your flash eg
fdisk /dev/hda
mkfs.ext2 /dev/hda1
mount it
mount /dev/hda1 /mnt
cd /mnt
ssh jam@tigger.ws cat /tmp/flash.tar |tar xvf -
and install grub
grub-install --root-directory=/mnt /dev/hda
This works on 5.1.1 fails on earlier versions as /dev/hda is really
/dev/ide/host0/target0/lun0/part and grub gets multiply confused.
You can now boot your thin client!
Files in extra
etc/ init* mnt/ root/ sbin/ usr/ var/
./etc:
inittab.base localtime lts.conf rc.d/ rc.sysinit* ssh/
./etc/rc.d:
nets*
./etc/ssh:
ssh_host_dsa_key ssh_host_key ssh_host_rsa_key
ssh_host_dsa_key.pub ssh_host_key.pub ssh_host_rsa_key.pub
./mnt:
./root:
.bash_history .bash_profile .bashrc .ssh/
./root/.ssh:
authorized_keys id_rsa id_rsa.pub
./usr:
share/
./usr/share:
i18n/ terminfo/
./usr/share/i18n:
locale/
./usr/share/i18n/locale:
en_US
./usr/share/terminfo:
l/
./usr/share/terminfo/l:
linux
./var:
build_initramfs
#!/bin/bash
PKGVER=2.2
REL=0
PKGFORMAT=0.5
CURDIR=`pwd`
PROMPT="y"
CFG_FILE=config.in
[ -f ${CFG_FILE} ] && . ${CFG_FILE}
KERNEL_SRC=${KERNEL_SRC:-/usr/src/linux-2.6.17.3}
LTSP_TREE=${LTSP_TREE:-/opt/ltsp-4.2}
TFTP_DIR=${TFTP_DIR:-/tftpboot/lts}
MODULE_DEST=/tmp/initramfs.d/lib/modules
#===============================================================================
#
# Functions for prompting the user
# "Borrowed" from the pcmcia-cs Config script
#
No Changes till
#===============================================================================
#
# Save the answers to make it easier next time
#
echo "#" >${CFG_FILE}
echo "# config file for LTSP kernel build script (Jim McQuillan)" >>${CFG_FILE}
echo "#" >>${CFG_FILE}
echo "KERNEL_SRC=${KERNEL_SRC}" >>${CFG_FILE}
echo "LTSP_TREE=${LTSP_TREE}" >>${CFG_FILE}
echo "TFTP_DIR=${TFTP_DIR}" >>${CFG_FILE}
#=========================================================================
#
# Now, calculate some settings
#
for TAG in VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION; do
eval `sed -ne "/^$TAG/s/[ ]//gp" ${KERNEL_SRC}/Makefile`
done
KERNEL_VER=${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}
KERNEL_IMG=${KERNEL_SRC}/arch/i386/boot/bzImage
SYSTEM_MAP=${KERNEL_SRC}/System.map
INITRAMFS_IMG=initramfs.ltsp
#=========================================================================
#
# Make sure we have a kernel
#
echo "#####################################################################"
echo ${KERNEL_IMG}
echo
if [ ! -f ${KERNEL_IMG} ]; then
echo ""
echo "Kernel not found at: ${KERNEL_IMG}"
echo ""
exit 1
fi
#=========================================================================
#
# Setup the temporary location where we insert the kernel modules into
# the initramfs tree.
#
[ -d /tmp/initramfs.d ] && rm -rf /tmp/initramfs.d
mkdir /tmp/initramfs.d
cd initramfs
find ./ -print | cpio -pmud /tmp/initramfs.d
cd ..
#=========================================================================
#
# Take the initramfs tree and cpio it into a single file, and gzip it
#
echo "cpio'ing and gzipping the initramfs tree"
cd /tmp/initramfs.d
find ./ -print | cpio -o -H newc | gzip -9 >/tmp/initramfs-${KERNEL_VER}.gz
echo
echo "finished !"
echo
build_embedded_root
#! /bin/sh
echo "===================== rm -fr i386"
rm -fr i386
echo "====== installing files into i386"
tar xzf opt/ltsp/pkg_cache/ltsp-glibc-1.0-1-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-ltsptree-1.17-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-bash-1.1-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-busybox-1.1-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-udev-1.0-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-udev-rules-1.5-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-ncurses-1.1-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-getltscfg-1.3-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-ssh-1.1-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-popt-1.2-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-sysvinit-1.1-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-ltspinfod-1.2-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-e2fsprogs-1.2-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-zlib-1.0-1-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-startsess-1.2-0-i386.tgz
tar xzf opt/ltsp/pkg_cache/ltsp-util-linux-1.1-0-i386.tgz
Add the files you want
cd i386
chmod 755 *
chmod 555 proc
chmod 1777 tmp
echo "=================== rmdir oldroot"
rmdir oldroot
echo "================ rm etc/localtime"
rm etc/localtime
echo "======= rm -fr usr/share/zoneinfo"
rm -fr usr/share/zoneinfo
echo "=========== rm -fr usr/share/i18n"
rm -fr usr/share/i18n/*
echo "========= rm -fr usr/share/locale"
rm -fr usr/share/locale
echo "======= rm -fr usr/share/terminfo"
rm -fr usr/share/terminfo/*
echo "======= find . |cpio -pdv ../i386"
cd ../extra
find . |cpio -pd ../i386
echo "========== tar to /tmp/flash.tar"
cd ../i386
tar cf /tmp/flash.tar *
init
#!/bin/sh
#
# init script for initramfs (Initial ram filesystem) for LTSP
#
# Copyright (C) 2005 James A. McQuillan
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
INITRD_DBG=0
echo "========================================================================="
echo "Running /init"
echo "Mounting /proc"
mount -t proc /proc /proc
[ "${INITRD_DBG}" = "1" ] && exec /bin/sh
echo "Creating new ramdisk to hold our root fs..."
mkdir /newroot
mount -n -t ramfs none /newroot
mkdir /newroot/ideroot
echo "Mounting root filesystem:"
mount -r /dev/hda1 /newroot/ideroot
[ "${INITRD_DBG}" = "2" ] && exec /bin/sh
echo "Setting up the new root ramdisk area..."
#
# Create the directories on our new root filesystem that should NOT
# come from the IDE filesystem that we mount
#
for i in dev home mnt oldroot proc sys tmp var; do
mkdir /newroot/$i
done
#
# Create symlinks to the items we are need from the IDE mounted filesystem
#
for i in bin etc lib libexec root sbin share usr; do
ln -s /ideroot/$i /newroot/$i
done
umount /proc
[ "${INITRD_DBG}" = "3" ] && exec /bin/sh
echo "Doing the switchroot"
exec /sbin/switchroot /newroot
echo "Hmm, switchroot must have failed!!!!"
exit
inittab.base
#
# Copyright (c) 2002 by James A. McQuillan (McQuillan Systems, LLC)
#
# This software is licensed under the Gnu General Public License.
# The full text of which can be found at http://www.LTSP.org/license.txt
#
id:5:initdefault:
si::sysinit:/etc/rc.sysinit
c:345:once:/sbin/sshd
l:5:respawn:/etc/run_ltspinfod
s:0:wait:/bin/sh /etc/rc.shutdown
r:6:wait:/bin/sh /etc/rc.reboot
nets
#!/bin/bash
echo $0
/sbin/ifconfig eth0 ${MY_IP}
menu.lst
default=0
timeout=0
title ThinClient
root (hd0,0)
kernel /bzImage ro root=/dev/hda1
initrd /initramfs.gz
rc.earlysysinit
#!/bin/bash
#
# This script runs AFTER the pivot_root, but BEFORE init runs.
# The primary purpose of this script it to create an /etc/inittab file
# based on entries found in the lts.conf file.
#
PATH=/bin:$PATH; export PATH
cd /
#==============================================================================
#
# We need to do this udev stuff here in the initram, because we need to have
# the device nodes created BEFORE we run /sbin/init. Otherwise, we won't see
# any of any output from our "echo" statements in the /etc/rc.sysinit script.
#
echo "Mounting /sys..."
mount -n -t sysfs none /sys
echo "Mounting /proc..."
mount -n -t proc none /proc
mknod /dev/console c 5 1
mknod /dev/null c 1 3
echo "Starting udevd..."
/sbin/udevd --daemon
echo "Running udevstart to create initial device nodes..."
/sbin/udevstart
ln -snf /proc/self/fd /dev/fd
ln -snf /proc/self/fd/0 /dev/stdin
ln -snf /proc/self/fd/1 /dev/stdout
ln -snf /proc/self/fd/2 /dev/stderr
ln -snf /proc/kcore /dev/core
echo "Mounting devpts..."
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo "Building /etc/inittab"
eval `/bin/getltscfg -a`
#==============================================================================
# Start with the basic inittab file
#
echo "Creating ramdisk on /tmp"
RAMDISK_SIZE=${RAMDISK_SIZE:-"1024"}
/sbin/mke2fs -q -m0 /dev/rd/0 ${RAMDISK_SIZE}
/bin/mount -n /dev/rd/0 /tmp
cat /etc/inittab.base >/tmp/inittab
#
# Add the SCREEN entries
#
for (( SCREEN=1; SCREEN <= 12; SCREEN++ )); do
SCREENOPT=`printf "SCREEN_%02d" ${SCREEN}`
if [ -n "${!SCREENOPT}" ]; then
echo "s${SCREEN}:5:respawn:/bin/startsess tty${SCREEN} /etc/screen_session"\
>>/tmp/inittab
fi
done
echo "Done with early_sysinit"
rc.sysinit
#!/bin/sh
#
# rc.sysinit
#
#==============================================================================
# This script will setup the environment for a diskless workstation, as
# part of the Linux Terminal Server Project (http://www.LTSP.org)
#
PATH=/bin:$PATH; export PATH
. /etc/ltsp_functions
eval `/bin/getltscfg -a`
#==============================================================================
# A place to store information that is retrieved by ltspinfod
#
mkdir /tmp/info
#==============================================================================
# Get the IP address of the default server. This is used for XDM_SERVER,
# TELNET_HOST and SYSLOG_HOST if any of them are not set explicitly.
# Default to '192.168.0.254' if it is NOT set in the config file.
#
DEFAULT_SERVER=${SERVER:-"192.168.0.254"}
reg_info DEFAULT_SERVER
NFS_SERVER=${NFS_SERVER:-${DEFAULT_SERVER}}
reg_info NFS_SERVER
#==============================================================================
# Setup hostname
#
hostname ${HOSTNAME}
echo "Current hostname: ${HOSTNAME}"
reg_info HOSTNAME
>/tmp/mtab
echo "/dev/hda1 /ideroot ext2 ro 1 1" >/tmp/fstab
################################################################################
#
# Setup USB modules
#
echo "Checking for USB host controller..."
for MODULE in ehci-hcd uhci-hcd ohci-hcd; do
modprobe $MODULE >/dev/null 2>&1
done
echo "Checking for USB keyboard and mouse..."
for MODULE in input usbmouse usbkbd; do
modprobe ${MODULE} >/dev/null 2>&1
done
mount -n -t usbfs none /proc/bus/usb
################################################################################
#
# Setup the resolv.conf file
#
if [ "${SEARCH_DOMAIN}" != "" ]; then
echo "search ${SEARCH_DOMAIN}" >/tmp/resolv.conf
fi
DNS_SERVER=${DNS_SERVER:-${DEFAULT_SERVER}}
echo "nameserver ${DNS_SERVER}" >>/tmp/resolv.conf
reg_info DNS_SERVER
#==============================================================================
#
# Mount filesystems
#
echo "Setting up loopback device"
ifconfig lo 127.0.0.1 netmask 255.0.0.0 broadcast 127.255.255.255
echo "Setting up /var"
mkdir /tmp/var
mkdir /var/run
mkdir /var/log
mkdir /var/lock
mkdir /var/lock/subsys
mkdir /var/lib
chmod 0777 /tmp
mkdir /tmp/mnt
mkdir /tmp/.privsep
chown 0:0 /tmp/.privsep
chmod 0700 /tmp/.privsep
#==============================================================================
#
# Check the hostname
#
IPADDR=`getipaddr`
echo "127.0.0.1 localhost" >/tmp/hosts
echo "${IPADDR} ${HOSTNAME}" >>/tmp/hosts
echo "${DEFAULT_SERVER} server" >>/tmp/hosts
#==============================================================================
#
# Start the syslog daemon
#
SYSLOG_HOST=${SYSLOG_HOST:-${DEFAULT_SERVER}}
reg_info SYSLOG_HOST
echo "Starting syslogd"
#syslog.conf not used
#echo "*.* @${SYSLOG_HOST}" >/tmp/syslog.conf
#syslogd -m 60 -R ${SYSLOG_HOST}
syslogd -m 60 -L
dmesg | logger
#==============================================================================
#
# Run the additional rc files.
# These are to make it easier to integrate additional functionality
# into an ltsp system. Add your scripts to etc/rc.d, and put the name
# of the script in the lts.conf file, and it will be executed.
#
for (( RCNUM=1; RCNUM<=10; RCNUM++)); do
RCVAR=`printf "RCFILE_%02d" ${RCNUM}`
RCFILE=${!RCVAR}
if [ -n "${RCFILE}" ]; then
reg_info ${RCVAR}
if [ -x /etc/rc.d/${RCFILE} ]; then
/etc/rc.d/${RCFILE}
else
echo
echo " ERROR: RCFILE_${i} is setup in lts.conf, but"
echo " it does not exist in the /etc/rc.d directory"
echo
echo -n "Press to continue "
read CMD
fi
fi
done
echo "rc.sysinit completed, switching to multi-session mode"
echo
Changing the Image using Local Hard Drive Access
Once an image is stored on a local device it may be necessary to alter the image a later date. There are several methods of doing this, but the easiest may be to boot the client into LTSP and then access the local command line from there, either directly on the client, or via ssh.
When the command line has been accessed it will be necessary to mount the local file systems in order to ammend them. LTSP only loads the most necessary drivers, so those for IDE support are not loaded by default. To activate the ide hard drive drivers, use:
modprobe ide-generic
On the local command line. Now the usual set of tools may be used to manipulate the drives contents. Make sure it is cleanly unmounted before reboot your thin client.
Please add to this section any other relevant drivers that activate other local devices.