Reader small image

You're reading from  Mastering Embedded Linux Programming - Third Edition

Product typeBook
Published inMay 2021
PublisherPackt
ISBN-139781789530384
Edition3rd Edition
Right arrow
Authors (2):
Frank Vasquez
Frank Vasquez
author image
Frank Vasquez

Frank Vasquez is an independent software consultant specializing in consumer electronics. He has over a decade of experience designing and building embedded Linux systems. During that time, he has shipped numerous devices including a rackmount DSP audio server, a diver-held sonar camcorder, and a consumer IoT hotspot. Before his career as an embedded Linux engineer, Frank was a database kernel developer at IBM where he worked on DB2. He lives in Silicon Valley.
Read more about Frank Vasquez

Chris Simmonds
Chris Simmonds
author image
Chris Simmonds

Chris Simmonds is a software consultant and trainer living in southern England. He has almost two decades of experience in designing and building open-source embedded systems. He is the founder and chief consultant at 2net Ltd, which provides professional training and mentoring services in embedded Linux, Linux device drivers, and Android platform development. He has trained engineers at many of the biggest companies in the embedded world, including ARM, Qualcomm, Intel, Ericsson, and General Dynamics. He is a frequent presenter at open source and embedded conferences, including the Embedded Linux Conference and Embedded World.
Read more about Chris Simmonds

View More author details
Right arrow

Chapter 14: Starting with BusyBox runit

In the previous chapter, we looked at the classic System V init and state-of-the-art systemd programs. We also touched on BusyBox's minimal init program. Now, it is time to look at BusyBox's implementation of the runit program. BusyBox runit strikes a sensible balance between the simplicity of System V init and the flexibility of systemd. For this reason, the full version of runit is used in popular modern Linux distributions like Void. While systemd may dominate the cloud, it is usually overkill
for many embedded Linux systems. BusyBox runit offers advanced features such as service supervision and dedicated service logging without the complexity and overhead
of systemd.

In this chapter, I will show you how to divide your system up into separate BusyBox runit services, each with its own directory and run script. Next, we will see how check scripts can be used to force some services to wait for other services to start. Then, we will...

Technical requirements

To follow along with the examples, make sure you have the following:

  • A Linux-based host system
  • Etcher for Linux
  • A microSD card reader and card
  • A USB to TTL 3.3V serial cable
  • A Raspberry Pi 4
  • A 5V 3A USB-C power supply

