#!/bin/sh
CMD="help addon create extension grub2 sdboot"
BIN="uki-tool"
VERBOSE=0
COMMON_ESP_PATH="/boot/efi"
COMMON_EFI_PATH="EFI/Linux"
common_is_efi_system() {
    [ -d "${COMMON_ESP_PATH}" ]
}
common_get_dev_name() {
    df -h "$1" | tail -1 | cut -d ' ' -f1
}
common_get_dev_uuid() {
    blkid "$1" | sed -e 's|.* UUID="\(.*\)|\1|' | sed 's|" .*||'
}
common_get_dev_avail() {
    df --block-size="1M" --output="avail" "$1" | tail -1 | tr -d ' '
}
common_install_uki_in_efi() {
    uki_path="$1"
    image=$(basename "$1")
    efi_d="$2"
    esp_efi_d="${COMMON_ESP_PATH}/${efi_d}"
    # uki_path="/usr/lib/modules/${kerver}/${image}"
    [ ! -d "${esp_efi_d}" ] && mkdir -p "${esp_efi_d}"
    if [ ! -f "${esp_efi_d}/${image}" ]; then
        if [ ! -f "${uki_path}" ]; then
            echo_error "Unable to find the UKI file: ${uki_path}"
            exit 2
        else
            echo_debug "Install UKI in ${esp_efi_d}/${image}"
            efi_dev="$(common_get_dev_name ${COMMON_ESP_PATH})"
            uki_size="$(du -m0 "$uki_path" | cut -f 1)"
            efi_avail="$(common_get_dev_avail "$efi_dev")"
            echo_debug "${efi_avail}M available on efi partition"
            echo_debug "Size of uki file: ${uki_size}M"
            if [ "$uki_size" -gt "$efi_avail" ]; then
                echo_error "No space left on efi partition to install uki"
                echo_error "Need ${uki_size}M, Available: ${efi_avail}M"
                exit 2
            fi
            if ! cp "$uki_path" "${esp_efi_d}/${image}"; then
                echo_error "Error when installing ${esp_efi_d}/${image}"
                exit 2
            fi
        fi
    fi
}

ADDON_STUB_PATH="/usr/lib/systemd/boot/efi/addonx64.efi.stub"
ADDON_EXTENSION="addon.efi"
_addon_usage() {
    usage_str="USAGE: $BIN addon [OPTIONS]
OPTIONS:
  -c|--cmdline:         To put in .cmdline section
  -s|--snapshot:        Dedicated the addon to add rootflag for the N° snapshot
  -n|--name:            Name of the addon
  -o|--output:          Output dir where to generate the addon.
                            [Default: $PWD]
  help:                 Print this helper
 
INFO:
    Generate an addon with a custom .cmdline section using the systemd tool
'ukify'
 
EXAMPLE:
    $BIN addon -c 'rootflags=subvol=@/.snapshots/92/snapshot' \
-o /boot/efi/EFI/Linux/uki-0.1.0.efi.extra.d -n snapshot92.addon.efi
    $BIN addon -s 92 -o /boot/efi/EFI/Linux/uki-0.1.0.efi.extra.d \
-n snapshot92.addon.efi"
    printf "%s\n" "$usage_str"
}
_addon_generate() {
    err=0
    if [ $# -lt 3 ]; then
        echo_error "Missing arguments"
        err=1
    elif [ ! -d "$1" ]; then
        echo_error "No dir at $1"
        err=1
    fi
    if [ $err -ne 1 ]; then
        if $UKIFY build \
            --stub="$ADDON_STUB_PATH" \
            --cmdline="$2" \
            --output="$1/$3"; then
            echo_info "Addon generated: $1/$3"
        else
            echo_error "$UKIFY failed to create the addon at $1/$3"
            err=1
        fi
    fi
    return $err
}
addon_helper() {
    _addon_usage
}
addon_tools_needed() {
    printf "%s" "$UKIFY"
}
addon_exec() {
    printf "Execute command addon\n"
    # Get arguments
    args=$(getopt -a -n extension -o n:c:s:o:\
        --long name:,cmdline:,snapshot:,output: -- "$@")
    eval set --"$args"
    while :
    do
        case "$1" in
            -n | --name)        name="$2"           ; shift 2 ;;
            -c | --cmdline)     cmdline="$2"        ; shift 2 ;;
            -s | --snapshot)    snapshot="$2"        ; shift 2 ;;
            -o | --output)      output="$2"         ; shift 2 ;;
            --)                 shift               ; break   ;;
            *) echo_warning "Unexpected option: $1"; _addon_usage   ;;
        esac
    done
    if [ ! ${name+x} ]; then
        echo_error "Missing Name"
        return 1
    fi
    if [ "$(echo "$name" | rev | cut -d '.' -f1-2 | rev)" \
      != "$ADDON_EXTENSION" ]; then
        name="${name}.$ADDON_EXTENSION"
    fi
    if [ ${snapshot+x} ]; then
        cmdline="rootflags=subvol=@/.snapshots/$snapshot/snapshot $cmdline"
    fi
    if [ ! ${cmdline+x} ]; then
        echo_error "Missing cmdline"
        return 1
    fi
    if [ ! ${output+x} ]; then
        output="$PWD"
    fi
    # Generate Addon
    _addon_generate "$output" "$cmdline" "$name"
}

