7.3 Image Options
The following sections discuss a list of options that affect how the Yocto Project build system creates your root filesystem images.
7.3.1 Languages and Locales
Additional languages for different territories can easily be added to a root filesystem or your image by adding the IMAGE_LINGUAS variable to an image recipe. Using
IMAGE_LINGUAS = "en-gb pt-br"
adds the specific language packages for British English and Brazilian Portuguese to the image. However, not all software packages provide locales separated by language and territory. Some of them provide the locale files only by language. In this case, the build system defaults to installing the correct language local files regardless of the territory.
The minimum default for all packages is en-us and is always installed. In addition, the image class defines
IMAGE_LINGUAS ?= "de-de fr-fr en-gb"
Any additional locale packages, of course, occupy additional space in your root filesystem image. Therefore, if your device does not require any additional language support, it is good practice to set
IMAGE_LINGUAS = ""
in image recipes.
The build system ignores the languages for packages that do not provide them.
7.3.2 Package Management
The build system can package software packages using the four different packaging formats dpkg (Debian Package Management), opkg (Open Package Management), RPM (Red Hat Package Manager), and tar. Only the first three can be used to create root filesystems. Tar does not provide the necessary metadata package information and database to log what packages in what versions have been installed, which packages conflict with each other, and so on.
The variable PACKAGE_CLASSES in conf/local.conf of your build environment controls what package management systems are used for your builds:
PACKAGE_CLASSES = "package_rpm package_ipk package_tar"
You can declare more than one packaging class, but you have to provide at least one. The build system creates packages for all classes specified; however, only the first packaging class in the list is used to create the root filesystem of your distribution images. The first packaging class in the list must not be tar.
The build system stores the package feeds organized by the package management system in separate directories in tmp/deploy/<pms>, where <pms> is the name of the respective package management system. Inside those directories, the packages are further subdivided into common, architecture, and machine-dependent packages.
What package management system should you choose for your project? That depends on the requirements of your project. Here are some considerations you may want to take into account:
Opkg creates and utilizes less package metadata than dpkg and RPM. That makes building faster, and the packages are smaller.
Dpkg and RPM offer better dependency handling and version management than opkg because of the enhanced package metadata.
The RPM package manager is written in Python and requires Python to be installed on the target to install packages during runtime of the system.
By default, the build system does not install the package manager on your target system. If you are looking to install packages during runtime of your embedded system, you have to add the package manager using its image feature:
IMAGE_FEATURES += "package_management"
The build system automatically installs the correct package manager depending on the first entry of PACKAGE_CLASSES.
The package management system for your root filesystem is ultimately controlled by the variable IMAGE_PKGTYPE. This variable is set automatically by the order of the packaging classes defined by PACKAGE_CLASSES. The first packaging class in the list sets the variable. We recommend that you do not set this variable directly.
7.3.3 Image Size
The final size of the root filesystem is dependent on multiple factors and is computed by the build system using the function _get_rootfs_size() in the Python module meta/lib/oe/image.py. The computation takes into account the actual space required by the root filesystem and the following variable settings. It also ensures that the final root filesystem image size is always sufficient to hold the entire image. Hence, even if you set IMAGE_ROOTFS_SIZE to a specific value, the final image may be larger than that value, but it is never smaller.
IMAGE_ROOTFS_SIZE: Defines the size in kilobytes of the created root filesystem image. The build system uses this value as a request or recommendation. The final root filesystem image size may be larger depending on the actual space required. The default value is 65536.
IMAGE_ROOTFS_ALIGNMENT: Defines the alignment of the root filesystem image in kilobytes. If the final size of the root filesystem image is not a multiple of this value, it is rounded up to the nearest multiple of it. The default value is 1.
IMAGE_ROOTFS_EXTRA_SPACE: Adds extra free space to the root filesystem image. The variable specifies the value in kilobytes. For example, to add an additional 4 GB of space, set the variable to IMAGE_ROOTFS_EXTRA_SPACE = "4194304". The default value is 0.
IMAGE_OVERHEAD_FACTOR: This variable specifies a multiplicator for the root filesystem image. The factor is applied after the actual space required by the root filesystem has been determined. The default value is 1.3.
After the build system has created the root filesystem in the staging area, a directory specified by the variable IMAGE_ROOTFS, it calculates its actual size in kilobytes using du -ks ${IMAGE_ROOTFS}. The function _get_rootfs_size() computes the final root filesystem image size, as shown by Listing 7-7 in pseudocode.
Listing 7-7 Root Filesystem Image Size Computation in Pseudocode
_get_rootfs_size():
ROOTFS_SIZE =`du -ks ${IMAGE_ROOTFS}`
BASE_SIZE = ROOTFS_SIZE * IMAGE_OVERHEAD_FACTOR
if (BASE_SIZE < IMAGE_ROOTFS_SIZE):
IMG_SIZE = IMAGE_ROOTFS_SIZE + IMAGE_ROOTFS_EXTRA_SPACE
else:
IMG_SIZE = BASE_SIZE + IMAGE_ROOTFS_EXTRA_SPACE
IMG_SIZE = IMG_SIZE + IMAGE_ROOTFS_ALIGNMENT – 1
IMG_SIZE = IMG_SIZE % IMAGE_ROOTFS_ALIGNMENT
return IMG_SIZE
Most commonly, your image recipes set IMAGE_ROOTFS_SIZE and IMAGE_ROOTFS_EXTRA_SPACE to adjust the final root filesystem image size. If you are concerned with the footprint of your root filesystem, then you may also want to reduce IMAGE_OVERHEAD_FACTOR or set it to 1 to shrink your image.
7.3.4 Root Filesystem Types
Eventually, you use the root filesystem image to create a bootable medium for your target or to launch the QEMU emulator. For that purpose, the build system provides the image_types class that can create a root filesystem for various filesystem types.
Your image recipes do not use the image_types class directly but rather set the variable IMAGE_FSTYPES to one or more of the filesystem types provided by the class. Using
IMAGE_FSTYPES = "ext3 tar.bz2"
creates two root filesystem images, one using the ext3 filesystem and one that is a tar archive compressed using the bzip2 algorithm.
The image_types class defines the variable IMAGE_TYPES, which contains a list of all image types you can specify in IMAGE_FSTYPES. The list shows the filesystem types ordered by core type. Commonly, some of the core types are also used in compressed formats to preserve space. If a compression algorithm is used for the filesystem, the name of the core type is appended with the compression type: <core name>.<compression type>.
tar, tar.gz, tar.bz2, tar.xz, tar.lz3: Create uncompressed and compressed root filesystem images in the form of tar archives.
ext2, ext2.gz, ext2.bz2, ext2.lzma: Root filesystem images using the ext2 filesystem without or with compression.
ext3, ext3.gz: Root filesystem images using the ext3 filesystem without or with compression.
btrfs: Root filesystem image with B-tree filesystem.
jffs2, jffs2.sum: Uncompressed or compressed root filesystems based on the second generation of the Journaling Flash File System (JFFS2). Since JFFS2 directly supports NAND flash devices, it is a popular choice for embedded systems. It also provides journaling and wear-leveling.
cramfs: Root filesystem image using the compressed ROM filesystem (cramfs). The Linux kernel can mount this filesystem without prior decompression. The compression uses the zlib algorithm that compresses files one page at a time to allow random access. This filesystem is read-only to simplify its design, as random write access with compression is difficult to implement.
iso: Root filesystem image type using the ISO 9660 standard for bootable CD-ROM. This filesystem type is not a standalone format. It uses ext3 as the underlying filesystem type.
hddimg: Root filesystem image for bootable hard drives. It uses ext3 as the actual filesystem type.
squashfs, squashfs-xz: Compressed read-only root filesystem type specifically for Linux, similar to cramfs but with better compression and support for larger files and filesystems. SquashFS also has a variable block size from 0.5 kB to 64 kB over the fixed 4 kB block size of cramfs, which allows for larger file and filesystem sizes. SquashFS uses gzip compression, while squashfs-xz uses Lempel–Ziv–Markov (LZMA) compression for even smaller images.
ubi, ubifs: Root filesystem images using the unsorted block image (UBI) format for raw flash devices. UBI File System (UBIFS) is essentially a successor to JFFS2. The main differences between the two is that UBIFS supports write caching. Using ubifs in IMAGE_FSTYPES just creates the ubifs root filesystem image. Using ubi creates the ubifs root filesystem image and also runs the ubinize utility to create an image that can be written directly to a flash device.
cpio, cpio.gz, cpio.xz, cpio.lzma: Root filesystem images using uncompressed or compressed copy in and out (CPIO) streams.
vmdk: Root filesystem image using the VMware virtual machine disk format. It uses ext3 as the underlying filesystem format.
elf: Bootable root filesystem image created with the mkelfImage utility from the Coreboot project (www.coreboot.org).
Once again, which image types to use depends entirely on the requirements of your project, particularly on your target hardware. Boot device, bootloader, memory constraints, and other factors determine what root filesystem types are appropriate for your project. Our recommendation is to specify the root filesystem types ext3 and tar, or better, one of the compressed formats such as tar.bz2, in the image recipe. The ext3 format allows you to easily boot your root filesystem with the QEMU emulator for testing. The tar filesystem can easily be extracted onto partitioned and formatted media. The machine configuration files for your target hardware can then add additional root filesystem types appropriate for it.
7.3.5 Users, Groups, and Passwords
The class extrausers provides a comfortable mechanism for adding users and groups to an image as well as setting passwords for user accounts (see Listing 7-8).
Listing 7-8 Modifying Users, Groups, and Passwords
SUMMARY = "Custom image recipe from scratch"
DESCRIPTION = "Directly assign IMAGE_INSTALL and IMAGE_FEATURES for \
for direct control over image contents."
LICENSE = "MIT"
# We are using the assignment operator (=) below to purposely overwrite
# the default from the core-image class.
IMAGE_INSTALL = "packagegroup-core-boot packagegroup-base-extended \
${CORE_IMAGE_EXTRA_INSTALL}"
inherit core-image
inherit extrausers
# set image root password
ROOT_PASSWORD = "secret"
DEV_PASSWORD = "hackme"
EXTRA_USERS_PARAMS = "\
groupadd developers; \
useradd -p `openssl passwd ${DEV_PASSWORD}` developer; \
useradd -g developers developer; \
usermod -p `openssl passwd ${ROOT_PASSWORD}` root; \
"
The listing adds a group named developers and a user account named developer and adds the user account to the group. It also changes the password for the root account. Commands for adding and modifying groups, users, and passwords are added to the variable EXTRA_USERS_PARMS, which is interpreted by the class. The commands understood by the class are
useradd: Add user account
usermod: Modify user account
userdel: Remove user account
groupadd: Add user group
groupmod: Modify user group
groupdel: Remove user group
The class executes the respective Linux utilities with the corresponding names. Hence, the options are exactly the same and can easily be found in the Linux man pages. Note that the individual commands must be separated with a semicolon.
Using the option -p with the commands useradd and usermod sets the password of the user account. The password must be provided as the password hash. You can either calculate the password hash manually and add it to the recipe or, as shown in the example, have the recipe calculate it.
A word about the root user account: the build system sets up the root user for an image with an empty password if debug-tweaks is included with IMAGE_FEATURES. Removing debug-tweaks replaces the empty root password with *, which disables the account, so logging in as root from the console is no longer possible. For production use, we strongly recommend removing debug-tweaks from the build. If your embedded system requires console login capability, you can either set the root password as shown previously or add the sudo recipe and set up user accounts as sudoers.
For example, if you want to give the developer user account sudoer privileges, simply add sudo to IMAGE_INSTALL and usermod -a -G sudo developer to EXTRA_USERS_PARAMS.
7.3.6 Tweaking the Root Filesystem
For further customization of the root filesystem after it has been created by the build system and before the actual root filesystem images are created, ROOTFS_POSTPROCESS_COMMAND is available (see Listing 7-9). The variable holds a list of shell functions separated by semicolons.
Listing 7-9 ROOTFS_POSTPROCESS_COMMAND
SUMMARY = "Custom image recipe from scratch"
DESCRIPTION = "Directly assign IMAGE_INSTALL and IMAGE_FEATURES for \
for direct control over image contents."
LICENSE = "MIT"
# We are using the assignment operator (=) below to purposely overwrite
# the default from the core-image class.
IMAGE_INSTALL = "packagegroup-core-boot packagegroup-base-extended \
${CORE_IMAGE_EXTRA_INSTALL}"
inherit core-image
# Additional root filesystem processing
modify_shells() {
printf "# /etc/shells: valid login shells\n/bin/sh\n/bin/bash\n" \
> ${IMAGE_ROOTFS}/etc/shells
}
ROOTFS_POSTPROCESS_COMMAND += "modify_shells;"
The example adds the bash shell to /etc/shells. Be sure to always use the += operator to add to ROOTFS_POSTPROCESS_COMMAND, as the build system adds its own postprocessing commands to it.
Sudo Configuration
If you followed the example on giving a user sudoer privileges in the previous paragraph, you probably noticed that it does not work unless you uncomment the line %sudo ALL=(ALL) ALL in /etc/sudoers. A simple shell function added to ROOTFS_POSTPROCESS_COMMAND takes care of that when the root filesystem image is created (see Listing 7-10).
Listing 7-10 Sudo Configuration
modify_sudoers() {
sed 's/# %sudo/%sudo/' < ${IMAGE_ROOTFS}/etc/sudoers > \
${IMAGE_ROOTFS}/etc/sudoers.tmp
mv ${IMAGE_ROOTFS}/etc/sudoers.tmp ${IMAGE_ROOTFS}/etc/sudoers
}
ROOTFS_POSTPROCESS_COMMAND += "modify_sudoers;"
The script simply uncomments the line using sed.
SSH Server Configuration
All core images automatically include an SSH server for remote shell access to the system. By default, the server is configured to allow login with user name and password. Using public key infrastructure (PKI) provides an additional level of security but requires configuration of the root server and installation of keys into the root filesystem. A ROOTFS_POSTPROCESS_COMMAND can also easily be used to accomplish that task (see Listing 7-11).
Listing 7-11 SSH Server Configuration
configure_sshd() {
# disallow password authentication
echo "PasswordAuthentication no" >> ${IMAGE_ROOTFS}/etc/ssh/sshd_config
# create keys in tmp/deploy/keys
mkdir -p ${DEPLOY_DIR}/keys
if [ ! -f ${DEPLOY_DIR}/keys/${IMAGE_BASENAME}-sshroot ]; then
ssh-keygen -t rsa -N '' \
-f ${DEPLOY_DIR}/keys/${IMAGE_BASENAME}-sshroot
fi
# add public key to authorized_keys for root
mkdir -p ${IMAGE_ROOTFS}/home/root/.ssh
cat ${DEPLOY_DIR}/keys/${IMAGE_BASENAME}-sshroot.pub \
>> ${IMAGE_ROOTFS}/home/root/.ssh/authorized_keys
}
ROOTFS_POSTPROCESS_COMMAND += "configure_sshd;"
The script first disables authentication with user name and password for SSH. It then creates a key pair in tmp/deploy/keys inside the build environment using the name of the root filesystem image, essentially the name of the image recipe. If a previous build has already created a set of keys, they are preserved. Finally, the script adds the public key to the authorized_keys file in /home/root/.ssh, which is typical for SSH configuration. Login keys for other users can be created in a similar way.
This method works well if you do not require different keys for each device that you build, as every copy of the root filesystem of course contains the same keys. If you need different keys or, in general, individual configuration for your devices, then you need to devise a provisioning system for your device production.