You should have already installed the 2020.02.9 LTS release of Buildroot for Chapter 6, Selecting a Build System. If you have not, then refer to the System requirements section of The Buildroot user manual (https://buildroot.org/downloads/manual/manual.html) before installing Buildroot on your Linux host according to the instructions from Chapter 6.

All of the code for this chapter can be found in the Chapter14 folder of the book's GitHub repository: https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition.

Getting BusyBox runit

To prepare the system for this chapter, we need to do the following:

  1. Navigate to the directory where you cloned Buildroot for Chapter 6, Selecting a Build System:
    $ cd buildroot
  2. Check to see if runit is provided by BusyBox:
    $ grep Runit package/busybox/busybox.config
    # Runit Utilities

    BusyBox runit was still an available option in the Buildroot 2020.02.9 LTS release at the time of writing. Revert to that tag if you can no longer find BusyBox runit in a later release.

  3. Undo any changes and delete any untracked files or directories:
    $ make clean
    $ git checkout .
    $ git clean –-force -d

    Note that git clean --force will delete the Nova U-Boot patch and any other files that we added to Buildroot in previous exercises.

  4. Create a new branch named busybox-runit to capture your work:
    $ git checkout -b busybox-runit
  5. Add BusyBox runit to a default configuration for the Raspberry Pi 4:
    $ cd configs
    $ cp raspberrypi4_64_defconfig rpi4_runit_defconfig...

Creating service directories and files

runit is a reimplementation of the daemontools process supervision kit. It was created by Gerrit Pape as a replacement for System V init and other Unix init schemes. At the time of writing, the two best sources of information on runit were Pape's website (http://smarden.org/runit/) and Void Linux's online documentation.

BusyBox's implementation of runit differs from standard runit mostly in terms of
self-documentation. For example, sv --help makes no mention of the sv utility's start and check options, which are in fact supported by BusyBox's implementation. The source code for BusyBox runit can be found in BusyBox's output/build/busybox-1.31.1/runit directory. You can also browse the latest version of the BusyBox runit source code online at https://git.busybox.net/busybox/tree/runit. If there are any bugs in, or features missing from, BusyBox's implementation of runit, you can fix or add them by patching...

Service supervision

Once we have created service directories with run scripts under /etc/sv and ensured that BusyBox init starts runsvdir, BusyBox runit handles all the rest. That includes starting, stopping, monitoring, and restarting all the services under its control. The runsvdir utility starts a runsv process for each service directory and restarts a runsv process if it terminates. Because run scripts run their respective daemons in the foreground, runsv expects run to block so that when run exits, runsv will restart it automatically.

Service auto-restart is desirable during system startup because run scripts can crash. This is especially true under BusyBox runit where services start virtually simultaneously instead of one after the other. For instance, a service may fail to start when a dependent service or essential system resource (such as a GPIO or device driver) is not yet available. In the next section, I will show you how to express dependencies between services so that...

Depending on other services

I mentioned how some services like connmand and bluetoothd require D-Bus. D-Bus is a message system bus that enables publish-subscribe interprocess communication. The Buildroot package for D-Bus provides a system dbus-daemon and a reference libdbus library. The libdbus library implements the low-level D-Bus C API but higher-level bindings to libdbus exist for other languages like Python. Some languages also offer alternative implementations of the D-Bus protocol that do not depend on libdbus at all. D-Bus services such as connmand and bluetoothd expect the system dbus-daemon to already be running before they can start.

Start dependencies

The official runit documentation recommends using sv start to express dependencies on other services under the control of runit. To make sure D-Bus is available before connmand starts, you should define your /etc/sv/connmand/run accordingly:

#!/bin/sh
/bin/sv start /etc/sv/dbus > /dev/null || exit 1
exec /usr...

Dedicated service logging

A dedicated service logger only logs the output coming from a single daemon. Dedicated logging is nice because diagnostic data for different services is distributed across separate log files. The monolithic log files generated by centralized system loggers such as syslogd are often hard to untangle. Both forms of logging have their purpose: dedicated logging excels at readability and centralized logging offers context. Your services can each have their own dedicated loggers and still write to syslog so you sacrifice neither.

How does it work?

Because service run scripts run in the foreground, adding a dedicated logger to a service only involves redirecting standard output from a service's run to a log file. You enable dedicated service logging by creating a log subdirectory inside the target service directory with another run script inside of it. This additional run is for the service's logger, not the service itself. When this log directory...

Signaling a service

Earlier, in the Start dependencies section, I showed how the sv command-line tool can be used to control a service. Later on, I demonstrated how the sv start and sv down commands can be used inside run and finish scripts to communicate between services. You may have already guessed that runsv is sending POSIX signals to the run processes that it supervises when an sv command executes. But what you may not have known is that the sv tool controls its target runsv process over a named pipe. The named pipes supervise/control and optionally log/supervise/control are opened so that other processes can send commands to runsv. Signaling a service is easy with sv commands, but if you want to, you can bypass sv entirely and write control characters directly to the control pipe(s).

The runtime directory layout of a service with no dedicated logging looks like this:

# tree /etc/sv/syslogd
/etc/sv/syslogd
|-- run
`-- supervise
    |-- control
 ...

Summary

This chapter was a deep dive into a lesser-known init system that I feel is largely underappreciated. Like systemd, BusyBox runit can enforce complex dependencies between services both during boot and at runtime. It just does it in a much simpler and I would argue more Unix-like way than systemd does. Plus, nothing beats BusyBox runit when it comes to boot times. If you are already using Buildroot as your build system, then I strongly encourage you to consider BusyBox runit for your device's init system.

We covered a lot of ground in our exploration. First, you learned how to get BusyBox runit onto your device and start it using Buildroot. Then I showed you how you can assemble and configure services together in different ways using out-of-tree umbrella packages. Next, we experimented with a live process supervision tree before delving into service dependencies and ways to express them. After that, I showed you how to add a dedicated logger and configure log rotation...

Further reading

Here are the various resources mentioned throughout the chapter:

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Mastering Embedded Linux Programming - Third Edition
Published in: May 2021Publisher: PacktISBN-13: 9781789530384
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Authors (2)

author image
Frank Vasquez

Frank Vasquez is an independent software consultant specializing in consumer electronics. He has over a decade of experience designing and building embedded Linux systems. During that time, he has shipped numerous devices including a rackmount DSP audio server, a diver-held sonar camcorder, and a consumer IoT hotspot. Before his career as an embedded Linux engineer, Frank was a database kernel developer at IBM where he worked on DB2. He lives in Silicon Valley.
Read more about Frank Vasquez

author image
Chris Simmonds

Chris Simmonds is a software consultant and trainer living in southern England. He has almost two decades of experience in designing and building open-source embedded systems. He is the founder and chief consultant at 2net Ltd, which provides professional training and mentoring services in embedded Linux, Linux device drivers, and Android platform development. He has trained engineers at many of the biggest companies in the embedded world, including ARM, Qualcomm, Intel, Ericsson, and General Dynamics. He is a frequent presenter at open source and embedded conferences, including the Embedded Linux Conference and Embedded World.
Read more about Chris Simmonds