CREATE_DEFAULT_UKI_NAME="uki"
CREATE_DEFAULT_CMDLINE="rw rhgb"
_create_usage() {
    usage_str="USAGE: $BIN create [OPTIONS]
OPTIONS:
  -k|--kerver:          Kernel Version 
                            [default: $KER_VER]
  -i|--initrd:          Path to the initrd
                            [default: /usr/share/initrd/initrd-dracut-generic-\
kerver.unsigned]
  -n|--name:            Name to the UKI to generate 
                            [Default: $CREATE_DEFAULT_UKI_NAME]
  -c|--cmdline:         kernel cmdline 
                            [Default: $CREATE_DEFAULT_CMDLINE]
  -o|--output:          Output dir where to generate the UKI.
                            [Default: $PWD]
  help:                 Print this helper
 
INFO:
    Generate PCR keys and use them to create an UKI using the systemd tool
'ukify'
 
EXAMPLE:
    $BIN create -k $KER_VER -n uki-0.1.0.efi -o /usr/lib/modules/$KER_VER/"
    printf "%s\n" "$usage_str"
}
_create_generate_pcr_keys() {
    err=0
    if [ "$1" = "" ]; then
        echo_error "Missing argument"
        err=1
    elif [ ! -d "$1" ]; then
        echo_error "No dir at $1"
        err=1
    fi
    output_dir="$1"
    if [ $err -eq 0 ]; then
        if $UKIFY genkey \
        --pcr-private-key="$output_dir"/pcr-initrd.key.pem \
        --pcr-public-key="$output_dir"/pcr-initrd.pub.pem \
        --phases='enter-initrd' \
        --pcr-private-key="$output_dir"/pcr-system.key.pem \
        --pcr-public-key="$output_dir"/pcr-system.pub.pem \
        --phases='enter-initrd:leave-initrd
            enter-initrd:leave-initrd:sysinit
            enter-initrd:leave-initrd:sysinit:ready'; then
            echo_info "PCR key generated"
        else
            echo_error "Failed to generate PCR key"
            err=1
        fi
    fi
    return $err
}
_create_generate_uki() {
    err=0
    if [ $# -lt 6 ]; then
        echo_error "Missing arguments"
        err=1
    elif [ ! -d "$1" ]; then
        echo_error "No dir at $1"
        err=1
    elif [ ! -d "$2" ]; then
        echo_error "No dir at $2"
        err=1
    fi
    if [ $err -ne 1 ]; then
        if $UKIFY build \
            --initrd="$6" \
            --linux="/usr/lib/modules/$3/$KER_NAME" \
            --uname="$3" \
            --pcr-private-key="$2/pcr-initrd.key.pem" \
            --pcr-public-key="$2/pcr-initrd.pub.pem" \
            --phases='enter-initrd' \
            --pcr-private-key="$2/pcr-system.key.pem" \
            --pcr-public-key="$2/pcr-system.pub.pem" \
            --pcrpkey="$2/pcr-system.pub.pem" \
            --phases='enter-initrd:leave-initrd
                enter-initrd:leave-initrd:sysinit
                enter-initrd:leave-initrd:sysinit:ready' \
            --pcr-banks=sha256 \
            --cmdline="$5" \
            --output="$1/$4"; then
            echo_info "UKI generated: $1/$4"
        else
            echo_error "$UKIFY failed to create the UKI at $1/$4"
            err=1
        fi
    fi
    return $err
}
create_helper() {
    _create_usage
}
create_tools_needed() {
    printf "%s" "$CREATE_UKIFY_BIN"
}
create_exec() {
    printf "Execute command create\n"
    # Get arguments
    args=$(getopt -a -n extension -o k:i:n:c:o:\
        --long kerver:,initrd:,name:,cmdline:,output: -- "$@")
    eval set --"$args"
    while :
    do
        case "$1" in
            -k | --kerver)      kerver="$2"         ; shift 2 ;;
            -i | --initrd)      initrd_path="$2"    ; shift 2 ;;
            -n | --name)        name="$2"           ; shift 2 ;;
            -c | --cmdline)     cmdline="$2"        ; shift 2 ;;
            -o | --output)      output="$2"         ; shift 2 ;;
            --)                 shift               ; break   ;;
            *) echo_warning "Unexpected option: $1"; _create_usage   ;;
        esac
    done
    if [ ! ${kerver+x} ]; then
        kerver="$KER_VER"
    fi
    if [ ! ${initrd_path+x} ]; then
        initrd_path="/usr/share/initrd/initrd-dracut-generic-$kerver"
    fi
    if [ ! ${name+x} ]; then
        name="$CREATE_DEFAULT_UKI_NAME"
    fi
    if [ ! ${cmdline+x} ]; then
        cmdline="$CREATE_DEFAULT_CMDLINE"
    fi
    if [ ! ${output+x} ]; then
        output="$PWD"
    fi
    # Generate UKI
    tmp_dir="$(mktemp -d)"
    if _create_generate_pcr_keys "$tmp_dir"; then
        _create_generate_uki "$output" "$tmp_dir" "$kerver" "$name" "$cmdline" \
