diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-01-06 20:45:13 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-01-21 18:00:26 -0500 |
| commit | f17830e62d531ad411a05e214270c5fc4939799f (patch) | |
| tree | fd65fe626abb745ce1d81d281e4bd8f4d9f4a082 /recipes-containers/vcontainer/files | |
| parent | 6ee20538e226d3696c73206c048f826b9aa92734 (diff) | |
| download | meta-virtualization-f17830e62d531ad411a05e214270c5fc4939799f.tar.gz | |
vcontainer: add vpdmn Podman support
Add vpdmn - Podman CLI wrapper for cross-architecture container operations:
Scripts:
- vpdmn.sh: Podman CLI entry point (vpdmn-x86_64, vpdmn-aarch64)
- vpdmn-init.sh: Podman init script for QEMU guest
Recipes:
- vpdmn-native: Installs vpdmn CLI wrappers
- vpdmn-rootfs-image: Builds Podman rootfs with crun, netavark, skopeo
- vpdmn-initramfs-create: Creates bootable initramfs blob
The vpdmn CLI provides Podman-compatible commands executed inside a
QEMU-emulated environment. Unlike vdkr, Podman is daemonless which
simplifies the guest init process.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/vcontainer/files')
4 files changed, 258 insertions, 0 deletions
diff --git a/recipes-containers/vcontainer/files/blobs/vpdmn/aarch64/README b/recipes-containers/vcontainer/files/blobs/vpdmn/aarch64/README new file mode 100644 index 00000000..a4197779 --- /dev/null +++ b/recipes-containers/vcontainer/files/blobs/vpdmn/aarch64/README | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | vpdmn aarch64 Blobs | ||
| 2 | ==================== | ||
| 3 | |||
| 4 | This directory should contain the boot blobs for vpdmn aarch64: | ||
| 5 | |||
| 6 | Required files: | ||
| 7 | - Image : Linux kernel for aarch64 | ||
| 8 | - initramfs.cpio.gz : Tiny initramfs for switch_root | ||
| 9 | - rootfs.img : Ext4 root filesystem with Podman tools | ||
| 10 | |||
| 11 | Build instructions: | ||
| 12 | 1. Set MACHINE for aarch64: | ||
| 13 | MACHINE=qemuarm64 | ||
| 14 | |||
| 15 | 2. Build the blobs: | ||
| 16 | bitbake vpdmn-initramfs-create | ||
| 17 | |||
| 18 | 3. Copy from deploy directory: | ||
| 19 | cp tmp/deploy/images/qemuarm64/vpdmn/aarch64/Image . | ||
| 20 | cp tmp/deploy/images/qemuarm64/vpdmn/aarch64/initramfs.cpio.gz . | ||
| 21 | cp tmp/deploy/images/qemuarm64/vpdmn/aarch64/rootfs.img . | ||
| 22 | |||
| 23 | Once these files are present, vpdmn-native will automatically include them. | ||
| 24 | |||
| 25 | Alternatively, set VPDMN_USE_DEPLOY = "1" in local.conf to use blobs | ||
| 26 | directly from DEPLOY_DIR without copying to the layer. | ||
diff --git a/recipes-containers/vcontainer/files/blobs/vpdmn/x86_64/README b/recipes-containers/vcontainer/files/blobs/vpdmn/x86_64/README new file mode 100644 index 00000000..30ddb445 --- /dev/null +++ b/recipes-containers/vcontainer/files/blobs/vpdmn/x86_64/README | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | vpdmn x86_64 Blobs | ||
| 2 | =================== | ||
| 3 | |||
| 4 | This directory should contain the boot blobs for vpdmn x86_64: | ||
| 5 | |||
| 6 | Required files: | ||
| 7 | - bzImage : Linux kernel for x86_64 | ||
| 8 | - initramfs.cpio.gz : Tiny initramfs for switch_root | ||
| 9 | - rootfs.img : Ext4 root filesystem with Podman tools | ||
| 10 | |||
| 11 | Build instructions: | ||
| 12 | 1. Set MACHINE for x86_64: | ||
| 13 | MACHINE=qemux86-64 | ||
| 14 | |||
| 15 | 2. Build the blobs: | ||
| 16 | bitbake vpdmn-initramfs-create | ||
| 17 | |||
| 18 | 3. Copy from deploy directory: | ||
| 19 | cp tmp/deploy/images/qemux86-64/vpdmn/x86_64/bzImage . | ||
| 20 | cp tmp/deploy/images/qemux86-64/vpdmn/x86_64/initramfs.cpio.gz . | ||
| 21 | cp tmp/deploy/images/qemux86-64/vpdmn/x86_64/rootfs.img . | ||
| 22 | |||
| 23 | Once these files are present, vpdmn-native will automatically include them. | ||
| 24 | |||
| 25 | Alternatively, set VPDMN_USE_DEPLOY = "1" in local.conf to use blobs | ||
| 26 | directly from DEPLOY_DIR without copying to the layer. | ||
diff --git a/recipes-containers/vcontainer/files/vpdmn-init.sh b/recipes-containers/vcontainer/files/vpdmn-init.sh new file mode 100755 index 00000000..52aa9129 --- /dev/null +++ b/recipes-containers/vcontainer/files/vpdmn-init.sh | |||
| @@ -0,0 +1,181 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # vpdmn-init.sh | ||
| 7 | # Init script for vpdmn: execute arbitrary podman commands in QEMU | ||
| 8 | # | ||
| 9 | # This script runs on a real ext4 filesystem after switch_root from initramfs. | ||
| 10 | # The preinit script mounted /dev/vda (rootfs.img) and did switch_root to us. | ||
| 11 | # | ||
| 12 | # Drive layout (rootfs.img is always /dev/vda, mounted as /): | ||
| 13 | # /dev/vda = rootfs.img (this script runs from here, mounted as /) | ||
| 14 | # /dev/vdb = input disk (optional, OCI/tar/dir data) | ||
| 15 | # /dev/vdc = state disk (optional, persistent Podman storage) | ||
| 16 | # | ||
| 17 | # Kernel parameters: | ||
| 18 | # podman_cmd=<base64> Base64-encoded podman command + args | ||
| 19 | # podman_input=<type> Input type: none, oci, tar, dir (default: none) | ||
| 20 | # podman_output=<type> Output type: text, tar, storage (default: text) | ||
| 21 | # podman_state=<type> State type: none, disk (default: none) | ||
| 22 | # podman_network=1 Enable networking (configure eth0, DNS) | ||
| 23 | # | ||
| 24 | # Version: 1.1.0 | ||
| 25 | # | ||
| 26 | # Note: Podman is daemonless - no containerd/dockerd required! | ||
| 27 | |||
| 28 | # Set runtime-specific parameters before sourcing common code | ||
| 29 | VCONTAINER_RUNTIME_NAME="vpdmn" | ||
| 30 | VCONTAINER_RUNTIME_CMD="podman" | ||
| 31 | VCONTAINER_RUNTIME_PREFIX="podman" | ||
| 32 | VCONTAINER_STATE_DIR="/var/lib/containers/storage" | ||
| 33 | VCONTAINER_SHARE_NAME="vpdmn_share" | ||
| 34 | VCONTAINER_VERSION="1.1.0" | ||
| 35 | |||
| 36 | # Source common init functions | ||
| 37 | # When installed as /init, common file is at /vcontainer-init-common.sh | ||
| 38 | . /vcontainer-init-common.sh | ||
| 39 | |||
| 40 | # ============================================================================ | ||
| 41 | # Podman-Specific Functions | ||
| 42 | # ============================================================================ | ||
| 43 | |||
| 44 | setup_podman_environment() { | ||
| 45 | # Podman needs XDG_RUNTIME_DIR | ||
| 46 | export XDG_RUNTIME_DIR="/run/user/0" | ||
| 47 | mkdir -p "$XDG_RUNTIME_DIR" | ||
| 48 | chmod 700 "$XDG_RUNTIME_DIR" | ||
| 49 | } | ||
| 50 | |||
| 51 | setup_podman_mounts() { | ||
| 52 | # Podman needs /dev/shm | ||
| 53 | mkdir -p /dev/shm | ||
| 54 | mount -t tmpfs tmpfs /dev/shm | ||
| 55 | |||
| 56 | # Mount /var/volatile for Yocto's volatile symlinks (/var/tmp -> volatile/tmp, etc.) | ||
| 57 | mkdir -p /var/volatile | ||
| 58 | mount -t tmpfs tmpfs /var/volatile | ||
| 59 | mkdir -p /var/volatile/tmp /var/volatile/log /var/volatile/run /var/volatile/cache | ||
| 60 | |||
| 61 | # Also mount /var/cache directly (not a symlink) | ||
| 62 | mount -t tmpfs tmpfs /var/cache | ||
| 63 | } | ||
| 64 | |||
| 65 | setup_podman_storage() { | ||
| 66 | mkdir -p /run/lock | ||
| 67 | |||
| 68 | # /var/lib/containers exists in rootfs.img (read-only), mount tmpfs over it | ||
| 69 | mount -t tmpfs tmpfs /var/lib/containers | ||
| 70 | mkdir -p /var/lib/containers/storage | ||
| 71 | |||
| 72 | # Handle Podman storage | ||
| 73 | if [ -n "$STATE_DISK" ] && [ -b "$STATE_DISK" ]; then | ||
| 74 | log "Mounting state disk $STATE_DISK as /var/lib/containers/storage..." | ||
| 75 | if mount -t ext4 "$STATE_DISK" /var/lib/containers/storage 2>&1; then | ||
| 76 | log "SUCCESS: Mounted $STATE_DISK as Podman storage" | ||
| 77 | log "Podman storage contents:" | ||
| 78 | [ "$QUIET_BOOT" = "0" ] && ls -la /var/lib/containers/storage/ 2>/dev/null || log "(empty)" | ||
| 79 | else | ||
| 80 | log "WARNING: Failed to mount state disk, using tmpfs fallback" | ||
| 81 | RUNTIME_STATE="none" | ||
| 82 | fi | ||
| 83 | else | ||
| 84 | log "Using tmpfs for Podman storage (ephemeral)..." | ||
| 85 | fi | ||
| 86 | } | ||
| 87 | |||
| 88 | verify_podman() { | ||
| 89 | # Podman is daemonless - just verify it's available | ||
| 90 | if [ -x "/usr/bin/podman" ]; then | ||
| 91 | log "Podman available: $(podman --version 2>/dev/null || echo 'version unknown')" | ||
| 92 | else | ||
| 93 | echo "===ERROR===" | ||
| 94 | echo "Podman not found at /usr/bin/podman" | ||
| 95 | sleep 2 | ||
| 96 | reboot -f | ||
| 97 | fi | ||
| 98 | } | ||
| 99 | |||
| 100 | # Podman is daemonless - nothing to stop | ||
| 101 | stop_runtime_daemons() { | ||
| 102 | : | ||
| 103 | } | ||
| 104 | |||
| 105 | handle_storage_output() { | ||
| 106 | # Export entire podman storage | ||
| 107 | # Tar from inside /var/lib/containers/storage so paths are vfs-images/... directly | ||
| 108 | echo "Packaging Podman storage..." | ||
| 109 | if ! cd /var/lib/containers/storage; then | ||
| 110 | echo "===ERROR===" | ||
| 111 | echo "Failed to cd to /var/lib/containers/storage" | ||
| 112 | echo "Contents of /var/lib/containers:" | ||
| 113 | ls -la /var/lib/containers/ 2>&1 || echo "(not found)" | ||
| 114 | poweroff -f | ||
| 115 | exit 1 | ||
| 116 | fi | ||
| 117 | tar -cf /tmp/storage.tar . | ||
| 118 | |||
| 119 | STORAGE_SIZE=$(stat -c%s /tmp/storage.tar 2>/dev/null || echo "0") | ||
| 120 | echo "Storage size: $STORAGE_SIZE bytes" | ||
| 121 | |||
| 122 | if [ "$STORAGE_SIZE" -gt 1000 ]; then | ||
| 123 | dmesg -n 1 | ||
| 124 | echo "===STORAGE_START===" | ||
| 125 | base64 /tmp/storage.tar | ||
| 126 | echo "===STORAGE_END===" | ||
| 127 | echo "===EXIT_CODE=$EXEC_EXIT_CODE===" | ||
| 128 | else | ||
| 129 | echo "===ERROR===" | ||
| 130 | echo "Storage too small" | ||
| 131 | fi | ||
| 132 | } | ||
| 133 | |||
| 134 | # ============================================================================ | ||
| 135 | # Main | ||
| 136 | # ============================================================================ | ||
| 137 | |||
| 138 | # Initialize base environment | ||
| 139 | setup_base_environment | ||
| 140 | setup_podman_environment | ||
| 141 | mount_base_filesystems | ||
| 142 | |||
| 143 | # Check for quiet boot mode | ||
| 144 | check_quiet_boot | ||
| 145 | |||
| 146 | log "=== vpdmn Init ===" | ||
| 147 | log "Version: $VCONTAINER_VERSION" | ||
| 148 | |||
| 149 | # Mount tmpfs directories and Podman-specific mounts | ||
| 150 | mount_tmpfs_dirs | ||
| 151 | setup_podman_mounts | ||
| 152 | setup_cgroups | ||
| 153 | |||
| 154 | # Parse kernel command line | ||
| 155 | parse_cmdline | ||
| 156 | |||
| 157 | # Detect and configure disks | ||
| 158 | detect_disks | ||
| 159 | |||
| 160 | # Set up Podman storage (Podman-specific) | ||
| 161 | setup_podman_storage | ||
| 162 | |||
| 163 | # Mount input disk | ||
| 164 | mount_input_disk | ||
| 165 | |||
| 166 | # Configure networking | ||
| 167 | configure_networking | ||
| 168 | |||
| 169 | # Verify podman is available (no daemon to start) | ||
| 170 | verify_podman | ||
| 171 | |||
| 172 | # Handle daemon mode or single command execution | ||
| 173 | if [ "$RUNTIME_DAEMON" = "1" ]; then | ||
| 174 | run_daemon_mode | ||
| 175 | else | ||
| 176 | prepare_input_path | ||
| 177 | execute_command | ||
| 178 | fi | ||
| 179 | |||
| 180 | # Graceful shutdown | ||
| 181 | graceful_shutdown | ||
diff --git a/recipes-containers/vcontainer/files/vpdmn.sh b/recipes-containers/vcontainer/files/vpdmn.sh new file mode 100755 index 00000000..30775d35 --- /dev/null +++ b/recipes-containers/vcontainer/files/vpdmn.sh | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 5 | # | ||
| 6 | # vpdmn: Podman-like interface for cross-architecture container operations | ||
| 7 | # | ||
| 8 | # This provides a familiar podman-like CLI that executes commands inside | ||
| 9 | # a QEMU-emulated environment with the target architecture's Podman. | ||
| 10 | # | ||
| 11 | # Command naming convention: | ||
| 12 | # - Commands matching Podman's syntax/semantics use Podman's name (import, load, save, etc.) | ||
| 13 | # - Extended commands with non-Podman behavior use 'v' prefix (vimport) | ||
| 14 | |||
| 15 | # Set runtime-specific parameters before sourcing common code | ||
| 16 | VCONTAINER_RUNTIME_NAME="vpdmn" | ||
| 17 | VCONTAINER_RUNTIME_CMD="podman" | ||
| 18 | VCONTAINER_RUNTIME_PREFIX="VPDMN" | ||
| 19 | VCONTAINER_IMPORT_TARGET="containers-storage:" | ||
| 20 | VCONTAINER_STATE_FILE="podman-state.img" | ||
| 21 | VCONTAINER_OTHER_PREFIX="VDKR" | ||
| 22 | VCONTAINER_VERSION="1.2.0" | ||
| 23 | |||
| 24 | # Source common implementation | ||
| 25 | source "$(dirname "${BASH_SOURCE[0]}")/vcontainer-common.sh" "$@" | ||
