Chapter 6. Root Filesystem Content

One of the last operations conducted by the Linux kernel during system startup is mounting the root filesystem. The root filesystem has been an essential component of all Unix systems from the start. The root filesystem’s current organization is a bit idiosyncratic and contains some redundancy because of how it grew over time and was influenced by Unix developments. I will not attempt to cover the reasons for the current structure and underlying conventions. Instead, I will explain how to organize the various components to adhere to the accepted standards and, thereby, obtain a functional root filesystem. In the process, we will use many of the components we built earlier, such as the kernel modules and the C library.

First, we will discuss the basic root filesystem structure. Then, we will discuss how and where to install the system libraries, the kernel modules, kernel images, device nodes, main system applications, and custom applications. Finally, we will discuss how to configure the system initialization scripts. At the end of this chapter, you will have a fully functional root filesystem for your target. In the next chapters, we will discuss how you can place this root filesystem on an actual filesystem type on a storage device for use in your target.

Basic Root Filesystem Structure

The top-level directories in the root filesystem each have a specific use and purpose. Many of these are meaningful only in multiuser systems in which a system administrator is in charge of many servers and/or workstations used by different users. In most embedded Linux systems, where there are no users and no administrators, the rules to build a root filesystem can be loosely interpreted. This doesn’t mean that all rules can be violated, but it does mean that breaking some rules will have little to no effect on the system’s proper operation. Interestingly, even mainstream commercial distributions for workstations and servers do not always adhere to the established rules for root filesystems.

The “official” rules to build a root filesystem are contained in the Filesystem Hierarchy Standard (FHS) introduced in Chapter 1. The document is less than 30 pages long and is fairly easy to read. If you are looking for answers or clarifications regarding the root filesystem, the FHS is probably the best place to start. Table 6-1 provides the complete list of root filesystem top-level directories and their content as specified by the FHS.

Table 6-1. Root filesystem top-level directories

Directory

Content

bin

Essential user command binaries

boot

Static files used by the bootloader

dev

Devices and other special files

etc

System configuration files, including startup files

home

User home directories, including entries for services such as FTP

lib

Essential libraries, such as the C library, and kernel modules

mnt

Mount point for temporarily mounted filesystems

opt

Add-on software packages

proc

Virtual filesystem for kernel and process information

root

Root user’s home directory

sbin

Essential system administration binaries

tmp

Temporary files

usr

Secondary hierarchy containing most applications and documents useful to most users, including the X server

var

Variable data stored by daemons and utilities

If you are using Linux for your day-to-day work, you are already familiar with some of these directories. Nevertheless, let’s take a closer look at the content of a typical root filesystem for use in an embedded Linux system.

First, all the directories that pertain to providing a multiuser extensible environment, such as /home, /mnt, /opt, and /root, can be omitted. We could trim the root filesystem even further by removing /tmp and /var, but these omissions may jeopardize the operation of certain programs. I do not encourage such a minimalistic approach.

Tip

This discussion does not revolve around size issues, but rather functionality. In fact, omitting a directory entry changes little to the resulting root filesystem’s size. The reason I state that /home can be omitted, for example, is that even if it were present in an embedded Linux system, it would be empty, because its content, as prescribed by the FHS, is useful only in workstation and server setups.

Depending on your bootloader and its configuration, you may not need to have a /boot directory. This will depend on whether your bootloader can retrieve kernel images from your root filesystem before your kernel is booted. You will be able to decide whether you should use a /boot directory and how to use it for your target after you read Chapter 9. Of course, you can redesign the root filesystem at any later time if need be.

The remaining directories, /bin, /dev, /etc, /lib, /proc, /sbin, and /usr, are essential.

At the extreme, you could omit /proc, which is useful only for mounting the virtual filesystem that has the same name. However, it would then become very hard to understand what is happening on your target if you needed to analyze it in the field. If you are very tight for space, you can configure your kernel without /proc support, but I encourage you to enable it whenever possible.

Two of the root directories, /usr and /var, have a predefined hierarchy of their own, much like that of the root directory. We will briefly discuss these hierarchies as we populate both directories in the steps below.

To work on the root filesystem, let’s move into the directory we created for this purpose:

$ cd ${PRJROOT}/rootfs

We now create the core root filesystem directories required for our system:

$ mkdir bin dev etc lib proc sbin tmp usr var
$ chmod 1777 tmp

Notice that we did not create /boot. We will come back to it later and create it if it becomes necessary. Also, note that we changed the permissions for the /tmp directory to turn the “sticky bit” on. This bit in the directory permissions field will ensure that files created in the /tmp directory can be deleted only by the user that created them. Though most embedded Linux systems are single-user systems, as I said above, there are cases in which embedded applications must not run with root privileges, hence the need to follow some basic rules about root filesystem permission bits. The OpenSSH package we discuss in Chapter 10, for example, is such an application.

We can proceed with the creation of the /usr hierarchy:

$ mkdir usr/bin usr/lib usr/sbin

On a fully featured root filesystem, the /usr directory usually contains many more entries. A simple demonstration of this is easily conducted by typing ls -al /usr on your workstation. You will find directories such as man, src, and local. The FHS contains a section addressing the layout of this directory in detail. For the purposes of most embedded Linux systems, nonetheless, the three directories we created will suffice.

The last entries to create are in the /var directory:

$ mkdir var/lib var/lock var/log var/run var/tmp
$ chmod 1777 var/tmp

Here, too, this directory usually contains many more entries. Directories such as cache, mail, and spool are useful for a workstation or a server, but few embedded systems need those directories. The directories we created are the bare minimum required for the normal operation of most applications found in an embedded Linux system. Of course, if you need functionality such as web page serving or printing, then you may want to add some of the additional directories required by the applications providing this functionality. See the FHS and the documentation provided with your application to find out your actual requirements.

With the root filesystem skeleton now ready, let’s place the various software components in their appropriate locations.

Libraries

In Chapter 4 we discussed how to build, install, and use the GNU C library and its alternatives for application development. Here, we will discuss how to install those same libraries on the target’s root filesystem so that they can be used at runtime by the applications we develop. We will not discuss diet libc, because it is mainly used as a static library.

glibc

As I said earlier, the glibc package contains a number of libraries. You can see the entire list of libraries installed during the package’s build process by looking at your ${TARGET_PREFIX}/lib directory. This directory contains mainly four types of files:

Actual shared libraries

These files’ names are formatted as lib LIBRARY_NAME-GLIBC_VERSION .so, where LIBRARY_NAME is the name of the library and GLIBC_VERSION is the version of the glibc package you are using. The name of the math library for glibc 2.2.3 is libm-2.2.3.so.

Major revision version symbolic links

Major revision versions do not follow the same numbering as the actual glibc version. The major revision version for the actual shared C library in glibc 2.2.3, libc-2.2.3.so, is 6. In contrast, the major revision version for libdl-2.2.3.so is 2. The names of the symbolic links for the major revision version are formatted as lib LIBRARY_NAME .so. MAJOR_REVISION_VERSION, where MAJOR_REVISION_VERSION is the major revision version of the library. For the actual C library, for instance, the symbolic link is libc.so.6. For libdl, it is libdl.so.2. Once a program has been linked to a library, it will refer to this symbolic link. At startup, the loader will therefore look for this file before loading the program.

Version-independent symbolic links to the major revision version symbolic links

The role of these links is to provide a universal entry for all the programs that need to link with a particular library, regardless of the actual major revision or the version of glibc involved. These symbolic links are typically formatted as lib LIBRARY_NAME .so. For example, libm.so points to libm.so.6, which itself points to the actual shared library, libm-2.2.3.so. The only exception to this is libc.so, which, as I said in Chapter 4, is a link script. The version-independent symbolic link is the one used when linking programs.

Static library archives

These archives are used by applications that choose to link statically with a library. The names of these archives are formatted as lib LIBRARY_NAME .a. The static archive for libdl, for instance, is libdl.a.

You will also find some other types of files in ${TARGET_PREFIX}/lib, such as crti.o and crt1.o, but you will not need to copy these to your target’s root filesystem.

Out of the four types of files described above, we will need only two for each library: the actual shared libraries and the major revision version symbolic links. The two other file types are needed only when linking executables and are not required for the runtime operation of our applications.

In addition to the library files, we will need to copy the dynamic linker and its symbolic link. The dynamic linker itself follows the naming convention of the various glibc libraries, and is usually called ld- GLIBC_VERSION .so. In what is probably one of the most bizarre aspects of the GNU toolchain, however, the name of the symbolic link to the dynamic linker depends on the architecture for which the toolchain has been built. If the toolchain is built for the i386, the ARM, the SuperH, or the m68k, the symbolic link to the dynamic linker is usually called ld-linux.so. MAJOR_REVISION_VERSION. If the toolchain is built for the MIPS or the PowerPC, the symbolic link to the dynamic linker is usually called ld.so. MAJOR_REVISION_VERSION.

Before we actually copy any glibc component to the target’s root filesystem, however, we need to select the glibc components required for our applications. Table 6-2 provides the description of all the components in glibc[1] and provides inclusion guidelines for each component. In addition to my guidelines, you will need to evaluate which components your programs need, depending on their linking.

Table 6-2. Library components in glibc and root filesystem inclusion guidelines

Library component

Content

Inclusion guidelines

ld

Dynamic linker.[2]

Compulsory.

libBrokenLocale

Fixup routines to get applications with broken locale features to run. Overrides application defaults through preloading. (Need to use LD_PRELOAD).

Rarely used.

libSegFault

Routines for catching segmentation faults and doing backtraces.

Rarely used.

libanl

Asynchronous name lookup routines.

Rarely used.

libc

Main C library routines.

Compulsory.

libcrypt

Cryptography routines.

Required for most applications involved in authentication.

libdl

Routines for loading shared objects dynamically.

Required for applications that use functions such as dlopen( ).

libm

Math routines.

Required for math functions.

libmemusage

Routines for heap and stack memory profiling.

Rarely used.

libnsl

NIS network services library routines.

Rarely used.

libnss_compat

Name Switch Service (NSS) compatibility routines for NIS.

Loaded automatically by the glibc NSS.[3]

libnss_dns

NSS routines for DNS.

Loaded automatically by the glibc NSS.

libnss_files

NSS routines for file lookups.

Loaded automatically by the glibc NSS.

libnss_hesiod

NSS routines for Hesiod name service.

Loaded automatically by the glibc NSS.

libnss_nis

NSS routines for NIS.

Loaded automatically by the glibc NSS.

libnss_nisplus

NSS routines for NIS plus.

Loaded automatically by the glibc NSS.

libpcprofile

Program counter profiling routines.

Rarely used.

libpthread

Posix 1003.1c threads routines for Linux.

Required for threads programming.

libresolv

Name resolver routines.

Required for name resolution.

librt

Asynchronous I/O routines.

Rarely used.

libthread_db

Thread debugging routines.

Loaded automatically by gdb when debugging threaded applications. Never actually linked to by any application.

libutil

Login routines, part of user accounting database.

Required for terminal connection management.

[2] This library component is actually not a library itself. Instead, ld.so is an executable invoked by the ELF binary format loader to load the dynamically linked libraries into an application’s memory space.

[3] See Chapter 4 for details.

Apart from keeping track of which libraries you link your applications with, you can usually use the ldd command to find out the list of dynamic libraries that an application depends on. In a cross-platform development environment, however, your host’s ldd command will fail when provided with target binaries. You could still use the cross-platform readelf command we installed in Chapter 4 to identify the dynamic libraries that your application depends on. Here is an example showing how the BusyBox utility’s dependencies can be retrieved using readelf:

$ powerpc-linux-readelf -a ${PRJROOT}/rootfs/bin/busybox | 
               > grep "Shared library"
 0x00000001 (NEEDED)                     Shared library: [libc.so.0]

Ideally, however, if you installed uClibc, you should use the cross-platform capable ldd-like command installed by uClibc. For our control module target, which is based on a PowerPC board, the command’s name is powerpc-uclibc-ldd. This way, you can build the list of libraries your target binaries depend on. Here are the dependencies of the BusyBox utility, for example (one line has been wrapped to fit the page):

$ powerpc-uclibc-ldd ${PRJROOT}/rootfs/bin/busybox
        libc.so.0 => /home/karim/control-project/control-module/tools/uclibc/lib/
            libc.so.0
/lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0

Having determined the library components we need, we can copy them and the relevant symbolic links to the /lib directory of the target’s root filesystem. Here is a set of commands that copy the essential glibc components:

$ cd ${TARGET_PREFIX}/lib
$ for file in libc libcrypt libdl libm 
               > libpthread libresolv libutil
               > do
               > cp $file-*.so ${PRJROOT}/rootfs/lib
               > cp -d $file.so.[*0-9] ${PRJROOT}/rootfs/lib
               > done
