Multistrap is a tool that starts off doing essentially same job as Debootstrap, using an entirely different method, and then extends the functionality to try to support automated creation of complete, bootable, root filesystems. It can merge packages from different repositories to make the rootfs. Extra packages are added to the rootfs simply by listing them - all dependencies are taken care of. Current development in multistrap is to support user-specified scripts being added as hooks during the unpacking phase to add customised startup scripts and configure things like device nodes. (For this support, check the package in Debian experimental.)

It was designed primarily for making root filesystems for foreign architecture embedded systems, but in fact can be used for many tasks where one might also use debootstrap.

Its main limitation compared to debootstrap is that it uses apt and dpkg directly so can only work on a debian system - deboostrap depends on nothing but shell, wget, binutils and thus can run pretty-much anywhere.

The main advantage with multistrap is the flexibility to mix packages from different repositories and different suites and manage customised variants with configuration files.

Use

Multistrap is available in squeeze or later. It is written in Perl and uses apt to chose a requested set of packages and their dependencies, then unpack (but not configure) those packages. This leaves a rootfs which just needs to be mounted (or chrooted) and configured with dpkg --configure -a.

Because it uses apt, the mixing of multiple repositories is automatic and apt features such as the use of proxies are all available.

For embedded use some modification to the generated rootfs are aften needed in order to make it bootable enough to be configured. An example is given below of a script to do this. See Customising Rootfs below. Development is ongoing to incorporate such support into hooks which can be called during the multistrap operation.

Options

You can choose to keep a copy of all the debs downloaded during the process (by setting the retainsources option to be the path in which they should be stored. This is the binary packages downloaded, not the corresponding sources. Butr does mean you have a cache of the stuff you made your rootfs with.

Syntax

Basic syntax is:

multistrap [-a arch] -d [dir] -f config_file

nearly all the config is done in the config_file

Configuration

You need a config file which contains at least one repository stanza, like:

[Grip]
packages=ntpdate udev lrzsz netcat telnetd
source=http://trx850:3142/www.emdebian.org/grip
keyring=emdebian-archive-keyring
suite=lenny

And you need to list that stanza title ('Grip') in the 'aptsources' line. And specify one repository in the debootstrap line, which is used as the 'main' repository, from which the list of 'priority: Required' packages is taken.

A directory in which to prepare the rootfs, and an arch to use are the last two items (which can be specified on the command line).

Here is a minimal example, which just does makes a normal Debian rootfs. use a command like: sudo multistrap -a armel -d /multistrap-debian-lenny -f simple-config

---simple-config---

[General]
unpack=true
debootstrap=Grip
aptsources=Debian

[Debian]
packages=
source=http://ftp.uk.debian.org/debian
keyring=debian-archive-keyring
suite=lenny

Grip/Debian example

Here is an example for an arm embedded system (balloonboard), where the rootfs is an emdebian Grip base, augmented by security updates, some extra packages from debian proper and some local packages from an internal repository.

Use it with a command like sudo multistrap -f grip-config

[General]
# arch and directory can be specified on the command line.
arch=armel
directory=/opt/multistrap/
# same as --tidy-up option if set to true
cleanup=true
# retain the sources outside the rootfs for distribution
# specify a directory to which all the .debs can be moved.
# or override with the --source-dir option.
retainsources=
# same as --no-auth option if set to true
# keyring packages listed in each debootstrap will
# still be installed.
noauth=true
# retries not needed.
#retries=5
# extract all downloaded archives
unpack=true
# the order of sections is no longer important.
# debootstrap determines which repository is used to
# calculate the list of Priority: required packages
debootstrap=Grip
# the order of sections is no longer important.
# aptsources is a list of sections to be listed
# in the /etc/apt/sources.list.d/multistrap.sources.list
 of the target.
aptsources=Grip Updates Debian Trx850

[Grip]
packages=ntpdate udev lrzsz netcat telnetd
source=http://www.emdebian.org/grip
keyring=emdebian-archive-keyring
suite=lenny

[Updates]
packages=apt
source=http://www.emdebian.org/grip
keyring=emdebian-archive-keyring
suite=lenny-proposed-updates

# fallback
[Debian]
packages=
source=http://ftp.uk.debian.org/debian
keyring=debian-archive-keyring
suite=lenny

[Trx850]
# space separated package list
packages=sl40-platform-debian balloon3-config
source=http://trx850/debian
suite=goats

Customising Rootfs

For the embedded context an optional rootfs-config script can be run after the rootfs has been generated, because often /etc/fstab, /etc/inittab, /etc/hosts, /etc/securetty, /etc/modules (for example) need to be modified before the rootfs will boot enough to be configured.

Here is an example script (as used on balloon3):

set -e -x

# Emdebian grip compatibility version
# variant tcl-sl40

#pass path to the root. Don't let it run without one as it will break your system
if [ "" = "$1" ] ; then 
  echo "You need to specify a path to the target rootfs"
else
  if [ -e "$1" ] ; then
    ROOTFS="$1"
  else 
    echo "Root dir $ROOTFS not found"
  fi
fi

if [ "/" = "ROOTFS" ] ; then echo "Refusing to change your build system's files" ; fi

#Do things that need to be done at 1st stage so that rootfs will boot.

#Set securetty
#Add modules - e.g. to support USB serial/ethernet console

# specify config to use
if [ "" = "$2" ] ; then 
  CONFIG="files"
fi
if [ -d "$2" ] ; then
  CONFIG="$2"
else
  echo "Config dir $CONFIG not found"
fi

#read in settings for $CONSOLE
if [ -e config ] ; then
  . config
fi

   #add serial ports to securetty - now idempotent
DONECONSOLE=`grep $CONSOLE $ROOTFS/etc/securetty || true`
if [ -z "$DONECONSOLE" ]; then
  echo "$CONSOLE" >> $ROOTFS/etc/securetty
fi
DONETELNET=`grep pts/0 $ROOTFS/etc/securetty || true`
if [ -z "$DONETELNET" ]; then
  echo "pts/0" >> $ROOTFS/etc/securetty
fi
   #put our standard fstab and network and modules files in
if [ ! -d $ROOTFS/etc/network ]; then mkdir -p $ROOTFS/etc/network; fi
if [ ! -d $ROOTFS/etc/init.d ]; then mkdir -p $ROOTFS/etc/init.d; fi
if [ ! -d $ROOTFS/etc/dhcp3 ]; then mkdir -p $ROOTFS/etc/dhcp3; fi
if [ ! -d $ROOTFS/etc/apt/apt.conf.d/ ]; then 
  mkdir -p $ROOTFS/etc/apt/apt.conf.d/
fi
cp -v $CONFIG/fstab $ROOTFS/etc/fstab
cp -v $CONFIG/interfaces $ROOTFS/etc/network/interfaces
cp -v $CONFIG/modules $ROOTFS/etc/modules
cp -v $CONFIG/dhclient.conf $ROOTFS/etc/dhcp3/
cp -v $CONFIG/urandom $ROOTFS/etc/init.d/
cp -v $CONFIG/inittab $ROOTFS/etc/
cp -v $CONFIG/10disablerecommends $ROOTFS/etc/apt/apt.conf.d/
   # make sure sudo uses group sudo by default
rm -f $ROOTFS/etc/sudoers
cp -v $CONFIG/sudoers $ROOTFS/etc/
chmod 400 $ROOTFS/etc/sudoers
   # creating devices
(cd $ROOTFS/dev; /dev/MAKEDEV -v fb )
(cd $ROOTFS/dev; /dev/MAKEDEV -v ttyS0 ttyS1 ttyS2 )
(cd $ROOTFS/dev; /dev/MAKEDEV -v sd )
(cd $ROOTFS/dev; /dev/MAKEDEV -v mtd )
(cd $ROOTFS/dev; mknod rtc c 253 0 )
   #mutter - it makes mtd butnot mtdblock - patch MAKEDEV?
(cd $ROOT/FSdev; /dev//MAKEDEV -v mtdblock{0..7})
(cd $ROOTFS/dev; mkdir -p mtdblock; for DEVICE in 0 1 2 3 4 5 6 7; do mknod mtdblock/$DEVICE b 31 $DEVICE; done)
   # making devices so far missing between old and new multistrap versions preventing
   # chroot from running into newly untarred rootfs
(cd $ROOTFS/dev; /dev/MAKEDEV -v std )
(cd $ROOTFS/dev; /dev/MAKEDEV -v fd )
(cd $ROOTFS/dev; /dev/MAKEDEV -v ptmx )
   # tidy up after old multistrap code
if [ -f $ROOTFS/etc/apt/sources.list.d/sources.list ]; then
   rm -v $ROOTFS/etc/apt/sources.list.d/sources.list
fi
#set hostname
echo sl40 > $ROOTFS/etc/hostname
#be nice to put these in the right places in files (perl -pi?)
echo "127.0.0.1       localhost.localdomain   localhost" > ${ROOTFS}/etc/hosts
echo "127.0.1.1       sl40   sl40" >> ${ROOTFS}/etc/hosts

Environment

To configure the unpacked packages, certain environment variables are needed. In native mode, multistrap does this for you but in cross mode, your setup script will need to set the correct environment for the maintainer scripts to run properly:

Debconf needs to be told to accept that user interaction is not desired:

DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true

Perl needs to be told to accept that no locales are available yet and not to complain:

LC_ALL=C LANGUAGE=C LANG=C

Then, dpkg can configure the packages:

chroot method (PATH = top directory of chroot):

DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
 LC_ALL=C LANGUAGE=C LANG=C chroot /PATH/ dpkg --configure -a

at a login shell:

# export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
# export LC_ALL=C LANGUAGE=C LANG=C 
# dpkg --configure -a

(Note: dpkg needs /proc and /sysfs mounted first. /dev/pts is also recommended. See Customising Rootfs)

Some devices may be able to boot to the point where the packages can be configured (possibly with a first-boot type script in /etc/init.d/) but this method needs to be tested. (Add feedback here if it works or if it fails.)

Other devices will need a temporary or secondary boot environment (possibly a rescue environment) that can provide a login shell and chroot capability on the device itself, at which point the environment variables can be set, /proc and /sysfs mounted inside the chroot and dpkg --configure -a started.

Other methods may involve mounting the multistrap filesystem over a network connection but only if it can be mounted from a device of the same architecture - this method also needs to be tested. (Add feedback here if it works or if it fails.)

The need to configure the packages on the device is an inevitable result of preparing a cross multistrap environment because the maintainer scripts in /PATH/var/lib/dpkg/info/ must be allowed to run binary executables within the multistrap filesystem and also obtain accurate meta data from the system itself (like real values in /proc etc.)

Cross chroot preparation

Multistrap in Debian experimental is being extended to allow the creation of a cross-building chroot which is compatible with [pbuilder]. The principle is to add toolchain packages from the Emdebian repositories and then add hooks to pbuilder which can set up the cross-dependencies.

Cascading configuration

To support multiple variants of a basic (common) configuration, multistrap is also being extended to allow configuration files to include other (more general) configuration files. i.e. the most detailed / specific configuration file is specified on the command line and that file includes another file which is shared by other configurations.

Base file:

/usr/share/multistrap/crosschroot.conf

Variations:

/usr/share/multistrap/armel.conf

Specifying just the armel.conf file will get the rest of the settings from crosschroot.conf so that common changes only need to be made in a single file.

For discussion about these experimental features, ask on the debian-embedded mailing list.