ix init script duplication and CPIO creation issues

- Remove duplicate /sbin/init copying from initramfs_setup_zinit()
- Only /init should be config/init (initramfs setup script)
- No /sbin/init needed - config/init calls 'switch_root /mnt/root /sbin/zinit init'
- Remove unsupported cpio --owner option that broke CPIO creation
- Fix validation to not expect /sbin/init file
- Correct boot flow: /init → switch_root → /sbin/zinit init
- Remove strip and UPX compression from zinit binary copying
- UPX compression was corrupting the zinit binary causing segfaults after switch_root
- Keep zinit unmodified as it's
This commit is contained in:
2025-09-05 11:43:25 +02:00
parent 38dee2de74
commit 8c3868b242
102 changed files with 589 additions and 375 deletions

View File

@@ -422,4 +422,4 @@ function main() {
}
# Run main function with all arguments
main "$@"
main "$@"

219
scripts/dev-container.sh Executable file
View File

@@ -0,0 +1,219 @@
#!/bin/bash
# Efficient development container workflow - persistent container for debugging
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Container configuration
CONTAINER_NAME="zero-os-dev"
BUILDER_IMAGE="zero-os-builder:latest"
# Source common functions
source "${SCRIPT_DIR}/lib/common.sh"
function show_usage() {
cat << EOF
Zero OS Development Container Manager
Usage: $0 [COMMAND]
Commands:
start Start persistent development container
stop Stop development container
shell Enter development container shell
build Run build in persistent container
clean Clean and restart container
status Show container status
logs Show container logs
Environment Variables:
DEBUG Enable debug output (default: 1 for dev)
Examples:
$0 start # Start persistent container
$0 shell # Enter container for debugging
$0 build # Run build in persistent container
EOF
}
function dev_container_start() {
section_header "Starting Development Container"
# Check if container already exists
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
if podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
log_info "Development container already running"
return 0
else
log_info "Starting existing development container"
safe_execute podman start "$CONTAINER_NAME"
return 0
fi
fi
log_info "Creating new development container: ${CONTAINER_NAME}"
# Create persistent container with all necessary mounts and environment
safe_execute podman run -d \
--name "$CONTAINER_NAME" \
--privileged \
-v "${PROJECT_ROOT}:/workspace" \
-w /workspace \
-e DEBUG=1 \
-e ALPINE_VERSION=3.22 \
-e KERNEL_VERSION=6.12.44 \
-e RUST_TARGET=x86_64-unknown-linux-musl \
-e OPTIMIZATION_LEVEL=max \
"$BUILDER_IMAGE" \
sleep infinity
log_info "Development container started successfully"
log_info "Container name: ${CONTAINER_NAME}"
log_info "Access with: $0 shell"
}
function dev_container_stop() {
section_header "Stopping Development Container"
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
log_info "Stopping development container: ${CONTAINER_NAME}"
safe_execute podman stop "$CONTAINER_NAME"
log_info "Development container stopped"
else
log_info "Development container not found"
fi
}
function dev_container_shell() {
section_header "Entering Development Container Shell"
if ! podman container exists "$CONTAINER_NAME" 2>/dev/null; then
log_info "Development container not found, starting..."
dev_container_start
fi
if ! podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
log_info "Starting stopped development container"
safe_execute podman start "$CONTAINER_NAME"
fi
log_info "Entering container shell (exit with 'exit' or Ctrl+D)"
# Use direct execution for interactive shell (don't use safe_execute)
exec podman exec -it "$CONTAINER_NAME" /bin/bash
}
function dev_container_build() {
section_header "Running Build in Development Container"
if ! podman container exists "$CONTAINER_NAME" 2>/dev/null; then
log_info "Development container not found, starting..."
dev_container_start
fi
if ! podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}' | grep -q "running"; then
log_info "Starting stopped development container"
safe_execute podman start "$CONTAINER_NAME"
fi
log_info "Running build in persistent container (real-time output)"
log_info "Command: podman exec $CONTAINER_NAME ./scripts/build.sh $*"
# Use direct execution to show real-time output (bypass safe_execute)
podman exec "$CONTAINER_NAME" ./scripts/build.sh "$@"
local exit_code=$?
if [[ $exit_code -eq 0 ]]; then
log_info "Build completed successfully in container"
else
log_error "Build failed in container with exit code: $exit_code"
fi
return $exit_code
}
function dev_container_clean() {
section_header "Cleaning Development Container"
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
log_info "Removing existing development container"
safe_execute podman rm -f "$CONTAINER_NAME"
fi
log_info "Starting fresh development container"
dev_container_start
}
function dev_container_status() {
section_header "Development Container Status"
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
local status=$(podman container inspect "$CONTAINER_NAME" --format '{{.State.Status}}')
local created=$(podman container inspect "$CONTAINER_NAME" --format '{{.Created}}')
log_info "Container: ${CONTAINER_NAME}"
log_info "Status: ${status}"
log_info "Created: ${created}"
if [[ "$status" == "running" ]]; then
log_info "✓ Ready for development"
else
log_info "⚠ Container stopped - use '$0 start' to start"
fi
else
log_info "Development container not found"
log_info "Use '$0 start' to create"
fi
}
function dev_container_logs() {
section_header "Development Container Logs"
if podman container exists "$CONTAINER_NAME" 2>/dev/null; then
safe_execute podman logs "$CONTAINER_NAME"
else
log_error "Development container not found"
return 1
fi
}
# Main function
function main() {
local command="${1:-help}"
case "$command" in
start)
dev_container_start
;;
stop)
dev_container_stop
;;
shell)
dev_container_shell
;;
build)
shift
dev_container_build "$@"
;;
clean)
dev_container_clean
;;
status)
dev_container_status
;;
logs)
dev_container_logs
;;
help|--help|-h)
show_usage
;;
*)
log_error "Unknown command: $command"
show_usage
exit 1
;;
esac
}
main "$@"

View File

@@ -374,17 +374,18 @@ function alpine_install_firmware() {
local initramfs_dir="$1"
local firmware_conf="$2"
section_header "Installing Required Firmware Packages"
section_header "Installing Required Firmware Packages (Selective)"
# Use smart firmware selection from module analysis if available
local firmware_packages=()
if [[ -n "${REQUIRED_FIRMWARE_PACKAGES:-}" ]]; then
log_info "Using intelligent firmware selection based on required modules"
log_info "Using intelligent firmware selection based on COPIED modules only"
read -ra firmware_packages <<< "$REQUIRED_FIRMWARE_PACKAGES"
log_info "Required firmware packages (${#firmware_packages[@]}):"
for package in "${firmware_packages[@]}"; do
log_info " Required by modules: ${package}"
log_info " ${package}"
done
else
log_info "Falling back to firmware configuration file"

View File

@@ -205,20 +205,41 @@ function docker_commit_builder() {
log_info "Container committed successfully: ${new_tag}"
}
# Clean up container images
# Clean up container images and running containers
function docker_cleanup() {
local keep_builder="${1:-false}"
section_header "Cleaning Up Container Images"
section_header "Cleaning Up Containers and Images"
if [[ "$keep_builder" != "true" ]]; then
log_info "Cleaning up builder containers and images"
# Stop and remove any containers using the builder image
local containers_using_image=$(${CONTAINER_RUNTIME} ps -a --filter "ancestor=${BUILDER_IMAGE}" --format "{{.ID}}" 2>/dev/null || true)
if [[ -n "$containers_using_image" ]]; then
log_info "Stopping containers using builder image"
for container_id in $containers_using_image; do
log_info "Stopping container: $container_id"
${CONTAINER_RUNTIME} stop "$container_id" 2>/dev/null || true
${CONTAINER_RUNTIME} rm "$container_id" 2>/dev/null || true
done
fi
# Stop and remove development container if it exists
local dev_container="zero-os-dev"
if ${CONTAINER_RUNTIME} container exists "$dev_container" 2>/dev/null; then
log_info "Removing development container: $dev_container"
${CONTAINER_RUNTIME} rm -f "$dev_container" 2>/dev/null || true
fi
# Now remove the images
log_info "Removing builder images"
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}" || true
safe_execute ${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}-cached" || true
${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}" 2>/dev/null || log_warn "Could not remove ${BUILDER_IMAGE} (may not exist)"
${CONTAINER_RUNTIME} rmi "${BUILDER_IMAGE}-cached" 2>/dev/null || log_warn "Could not remove ${BUILDER_IMAGE}-cached (may not exist)"
fi
log_info "Pruning unused containers and images"
safe_execute ${CONTAINER_RUNTIME} system prune -f
${CONTAINER_RUNTIME} system prune -f 2>/dev/null || log_warn "Container prune failed"
log_info "Container cleanup complete"
}