$ cp -d ld*.so* ${PRJROOT}/rootfs/lib

The first cp command copies the actual shared libraries, the second one copies the major revision version symbolic links, and the third one copies the dynamic linker and its symbolic link. All three commands are based on the rules outlined earlier in this section regarding the naming conventions of the different files in ${TARGET_PREFIX}/lib. The -d option is used with the second and third cp commands to preserve the symbolic links as-is. Otherwise, the files pointed to by the symbolic links are copied in their entirety.

Of course, you can remove the libraries that are not used by your applications from the list in the set of commands above. If you would rather have the complete set of libraries included in glibc on your root filesystem, use the following commands:

$ cd ${TARGET_PREFIX}/lib
$ cp *-*.so ${PRJROOT}/rootfs/lib
$ cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib
$ cp libSegFault.so libmemusage.so libpcprofile.so 
               > ${PRJROOT}/rootfs/lib

If you have applications that use the glibc NSS, don’t forget to copy the libnss_ SERVICE libraries you need to your target’s root filesystem. libnss_files and libnss_dns are the ones most often used. You will also need to copy the sample nsswitch.conf provided with glibc to your target’s /etc directory and customize it to your setup:[4]

$ cp ${PRJROOT}/build-tools/glibc-2.2.1/nss/nsswitch.conf 
               > ${PRJROOT}/rootfs/etc

Whether you copy all or part of the glibc libraries, you will notice that some of these libraries are large. To reduce the size of the libraries installed, we can use the cross-platform strip utility we built earlier. Be careful not to strip the original libraries, since you would have to install them all over again. Strip the libraries only after you copy them to the root filesystem:

