Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions bin/systemd-major
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
echo "Usage: $(basename "$0") <chroot-path>" >&2
exit 2
}

[ $# -ge 1 ] || usage
root="$1"
[ -d "$root" ] || { echo "error: chroot path not found: $root" >&2; exit 2; }

# Try to run systemd --version inside the chroot and extract the major number.
# Requires privileges to chroot into <root>.
ver="$(
chroot "$root" /bin/sh -eu -c '
PATH=/usr/sbin:/usr/bin:/sbin:/bin
for b in /usr/lib/systemd/systemd /lib/systemd/systemd systemd; do
if [ "$b" = systemd ]; then
command -v systemd >/dev/null 2>&1 || continue
cmd=systemd
else
[ -x "$b" ] || continue
cmd="$b"
fi
"$cmd" --version 2>/dev/null | sed -n "s/^systemd \([0-9][0-9]*\).*/\1/p" | head -n1
exit 0
done
exit 127
' 2>/dev/null || true
)"

[ -n "${ver:-}" ] || { echo "error: could not determine systemd version in $root" >&2; exit 1; }
printf '%s\n' "$ver"
5 changes: 3 additions & 2 deletions config/bookworm-minbase-ab.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ device:

image:
layer: image-rota
boot_part_size: 200%
system_part_size: 300%
boot_part_size: 128M
system_part_size: 512M
data_part_size: 1G
name: deb12-arm64-min-ab

layer:
Expand Down
5 changes: 3 additions & 2 deletions config/trixie-minbase-ab.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ device:

image:
layer: image-rota
boot_part_size: 200%
system_part_size: 300%
boot_part_size: 128M
system_part_size: 512M
data_part_size: 1G
name: deb13-arm64-min-ab

layer:
Expand Down
183 changes: 172 additions & 11 deletions docs/layer/image-rota.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,175 @@
<div class="header">
<h1>image-rota</h1>
<span class="badge">image</span>
<span class="badge">v3.1.0</span>
<p>Rotational OTA and AB support with GPT.
This layout supports redundancy of boot and system slot components and
provides a seperate user data partition.</p>
<span class="badge">v4.0.0</span>
<p>Immutable GPT A/B layout for rotational OTA updates,
boot/system redundancy, and a shared persistent data partition.</p>
</div>


<div class="section">
<h2>Additional Documentation</h2>
<div class="companion-content">
<div class="sect1">
<h2 id="_immutable_root_and_mount_points">Immutable root and mount points</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This system uses an A/B, read‑only root with all writable state on a single persistent partition.</p>
</div>
<table class="tableblock frame-all grid-all stripes-even stretch">
<colgroup>
<col style="width: 11.1111%;">
<col style="width: 33.3333%;">
<col style="width: 11.1111%;">
<col style="width: 44.4445%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Mount point</th>
<th class="tableblock halign-left valign-top">Backing</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/system</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Read‑only system root (active slot A or B)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/boot/firmware</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/boot</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot files (active slot A or B)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/bootfs</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">BOOTFS</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot metadata</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">PERSISTENT</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Shared persistent storage</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/var</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/slots/&lt;slot&gt;/var</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Per‑slot runtime state (systemd, caches, etc.)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/home</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/home</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">User data shared across slots</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">/var/log/journal</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/log/journal</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Single log directory used by both slots</p></td>
</tr>
</tbody>
</table>
<div class="ulist">
<ul>
<li>
<p><strong>Rationale (immutable root + A/B)</strong></p>
<div class="ulist">
<ul>
<li>
<p>Supports delta/incremental OTA updates by treating root as a static image.</p>
</li>
<li>
<p>Reliable rollbacks: slot can be flipped if a new root fails health checks.</p>
</li>
<li>
<p>Reduced write amplification and storage wear, clearer separation of state.</p>
</li>
<li>
<p>Predictable per‑slot state in <code>/var</code>.</p>
</li>
<li>
<p>Shared <code>/home</code> for slot agnostic user storage.</p>
</li>
<li>
<p>Shared journalling: centralised point for device logging.</p>
</li>
<li>
<p>Preserves SBOM accuracy: executing software matches the manifest exactly.</p>
</li>
<li>
<p>Blocks on-device package installs, preventing SBOM drift.</p>
</li>
<li>
<p>Enables auditable, reproducible releases and stronger supply-chain assurances.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Logging</strong></p>
<div class="ulist">
<ul>
<li>
<p>A single persistent journal directory at <code>/persistent/log/journal</code> stores logs from either slot.</p>
</li>
<li>
<p>Journaling is configured for endurance and reliability.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Machine Identity (systemd)</strong></p>
<div class="ulist">
<ul>
<li>
<p><code>/etc/machine-id</code> is synchronised early with <code>/persistent/common/etc/machine-id</code> using a oneshot unit.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<div class="title">Warning</div>
</td>
<td class="content">
<div class="paragraph">
<p>Slot partition GPT labels are mandatory to associate the immutable root with its matching persistent storage.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Root slots must have stable PARTLABELs: e.g. <code>system_a</code> and <code>system_b</code>.</p>
</li>
<li>
<p>At boot, a generator reads the root slot’s <code>PARTLABEL</code> to select <code>/persistent/slots/&lt;slot&gt;/var</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If slot GPT labels are missing/duplicated, <code>/var</code> binding will fail.</p>
</div>
<div class="paragraph">
<p>If slot GPT labels can’t be guaranteed, this layout is not suitable for your device.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_slot_selection_run_time">Slot Selection (run-time)</h2>
<div class="sectionbody">
<div class="paragraph">
Expand Down Expand Up @@ -227,6 +385,7 @@ <h2>Relationships</h2>
<a href="image-base.html" class="dep-badge">image-base</a>
<a href="device-base.html" class="dep-badge">device-base</a>
<a href="rpi-ab-slot-mapper.html" class="dep-badge">rpi-ab-slot-mapper</a>
<a href="systemd-min.html" class="dep-badge">systemd-min</a>
</div>


Expand Down Expand Up @@ -263,11 +422,11 @@ <h2>Configuration Variables</h2>

<tr>
<td><code>IGconf_image_boot_part_size</code></td>
<td>Boot partition size per slot.</td>
<td>Boot partition size per-slot.</td>
<td>


<code>100%</code>
<code>96M</code>


</td>
Expand All @@ -279,11 +438,11 @@ <h2>Configuration Variables</h2>

<tr>
<td><code>IGconf_image_system_part_size</code></td>
<td>System partition size per slot.</td>
<td>System partition size per-slot.</td>
<td>


<code>100%</code>
<code>512M</code>


</td>
Expand All @@ -295,11 +454,12 @@ <h2>Configuration Variables</h2>

<tr>
<td><code>IGconf_image_data_part_size</code></td>
<td>Data partition retained across rotations.</td>
<td>Writable storage partition retained across
slot rotations.</td>
<td>


<code>256M</code>
<code>1G</code>


</td>
Expand Down Expand Up @@ -329,7 +489,8 @@ <h2>Configuration Variables</h2>
<td><code>IGconf_image_pmap</code></td>
<td>Provisioning Map type for this image layout.
All partitions will be provisioned unencrypted (clear).
System partitions will be provisioned encrypted (crypt).</td>
System partitions will be provisioned encrypted (crypt).
System B will be provisioned encrypted (hybrid). Development only.</td>
<td>


Expand Down
9 changes: 6 additions & 3 deletions docs/layer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,10 @@ <h3>General</h3>
<a href="rpi-misc-utils.html">rpi-misc-utils</a><span class="layer-desc">- Raspberry Pi system utilities</span>
</div>

<div class="layer-item">
<a href="rpi-splash-screen.html">rpi-splash-screen</a><span class="layer-desc">- Raspberry Pi fullscreen splash screen support with custom image configuration.</span>
</div>

<div class="layer-item">
<a href="rpi-user-credentials.html">rpi-user-credentials</a><span class="layer-desc">- Raspberry Pi base layer for local user admin.
Creates a local account for user IGconf_device_user1, a home directory...</span>
Expand All @@ -867,9 +871,8 @@ <h3>Image</h3>
</div>

<div class="layer-item">
<a href="image-rota.html">image-rota</a><span class="layer-desc">- Rotational OTA and AB support with GPT.
This layout supports redundancy of boot and system slot components and
prov...</span>
<a href="image-rota.html">image-rota</a><span class="layer-desc">- Immutable GPT A/B layout for rotational OTA updates,
boot/system redundancy, and a shared persistent data partition.</span>
</div>

<div class="layer-item">
Expand Down
2 changes: 2 additions & 0 deletions docs/layer/systemd-min.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ <h2>Relationships</h2>
<p><strong>Required by:</strong></p>
<div class="deps">

<a href="image-rota.html" class="dep-badge">image-rota</a>

<a href="rpi-connect.html" class="dep-badge">rpi-connect</a>

<a href="rpi-connect-lite.html" class="dep-badge">rpi-connect-lite</a>
Expand Down
2 changes: 1 addition & 1 deletion image/gpt/ab_userdata/device/provisionmap-clear.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
{
"partitions": [
{
"image": "data"
"image": "persistent"
}
]
}
Expand Down
14 changes: 6 additions & 8 deletions image/gpt/ab_userdata/device/provisionmap-crypt.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,12 @@
}
]
}
}
},
"partitions": [
{
"image": "persistent"
}
]
}
},
{
"partitions": [
{
"image": "data"
}
]
}
]
2 changes: 1 addition & 1 deletion image/gpt/ab_userdata/device/provisionmap-hybrid.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
{
"partitions": [
{
"image": "data"
"image": "persistent"
}
]
}
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=Sync machine-id to/from persistent storage
DefaultDependencies=no
Requires=persistent.mount
After=persistent.mount
Before=systemd-journald.service sysinit.target
ConditionPathExists=/persistent/common/etc
RequiresMountsFor=/persistent/common/etc/machine-id

[Service]
Type=oneshot
ExecStart=/bin/sh -eu -c 'P=/persistent/common/etc/machine-id; R=/run/machine-id; if [ -s "$P" ]; then cat "$P" > "$R"; else /usr/bin/install -m0644 "$R" "$P"; fi'

[Install]
WantedBy=sysinit.target
Loading