So I decided to try to write a simple replacement that would not try to feature creep. It's only a start but after one day of work, I thought that I should probably share it early so others can comment on it. It's also shell based but I decided against "copying the good parts" and for rewriting an installer from scratch. Well, it bears the name "installer", but to be honest, it does currently not do any installations. On the contrary - I started out with destroying existing installations. Sure thing: The cleanest solution is definitely to spill /dev/zero all over the drive. But people probably don't want to wait that long.
The only functionality that it currently has is to wipe a drive clean of previous installations. I successfully wiped installations with old BSD-style disklables in "dangerously dedicated" mode as well as ordinary MBR installations. I then tried to create some crazy partitioning combinations and it all worked out well. I also implemented wiping away extended MBR containers and successfully cleaned a Linux test installation from my drive (this being the first time this year that I installed Linux on real hardware - and it lasted only minutes ).
Handling GPT (and corrupted GPT), too, is next. Currently the installer does not know about ZFS, GELI, gmirror or any other GEOM classes. Those need to be handled as well. And then it has to be tested against OpenBSD, NetBSD, DragonFly, Illumos - and Windows. The newest Windows version that I own being XP, I hope that somebody steps up to test that for me. If not I'll have to see if I can get Win7 or something from a friend.
Once that works, the focus will shift from destroying partitions to creating and populating them with filesystems. And then the actual installation. But I think properly destroying things is interesting enough for now!
Here's the very first "write only" version of the code (no license, no refactoring/polishing whatsoever - I just wrote it and added to it when needed):
Code: Select all
#!/bin/sh
###################
# INTERNAL VARS #
###################
# !Do not change!
MYNAME=${0}
HAVE_LOG=0
RECURSIVE=0
#############
# GLOBALS #
#############
TMP_DIR="/tmp/.gbi"
LOG_FILE="/tmp/.gbi/installer.log"
CFG_FILE="./gbsd.cfg"
VERBOSITY=1 # 0 = quiet, 1 = info, 2 = debug
LOGGING=0
###############
# FUNCTIONS #
###############
show_help() {
echo -e "\nUsage: ${MYNAME} COMMAND [FLAGS]\n"
echo -e "COMMANDS:\n"
echo "help Display this help text"
echo "wipe Wipe disk of existing installation(s)"
echo -e "\nFLAGS:\n"
echo "-l Enable logging"
echo "-q Quiet mode (no output)"
echo -e "-v Be verbose (use twice for debug level verbosity)\n"
}
die() {
echo -e "\n${1}\n"
[ ${HAVE_LOG} -eq 1 ] && echo -e "\n${1}\n" >> ${LOG_FILE}
exit 1
}
print_msg() {
echo -e "${1}"
[ ${HAVE_LOG} -eq 1 ] && echo -e "${1}" >> ${LOG_FILE}
}
check_arg_count_valid() {
[ ${1} -lt 1 ] && die "ERROR: No arguments given!\nUse \"${MYNAME} help\" for help."
[ ${1} -gt 4 ] && die "ERROR: Too many arguments given!\nUse \"${MYNAME} help\" for help."
}
check_cmd_valid() {
case ${1} in
"wipe"|"WIPE")
;;
*)
die "ERROR: Unknown command \"${1}\"!\nUse \"${MYNAME} help\" for help."
;;
esac
}
parse_args() {
[ ${1} == "-l" -o ${1} == "-L" ] && LOGGING=1 && return
if [ ${1} == "-q" -o ${1} == "-Q" ] ; then
[ ${VERBOSITY} -eq 1 ] && VERBOSITY=0 && return
die "ERROR: Cannot be verbose and quiet at the same time!"
fi
if [ ${1} == "-v" -o ${1} == "-V" ] ; then
[ ${VERBOSITY} -eq 0 ] && VERBOSITY=1 && return
[ ${VERBOSITY} -eq 1 ] && VERBOSITY=2
return
fi
die "ERROR: Unknown argument \"${1}\"!\nUse \"${MYNAME} help\" for help."
}
prepare_tmp_dir() {
mkdir -p ${TMP_DIR}
[ -d ${TMP_DIR} ] && return
die "ERROR: Could not create directory \"${TMP_DIR}\"! Aborting..."
}
prepare_log() {
echo -n > ${LOG_FILE}
[ -f ${LOG_FILE} -a ! -s ${LOG_FILE} ] && HAVE_LOG=1 && return
die "ERROR: Logging requested but cannot write to logfile \"${LOG_FILE}\"! Aborting..."
}
check_priv() {
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Checking for sufficient privileges..."
[ `id -u` -eq 0 ] && return
die "ERROR: Root privileges needed! Cannot run as user \"`id -un`\". Aborting..."
}
check_exists_cfg_file() {
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Checking if config file exists..."
[ -f ${CFG_FILE} ] && return
die "ERROR: Config file (\"${CFG_FILE}\") not found! Aborting..."
}
check_vars_set() {
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Check if all needed cfg variables are set..."
[ -z ${disk} ] && die "ERROR: \"disk\" not set in ${CFG_FILE}! Aborting..."
}
check_exists_disk() {
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Check if disk \"${disk}\" exists..."
for _DRIVE in `sysctl -n kern.disks` ; do
[ ${disk} == ${_DRIVE} ] && return
done
die "ERROR: Disk \"${disk}\" does not exist! Aborting..."
}
scan_partition_table() {
if [ `gpart show ${1} 2>&1 | head -n 1 | grep -c "No such geom:"` -gt 0 ] ; then
PARTITION_TYPE="None"
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Disk ${1} seems to not be partitioned."
return
fi
PARTITION_TYPE="`gpart show ${1} | head -n 1 | cut -d"(" -f1 | rev | cut -c3- | cut -d" " -f1 | rev`"
case ${PARTITION_TYPE} in
"BSD")
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Disk ${1} has a BSD partition scheme."
;;
"EBR")
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Disk ${1} is an extended MBR container."
;;
"GPT")
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Disk ${1} has a GPT partition scheme."
;;
"MBR")
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: Disk ${1} has an MBR partition scheme."
;;
*)
die "ERROR: Unknown partition scheme \"${PARTITION_TYPE}\" found on ${1}! Cowardly aborting now..."
;;
esac
}
backup_partition_table() {
gpart backup ${1} > ${TMP_DIR}/partition_table.${1}
[ -f ${TMP_DIR}/partition_table.${1} ] || die "ERROR: Backing up existing partition table failed!"
[ ${VERBOSITY} -gt 1 -a ${HAVE_LOG} -gt 0 ] && echo -e "----------\n${1}\n" >> ${LOG_FILE} && cat ${TMP_DIR}/partition_table.${1} >> ${LOG_FILE} && echo "----------" >> ${LOG_FILE}
}
delete_partition() {
[ ${VERBOSITY} -gt 0 ] && print_msg "INFO: Deleting partition/slice #${2} on ${1}..."
gpart delete -i ${2} ${1} > /dev/null 2>&1
[ ${?} -eq 0 ] && return
die "ERROR: Failed to delete partition #${2} on ${1}!\n\nYou may want to restore the partition table backup e.g. by executing:\n\n cat ${TMP_DIR}/partition_table.${1} | gpart restore -F ${1}\n\nAnd don't forget that you might need to restore the bootcode, too!"
}
destroy_disklabel() {
if [ ${_TMP} -gt 1 ] ; then # at least one partition present (-gt is correct!)
for n in `seq 2 ${_TMP}` ; do
delete_partition ${1} `sed -n ${n}p ${TMP_DIR}/partition_table.${1} | cut -d" " -f1`
done
fi
[ ${VERBOSITY} -gt 0 ] && print_msg "INFO: Destroying disklabel on ${1}..."
gpart destroy ${1} > /dev/null 2>&1
[ ${?} -eq 0 ] || die "ERROR: Could not destroy BSD disklabel ${1}! Aborting..."
}
destroy_ebr() {
if [ ${_TMP} -gt 1 ]; then # at least one partition present (-gt is correct!)
for n in `seq 2 ${_TMP}` ; do
delete_partition ${1} `sed -n ${n}p ${TMP_DIR}/partition_table.${1} | cut -d" " -f1`
done
fi
[ ${VERBOSITY} -gt 0 ] && print_msg "INFO: Destroying extended MBR container on ${1}..."
gpart destroy ${1} > /dev/null 2>&1
[ ${?} -eq 0 ] || die "ERROR: Could not destroy EBR container ${1}! Aborting..."
}
destroy_mbr() {
if [ ${_TMP} -gt 1 ] ; then # at least one partition present (-gt is correct!)
for n in `seq 2 ${_TMP}` ; do
_LABEL_INSIDE_MBR="${1}s`sed -n ${n}p ${TMP_DIR}/partition_table.${1} | cut -d" " -f1`"
scan_partition_table ${_LABEL_INSIDE_MBR}
[ ${PARTITION_TYPE} != "None" ] && backup_partition_table ${_LABEL_INSIDE_MBR}
[ ${PARTITION_TYPE} != "None" ] && delete_partitions ${_LABEL_INSIDE_MBR}
scan_partition_table ${1}
done
fi
_TMP="`cat ${TMP_DIR}/partition_table.${1} | wc -l`" # lines = partitions + 1
for n in `seq 2 ${_TMP}` ; do
delete_partition ${1} `sed -n ${n}p ${TMP_DIR}/partition_table.${1} | cut -d" " -f1`
done
[ ${VERBOSITY} -gt 0 ] && print_msg "INFO: Destroying MBR on ${1}..."
gpart destroy ${1} > /dev/null 2>&1
[ ${?} -eq 0 ] || die "ERROR: Could not destroy MBR on ${1}! Aborting..."
}
delete_partitions() {
_TMP="`cat ${TMP_DIR}/partition_table.${1} | wc -l`" # lines = partitions + 1
[ ${VERBOSITY} -gt 1 ] && print_msg "DEBUG: `echo ${_TMP} | xargs expr -- -1 +` ${PARTITION_TYPE} partition(s) found on ${1}."
case ${PARTITION_TYPE} in
"BSD")
destroy_disklabel ${1}
;;
"EBR")
;;
"MBR")
destroy_mbr ${1}
;;
*)
die "Not yet supported!"
;;
esac
}
nuke_disk() {
scan_partition_table ${disk}
[ ${PARTITION_TYPE} == "None" -a ${VERBOSITY} -gt 1 ] && print_msg "INFO: ${disk} not partitioned." && return
[ ${PARTITION_TYPE} != "None" ] && backup_partition_table ${disk}
[ ${PARTITION_TYPE} != "None" ] && delete_partitions ${disk}
}
##########
# MAIN #
##########
echo
check_arg_count_valid ${#}
[ ${1} == "help" -o ${1} == "HELP" ] && show_help && exit 0
check_cmd_valid ${1}
[ ${#} -gt 1 ] && parse_args ${2}
[ ${#} -gt 2 ] && parse_args ${3}
[ ${#} -gt 3 ] && parse_args ${4}
prepare_tmp_dir
[ ${LOGGING} -gt 0 ] && prepare_log
check_priv
check_exists_cfg_file
. ${CFG_FILE}
check_vars_set
check_exists_disk
nuke_disk
echo