# This bash library contains the functions that allow osbash to produce # Windows batch files. : ${WBATCH_DIR:="$TOP_DIR/wbatch"} # By default, Windows batch file templates are in the same directory as this # file : ${WBATCH_TEMPLATE_DIR:=$(dirname "$BASH_SOURCE")} # wbatch cannot use ssh for talking to the VM; install VirtualBox guest # additions VM_ACCESS=vbadd # With VirtualBox 5.1.6, console type "headless" often gives no access to the # VM console which on Windows is the main method for interacting with the # cluster. Use "separate" which works at least on 5.0.26 and 5.1.6. VM_UI=separate #------------------------------------------------------------------------------- # Helper functions #------------------------------------------------------------------------------- # See functions.host for definition and explanation of exec_cmd WBATCH=exec_cmd # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function wbatch_reset { clean_dir "$WBATCH_DIR" cp -v "$WBATCH_TEMPLATE_DIR/config_bat" "$WBATCH_DIR/config.bat" } function wbatch_new_file { local file_name=$1 mkdir -p "$WBATCH_DIR" WBATCH_OUT="$WBATCH_DIR/$file_name" echo -n > "$WBATCH_OUT" } function wbatch_close_file { unset WBATCH_OUT } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function wbatch_write_line { if [ -n "${WBATCH_OUT:-}" ]; then # Don't expand backslash escapes except for ending the line with CRLF # # Note: Windows batch scripts with LF may seem to work, but (for # instance) jump labels don't work properly echo -n "$@" >> "$WBATCH_OUT" echo -e "\r" >> "$WBATCH_OUT" fi } function wbatch_write_stdin { local line="" # Set IFS to preserve leading whitespace while IFS= read -r line; do wbatch_write_line "$line" done } function wbatch_echo { wbatch_write_line "ECHO %time% $@" } #------------------------------------------------------------------------------- # Batch function calls #------------------------------------------------------------------------------- function wbatch_abort_if_vm_exists { local vm_name=$1 wbatch_write_line "CALL :vm_exists $vm_name" } function wbatch_wait_poweroff { local vm_name=$1 cat << WBAT | wbatch_write_stdin ECHO %time% Waiting for VM $vm_name to power off. CALL :wait_poweroff $vm_name ECHO %time% VM $vm_name powered off. WBAT } function wbatch_wait_auto { cat << WBAT | wbatch_write_stdin ECHO %time% Waiting for autostart files to execute. CALL :wait_auto ECHO %time% All autostart files executed. WBAT } #------------------------------------------------------------------------------- # Batch commands #------------------------------------------------------------------------------- function wbatch_delete_disk { local disk=$(basename "$1") wbatch_write_line "IF EXIST %IMGDIR%\\$disk DEL %IMGDIR%\\$disk" } function wbatch_rename_disk { local src=$(basename "$1") local target=$(basename "$2") wbatch_write_line "MOVE /y %IMGDIR%\\$src %IMGDIR%\\$target" } function wbatch_cp_auto { local src=$(wbatch_path_to_windows "$1") local target=$(basename "$2") src=${src//\//\\} wbatch_write_line "COPY %TOPDIR%\\$src %AUTODIR%\\$target" } function wbatch_sleep { local sec=$1 wbatch_write_line "TIMEOUT /T $sec /NOBREAK" } #------------------------------------------------------------------------------- # Templated parts #------------------------------------------------------------------------------- # Note: BSD and GNU sed behavior is different. Don't try anything fancy # like inserting \r or in-place editing (-i). function wbatch_file_header { local product=$1 sed -e " s,%PRODUCT%,$product,g; " "$WBATCH_TEMPLATE_DIR/template-file_header_bat" | wbatch_write_stdin } function wbatch_end_file { cat "$WBATCH_TEMPLATE_DIR/template-end_file_bat" | wbatch_write_stdin wbatch_close_file } function wbatch_elevate_privileges { cat "$WBATCH_TEMPLATE_DIR/template-elevate_privs_bat" | wbatch_write_stdin } function wbatch_find_vbm { cat "$WBATCH_TEMPLATE_DIR/template-find_vbm_bat" | wbatch_write_stdin } function wbatch_mkdirs { local autodir=$(wbatch_path_to_windows "$AUTOSTART_DIR") local imgdir=$(wbatch_path_to_windows "$IMG_DIR") local logdir=$(wbatch_path_to_windows "$LOG_DIR") local statusdir=$(wbatch_path_to_windows "$STATUS_DIR") autodir=$(wbatch_escape_backslash "$autodir") imgdir=$(wbatch_escape_backslash "$imgdir") logdir=$(wbatch_escape_backslash "$logdir") statusdir=$(wbatch_escape_backslash "$statusdir") sed -e " s,%P_AUTODIR%,$autodir,g; s,%P_IMGDIR%,$imgdir,g; s,%P_LOGDIR%,$logdir,g; s,%P_STATUSDIR%,$statusdir,g; " "$WBATCH_TEMPLATE_DIR/template-mkdirs_bat" | wbatch_write_stdin } function wbatch_create_hostnet { wbatch_new_file "create_hostnet.bat" wbatch_file_header "host-only networks" cat "$WBATCH_TEMPLATE_DIR/template-begin_hostnet_bat" | wbatch_write_stdin # Creating networks requires elevated privileges wbatch_elevate_privileges wbatch_find_vbm local index local if_name local win_adapter for index in "${!NET_NAME[@]}"; do if_name=${NET_IFNAME[index]} # The host-side interface is the default gateway of the network if_ip=${NET_GW[index]} # Translate if_name to Windows-type interface name win_adapter=$if_name sed -e " s,%IFNAME%,${win_adapter},g; s,%IFIP%,${if_ip},g; " "$WBATCH_TEMPLATE_DIR/template-create_hostnet_bat" | wbatch_write_stdin echo | wbatch_write_stdin done wbatch_end_file } function wbatch_begin_base { local iso_name=$(get_iso_name) if [ -z "$iso_name" ]; then echo >&2 "Windows batch file needs install ISO URL (ISO_URL)." exit 1 fi wbatch_new_file "create_base.bat" wbatch_file_header "base disk" wbatch_find_vbm wbatch_mkdirs sed -e " s,%INSTALLFILE%,$iso_name,g; s,%ISOURL%,$ISO_URL,g; " "$WBATCH_TEMPLATE_DIR/template-begin_base_bat" | wbatch_write_stdin } function wbatch_begin_node { local node_name=$1 wbatch_new_file "create_${node_name}_node.bat" wbatch_file_header "$node_name VM" wbatch_find_vbm wbatch_mkdirs local basedisk=$(get_base_disk_name).vdi sed -e " s,%BASEDISK%,$basedisk,g; " "$WBATCH_TEMPLATE_DIR/template-begin_node_bat" | wbatch_write_stdin } #------------------------------------------------------------------------------- # VBoxManage call handling #------------------------------------------------------------------------------- function wbatch_log_vbm { ARGS=( "$@" ) for i in "${!ARGS[@]}"; do case "${ARGS[i]}" in --hostonlyadapter*) # The next arg is the host-only interface name -> change it ARGS[i+1]=\"%${ARGS[i+1]}%\" ;; --hostpath) # The next arg is the shared dir -> change it ARGS[i+1]='%SHAREDIR%' continue ;; esac # On Windows, ISO and base disk images must be in IMGDIR re='\.(iso|vdi)$' if [[ "${ARGS[i]}" =~ $re ]]; then local img_name=$(basename "${ARGS[i]}") ARGS[i]="%IMGDIR%\\$img_name" continue fi done # Echo what we are about to do wbatch_write_line "ECHO VBoxManage ${ARGS[@]}" wbatch_write_line "\"%VBM%\" ${ARGS[@]}" # Abort if VBoxManage call raised errorlevel wbatch_write_line "IF %errorlevel% NEQ 0 GOTO :vbm_error" # Blank line for readability wbatch_write_line "" } #------------------------------------------------------------------------------- # Windows path name helpers #------------------------------------------------------------------------------- # On Windows, all paths are relative to TOP_DIR function wbatch_path_to_windows { local full_path=$1 # strip off ${TOP_DIR}/ full_path="${full_path/$TOP_DIR\//}" full_path=$(wbatch_slash_to_backslash "$full_path") echo "$full_path" } # Escape backslashes in (path) variables that are given to sed function wbatch_escape_backslash { local string=$1 string="${string//\\/\\\\}" echo "$string" } function wbatch_slash_to_backslash { local some_path=$1 some_path="${some_path//\//\\}" echo "$some_path" } # vim: set ai ts=4 sw=4 et ft=sh: