#!/bin/sh
CMD="help addon create extension grub2 sdboot"
BIN="uki-tool"
VERBOSE=0
QUIET=0
BIN_VERSION="1.5.0"
COMMON_ESP_PATH="/boot/efi"
COMMON_EFI_PATH="EFI/Linux"
COMMON_OSREL_PATH="/etc/os-release"
COMMON_KERNEL_MODULESDIR="/usr/lib/modules"
COMMON_INITRD_DIR="/usr/share/initrd"
COMMON_INITRD_BASENAME="static-initrd-generic"
COMMON_CMDLINE_DEFAULT="splash=silent mitigations=auto quiet"
common_is_efi_system() {
    COMMON_ESP_PATH="$(findmnt -t vfat -o TARGET \
                        | grep -oE '/boot|/boot/efi|/efi')"
    [ -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_get_machine_id() {
    if [ -f /etc/machine-id ]; then
        read -r machine_id < /etc/machine-id
    else
        echo_error "Couldn't determine machine-id"
    fi
}
common_extract_from_osrel () {
   grep "$2=" "${COMMON_OSREL_PATH}"\
        | cut -d "=" -f2\
        | tr -d '"'
}
common_get_pretty_name() {
    common_extract_from_osrel "PRETTY_NAME"
}
common_verify_efi_size() {
    if [ ! -f "$1" ]; then
        echo_warning "Verifying an unknow file ($1)"
        return 1
    fi
    efi_dev="$(common_get_dev_name "${COMMON_ESP_PATH}")"
    file_size="$(du -m0 "$1" | cut -f 1)"
    efi_avail="$(common_get_dev_avail "$efi_dev")"
    echo_debug "${efi_avail}M available on efi partition"
    echo_debug "Size of $1: ${file_size}M"
    if [ "$file_size" -gt "$efi_avail" ]; then
        echo_error "No space left on efi partition to install $1"
        echo_error "Need ${file_size}M, Available: ${efi_avail}M"
        return 1
    fi
    return 0
}
common_format_uki_name() {
    uki_path="$1"
    image=$(basename "$1")
    majorver="$(echo "${2}" | cut -d '-' -f1)"
    if echo "${image}" | grep -q "${majorver}"; then
        echo "${image}"
    else
        echo "$(basename "${image}" .efi)_k${majorver}.efi"
    fi
}
common_install_file() {
    src_path="$1"
    dst_path="$2"
    if [ ! -f "${src_path}" ]; then
        echo_error "Unknow source ${src_path}"
        return 1
    fi
    if [ -e "${dst_path}" ]; then
        if cmp -s "${src_path}" "${dst_path}"; then
            echo_debug "${dst_path} unchanged"
            return 0
        fi
    fi
    install -p -m 0644 "${src_path}" "${dst_path}" || return "$?"
    chown root:root "${dst_path}" 2>/dev/null || :
    echo_info "${dst_path} installed"
}
common_is_uki_installed_in_efi() {
    uki_path="$1"
    efi_d="$2"
    kerver="$3"
    image=$(common_format_uki_name "${uki_path}" "${kerver}")
    esp_efi_d="${COMMON_ESP_PATH}/${efi_d}"
    [ -f "${esp_efi_d}/${image}" ] && return 0
    return 1
}
common_install_uki_in_efi() {
    common_is_uki_installed_in_efi "$1" "$2" "$3" && return 0
    uki_path="$1"
    efi_d="$2"
    kerver="$3"
    image=$(common_format_uki_name "$1" "${kerver}")
    extra_d="${image}.extra.d"
    esp_efi_d="${COMMON_ESP_PATH}/${efi_d}"
    [ ! -d "${esp_efi_d}" ] && mkdir -p "${esp_efi_d}"
    if [ ! -f "${uki_path}" ]; then
        echo_error "Unable to find the UKI file: ${uki_path}"
        exit 2
    else
        echo_debug "Install UKI ${esp_efi_d}/${image}"
        common_verify_efi_size "$uki_path" || exit 2
        common_install_file "$uki_path" "${esp_efi_d}/${image}" || {
            echo_error "Error when installing ${esp_efi_d}/${image}"
            exit 2
        }
        echo_debug "Installing UKI extra dir ${esp_efi_d}/${extra_d}"
        [ ! -d "${esp_efi_d}/${extra_d}" ] \
            && mkdir -p "${esp_efi_d}/${extra_d}"
    fi
}
common_remove_uki_from_efi() {
    uki_path="$1"
    if echo "${uki_path}" | grep -q "^${COMMON_ESP_PATH}"; then
        [ -f "${uki_path}" ] && rm "${uki_path}"
        [ -d "${uki_path}.extra.d" ] && rm -r "${uki_path}.extra.d"
    else
        echo_debug "${uki_path} isn't a path from ${COMMON_ESP_PATH}"
    fi
}
common_uki_get_section_data() {
    [ -f "$1" ] || return 1
    objdump -h "$1" | grep -wq ".$2" || return 1
    objdump -s -j ".$2" "$1" \
        | sed -n '/[0-9a-f]/s/^[^ ]* *\([^ ]* *\)\{5\}//p' \
        | tr -d '\n' \
        | tr -d '\0'
}
common_uki_extract_osrel() {
    data=$(common_uki_get_section_data "$1" "osrel")
    [ "$data" = "" ] ||\
        printf %s "$data" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p"
}
common_uki_get_pretty_name() {
    common_uki_extract_osrel "$1" "PRETTY_NAME"
}
common_uki_get_osrel_id() {
    common_uki_extract_osrel "$1" "ID"
}
common_uki_get_osrel_version() {
    common_uki_extract_osrel "$1" "VERSION_ID"
}
common_uki_get_uname() {
    common_uki_get_section_data "$1" "uname"
}
common_install_initrd_in_efi() {
    initrd_path="$1"
    ker_ver="$2"
    common_get_machine_id
    [ ! ${machine_id+x} ] && exit 2
    esp_machine_d="${COMMON_ESP_PATH}/${machine_id}"
    [ ! -d "${esp_machine_d}" ] && mkdir -p "${esp_machine_d}"
    esp_uname_d="${esp_machine_d}/${ker_ver}"
    [ ! -d "${esp_uname_d}" ] && mkdir -p "${esp_uname_d}"
    linux_file="$(find "${esp_uname_d}" -name "linux*")"
    if [ "${linux_file}" = "" ]; then
        # Copy the kernel
        linux_file="${COMMON_KERNEL_MODULESDIR}/${ker_ver}/${KER_NAME}"
        common_verify_efi_size "${linux_file}" || exit 2
        common_install_file \
            "${linux_file}" \
            "${esp_uname_d}/linux" || {
            echo_error "Error when installing ${esp_uname_d}/linux"
            exit 2
        }
    fi
    common_verify_efi_size "${initrd_path}" || exit 2
    common_install_file "${initrd_path}" "${esp_uname_d}/static-initrd" || {
        echo_error "Error when installing ${esp_uname_d}/static-initrd"
        exit 2
    }
}
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_GENKEYS="n"
_create_usage() {
    usage_str="USAGE: $BIN create [OPTIONS]
OPTIONS:
  -k|--kerver:          Kernel Version
                            [default: $KER_VER]
  -i|--initrd:          Path to the initrd
                            [default: ${COMMON_INITRD_DIR}/\
${COMMON_INITRD_BASENAME}-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]
  --pcrkeys :           Generate and use PCR keys for the generated UKI
                            Keys will be stored in ./<ukiname>.keys/
                            (see ukify genkey for more info)
  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 ${COMMON_KERNEL_MODULESDIR}\
/$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 test -f "$output_dir"/pcr-initrd.key.pem \
       -o -f "$output_dir"/pcr-initrd.pub.pem \
       -o -f "$output_dir"/pcr-system.key.pem \
       -o -f "$output_dir"/pcr-system.pub.pem; then
        echo_info "PCR keys has already been created. Cancel."
    elif [ $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() {
    output_dir="$1"
    pcrkeys_dir="$2"
    kerver="$3"
    name="$4"
    cmdline="$5"
    initrd_p="$6"
    err=0
    if [ $# -lt 6 ]; then
        echo_error "Missing arguments"
        err=1
    elif [ ! -d "${output_dir}" ]; then
        echo_error "No dir at ${output_dir}"
        err=1
    fi
    if [ $err -ne 1 ]; then
        set -- --initrd="${initrd_p}"
        set -- "$@" --linux="${COMMON_KERNEL_MODULESDIR}/${kerver}/${KER_NAME}"
        set -- "$@" --uname="${kerver}"
        set -- "$@" --output="${output_dir}/${name}"
        set -- "$@" --cmdline="${cmdline}"
        if test "${CREATE_GENKEYS}" = "y" -a -d "${pcrkeys_dir}"; then
            set -- "$@" --pcr-private-key="${pcrkeys_dir}/pcr-initrd.key.pem"
            set -- "$@" --pcr-public-key="${pcrkeys_dir}/pcr-initrd.pub.pem"
            set -- "$@" --phases='enter-initrd'
            set -- "$@" --pcr-private-key="${pcrkeys_dir}/pcr-system.key.pem"
            set -- "$@" --pcr-public-key="${pcrkeys_dir}/pcr-system.pub.pem"
            set -- "$@" --pcrpkey="${pcrkeys_dir}/pcr-system.pub.pem"
            set -- "$@" --phases='enter-initrd:leave-initrd
                enter-initrd:leave-initrd:sysinit
                enter-initrd:leave-initrd:sysinit:ready'
            set -- "$@" --pcr-banks=sha256
        fi
        if $UKIFY build "$@"; then
            echo_info "UKI generated: ${output_dir}/${name}"
        else
            echo_error "$UKIFY failed to create the UKI at ${output_dir}/\
${name}"
            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:,pcrkeys -- "$@")
    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 ;;
            --pcrkeys)          CREATE_GENKEYS="y"  ; shift 1 ;;     
            --)                 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="${COMMON_INITRD_DIR}/${COMMON_INITRD_BASENAME}-$kerver"
        if [ ! -f "${initrd_path}" ]; then
            initrd_path="${initrd_path}.unsigned"
        fi
    fi
    if [ ! -f "${initrd_path}" ]; then
        echo_error "Unable to find an initrd at ${initrd_path}"
        return 1
    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
    keys_dir=""
    if [ "${CREATE_GENKEYS}" = "y" ]; then
        keys_dir="./${name}.keys"
        mkdir -p "$keys_dir"
        if _create_generate_pcr_keys "$keys_dir"; then
            echo_info "PCR keys generated in: $keys_dir/"
        else
            echo_error "Failed to generate PCR keys"
            return 1
        fi
    fi
    # Generate UKI
    _create_generate_uki "$output" "$keys_dir" "$kerver" "$name" "$cmdline" \
"$initrd_path"
}

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 '${COMMON_KERNEL_MODULESDIR}/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
    '${COMMON_KERNEL_MODULESDIR}/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 -d ' ' \
                | 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_ARG_ALL="all"
GRUB2_CONFIG_INITRD="43_ukit_initrd"
GRUB2_CONFIG_DIR="/etc/grub.d"
GRUB2_CONFIG_UKI="${GRUB2_CONFIG_DIR}/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_uki_from_efi() {
    efi_uki_path="$2/$(basename "$1")"
    if ! grep -qR "${efi_uki_path}" "${GRUB2_CONFIG_DIR}"/*; then
        common_remove_uki_from_efi "${COMMON_ESP_PATH}${efi_uki_path}"
        echo_debug "UKI ${COMMON_ESP_PATH}${efi_uki_path} has been removed."
    fi
}
_grub2_remove_menuentry() {
    grub_config_path="$1"
    entry_id="$2"
    if [ -f "$grub_config_path" ]; then
        if grep -q "menuentry.*--id $entry_id " "$grub_config_path"; then
            echo_info "Removing menuentry for $entry_id ..."
            # Get the block to remove:
            start_line=$(grep -n "menuentry.*--id $entry_id "\
                "$grub_config_path" | cut -d':' -f1 | head -n 1)
            if [ "${start_line}" = "" ]; then
                echo_warning \
                    "Failed to find the menuentry id ${entry_id}"
                echo_warning \
                    "Abort ..."
                return
            fi
            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
                if expr "$line" : ".*chainloader.*" > /dev/null; then
                    uki_path=$( echo "${line}" | awk '{print $2}')
                    uki_path="${COMMON_ESP_PATH}${uki_path}"
                fi
            done < "$grub_config_path"
            sed -i "${start_line},${end_line}d" "$grub_config_path"
            # Removing the referred UKI
            common_remove_uki_from_efi "${uki_path}"
            echo_debug "UKI ${uki_path} has been removed."
            _grub2_grub_cfg
        else
            echo_warning "There isn't a menu entry with ID $entry_id"
            echo_warning "Can only removed entries created by this tool."
            return
        fi
    else
        echo_warning "Grub config file not already created."
    fi
}
_grub2_set_default() {
    if [ ! "${1+x}" ]; then
        echo_error "Missing argument"
        return
    fi
    sed -i \
        "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=${1}|" \
        "${GRUB2_DEFAULT_FILE}"
}
_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
  --all-ukis:           Add or remove all ukis installed on the system
  -e|--efi:             efi directory [Default $COMMON_EFI_PATH]
  -D|--default:         set entry as default (only with --add)
  -t|--title:           Title of the entry
  -c|--cmdline:         cmdline arguments (works only with initrd)
  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 ${COMMON_KERNEL_MODULESDIR}/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"
    default="$4"
    title="$5"
    cmdline="$6"
    root_dev="$(common_get_dev_name /)"
    root_uuid="$(common_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 [ "${title}" = "" ]; then
            title="$(common_get_pretty_name) (Static Initrd)"
        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 ..."
        entry_id=$(basename "${initrd_path}")
        cat >> "$grub_config_path" <<EOF
cat << $eof
menuentry '${title}' --id ${entry_id} {
    load_video
    set gfxpayload=keep
    search --no-floppy --fs-uuid --set=root ${root_uuid}
    echo "Loading Linux ${kerver} ..."
    linux /boot/vmlinuz-${kerver} root=UUID=${root_uuid} ${cmdline}
    echo "Loading ${initrd_path}..."
    initrd ${initrd_path}
}
$eof
EOF
        if [ "${default}" = "1" ]; then
            _grub2_set_default "${entry_id}"
        fi
        _grub2_grub_cfg
    elif [ "$cmd" -eq "$GRUB2_CMD_REMOVE" ]; then
        entry_id=$(basename "${initrd_path}")
        _grub2_remove_menuentry "$grub_config_path" "$entry_id"
    fi
}
_grub2_uki() {
    cmd=$1
    uki_path="$2"
    efi_d="$3"
    default="$4"
    title="$5"
    kerver="$6"
    if [ ! -f "${uki_path}" ]; then
        echo_error "Unified Kernel Image not found at ${uki_path}."
        exit 2
    fi
    uki_file=$(common_format_uki_name "${uki_path}" "${kerver}")
    uki_name_id=$(basename "${uki_file}" .efi)
    eof="EOF"
    echo_debug "UUID boot partition: $efi_uuid"
    if [ "$cmd" -eq "$GRUB2_CMD_ADD" ]; then
        efi_dev="$(common_get_dev_name "${COMMON_ESP_PATH}")"
        efi_uuid="$(common_get_dev_uuid "$efi_dev")"
        efi_uki_path="/${efi_d}/$uki_file"
        uki_ver=$(basename "${efi_uki_path}" .efi | sed -e 's|^uki-||')
        if [ "${title}" = "" ]; then
            title="$(common_uki_get_pretty_name "${uki_path}")"
        fi
        common_install_uki_in_efi "${uki_path}" "${efi_d}" "${kerver}"
        if [ -f "$GRUB2_CONFIG_UKI" ]; then
            if grep -q "${efi_uki_path}" "$GRUB2_CONFIG_UKI"; then
                echo_warning "There's already a menu entry for ${efi_uki_path}"
                echo_warning "Remove it before adding it"
                return
            fi
        else
            cat > $GRUB2_CONFIG_UKI <<EOF
#!/bin/sh
set -e
EOF
            chmod +x $GRUB2_CONFIG_UKI
        fi
        echo_info "Add UKI menuentry for ${efi_uki_path}..."
        cat >> $GRUB2_CONFIG_UKI <<EOF
cat << $eof
menuentry '${title} (uki-${uki_ver})' --id ${uki_name_id} {
    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
            _grub2_set_default "${uki_name_id}"
        fi
        _grub2_grub_cfg
    elif [ "$cmd" -eq "$GRUB2_CMD_REMOVE" ]; then
        _grub2_remove_menuentry "$GRUB2_CONFIG_UKI" "${uki_name_id}"
        _grub2_remove_uki_from_efi "${uki_file}" "${efi_d}"
    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,t:,c:\
        --long add,remove,kerver:,initrd:,uki:,efi:,default,title:,cmdline:\
        --long all-ukis\
        -- "$@")
    eval set --"$args"
    default=0
    cmdline="${COMMON_CMDLINE_DEFAULT}"
    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 ;;
            --all-ukis)         uki_path="${GRUB2_ARG_ALL}"  ; shift 1 ;;
            -e | --efi)         efi_d="$2"                   ; shift 2 ;;
            -D | --default)     default=1                    ; shift 1 ;;
            -t | --title)       title="$2"                   ; shift 2 ;;
            -c | --cmdline)     cmdline="$2"                 ; shift 2 ;;
            --)                 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
    if [ ! ${kerver+x} ]; then
        kerver="$KER_VER"
    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
        if [ "${uki_path}" = "${GRUB2_ARG_ALL}" ]; then
            for kerdir in "${COMMON_KERNEL_MODULESDIR}"/*; do
                if [ -d "$kerdir" ]; then
                    find "$kerdir" -type f -name "uki*.efi" 2>/dev/null \
                        | while read -r file; do
                        tmp_kver=$(basename "$kerdir")
                        _grub2_uki "${cmd}" \
                            "${file}" \
                            "${efi_d}" \
                            "${default}" \
                            ""\
                            "${tmp_kver}"
                    done
                fi
            done
        else
            if [ ! -f "${uki_path}" ]; then
                uki_file=$(basename "${uki_path}")
                uki_path="${COMMON_KERNEL_MODULESDIR}/${kerver}/${uki_file}"
            else
                uki_uname=$(common_uki_get_uname "${uki_path}")
                [ "$uki_uname" = "" ] || kerver="${uki_uname}"
            fi
            _grub2_uki "${cmd}" \
                "${uki_path}" \
                "${efi_d}" \
                "${default}" \
                "${title}"\
                "${kerver}"
        fi
    else
        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" "${default}" "${title}" "${cmdline}"
    fi
}

SDBOOT_CMD_ADD=1
SDBOOT_CMD_REMOVE=2
SDBOOT_ARG_ALL="all"
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 config 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}"
        bootctl set-default "${conf_name}"
    else
        echo "${SDBOOT_CONF_DEAFULT_KEY} ${conf_name}" \
            >> "${SDBOOT_LOADER_CONF}"
        bootctl set-default "${conf_name}"
    fi
}
_sdboot_get_initrd_from_conf() {
    conf_file="$1"
    [ ! -f "${conf_file}" ] && return
    tmp=$(grep -x "^initrd.*" "${conf_file}" | awk '{print $2}')
    echo "${COMMON_ESP_PATH}/${tmp}"
}
_sdboot_get_linux_from_conf() {
    conf_file="$1"
    [ ! -f "${conf_file}" ] && return
    tmp=$(grep -x "^linux.*" "${conf_file}" | awk '{print $2}')
    echo "${COMMON_ESP_PATH}/${tmp}"
}
_sdboot_get_uki_from_conf() {
    conf_file="$1"
    [ ! -f "${conf_file}" ] && return
    tmp=$(grep -x "^efi.*" "${conf_file}" | awk '{print $2}')
    echo "${COMMON_ESP_PATH}/${tmp}"
}
_sdboot_uki_add_entry() {
    uki="$1"
    efi_d="$2"
    arch="$3"
    kerver="$4"
    default="$5"
    title="$6"
    common_install_uki_in_efi "${uki}" "${efi_d}" "${kerver}"
    image=$(common_format_uki_name "${uki}" "${kerver}")
    conf_name=$(basename "${image}" ".efi")
    uki_ver=$(basename "${image}" .efi | sed -e 's|^uki-||')
    id=$(common_uki_get_osrel_id "${uki}")
    [ "$id" = "" ] && id="unified"
    cat > "${SDBOOT_LOADER_ENTRIES_D}/${conf_name}.conf" <<EOF
title         ${title} (uki-${uki_ver})
sort-key      ${id}
version       ${uki_ver}
efi           ${efi_d}/${image}
architecture  ${arch}
EOF
    echo_debug "UKI sdboot entry has been added."
    if [ "${default}" = "1" ]; then
        _sdboot_set_default "${image}.conf"
    fi
}
_sdboot_initrd_add_entry() {
    initrd_path="$1"
    efi_d="$2"
    kerver="$3"
    default="$4"
    title="$5"
    cmdline="$6"
    root_dev="$(common_get_dev_name /)"
    root_uuid="$(common_get_dev_uuid "$root_dev")"
    initrd_file=$(basename "${initrd_path}")
    common_install_initrd_in_efi "${initrd_path}" "${kerver}"
    common_get_machine_id
    [ ! ${machine_id+x} ] && exit 2
    esp_uname_d="${COMMON_ESP_PATH}/${machine_id}/${kerver}"
    linux_file=$(find "${esp_uname_d}" -name "linux*")
    linux_file=$(basename "${linux_file}")
    cat > "${SDBOOT_LOADER_ENTRIES_D}/static-${machine_id}-${kerver}.conf" <<EOF
title         ${title}
sort-key      static-initrd
version       ${kerver}
machine-id    ${machine_id}
options       root=UUID=${root_uuid} ${cmdline} \
security=apparmor systemd.machine_id=${machine_id}
linux         /${machine_id}/${kerver}/${linux_file}
initrd        /${machine_id}/${kerver}/static-initrd
EOF
    echo_debug "initrd sdboot entry has been added."
    if [ "${default}" = "1" ]; then
        _sdboot_set_default "static-${machine_id}-${kerver}.conf"
    fi
}
_sdboot_remove_uki_from_efi() {
    if ! grep -qR "$1" "${SDBOOT_LOADER_ENTRIES_D}"/*; then
        common_remove_uki_from_efi "$1"
        echo_debug "UKI $1 has been removed."
    fi
}
_sdboot_uki_remove_entry() {
    uki="$1"
    # config file should be named by the uki filename and the uname
    uki_uname=$(common_uki_get_uname "${uki}")
    image=$(common_format_uki_name "${uki}" "${uki_uname}")
    conf_file="${SDBOOT_LOADER_ENTRIES_D}/${image}.conf"
    # Old way to create conf was wihtout removing the "efi" extension.
    if [ ! -f "${conf_file}" ]; then
        conf_name=$(basename "${image}" ".efi")
        conf_file="${SDBOOT_LOADER_ENTRIES_D}/${conf_name}.conf"
    fi
    if [ -f "${conf_file}" ]; then
        uki_path=$(_sdboot_get_uki_from_conf "${conf_file}")
        rm "${conf_file}"
        echo_debug "UKI sdboot entry has been removed..."
        _sdboot_remove_uki_from_efi "${uki_path}"
    else
        echo_debug "No ${conf_file} found to remove."
    fi
}
_sdboot_initrd_remove_entry() {
    kerver="$1"
    common_get_machine_id
    [ ! ${machine_id+x} ] && exit 2
    conf_file="${SDBOOT_LOADER_ENTRIES_D}/static-${machine_id}-${kerver}.conf"
    if [ -f "${conf_file}" ]; then
        # remove the installed static initrd
        initrd_path=$(_sdboot_get_initrd_from_conf "${conf_file}")
        [ -f "${initrd_path}" ] && rm "${initrd_path}"
        echo_info "${initrd_path} has been removed"
        linux_path=$(_sdboot_get_linux_from_conf "${conf_file}")
        # Remove the installed linux if no initrd follows him. Means that the
        # linux has been installed in the same time as the static-initrd.
        linux_dir=$(dirname "$linux_path")
        num=$(find "$linux_dir" -maxdepth 1 -type f | wc -l)
        if [ "$num" = "1" ]; then
            [ -f "${linux_path}" ] && rm "${linux_path}"
            echo_info "${linux_path} has been removed"
            [ -d "${linux_dir}" ] && rm -r "${linux_dir}"
        fi
        # Remove the conf file
        rm "${conf_file}"
        echo_info "${conf_file} has been removed."
    else
        echo_debug "No ${conf_file} to remove."
    fi
}
_sdboot_usage() {
    usage_str="USAGE: $BIN sdboot [OPTIONS]
OPTIONS:
  --add | --remove:     Add / Remove sdboot entry (mandatory)
  -k|--kerver:          Kernel Version [Default: $KER_VER]
  -i|--initrd:          Path to the initrd
  -u|--uki:             Path to the UKI name (should be end by .efi)
  --all-ukis:           Add or remove all ukis installed on the system
  -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)
  -t|--title:           Title of the entry
  -c|--cmdline:         cmdline arguments (works only with initrd)
  help:                 Print this helper
 
INFO:
  Create or remove a sdboot entry for the specified UKI or initrd.
  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 \
'${COMMON_KERNEL_MODULESDIR}/\$ker_ver/\$image'.
  If the initrd provided isn't in the boot partition, it will copy it in \
/boot.
 
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_uki() {
    cmd=$1
    uki="$2"
    efi_d="$3"
    default="$4"
    title="$5"
    kerver="$6"
    if [ "$cmd" = "$SDBOOT_CMD_ADD" ]; then
        if [ "${title}" = "" ]; then
            title="$(common_uki_get_pretty_name "${uki}")"
            title="${title} $(common_uki_get_osrel_version "${uki}")"
        fi
        _sdboot_uki_add_entry \
            "${uki}" "${efi_d}" "${arch}" "${kerver}" "${default}" "${title}"
    else
        common_is_uki_installed_in_efi "${uki}" "${efi_d}" "${kerver}" && {
            _sdboot_uki_remove_entry "${uki}"
        }
    fi
}
_sdboot_initrd() {
    if [ "$cmd" = "$SDBOOT_CMD_ADD" ]; then
        if [ "${title}" = "" ]; then
            title="$(common_get_pretty_name) (Static Initrd)"
        fi
        _sdboot_initrd_add_entry \
            "${initrd}" "${efi_d}" "${kerver}" "${default}" "${title}" \
            "${cmdline}"
    else
        _sdboot_initrd_remove_entry "${kerver}"
    fi
}
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:,i:,k:,a:,e:,D,t:,c: \
        --long add,remove,kerver:,initrd:,uki:,arch:,efi:,default,title: \
        --long all-ukis \
        --long cmdline: \
        -- "$@")
    eval set --"$args"
    # Init some variables
    kerver="$KER_VER"
    arch=$(uname -m)
    default=0
    cmdline="${COMMON_CMDLINE_DEFAULT}"
    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="$2"             ; shift 2 ;;
            -u | --uki)         uki="$2"                ; shift 2 ;;
            --all-ukis)         uki="${SDBOOT_ARG_ALL}" ; shift 1 ;;
            -a | --arch)        arch="$2"               ; shift 2 ;;
            -e | --efi)         efi_d="$2"              ; shift 2 ;;
            -D | --default)     default=1               ; shift 1 ;;
            -t | --title)       title="$2"              ; shift 2 ;;
            -c | --cmdline)     cmdline="$2"            ; shift 2 ;;
            --)                 shift                   ; break   ;;
            *) echo_warning "Unexpected option: $1" ; _sdboot_usage   ;;
        esac
    done
    case "$arch" in
        aarch64) arch=aa64 ;;
        x86_64)  arch=x64 ;;
        # TODO: add more verification about possibles architecture
    esac
    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
        cmd=$SDBOOT_CMD_ADD
    else
        cmd=$SDBOOT_CMD_REMOVE
    fi
    # Check the mode
    if [ ${initrd+x} ] && [ ${uki+x} ]; then
        echo_error "Please choose between initrd or uki arguments. Not both!"
        _sdboot_usage
        exit 2
    elif [ ! ${initrd+x} ] && [ ! ${uki+x} ]; then
        echo_error "Missing initrd path OR uki path to add to the menu entry"
        _sdboot_usage
        exit 2
    elif [ ${uki+x} ]; then
        if [ "${uki}" = "${SDBOOT_ARG_ALL}" ]; then
            for kerdir in "${COMMON_KERNEL_MODULESDIR}"/*; do
                if [ -d "$kerdir" ]; then
                    find "$kerdir" -type f -name "uki*.efi" 2>/dev/null \
                        | while read -r file; do
                        _sdboot_uki "${cmd}" \
                            "${file}" \
                            "${efi_d}" \
                            "${default}" \
                            ""\
                            "$(basename "$kerdir")"
                    done
                fi
            done
        else
            if [ ! -f "${uki}" ]; then
                uki_file=$(basename "${uki}")
                uki="${COMMON_KERNEL_MODULESDIR}/${kerver}/${uki_file}"
            else
                uki_uname=$(common_uki_get_uname "${uki}")
                [ "$uki_uname" = "" ] || kerver="${uki_uname}"
            fi
            _sdboot_uki "${cmd}" \
                "${uki}" \
                "${efi_d}" \
                "${default}" \
                "${title}"\
                "${kerver}"
        fi
    elif [ ${initrd+x} ]; then
        if [ ! -f "${initrd}" ]; then
            initrd_file=$(basename "${initrd}")
            initrd="${COMMON_INITRD_DIR}/${initrd_file}"
        fi
        _sdboot_initrd
    fi
}

TOOLS_NEEDED=""
KER_NAME=""
KER_VER=""
UKIFY="/usr/lib/systemd/ukify"
echo_warning() {
    [ "$QUIET" -eq 1 ] && return
    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() {
    [ "$QUIET" -eq 1 ] && return
    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() {
    [ "$QUIET" -eq 1 ] && return
    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] [quiet] [version] COMMAND \
[help | COMMAND OPTION]
OPTIONS:
  - help:               Print this helper
  - verbose:            Print debug information to the output
  - quiet:              Quiet mode. Not output information
  - version:            Print the binary version
  - 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
}
print_version() {
    printf "%s\n" "$BIN_VERSION"
}
if [ $# -lt 1 ]; then
    echo_error "Missing command"
    usage & exit 2
fi
cmd_in="$1"
case "$cmd_in" in
    help|--help|-h)        usage ;                   exit 0 ;;
    verbose|--verbose|-v)  VERBOSE=1; cmd_in="$2";   shift 1 ;;
    quiet|--quiet|-q)      QUIET=1;   cmd_in="$2";   shift 1 ;;
    version|--version)     print_version;            exit 0 ;;
esac
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;
ret=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
            if ! "${cmd}_exec" "$@"; then
                ret=1
            fi
        fi
    fi
done
if [ $found -eq 0 ]; then
    echo_error "Unknown command \"$cmd_in\""
    usage
    exit 1
fi
exit $ret