"$initrd_path"
    fi
    # Clean
    rm -rf "$tmp_dir"
}

EXTENSION_TYPE_DEFAULT="raw"
EXTENSION_FORMAT_DEFAULT="squashfs"
EXTENSION_PART_UUID="0fc63daf-8483-4772-8e79-3d69d8477de4"
EXTENSION_PART_LABEL="Linux filesystem"
EXTENSION_LIST_DEPS=""
EXTENSION_DEPS=1
EXTENSION_LSINITRD=""
EXTENSION_INITRD_RELEASE=""
_extension_deps_packages() {
    pkg="$1"
    found=0
    rpm_cmd=$(rpm -qR "$pkg")
    while read -r require; do
        dep_pkg=$(rpm -q --whatprovides "$require")
        if [ $? -eq 1 ]; then
            continue
        fi
        for elem in $EXTENSION_LIST_DEPS; do
            if [ "$elem" = "$dep_pkg" ]; then
                found=1
                break
            fi
        done
        if [ $found -eq 0 ]; then
            EXTENSION_LIST_DEPS="$EXTENSION_LIST_DEPS $dep_pkg"
            _extension_deps_packages "$dep_pkg"
        else
            found=0
        fi
    done <<rpm_cmd_input
$rpm_cmd
rpm_cmd_input
    # while IFS= read -r require; do
    #     dep_pkg=$(rpm -q --whatprovides "$require")
    #     if [ $? -eq 1 ]; then
    #         continue
    #     fi
    #     for elem in "${EXTENSION_LIST_DEPS[@]}"; do
    #         if [ "$elem" == "$dep_pkg" ]; then
    #             found=1
    #             break
    #         fi
    #     done
    #     if [[ $found -eq 0 ]]; then
    #         EXTENSION_LIST_DEPS+=("$dep_pkg")
    #         _extension_deps_packages "$dep_pkg"
    #     else
    #         found=0
    #     fi
    # done < <(rpm -qR "$pkg")
}
_extension_usage() {
    usage_str="USAGE: $BIN extension [OPTIONS]
OPTIONS:
  -n|--name:        Extension's name
  -p|--packages:    List of packages to install into the extension
  -f|--format:      Extension format (squashfs by default)
  -t|--type:        Type of the extension (dir, raw)
  -u|--uki:         Path to the referenced UKI (dedicated exetnsion)
  -a|--arch:        Specify an architecture
                        See https://uapi-group.org/specifications/specs/extension_image
                        For the list of potential value.
  --no-deps:        Build without any dependences
  help:             Print this helper
 
INFO:
    - Generate an extension for an UKI 'name-ext.format'.
    - If 'uki' parameter is set, the extension will be optimized by not taking
    into account the files already installed into it. The extension will be
    lighter. These extensions, depending on a specific uki, will have to be
    installed in '/usr/lib/modules/KERV_VER/UKI_NAME.extrad.d/'.
    - Without 'uki', it will generate gloabal extension that could extends all
    UKI. It will need to be installed into '/usr/lib/modules/uki.extra.d/'.
 
EXAMPLE:
    $BIN extension -n \"debug\" -p \"strace,gdb\" -t \"raw\""
    printf "%s\n" "$usage_str"
}
_extension_size_partition() {
    fs_size=$1
    inode_size=$(head -n 7 /etc/mke2fs.conf\
        | grep "inode_size" | awk -F' = ' '{print $2}')
    inode_ratio=$(head -n 7 /etc/mke2fs.conf\
        | grep "inode_ratio" | awk -F' = ' '{print $2}')
    echo "$((fs_size*1000/(1000-(inode_size*1000)/(inode_ratio*1000)-50)))"
}
_extension_create() {
    if [ $# -lt 6 ]; then
        echo_debug "Missing arguments"
        return 1
    fi
    name="${1}"
    img_name="${name}-ext.raw"
    pkgs="$2"
    format="$3"
    type="$4"
    uki="$5"
    arch="$6"
    echo_info "Create the extension '$img_name' with '$pkgs' in format $format\
 at type $type"
    for pkg in $(printf "%s" "$pkgs" | sed 's/,/ /g'); do
        if [ "$pkg_list" = "" ]; then
            pkg_list="$pkg";
        else
            pkg_list="$pkg_list $pkg"
        fi
    done
    # Check if all packages requires are installed
    for pkg in $pkg_list; do
        if ! rpm -q "$pkg" > /dev/null 2>&1; then
            echo_error "'$pkg' is not installed"
            exit 1
        fi
    done
    # Get dependencies of packages
    if [ "$EXTENSION_LIST_DEPS" = "" ]; then
        EXTENSION_LIST_DEPS="$pkg_list";
    else
        EXTENSION_LIST_DEPS="$EXTENSION_LIST_DEPS $pkg_list"
    fi
    if [ $EXTENSION_DEPS -eq 1 ]; then 
        echo_info "Get all dependencies to install..."
        for pkg in $pkg_list; do
            _extension_deps_packages "$pkg"
        done
    fi
    
    # Get list of files to install
    tmp_dir=$(mktemp -d)
    for pkg in $EXTENSION_LIST_DEPS; do
        for file in $(rpm -ql "$pkg" | sed 's/\n/ /g'); do
            if [ -f "$file" ]; then
                echo "$EXTENSION_LSINITRD" | grep -Fxq "$file" && continue
                cp --parents "$file" "$tmp_dir"
            fi
        done
    done
    ext_name="${img_name%.*}"
    ext_dir=$tmp_dir/usr/lib/extension-release.d
    ext_file=$ext_dir/extension-release.$ext_name
    mkdir -p "$ext_dir"
    touch "$ext_file"
    id_arg=$(echo "$EXTENSION_INITRD_RELEASE" | grep "^ID=\"*\"")
    ver_id_arg=$(echo "$EXTENSION_INITRD_RELEASE" | grep "^VERSION_ID=\"*\"")
    {
        echo "SYSEXT_LEVEL=2"
        echo "$id_arg"
        echo "$ver_id_arg"
        echo "SYSEXT_ID=$name"
        # scope=[initrd,system,portable]
        echo "SYSEXT_SCOPE=initrd"
        echo "ARCHITECTURE=$arch"
    } > "$ext_file"
    # Create an empty disk raw image extensions
    sized=$(du -s --block-size=1M "$tmp_dir" | awk '{print $1}')
    sized=$((sized+1)) # Add at minimum 1M
    part_sized=$(_extension_size_partition ${sized})
    echo_info "Create an image of sized ${part_sized}M..."
    if [ "$format" = "$EXTENSION_FORMAT_DEFAULT" ]; then
        mksquashfs \
            "$tmp_dir" \
            "./$img_name" \
            -quiet
    else
        dd if=/dev/zero of="./$img_name" bs=1M count="$part_sized" \
            > /dev/null 2>&1
        mkfs."$format" \
            -U "$EXTENSION_PART_UUID" \
            -L "$EXTENSION_PART_LABEL" \
            -d "$tmp_dir" \
            -q \
            "./$img_name"
    fi
    # Clean
    [ "$tmp_dir" ] && rm -r "$tmp_dir"
    echo_info "extension image created at ./$img_name"
    return 0
}
extension_helper() {
    _extension_usage
}
extension_tools_needed() {
    printf "objcopy lsinitrd mksquashfs mktemp"
}
extension_exec() {
    [ $# -lt 2 ] \
        && echo_error "Missing arguments"\
        && _extension_usage && exit 2
    args=$(getopt -a -n extension -o n:p:f:t:u:a:\
        --long name:,packages:,format:,type:,uki:,arch:,no-deps -- "$@")
    eval set --"$args"
    while :
    do
        case "$1" in
            -n | --name)        name="$2"           ; shift 2 ;;
            -p | --packages)    packages="$2"       ; shift 2 ;;
            -t | --type)        type="$2"           ; shift 2 ;;
            -f | --format)      format="$2"         ; shift 2 ;;
            -u | --uki)         uki="$2"            ; shift 2 ;;
            -a | --arch)        arch="$2"           ; shift 2 ;;
            --no-deps)          EXTENSION_DEPS=0    ; shift 1 ;;
            --)                 shift               ; break   ;;
            *) echo_warning "Unexpected option: $1"; _extension_usage   ;;
        esac
    done
    if [ ! ${packages+x} ]; then
        echo_error "Missing packages to install in the extension"
        _extension_usage
        exit 2
    fi
    if [ ${uki+x} ]; then
        echo_info "Check the uki $uki and extract the initrd..."
        objcopy --dump-section .initrd=initrd-tmp "$uki"
        EXTENSION_LSINITRD=$(lsinitrd ./initrd-tmp \
                | grep "usr/" \
                | tr -s ' ' \
                | cut -d ' ' -f9 \
                | sed 's|^|/|')
        EXTENSION_INITRD_RELEASE=$(lsinitrd -f usr/lib/initrd-release ./initrd-tmp)
        rm ./initrd-tmp
    fi
    if [ ! ${arch+x} ]; then
        arch=$(printf "%s" "$(uname -m)" | sed 's/_/-/g')
    fi
    [ ! ${type+x} ] && type="$EXTENSION_TYPE_DEFAULT"
    if [ ! ${format+x} ]; then 
        format="$EXTENSION_FORMAT_DEFAULT"
    elif [ ! -f "/usr/sbin/mkfs.$format" ]; then
        echo_error "No mkfs.$format found, use another format"
        exit 1
    fi
    _extension_create "$name" "$packages" "$format" "$type" "$uki" "$arch"
    exit $?
}

