Rfsbuild
A utility for creating Root File Systems.
Tony Egan Jan-08
There's a Quickstart below for the impatient.
Also, all the download links are collected at the bottom of this document.
Introduction
Rfsbuild
is intended to help developers create Root File Systems for
embedded systems. This initial release is aimed at the ARM
architecture. It was tested using qemu-system-arm(0.8.2)
emulating
an ARM-Versatile PB
board and using packages from the Fedora ARM
repositories.
Development Environment
There are two typical environments for embedded system development. One, a native environment where the compilation and build and sometimes even the source editing takes place on the target processor. This requires a target which is powerful enough in terms of speed and resources to support such development.
The other is a cross-development environment where a cross-compiler and build tools are run on a host (typically an x86 PC) to generate code which is then downloaded to the target system. This leverages the power of the PC for the development work and requires nothing from the target except the resources to run the final application.
Rfsbuild
can be used in either of these development environments.
Components
The main rfsbuild
utility is a python program which can run on any
architecture that supports Python 2.4 and yum
. The main tools used by
rfsbuild
are yum
and QEMU. Yum
which is also written in Python
is a package management utility used by some major Linux
distributions, most notably Fedora. Yum
depends on a number of
other utilities and libraries including rpm
, cpio
, libsqlite
, etc..
In fact yum
requires a fairly capable linux environment in order to
function.
QEMU is an emulator which runs on Linux and allows binary code for several
different architectures to run on an Intel-based system. The first release
of rfsbuild
was tested using qemu-system-arm(0.8.2)
emulating an
ARM-Versatile PB
system. This can support the Linux environment required
by yum
.
Operation
Rfsbuild
builds the target RFS from RPM packages. Obviously the RPMs
must have been built for the target architecture. RPMs are available
from many repositories on the Internet. The ARM RPMs used for testing
rfsbuild
were produced by the hard work of Lennert Buytenhek and others and
were obtained here:
- Fedora Core 6
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/6/arm/os/ http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/updates/6/arm http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/extras/6/arm
- Fedora 8
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm
You will also likely be developing your own RPMs containing your
embedded application. You can make these available to rfsbuild
in a
local repository. Rfsbuild
supports NFS access to local repositories
saving you the trouble of setting up web access. This is discussed
further below.
Rfsbuild
takes a list of packages and a list of repositories where the
packages can be found. It passes these on to yum
which installs the RPM
packages into the target RFS. Yum
takes care of all the dependency checking,
downloading and installation of the packages. Yum
must execute on the target
architecture in order to do this properly hence the need for a native system
or an emulated system (QEMU) on which to execute yum
.
Here is a sample invocation of rfsbuild
on a PC host:
rfsbuild --repo=c6,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/6/arm/os/ --repo=c6u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/updates/6/arm --repo=c6e,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/extras/6/arm --package=bash --package=nfs-utils --server=./startQEMU --rfspath=/newrfs --initrd=0 --verbosity=2
The repositories, specified by --repo
, are the Fedora ARM
repositories mentioned earlier. Each one is prefixed by an arbitrary
unique name, e.g. c6e, which yum
will use in its reporting. RPMs are
specified by --package
. You can specify as many repositories and
packages as you like. Keep in mind that dependencies will be handled
automatically. So when you specify --package=bash
, yum
will check
its dependencies and install all the other packages required by bash.
More on this later.
The --server=./startQEMU
parameter specifies a script which starts the
qemu-system-arm emulator. This is a bash script in this release, but it
could be any type of executable. There are some requirements for how it
invokes QEMU which we will discuss below. Rfsbuild
doesn't check the return
from this script, but it may in future revisions.
Rfsbuild
uses NFS mounted directories to share information between the
PC host and QEMU. The --rfspath=/newrfs
parameter must specify an
exported directory which can be mounted via NFS from QEMU. The new
RFS will be built in a unique subdirectory of this directory,
(e.g. /newrfs/build-WuCZTU-12311901/install_root
). Existing RFS
sub-directories will not be deleted or overwritten by rfsbuild
.
The --initrd
parameter specifies that an init ramdisk image should
be created from the new RFS. It will appear at
/newrfs/build-WuCZTU-12311901/initrd.gz
for the example above.
The zero means that rfsbuild
will automatically set the size of
the initrd.
The --verbosity
parameter tries to reassure the user that yum
is actually
doing something.
When it starts, rfsbuild
first prints some informative messages then it
copies itself to the directory specified by --rfspath
and creates a run
script rfsb
which will be used by QEMU to start rfsbuild
in the emulated
environment. Rfsb is created in rfspath. Rfsbuild
then invokes the script
specified by --server
.
The program specified by --server
is responsible for starting
QEMU. The startQEMU
script provided tries to do this in a general
way so that it can easily be adapted to different setups. Most of the
parameters affecting the networking are specified at the top of the
file. You need to edit this to suit your network.
By the way the startQEMU
script uses sudo
. You should make
sure you have a line like:
egan ALL=(ALL) NOPASSWD: ALL
to your /etc/sudoers file.
Installing Rfsbuild
Simply download the RPM to your x86 host and enter:
The RPM installs /usr/bin/rfsbuild
and some miscellaneous support
files in /usr/share/rfsbuild
- Copy
startQEMU
andqemu-ifup
from/usr/share/rfsbuild
to a work
directory where you can modify them and where you will invoke rfsbuild
.
- Download the ARM kernel and
initrd
to the same work directory. zImage
-- kernel used by QEMUinitrd.gz
-- initrd used by QEMU
You may want to rename these and create links using the original names
in your work directory. Then when you want to test a new initrd or a
a new kernel you can just change the links and keep the originals.
Keep in mind that startQEMU
will look for the files "zImage" and
"initrd.gz".
- Create a directory for your new RFS and export it via NFS.
Starting QEMU
Please see the local
Architectures/ARM/HowToQemu and
Fabrice Bellard's QEMU site for
details of how to set up QEMU to emulate ARM. I have attached an ARM
kernel and initrd which are convenient for rfsbuild
development. It
is assumed that your QEMU installation is able to run the kernel and
initrd provided and is able to access the host via the network so that
it can NFS mount directories. It must also have access to the
repositories specified by the rfsbuild
command. I.e. it must be able
to access the internet.
A key feature of startQEMU
which must not be affected by any
modifications is its method of passing the path of the rfsb
run
script on the kernel command line. When QEMU starts running the
kernel and initrd provided, the root user is automatically logged in
and the /root/startrfsb
script extracts this information from
/proc/cmdline
. Startrfsb
then mounts the directory specified on the
command line and invokes the rfsb
script. The rfsb
script then starts
rfsbuild
in the QEMU environment. It sounds more complicated than it is.
You can test ./startQEMU
by invoking it directly on the x86 host without
any parameters. QEMU should start, boot Linux and automatically logon root
and run the startrfsb
script. This should fail harmlessly and leave you in
QEMU at the Linux shell prompt.
To exit QEMU you can enter [Ctl-a] [c] to get to the QEMU monitor and then [q] to quit. If this fails you can always open another shell on the host and enter:
killall -9 qemu-system-arm
To test NFS from QEMU, make sure NFS is enabled on the host and /newrfs is exported. Then invoke:
./startQEMU ip:/newrfs/abcde
Where ip is the IP address of your x86 host. QEMU should start, boot Linux
and automatically logon root and run the startrfsb
script. The startrfsb
script should extract the NFS path from the kernel command line and mount the
ip:/newrfs
directory. The script should then fail harmlessly and leave you
in QEMU Linux at the shell prompt. This is assuming there is no abcde
script in /newrfs
.
Then you can check that the NFS mount is working in QEMU :
df -a /
If NFS is not working it is likely that QEMU networking is not set up
properly or NFSD on the host is not successfully exporting /newrfs
.
Kill QEMU again.
At this point you should have ./startQEMU
& NFS working. If not, you need to
do some troubleshooting before continuing.
Local Repositories
At some point you will probably want to create your own RPMs in a repository
on a local machine. Once you have created the RPMS just copy them to a
directory on your host, e.g. /repo
and then enter:
createrepo /repo
You can then follow the procedure for making this repository accessible via
http so that it can be accessed from anywhere. Rfsbuild
supports a simpler
alternative by using a naming convention to specify local repositories which
are accessible via NFS. On the host ensure that the repository directory is
exported via NFS. Then the --repo
parameter in the rfsbuild
command
will look like:
--repo=nfs10.0.0.105,file:///repo
The three letters "nfs
" are required, followed by the IP
address
of the NFS server, followed by ,
file :// followed by the
exported path to the repo. When
rfsbuild
sees this name it will
create an alias nfs1
and mount the repository via NFS prior to
invoking yum
.
Keystone Package
This idea comes from David Zeuthen's livecd-creator program, as does a lot of
the code in rfsbuild
. We saw above how to specify packages when invoking
rfsbuild
. This can get a bit unwieldy when there are a lot of packages.
It would be nice if we could specify one package which in turn specified
all the packages we want to install. That is the first function of a
Keystone Package.
The other issue we haven't discussed is the ever present need to fix, tweak,
modify and customize the RFS after the packages have been installed. RPMs
already support post-install scripts but an embedded application often
requires different modifications than the desktop application most RPMs are
designed for. Different target devices usually require different post
install modifications to the RFS. This is also handled by the keystone
package.
A Keystone Package is an RPM which encapsulates a set of requirements for a
particular RFS. So, you could have a Keystone Package for your router and
another Keystone Package for your NAS device and another for your music
server. Each would specify the specific set of packages and modifications
required for each of those unique devices.
Keystone Packages take advantage of the version control, distribution and
install mechanisms already in place for RPMs. You just give a user the
appropriate version Keystone Package for his device and it encapsulates all
the information needed to generate the RFS for that device perfectly. You
don't even have to give him the package. If it is available in a repository
on the Internet he just needs to specify it as a parameter to rfsbuild
, and
it will automatically be downloaded from the repository and installed.
The mechanism is very simple. A Keystone Package contains one or more
special conf
scripts which are installed in a dedicated directory,
/etc/rfsbuild
. The package can contain miscellaneous other files also as
needed just like any RPM but the conf
scripts are special.
During execution rfsbuild
first invokes yum
to install the packages specified
on the command line. Yum
performs its normal dependency checking so more
packages than those explicitly specified using --package
arguments may be
installed at this time.
When this is finished rfsbuild
invokes any conf
scripts it finds in
/etc/rfsbuild
with the parameter pkgadd
. See the
10-fedora-rfsbuild-mini-glibc.conf
script included in the rfsbuild
package.
If there are multiple conf
scripts they will be invoked in alphabetical
order. This call to the conf scripts will return a set of additional
packages to be installed. Rfsbuild
invokes yum
a second time to install
these. Again, yum
checks dependencies and installs all required packages.
When the second run of yum
is complete rfsbuild
invokes the
conf
scripts in /etc/rfsbuild
again but this time with the
parameter post
. This causes the commands listed in the post
section of the conf scripts to be executed in a chroot context using
the new RFS as root. I.e the new RFS appears to be the root directory
while these post
commands are executed. So any command specified
in the post
section must be available in the new RFS. This is
where fixes, tweaks and modifications to the RFS are performed.
The sample Keystone Package included in the rfsbuild
RPM,
fedora-rfsbuild-mini-glibc.rpm
uses the conf script
10-fedora-rfsbuild-mini-glibc.conf
. This Keystone Package was
used to build the initrd supplied here for use with rfsbuild
. So, to
recreate the RFS and initrd you would simply need to invoke rfsbuild
as follows:
rfsbuild --repo=nfs10.0.0.105,file:///repo
--repo=f8,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages
--repo=f8u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm
--package=fedora-rfsbuild-mini-glibc --package=findutils --server=./startQEMU --rfspath=/newrfs --initrd=0
--verbosity=2
This assumes you have put fedora-rfsbuild-mini-glibc.rpm in your local
NFS exported repository 10.0.0.105:/repo.
Quickstart
For those who like to live dangerously.... :-)
ARM Host
If you have an ARM host that is already running Linux and supports python
and yum
then you can simply install rfsbuild
on that machine and run it
locally to build ARM RFSs. E.g.:
rfsbuild --repo=/repo
--repo=c6,http://fedora-arm.wantstofly.org/pub/fedora/linux/core/6/arm/os
--repo=c6u,http://fedora-arm.wantstofly.org/pub/fedora/linux/core/updates/6/arm
--repo=c6e,http://fedora-arm.wantstofly.org/pub/fedora/linux/extras/6/arm
--package=fedora-rfsbuild-mini-glibc
--rfspath=/newrfs --initrd=0 --verbosity=2
Here the /repo
directory is a local repository which contains the
fedora-rfsbuild-mini-glibc
keystone package. /newrfs
is where we want the
new RFS to be built. We are using ARM packages from the fedora-arm
repositories. Since the server
parameter is absent the build will be
performed locally.
X86 Host and ARM QEMU server
How to set up an x86 host and an ARM QEMU environment so that rfsbuild
can be invoked on the host to initiate a build under QEMU.
- Install
rfsbuild
on the X86 host if you haven't already done so.
This will create a directory /usr/share/rfsbuild containing the following:
- README
- startQEMU -- support utility for starting QEMU
- qemu-ifup -- network configuration used by QEMU
- 10-fedora-rfsbuild-mini-glibc.conf -- sample conf script
- fedora-rfsbuild-mini-glibc.rpm -- sample keystone rpm
- fedora-rfsbuild-mini-glibc.spec -- sample spec file
- startrfsb -- this is installed in the /root directory of QEMU ARM linux. It starts a build when QEMU boots Linux.
- Copy startQEMU and qemu-ifup to a work directory where you can modify them and where you will invoke rfsbuild.
- Download the ARM kernel and initrd to the same work directory.
- zImage -- kernel used by QEMU
- initrd.gz -- initrd used by QEMU
- Install QEMU and get it working with startQEMU and the zImage and initrd.gz you downloaded. This includes setting up networking via the TUN/TAP interface. You can find some help at [1] and [2] .
AT this stage invoking ./startQEMU
with no parameters should start
QEMU, boot Linux and automatically logon root and try to start an RFS
build. This should fail harmlessly and leave you in QEMU Linux at the
shell prompt.
To exit QEMU you can enter Ctl-a c to get to the QEMU monitor and then q to
quit. If this fails you can always open another shell on the host and enter:
killall -9 qemu-system-arm
- Create the RFS directory:
mkdir /newrfs
- Export /newrfs via NFS. (
Edit /etc/exports and restart NFS daemons
)
Check that it is accessible from QEMU.
./startQEMU 10.0.0.105:/newrfs/abcde
Replace 10.0.0.105 with the IP address of your PC. This should behave the
same as described above except you should see 10.0.0.105:/newrfs
mounted on /newrfs when you check under QEMU:
df -a /
If NFS is not working it is likely that QEMU networking is not set up
properly or NFSD on the host is not successfully exporting /newrfs
.
Kill QEMU again.
At this point you should have ./startQEMU
& NFS
working. If
not, you need to do some troubleshooting before continuing.
- Finally, invoke a test run of
rfsbuild
:
rfsbuild
--repo=f8,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages
--repo=f8u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm --package=setup
--server=./startQEMU --rfspath=/newrfs --verbosity=2
This should create a skeleton RFS with only the setup
package installed.
Miscellaneous Options
- --exclude is used to omit packages from package groups.
Yum
, and consequently rfsbuild will not exclude a package required by dependencies.
- --cache specifies a
yum
cache to use. It's not expected to be empty.
- --cacheonly tells
yum
to use cache only i.e. yum -C
- --initramfs create an initramfs archive. Not working.
- --runscript the name of the script that
rfsbuild
creates in /newrfs
for QEMU to run. The default is rfsb
- --label the suffix of the RFS build directory. The default is constructed from the date. Not working correctly yet since QEMU is not initializing the date correctly.
For those of you (like me) who don't like repeatedly typing long command lines I've included a sample script in the RPM called go
. Simply invoke it as:
./go fc8
This will re-create the sample initrd.
By the way the samples are just that "samples" to show you how rfsbuild
works. You will see some harmless error messages as you run the sample commands given above. The sample conf
script performs some very crude reductions on the RFS. Clean up and more structured pruning are left as an exercise for the reader. Useful revisions to the scripts and suggestions for improvement will be welcomed on the Fedora ARM mailing list.
Downloads