$ powerpc-linux-strip ${PRJROOT}/rootfs/lib/*.so

On my control module, the ${PRJROOT}/rootfs/lib directory with all the glibc libraries weighs around 10 MB before stripping. By stripping all the libraries, the directory is reduced to 2.5 MB.

The glibc components have now been installed on the target’s root filesystem and are ready to be used at runtime by our applications.

uClibc

As with glibc, uClibc contains a number of libraries. You can see the entire list by looking at your ${PREFIX}/uclibc/lib directory. This directory contains the same four different types of files as the glibc directory.

Because uClibc is meant to be a glibc replacement, the names of the uClibc components and their use is identical to the glibc components. Hence, you can use Table 6-2 for uClibc components. Note, however, that not all glibc components are implemented by uClibc. uClibc implements only ld, libc, libcrypt, libdl, libm, libpthread, libresolv, and libutil. Use the same method as described for glibc to identify the uClibc components you will need on your target.

Having determined the list of components we need, we can now copy them and their relevant symbolic links to the /lib directory of our target’s root filesystem. The following set of commands copies the essential uClibc components:

$ cd ${PREFIX}/uclibc/lib
$ for file in libuClibc ld-uClibc libc libdl 
               > libcrypt libm libresolv libutil
               > do
               > cp $file-*.so ${PRJROOT}/rootfs/lib
               > cp -d $file.so.[*0-9] ${PRJROOT}/rootfs/lib
               > done 

The commands are likely to report that two files haven’t been found:

cp: libuClibc.so.[*0-9]: No such file or directory
cp: libc-*.so: No such file or directory

This is not a problem, since these files don’t exist. The set of commands above is meant to be easy to type in, but you could add conditional statements around the cp commands if you prefer not to see any errors.

As with glibc, you can modify the list of libraries you copy according to your requirements. Note that, in contrast to glibc, you will not save much space by copying only a select few uClibc components. For my control module, for instance, the root filesystem’s /lib directory weighs around 300 KB when all the uClibc components are copied. The following commands copy all uClibc’s components to your target’s root filesystem:

$ cd ${PREFIX}/uclibc/lib
$ cp *-*.so ${PRJROOT}/rootfs/lib
$ cp -d *.so.[*0-9] ${PRJROOT}/rootfs/lib

There is no need to strip uClibc components, since they were already stripped by uClibc’s own build scripts. You can verify this using the file command.

Kernel Modules

In Chapter 5, we built the kernel modules and installed them in a temporary directory, ${PRJROOT}/images. We are now ready to copy these modules to their final destination in the target’s /lib directory.

Since you may have compiled many kernels to test for your target, you will need to select which set of kernel modules to copy to the root filesystem. In the case of my control module, for example, I chose a 2.4.18 kernel for my target. The following command copies that kernel’s entire modules directory to the root filesystem:

$ cp -a ${PRJROOT}/images/modules-2.4.18/* ${PRJROOT}/rootfs

We the use cp’s -a option here to copy the files and directories in archive mode. This has the effect of preserving file attributes and links, and copying directories recursively. Note that there is no need to explicitly append the /lib/modules path to ${PRJROOT}/rootfs in the above command because of the way we installed the modules in the ${PRJROOT}/images/modules-2.4.18 directory in Chapter 5.

That’s it; the kernel modules are now ready for use on your target. You may also want to add a /etc/modules.conf file to automate the loading of the modules during system operation. See Chapter 11 in Linux Device Drivers for more details regarding module management and the use of the /etc/modules.conf file.

Kernel Images

As I said earlier, the presence of the actual kernel image on your root filesystem largely depends on your bootloader’s capabilities. If you anticipate that your bootloader’s setup will provide for booting a kernel from the root filesystem, you may copy the kernel image to your target’s root filesystem at this time:

$ mkdir ${PRJROOT}/rootfs/boot
$ cd ${PRJROOT}/images
$ cp zImage-2.4.18 ${PRJROOT}/rootfs/boot

In addition to the kernel image, you may want to make it a standard practice to copy the configuration file used to create the kernel so that you may be able to service units for which the original project workspace may be lost:

$ cp 2.4.18.config ${PRJROOT}/rootfs/boot

Because we are discussing the actual bootloader setup in Chapter 9, there is nothing more to be done here about the kernel’s setup for now. We will continue the kernel image’s setup later.

Device Files

Following Unix tradition, every object in a Linux system is visible as a file, including devices.[5] All the device files (a.k.a. device “nodes”) in a Linux root filesystem are located in the /dev directory. Most workstation and server distributions come packaged with a /dev directory containing more than 2,000 entries to account for all the possible system variations. Because embedded Linux systems are custom built, there is no need to fill the target’s /dev directory with as many entries as a Linux workstation or server. Only the entries required for the system’s proper operation should be created.

Identifying which entries you need can be difficult if you don’t have the required information. If you choose to use devfs instead of creating fixed static device entries, you will avoid having to look for the device information. Devfs has not been widely adopted, however, and static device entries are still the norm.

The official source of information for static device major and minor numbers is the Documentation/devices.txt file in the kernel sources. You can consult this file whenever you are uncertain about the name or numbering of a certain device.

Table 6-3 lists the most basic entries you will need in your /dev directory. Depending on your particular setup, you will probably need to add a few extra entries. In some cases, you may even need to use entries other than the ones listed below. On some systems, for example, the first serial port is not ttyS0. Such is the case of SuperH-based systems, for instance, where the first serial port is ttySC0 (major number: 204, minor number: 8), and StrongARM-based systems where the first serial port is ttySA0 (major number: 204, minor number: 5).

Table 6-3. Basic /dev entries

Filename

Description

Type

Major number

Minor number

Permission bits

mem

Physical memory access

char

1

1

600

null

Null device

char

1

3

666

zero

Null byte source

char

1

5

666

random

Nondeterministic random number generator

char

1

8

644

tty0

Current virtual console

char

4

0

600

tty1

First virtual console

char

4

1

600

ttyS0

First UART serial port

char

4

64

600

tty

Current TTY device

char

5

0

666

console

System console

char

5

1

600

C hapter 6 of Running Linux explains how to create device files. Essentially, you will need to use the mknod command for each entry to be created. In contrast to most other commands we have used up until now, you will need to be logged in as root to use this command. Remember to log out from the root user mode once you are done creating the device files.

Here is a simple example showing the creation of the first few entries in Table 6-3:

$ cd ${PRJROOT}/rootfs/dev
$ su -m
Password:
# mknod -m 600 mem c 1 1
# mknod -m 666 null c 1 3
# mknod -m 666 zero c 1 5
# mknod -m 644 random c 1 8
            ...
# exit

In addition to the basic device files, there are a few compulsory symbolic links that have to be part of your /dev directory. Table 6-4 provides a description of these symbolic links. As with other symbolic links, you can use the ln -s command to create these links.

Table 6-4. Compulsory /dev symbolic links

Link name

Target

fd

/proc/self/fd

stdin

fd/0

stdout

fd/1

stderr

fd/2

We have now prepared a basic /dev directory for our target. We will come back to this directory later to create some additional entries for some types of storage devices. You can consult Linux Device Drivers for a more complete discussion about device files and device drivers in general.

Main System Applications

Beyond the kernel’s functionality and the root filesystem’s structure, Linux inherits Unix’s very rich command set. The problem is that a standard workstation or server distribution comes equipped with thousands of command binaries, each providing its own set of capabilities. Obviously, developers cannot be expected to cross-compile such a large amount of binaries one by one, nor do most embedded systems require such a large body of binaries.

There are, therefore, two possibilities: either we choose a few select standard commands, or we try to group as many commands as possible into a very few trimmed-down applications that implement the essential overall functionality. In the following, we will start by discussing the first approach. I do not favor this approach, however, because it is tedious at best. Instead, I will mostly focus on the second approach and the various projects that implement it. In particular, we will discuss BusyBox, TinyLogin, and Embutils, which are the main packages used for this purpose.

Complete Standard Applications

If you would like to selectively include some of the standard applications found in mainstream distributions, your best bet is to start with the Linux From Scratch project located at http://www.linuxfromscratch.org/. This project aims at providing explanations and links to packages to help you build your own custom distributions. The Linux From Scratch book available through the project’s web site is its main documentation. It includes instructions and links to build each application one by one. For each package, the instructions provide build-time and disk-space estimates.

Alternatively, you can download applications off the Net one by one and follow the instructions of each package for compiling and cross-compiling. Because few packages include full cross-compilation instructions, you may need to look in the packages’ Makefiles to determine the appropriate build flags or make the proper modifications for the packages to cross-compile adequately.

BusyBox

The BusyBox project was initiated by Bruce Perens in 1996 to help build install disks for the Debian distribution. Since 1999, the project has been maintained by Erik Andersen, the maintainer of uClibc, first as part of Lineo’s open source efforts and currently as a vendor-independent project. During this time, the BusyBox project has grown extensively and is now one of the corner stones of many embedded Linux systems. It is included in most embedded Linux distributions and has a very active user community. The project’s current location is http://www.busybox.net/. The project’s web site includes documentation, links, and a mailing list archive. The BusyBox package is available under the terms of the GNU GPL from this same web site.

Enthusiasm for BusyBox stems from the functionality it provides while still remaining a very small-sized application. BusyBox implements many commands. Here are a few: ar, cat, chgrp, chmod, chown, chroot, cp, cpio, date, dd, df, dmesg, dos2unix, du, echo, env, expr, find, grep, gunzip, gzip, halt, id, ifconfig, init, insmod, kill, killall, ln, ls, lsmod, md5sum, mkdir, mknod, modprobe, more, mount, mv, ping, ps, pwd, reboot, renice, rm, rmdir, rmmod, route, rpm2cpio, sed, stty, swapon, sync, syslogd, tail, tar, telnet, tftp, touch, traceroute, umount, uname, uuencode, vi, wc, which, and whoami.

Although BusyBox does not support all the options provided by the commands it replaces, the subset it provides is sufficient for most typical uses. See the docs directory of the BusyBox distribution for the documentation in a number of different formats.

BusyBox supports all the architectures covered in Chapter 3. It can be linked both statically and dynamically to either glibc or uClibc. You can also modify the BusyBox default build configuration to remove support for the commands you are unlikely to use.

Setup

First, you need to download a copy of the BusyBox package from the project’s web site and into your ${PRJROOT}/sysapps directory. For my control module, I will be using BusyBox 0.60.5.

Once the package is extracted, we can move into its directory for the rest of the setup:

$ cd ${PRJROOT}/sysapps/busybox-0.60.5

Although the CVS version includes a terminal-based menu utility for configuring options, such as the one I described for uClibc in Chapter 4, the main stable version, such as the one I’m using, has to be configured by editing the appropriate file. The main file for configuring options is Config.h. This file contains C-language #define statements for each option. By commenting out an option’s #define using // (slash slash) you effectively disable this option.

There are two types of options that can be configured: command support options and feature support options. Disabling or enabling a command support option removes or adds the corresponding command. Changing the #define BB_MKNOD line to //#define BB_MKNOD disables support for the mknod command in BusyBox. Feature support options have a similar behavior. Features, however, are not necessarily related to a particular command. Consequently, every #define BB_FEATURE_... line is preceded with a comment line describing the feature.

Make sure you verify the command support options selected by default. Some important commands, such as ifconfig, insmod, and ping, are disabled by default.

I n addition to the configuration file, the main Makefile contains a few flags to control the way BusyBox is built. Most of these flags are used during the development of BusyBox for debugging purposes and are disabled in the standard distribution. The only flag you may be interested in modifying is the DOSTATIC flag. When set to true, the resulting BusyBox binary is statically linked with the C library. The default value of DOSTATIC is false, causing the binary to be dynamically linked. You can change this either by modifying the Makefile or by adding DOSTATIC=true as part of the make command.

Once BusyBox is configured, we can compile and install it. When linking with glibc, use the following command:

$ make TARGET_ARCH=ppc CROSS=powerpc-linux- 
                  > PREFIX=${PRJROOT}/rootfs all install

The TARGET_ARCH variable is used by the Makefile to determine whether some architecture-dependent optimizations can be carried out. CROSS is used, as in other circumstances, to specify the prefix of the cross-platform development tools. Finally, PREFIX is set to the root filesystem base directory. The Makefile will install all BusyBox’s components within this directory.

To build BusyBox with uClibc instead of the GNU C library, use the following command:

$ make TARGET_ARCH=ppc CROSS=powerpc-uclibc- 
                  > PREFIX=${PRJROOT}/rootfs all install

BusyBox has now been installed on your target’s root filesystem and is ready to be used.

Usage

To understand how best to use BusyBox, let’s first take a look at the components installed on the target’s root filesystem by BusyBox’s build process. As expected, only one executable was installed, /bin/busybox. This is the single binary with support for all the commands configured using Config.h. This binary is never called directly, however. Instead, symbolic links bearing the original commands’ names have been created to /bin/busybox. Such symbolic links have been created in all the directories in which the original commands would be found, including /bin, /sbin, /usr/bin, and /usr/sbin.

When you type a command during the system’s normal operation, the busybox command is invoked via the symbolic link. In turn, busybox determines the actual command you were invoking using the name being used to run it. /bin/ls, for instance, points to /bin/busybox. When you type ls, the busybox command is called and it determines that you were trying to use the ls command, because ls is the first argument on the command line.[6]

Although this scheme is simple and effective, it means you can’t use arbitrary names for symbolic links. Creating a symbolic link called /bin/dir to either /bin/ls or /bin/busybox will not work, since busybox does not recognize the dir command.

Note that, though symbolic links are the usual way of linking commands to /bin/busybox, BusyBox can also be instructed to create hard links instead of symbolic ones during its installation. Its behavior at runtime is the same, however, regardless of the type of links being used.

The documentation on the project’s web site, which is also provided with the package, describes all the options available for each command supported. In most cases, the options supported by BusyBox have the same function as the options provided by the original commands. For instance, Using the -al options to BusyBox’s ls will have the same effect as using the same options with the original ls.

When using one of the shells provided in BusyBox, such as ash, lash, or msh, you will find it convenient to use a /etc/profile file to define a few global variables for all shell users. Here is a sample /etc/profile file for a single-user target:

# Set path
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH

In addition to setting the path, you could set the LD_LIBRARY_PATH environment variable, which is used during the startup of each application to locate the libraries it depends on. Though the default location for libraries is /lib, your system may have libraries located in other directories. If that is the case, you can force the dynamic linker to look for the other libraries by adding the appropriate directory paths to LD_LIBRARY_PATH. As with the PATH environment variable, you can add more directories to the library path by separating each directory path with a colon.

Note that on a workstation or a server LD_LIBRARY_PATH would actually be used only as a temporary holding place for new library paths. Instead, the /etc/ld.so.conf is the file to edit to permanently add another library path. This file is then used by the ldconfig command to generate /etc/ld.so.cache, which is itself read by the dynamic linker to find libraries for dynamically linked applications. Though ldconfig was generated when we compiled glibc in Chapter 4, it is a target binary and cannot be run on the host to generate a target ld.so.cache.

TinyLogin

Much like BusyBox, TinyLogin is a collection of many login utilities into a single binary. TinyLogin is often used in conjunction with BusyBox, although it can be used alone. Both packages are maintained by the same developers and are therefore easy to use together. Because of their common use together, the project developers have integrated all of TinyLogin’s functionality into the BusyBox CVS, and once the CVS development version is released as a stable version, it will be possible to rely on a single package instead of two. There are, however, advantages to continue using the TinyLogin functionality separately from BusyBox. Mainly, many of the commands implemented in TinyLogin must run with root privileges, which in turn requires that the TinyLogin binary file belong to the root user and have its “set user” permission bit enabled—a configuration commonly known as “setuid root.” Since TinyLogin uses symbolic links in the same way BusyBox does, a single binary containing the functionality of both packages would also result in having commands such as ls and cat run as root, which increases the likeliness that a programming error in one command could be exploited to gain root privileges. Though BusyBox drops its root privileges when unnecessary, and though it can be configured to check a configuration file for those commands requiring root privileges, it remains that using separate packages is the safest setup.

The TinyLogin project’s web site is located at http://tinylogin.busybox.net/. It contains documentation, a mailing list archive, links to other sites, and pointers to download the TinyLogin package both using FTP or CVS. For my control module, I will be using TinyLogin 1.2.

As with BusyBox, TinyLogin supports all the architectures we discussed in depth in Chapter 3 and can be linked either statically or dynamically with glibc or uClibc. TinyLogin can effectively replace the following commands: addgroup, adduser, delgroup, deluser, getty, login, passwd, su, sulogin, and vlock.

Setup

The first step in installing TinyLogin is to download the package and extract it into your ${PRJROOT}/sysapps directory. Once this is done, we can move into the package’s directory for the rest of the setup:

$ cd ${PRJROOT}/sysapps/tinylogin-1.2

The configuration of TinyLogin is done much the same as with BusyBox, by editing the Config.h configuration file and commenting out the unwanted command support options and feature options. TinyLogin also has a Makefile with similar options to BusyBox. The same rules explained above for BusyBox’s Config.h file and Makefile also apply to TinyLogin.

Apart from the other options you need to configure, pay special attention to the USE_SYSTEM_PWD_GRP and USE_SYSTEM_SHADOW options in the Makefile. The explanations above the statements in the Makefile provide a good idea about the effect of these options. Mainly, USE_SYSTEM_PWD_GRP should be set to false unless you plan to use glibc’s NSS libraries with a properly configured /etc/nsswitch.conf file. If you set this option to false, TinyLogin will directly use the /etc/passwd and /etc/group files instead of using the password and group functions provided by glibc.

Similarly, if you set USE_SYSTEM_SHADOW to false, TinyLogin will use its own shadow functions for accessing shadow passwords. Traditionally, /etc/passwd could be read by anyone in the system and this in turn became a security risk as more and more programs for cracking passwords were available. Hence, the use of so-called shadow passwords became the norm. When in use, the password fields in /etc/passwd only contain filler characters and the real encrypted passwords are stored in /etc/shadow, which can be read only by a process running with root privileges. Note that if you had configured uClibc without shadow password support, setting USE_SYSTEM_SHADOW to true and linking with uClibc will result in a failed build.

As with BusyBox, you can set DOSTATIC to true if you would like TinyLogin to be built statically.

Once you have completed TinyLogin’s configuration, you are ready to build the package. (Instead of compiling and installing the package in the same step, as we did for BusyBox, you will first compile and then install for the reasons explained below.)

To compile TinyLogin with glibc, use the following command:

$ make CROSS=powerpc-linux- 
                  > PREFIX=${PRJROOT}/rootfs all

To compile TinyLogin with uClibc, use the following command:

$ make CROSS=powerpc-uclibc- 
                  > PREFIX=${PRJROOT}/rootfs all

Once the package has been built, you can now install the package. Because the installation process must setuid the TinyLogin binary, the installation command must be done while logged in as root:

$ su -m
Password:
# make PREFIX=${PRJROOT}/rootfs install
# exit

TinyLogin has now been installed in the target’s root filesystem and is ready to be used.

Usage

The TinyLogin installation copied only one binary to the root filesystem, /bin/tinylogin. As with BusyBox, symbolic links were created with the original commands’ names in the appropriate binary directories.

You will need to create appropriate group, password, and shadow password files (/etc/group, /etc/passwd, and /etc/shadow, respectively) for use by the various TinyLogin commands. Unfortunately, the TinyLogin package does not provide a means to create these files prior to having TinyLogin running on the target. Hence, you will have to copy existing files and edit them manually for use on your target’s root filesystem. A simple alternative is to use those files that are part of your workstation setup and keep only those entries for users who will exist on your target as well. Usually, this ends up being only the root user.

The group and password files on your workstation can be copied as-is to your target’s /etc directory. You can then edit your target’s copies by hand and remove the entries that will not be used on your target. The shadow password file requires a little more care, however, since you may not want to reveal your own workstation’s passwords to the users of your embedded system. To create valid entries in your target’s shadow file, the simplest method is to create phony users on your workstation, set those users’ passwords, and then copy the resulting entries. Here’s the entry for a phony “tmp” user I added to my workstation:

tmp:$1$3cdOSELf$XWRLoKIL7vMSfLYbRCWaf/:11880:0:99999:7:-1:-1:0

I set this user’s password to “root” for convenience. I then copied this entry as-is to my target’s shadow file and edited the username appropriately:

root:$1$3cdOSELf$XWRLoKIL7vMSfLYbRCWaf/:11880:0:99999:7:-1:-1:0

There is now a user known as “root” with the password “root” on my target.

Remember that the password file contains the name of the shell used for each user. Since the command name for the shell provided by BusyBox is sh, and since the default on most workstations is bash, you need to change this to the shell on your target. Here is the password file entry for the root user for the same system:

root:x:0:0:root:/root:/bin/sh

By default, TinyLogin will set the path of each user as follows:

PATH=/bin:/usr/bin

If you would like to change this, you can either create a global /etc/profile file, as I explained earlier, or a .profile file in each user’s home directory. You will find the following .profile file useful for the root user:

PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH

For more information on the creation and manipulation of group, password, or shadow password files, and system administration in general, see the Linux System Administrator’s Guide from the LDP, Running Linux from O’Reilly, and the Linux From Scratch book I mentioned earlier.

embutils

embutils is a set of miniaturized and optimized replacements for mainstream Unix commands. embutils was written and is maintained by Felix von Leitner, the author of diet libc, with goals very similar to those of diet libc. Currently, embutils supports four of the architectures discussed in Chapter 3, the ARM, the i386, the PPC, and the MIPS. embutils is available from http://www.fefe.de/embutils/.[7]

Although embutils groups some of the commands in a single binary, its main approach is to provide one small binary for each command. embutils provides the following commands: arch, basename, cat, chmgrp, chmod, chown, chroot, chvt, clear, cp, dd, df, dirname, dmesg, domainname, du, echo, env, false, head, hostname, id, install, kill, ln, ls, md5sum, mesg, mkdir, mkfifo, mknod, mv, pwd, rm, rmdir, sleep, sleep2, soscp, sosln, soslns, sosmv, sosrm, sync, tail, tar, tee, touch, tr, true, tty, uname, uniq, wc, which, whoami, write, and yes.

As with BusyBox, not all the options provided by the full commands are supported, but the subset provided is sufficient for most system operations. In contrast to BusyBox, however, embutils can only be statically linked with diet libc. It can’t be linked to any other library. Because diet libc is already very small, the resulting command binaries are reasonably small. In terms of overall size, nevertheless, BusyBox and embutils are fairly similar.

Setup

Before we start the setup, you will need to have diet libc installed on your host system as I described in Chapter 4. Now download embutils and extract it in your ${PRJROOT}/sysapps directory. For my control module, for example, I use embutils 0.15. You can then move into the package’s directory for the rest of the setup:

$ cd ${PRJROOT}/sysapps/embutils-0.15

There is no configuration capability for embutils. You can, therefore, build the package right away:

$ make ARCH=ppc CROSS=powerpc-linux- all

You can then install embutils:

$ make ARCH=ppc DESTDIR=${PRJROOT}/rootfs prefix="" install

The options and variables used in the build and installation of embutils have the same meaning as those used for diet libc.

Usage

The embutils installation procedure has copied quite a few statically linked binaries to your target root filesystem’s /bin directory. In contrast to BusyBox, this is the only directory where binaries have been installed.

A BusyBox-like all-in-one binary has also been installed, allinone. This binary reacts the same way as BusyBox when proper symbolic links are created to it. Note that unlike BusyBox, you need to create these symbolic links manually, since they are not created automatically by the installation scripts. allinone provides the following commands: arch, basename, chvt, clear, dmesg, dirname, domainname, echo, env, false, hostname, pwd, sleep, sync, tee, true, tty, uname, which, whoami, and yes.

Custom Applications

There are many places in the root filesystem where you can put your own application, depending on the number and types of components it has. Usually, it is preferable to follow the FHS’s guidelines to place your software.

If your application consists of a relatively small number of binaries, placing them in /bin is probably the best choice. This is the actual installation path used for the control daemon in Chapter 4.

If your application consists of a complex set of binaries, and possibly datafiles, consider adding an entry in the root filesystem for your project. You may either call this new directory project or name it after your own project. In the case of my control module, this directory could be control-module.

The custom directory can contain a hierarchy of its own that you can customize to best suit your needs. You may have to set the PATH environment variable on your target to include the custom directory if your binaries are placed there.

Note that the addition of a custom entry in the root filesystem is contrary to the FHS. This is a minor violation to the standard, however, since your filesystem is custom built for your target and is unlikely to become a distribution of its own.

System Initialization

System initialization is yet another particularity of Unix systems. As explained in Chapter 2, the kernel’s last initialization action is to start the init program. This program is in charge of finalizing system startup by spawning various applications and starting some key software components. In most Linux systems, init mimics System V init and is configured much the same way. In embedded Linux systems, the flexibility of System V init is overkill since such systems are rarely run as multiuser systems.

There is no actual requirement for you to have a standard init program, such as System V init, on your root filesystem. The kernel itself doesn’t really care. All it needs is an application it can start once it’s done initializing the system. For instance, you can add an init= PATH_TO_YOUR_INIT boot parameter to tell the kernel to use your init, which could be your main application. There are, however, drawbacks to this approach, since your application will be the one and only application the kernel ever starts. Your application would then be responsible for starting other applications on the system. Furthermore, if your application unexpectedly dies, its exit will cause a kernel panic followed by a system reboot; as would an unexpected exit of System V init. Though this may be the desired behavior in some cases, in most cases, the system is most likely rendered useless. For these reasons, it is usually much safer and useful to actually have a real init on your root filesystem.

In the following subsections, I will cover the standard init package found in most Linux distributions, the BusyBox init, and Minit, a miniature init provided by the author of embutils and diet libc.

As with other issues in Unix, init is a broad subject. There are quite a few documents that discuss Linux init at length. Chapter 5 of Running Linux describes the mainstream workstation and server init setups. Alessandro Rubini wrote a very interesting piece about init that goes into the nuances of the various initialization schemes. His article is available at http://www.linux.it/kerneldocs/init/.

Standard System V init

The standard init package found in most Linux distributions is written by Miquel van Soorenburg and is available at ftp://ftp.cistron.nl/pub/people/miquels/sysvinit/. By using this package, you get the same flexibility to configure your target’s startup as you would in configuring the startup of a workstation or a server. However, the extra functionality and flexibility requires additional space. Also, it requires that you keep track of the development of yet another software package. The package includes the following commands: halt, init, killall5, last, mesg, runlevel, shutdown, sulogin, utmpdump, and wall.

The package can be cross-compiled easily. First, download the package and uncompress it into your ${PRJROOT}/sysapps directory. For my control module, I used sysvinit Version 2.84. Then, move into the package’s source directory and build it:

$ cd ${PRJROOT}/sysapps/sysvinit-2.84/src
$ make CC=powerpc-linux-gcc

Replace the value of CC to match the cross-compiler for your target. With the package now built, we can install it on the target’s root filesystem:

$ make BIN_OWNER="$(id -un)" BIN_GROUP="$(id -gn)" 
               > ROOT=${PRJROOT}/rootfs install

This command will install all the binaries in the target’s root filesystem but will fail afterward, since the Makefile tries to install the manpages on the root filesystem as well. You can modify the Makefile to avoid this, but you can also ignore the failure.

The command just shown set the BIN_OWNER and BIN_GROUP variables to be that of your own current user. By default, the Makefile attempts to install the various components and set their ownership to the root user. Since you aren’t logged in as root, the Makefile would fail. The ownership of the binaries matters little on the target, since it isn’t a multiuser system. If it were, however, you need to log in as root and then run the install command. Be very careful, in any case, to appropriately set the value of ROOT to point to your target’s root filesystem. Otherwise, you may end up overwriting your workstation’s init with a target binary. Alternatively, to avoid having to log in as root, you could still run the install command using your normal user privileges and then use the chown command as root to change the privileges on each file installed. This, however, involves going through the Makefile to find each file installed and its destination.

With init installed on your target’s root filesystem, you will need to add the appropriate /etc/inittab file and fill the /etc/rc.d directory with the appropriate files. In essence, /etc/inittab will define the runlevels for your system, and the files in /etc/rc.d will define which services run on each runlevel. Table 6-5 lists init’s seven runlevels and their typical use in a workstation and server distribution.

Table 6-5. System V init runlevels

Runlevel

Description

0

System is halted

1

Only one user on system, no need for login

2

Multiuser mode without NFS, command-line login

3

Full multiuser mode, command-line login

4

Unused

5

X11, graphical user interface login

6

Reboot the system

Each runlevel corresponds to a certain set of applications. When entering runlevel 5 on a workstation, for example, init starts X11 and the user is prompted to enter his username and password using a graphical login. When switching between runlevels, the services started in the previous runlevel are shut down and the services of the new runlevel are started. In this scheme, runlevels 0 and 6 have a special meaning. Particularly, they are used for stopping the system safely. This may involve, for example, unmounting all the filesystems except the root filesystem and remounting the root filesystem read-only so that no filesystem corruption occurs.

On most workstations, the default runlevel at system startup is 5. For an embedded system, it can be set to 1, if no access control is necessary. The system’s runlevel can be changed after system startup either using init or telinit, which is a symbolic link to init. In both cases, the newly issued init command communicates with the original init through the /dev/initctl fifo. To this end, we need to create a corresponding entry in our target’s root filesystem:

$ mknod -m 600 ${PRJROOT}/rootfs/dev/initctl p

For more information on the format of /etc/inittab and the files found in /etc/rc.d, refer to the resources provided above.

BusyBox init

Among the commands it supports by default, BusyBox provides init-like capabilities. As with the original mainstream init, BusyBox can handle the system’s startup. BusyBox init is particularly well adapted to embedded systems, because it provides most of the init functionality an embedded system typically needs without dragging the weight of the extra features found in System V init. Also, because BusyBox is a single package, there is no need to keep track of an additional software package when developing or maintaining your system. There are cases, however, where BusyBox init may not be sufficient for your system. BusyBox init, for example, does not provide runlevel support.

Since I already described how to obtain, configure, and build BusyBox, I will limit this discussion to the setup of the init configuration files.

Because /sbin/init is a symbolic link to /bin/busybox, BusyBox is the first application to run on the target system. BusyBox identifies that the command being invoked is init and immediately jumps to the init routine.

The init routine of BusyBox carries out the following main tasks in order:

  1. Sets up signal handlers for init.

  2. Initializes the console(s).

  3. Parses the inittab file, /etc/inittab.

  4. Runs the system initialization script. /etc/init.d/rcS is the default for BusyBox.

  5. Runs all the inittab commands that block (action type: wait).

  6. Runs all the inittab commands that run only once (action type: once).

Once it has done this, the init routine loops forever carrying out the following tasks:

  1. Runs all the inittab commands that have to be respawned (action type: respawn).

  2. Runs all the inittab commands that have to be asked for first (action type: askfirst).

During console initialization, BusyBox determines whether the system was configured to run the console on a serial port (by passing console=ttyS0 as a kernel boot parameter, for instance). If so, BusyBox versions prior to 0.60.4 used to disable all virtual terminals. Since 0.60.4, however, BusyBox continues through its initialization without disabling virtual terminals. If in fact there are no virtual terminals, its attempts to start shells on some virtual terminals later will fail anyway, so there is no need to disable virtual terminals outright.

After having initialized the console, BusyBox checks for the existence of an /etc/inittab file. If no such file exists, BusyBox uses a default inittab configuration. Mainly, it sets up default actions for system reboot, system halt, and init restart. Also, it sets up actions to start shells on the first four virtual consoles, /dev/tty1 through /dev/tty4. BusyBox will complain if you haven’t created these device entries.

If an /etc/inittab file is found, it is parsed and the commands it contains are recorded inside internal structures to be carried out at the appropriate time. The format of the inittab file as recognized by BusyBox is well explained in the documentation included in the BusyBox package. The documentation provided in the BusyBox package includes an elaborate example inittab file.

Each line in the inittab file follows this format:

               id:runlevel:action:process

Although this format resembles that of traditional System V init, take note that the meaning of id is different in BusyBox init. Mainly, the id is used to specify the controlling tty for the process to be started. You can safely leave this entry empty if the process to be started isn’t an interactive shell. Interactive shells, such as BusyBox’s sh, should always have a controlling tty. BusyBox’s sh will actually complain if it has no controlling tty. BusyBox completely ignores the runlevel field, so you can leave it blank. The process field specifies the path of the program to run, along with its command-line options. The action field is one of eight recognized actions to be applied to process as described in Table 6-6.

Table 6-6. Types of inittab actions recognized by BusyBox init

Action

Effect

sysinit

Provide init with the path to the initialization script.

respawn

Restart the process every time it terminates.

askfirst

Similar to respawn, but is mainly useful for reducing the number of terminal applications running on the system. It prompts init to display “Please press Enter to activate this console.” at the console and wait for the user to press Enter before restarting the process.

wait

Tell init that it has to wait for the process to complete before continuing.

once

Run process only once without waiting for them.

ctrlaltdel

Run process when the Ctrl-Alt-Delete key combination is pressed.

shutdown

Run process when the system is shutting down.

restart

Run process when init restarts. Usually, the process to be run here is init itself.

The following is a simple inittab file for my control module:

::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty 115200 ttyS0
::respawn:/control-module/bin/init
::restart:/sbin/init
::shutdown:/bin/umount -a -r

This inittab file does the following:

  1. Sets /etc/init.d/rcS as the system initialization file.

  2. Starts a login session on the serial port at 115200 bps.

  3. Starts the control module’s custom software initialization script.

  4. Sets /sbin/init as the program to execute if init restarts.

  5. Tells init to run the umount command to unmount all filesystems it can at system shutdown and set the others as read-only to preserve the filesystems.

The id is left blank in this case, because it doesn’t matter to the normal operation of the commands. runlevel is also left blank, since it’s completely ignored by BusyBox.

As shown earlier, however, none of these actions will take place until init runs the system initialization script. This script can be quite elaborate and can actually call other scripts. Use this script to set all the basic settings and initialize the various components of the system that need special handling. Particularly, this is a good place to:

  • Remount the root filesystem in read-write mode.

  • Mount additional filesystems.

  • Initialize and start networking interfaces.

  • Start system daemons.

Here is the initialization script for my control module:

#!/bin/sh
 
# Remount the root filesystem in read-write (requires /etc/fstab)
mount -n -o remount,rw /

# Mount /proc filesystem
mount /proc
 
# Start the network interface
/sbin/ifconfig eth0 192.168.172.10

The above initialization script depends on the existence of an /etc/fstab file in the target’s root filesystem. I will not discuss the content and use of this file as it is already discussed in depth in Running Linux. Nevertheless, here’s the /etc/fstab file I use for my control module during development:

# /etc/fstab
# device      directory   type    options
#
/dev/nfs      /           nfs     defaults
none          /proc       proc    defaults

In this case, I mount the target’s root filesystem on NFS to simplify development. We will discuss filesystem types in Chapter 8 and NFS mounting in Chapter 9.

Minit

Minit is part of the miniaturized tools developed by Felix von Leitner, such as diet libc and embutils. Minit is available from http://www.fefe.de/minit/.[8] As with the other tools distributed by Felix, Minit requires a properly configured diet libc.

Minit’s initialization procedure is a complete departure from the traditional System V init. Instead of using a /etc/inittab, for instance, Minit relies on the existence of a properly built /etc/minit directory. Firdtjof Busse wrote a description of how Minit operates at http://www.fbunet.de/minit.shtml. Firdtjof also provides pointers to example /etc/minit directories.

Unfortunately, as of Version 0.8, Minit is not yet as mature as the other tools provided by Felix. Its Makefile, for instance, is unable to deal with installing the various components in a root filesystem other than the host’s own. For the time being, Minit is not appropriate for an embedded system, but it may be worth considering sometime in the near future.



[1] See the glibc manual for a complete description of the facilities provided.

[4] Have a look at Linux Network Administrator’s Guide (O’Reilly) for details about the customization of the nsswitch.conf file.

[5] The notable exception to this is networking interfaces, such as Ethernet cards, for which there are no device files.

[6] As any other application, busybox’s main( ) function is passed the command line used to invoke it.

[7] As with diet libc, the last slash (“/”) is important.

[8] As with the other tools available from fefe.de, the last slash (“/”) is important.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.137.183