View File

@@ -27,18 +27,9 @@ function initramfs_setup_zinit() {
return 1
fi
# Copy config/init as /sbin/init (not a symlink to zinit)
log_info "Installing config/init as /sbin/init"
safe_execute rm -f "${initramfs_dir}/sbin/init"
local config_init="${PROJECT_ROOT}/config/init"
if [[ -f "$config_init" ]]; then
safe_execute cp "$config_init" "${initramfs_dir}/sbin/init"
safe_execute chmod 755 "${initramfs_dir}/sbin/init"
log_info "✓ Installed config/init as /sbin/init"
else
log_error "config/init not found: $config_init"
return 1
fi
# Note: /sbin/init is not needed - config/init calls "switch_root /mnt/root /sbin/zinit init"
# So after switch_root, /sbin/zinit is the init system (not /sbin/init)
log_info "zinit will be called directly via '/sbin/zinit init' after switch_root"
# Copy zinit configuration (all YAML and scripts)
log_info "Installing zinit configuration"
@@ -117,29 +108,16 @@ function initramfs_copy_components() {
local copied_count=0
local missing_count=0
# Copy zinit to /sbin
# Copy zinit to /sbin (NO stripping/UPX - critical init system)
local zinit_binary="${components_dir}/zinit/target/x86_64-unknown-linux-musl/release/zinit"
if [[ -f "$zinit_binary" ]]; then
safe_mkdir "${initramfs_dir}/sbin"
safe_execute cp "$zinit_binary" "${initramfs_dir}/sbin/zinit"
safe_execute chmod +x "${initramfs_dir}/sbin/zinit"
# Strip and UPX compress zinit
local original_size=$(get_file_size "${initramfs_dir}/sbin/zinit")
if strip "${initramfs_dir}/sbin/zinit" 2>/dev/null || true; then
log_debug "Stripped zinit"
else
log_debug "zinit already stripped or strip failed"
fi
if command_exists "upx" && upx --best --force "${initramfs_dir}/sbin/zinit" >/dev/null 2>&1 || true; then
log_debug "UPX compressed zinit"
else
log_debug "UPX failed or already compressed"
fi
local final_size=$(get_file_size "${initramfs_dir}/sbin/zinit")
log_info "✓ Copied zinit ${original_size}${final_size} to /sbin/zinit"
# Keep zinit unmodified to prevent segfaults after switch_root
local size=$(get_file_size "${initramfs_dir}/sbin/zinit")
log_info "✓ Copied zinit (${size}) to /sbin/zinit (no optimization - critical binary)"
((copied_count++))
else
log_error "✗ zinit binary not found: ${zinit_binary}"
@@ -669,7 +647,6 @@ function initramfs_validate() {
# Check essential files and directories
local essential_items=(
"init"
"sbin/init"
"sbin/zinit"
"bin/busybox"
"etc/zinit"
@@ -699,12 +676,11 @@ function initramfs_validate() {
((errors++))
fi
# Check that /sbin/init is a script
if [[ -f "${initramfs_dir}/sbin/init" && -x "${initramfs_dir}/sbin/init" ]]; then
log_info " /sbin/init script found"
# Check that /sbin/init does NOT exist (zinit called directly)
if [[ -e "${initramfs_dir}/sbin/init" ]]; then
log_warn " /sbin/init exists but should not (zinit called directly)"
else
log_error " /sbin/init is missing or not executable"
((errors++))
log_info " /sbin/init correctly absent (zinit called directly)"
fi
# Check zinit configuration

View File

@@ -10,6 +10,20 @@ KERNEL_VERSION="${KERNEL_VERSION:-6.12.44}"
KERNEL_SOURCE_URL="${KERNEL_SOURCE_URL:-https://cdn.kernel.org/pub/linux/kernel}"
KERNEL_CONFIG_SOURCE="${KERNEL_CONFIG_SOURCE:-${PROJECT_ROOT}/configs/kernel-config-generic}"
# Get actual kernel version including LOCALVERSION from kernel config
function kernel_get_full_version() {
local base_version="${1:-$KERNEL_VERSION}"
local config_file="${2:-${PROJECT_ROOT}/config/kernel.config}"
# Extract LOCALVERSION from kernel config
local localversion=""
if [[ -f "$config_file" ]] && grep -q "^CONFIG_LOCALVERSION=" "$config_file"; then
localversion=$(grep "^CONFIG_LOCALVERSION=" "$config_file" | cut -d'"' -f2)
fi
echo "${base_version}${localversion}"
}
# Download kernel source
function kernel_download_source() {
local kernel_dir="$1"
@@ -176,10 +190,6 @@ function kernel_build_with_initramfs() {
local source_dir="${kernel_dir}/current"
safe_execute cd "$source_dir"
# Clean previous build
log_info "Cleaning previous kernel build"
safe_execute make clean
# Determine number of cores for parallel build
local cores=$(nproc)
local jobs=$((cores > 1 ? cores - 1 : 1)) # Leave one core free
@@ -215,13 +225,13 @@ function kernel_build_with_initramfs() {
fi
}
# Build modules for initramfs
# Build and install modules in container for proper dependency resolution
function kernel_build_modules() {
local kernel_dir="$1"
local modules_install_dir="$2"
local version="${3:-$KERNEL_VERSION}"
local initramfs_dir="$2"
local base_version="${3:-$KERNEL_VERSION}"
section_header "Building Kernel Modules"
section_header "Building Kernel Modules in Container"
local source_dir="${kernel_dir}/current"
@@ -232,6 +242,11 @@ function kernel_build_modules() {
safe_execute cd "$source_dir"
# Get the full kernel version including LOCALVERSION
local full_version=$(kernel_get_full_version "$base_version" "${PROJECT_ROOT}/config/kernel.config")
log_info "Base kernel version: ${base_version}"
log_info "Full kernel version: ${full_version}"
# Build modules
local cores=$(nproc)
local jobs=$((cores > 1 ? cores - 1 : 1))
@@ -239,19 +254,30 @@ function kernel_build_modules() {
log_info "Building kernel modules with ${jobs} parallel jobs"
safe_execute make -j${jobs} modules
# Install modules to staging area
log_info "Installing modules to: ${modules_install_dir}"
safe_mkdir "$modules_install_dir"
safe_execute make modules_install INSTALL_MOD_PATH="$modules_install_dir"
# Install modules in container for proper modinfo/depmod access
local container_modules_dir="/lib/modules"
log_info "Installing modules in container: ${container_modules_dir}"
safe_execute make modules_install INSTALL_MOD_PATH=/
# Run depmod to create module dependencies
local modules_dir="${modules_install_dir}/lib/modules/${version}"
if [[ -d "$modules_dir" ]]; then
log_info "Running depmod for module dependencies"
safe_execute depmod -a -b "$modules_install_dir" "$version"
# Run depmod in container context for proper dependency resolution
log_info "Running depmod in container for ${full_version}"
safe_execute depmod -a "$full_version"
# Verify module installation in container
if [[ -d "/lib/modules/${full_version}" ]]; then
local module_count=$(find "/lib/modules/${full_version}" -name "*.ko*" | wc -l)
log_info "Container modules installed: ${module_count} modules in /lib/modules/${full_version}"
# Export the container modules path for dependency resolution
export CONTAINER_MODULES_PATH="/lib/modules/${full_version}"
export KERNEL_FULL_VERSION="$full_version"
log_info "Module dependency resolution will use: ${CONTAINER_MODULES_PATH}"
else
log_error "Module installation in container failed"
return 1
fi
log_info "Kernel modules build complete"
log_info "Kernel modules build and container installation complete"
}
# Clean kernel build artifacts
@@ -278,4 +304,4 @@ function kernel_cleanup() {
# Export functions
export -f kernel_download_source kernel_apply_config kernel_modify_config_for_initramfs
export -f kernel_build_with_initramfs kernel_build_modules kernel_cleanup
export -f kernel_build_with_initramfs kernel_build_modules kernel_cleanup kernel_get_full_version

171
scripts/lib/stages.sh Normal file
View File

@@ -0,0 +1,171 @@
#!/bin/bash
# Build stage tracking and incremental build support
# Source common functions
LIB_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${LIB_SCRIPT_DIR}/common.sh"
# Stage tracking configuration
STAGES_DIR="${PROJECT_ROOT:-/workspace}/.build-stages"
# Initialize stage tracking
function stages_init() {
section_header "Initializing Stage Tracking"
safe_mkdir "$STAGES_DIR"
log_info "Stage tracking directory: ${STAGES_DIR}"
# Show existing completed stages
local completed_stages=()
if ls "$STAGES_DIR"/*.done >/dev/null 2>&1; then
while IFS= read -r stage_file; do
local stage_name=$(basename "$stage_file" .done)
completed_stages+=("$stage_name")
done < <(ls "$STAGES_DIR"/*.done 2>/dev/null)
log_info "Previously completed stages: ${completed_stages[*]}"
else
log_info "No previously completed stages found"
fi
}
# Check if stage is already completed
function stage_is_completed() {
local stage_name="$1"
local stage_file="${STAGES_DIR}/${stage_name}.done"
if [[ -f "$stage_file" ]]; then
local completion_time=$(stat -c %Y "$stage_file" 2>/dev/null || echo "unknown")
log_debug "Stage '$stage_name' already completed at $(date -d @$completion_time 2>/dev/null || echo "unknown time")"
return 0
else
log_debug "Stage '$stage_name' not completed yet"
return 1
fi
}
# Mark stage as completed
function stage_mark_completed() {
local stage_name="$1"
local stage_details="${2:-}"
local stage_file="${STAGES_DIR}/${stage_name}.done"
# Create completion marker with metadata
cat > "$stage_file" << EOF
# Stage completion marker
STAGE_NAME="$stage_name"
COMPLETED_AT="$(date -Iseconds)"
COMPLETED_BY="$(whoami)"
DETAILS="$stage_details"
EOF
log_info "Stage completed: $stage_name"
if [[ -n "$stage_details" ]]; then
log_debug " Details: $stage_details"
fi
}
# Remove stage completion marker (force rebuild)
function stage_force_rebuild() {
local stage_name="$1"
local stage_file="${STAGES_DIR}/${stage_name}.done"
if [[ -f "$stage_file" ]]; then
safe_execute rm "$stage_file"
log_info "Stage marked for rebuild: $stage_name"
else
log_debug "Stage not completed, no need to force rebuild: $stage_name"
fi
}
# Clear all stage markers (full rebuild)
function stages_clear_all() {
section_header "Clearing All Stage Markers"
if [[ -d "$STAGES_DIR" ]]; then
local marker_count=$(ls "$STAGES_DIR"/*.done 2>/dev/null | wc -l || echo "0")
if [[ $marker_count -gt 0 ]]; then
safe_execute rm -f "$STAGES_DIR"/*.done
log_info "Cleared ${marker_count} stage completion markers"
else
log_info "No stage markers to clear"
fi
fi
log_info "Next build will rebuild all stages"
}
# Run stage with completion tracking
function stage_run() {
local stage_name="$1"
local stage_function="$2"
shift 2
local stage_args=("$@")
log_info "=== STAGE: $stage_name ==="
# Check if stage is already completed (unless forced)
if [[ "${FORCE_REBUILD:-false}" != "true" ]] && stage_is_completed "$stage_name"; then
log_info "Skipping completed stage: $stage_name"
log_info " (Use FORCE_REBUILD=true or remove ${STAGES_DIR}/${stage_name}.done to rebuild)"
return 0
fi
log_info "Running stage: $stage_name"
local start_time=$(date +%s)
# Run the stage function with its arguments
if "$stage_function" "${stage_args[@]}"; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Mark as completed with timing info
stage_mark_completed "$stage_name" "Duration: ${duration}s, Function: $stage_function"
log_info "Stage '$stage_name' completed successfully (${duration}s)"
return 0
else
local exit_code=$?
log_error "Stage '$stage_name' failed with exit code: $exit_code"
return $exit_code
fi
}
# Show stage status
function stages_status() {
section_header "Build Stages Status"
local all_stages=(
"alpine_extract"
"alpine_configure"
"alpine_packages"
"alpine_firmware"
"components_build"
"components_verify"
"kernel_modules"
"zinit_setup"
"init_script"
"modules_setup"
"modules_copy"
"cleanup"
"validation"
"initramfs_create"
"initramfs_test"
"kernel_build"
)
log_info "Stage completion status:"
for stage in "${all_stages[@]}"; do
if stage_is_completed "$stage"; then
local completion_time=$(stat -c %Y "${STAGES_DIR}/${stage}.done" 2>/dev/null || echo "0")
local time_str=$(date -d @$completion_time '+%H:%M:%S' 2>/dev/null || echo "unknown")
log_info " [DONE] $stage (completed at $time_str)"
else
log_info " [TODO] $stage (pending)"
fi
done
}
# Export functions
export -f stages_init stage_is_completed stage_mark_completed stage_force_rebuild
export -f stages_clear_all stage_run stages_status