#! /bin/sh
set -e

# Скрипт для сборки uird для всех ядер
# https://rosa.ru

show_help() {
    cat << EOF
Usage: uird-regen [OPTIONS]

Rebuild uird (initial ramdisk) for all kernels with all available configs.

DESCRIPTION:
    This script automatically detects all installed kernels and builds
    a corresponding uird for each kernel using configuration files from
    /etc/uirdcfg.d/. Each config is temporarily copied to
    /usr/share/uird/configs/uird_configs/ before building and removed
    afterwards.

CONFIGURATION:
    All settings can be overridden by environment variables:

    UIRD_OUTPUT_DIR          Output directory for uird files (default: /boot)
    UIRD_CONFIG_DIR          Directory with uird configs (default: /etc/uirdcfg.d)
    UIRD_CONFIGS_DIR         Target directory for configs (default: /usr/share/uird/configs/uird_configs)
    UIRD_DEFAULT_CONFIG      Default config name (default: legacy)
    UIRD_DEFAULT_OPTS        Default build options (default: -l)

CONFIG FILE FORMAT:
    Config files in $UIRD_CONFIG_DIR are plain text files with uird boot
    parameters. Build options can be specified with a special comment:

    #MKUIRD_CMDLINE=-e qemu,aria -l

    If #MKUIRD_CMDLINE is not present, default options from UIRD_DEFAULT_OPTS
    are used.

BUILD PROCESS:
    1. Scan /boot/ for all installed kernels
    2. For each config in $UIRD_CONFIG_DIR:
       a. Copy config to $UIRD_CONFIGS_DIR
       b. For each kernel:
          - Build uird with name: uird.CONFIG-KERNEL_VERSION.cpio.xz
          - Place it in $UIRD_OUTPUT_DIR
       c. Remove config from $UIRD_CONFIGS_DIR
    3. Remove old uird files for kernels that no longer exist

OPTIONS:
    -h, --help     Show this help message
    -v, --version  Show version information

EXAMPLES:
    uird-regen                    # Rebuild all uird files with default settings
    UIRD_OUTPUT_DIR=/tmp uird-regen  # Build to /tmp instead of /boot
    UIRD_DEFAULT_OPTS="" uird-regen  # Build with no default options

EXIT STATUS:
    0   All builds succeeded
    1   One or more builds failed

FILES:
    /etc/uirdcfg.d/               Directory for uird config files
    /usr/share/uird/configs/     Target directory for configs during build
    /boot/                       Default output directory

NOTES:
    - Requires mkuird command to be installed
    - Only kernels with corresponding /lib/modules/KERNEL_VERSION are processed
    - Old uird files are automatically removed when kernel is removed
    - Comments in config files are only allowed at the end of the file

EOF
}

# Настройки по умолчанию
: ${UIRD_OUTPUT_DIR:="/boot"}
: ${UIRD_CONFIG_DIR:="/etc/uirdcfg.d"}
: ${UIRD_CONFIGS_DIR:="/usr/share/uird/configs/uird_configs"}
: ${UIRD_DEFAULT_CONFIG:="legacy"}
: ${UIRD_DEFAULT_OPTS:="-l"}

# Обработка аргументов командной строки
case "$1" in
    -h|--help)
        show_help
        exit 0
        ;;
    -v|--version)
        rpm -q uird-regen 2>/dev/null || echo "version: unknown" 
        exit 0
        ;;
    "")
        ;;
    *)
        echo "Unknown option: $1"
        echo "Try 'uird-regen --help' for more information."
        exit 1
        ;;
esac

[ "$(id -un)" == 'root' ] || { echo "you must be root" ; exit 2 ; }

# Функция для поиска всех ядер
find_kernels() {
    machine=`uname -m`
    case "x$machine" in
        xi?86 | xx86_64)
            list=
            for i in /boot/vmlinuz-* /boot/kernel-* ; do
                if [ -f "$i" ] && ! echo "$i" | grep -q "\.old$" ; then
                    list="$list $i"
                fi
            done ;;
        *) 
            list=
            for i in /boot/vmlinuz-* /boot/vmlinux-* /boot/kernel-* ; do
                if [ -f "$i" ] && ! echo "$i" | grep -q "\.old$" ; then
                    list="$list $i"
                fi
            done ;;
    esac
    
    echo "$list"
}