GRUB2_CMD_ADD=1
GRUB2_CMD_REMOVE=2
GRUB2_CONFIG_INITRD="43_ukit_initrd"
GRUB2_CONFIG_UKI="44_ukit_uki"
GRUB2_CONFIG_FILE="/boot/grub2/grub.cfg"
GRUB2_DEFAULT_FILE="/etc/default/grub"
GRUB2_TRANSACTIONAL_UPDATE=0
_grub2_grub_cfg() {
    if [ "$GRUB2_TRANSACTIONAL_UPDATE" -eq 1 ]; then
        transactional-update grub.cfg
    else
        grub2-mkconfig -o "$GRUB2_CONFIG_FILE"
    fi
}
_grub2_remove_menuentry() {
    grub_config_path="$1"
    file_path="$2"
    file=$(basename "$file_path")
    if [ -f "$grub_config_path" ]; then
        if grep -q "$file_path" "$grub_config_path"; then
            echo_info "Removing menuentry for $file_path ..."
            # Get the block to remove:
            start_line=$(grep -n "menuentry.*$file'" "$grub_config_path"\
                | cut -d':' -f1 | head -n 1)
            start_line=$((start_line-1))
            end_line=0
            while IFS= read -r line; do
                end_line=$((end_line+1))
                if expr "$line" : "^EOF" > /dev/null; then
                    [ "$end_line" -gt "$start_line" ] && break
                fi
            done < "$grub_config_path"
            echo_debug "sed -i \"${start_line},${end_line}d\" $grub_config_path"
            sed -i "${start_line},${end_line}d" "$grub_config_path"
            _grub2_grub_cfg
        else
            echo_warning "There isn't a menu entry for $file_path"
            return
        fi
    else
        echo_warning "Grub config file not already created."
    fi
}
_grub2_usage() {
    usage_str="USAGE: $BIN grub2 [OPTIONS]
OPTIONS:
  --add|--remove:       Add/Remove grub2 entry (mandatory)
  -k|--kerver:          Kernel Version [Default: $KER_VER]
  -i|--initrd:          Path to the initrd
  -u|--uki:             Path to the UKI
  -e|--efi:             efi directory [Default $COMMON_EFI_PATH]
  -D|--default:         set entry as default (only with --add)
  help:                 Print this helper
 
INFO:
    Create or remove an entry to the grub2 menu. If initrd argurment is \
provided, uki shouldn't, and vice versa.
    If the initrd provided isn't in the boot partition, it will copy it in \
/boot
    If the uki provided isn't in the the efi partition, it will copy it in \
$COMMON_EFI_PATH
    When remove is asked, --uki should point to the installed uki (in /boot \
partition )
 
EXAMPLE:
    $BIN grub2 --add -k 6.3.4-1-default -u /usr/lib/modules/kerver/uki.efi
    $BIN grub2 --remove -u /boot/efi/EFI/Linux/uki.efi"
    printf "%s\n" "$usage_str"
}
_grub2_initrd() {
    cmd=$1
    kerver="$2"
    initrd_path="$3"
    root_dev="$(_grub2_get_dev_name /)"
    root_uuid="$(_grub2_get_dev_uuid "$root_dev")"
    grub_config_path="/etc/grub.d/$GRUB2_CONFIG_INITRD"
    eof="EOF"
    initrd_file=$(basename "$initrd_path")
    echo_debug "UUID root fs: $root_uuid"
    if [ "$cmd" -eq "$GRUB2_CMD_ADD" ]; then
        if [ ! -f "${initrd_path}" ]; then
            echo_error "Initrd not found at ${initrd_path}."
            exit 2
        fi
        if [ ! -f "/boot/$initrd_file" ]; then
            echo_info "$initrd_file isn't in boot partition, copy it to \
/boot/$initrd_file"
            if ! cp "$initrd_path" "/boot/$initrd_file"; then
                echo_error "Error when adding the initrd to the boot partition"
                exit 2
            fi
        fi
        initrd_path="/boot/$initrd_file"
        if [ -f "$grub_config_path" ]; then
            if grep -q "$initrd_path" "$grub_config_path"; then
                echo_warning "There is already a menu entry for $initrd_path"
                return
            fi
        else
            cat > "$grub_config_path" <<EOF
#!/bin/sh
set -e
EOF
            chmod +x "$grub_config_path"
        fi
        echo_info "Add initrd menuentry for $initrd_path ..."
        cat >> "$grub_config_path" <<EOF
cat << $eof
menuentry 'Linux ${kerver} and initrd ${initrd_file}' {
    load_video
    set gfxpayload=keep
    insmod gzio
    insmod part_gpt
    search --no-floppy --fs-uuid --set=root ${root_uuid}
    echo "Loading Linux ${kerver} ..."
    linux /boot/vmlinuz-${kerver} root=UUID=${root_uuid}
    echo "Loading ${initrd_path}..."
    initrd ${initrd_path}
}
$eof
EOF
        _grub2_grub_cfg
    elif [ "$cmd" -eq "$GRUB2_CMD_REMOVE" ]; then
        _grub2_remove_menuentry "$grub_config_path" "$initrd_path"
    fi
}
_grub2_uki() {
    cmd=$1
    uki_path="$2"
    efi_d="$3"
    default="$4"
    efi_dev="$(common_get_dev_name "${COMMON_ESP_PATH}")"
    efi_uuid="$(common_get_dev_uuid "$efi_dev")"
    grub_config_path="/etc/grub.d/$GRUB2_CONFIG_UKI"
    uki_file=$(basename "$uki_path")
    efi_uki_path="/${efi_d}/$uki_file"
    eof="EOF"
    echo_debug "UUID boot partition: $efi_uuid"
    if [ "$cmd" -eq "$GRUB2_CMD_ADD" ]; then
        if [ ! -f "${uki_path}" ]; then
            echo_error "Unified Kernel Image not found at ${uki_path}."
            exit 2
        fi
        common_install_uki_in_efi "${uki_path}" "${efi_d}"
        if [ -f "$grub_config_path" ]; then
            if grep -q "${efi_uki_path}" "${grub_config_path}"; then
                echo_warning "There is already a menu entry for ${efi_uki_path}"
                echo_warning "Remove it before adding it"
                return
            fi
        else
            cat > $grub_config_path <<EOF
#!/bin/sh
set -e
EOF
            chmod +x $grub_config_path
        fi
        echo_info "Add UKI menuentry for $efi_uki_path..."
        uki_name_id=$(basename "${efi_uki_path}" .efi)
        cat >> $grub_config_path <<EOF
cat << $eof
menuentry 'Unified Kernel Image ${uki_file}' --id ${uki_name_id} {
    insmod part_gpt
    insmod btrfs
    insmod chain
    search --no-floppy --fs-uuid --set=root ${efi_uuid}
    echo "Loading unified kernel image ${uki_file} ..."
    chainloader ${efi_uki_path}
}
$eof
EOF
        if [ "${default}" = "1" ]; then
            sed -i \
                "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=${uki_name_id}|" \
                "${GRUB2_DEFAULT_FILE}"
        fi
        _grub2_grub_cfg
    elif [ "$cmd" -eq "$GRUB2_CMD_REMOVE" ]; then
        _grub2_remove_menuentry "${grub_config_path}" "${efi_uki_path}"
    fi
}
grub2_tools_needed() {
    printf "grub2-mkconfig"
}
grub2_helper() {
    _grub2_usage
}
grub2_exec() {
    [ $# -lt 2 ] \
        && echo_error "Missing arguments"\
        && _extension_usage && exit 2
    args=$(getopt -a -n extension -o k:i:u:e:D\
        --long add,remove,kerver:,initrd:,uki:,efi:,default -- "$@")
    eval set --"$args"
    while :
    do
        case "$1" in
            --add)              cmd_add=1         ; shift 1 ;;
            --remove)           cmd_remove=1      ; shift 1 ;;
            -k | --kerver)      kerver="$2"       ; shift 2 ;;
            -i | --initrd)      initrd_path="$2"  ; shift 2 ;;
            -u | --uki)         uki_path="$2"     ; shift 2 ;;
            -e | --efi)         efi_d="$2"        ; shift 2 ;;
            -D | --default)     default=1           ; shift 1 ;;
            --)                 shift             ; break   ;;
            *) echo_warning "Unexpected option: $1"; _grub2_usage   ;;
        esac
    done
    # Check transactional update system
    if command -v transactional-update; then
        GRUB2_TRANSACTIONAL_UPDATE=1
    fi
    # Check the command
    if [ ! ${cmd_add+x} ] && [ ! ${cmd_remove+x} ]; then
        echo_error "Need \"add\" or \"remove\" command"
        _grub2_usage
        exit 2
    elif [ ${cmd_add+x} ] && [ ${cmd_remove+x} ]; then
        echo_error "Please choose between add or remove a menue entry. Not\
both!"
        _grub2_usage
        exit 2
    elif [ ${cmd_add+x} ]; then
        cmd=$GRUB2_CMD_ADD
    else
        cmd=$GRUB2_CMD_REMOVE
    fi
    if [ ! ${efi_d+x} ]; then
        efi_d="$COMMON_EFI_PATH"
    else
        efi_d="$(echo "${efi_d}" | sed "s|^/||")"
    fi
    # Check the mode
    if [ ${initrd_path+x} ] && [ ${uki_path+x} ]; then
        echo_error "Please choose between initrd or uki arguments. Not both!"
        _grub2_usage
        exit 2
    elif [ ! ${initrd_path+x} ] && [ ! ${uki_path+x} ]; then
        echo_error "Missing initrd path OR uki path to add to the menu entry"
        _grub2_usage
        exit 2
    elif [ ${uki_path+x} ]; then
        # Check if system is EFI
        if ! common_is_efi_system; then
            echo_error "System doesn't contains ESP partition"
            exit 2
        fi
        _grub2_uki ${cmd} "${uki_path}" "${efi_d}" "${default}"
    else
        # Check the kernel version
        if [ ! ${kerver+x} ]; then
            kerver="$KER_VER"
        fi
        if [ ! -f "/boot/vmlinuz-${kerver}" ]; then
            echo_error "Unable to find the Kernel file: /boot/vmlinuz-${kerver}\
, wrong kernel version ?"
            exit 2
           fi
        _grub2_initrd $cmd "$kerver" "$initrd_path"
    fi
}
SDBOOT_LOADER_CONF="${COMMON_ESP_PATH}/loader/loader.conf"
SDBOOT_LOADER_ENTRIES_D="${COMMON_ESP_PATH}/loader/entries"
SDBOOT_CONF_DEAFULT_KEY="default"
_sdboot_install_bootctl() {
    if [ "$(bootctl is-installed)" = "no" ]; then
        bootctl install --esp-path="${COMMON_ESP_PATH}"
    fi
}
_sdboot_set_default() {
    conf_name="$1"
    if [ ! -f "${SDBOOT_LOADER_ENTRIES_D}/${conf_name}" ]; then
        echo_error "Failed to set default, No confif file \
${SDBOOT_LOADER_ENTRIES_D}/${conf_name}."
        exit 2
    fi
    if grep -q "^${SDBOOT_CONF_DEAFULT_KEY}" "${SDBOOT_LOADER_CONF}"; then
        sed -i \
"s|^${SDBOOT_CONF_DEAFULT_KEY}.*|${SDBOOT_CONF_DEAFULT_KEY} ${conf_name}|" \
"${SDBOOT_LOADER_CONF}"
    else
        echo "${SDBOOT_CONF_DEAFULT_KEY} ${conf_name}" \
            >> "${SDBOOT_LOADER_CONF}"
    fi
}
_sdboot_add_entry() {
    uki="$1"
    efi_d="$2"
    arch="$3"
    kerver="$4"
    default="$5"
    common_install_uki_in_efi "$uki" "$efi_d"
    case "$arch" in
        aarch64) arch=aa64 ;;
        x86_64)  arch=x64 ;;
        # TODO: add more verification about possibles architecture
    esac
    uki_file=$(basename "${uki}")
    uki_name=$(basename "${uki}" .efi)
    uki_ver=$(echo "$uki_name" | sed -e 's|^uki-||')
    cat > "${SDBOOT_LOADER_ENTRIES_D}/${uki_name}_k${kerver}.conf" <<EOF
