#!/bin/bash # # Purpose {{{ # This script will try to start an iDrac 6/7 virtual console # 1. Ask for an host if not given with the option. # 2. Ask for a user if not given with the option. # 3. Ask for the password. # # Inspired from : https://gist.github.com/xbb/4fd651c2493ad9284dbcb827dc8886d6 # # 2023-02-15 # }}} # 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 IDRAC_USERNAME_DEFAULT="root" readonly IDRAC_PORT_DEFAULT="443" readonly JAVA_DISABLE_ALGO_DEFAULT="SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224, anon, NULL, include jdk.disabled.namedCurves" readonly JAVA_SECURITY_FILENAME="idrac.java.security" readonly TEMP_DIR_BASE="/tmp" ## 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] [--user idrac_username] [--host idrac.hostname] [--port port.number] [--temp /path/to/temp/directory] Try to start an iDrac 6/7 virtual console with given informations. The password will always be asked and not displayed. EXAMPLES : - Start the script and wait for prompt to enter informations ${PROGNAME} - Start virtual console of a given host ${PROGNAME} --host "oob-myserver.domain.tld" - Start virtual console with SSH Port forwarding ${PROGNAME} --host "localhost:7200" - Use specific username ${PROGNAME} --user "admin" - Store downloaded files and informations in a specific temp directory ${PROGNAME} --temp /tmp/my.idrac.temp.dir OPTIONS : -d,--debug Enable debug messages. -h,--help Print this help message. --host Define the iDrac host to connect. Fallback: It will be asked to the user. -p,--port Define the iDrac port to use. The script will try to parse the given value of hostname and extract a port number Fallback: It will be asked to the user. Default : ${IDRAC_PORT_DEFAULT} -u,--user Define the iDrac username. Fallback: It will be asked to the user. Default : ${IDRAC_USERNAME_DEFAULT} --temp,--temp-dir,--temp-directory Define the temp directoy to store downloaded files and informations. Default : ${TEMP_DIR_BASE}/idrac.given_hostname --java-disable-algo Override global java security settings. To allow old console to work on new java environment. Default : ${JAVA_DISABLE_ALGO_DEFAULT} For more informations, see : https://gist.github.com/xbb/4fd651c2493ad9284dbcb827dc8886d6?permalink_comment_id=4006481#gistcomment-4006481 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() { # {{{ ## If idrac_host_temp wasn't defined (argument) {{{ if [ -z "${idrac_host_temp-}" ]; then ### Ask the user to enter a username printf -- '%b' 'Enter iDrac host (eg. "oob-myserver.domain.tld", "localhost:7200"…): ' read -r idrac_host_temp fi ## }}} ## If idrac_host_temp is still empty {{{ if [ -z "${idrac_host_temp-}" ]; then error_message "iDrac host can't be empty, please check your input or use --host option. For more informations, see the help message (--help)." 11 fi ## }}} ## If idrac_host_temp is still empty {{{ if [ -z "${idrac_host_temp-}" ]; then error_message "iDrac host can't be empty, please check your input or use --host option. For more informations, see the help message (--help)." 11 fi ## }}} ## If idrac_host_temp contains a port number {{{ if printf '%b' "${idrac_host_temp}" | awk 'BEGIN { FS = ":" } ; /^[a-zA-Z]+.*:[0-9]+$/' >/dev/null; then ### Extract hostname and port number idrac_host=$(printf '%b' "${idrac_host_temp}" | awk 'BEGIN { FS = ":" } ; /^[a-zA-Z]+.*:[0-9]+$/ { print $1 }') idrac_port=$(printf '%b' "${idrac_host_temp}" | awk 'BEGIN { FS = ":" } ; /^[a-zA-Z]+.*:[0-9]+$/ { print $2 }') else ### Simply use idrac_host_temp as hostname idrac_host="${idrac_host_temp}" fi ## }}} ## If idrac_username wasn't defined (argument) {{{ if [ -z "${idrac_username-}" ]; then ### Ask the user to enter a username printf -- '%b' "Enter iDrac username (default ${RED}${IDRAC_USERNAME_DEFAULT}${RESET}): " read -r idrac_username fi ## }}} ## If idrac_username is still empty {{{ if [ -z "${idrac_username-}" ]; then ### Use default value readonly idrac_username="${IDRAC_USERNAME_DEFAULT}" fi ## }}} ## If idrac_port wasn't defined (argument or by idrac_host_temp) {{{ if [ -z "${idrac_port-}" ]; then ### Ask the user to enter a port number printf -- '%b' "Enter iDrac port (default ${RED}${IDRAC_PORT_DEFAULT}${RESET}): " read -r idrac_port fi ## }}} ## If idrac_port is still empty {{{ if [ -z "${idrac_port-}" ]; then idrac_port="${IDRAC_PORT_DEFAULT}" fi ## }}} ## Ask for idrac_password {{{ printf -- '%b' "Enter iDrac password (it won't be displayed): " read -r -s idrac_password printf -- '%b\n' "" ## }}} ## If idrac_password is empty, ask again {{{ if [ -z "${idrac_password-}" ]; then printf -- '%b' "A password is required to connect iDrac host (it won't be displayed): " read -r -s idrac_password printf -- '%b\n' "" fi ## }}} ## If idrac_password is still empty {{{ if [ -z "${idrac_password-}" ]; then error_message "iDrac password can't be empty, please check your input. For more informations, see the help message (--help)." 12 fi ## }}} ## If java_disable_algo wasn't defined (argument) {{{ if [ -z "${java_disable_algo-}" ]; then ### Use default value readonly java_disable_algo="${JAVA_DISABLE_ALGO_DEFAULT}" fi ## }}} ## If temp_dir wasn't defined (argument) {{{ if [ -z "${temp_dir-}" ]; then ### Use default value readonly temp_dir="${TEMP_DIR_BASE}/idrac.${idrac_host}" fi ## }}} } # }}} 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}" } # }}} is_file_empty() { # {{{ local_file="${1}" debug_prefix="${2:-}" ## File is empty by default return_is_file_empty="0" ## Check if the file is empty if [ ! -s "${local_file}" ]; then return_is_file_empty="0" debug_message "${debug_prefix}is_file_empty − \ The file ${RED}${local_file}${COLOR_DEBUG} is empty or doesn't exists." else return_is_file_empty="1" debug_message "${debug_prefix}is_file_empty − \ The file ${RED}${local_file}${COLOR_DEBUG} exists and has a size greater than zero." fi unset local_file unset debug_prefix return "${return_is_file_empty}" } # }}} main() { # {{{ debug_message "--- MAIN BEGIN" ## If java command is not available {{{ ### Exit with error message is_command_available "java" "| " \ || error_message "No java command available. Please install a openjdk-*-jre package with your package manager." 01 ## }}} ## If wget command is not available {{{ ### Exit with error message is_command_available "wget" "| " \ || error_message "No wget command available. Please install =>wget<= package with your package manager." 02 ## }}} ## Define all vars define_vars debug_message "| Define vars" debug_message "| Given informations are : idrac_host: ${RED}${idrac_host}${COLOR_DEBUG} idrac_port: ${RED}${idrac_port}${COLOR_DEBUG} idrac_username: ${RED}${idrac_username}${COLOR_DEBUG} idrac_password: ${RED}HIDDEN${COLOR_DEBUG} temp_dir: ${RED}${temp_dir}${COLOR_DEBUG}" ## Create and move to temp directory debug_message "| Create and move to temp directory (${RED}${temp_dir}${COLOR_DEBUG})." mkdir --parents -- "${temp_dir}" cd -- "${temp_dir}" || error_message "Can't move to temp directory (${temp_dir})." 21 ## Set Java security parameters debug_message "| Set java security parameters (to ${RED}${JAVA_SECURITY_FILENAME}${COLOR_DEBUG} file)." true > "${JAVA_SECURITY_FILENAME}" printf '%b\n' "jdk.tls.disabledAlgorithms=${JAVA_DISABLE_ALGO_DEFAULT}" >> "${JAVA_SECURITY_FILENAME}" ## Try to download main jar file from idrac host avctkmv_filename="avctKVM.jar" avctkvm_url="https://${idrac_host}:${idrac_port}/software/${avctkmv_filename}" debug_message "| Try to download ${RED}${avctkmv_filename}${COLOR_DEBUG} file from URL (${RED}${avctkvm_url}${COLOR_DEBUG})." wget --timestamping --no-check-certificate --quiet -- "${avctkvm_url}" is_file_empty "${avctkmv_filename}" "| " \ && error_message "Downloaded ${avctkmv_filename} file is empty (from ${avctkvm_url} URL)." 22 ## Librairies ## TODO? Download avctKVMIOLinux64.jar and avctVMLinux64.jar files to extract librairies (for keyboard…). ## Try to start iDrac console with java printf '%b\n' "Try to start ${RED}${idrac_host}:${idrac_port}${RESET} virtual console with user (${RED}${idrac_username}${RESET}) and given password." java -cp "${avctkmv_filename}" -Djava.security.properties=idrac.java.security -Djava.library.path=./lib com.avocent.idrac.kvm.Main ip="${idrac_host}" kmport=5900 vport=5900 user="${idrac_username}" passwd="${idrac_password}" apcp=1 version=2 vmprivilege=true "helpurl=https://${idrac_host}:${idrac_port}/help/contents.html" cd -- - >/dev/null || error_message "Can't move to previous directory (before ${temp_dir})." 23 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" ;; --host ) ## Define idrac_host_temp with given arg ## Move to the next argument shift ## Define var readonly idrac_host_temp="${1}" ;; -p,--port ) ## Define idrac_port with given arg ## Move to the next argument shift ## Define var readonly idrac_port="${1}" ;; -u|--user ) ## Define idrac_username with given arg ## Move to the next argument shift ## Define var readonly idrac_username="${1}" ;; --temp,--temp-dir,--temp-directory ) ## Define temp_dir with given arg ## Move to the next argument shift ## Define var readonly temp_dir="${1}" ;; --java-disable-algo ) ## Define java_disable_algo with given arg ## Move to the next argument shift ## Define var readonly java_disable_algo="${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 0