# Функция для получения версии ядра
get_kernel_version() {
    local kernel="$1"
    local basename=$(basename "$kernel")
    echo "$basename" | sed -e "s,^[^0-9]*-,,g" | sed -e 's/\.old$//'
}

# Функция для выполнения команды с отображением
run() {
    echo "  $@"
    "$@"
}

# Функция для вывода ошибки в рамке
print_error() {
    local message="$1"
    local len=$(echo "$message" | wc -c)
    local border=$(printf '%*s' "$len" '' | tr ' ' '=')
    
    echo
    echo "╔${border}╗"
    echo "║${message} ║"
    echo "╚${border}╝"
    echo
}

# Функция для получения опций сборки из конфига
get_mkuird_opts() {
    local config="$1"
    local opts=""
    if [ -f "$config" ]; then
        opts=$(grep -E "^#MKUIRD_CMDLINE[=:][[:space:]]*" "$config" | head -1 | sed 's/^#MKUIRD_CMDLINE[=:][[:space:]]*//')
    fi
    echo "$opts"
}

echo "# Rebuilding uird for all kernels"
echo "# Configuration:"
echo "#  Output dir: $UIRD_OUTPUT_DIR"
echo "#  Config dir: $UIRD_CONFIG_DIR"
echo "#  Configs target dir: $UIRD_CONFIGS_DIR"
echo "#  Default config: $UIRD_DEFAULT_CONFIG"
echo "#  Default opts: $UIRD_DEFAULT_OPTS"
echo

# Проверяем наличие mkuird
if ! command -v mkuird > /dev/null 2>&1; then
    print_error "ERROR: mkuird command not found"
    exit 1
fi

# Проверяем выходную директорию
if [ ! -d "$UIRD_OUTPUT_DIR" ]; then
    print_error "ERROR: Output directory $UIRD_OUTPUT_DIR does not exist"
    exit 1
fi

# Проверяем директорию с конфигами
if [ ! -d "$UIRD_CONFIG_DIR" ]; then
    echo "WARNING: Config directory $UIRD_CONFIG_DIR does not exist, using default config only"
    CONFIGS="$UIRD_DEFAULT_CONFIG"
else
    # Получаем список конфигов (файлы в каталоге, исключая .opts файлы)
    CONFIGS=$(find "$UIRD_CONFIG_DIR" -maxdepth 1 -type f ! -name "*.opts" -exec basename {} \; | sort)
    if [ -z "$CONFIGS" ]; then
        echo "WARNING: No configs found in $UIRD_CONFIG_DIR, using default config only"
        CONFIGS="$UIRD_DEFAULT_CONFIG"
    fi
fi

# Проверяем целевую директорию для конфигов
if [ ! -d "$UIRD_CONFIGS_DIR" ]; then
    echo "WARNING: Target config directory $UIRD_CONFIGS_DIR does not exist, creating..."
    mkdir -p "$UIRD_CONFIGS_DIR"
fi

# Получаем список ядер
kernel_list=$(find_kernels)

if [ -z "$kernel_list" ]; then
    print_error "ERROR: No kernels found"
    exit 1
fi

# Счетчики
total_success=0
total_fail=0
total_skip=0

