Skip to content

Instantly share code, notes, and snippets.

@noelruault
Last active August 24, 2023 08:26
Show Gist options
  • Save noelruault/cf65e0e586f14f74f2bb3194042e267a to your computer and use it in GitHub Desktop.
Save noelruault/cf65e0e586f14f74f2bb3194042e267a to your computer and use it in GitHub Desktop.
Script that helps to erase a disk content using `hdparm` and then copies a designated source disk (or `/dev/null`) into a specified target disk.
#!/bin/bash
#================================================================
# HEADER
#================================================================
#% SYNOPSIS
#+ ${SCRIPT_NAME} [-hv] args ...
#%
#% DESCRIPTION
#% This is a script that erases a disk and then copies a designated source disk into an specified target disk.
#% You can run it in one line when connected to the internet like this:
#% wget -qO - http://link/to/raw/wipedisk.sh | bash
#%
#%
#% OPTIONS
#% -t, --timelog Add timestamp to log ("+%y/%m/%d@%H:%M:%S")
#% -h, --help Print this help
#% -v, --version Print script information
#%
#% EXAMPLES
#% ${SCRIPT_NAME} -o DEFAULT arg1 arg2
#%
#================================================================
#- IMPLEMENTATION
#- version ${SCRIPT_NAME}
#- author Noël RUAULT
#- copyright Copyright (c) (www.noel.engineer)
#- license GNU General Public License
#-
#================================================================
# HISTORY
# 2023/04/01 : noelruault : Script creation
#
#================================================================
# DEBUG OPTION
# set -n # Uncomment to check your syntax, without execution.
# set -x # Uncomment to debug this shell script
#
#================================================================
# END_OF_HEADER
#================================================================
#== ==#
SCRIPT_HEADSIZE=$(head -200 ${0} | grep -n "^# END_OF_HEADER" | cut -f1 -d:)
SCRIPT_NAME="$(basename ${0})"
MAIN_COLOR="#04B575"
#== ==#
usage() {
printf "Usage: "
head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#+" | sed -e "s/^#+[ ]*//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"
}
usagefull() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#[%+-]" | sed -e "s/^#[%+-]//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"; }
scriptinfo() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#-" | sed -e "s/^#-//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"; }
trap "exit" INT
if [ "$(id -u)" -ne 0 ]; then
echo 'This script must be run by root' >&2
exit 1
fi
## dependencies will install all the additional software required to run this script
dependencies() {
if command -v gum &>/dev/null; then return; fi # All good, exit gracefully
case $1 in
darwin)
yes | brew install gum
;;
linux)
mkdir -p /etc/apt/keyrings
wget -qO - https://repo.charm.sh/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/charm.gpg
# curl -fsSL https://repo.charm.sh/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/charm.gpg
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | tee /etc/apt/sources.list.d/charm.list
apt update && apt -y install gum
;;
*)
echo "$0: error: unrecognized operating system $1"
echo "$0: $scriptinfo"
exit 0
;;
esac
}
wipe() {
gum confirm "Wipe all data from $DST?" &&
hdparm --user-master user --security-set-pass pass $1 &&
hdparm --user-master user --security-erase-enhanced pass $1
}
## copy receives two parameters $1=source and $2=destination and copies src into dst
copy() {
gum confirm "Copy content from $1 to $2 ?" &&
dd if=$1 of=$2 bs=8M status=progress
}
## is_frozen returns true if a disk is in a frozen state, false otherwise
is_frozen() {
diskstate=$(hdparm -I $1 | grep frozen | xargs | sed 's/ //g')
case $diskstate in
notfrozen)
return 1 # 1 = false
;;
frozen)
return 0 # 0 = true
;;
*)
echo "error: couldn't find disk state"
return 0 # 0 = true
;;
esac
}
## disks sets SSDOPTS and VERBOSESSDOPTS, as well as SRC and DST
## - SSDOPTS: The available list of disks attached to the computer
## - VERBOSESSDOPTS: The verbose list of disks attached to the computer
## - SRC: The source disk
## - DST: The destination disk
disks() {
case $1 in
darwin)
SSDOPTS=($(diskutil list | grep physical | egrep -o '^[^(]+'))
SSDOPTS+=("/dev/zero")
VERBOSESSDOPTS=$(diskutil list)
;;
linux)
SSDOPTS=($(lsblk -I 8 -nd --output PATH))
SSDOPTS+=("/dev/zero")
VERBOSESSDOPTS=$(lsblk -I 8 -nd --output PATH,SIZE,TYPE)
;;
*)
echo "$0: error: unrecognized operating system $1"
echo "$0: $scriptinfo"
exit 0
;;
esac
if ((${#SSDOPTS[@]} < 2)); then
echo "error: not enough disks"
exit 1
fi
echo "Disks detected:"
gum style --padding '1 2' --border double --border-foreground $MAIN_COLOR "${VERBOSESSDOPTS[*]}"
echo "Select $(gum style --foreground $MAIN_COLOR "source disk") from the list above"
sleep 1
SRC=$(gum choose ${SSDOPTS[@]})
echo "Select $(gum style --foreground $MAIN_COLOR "destination disk") from the list above"
sleep 1
DST=$(gum choose ${SSDOPTS[@]})
if [ "$SRC" = "$DST" ]; then
echo "error: disks selected match"
exit 1
fi
}
## Returns the current operating system in lowercase using 'uname -a'
os(){
current_operating_system="$(uname -a | awk '{print $1}' | tr '[:upper:]' '[:lower:]')"
if [ -z "$current_operating_system" ]; then
echo "\$CURRENT_OS couldn't be found"
exit 1
else
echo "$current_operating_system"
fi
}
freeze(){
# NOTE: The computer can respond "write error: Device or resource busy". This usually goes away after waiting a few seconds (10-30 sec), so just try again after a while.
gum confirm "Target disk is frozen, the system is about to hibernate to force the disks to an unfrozen state" && echo -n mem >/sys/power/state # rtcwake -m mem -s 10
if is_frozen "$DST"; then echo "couldn't unfreeze disk" && exit 1; fi
}
main(){
CURRENT_OS=$(os)
dependencies "$CURRENT_OS"
disks "$CURRENT_OS"
is_frozen "$DST" && freeze
wipe $DST
copy $SRC $DST
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment