diff --git a/win.uefi.boot.sh b/win.uefi.boot.sh new file mode 100755 index 0000000..1352088 --- /dev/null +++ b/win.uefi.boot.sh @@ -0,0 +1,301 @@ +#!/bin/sh +# +# Purpose {{{ +# This script will reboot on Windows system +# 1. Try to get UEFI entry number for Windows. +# 2. Define it as default only for next boot. +# 3. Reboot (default) or poweroff the system. +# … +# +# 2023-08-18 +# }}} +# Flags {{{ +## Exit on error {{{ +set -o errexit +## }}} +## Exit on unset var {{{ +### Use "${VARNAME-}" to test a var that may not have been set +set -o nounset +## }}} +## Pipeline command is treated as failed {{{ +### Not available in POSIX sh − https://github.com/koalaman/shellcheck/wiki/SC3040 +#set -o pipefail +## }}} +## Help with debugging {{{ +### Call the script by prefixing it with "TRACE=1 ./script.sh" +if [ "${TRACE-0}" -eq 1 ]; then set -o xtrace; fi +## }}} +# }}} +# Vars {{{ +PROGNAME=$(basename "${0}"); readonly PROGNAME +PROGDIR=$(readlink --canonicalize-missing $(dirname "${0}")); readonly PROGDIR +ARGS="${*}"; readonly ARGS +readonly NBARGS="${#}" +[ -z "${DEBUG-}" ] && DEBUG=1 +## Export DEBUG for sub-script +export DEBUG + +## Default values for some vars +readonly FLAG_REBOOT_DEFAULT="0" +readonly FLAG_POWEROFF_DEFAULT="1" +readonly WINDOWS_BASE_LABEL_DEFAULT="Windows" + +## Colors +readonly PURPLE='\033[1;35m' +readonly RED='\033[0;31m' +readonly RESET='\033[0m' +readonly COLOR_DEBUG="${PURPLE}" +# }}} +usage() { # {{{ + + cat <<- HELP +usage: $PROGNAME [-d|-h|-l|-r|-s] + +Try to reboot to Windows system. + +EXAMPLES : + - Reboot to Windows system + ${PROGNAME} + + - Define Windows for next boot but poweroff the system + ${PROGNAME} --poweroff + + - Search for a specific Windows label + ${PROGNAME} --label "Windows Boot Manager" + +OPTIONS : + -d,--debug + Enable debug messages. + + -h,--help + Print this help message. + + -l,--label,--windows-label + Define a different label to search for Windows UEFI entry. + (default: ${WINDOWS_BASE_LABEL_DEFAULT}) + + -r,--reboot + Reboot the system (default behaviour). + + -s,--shutdown,--poweroff,--halt + Ask to shutdown the system. + + -u,--uefi,--uefi-entry + Define the UEFI entry to use. + (default: first matching from efibootmgr + windows-label) + +HELP +} +# }}} +debug_message() { # {{{ + + local_debug_message="${1}" + + ## Print message if DEBUG is enable (=0) + [ "${DEBUG}" -eq "0" ] && printf '\e[1;35m%-6b\e[m\n' "DEBUG − ${PROGNAME} : ${local_debug_message}" + + unset local_debug_message + + return 0 +} +# }}} +error_message() { # {{{ + + local_error_message="${1}" + local_error_code="${2}" + + ## Print message + printf '%b\n' "ERROR − ${PROGNAME} : ${RED}${local_error_message}${RESET}" >&2 + + unset local_error_message + + exit "${local_error_code:=66}" +} +# }}} +define_vars() { # {{{ + debug_message "-- Define vars BEGIN" + # If flag_reboot wasn't defined (argument) {{{ + if [ -z "${flag_reboot-}" ]; then + ## Use default value + readonly flag_reboot="${FLAG_REBOOT_DEFAULT}" + fi + # }}} + # If flag_poweroff wasn't defined (argument) {{{ + if [ -z "${flag_poweroff-}" ]; then + ## Use default value + readonly flag_poweroff="${FLAG_POWEROFF_DEFAULT}" + fi + # }}} + # If windows_base_label wasn't defined (argument) {{{ + if [ -z "${windows_base_label-}" ]; then + ## Use default value + readonly windows_base_label="${WINDOWS_BASE_LABEL_DEFAULT}" + fi + # }}} + # If windows_uefi_entry wasn't defined (argument) {{{ + if [ -z "${windows_uefi_entry-}" ]; then + ## Try to get the first matching entry with efibootmgr + windows_base_label + debug_message "||define_vars − \ +Try to get Windows UEFI entry with 'efibootmgr' + windows-label (${windows_base_label})." + windows_uefi_entry=$(efibootmgr | grep --extended-regexp --max-count=1 -- ".*${windows_base_label}.*" | cut --characters=5-8) + fi + # }}} + # If windows_uefi_entry remains empty {{{ + if [ -z "${windows_uefi_entry-}" ]; then + error_message "windows_uefi_entry seems empty! Verify the windows_base_label used (${windows_base_label}) and your UEFI configuration ('efibootmgr -v')." 11 + elif [ -n "${windows_uefi_entry}" ]; then + debug_message "||define_vars − \ +The ${RED}${windows_uefi_entry}${COLOR_DEBUG} UEFI entry will be used for Windows." + else + error_message "I am not supposed to happen!" 12 + fi + # }}} + debug_message "-- Define vars END" +} +# }}} + +is_command_available() { # {{{ + + local_command_available_cmd="${1}" + debug_prefix="${2:-}" + + ## Return False by default + return_command_available="1" + + if [ "$(command -v ${local_command_available_cmd})" ]; then + debug_message "${debug_prefix}is_command_available − \ +${RED}${local_command_available_cmd}${COLOR_DEBUG} seems present on this host." + return_command_available="0" + else + debug_message "${debug_prefix}is_command_available − \ +${RED}${local_command_available_cmd}${COLOR_DEBUG} is not available on this host." + return_command_available="1" + fi + + unset local_command_available_cmd + unset debug_prefix + + return "${return_command_available}" +} +# }}} + +main() { # {{{ + + debug_message "--- MAIN BEGIN" + + ## If a command is missing {{{ + ### Exit with error message + is_command_available "efibootmgr" "| " \ + || error_message "No test_me command available. Please install efibootmgr package with your package manager." 01 + ## }}} + + ## Define all vars + define_vars + + ## If the expected UEFI entry doesn't exists {{{ + if ! sudo efibootmgr | grep --quiet -- "Boot${windows_uefi_entry}"; then + error_message "The expected UEFI entry (${windows_uefi_entry}) doesn't seem to exist…" 21 + fi + ## }}} + + ## Define next boot entry + sudo efibootmgr --quiet --bootnext "${windows_uefi_entry}" + + ## If BootNext entry doesn't match {{{ + if ! sudo efibootmgr | grep --quiet -- "BootNext: ${windows_uefi_entry}"; then + error_message "The expected UEFI entry (${windows_uefi_entry}) doesn't seem to be the next one (see 'sudo efibootmgr' BootNext entry)." 22 + fi + ## }}} + + printf '%b' "Reboot/shutdown as requested to winbouse partition\\n" + [ "${flag_reboot}" -eq "0" ] && sudo systemctl reboot + [ "${flag_poweroff}" -eq "0" ] && sudo systemctl poweroff + + debug_message "--- MAIN END" +} +# }}} + +# Manage arguments # {{{ +# This code can't be in a function due to argument management + +if [ ! "${NBARGS}" -eq "0" ]; then + + manage_arg="0" + + ## If the first argument ask for help (h|help|-h|-help|-*h|-*help) {{{ + if printf -- '%s' "${1-}" | grep --quiet --extended-regexp -- "^-*h(elp)?$"; then + usage + exit 0 + fi + ## }}} + + ## If the first argument is not an option + if ! printf -- '%s' "${1}" | grep --quiet --extended-regexp -- "^-+"; + then + ## Print help message and exit + printf '%b\n' "${RED}Invalid option: ${1}${RESET}" + printf '%b\n' "---" + usage + + exit 1 + fi + + # Parse all options (start with a "-") one by one + while printf -- '%s' "${1-}" | grep --quiet --extended-regexp -- "^-+"; do + + case "${1}" in + -d|--debug ) ## debug + DEBUG=0 + debug_message "--- Manage argument BEGIN" + ;; + -h|--help ) ## help message (if it's not the first option) + usage + exit 0 + ;; + -l|--label|--windows-label ) ## Set windows_base_label var + ## Move to the next argument + shift + ## Define var + readonly windows_base_label="${1}" + ;; + -r|--reboot ) ## If a reboot was requested (default behaviour) + flag_reboot="0" + flag_poweroff="1" + ;; + -s|--shutdown|--poweroff|--halt ) ## If a poweroff was requested + flag_reboot="1" + flag_poweroff="0" + ;; + -u|--uefi|--uefi-entry ) ## Set windows_uefi_entry var + ## Move to the next argument + shift + ## Define var + readonly windows_uefi_entry="${1}" + ;; + * ) ## unknow option + printf '%b\n' "${RED}Invalid option: ${1}${RESET}" + printf '%b\n' "---" + usage + exit 1 + ;; + esac + + debug_message "| ${RED}${1}${COLOR_DEBUG} option managed." + + ## Move to the next argument + shift + manage_arg=$((manage_arg+1)) + + done + + debug_message "| ${RED}${manage_arg}${COLOR_DEBUG} argument(s) successfully managed." +else + debug_message "| No arguments/options to manage." +fi + + debug_message "--- Manage argument END" +# }}} + +main + +exit 255