title         Unified Kernel Image ${uki_name}
sort-key      UKI
version       ${uki_ver}_k${kerver}
efi           ${efi_d}/${uki_file}
architecture  ${arch}
EOF
    echo_debug "UKI sdboot entry has been added."
    if [ "${default}" = "1" ]; then
        _sdboot_set_default "${uki_name}_k${kerver}.conf"
    fi
}
_sdboot_remove_entry() {
    uki="$1"
    kerver="$2"
    uki_file=$(basename "${uki}")
    uki_name=$(basename "${uki}" .efi)
    conf_file="${SDBOOT_LOADER_ENTRIES_D}/${uki_name}_k${kerver}"
    if [ -f "${conf_file}" ]; then
        rm "${SDBOOT_LOADER_ENTRIES_D}/${uki_name}_k${kerver}.conf"
        echo_debug "UKI sdboot entry has been removed..."
    else
        echo_debug "No ${conf_file} to remove."
    fi
}
_sdboot_usage() {
    usage_str="USAGE: $BIN sdboot [OPTIONS]
OPTIONS:
  --add:                Add entry
  --remove:             Remove entry
  -k|--kerver:          Kernel Version [Default: $KER_VER]
  -u|--uki:             Path to the UKI name (should be end by .efi)
  -a|--arch:            Architecture to use [Default 'uname -m']
  -e|--efi:             efi directory [Default $COMMON_EFI_PATH]
  -D|--default:         set entry as default (only with --add)
  help:                 Print this helper
 
INFO:
    Create or remove a sdboot entry for the specified UKI.
    If uki from path (--uki) point to a binary outside the boot partition, it
    will try to install it into ${COMMON_ESP_PATH}/$efi_d.
    If uki just mention an uki name file, it will search the binary from
    '/usr/lib/modules/\$ker_ver/\$image'.
 
EXAMPLE:
  $BIN sdboot --add -k $(uname -r) -efi /EFI/opensuse -u uki-0.1.0.efi
  $BIN sdboot --remove -k $(uname -r) -u uki-0.1.0.efi
"
    printf "%s\n" "$usage_str"
}
sdboot_tools_needed() {
    printf "bootctl"
}
sdboot_helper() {
    _sdboot_usage
}
sdboot_exec() {
    [ $# -lt 2 ] \
        && echo_error "Missing arguments"\
        && _extension_usage && exit 2
    args=$(getopt -a -n extension -o u:,k:,a:,e:,D\
        --long add,remove,kerver:,uki:,arch:,efi:,default -- "$@")
    eval set --"$args"
    while :
    do
        case "$1" in
            --add)              cmd_add=1           ; shift 1 ;;
            --remove)           cmd_remove=1        ; shift 1 ;;
            -k | --kerver)      kerver="$2"         ; shift 2 ;;
            -u | --uki)         uki="$2"            ; shift 2 ;;
            -a | --arch)        arch="$2"           ; shift 2 ;;
            -e | --efi)         efi_d="$2"          ; shift 2 ;;
            -D | --default)     default=1           ; shift 1 ;;
            --)                 shift               ; break   ;;
            *) echo_warning "Unexpected option: $1"; _sdboot_usage   ;;
        esac
    done
    if [ ! ${kerver+x} ]; then
        kerver="$KER_VER"
    fi
    if [ ! ${uki+x} ]; then
        echo_error "Missing uki name (--uki)"
        exit 2
    fi
    if [ ! ${arch+x} ]; then
        arch=$(uname -m)
    fi
    if [ ! ${efi_d+x} ]; then
        efi_d="$COMMON_EFI_PATH"
    else
        efi_d="$(echo "${efi_d}" | sed "s|^/||")"
    fi
    # Check if system is EFI
    if ! common_is_efi_system; then
        echo_error "System doesn't contains ESP partition"
        exit 2
    fi
    # Check the command
    if [ ! ${cmd_add+x} ] && [ ! ${cmd_remove+x} ]; then
        echo_error "Need \"add\" or \"remove\" command"
        _sdboot_usage
        exit 2
    elif [ ${cmd_add+x} ] && [ ${cmd_remove+x} ]; then
        echo_error "Please choose between add or remove a menue entry. Not\
both!"
        _sdboot_usage
        exit 2
    elif [ ${cmd_add+x} ]; then
        _sdboot_install_bootctl
        if [ ! -f "${uki}" ]; then
            uki_file=$(basename "${uki}")
            uki="/usr/lib/modules/${kerver}/${uki_file}"
        fi
        _sdboot_add_entry "${uki}" "${efi_d}" "${arch}" "${kerver}" "${default}"
        # err=$(sdbootutil \
        #     --arch="$arch" \
        #     --image="$uki" \
        #     add-uki "$kerver" 2>&1)
        # ret=$?
    else
        _sdboot_remove_entry "${uki}" "${kerver}"
        # err=$(sdbootutil \
        #     --arch="$arch" \
        #     --image="$uki" \
        #     remove-uki "$kerver" \
        #     2>&1)
        # ret=$?
    fi
    # if [ $ret -ne 0 ]; then
    #     echo_error "sdbootutil : '$err'"
    #     exit 2
    # fi
}

TOOLS_NEEDED=""
KER_NAME=""
KER_VER=""
UKIFY="/usr/lib/systemd/ukify"
echo_warning() {
    color="\033[0;33m"
    color_light="\033[1;33m"
    color_none="\033[0m"
    printf "%b[WARNING]%b %s%b\n" "${color}" "${color_light}" "$1" \
"${color_none}"
}
echo_error() {
    color="\033[0;31m"
    color_light="\033[1;31m"
    color_none="\033[0m"
    printf "%b[ERROR]%b %s%b\n" "${color}" "${color_light}" "$1" "${color_none}"
}
echo_info() {
    color="\033[0;32m"
    color_light="\033[1;32m"
    color_none="\033[0m"
    printf "%b[INFO]%b %s%b\n" "${color}" "${color_light}" "$1" "${color_none}"
}
echo_debug() {
    [ "$VERBOSE" -eq 0 ] && return
    color="\033[0;34m"
    color_light="\033[1;34m"
    color_none="\033[0m"
    printf "%b[DEBUG]%b %s%b\n" "${color}" "${color_light}" "$1" "${color_none}"
}
usage() {
    usage_str="USAGE: $BIN [help] [verbose] COMMAND [help | COMMAND OPTION]
OPTIONS:
  - help:               Print this helper
  - verbose:            Print debug information to the output
  - COMMAND help:       Print the helper of the command
  - COMMAND [OPTION]:   Execute the command with additional options.
 
COMMANDS:"
    for cmd in $CMD; do
        usage_str=$(printf "%s\n  - %s" "$usage_str" "$cmd")
    done
    printf "%s\n" "$usage_str"
}
check_tools_needed() {
    for dep in $TOOLS_NEEDED; do
        if ! command -v "$dep" > /dev/null 2>&1; then
            if [ ${missing_deps+x} ]; then
                missing_deps="$missing_deps $dep"
            else
                missing_deps="$dep"
            fi
        fi
    done
    if [ ${missing_deps+x} ]; then
        echo_error "Some tools are missing on your system: $missing_deps"
        exit 1
    fi
}
if [ $# -lt 1 ]; then
    echo_error "Missing command"
    usage & exit 2
fi
cmd_in="$1"
if [ "$cmd_in" = "help" ]\
    || [ "$cmd_in" = "--help" ]\
    || [ "$cmd_in" = "-h" ]; then
        usage
        exit 0
elif [ "$cmd_in" = "verbose" ]\
    || [ "$cmd_in" = "-v" ]; then
    VERBOSE=1
    cmd_in="$2"
    shift 1
fi
case $(uname -m) in
    "x86_64"|"i386"|"i486"|"i586"|"i686")       KER_NAME="vmlinuz";;
    "ppc"|"ppc64"|"ppcle")                      KER_NAME="vmlinux";;
    "s390"|"s390x")                             KER_NAME="image";;
    "arm")                                      KER_NAME="zImage";;
    "aarch64"|"riscv64")                        KER_NAME="Image";;
    *) echo_error "Unknow Arch" && return 1;;
esac
KER_VER="$(uname -r)"
found=0;
for cmd in $CMD; do
    if [ "$cmd" = "$cmd_in" ]; then
        found=1
        if [ "$2" = "help" ]\
            || [ "$2" = "--help" ]\
            || [ "$2" = "-h" ]; then
                "${cmd}_helper"
        else
            # Get dependencies of the command and check them
            TOOLS_NEEDED="$("${cmd}_tools_needed")"
            check_tools_needed
            # Exec the command
            "${cmd}_exec" "$@"
        fi
    fi
done
if [ $found -eq 0 ]; then
    echo_error "Unknown command \"$cmd_in\""
    usage & exit 1
fi
exit 0
