mirror of
https://github.com/morgant/basiliskiivm.git
synced 2025-03-19 17:30:09 +00:00
517 lines
12 KiB
Bash
Executable File
517 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
#
|
|
# basiliskiivm - Package BasiliskII emulator settings & disk images
|
|
# into a single '.basiliskiivm' directory and run
|
|
# from there.
|
|
#
|
|
# CHANGE LOG:
|
|
#
|
|
# v0.1 2016-12-08 - Morgan T. Aldridge <morgant@makkintosshu.com>
|
|
# Initial version.
|
|
# v0.1.1 2017-11-07 - Morgan T. Aldridge
|
|
# Automatically search for BasiliskII binary on macOS.
|
|
# v0.1.2 2019-01-28 0 Morgan T. Aldridge
|
|
# Better *nix compatibility (tested OpenBSD).
|
|
#
|
|
# LICENSE:
|
|
#
|
|
# Copyright (c) 2016-2017, Morgan T. Aldridge. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# - Redistributions of source code must retain the above copyright notice, this
|
|
# list of conditions and the following disclaimer.
|
|
# - Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions and the following disclaimer in the documentation
|
|
# and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
# info
|
|
tool="$(basename "$0")"
|
|
version="0.1.2"
|
|
copyright="Copyright (c) 2016-2019 Morgan Aldridge"
|
|
|
|
# global variables
|
|
BASILISKII_BINARY="${BASILISKII_BINARY:=BasiliskII}"
|
|
BASILISKII_VMS_PATH="${BASILISKII_VMS_PATH:="${HOME}/Documents/BasiliskII"}"
|
|
BASILISKII_SNAPSHOT_COMPRESSION="${BASILISKII_SNAPSHOT_COMPRESSION:=gzip}"
|
|
basiliskii_prefs_file=".basilisk_ii_prefs"
|
|
basiliskii_pid_file=".basiliskii.pid"
|
|
|
|
function in_array() {
|
|
local found=false
|
|
local value="$1"
|
|
shift
|
|
|
|
if [ -z "$value" ]; then $found; fi
|
|
if [ ${#@} -lt 1 ]; then $found; fi
|
|
for array_value in "$@"; do
|
|
if [ "$value" = "$array_value" ]; then found=true; fi
|
|
done
|
|
|
|
$found
|
|
}
|
|
|
|
function usage() {
|
|
echo "Usage: ${tool} [options] <command>"
|
|
echo
|
|
echo "Options:"
|
|
echo " -h, --help : print these usage instructions"
|
|
echo " -V, --version : print version information"
|
|
echo
|
|
echo "Commands:"
|
|
echo " info <vm> : print basic configuration info for a .BasiliskIIVM"
|
|
echo " list [<path>] : list all .BasiliskIIVM in path (or none for default directory)"
|
|
echo " package : package the current BasiliskII configuration into a .BasiliskIIVM"
|
|
echo " snapshot <vm> : create a snapshot of the current state of disks in the .BasiliskIIVM"
|
|
echo " snapshots <vm>: list all snapshots in a .BasiliskIIVM"
|
|
echo " start <vm> : start a BasiliskII instance from a .BasiliskIIVM"
|
|
echo " status <vm> : get the status of a .BasiliskIIVM"
|
|
echo " stop <vm> : stop a running .BasiliskIIVM"
|
|
echo
|
|
echo "Environment Variables:"
|
|
echo " BASILISKII_BINARY : path to your BasiliskII binary, if it's not in your search path"
|
|
echo " BASILISKII_VMS_PATH : path where you store your BasiliskII VMs"
|
|
echo " BASILISKII_SNAPSHOT_COMPRESSION: method used for snapshot compression ('none', 'gzip')"
|
|
echo
|
|
}
|
|
|
|
function version() {
|
|
echo "${tool} v${version} ${copyright}"
|
|
}
|
|
|
|
function find_basilisk() { # but don't look!
|
|
local success=false
|
|
|
|
if [ -x "$BASILISKII_BINARY" ]; then
|
|
success=true
|
|
elif which "$BASILISKII_BINARY" >/dev/null; then
|
|
success=true
|
|
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
|
local mac_binary="$(find ~/Applications /Applications -type f -path "*.app/Contents/MacOS*" -name "$BASILISKII_BINARY" | head -n 1)"
|
|
if [ -n "$mac_binary" ]; then
|
|
read -p "Found '$mac_binary', would you like to use this binary? [Y/n] " response
|
|
if [ "$response" = "Y" ]; then
|
|
BASILISKII_BINARY="$mac_binary"
|
|
success=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if ! $success; then
|
|
echo "ERROR! Unable to locate the BasiliskII binary!"
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_name() {
|
|
local success=false
|
|
|
|
local vm="$(basename "$1")"
|
|
if [[ "$vm" =~ ^(.+)\.[Bb]asilisk[Ii]{2}[Vv][Mm]$ ]]; then
|
|
echo "${BASH_REMATCH[1]}"
|
|
success=true
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_config_file() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local prefs_file="${1}/${basiliskii_prefs_file}"
|
|
if [ -f "$prefs_file" ]; then
|
|
echo "$prefs_file"
|
|
sucess=true
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_config_parse() {
|
|
local success=false
|
|
local vm_path="$1"
|
|
shift
|
|
|
|
local vm="$(vm_pkg_name "$vm_path")"
|
|
if [ -n "$vm" ]; then
|
|
local config="$(vm_pkg_config_file "$vm_path")"
|
|
if [ -z "$config" ]; then
|
|
echo "Error! The '$vm' BasiliskII VM's config file couldn't be found."
|
|
else
|
|
while IFS= read -r line; do
|
|
if [ -n "$line" ]; then
|
|
if [[ "$line" =~ ^([A-Za-z0-9]+)\ (.+)$ ]]; then
|
|
if in_array "${BASH_REMATCH[1]}" "$@"; then
|
|
echo "$line"
|
|
fi
|
|
fi
|
|
fi
|
|
done < "$config"
|
|
success=true
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_disks() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local disk_paths=()
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^disk\ (.+)$ ]]; then
|
|
# if [ ! -f "${BASH_REMATCH[1]}" ]; then
|
|
# echo "Warning: '${vm}' VM disk '${BASH_REMATCH[1]}' does not exist!"
|
|
# fi
|
|
disk_paths+=("${BASH_REMATCH[1]}")
|
|
fi
|
|
done <<< "$(vm_pkg_config_parse "$1" disk)"
|
|
|
|
for disk in "${disk_paths[@]}"; do
|
|
echo "$(basename "$disk")"
|
|
success=true
|
|
done
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_is_running() {
|
|
local running=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local pid_path="${1}/${basiliskii_pid_file}"
|
|
if [ -f "$pid_path" ]; then
|
|
if ps -p "$(cat "$pid_path")" > /dev/null 2>&1; then
|
|
running=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
$running
|
|
}
|
|
|
|
function vm_create_pid_file() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local pid_path="${1}/${basiliskii_pid_file}"
|
|
|
|
# create the pid file
|
|
if ! echo "$2" > "$pid_path"; then
|
|
echo "ERROR! Unable to create the '$vm' BasiliskII VM PID file '$pid_path'."
|
|
else
|
|
success=true
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_delete_pid_file() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local pid_path="${1}/${basiliskii_pid_file}"
|
|
|
|
if [ ! -f "$pid_path" ]; then
|
|
echo "ERROR! The '$vm' BasiliskII VM PID file '$pid_path' doesn't exist, so cannot delete it."
|
|
else
|
|
if ! rm "$pid_path"; then
|
|
echo "ERROR! Unable to delete the '$vm' BasiliskII VM PID file '$pid_path'."
|
|
else
|
|
success=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_create_package() {
|
|
success=false
|
|
|
|
echo "ERROR! This functionality isn't implemented yet."
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_info() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local rom_path=""
|
|
local shared_path=""
|
|
local disk_paths=()
|
|
local screen_size=""
|
|
local ram_size=0
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ ^([A-Za-z0-9]+)\ (.+)$ ]]; then
|
|
case "${BASH_REMATCH[1]}" in
|
|
"extfs")
|
|
shared_path="${BASH_REMATCH[2]}"
|
|
;;
|
|
"rom")
|
|
rom_path="${BASH_REMATCH[2]}"
|
|
;;
|
|
"ramsize")
|
|
ram_size=$(( ${BASH_REMATCH[2]} / 1024 / 1024 ))
|
|
;;
|
|
"disk")
|
|
disk_paths+=("${BASH_REMATCH[2]}")
|
|
;;
|
|
"screen")
|
|
if [[ "${BASH_REMATCH[2]}" =~ ^([A-Za-z]+)/([0-9]+)/([0-9]+)$ ]]; then
|
|
screen_size="${BASH_REMATCH[2]}x${BASH_REMATCH[3]}"
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
done <<< "$(vm_pkg_config_parse "$1" disk extfs screen rom ramsize)"
|
|
|
|
echo "$(basename "$1"):"
|
|
echo
|
|
echo "Path: $(dirname "$1")"
|
|
echo "ROM: $(basename "${rom_path}")"
|
|
echo "RAM: ${ram_size}MB"
|
|
echo "Resolution: ${screen_size}"
|
|
echo "Shared Folder: ${shared_path}"
|
|
echo "Disks:"
|
|
for disk in "${disk_paths[@]}"; do
|
|
echo " $(basename "$disk")"
|
|
done
|
|
success=true
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_status() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
echo -n "'$vm': "
|
|
if vm_is_running "$1"; then
|
|
echo "Running"
|
|
else
|
|
echo "Stopped"
|
|
fi
|
|
success=true
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_start() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
if vm_is_running "$1"; then
|
|
echo "Error! The '$vm' BasiliskII VM is already running."
|
|
else
|
|
local config="$(vm_pkg_config_file "$1")"
|
|
if [ -z "$config" ]; then
|
|
echo "Error! The '$vm' BasiliskII VM's config file couldn't be found."
|
|
else
|
|
echo "Starting the '$vm' BasiliskII VM..."
|
|
"$BASILISKII_BINARY" --nogui true --config "$config" >/dev/null &
|
|
local pid="$!"
|
|
if [ -z "$pid" -a "$pid" -lt 1 ]; then
|
|
echo "ERROR! The '$vm' BasiliskII VM didn't start correctly."
|
|
elif vm_create_pid_file "$1" "$pid"; then
|
|
success=true
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_stop() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
if ! vm_is_running "$1"; then
|
|
echo "Error! The '$vm' BasiliskII VM is not running."
|
|
else
|
|
echo "Warning! It is not safe to stop the '$vm' BasiliskII VM while it is running."
|
|
echo "Please choose Special > Shutdown from within the BasiliskII VM instance to shut it down."
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_snapshots() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
local snapshots_path="${1}/Snapshots"
|
|
if [ -d "$snapshots_path" ]; then
|
|
while IFS= read -r line; do
|
|
echo "$(basename "$line")"
|
|
done <<< "$(find "$snapshots_path" -type d -mindepth 1 -maxdepth 1)"
|
|
fi
|
|
success=true
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function vm_pkg_create_snapshot() {
|
|
local success=false
|
|
|
|
local vm="$(vm_pkg_name "$1")"
|
|
if [ -n "$vm" ]; then
|
|
if vm_is_running "$1"; then
|
|
echo "Error! You cannot create a snapshot while the '${vm}' BasiliskII VM is running. Please stop it and try again."
|
|
return 1
|
|
fi
|
|
|
|
local snapshots_path="${1}/Snapshots"
|
|
if [ ! -d "$snapshots_path" ]; then
|
|
if ! mkdir "$snapshots_path"; then
|
|
echo "Error! Unable to create snapshots directory '${snapshots_path}'."
|
|
return 1
|
|
fi
|
|
fi
|
|
local timestamp="$(date +%Y%m%d-%H%M%S)"
|
|
local snapshot="${snapshots_path}/${timestamp}"
|
|
if ! mkdir "$snapshot"; then
|
|
echo "Error! Unable to create snapshot directory '${snapshot}'."
|
|
else
|
|
local disks_copy_success=true
|
|
while IFS= read -r disk; do
|
|
local src_disk="${1}/${disk}"
|
|
local dst_disk="${snapshot}/${disk}"
|
|
local copy_success=true
|
|
case "$BASILISKII_SNAPSHOT_COMPRESSION" in
|
|
"gzip")
|
|
dst_disk="${dst_disk}.gz"
|
|
if ! gzip -k -c "$src_disk" > "$dst_disk"; then
|
|
copy_success=false
|
|
fi
|
|
;;
|
|
*)
|
|
if ! cp "$src_disk" "$dst_disk"; then
|
|
copy_success=false
|
|
fi
|
|
;;
|
|
esac
|
|
if ! $copy_success; then
|
|
echo "Error! Unable to copy disk '${src_disk}' to '${dst_disk}'."
|
|
disks_copy_success=false
|
|
fi
|
|
done <<< "$(vm_pkg_disks "$1")"
|
|
if $disks_copy_success; then
|
|
echo "Created '${timestamp}' snapshot of '${vm}' BasiliskII VM."
|
|
success=true
|
|
else
|
|
echo "Error! Unable to create '${timestamp}' snapshot of '${vm}' BasiliskII VM."
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
$success
|
|
}
|
|
|
|
function list_vms() {
|
|
success=false
|
|
|
|
local vms_path="$1"
|
|
if [ -z "$vms_path" ]; then
|
|
vms_path="$BASILISKII_VMS_PATH"
|
|
fi
|
|
while IFS= read -r line; do
|
|
echo "$(basename "$line")"
|
|
done <<< "$(find "$vms_path" -iname "*.BasiliskIIVM")"
|
|
success=true
|
|
|
|
$success
|
|
}
|
|
|
|
function main() {
|
|
if ! find_basilisk; then
|
|
echo "Exiting."
|
|
exit 1
|
|
fi
|
|
|
|
case "$1" in
|
|
"-h" | "--help")
|
|
usage
|
|
exit 0
|
|
;;
|
|
"-V" | "--version")
|
|
version
|
|
exit 0
|
|
;;
|
|
"info")
|
|
shift
|
|
vm_info "$1"
|
|
;;
|
|
"list")
|
|
shift
|
|
list_vms "$1"
|
|
;;
|
|
"snapshot")
|
|
shift
|
|
vm_pkg_create_snapshot "$1"
|
|
;;
|
|
"snapshots")
|
|
shift
|
|
vm_pkg_snapshots "$1"
|
|
;;
|
|
"start")
|
|
shift
|
|
vm_start "$1"
|
|
;;
|
|
"status")
|
|
shift
|
|
vm_status "$1"
|
|
;;
|
|
"stop")
|
|
shift
|
|
vm_stop "$1"
|
|
;;
|
|
"package")
|
|
shift
|
|
vm_create_package "$1"
|
|
;;
|
|
*)
|
|
echo "ERROR! Unknown option '$1'. Exiting"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|