#!/bin/bash # resolve-module-dependencies.sh - Automatically resolve kernel module dependencies using modinfo # This script builds a complete dependency tree for specified kernel modules # Don't exit on errors - we want to handle missing modules gracefully set -o pipefail # Configuration KERNEL_BUILD_DIR="" MODULES_DIR="" KERNEL_VERSION="" VERBOSE=${VERBOSE:-0} # Global arrays for tracking declare -A RESOLVED_MODULES=() # Track resolved modules to avoid duplicates declare -A PROCESSING_MODULES=() # Track modules being processed to detect cycles declare -a DEPENDENCY_ORDER=() # Final ordered list of modules usage() { echo "Usage: $0 [OPTIONS] MODULE [MODULE...]" echo "" echo "Resolve kernel module dependencies using modinfo and copy to initramfs" echo "" echo "Options:" echo " -k DIR Kernel build directory (e.g., /build/kernel/linux-6.12.41)" echo " -m DIR Target modules directory (e.g., /build/initramfs/lib/modules/6.12.41-Zero-OS)" echo " -v VER Kernel version (e.g., 6.12.41-Zero-OS)" echo " -V Verbose output" echo " -h Show this help" echo "" echo "Examples:" echo " $0 -k /build/kernel/linux-6.12.41 -m /build/initramfs/lib/modules/6.12.41-Zero-OS -v 6.12.41-Zero-OS tun e1000e" echo " $0 -k /build/kernel/linux-6.12.41 -m /build/initramfs/lib/modules/6.12.41-Zero-OS -v 6.12.41-Zero-OS mycelium-modules.list" } log() { if [ "$VERBOSE" -eq 1 ]; then echo "[INFO] $*" >&2 fi } warn() { echo "[WARN] $*" >&2 } error() { echo "[ERROR] $*" >&2 exit 1 } # Find a kernel module file in the build directory find_module() { local module_name="$1" # Try with .ko extension local ko_file=$(find "$KERNEL_BUILD_DIR" -name "${module_name}.ko" -type f 2>/dev/null | head -1) if [ -n "$ko_file" ]; then echo "$ko_file" return 0 fi # Try without extension (in case it was passed with .ko) local base_name="${module_name%.ko}" ko_file=$(find "$KERNEL_BUILD_DIR" -name "${base_name}.ko" -type f 2>/dev/null | head -1) if [ -n "$ko_file" ]; then echo "$ko_file" return 0 fi return 1 } # Get dependencies for a module using modinfo get_module_dependencies() { local module_file="$1" local module_name=$(basename "$module_file" .ko) if [ ! -f "$module_file" ]; then warn "Module file not found: $module_file" return 0 # Continue processing, just no dependencies fi # Use modinfo to get dependencies local deps=$(modinfo -F depends "$module_file" 2>/dev/null | tr ',' ' ') if [ -n "$deps" ]; then log "Dependencies for $module_name: $deps" echo "$deps" else log "No dependencies for $module_name" fi } # Recursively resolve module dependencies resolve_dependencies() { local module_name="$1" local depth="$2" local indent="" # Create indentation for visual tree for i in $(seq 1 $depth); do indent=" $indent" done log "${indent}Resolving: $module_name (depth: $depth)" # Check if we're already processing this module (circular dependency) if [ "${PROCESSING_MODULES[$module_name]}" = "1" ]; then warn "${indent}Circular dependency detected for: $module_name" return 0 fi # Check if already resolved if [ "${RESOLVED_MODULES[$module_name]}" = "1" ]; then log "${indent}Already resolved: $module_name" return 0 fi # Mark as being processed PROCESSING_MODULES[$module_name]=1 # Find the module file local module_file=$(find_module "$module_name") if [ -z "$module_file" ]; then warn "${indent}Module not found: $module_name (skipping)" unset PROCESSING_MODULES[$module_name] # Don't mark as resolved since we couldn't find it return 0 # Continue processing other modules fi # Get dependencies local deps=$(get_module_dependencies "$module_file") # Recursively resolve dependencies first for dep in $deps; do if [ -n "$dep" ]; then resolve_dependencies "$dep" $((depth + 1)) fi done # Mark as resolved and add to ordered list RESOLVED_MODULES[$module_name]=1 DEPENDENCY_ORDER+=("$module_name") log "${indent}Resolved: $module_name" # Remove from processing list unset PROCESSING_MODULES[$module_name] } # Copy module to target directory copy_module() { local module_name="$1" local module_file=$(find_module "$module_name") if [ -z "$module_file" ]; then warn "Cannot copy module, file not found: $module_name" return 1 fi # Determine target subdirectory based on module path local rel_path=$(echo "$module_file" | sed "s|^$KERNEL_BUILD_DIR/||") local target_dir="$MODULES_DIR/$(dirname "$rel_path")" local target_file="$target_dir/$(basename "$module_file")" # Create target directory mkdir -p "$target_dir" # Copy module cp "$module_file" "$target_file" log "Copied: $module_name -> $target_file" } # Parse command line arguments while getopts "k:m:v:Vh" opt; do case $opt in k) KERNEL_BUILD_DIR="$OPTARG" ;; m) MODULES_DIR="$OPTARG" ;; v) KERNEL_VERSION="$OPTARG" ;; V) VERBOSE=1 ;; h) usage; exit 0 ;; *) usage; exit 1 ;; esac done shift $((OPTIND-1)) # Validate required parameters if [ -z "$KERNEL_BUILD_DIR" ] || [ -z "$MODULES_DIR" ] || [ -z "$KERNEL_VERSION" ]; then error "Missing required parameters. Use -h for help." fi if [ ! -d "$KERNEL_BUILD_DIR" ]; then error "Kernel build directory not found: $KERNEL_BUILD_DIR" fi if [ $# -eq 0 ]; then error "No modules specified. Use -h for help." fi # Parse module list (can be individual modules or a file containing module names) REQUIRED_MODULES=() for arg in "$@"; do if [ -f "$arg" ]; then # Read modules from file log "Reading modules from file: $arg" while IFS= read -r line; do # Skip empty lines and comments line=$(echo "$line" | sed 's/#.*//' | xargs) if [ -n "$line" ]; then REQUIRED_MODULES+=("$line") fi done < "$arg" else # Individual module name REQUIRED_MODULES+=("$arg") fi done if [ ${#REQUIRED_MODULES[@]} -eq 0 ]; then error "No valid modules found to process" fi log "Processing ${#REQUIRED_MODULES[@]} required modules: ${REQUIRED_MODULES[*]}" # Resolve dependencies for all required modules echo "Resolving dependencies for: ${REQUIRED_MODULES[*]}" for module in "${REQUIRED_MODULES[@]}"; do echo "Processing: $module" resolve_dependencies "$module" 0 done # Report results echo "" echo "Dependency resolution complete!" echo "Total modules required: ${#DEPENDENCY_ORDER[@]}" echo "" echo "Dependency order:" for i in "${!DEPENDENCY_ORDER[@]}"; do printf "%3d. %s\n" $((i+1)) "${DEPENDENCY_ORDER[$i]}" done # Copy all modules echo "" echo "Copying modules to: $MODULES_DIR" mkdir -p "$MODULES_DIR" COPIED_COUNT=0 FAILED_COUNT=0 for module in "${DEPENDENCY_ORDER[@]}"; do if copy_module "$module"; then ((COPIED_COUNT++)) else ((FAILED_COUNT++)) fi done # Summary echo "" echo "Module installation summary:" echo " Successfully copied: $COPIED_COUNT modules" echo " Failed to copy: $FAILED_COUNT modules" echo " Target directory: $MODULES_DIR" # Exit with appropriate code if [ $COPIED_COUNT -gt 0 ]; then echo "SUCCESS: At least some modules were installed successfully" exit 0 elif [ $FAILED_COUNT -gt 0 ]; then echo "ERROR: All module installations failed" exit 1 else echo "ERROR: No modules were processed" exit 1 fi