2 unstable releases
0.2.0 | Sep 28, 2023 |
---|---|
0.1.0 | Sep 6, 2023 |
#426 in Unix APIs
145KB
3K
SLoC
caterpillar
A tool for the detection and installation of RAUC update bundles found on attached block devices. RAUC is a way to update firmware on embedded devices.
Caterpillar makes use of D-Bus to communicate with
- UDisks2 (for enumeration and (un)mounting of block devices)
- RAUC (for validation and installation of update bundles)
- logind (for reboot after successful installation)
The application also exposes its own D-Bus interface. More information on how to use it can be found in the interactive update section.
Configuration
Some aspects of caterpillar
's behavior can be configured using a configuration file in /etc/caterpillar/caterpillar.toml
.
It is also possible to override behavior using environment variables in all caps, prefixed with CATERPILLAR_
(e.g. autorun = true
-> CATERPILLAR_AUTORUN=true
).
Use-cases
Caterpillar supports two modes of operation, non-interactive and interactive, which are explained in more detail in the sections below.
The application is run in the background using the caterpillar.service
systemd unit.
Other applications running as root
can communicate with it over D-Bus.
Caterpillar takes care of detecting all attached block devices and mounts compatible filesystems found on them.
In the top-level directory of each mounted filesystem it searches for compatible RAUC update bundles with a version higher than the current system version and allows for installing them.
When placing a single compatible bundle in a configurable override directory, caterpillar
is able to install bundles of lower version as well.
NOTE: Only semver version comparison is supported!
After successful update, caterpillar
unmounts all previously mounted devices and can optionally trigger a reboot of the system (to boot into the updated system).
A rough overview of caterpillar
's interaction with rauc
and udisks2
is outlined in the below diagram:
Interactive update
Caterpillar exposes a D-Bus interface, which allows external applications running as root
to communicate with it.
Introspection
The application starts in idle
mode, waiting on external input.
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b false emits-change
.State property s "idle" emits-change
.Updated property b false emits-change
.UpdateFound signal a(sssb) - -
Searching for updates
NOTE: It is advised to subscribe to the UpdateFound
signal, which will propagate a found update.
Using the SearchForUpdate
method, caterpillar
can be requested to search for compatible updates:
[root@system ~]# busctl call de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar SearchForUpdate
If a compatible update is found, caterpillar
's State
property changes to updatefound
(noupdatefound
, if no update is found, shortly after which it unmounts mounted devices again and returns to idle
).
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b false emits-change
.State property s "updatefound" emits-change
.Updated property b false emits-change
.UpdateFound signal a(sssb) - -
The UpdateFound
signal is emitted, providing an array of length one with information on the available update:
- absolute path of update file (s)
- current version (s)
- new version (s)
- whether the update is an override (b)
[root@system ~]# dbus-monitor --system "type='signal',path='/de/sleepmap/Caterpillar',interface='de.sleepmap.Caterpillar',member='UpdateFound'"
signal time=1695853835.109057 sender=:1.37 -> destination=(null destination) serial=8 path=/de/sleepmap/Caterpillar; interface=de.sleepmap.Caterpillar; member=UpdateFound
array [
struct {
string "/run/media/root/bundle_disk_btrfs/update.raucb"
string "0.0.0"
string "1.0.0"
boolean false
}
]
Installing updates
Using the InstallUpdate
method, caterpillar
can be triggered to either install (and optionally reboot) or skip a found update.
When requesting to skip the update and not reboot (requesting to reboot has no effect when not also updating), caterpillar
unmounts all previously mounted devices and returns to its idle
state (with the Updated
property unchanged).
[root@system ~]# busctl call de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar InstallUpdate bb false false
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b false emits-change
.State property s "idle" emits-change
.Updated property b false emits-change
.UpdateFound signal a(sssb) - -
When requested to update but not reboot, caterpillar
updates the system, unmounts all previously mounted devices and returns to its idle
state, setting its Updated
property to true
on successful update.
[root@system ~]# busctl call de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar InstallUpdate bb true false
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b false emits-change
.State property s "updating" emits-change
.Updated property b false emits-change
.UpdateFound signal a(sssb) - -
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b false emits-change
.State property s "idle" emits-change
.Updated property b true emits-change
.UpdateFound signal a(sssb) - -
When requested to update and reboot, caterpillar
updates the system, unmounts all previously mounted devices and goes to done
state. Its Updated
and MarkedForReboot
properties are both set to true
.
[root@system ~]# busctl call de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar InstallUpdate bb true false
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b true emits-change
.State property s "updating" emits-change
.Updated property b false emits-change
.UpdateFound signal a(sssb) - -
[root@system ~]# busctl introspect de.sleepmap.Caterpillar /de/sleepmap/Caterpillar de.sleepmap.Caterpillar
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
.InstallUpdate method bb - -
.SearchForUpdate method - - -
.MarkedForReboot property b true emits-change
.State property s "done" emits-change
.Updated property b true emits-change
.UpdateFound signal a(sssb) - -
Non-interactive update during boot
Caterpillar can be configured to run non-interactively the first time it is run, using the autorun
configuration option.
In this mode the application will automatically (without user input):
- detect all block devices and mount compatible filesystems
- search and select one compatible update bundle
- if a (top-level) override directory with a single update bundle in it is found in the mountpoint
- if more than one update bundle exists in the (top-level) directory of the mountpoint, the one with the highest version is selected
- install the selected update bundle
- reboot
Building
Caterpillar is written in Rust and built using cargo:
cargo build --frozen --release --all-features
Tests
Unit tests can be executed using
cargo test -- --skip integration
NOTE: The integration test setup requires quite some space (ca. 10 - 20 GiB) and can only be run serially (which takes quite long).
cargo test integration
The integration tests require the following tools to be available on the test system:
- guestmount (libguestfs)
- guestunmount (libguestfs)
- mkosi (mkosi)
- openssl (openssl)
- pacman (pacman)
- qemu-img (QEMU)
- qemu-system-x86_64 (QEMU)
- rauc (RAUC)
License
All code contributions are dual-licensed under the terms of the Apache-2.0 and MIT. For further information on licensing refer to the contributing guidelines.
Funding
This project has been made possible by the funding of Nonlinear Labs GmbH.
Dependencies
~18–30MB
~485K SLoC