# Для каждого конфига
for config_name in $CONFIGS; do
    echo
    echo "# Config: $config_name"
    
    config_file="${UIRD_CONFIG_DIR}/${config_name}"
    
    # Проверяем существует ли файл конфига (если это не дефолтный конфиг)
    if [ "$config_name" != "$UIRD_DEFAULT_CONFIG" ] && [ ! -f "$config_file" ]; then
        echo "  WARNING: Config file $config_file not found, skipping"
        total_skip=$((total_skip + 1))
        continue
    fi
    
    # Получаем опции сборки для этого конфига
    if [ -f "$config_file" ]; then
        opts=$(get_mkuird_opts "$config_file")
    else
        opts=""
    fi
    
    # Если опции не найдены - используем дефолтные
    if [ -z "$opts" ]; then
        opts="$UIRD_DEFAULT_OPTS"
    fi
    
    echo "  Build options: $opts"
    
    # Копируем конфиг в целевую директорию
    if [ -f "$config_file" ]; then
        cp "$config_file" "$UIRD_CONFIGS_DIR/"
        echo "  Config copied to $UIRD_CONFIGS_DIR/"
    fi
    
    success_count=0
    fail_count=0
    skip_count=0
    
    # Для каждого ядра собираем uird с этим конфигом
    for kernel in $kernel_list; do
        version=$(get_kernel_version "$kernel")
        uird_name="uird.${config_name}-${version}.cpio.xz"
        uird_path="${UIRD_OUTPUT_DIR}/${uird_name}"
        tmp_uird="/tmp/${uird_name}"
        
        # Проверяем существует ли директория /lib/modules
        if [ ! -d "/lib/modules/${version}" ]; then
            echo "  WARNING: /lib/modules/${version} not found, skipping kernel $version"
            skip_count=$((skip_count + 1))
            continue
        fi
        
        # Проверяем существует ли vmlinuz
        if [ ! -f "/boot/vmlinuz-${version}" ]; then
            echo "  WARNING: /boot/vmlinuz-${version} not found, skipping kernel $version"
            skip_count=$((skip_count + 1))
            continue
        fi
        
        # Собираем uird в /tmp
        echo "  Building uird for kernel $version with config $config_name..."
        pushd /tmp > /dev/null
        cmd="mkuird -k $version -n uird.${config_name}-${version}.cpio.xz $opts $config_name"
        if run $cmd; then
            # Сборка успешна - проверяем наличие файла
            if [ -f "$tmp_uird" ]; then
                # Перемещаем в /boot
                mv "$tmp_uird" "$uird_path"
                size=$(du -h "$uird_path" | cut -f1)
                echo -e "  [OK] uird created: $uird_path ($size)\n"
                success_count=$((success_count + 1))
            else
                echo "  [FAIL] uird file not found after build: $tmp_uird"
                print_error "ERROR: uird file not created for kernel $version with config $config_name"
                fail_count=$((fail_count + 1))
            fi
        else
            # Сборка не удалась
            echo "  [FAIL] Failed to build uird for kernel $version with config $config_name"
            
            # Удаляем временный файл если есть
            if [ -f "$tmp_uird" ]; then
                rm -f "$tmp_uird"
            fi
            
            # Выводим ошибку в рамке
            print_error "ERROR: Failed to build uird for kernel $version with config $config_name"
            
            fail_count=$((fail_count + 1))
        fi
        popd > /dev/null
    done
    
    # Удаляем конфиг из целевой директории
    if [ -f "$UIRD_CONFIGS_DIR/${config_name}" ]; then
        rm -f "$UIRD_CONFIGS_DIR/${config_name}"
        echo "  Config removed from $UIRD_CONFIGS_DIR/"
    fi
    
    # Итоговый отчет по конфигу
    echo
    echo "  Summary for config $config_name:"
    echo "    Successful builds: $success_count"
    echo "    Failed builds: $fail_count"
    echo "    Skipped (missing modules/vmlinuz): $skip_count"
    
    total_success=$((total_success + success_count))
    total_fail=$((total_fail + fail_count))
    total_skip=$((total_skip + skip_count))
    
    # Если были ошибки для этого конфига - выводим предупреждение
    if [ $fail_count -gt 0 ]; then
        print_error "WARNING: $fail_count kernel(s) failed to build uird for config $config_name"
    fi
done

# Общий итоговый отчет
echo
echo "# Overall Summary:"
echo "#  Total successful builds: $total_success"
echo "#  Total failed builds: $total_fail"
echo "#  Total skipped: $total_skip"
echo

if [ $total_fail -gt 0 ]; then
    print_error "WARNING: $total_fail kernel(s) failed to build uird"
    echo "Please check the logs above and rebuild manually if needed."
    echo
fi

echo "# Done"

# Выходим с кодом ошибки если были неудачные сборки
if [ $total_fail -gt 0 ]; then
    exit 1
fi

exit 0
