forked from tfgrid/zosbuilder
initramfs: switch to passwd -d -R in scripts/lib/initramfs.sh:initramfs_finalize_customization() for shadow-aware passwordless root (aligned with 9423b708 intent), drop sed and chpasswd paths, and add validation diagnostics. common: normalize INSTALL_DIR/COMPONENTS_DIR/KERNEL_DIR/DIST_DIR to absolute paths after sourcing config to prevent validation resolving under kernel/current. Dockerfile: include shadow (for passwd/chpasswd), ensure openssl and openssl-dev present; remove perl. config: introduce ZEROOS_PASSWORDLESS_ROOT default true and comment password vars. docs: NOTES.md updated with diagnostics and flow.
269 lines
6.7 KiB
Bash
269 lines
6.7 KiB
Bash
#!/bin/bash
|
|
# Common functions and utilities for Zero OS Alpine Initramfs Builder
|
|
|
|
# Strict error handling
|
|
set -euo pipefail
|
|
|
|
# Script directory detection (only if not already set)
|
|
if [[ -z "${SCRIPT_DIR:-}" ]]; then
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
fi
|
|
if [[ -z "${PROJECT_ROOT:-}" ]]; then
|
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
fi
|
|
|
|
# Colors for output (if terminal supports it)
|
|
if [[ -t 1 ]]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
else
|
|
RED=''
|
|
GREEN=''
|
|
YELLOW=''
|
|
BLUE=''
|
|
NC=''
|
|
fi
|
|
|
|
# Logging functions
|
|
function log_info() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${GREEN}[INFO]${NC} ${timestamp} - $*" >&2
|
|
}
|
|
|
|
function log_warn() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $*" >&2
|
|
}
|
|
|
|
function log_error() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo -e "${RED}[ERROR]${NC} ${timestamp} - $*" >&2
|
|
}
|
|
|
|
function log_debug() {
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
if [[ "${DEBUG:-0}" == "1" ]]; then
|
|
echo -e "${BLUE}[DEBUG]${NC} ${timestamp} - $*" >&2
|
|
fi
|
|
}
|
|
|
|
# Command execution with full transparency
|
|
function safe_execute() {
|
|
local cmd="$*"
|
|
log_info "Executing: ${cmd}"
|
|
|
|
if [[ "${DEBUG:-0}" == "1" ]] || in_container; then
|
|
# In debug mode or container, show all output for visibility
|
|
if ! ${cmd}; then
|
|
log_error "Command failed: ${cmd}"
|
|
exit 1
|
|
fi
|
|
else
|
|
# Normal mode, capture output and show only on error
|
|
local output
|
|
if ! output=$(${cmd} 2>&1); then
|
|
log_error "Command failed: ${cmd}"
|
|
log_error "Output: ${output}"
|
|
exit 1
|
|
else
|
|
# Show minimal progress indicator for non-debug mode
|
|
log_debug "Command completed successfully: ${cmd}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Section headers with clear text separators
|
|
function section_header() {
|
|
local title="$1"
|
|
echo ""
|
|
echo "=================================================="
|
|
echo "SECTION: ${title}"
|
|
echo "=================================================="
|
|
log_info "Starting section: ${title}"
|
|
}
|
|
|
|
# Check if command exists
|
|
function command_exists() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
# Check if we're running in a container
|
|
function in_container() {
|
|
[[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]] || grep -q 'container' /proc/1/cgroup 2>/dev/null
|
|
}
|
|
|
|
# Verify required tools are available
|
|
function check_dependencies() {
|
|
local missing_deps=()
|
|
|
|
# Core build tools
|
|
local required_tools=(
|
|
"git"
|
|
"wget"
|
|
"tar"
|
|
"gzip"
|
|
"xz"
|
|
"cpio"
|
|
"strip"
|
|
"upx"
|
|
"rustc"
|
|
"cargo"
|
|
)
|
|
|
|
for tool in "${required_tools[@]}"; do
|
|
if ! command_exists "$tool"; then
|
|
missing_deps+=("$tool")
|
|
fi
|
|
done
|
|
|
|
# Check for container runtime (if not in container)
|
|
if ! in_container; then
|
|
if ! command_exists "podman" && ! command_exists "docker"; then
|
|
missing_deps+=("podman or docker")
|
|
fi
|
|
fi
|
|
|
|
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
|
log_error "Missing required dependencies:"
|
|
for dep in "${missing_deps[@]}"; do
|
|
log_error " - $dep"
|
|
done
|
|
return 1
|
|
fi
|
|
|
|
log_info "All dependencies satisfied"
|
|
return 0
|
|
}
|
|
|
|
# Create directory safely
|
|
function safe_mkdir() {
|
|
local dir="$1"
|
|
log_debug "Creating directory: ${dir}"
|
|
safe_execute mkdir -p "$dir"
|
|
}
|
|
|
|
# Remove directory safely
|
|
function safe_rmdir() {
|
|
local dir="$1"
|
|
if [[ -d "$dir" ]]; then
|
|
log_debug "Removing directory: ${dir}"
|
|
safe_execute rm -rf "$dir"
|
|
fi
|
|
}
|
|
|
|
# Copy file/directory safely
|
|
function safe_copy() {
|
|
local src="$1"
|
|
local dst="$2"
|
|
log_debug "Copying: ${src} -> ${dst}"
|
|
safe_execute cp -r "$src" "$dst"
|
|
}
|
|
|
|
# Check if path is absolute
|
|
function is_absolute_path() {
|
|
[[ "$1" = /* ]]
|
|
}
|
|
|
|
# Resolve relative path to absolute
|
|
function resolve_path() {
|
|
local path="$1"
|
|
if is_absolute_path "$path"; then
|
|
echo "$path"
|
|
else
|
|
echo "$(pwd)/$path"
|
|
fi
|
|
}
|
|
|
|
# Get file size in human readable format
|
|
function get_file_size() {
|
|
local file="$1"
|
|
if [[ -f "$file" ]]; then
|
|
du -h "$file" | cut -f1
|
|
else
|
|
echo "0B"
|
|
fi
|
|
}
|
|
|
|
# Wait for file to exist with timeout
|
|
function wait_for_file() {
|
|
local file="$1"
|
|
local timeout="${2:-30}"
|
|
local count=0
|
|
|
|
while [[ ! -f "$file" && $count -lt $timeout ]]; do
|
|
sleep 1
|
|
((count++))
|
|
done
|
|
|
|
[[ -f "$file" ]]
|
|
}
|
|
|
|
# Cleanup function for traps
|
|
function cleanup_on_exit() {
|
|
local exit_code=$?
|
|
log_info "Build process exiting with code: ${exit_code}"
|
|
|
|
# Unmount any mounted filesystems
|
|
if [[ -n "${CLEANUP_MOUNTS:-}" ]]; then
|
|
for mount in $CLEANUP_MOUNTS; do
|
|
if mountpoint -q "$mount" 2>/dev/null; then
|
|
log_info "Unmounting: $mount"
|
|
umount "$mount" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
|
|
exit $exit_code
|
|
}
|
|
|
|
# Set up exit trap
|
|
trap cleanup_on_exit EXIT INT TERM
|
|
|
|
# Load build configuration after functions are defined
|
|
BUILD_CONF="${PROJECT_ROOT}/config/build.conf"
|
|
if [[ -f "$BUILD_CONF" ]]; then
|
|
log_debug "Loading build configuration from: ${BUILD_CONF}"
|
|
# shellcheck source=/dev/null
|
|
source "$BUILD_CONF"
|
|
else
|
|
log_warn "Build configuration not found: ${BUILD_CONF}"
|
|
log_warn "Using default values"
|
|
fi
|
|
|
|
# Normalize key directory variables to absolute paths anchored at PROJECT_ROOT.
|
|
# This prevents later re-sourcing from accidentally re-introducing relative paths.
|
|
if [[ -z "${INSTALL_DIR:-}" ]]; then
|
|
INSTALL_DIR="${PROJECT_ROOT}/initramfs"
|
|
elif [[ "${INSTALL_DIR}" != /* ]]; then
|
|
INSTALL_DIR="${PROJECT_ROOT}/${INSTALL_DIR#./}"
|
|
fi
|
|
|
|
if [[ -z "${COMPONENTS_DIR:-}" ]]; then
|
|
COMPONENTS_DIR="${PROJECT_ROOT}/components"
|
|
elif [[ "${COMPONENTS_DIR}" != /* ]]; then
|
|
COMPONENTS_DIR="${PROJECT_ROOT}/${COMPONENTS_DIR#./}"
|
|
fi
|
|
|
|
if [[ -z "${KERNEL_DIR:-}" ]]; then
|
|
KERNEL_DIR="${PROJECT_ROOT}/kernel"
|
|
elif [[ "${KERNEL_DIR}" != /* ]]; then
|
|
KERNEL_DIR="${PROJECT_ROOT}/${KERNEL_DIR#./}"
|
|
fi
|
|
|
|
if [[ -z "${DIST_DIR:-}" ]]; then
|
|
DIST_DIR="${PROJECT_ROOT}/dist"
|
|
elif [[ "${DIST_DIR}" != /* ]]; then
|
|
DIST_DIR="${PROJECT_ROOT}/${DIST_DIR#./}"
|
|
fi
|
|
|
|
# Export common variables
|
|
export SCRIPT_DIR PROJECT_ROOT
|
|
export INSTALL_DIR COMPONENTS_DIR KERNEL_DIR DIST_DIR
|
|
export -f log_info log_warn log_error log_debug
|
|
export -f safe_execute section_header
|
|
export -f command_exists in_container check_dependencies
|
|
export -f safe_mkdir safe_rmdir safe_copy
|
|
export -f is_absolute_path resolve_path get_file_size wait_for_file |