diff --git a/Dockerfile b/Dockerfile index a48e1ac..3da2156 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get install -y \ flex \ parted \ fdisk \ + btrfs-progs \ python3-dev \ python3-libfdt \ python3-setuptools \ diff --git a/README.md b/README.md index d27a3c6..b3cab4f 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ sudo apt install git build-essential crossbuild-essential-arm64 bc bison flex \ For assembling disk images — debos, bmaptool, and zeekstd all need to be installed from source: ```bash -sudo apt install golang pipx pigz cargo parted fdisk mmdebstrap \ +sudo apt install golang pipx pigz cargo parted fdisk btrfs-progs mmdebstrap \ systemd-resolved systemd-container qemu-user-binfmt \ libglib2.0-dev libostree-dev fakemachine diff --git a/build-images.sh b/build-images.sh index 7f864b1..2c92db3 100755 --- a/build-images.sh +++ b/build-images.sh @@ -23,20 +23,10 @@ trap cleanup EXIT bmaptool copy "$IMG_OUT"/debian-rootfs.img.zst "$IMG_OUT"/debian-rootfs.img sync "$IMG_OUT"/debian-rootfs.img -TMP=`mktemp` -chmod 644 "$TMP" # Boot entries are pure BLS in /boot/loader/entries, read directly by U-Boot's # 'bls' bootmeth (no extlinux.conf). Nothing to rewrite here for the boot menu. -debugfs -R "cat /usr/lib/os-release" "$IMG_OUT"/debian-rootfs.img > "$TMP" -echo "BUILD_ID=$BUILD_ID" >> "$TMP" -debugfs -w -R "rm /usr/lib/os-release" "$IMG_OUT"/debian-rootfs.img -debugfs -w -R "write $TMP /usr/lib/os-release" "$IMG_OUT"/debian-rootfs.img -sync "$IMG_OUT"/debian-rootfs.img - -rm -f "$TMP" - for s in 512 4096; do echo "Creating images for $s-byte sector size" truncate -s "$IMGSIZE" "$TMPDIR"/debian-"$s"-nobootloader-"$BUILD_ID".img diff --git a/build-rootfs-img.sh b/build-rootfs-img.sh index 99075b3..4dec7af 100755 --- a/build-rootfs-img.sh +++ b/build-rootfs-img.sh @@ -5,6 +5,17 @@ set -e +# Capture Git information +[ -n "${GIT_HASH}" ] || GIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") +[ -n "${GIT_BRANCH}" ] || GIT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git describe --tags --exact-match 2>/dev/null || echo "detached") +[ -n "${GIT_MSG}" ] || GIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null | sed 's/[\"()]/\\&/g; s/'"'"'/\\&/g' || echo "unknown") +[ -n "${GIT_INFO}" ] || GIT_INFO="${GIT_BRANCH}@${GIT_HASH}: ${GIT_MSG}" +GIT_INFO=$(echo "$GIT_INFO" | tr -dc '[:alnum:][:space:]') + +# Build identifier: CI sets BUILD_ID in the environment (e.g. "build-1419"); otherwise +# fall back to a UTC timestamp, same default/format as build-images.sh. +: "${BUILD_ID:=$(date -u '+%Y%m%d-%H%M')}" + if [ -c /dev/kvm -a -w /dev/kvm ]; then # Have virtualization support, can use fakemachine (default, fast, safe) DEBOS="debos -c $(nproc) -m 6Gb" @@ -29,7 +40,7 @@ mkdir -p "$IMG_OUT"/linux_tmp cp -r "$LINUX_OUT"/* "$IMG_OUT"/linux_tmp echo "Creating the root FS image" -$DEBOS --artifactdir="$IMG_OUT" -t imagesize:"$IMGSIZE" -t kerneldir:linux_tmp debian-rk3576-img.yaml +$DEBOS --artifactdir="$IMG_OUT" -t imagesize:"$IMGSIZE" -t kerneldir:linux_tmp -t gitinfo:"$GIT_INFO" -t buildid:"$BUILD_ID" debian-rk3576-img.yaml sync "$IMG_OUT"/debian-nobootloader.img owner=$(stat -c %u "$IMG_OUT"/debian-nobootloader.img) diff --git a/debian-rk3576-img.yaml b/debian-rk3576-img.yaml index 2e2aff3..cd01fa5 100644 --- a/debian-rk3576-img.yaml +++ b/debian-rk3576-img.yaml @@ -1,16 +1,25 @@ {{ $sectorsize := or .sectorsize 512 }} -{{ $imagesize := or .imagesize "4GiB" }} -{{ $kerneldir := or .kerneldir "prebuilt/linux" }} -{{ $cmdline := or .cmdline "audit=0 console=tty1 console=ttyS0,1500000n8" }} +{{ $imagesize := or .imagesize "4GiB" }} +{{ $kerneldir := or .kerneldir "prebuilt/linux" }} +{{ $cmdline := or .cmdline "audit=0 console=tty1 console=ttyS0,1500000n8" }} +{{ $fslabel := or .fslabel "flipperos" }} +{{ $gitinfo := or .gitinfo "unknown" }} +{{ $buildid := or .buildid "unknown" }} +{{- /* fs-wide btrfs mount options. ssd is FORCED (loopback/eMMC isn't reliably + detected as non-rotational). */ -}} +{{ $btrfsopts := or .btrfsopts "compress=zstd,noatime,ssd,discard=async" }} +{{ $rootdev := "findmnt -fn --nofsroot -o SOURCE --target $IMAGEMNTDIR" }} architecture: arm64 sectorsize: {{ $sectorsize }} - actions: - action: unpack description: Load a tarball of the root filesystem file: debian-ospack.tar.gz + # debos formats btrfs (default flags, random FSUUID), mounts the top level at + # $IMAGEMNTDIR, and records the standard root fstab line + root=UUID. + # fsck:false -> passno 0 (no boot-time fsck for btrfs). - action: image-partition imagename: debian-nobootloader.img imagesize: {{ $imagesize }} @@ -21,17 +30,85 @@ actions: start: 32KiB end: 64MiB - name: root - fs: ext4 + fs: btrfs + fslabel: {{ $fslabel }} + fsck: false start: 64MiB end: -1MiB - flags: [ boot ] mountpoints: - mountpoint: / partition: root - options: [ x-systemd.growfs ] + options: [ compress=zstd, noatime, ssd, discard=async, x-systemd.growfs ] + + # Subvolume layout (no reformat). /boot is a TOP-LEVEL subvolume named "boot" + # (not "@boot") so it sits outside the per-profile root subvolumes -> a single shared + # /boot that root snapshots never capture, and so U-Boot, which reads the default + # (top-level) subvolume, resolves /boot/extlinux/... straight into it. Keep the default + # subvolume as the top level (do NOT set a default subvolume) or U-Boot won't see /boot. + - action: run + description: Create subvolumes and mount the layout + chroot: false + command: | + set -eu + ROOTPART="$({{ $rootdev }})" + + # debos has the top level (subvolid=5) mounted here; create subvolumes on it. + # @Minimal is the common base / build target; @snapshots holds runtime snapshots. + for sv in @Minimal boot @home @snapshots @var-log @var-cache; do + btrfs subvolume create "$IMAGEMNTDIR/$sv" + done + umount "$IMAGEMNTDIR" + + # Mount @Minimal at the mount point debos owns, then nest the rest under it. + mount -t btrfs -o "{{ $btrfsopts }},subvol=@Minimal" "$ROOTPART" "$IMAGEMNTDIR" + mkdir -p "$IMAGEMNTDIR/boot" \ + "$IMAGEMNTDIR/home" \ + "$IMAGEMNTDIR/var/log" \ + "$IMAGEMNTDIR/var/cache" + mount -t btrfs -o "{{ $btrfsopts }},subvol=boot" "$ROOTPART" "$IMAGEMNTDIR/boot" + # Keep /boot uncompressed so U-Boot can read the kernel/dtb/initrd regardless + # of whether its btrfs driver was built with zstd support. The property is + # stored on the subvolume and inherited by files added later (kernel updates). + btrfs property set "$IMAGEMNTDIR/boot" compression none + mount -t btrfs -o "{{ $btrfsopts }},subvol=@home" "$ROOTPART" "$IMAGEMNTDIR/home" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-log" "$ROOTPART" "$IMAGEMNTDIR/var/log" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@var-cache" "$ROOTPART" "$IMAGEMNTDIR/var/cache" + # debos writes the standard root line into /etc/fstab and root=UUID into the cmdline; + # the root subvolume is selected by the appended rootflags. @Desktop is the default root + # (the desktop); the other profiles' BLS entries append rootflags=subvol=@ + # and the last rootflags= on the line wins. (Deploy still happens into @Minimal.) - action: filesystem-deploy - append-kernel-cmdline: {{ $cmdline }} + append-kernel-cmdline: rootflags=subvol=@Desktop {{ $cmdline }} + + # Add fstab entries for the non-root subvolumes (debos only generated the root + # line). The FSUUID is read back at build time. These (/boot, /home, /var/log, + # /var/cache) are shared by every profile root. @snapshots is intentionally omitted + # -- it holds runtime snapshots, managed on demand, never auto-mounted. + - action: run + description: Append subvolume entries to /etc/fstab + chroot: false + command: | + set -eu + ROOTPART="$({{ $rootdev }})" + FSUUID="$(blkid -o value -s UUID -p -c none "$ROOTPART")" + cat >> "$IMAGEMNTDIR/etc/fstab" <> "$osr" + echo 'BUILD_ID={{ $buildid }}' >> "$osr" + + # =========================================================================== + # Stock (RO) bases + per-profile bootable roots. + # + # @Minimal_stock is the RO golden image of the common base. Each profile is a + # WRITABLE snapshot of it (COW: shares the base, only profile packages add new extents), + # mounted at $IMAGEMNTDIR so standard debos `apt`/`overlay` actions install into it; + # it is then locked RO and the writable @ bootable root is snapshotted from it. + # BLS selects a profile via rootflags=subvol=@. The kernel lives in + # @Minimal_stock (installed above), so every profile inherits it via COW. /boot, + # @home and @var-* are separate top-level subvols shared by all profiles. + # + # KDE and TV-Media-Box install their packages (apt) + config (overlay); only @Desktop changes + # its default target (to graphical, for sddm) -- TV/Router/Minimal stay on multi-user. + # Router applies only its config overlay (NM AP/WAN + ip-forwarding); no packages needed. + # =========================================================================== + + - action: run + description: Lock @Minimal_stock (RO), re-create @Minimal from it, free the mountpoint + chroot: false + command: | + set -eu + sync + ROOTPART="$({{ $rootdev }})" # @Minimal is still mounted at $IMAGEMNTDIR + # Mount the btrfs top level at a fixed path and KEEP it mounted for the whole profile + # sequence: it is our stable handle for `findmnt --target` (device lookup then works in + # every debos mode, whether or not $IMAGEMNTDIR is mounted) and where the subvols live. + TOP="${IMAGEMNTDIR%/}.toplevel" + mkdir -p "$TOP" + mount -t btrfs -o subvolid=5 "$ROOTPART" "$TOP" + btrfs subvolume snapshot -r "$TOP/@Minimal" "$TOP/@Minimal_stock" + # release @Minimal and its nested submounts so we can replace it and profile + # factories can mount here + findmnt -rno TARGET --submounts "$IMAGEMNTDIR" \ + | awk '{ print length, $0 }' | sort -rn | cut -d" " -f2- \ + | while read -r mp; do umount "$mp"; done + # Re-create @Minimal as a writable child of the golden base (parent = @Minimal_stock) + # so it matches the other profiles -- which are snapshots of their _stock -- instead + # of being the build origin with no parent. + btrfs subvolume delete "$TOP/@Minimal" + btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Minimal" + + # --- @Desktop ------------------------------------------------------------------ + - action: run + description: Snapshot writable @Desktop_stock and mount it + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")" + btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Desktop_stock" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@Desktop_stock" "$ROOTPART" "$IMAGEMNTDIR" + - action: apt + description: KDE desktop packages + recommends: false # image is minbase/no-recommends; deps are listed explicitly + packages: + - bluedevil + - blueman + - breeze-gtk-theme + - kde-config-gtk-style + - kde-config-screenlocker + - kde-spectacle + - kde-standard + - konsole + - kscreen + - mpv + - plasma-discover + - plasma-nm + - plasma-systemmonitor + - powerdevil + - sddm + - sddm-theme-breeze + - systemsettings + - action: overlay + description: KDE profile config (sddm autologin, plasma, wallpaper) + source: overlays/profile-kde + destination: / + - action: run + description: Make the KDE desktop the default session on @Desktop + chroot: true + command: systemctl set-default graphical.target + - action: run + description: Lock @Desktop_stock RO and spawn writable @Desktop + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + umount "$IMAGEMNTDIR" + btrfs property set "$TOP/@Desktop_stock" ro true + btrfs subvolume snapshot "$TOP/@Desktop_stock" "$TOP/@Desktop" + + # --- @TV-Media-Box --------------------------------------------------------- + - action: run + description: Snapshot writable @TV-Media-Box_stock and mount it + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")" + btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@TV-Media-Box_stock" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@TV-Media-Box_stock" "$ROOTPART" "$IMAGEMNTDIR" + - action: apt + description: TV media box packages + recommends: false + packages: + - cage + - kodi + - kodi-eventclients-kodi-send + - action: overlay + description: TV media box profile config (kodi, enabled under multi-user) + source: overlays/profile-tv-media-box + destination: / + - action: run + description: Lock @TV-Media-Box_stock RO and spawn writable @TV-Media-Box + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + umount "$IMAGEMNTDIR" + btrfs property set "$TOP/@TV-Media-Box_stock" ro true + btrfs subvolume snapshot "$TOP/@TV-Media-Box_stock" "$TOP/@TV-Media-Box" + + # --- @Router --------------------------------------------------------------- + - action: run + description: Snapshot writable @Router_stock and mount it + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")" + btrfs subvolume snapshot "$TOP/@Minimal_stock" "$TOP/@Router_stock" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@Router_stock" "$ROOTPART" "$IMAGEMNTDIR" + # @Router needs no packages beyond Minimal (NetworkManager + dnsmasq-base + wpa_supplicant + # are common); it just adds the AP/WAN connections + ip-forwarding via the overlay. Its + # default session stays multi-user (inherited); NM autoconnect brings the router up. + - action: overlay + description: Router profile config (NM AP/WAN connections, ip-forwarding) + source: overlays/profile-router + destination: / + - action: run + description: Fix NetworkManager connection permissions on @Router + chroot: true + command: chmod 600 /etc/NetworkManager/system-connections/*.nmconnection + - action: run + description: Lock @Router_stock RO and spawn writable @Router + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + umount "$IMAGEMNTDIR" + btrfs property set "$TOP/@Router_stock" ro true + btrfs subvolume snapshot "$TOP/@Router_stock" "$TOP/@Router" + + # Unmount the top level and remount @Minimal at the mountpoint debos owns, so its + # image-partition Cleanup can unmount it and detach the loop. (The profile builds left + # $IMAGEMNTDIR unmounted, and the lingering top-level mount would hold the loop busy.) + - action: run + description: Unmount the top level and remount @Minimal for debos cleanup + chroot: false + command: | + set -eu + TOP="${IMAGEMNTDIR%/}.toplevel" + ROOTPART="$(findmnt -fn --nofsroot -o SOURCE --target "$TOP")" + umount "$TOP"; rmdir "$TOP" + mount -t btrfs -o "{{ $btrfsopts }},subvol=@Minimal" "$ROOTPART" "$IMAGEMNTDIR" \ No newline at end of file diff --git a/debian-rk3576-ospack.yaml b/debian-rk3576-ospack.yaml index 1a34ecd..981f969 100644 --- a/debian-rk3576-ospack.yaml +++ b/debian-rk3576-ospack.yaml @@ -19,22 +19,20 @@ actions: include: - acl - adduser - - avahi-daemon - alsa-ucm-conf - alsa-utils - apt-transport-https + - avahi-daemon - bash-completion - bc - - bluedevil - - blueman - bluez - bluez-firmware - bluez-meshd - - breeze-gtk-theme - bsdextrautils - bsdmainutils + - btrfs-compsize + - btrfs-progs - build-essential - - cage - ca-certificates - cec-utils - chromium @@ -43,8 +41,7 @@ actions: - dmidecode - dnsmasq-base - drm-info - - gstreamer1.0-plugins-bad - - gstreamer1.0-tools + - duperemove - e2fsprogs - edid-decode - evtest @@ -55,6 +52,8 @@ actions: - git - glmark2-wayland - gpiod + - gstreamer1.0-plugins-bad + - gstreamer1.0-tools - i2c-tools - inetutils-telnet - initramfs-tools @@ -64,25 +63,17 @@ actions: - iputils-ping - iw - iwd - - kde-config-gtk-style - - kde-config-screenlocker - - kde-spectacle - - kde-standard - kmscube - - kodi - - kodi-eventclients-kodi-send - - konsole - - kscreen - less - libcanberra-pulse + - libdrm-tests - libfreeaptx0 + - liblc3-1 - libldacbt-abr2 - libldacbt-enc2 - - liblc3-1 - - libsbc1 - libopus0 + - libsbc1 - libspa-0.2-bluetooth - - libdrm-tests - libtypec-utils - libubootenv-tool - libusb-1.0-0-dev @@ -104,10 +95,7 @@ actions: - p7zip - pciutils - pipewire-alsa - - plasma-nm - - plasma-systemmonitor - powercap-utils - - powerdevil - python3-evdev - python3-pandas - python3-plotly @@ -123,11 +111,11 @@ actions: - systemd-resolved - systemd-sysv - systemd-timesyncd - - systemsettings - tcpdump - traceroute - udev - unzip + - upower - usb-modeswitch - usbutils - util-linux-extra @@ -255,9 +243,6 @@ actions: - libavformat-extra - lpac - mesa-teflon-delegate - - mpv - - sddm - - sddm-theme-breeze - action: overlay description: Overlay Kodi Chromium kiosk launcher diff --git a/overlays/configs/kernel/flipper-profiles b/overlays/configs/kernel/flipper-profiles index 42c71d7..bd43abc 100644 --- a/overlays/configs/kernel/flipper-profiles +++ b/overlays/configs/kernel/flipper-profiles @@ -1,15 +1,21 @@ # Flipper One boot profiles -> one BLS entry per profile per kernel. # -# Each profile is derived from the base ("[DEFAULT]") entry that +# Each profile is derived from the base ("[DESKTOP]") entry that # 90-loaderentry.install generates: it reuses the same linux/initrd/devicetreedir # and only adds a title suffix, extra kernel cmdline, and/or DTB overlays. # -# This replaces the old u-boot-menu enabled.d/features.d profile engine. +# Each bootable session lives in its own btrfs root subvolume (@Desktop, @Minimal, +# @TV-Media-Box, @Router). A profile selects its root by appending +# `rootflags=subvol=@` to the cmdline -- this overrides the base +# `rootflags=subvol=@Desktop` because the LAST rootflags= on the line wins. So [DESKTOP] +# boots @Desktop (the desktop) and the entries below switch to the other roots. The session +# (KDE/kodi/router) is baked into each root's default.target; no systemd.unit= needed. # # Columns (pipe-separated; '#' starts a comment line): # 1 token short id, used in the entry filename + sort ordering # 2 suffix appended to the menu title, e.g. "[ROUTER]" -# 3 cmdline extra kernel command-line fragment (may be empty) +# 3 cmdline extra kernel command-line fragment: root selection +# (rootflags=subvol=@) and/or tweaks (may be empty) # 4 gate required kernel config symbol(s) (=y/=m in /boot/config-); # space-separated = ALL must be present; the profile is skipped # for kernels that lack any of them (may be empty) @@ -20,12 +26,12 @@ # them (else the entry is still produced without overlays). # 6 legend short description shown in the menu header (may be empty) # -# Order here = order in the generated menu (after the [DEFAULT] entry). +# Order here = order in the generated menu (after the [DESKTOP] = @Desktop entry). -nokde |[NO KDE] |systemd.unit=multi-user.target | | |Same as DEFAULT but no KDE autostart -tvbox |[TV MEDIA BOX]|systemd.unit=tv-media-box.target| |hdmi-cec |Kodi media-box session -router|[ROUTER] |systemd.unit=router.target | | |Wi-Fi router mode -#hdmi4k|[HDMI-4K] |dyndbg="file drivers/gpu/drm/rockchip/dw-dp.c +p"|ROCKCHIP_DW_DP|!rk3576-evb1-v10-typec-base rk3576-evb1-v10-typec-hdmi-dp|HDMI 4k -dp4k |[DP-4K] |dyndbg="file drivers/gpu/drm/rockchip/dw-dp.c +p"|ROCKCHIP_DW_DP|!rk3576-evb1-v10-typec-base rk3576-evb1-v10-typec-dp-hdmi|DisplayPort 4k +minimal|[MINIMAL] |rootflags=subvol=@Minimal | | |No desktop (cog kiosk only) +tvbox |[TV MEDIA BOX]|rootflags=subvol=@TV-Media-Box | |hdmi-cec |Kodi media-box session +router |[ROUTER] |rootflags=subvol=@Router | | |Wi-Fi router mode +#hdmi4k|[HDMI-4K] |dyndbg="file drivers/gpu/drm/rockchip/dw-dp.c +p"|ROCKCHIP_DW_DP|!rk3576-evb1-v10-typec-base rk3576-evb1-v10-typec-hdmi-dp|HDMI 4k (on @Desktop) +dp4k |[DP-4K] |dyndbg="file drivers/gpu/drm/rockchip/dw-dp.c +p"|ROCKCHIP_DW_DP|!rk3576-evb1-v10-typec-base rk3576-evb1-v10-typec-dp-hdmi|DisplayPort 4k (on @Desktop) #fan |[FAN] | | |!rk3576-evb1-v10-fan-pwm|PWM fan control #ncm |[USB NCM] |systemd.wants=usb-ncm-gadget.service|USB_CONFIGFS_NCM USB_F_NCM|usb-ncm|USB NCM Ethernet gadget diff --git a/overlays/configs/kernel/install.d/90-loaderentry.install b/overlays/configs/kernel/install.d/90-loaderentry.install index 762d0c6..a07b5eb 100755 --- a/overlays/configs/kernel/install.d/90-loaderentry.install +++ b/overlays/configs/kernel/install.d/90-loaderentry.install @@ -35,6 +35,19 @@ TITLE_MAX="${FLIPPER_TITLE_MAX:-26}" DTB_DIR="/usr/lib/linux-image-$KERNEL_VERSION" DTB_DIRS="$DTB_DIR /usr/lib/modules/$KERNEL_VERSION/dtb" +# U-Boot reads the btrfs TOP LEVEL (subvolid 5), so in-entry devicetreedir/overlay paths must +# be prefixed with the root subvolume the entry boots -- /@/usr/lib/... -- where that +# root's DTBs live (the kernel package installs them in every root's /usr/lib). The prefix is +# derived per entry from its own rootflags=subvol= (each profile boots a different root). The +# staged kernel/initrd live under /boot (the boot subvolume) and are never prefixed. +# Set FDT_SUBVOL_PREFIX to override the derivation (e.g. "" if the root is not a subvolume). +fdt_prefix() { # $1 = options string -> "/" (or $FDT_SUBVOL_PREFIX if set) + if [ "${FDT_SUBVOL_PREFIX+x}" = x ]; then printf '%s' "$FDT_SUBVOL_PREFIX"; return 0; fi + _sv=$(printf '%s' "$1" | tr ' ' '\n' | sed -n 's/^rootflags=subvol=//p' | tail -n1) + [ -n "$_sv" ] && printf '/%s' "$_sv" + return 0 +} + log() { [ "${KERNEL_INSTALL_VERBOSE:-0}" -gt 0 ] && echo "flipper-bls: $*" >&2; return 0; } die() { echo "flipper-bls: $*" >&2; exit 1; } trim() { printf '%s' "$1" | sed 's/^ *//; s/ *$//'; } @@ -53,8 +66,12 @@ if [ "$COMMAND" = "remove" ]; then fi [ "$COMMAND" = "add" ] || exit 0 -# --- locate the boot mount, so in-entry paths are relative to the boot partition -- -[ -n "$BOOT_MNT" ] || BOOT_MNT="$(stat -c %m "$BOOT_ROOT")" +# In-entry paths are relative to the partition U-Boot actually reads. On Flipper One +# that is always the root partition — /boot is a directory/subvolume on it, never a +# separate boot partition U-Boot mounts — so paths must keep the /boot prefix, i.e. +# BOOT_MNT=/. (Override BOOT_MNT in the environment if /boot ever becomes its own +# partition, which would make boot_rel strip the mountpoint again.) +[ -n "$BOOT_MNT" ] || BOOT_MNT="/" # absolute path under BOOT_ROOT -> path as it must appear inside a BLS entry (relative # to the boot partition; identical to the absolute path when /boot is on the rootfs). @@ -130,7 +147,7 @@ fi # devicetreedir (U-Boot fdtdir extension) — referenced in place, opt-in via a # /etc/kernel/devicetreedir file containing a true boolean. Only when no single # devicetree was configured. -DEVICETREEDIR_SRC=""; DEVICETREEDIR_ENTRY="" +DEVICETREEDIR_SRC=""; DEVICETREEDIR_REL="" if [ -z "$DEVICETREE" ] && [ -f "$CONF_ROOT/devicetreedir" ]; then _v=""; read -r _v <"$CONF_ROOT/devicetreedir" || : case "$_v" in @@ -143,7 +160,7 @@ if [ -z "$DEVICETREE" ] && [ -f "$CONF_ROOT/devicetreedir" ]; then [ -n "$DEVICETREEDIR_SRC" ] && break done [ -n "$DEVICETREEDIR_SRC" ] || die "no device-tree directory for $KERNEL_VERSION" - DEVICETREEDIR_ENTRY="$(boot_rel "$DEVICETREEDIR_SRC")" + DEVICETREEDIR_REL="$(boot_rel "$DEVICETREEDIR_SRC")" # /usr/lib/...; prefixed per entry ;; esac fi @@ -180,6 +197,8 @@ done # write_entry FILE TITLE OPTIONS FDTOVERLAYS write_entry() { _f="$1"; _title="$2"; _opts="$3"; _fdtov="$4" + # devicetreedir is prefixed with THIS entry's root subvol (U-Boot reads subvolid 5) + _dtdir=""; [ -n "$DEVICETREEDIR_REL" ] && _dtdir="$(fdt_prefix "$_opts")$DEVICETREEDIR_REL" { echo "# Boot Loader Specification type#1 entry (Flipper One)" echo "title $_title" @@ -188,9 +207,9 @@ write_entry() { [ -n "$SORT_KEY" ] && echo "sort-key $SORT_KEY" [ -n "$_opts" ] && echo "options $_opts" echo "linux $KERNEL_ENTRY" - [ -n "$DEVICETREE_ENTRY" ] && echo "devicetree $DEVICETREE_ENTRY" - [ -n "$DEVICETREEDIR_ENTRY" ] && echo "devicetreedir $DEVICETREEDIR_ENTRY" - [ -n "$_fdtov" ] && echo "devicetree-overlay $_fdtov" + [ -n "$DEVICETREE_ENTRY" ] && echo "devicetree $DEVICETREE_ENTRY" + [ -n "$_dtdir" ] && echo "devicetreedir $_dtdir" + [ -n "$_fdtov" ] && echo "devicetree-overlay $_fdtov" for _i in $INITRD_ENTRIES; do echo "initrd $_i"; done } >"$_f" return 0 # don't let a trailing empty conditional's status abort `set -e` @@ -198,11 +217,12 @@ write_entry() { # Echo the space-separated paths of the named DT overlays (flat, next to the DTBs). # Returns non-zero with no output if any one is missing. -overlay_paths() { +overlay_paths() { # $1 = subvol prefix (e.g. /@Desktop); remaining args = overlay names + _pfx="$1"; shift _paths="" for _n in "$@"; do - [ -f "$OVERLAY_DIR/$_n.dtbo" ] || return 1 - _paths="$_paths${_paths:+ }$OVERLAY_DIR/$_n.dtbo" + [ -f "$OVERLAY_DIR/$_n.dtbo" ] || return 1 # check the real on-disk path + _paths="$_paths${_paths:+ }$_pfx$OVERLAY_DIR/$_n.dtbo" done printf '%s' "$_paths" } @@ -225,10 +245,10 @@ make_title() { fi } -# Base entry == the [DEFAULT] profile. -write_entry "$ENTRY_STEM.conf" "$(make_title '[DEFAULT]')" "$BASE_OPTS" "" +# Base entry == the [DESKTOP] profile. +write_entry "$ENTRY_STEM.conf" "$(make_title '[DESKTOP]')" "$BASE_OPTS" "" -[ -f "$PROFILES" ] || { log "$PROFILES not found, only the [DEFAULT] entry written"; exit 0; } +[ -f "$PROFILES" ] || { log "$PROFILES not found, only the [DESKTOP] entry written"; exit 0; } while IFS='|' read -r tok suf extra gate dtbos legend; do tok="$(echo "$tok" | tr -d '[:space:]')" @@ -257,7 +277,7 @@ while IFS='|' read -r tok suf extra gate dtbos legend; do required=0 case "$dtbos" in '!'*) required=1; dtbos="$(trim "${dtbos#\!}")" ;; esac - paths="$(overlay_paths $dtbos)" || paths="" + paths="$(overlay_paths "$(fdt_prefix "$opts")" $dtbos)" || paths="" if [ -z "$paths" ] && [ "$required" = 1 ]; then log "skip $tok (overlays not found for $KERNEL_VERSION)" continue diff --git a/overlays/configs/systemd/system/kodi.service.d/handoff.conf b/overlays/configs/systemd/system/kodi.service.d/handoff.conf deleted file mode 100644 index 7e7a007..0000000 --- a/overlays/configs/systemd/system/kodi.service.d/handoff.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Unit] -Conflicts=sddm.service -After=sddm.service -[Service] -ExecStop=-/usr/bin/kodi-send --action=Quit -TimeoutStopSec=5s \ No newline at end of file diff --git a/overlays/configs/systemd/system/sddm.service.d/handoff.conf b/overlays/configs/systemd/system/sddm.service.d/handoff.conf deleted file mode 100644 index e32a433..0000000 --- a/overlays/configs/systemd/system/sddm.service.d/handoff.conf +++ /dev/null @@ -1,3 +0,0 @@ -[Unit] -Conflicts=kodi.service -After=kodi.service \ No newline at end of file diff --git a/overlays/configs/systemd/system/tv-media-box.target b/overlays/configs/systemd/system/tv-media-box.target deleted file mode 100644 index 83419fe..0000000 --- a/overlays/configs/systemd/system/tv-media-box.target +++ /dev/null @@ -1,5 +0,0 @@ -[Unit] -Description=TV Media Box -Requires=multi-user.target -After=multi-user.target -AllowIsolate=yes \ No newline at end of file diff --git a/overlays/configs/sddm.conf.d/autologin.conf b/overlays/profile-kde/etc/sddm.conf.d/autologin.conf similarity index 100% rename from overlays/configs/sddm.conf.d/autologin.conf rename to overlays/profile-kde/etc/sddm.conf.d/autologin.conf diff --git a/overlays/configs/sddm.conf.d/wayland.conf b/overlays/profile-kde/etc/sddm.conf.d/wayland.conf similarity index 100% rename from overlays/configs/sddm.conf.d/wayland.conf rename to overlays/profile-kde/etc/sddm.conf.d/wayland.conf diff --git a/overlays/configs/systemd/system/sddm.service.d/seat.conf b/overlays/profile-kde/etc/systemd/system/sddm.service.d/seat.conf similarity index 100% rename from overlays/configs/systemd/system/sddm.service.d/seat.conf rename to overlays/profile-kde/etc/systemd/system/sddm.service.d/seat.conf diff --git a/overlays/configs/systemd/user/drkonqi-coredump-launcher.socket.d/override.conf b/overlays/profile-kde/etc/systemd/user/drkonqi-coredump-launcher.socket.d/override.conf similarity index 100% rename from overlays/configs/systemd/user/drkonqi-coredump-launcher.socket.d/override.conf rename to overlays/profile-kde/etc/systemd/user/drkonqi-coredump-launcher.socket.d/override.conf diff --git a/overlays/configs/systemd/user/plasma-kwin_wayland.service.d/override.conf b/overlays/profile-kde/etc/systemd/user/plasma-kwin_wayland.service.d/override.conf similarity index 100% rename from overlays/configs/systemd/user/plasma-kwin_wayland.service.d/override.conf rename to overlays/profile-kde/etc/systemd/user/plasma-kwin_wayland.service.d/override.conf diff --git a/overlays/configs/xdg/kdeglobals b/overlays/profile-kde/etc/xdg/kdeglobals similarity index 100% rename from overlays/configs/xdg/kdeglobals rename to overlays/profile-kde/etc/xdg/kdeglobals diff --git a/overlays/configs/xdg/kscreenlockerrc b/overlays/profile-kde/etc/xdg/kscreenlockerrc similarity index 100% rename from overlays/configs/xdg/kscreenlockerrc rename to overlays/profile-kde/etc/xdg/kscreenlockerrc diff --git a/overlays/configs/xdg/kwinrulesrc b/overlays/profile-kde/etc/xdg/kwinrulesrc similarity index 100% rename from overlays/configs/xdg/kwinrulesrc rename to overlays/profile-kde/etc/xdg/kwinrulesrc diff --git a/overlays/configs/xdg/plasmarc b/overlays/profile-kde/etc/xdg/plasmarc similarity index 100% rename from overlays/configs/xdg/plasmarc rename to overlays/profile-kde/etc/xdg/plasmarc diff --git a/overlays/configs/xdg/powerdevilrc b/overlays/profile-kde/etc/xdg/powerdevilrc similarity index 100% rename from overlays/configs/xdg/powerdevilrc rename to overlays/profile-kde/etc/xdg/powerdevilrc diff --git a/overlays/usr/share/plasma-systemmonitor/flipper.page b/overlays/profile-kde/usr/share/plasma-systemmonitor/flipper.page similarity index 100% rename from overlays/usr/share/plasma-systemmonitor/flipper.page rename to overlays/profile-kde/usr/share/plasma-systemmonitor/flipper.page diff --git a/overlays/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/defaults b/overlays/profile-kde/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/defaults similarity index 100% rename from overlays/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/defaults rename to overlays/profile-kde/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/defaults diff --git a/overlays/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/layouts/org.kde.plasma.desktop-layout.js b/overlays/profile-kde/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/layouts/org.kde.plasma.desktop-layout.js similarity index 100% rename from overlays/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/layouts/org.kde.plasma.desktop-layout.js rename to overlays/profile-kde/usr/share/plasma/look-and-feel/org.kde.breezedark.desktop/contents/layouts/org.kde.plasma.desktop-layout.js diff --git a/overlays/usr/share/wallpapers/FlipperOne/contents/images/wallpaper.jpg b/overlays/profile-kde/usr/share/wallpapers/FlipperOne/contents/images/wallpaper.jpg similarity index 100% rename from overlays/usr/share/wallpapers/FlipperOne/contents/images/wallpaper.jpg rename to overlays/profile-kde/usr/share/wallpapers/FlipperOne/contents/images/wallpaper.jpg diff --git a/overlays/usr/share/wallpapers/FlipperOne/metadata.json b/overlays/profile-kde/usr/share/wallpapers/FlipperOne/metadata.json similarity index 100% rename from overlays/usr/share/wallpapers/FlipperOne/metadata.json rename to overlays/profile-kde/usr/share/wallpapers/FlipperOne/metadata.json diff --git a/overlays/configs/NetworkManager/system-connections/flipusb0-router.nmconnection b/overlays/profile-router/etc/NetworkManager/system-connections/flipusb0-router.nmconnection similarity index 100% rename from overlays/configs/NetworkManager/system-connections/flipusb0-router.nmconnection rename to overlays/profile-router/etc/NetworkManager/system-connections/flipusb0-router.nmconnection diff --git a/overlays/configs/NetworkManager/system-connections/wifi-router2g.nmconnection b/overlays/profile-router/etc/NetworkManager/system-connections/wifi-router2g.nmconnection similarity index 100% rename from overlays/configs/NetworkManager/system-connections/wifi-router2g.nmconnection rename to overlays/profile-router/etc/NetworkManager/system-connections/wifi-router2g.nmconnection diff --git a/overlays/configs/NetworkManager/system-connections/wifi-router5g.nmconnection b/overlays/profile-router/etc/NetworkManager/system-connections/wifi-router5g.nmconnection similarity index 100% rename from overlays/configs/NetworkManager/system-connections/wifi-router5g.nmconnection rename to overlays/profile-router/etc/NetworkManager/system-connections/wifi-router5g.nmconnection diff --git a/overlays/configs/NetworkManager/system-connections/wifi-router6g.nmconnection b/overlays/profile-router/etc/NetworkManager/system-connections/wifi-router6g.nmconnection similarity index 100% rename from overlays/configs/NetworkManager/system-connections/wifi-router6g.nmconnection rename to overlays/profile-router/etc/NetworkManager/system-connections/wifi-router6g.nmconnection diff --git a/overlays/configs/sysctl.d/50-ip-forwarding.conf b/overlays/profile-router/etc/sysctl.d/50-ip-forwarding.conf similarity index 100% rename from overlays/configs/sysctl.d/50-ip-forwarding.conf rename to overlays/profile-router/etc/sysctl.d/50-ip-forwarding.conf diff --git a/overlays/configs/systemd/system/kodi.service b/overlays/profile-tv-media-box/etc/systemd/system/kodi.service similarity index 93% rename from overlays/configs/systemd/system/kodi.service rename to overlays/profile-tv-media-box/etc/systemd/system/kodi.service index 25d1a41..54166d9 100644 --- a/overlays/configs/systemd/system/kodi.service +++ b/overlays/profile-tv-media-box/etc/systemd/system/kodi.service @@ -16,4 +16,4 @@ ExecStart=/usr/bin/cage -- /usr/bin/kodi-standalone Restart=on-abort [Install] -WantedBy=tv-media-box.target \ No newline at end of file +WantedBy=multi-user.target \ No newline at end of file diff --git a/overlays/profile-tv-media-box/etc/systemd/system/kodi.service.d/shutdown.conf b/overlays/profile-tv-media-box/etc/systemd/system/kodi.service.d/shutdown.conf new file mode 100644 index 0000000..ea2c5a5 --- /dev/null +++ b/overlays/profile-tv-media-box/etc/systemd/system/kodi.service.d/shutdown.conf @@ -0,0 +1,5 @@ +[Service] +# graceful stop (sddm/kodi never coexist on a profile, so the old handoff Conflicts/After +# were dead; this just asks kodi to quit cleanly on stop) +ExecStop=-/usr/bin/kodi-send --action=Quit +TimeoutStopSec=5s diff --git a/overlays/configs/systemd/system/tv-media-box.target.wants/kodi.service b/overlays/profile-tv-media-box/etc/systemd/system/multi-user.target.wants/kodi.service similarity index 100% rename from overlays/configs/systemd/system/tv-media-box.target.wants/kodi.service rename to overlays/profile-tv-media-box/etc/systemd/system/multi-user.target.wants/kodi.service