Files
arch-create-system/usr/bin/arch-create-system
Ralf Zerres 4d5676a4e0 arch-create-system: function update
- resolve prefix in all functions
- check for mountpoint in 'create_fs_streucture'
- fix printf() missing quote

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-02-03 19:15:15 +01:00

1286 lines
32 KiB
Plaintext
Executable File

#/bin/sh
# arch-create-system
# https://github.com/rzerres/arch-create-vm
# Copyright (C) 2019 Ralf Zerres
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# -------------------------------------------------------------------------
progname="${0##*/}"
prog="$0"
version="0.1.0"
# global variables
color=1
config_json="$progname.json"
config_name="null"
dryrun=0
quiet=0
verbose=0
mount_point="/run/mount/target"
label_prefix="BTRFS"
root_password="password"
disk_dev_root="/dev/sda"
disk_label_root="System"
disk_uuid_root=
disk_mount_root="/"
disk_label_cache="System"
disk_mount_cache="/var/cache"
disk_label_log="System"
disk_mount_log="/var/log"
disk_label_data="System"
disk_uuid_data=
disk_mount_data="/home"
disk_label_home="System"
disk_uuid_home=
disk_mount_home="/home"
disk_label_machines="System"
disk_uuid_machines=
disk_mount_machines="/var/lib/machines"
disk_label_uefi="UEFI"
disk_mount_uefi="/boot"
# ascii color
BLUE=
GREEN=
MAGENTA=
RED=
YELLOW=
NO_COLOR=
###
# Functions
###
echo () { printf %s\\n "$*" ; }
check_prerequisites () {
# requested binaries:
command -v chattr >/dev/null 2>&1 || { printf "'chattr' is not installed." && exit 1; }
command -v btrfs >/dev/null 2>&1 || { printf "'btrfs' is not installed." && exit 1; }
command -v fallocate >/dev/null 2>&1 || { printf "'findmnt' is not installed." && exit 1; }
command -v gawk >/dev/null 2>&1 || { printf "'gawk' is not installed." && exit 1; }
command -v mkfs >/dev/null 2>&1 || { printf "'mkfs' is not installed." && exit 1; }
command -v mkswap >/dev/null 2>&1 || { printf "'mkswap' is not installed." && exit 1; }
command -v mount >/dev/null 2>&1 || { printf "'mount' is not installed." && exit 1; }
command -v pacman >/dev/null 2>&1 || { printf "'pacman' is not installed." && exit 1; }
command -v pacstrap >/dev/null 2>&1 || { printf "'pacstrap' is not installed." && exit 1; }
command -v sed >/dev/null 2>&1 || { printf "'sed' is not installed." && exit 1; }
command -v sgdisk >/dev/null 2>&1 || { printf "'sgdisk' is not installed." && exit 1; }
command -v umount >/dev/null 2>&1 || { printf "'umount' is not installed." && exit 1; }
if [ $(id -u) -ne 0 ] ; then printf "$progname: must be run as root\n" ; exit 1 ; fi
}
create_bootloader () {
local cmd
# Needs to be executed in chroot environment (target)
cmd="bootctl install"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
cmd="bootctl status"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
}
create_btrfs () {
local LABEL=$1
local PREFIX=${2:-BTRFS}
local MOUNT_POINT=$3
local cmd
if [ $dryrun -eq 1 ]; then
printf "${BLUE}Would prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" \
"${PREFIX}-${LABEL}"
else
printf "${BLUE}Prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" \
"${PREFIX}-${LABEL}"
fi
if [ -h /dev/disk/by-partlabel/${PREFIX}-${LABEL} ]; then
# create filesystem
cmd="mkfs -t btrfs --force \
--label $PREFIX-$LABEL \
--data single \
--metadata single \
/dev/disk/by-partlabel/${PREFIX}-${LABEL}"
cmd=`echo $cmd | tr -s \"[:blank:]\"`
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run: ${GREEN}'%s'${NO_COLOR}\n" \
"$cmd"
else
#printf "${MAGENTA}Run: ${GREEN}'%s'${NO_COLOR}\n" \
# "$cmd"
eval "$cmd"
fi
fi
}
create_disk () {
local TARGET=$1
local LABEL=$2
local PREFIX=$3
local NR_PARTITIONS=${4:-1}
local PARTITION=1
local cmd
# test if block-device is available
test -b $TARGET || exit 1
printf "${BLUE}Prepare disc ${GREEN}'%s'${NO_COLOR}\n" "$TARGET"
# Cleanup disk and create GPT-Partition
cmd="sgdisk --zap-all $TARGET"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
while expr $PARTITION '<=' $NR_PARTITIONS >>/dev/null; do
if [ $PARTITION '=' 1 ] && [ $NR_PARTITIONS '>' 1 ]; then
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would create ${GREEN}${disk_label_uefi}-Partition '%s'${NO_COLOR}\n" $PARTITION
else
# create UEFI partitions
sgdisk --new=$PARTITION:2048:+512M $TARGET
# assign label
sgdisk --change-name=$PARTITION:$disk_label_uefi $TARGET
# adapt partition-type
sgdisk --typecode=$PARTITION:EF00 $TARGET
fi
PARTITION=$(expr $PARTITION + 1)
continue
else
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would create ${GREEN}${LABEL}-Partition '%s'${NO_COLOR}\n" $PARTITION
else
# create partitions
sgdisk --new=$PARTITION $TARGET
# assign label
sgdisk --change-name=$PARTITION:${PREFIX}-${LABEL} $TARGET
# adapt partition-type
sgdisk --typecode=$PARTITION:8300 $TARGET
fi
PARTITION=$(expr $PARTITION + 1)
fi
done
# reread partition-table
cmd="partprobe $TARGET"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would reread partition table ${GREEN}'%s'${NO_COLOR}\n" $TARGET
else
eval "$cmd"
fi
# dump settings
cmd="sgdisk -p $TARGET"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would created partition table for target ${GREEN}'%s'${NO_COLOR}\n" $TARGET
else
eval "$cmd"
fi
}
create_fs_structure () {
local LABEL=$1
local PREFIX=${2:-BTRFS}
local MOUNT_POINT=$3
local SUBVOL=$4
local cmd
printf "${BLUE}Prepare root filesystem structure with subvol=${GREEN}'%s'${NO_COLOR}\n" \
"$SUBVOL"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would mount target root at ${GREEN}'%s'${NO_COLOR}\n" \
"$SUBVOL"
else
if [ ! -d $MOUNT_POINT ]; then
printf "${BLUE} - create target mount point${NO_COLOR}\n"
cmd="mkdir -p $MOUNT_POINT"
eval "$cmd"
fi
printf "${BLUE} - mount target root${NO_COLOR}\n"
mount_target /dev/disk/by-partlabel/${PREFIX}-${LABEL} $MOUNT_POINT btrfs $SUBVOL
if [ $? -gt 0 ]; then
die "Can't mount root filesystem ${PREFIX}-${LABEL}"
else
findmount -t btrfs
fi
fi
if [ $LABEL=$disk_label_root ]; then
printf "${BLUE}Create target filesystem structure${NO_COLOR}\n"
printf "${BLUE} - Create base subdirs${NO_COLOR}\n"
for subdir in lib; do
cmd="mkdir -p $MOUNT_POINT/var/$subdir"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
done
# Subvolumes storing custom data
printf "${BLUE} - Create btrfs subvolumes${NO_COLOR}\n"
for subvol in boot root home data var/cache var/log var/lib/machines ; do
cmd="btrfs subvolume create $MOUNT_POINT/$subvol"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" \
"$cmd"
else
printf "${MAGENTA}create btrfs subvol ${GREEN}'%s'${MAGENTA} at ${GREEN}'%s'${NO_COLOR}\n" \
"$subvol" "$MOUNT_POINT"
eval "$cmd"
fi
done
create_swapfs $PREFIX $MOUNT_POINT
fi
cmd="umount --recursive $MOUNT_POINT"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
}
create_network_stack () {
# Needs to be executed in chroot environment (target)
systemctl enable systemd-networkd.service
systemctl enable systemd-resolved.service
systemctl enable dropbear.service
rm /etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
}
create_swapfs () {
local PREFIX=$1
local MOUNT_POINT=$2
local RamSize
local SwapSize
local cmd
printf "${BLUE}create_swapfs${NO_COLOR}\n"
# create a subvolume for the swapfs file
cmd="btrfs subvol create $MOUNT_POINT/swap"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
# Disable COW functionality for Subvolume swap
cmd="chattr +C $MOUNT_POINT/swap"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
chmod 0700 $MOUNT_POINT/swap
fi
# prepare the swapfile (size: 2x RAM)
cmd='gawk '/^MemTotal:/{print $2}' /proc/meminfo'
RamSize=$(eval $cmd)
SwapSize=$(expr 2 '*' $RamSize)
SwapFile=$MOUNT_POINT/swap/swapfile
cmd="fallocate --length $SwapSize $SwapFile"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
touch $SwapFile
chmod 0600 $SwapFile
eval "$cmd"
fi
# create the swap filesystem
cmd="mkswap --label $PREFIX-Swap $SwapFile"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
}
create_fat () {
local LABEL=$1
local PREFIX=${2:-BTRFS}
local MOUNT_POINT=$3
local cmd
if [ $dryrun -eq 1 ]; then
printf "${BLUE}Would prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" "${PREFIX}-${LABEL}"
else
printf "${BLUE}Prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" "${PREFIX}-${LABEL}"
fi
if [ -h /dev/disk/by-partlabel/${PREFIX}-${LABEL} ]; then
# create filesystem
cmd="mkfs -t fat \
-F 32 \
-n $LABEL \
/dev/disk/by-partlabel/${PREFIX}-${LABEL}"
cmd="echo $cmd | tr -s \"[:blank:]\""
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
fi
}
create_vfat () {
local LABEL=$1
local PREFIX=${2:-BTRFS}
local MOUNT_POINT=$3
local cmd
if [ $dryrun -eq 1 ]; then
printf "${BLUE}Would prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" "${PREFIX}-${LABEL}"
else
printf "${BLUE}Prepare filesystem ${GREEN}'%s'${NO_COLOR}\n" "${PREFIX}-${LABEL}"
fi
if [ -h /dev/disk/by-partlabel/${PREFIX}-${LABEL} ]; then
# create filesystem
cmd="mkfs -t vfat \
-n $PREFIX-$LABEL \
/dev/disk/by-partlabel/${PREFIX}-${LABEL}"
cmd="echo $cmd | tr -s \"[:blank:]\""
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
fi
}
die () {
error "$@"
if [ $dryrun -eq 1 ]; then
printf "${RED}Error: ${GREEN}'%s'${NO_COLOR}\n" "$@"
fi
exit 1
}
error () {
printf "\n==> ERROR: %s\n" "$@"
} >&2
get_config_names () {
local config_json=${config_file:-$config_json}
local cmd
if [ $verbose -ge 1 ]; then
printf "${BLUE}get_config_names ...${NO_COLOR}\n"
fi
if [ ! -r ${config_json} ]; then
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} config file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \
"config_json"
fi
exit 1
fi
cmd="jq --monochrome-output --ascii-output ' .ConfigEnvironment[] \
| .ConfigName ' \
${config_json}"
config_names=$(eval $cmd)
config_names=$(echo $config_names | sed -e 's/\n//g')
}
get_config_values () {
local config_json=${configfile:-$config_json}
local config_file=${1}
local cmd
if [ $verbose -ge 1 ]; then
printf "${BLUE}get_config_values ...${NO_COLOR}\n"
fi
if [ ! -r ${config_json} ] ; then
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} config file ${GREEN}'%s'${MAGENTA} can\'t be opend!${NO_COLOR}\n" \
"config_json"
fi
return 1
fi
cmd="jq -r --monochrome-output --ascii-output ' \
.ConfigEnvironment[] \
| select( .ConfigName == \"${config_file}\" ) \
| .ConfigName ' \
${config_json}"
config_name=$(eval $cmd)
#config_name=$(echo $config_name | sed -e 's/\n//g')
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Config environment name: ${GREEN}'%s'${NO_COLOR}\n" \
"$config_name"
fi
cmd="jq -r --monochrome-output --ascii-output ' \
.ConfigEnvironment[] \
| select( .ConfigName == \"${config_file}\" ) \
| .ConfigName, ._comment, .ConfigType, .ConfigEnv' \
${config_json}"
values=$(eval $cmd)
values=$(echo $values | sed -e 's/[\n]//g')
cmd="jq -r --monochrome-output --ascii-output ' \
.ConfigEnvironment[] \
| select( .ConfigName == \"${config_file}\" ) \
| .Disk | flatten | length' \
${config_json}"
disks=$(eval $cmd)
cmd="jq -r --monochrome-output --ascii-output ' \
.ConfigEnvironment[] \
| select( .ConfigName == \"${config_file}\" ) \
| to_entries ' \
$config_json"
entries=$(eval $cmd)
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Parsed from: ${GREEN}'%s'${MAGENTA}\n" \
"$config"
printf "${BLUE} '%s': ${GREEN}'%s'${NO_COLOR}%s\n" \
"$values"
printf "${BLUE} Entries: ${GREEN}'%s'${NO_COLOR}%s\n" \
"$entries"
fi
}
get_config_disk_names () {
local config_json=${config_file:-$config_json}
local cmd
if [ $verbose -ge 1 ]; then
printf "${BLUE}get_config_disk_names ...${NO_COLOR}\n"
fi
if test ! -r ${config_json}; then
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} config file ${GREEN}'%s'${MAGENTA} can't be opend!${NO_COLOR}\n" \
"config_json"
fi
return 1
fi
cmd="jq --monochrome-output --ascii-output '.ConfigEnvironment[] \
| select(.ConfigName == \"${config_name}\") \
| .Disk[].DiskName' \
${config_json}"
disk_names=$(eval $cmd)
disk_names=$(echo $disk_names | sed -e 's/["\n]//g')
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Disks in config environment ${GREEN}'%s'${MAGENTA} are: ${NO_COLOR}%s${NO_COLOR}\n" \
"$config_name" "$disk_names"
fi
if [ $verbose -ge 2 ]; then
i=1
for disk in $disk_names; do
printf "${MAGENTA}Disk ${GREEN}'%d'${MAGENTA} with name ${GREEN}'%s'${NO_COLOR}\n" \
"$i" "$disk"
i=$(expr $i + 1)
done
fi
}
mount_target_filesystems () {
local LABEL=$1
local MOUNT_POINT=$2
printf "${BLUE}mount_target_filesystems${NO_COLOR}\n"
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_root} $MOUNT_POINT/ btrfs root
if [ $? -gt 0 ]; then
die "Cant mount root filesystem $disk_label_root"
else
findmount -t btrfs
fi
for subdir in boot data home swap var/log var/cache var/lib/machines; do
if [ ! -d $MOUNT_POINT/$subdir ]; then
mkdir -p $MOUNT_POINT/$subdir
fi
done
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_uefi} $MOUNT_POINT/boot fat
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_data} $MOUNT_POINT/data btrfs data
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_root} $MOUNT_POINT/var/cache btrfs var/cache
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_machines} $MOUNT_POINT/var/lib/machines btrfs var/lib/machines
mount_target /dev/disk/by-partlabel/${label_prefix}-${disk_label_root} $MOUNT_POINT/var/log btrfs var/log
}
install_target () {
local MOUNT_POINT=$1
local cmd
printf "${BLUE}Install target ${GREEN}'%s' -> '%s'${NO_COLOR}\n" \
"$MOUNT_POINT"
# change new root to target
cmd="arch-chroot $MOUNT_POINT arch-create-system install_target"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
}
install_target_packages () {
local MOUNT_POINT=$1
local cmd
printf "${BLUE}Install target packages ${GREEN}'%s' -> '%s'${NO_COLOR}\n" \
"$LABEL" "$MOUNT_POINT"
# Find fast mirror
# TODO: Aachen
#sed '1 i \
#Server = http://ftp.halifax.rwth-aachen.de/archlinux/$repo/os/$arch' \
# /etc/pacman.d/mirrorlist
# Update available packages
cmd="pacman -Sy"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
# Basic packages
# "TODO: systemd-firstboot"
cmd="pacstrap $MOUNT_POINT arch-install-scripts base btrfs-progs gdisk intel-ucode \
linux linux-firmware dosfstools iptables-nft man sudo"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
# Hyper-V packages
cmd="pacstrap $MOUNT_POINT hyperv"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
# SSH-Daemon, FileManager and Editor
cmd="pacstrap $MOUNT_POINT dropbear vim mc bash-completion"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
eval "$cmd"
fi
cmd="cp $prog $MOUNT_POINT/usr/bin/"
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would run ${GREEN}'%s'${NO_COLOR}\n" "$cmd"
else
if [ -x $install_script ]; then
eval "$cmd"
else
die "Can't find '$progname' in default path\n"
fi
fi
}
install () {
printf "${BLUE}Install target${NO_COLOR}\n" "$TARGET"
# Needs to be executed in chroot environment (target)
prepare_mount_units
prepare_network_units
create_network_stack
prepare_bootloader
create_bootloader
prepare_locale $label_prefix
#systemctl enable hvkvpdaemon.service
#systemctl enable hvvssdaemon.service
# if you dislike systemd mount units uncomment next line
#genfstab / >/etc/fstab
}
mount_target () {
local DEV_NAME=$1
local MOUNT_POINT=$2
local FS_TYPE=$3
local FS_SUBVOL=$4
local cmd
if [ ! -h $DEV_NAME ]; then
printf "${RED}Error: ${NO_COLOR}Can't access given device ${GREEN}'%s'${NO_COLOR}\n" \
"$DEV_NAME"
return 1
fi
case $FS_TYPE in
"btrfs")
cmd="mount -t $FS_TYPE \
-o subvol=$FS_SUBVOL \
$DEV_NAME \
$MOUNT_POINT"
printf "${BLUE}mounting subvol ${GREEN}'%s' ${BLUE}of ${GREEN}'%s' ${BLUE}at ${GREEN}'%s'${NO_COLOR}\n" \
"$FS_SUBVOL" "$DEV_NAME" "$MOUNT_POINT"
;;
"fat")
# we are mounting a vfat for gain long filenames
cmd="mount -t vfat \
$DEV_NAME \
$MOUNT_POINT"
printf "${BLUE}mounting ${GREEN}'%s' ${BLUE}at ${GREEN}'%s'${NO_COLOR}\n" \
"$DEV_NAME" "$MOUNT_POINT"
;;
"vfat")
cmd="mount -t $FS_TYPE \
$DEV_NAME \
$MOUNT_POINT"
printf "${BLUE}mounting ${GREEN}'%s' ${BLUE}at ${GREEN}'%s'${NO_COLOR}\n" \
"$DEV_NAME" "$MOUNT_POINT"
;;
esac
cmd="echo $cmd | tr -s "[:blank:]""
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would mount ${GREEN}'%s'${MAGENTA} of ${GREEN}'%s'${MAGENTA} at ${GREEN}'%s'${NO_COLOR}\n" \
"$FS_SUBVOL" "$DEV_NAME" "$MOUNT_POINT"
else
eval "$cmd"
if [ $? -gt 0 ]; then
die "Cant mount filesystem $DEV_NAME"
fi
fi
}
parse_config_file () {
# printf "${BLUE}parse_config_file ...${NO_COLOR}\n"
# check if config is accessible in given path
for config in \
${config_json} \
./${config_json} \
${HOME}/${config_json} \
/etc/${progname}/${config_json}
do
if [ -r $config ] ; then
config_json=$config
break
else
config_json="NONE"
fi
done
if [ ${config_json} = "NONE" ]; then
# if [ $verbose -ge 1 ]; then
# printf "${BLUE}No config file!${NO_COLOR}\n"
# fi
return
fi
#if [ ${#config_name} -eq 0 ]; then
# get_config_names
#fi
}
parse_params () {
#printf "\n${BLUE}Parse arguments...${NO_COLOR}\n"
# Evaluate given call parameters
i=0
while [ $# -gt 0 ]; do
key="$1"
case $key in
-h | --help | \-\? | --usage)
# Call usage() function.
usage
;;
--color)
color=1
shift 1
;;
--config-file)
config_json="$2"
shift 2
;;
-c | --config)
shift 1
config_params=${*}
config_params="${config_params%% -?[a-z]*}"
params=$*
set -- $config_params
count=$#
( test $count -lt 1 || test ${#config_params} -eq 0 ) \
&& printf "missing argument: config_name\n" \
&& exit 1
test $count -ge 1 && config_name="$1"
set -- $params
shift $count
;;
--get-config-names)
shift 1
cmd=get-config-names
;;
--get-config-values)
shift 1
cmd=get-config-values
;;
--get-config-disk-names)
shift 1
config_params=${*}
config_params="${config_params%% -?[a-z]*}"
params=$*
set -- "$config_params"
count=$#
( test $count -lt 1 || test ${#config_params} -eq 0 ) \
&& printf "missing argument: config_name\n" \
&& exit 1
test $count -ge 1 && config_name="$1"
#test $count -ge 2 && disk_name="$2"
set -- $params
shift $count
cmd=get-config-disk-names
;;
--disk-label-data)
disk_label_data="$2"
shift 2
;;
--disk-label-machines)
disk_label_machines="$2"
shift 2
;;
--disk-label-root)
disk_label_root="$2"
shift 2
;;
--disk-data)
disk_dev_data="$2"
shift 2
;;
--disk-machines)
disk_dev_machines="$2"
shift 2
;;
--disk-root)
disk_dev_root="$2"
shift 2
;;
--dry-run|--dryrun)
dryrun=1
shift 1
;;
-i|--install)
shift 1
cmd=install
;;
--install-target)
shift 1
cmd=install-target
;;
--mountpoint|--MOUNTPOINT)
mount_point="$2"
shift 2
;;
--label-prefix)
label_prefix="$2"
shift 2
;;
-v|--verbose)
verbose=$(expr $verbose '+' 1)
shift 1
;;
--version)
printf "%s v%s\n" "$progname" "$version"
exit 0
;;
--color=*)
case ${1#*=} in
yes | Yes | True | true)
color=1;
;;
*)
;;
esac
shift
;;
--config-file=*)
config_json=${1#*=}
shift
;;
-c | --config=*)
config_name=${1#*=}
shift
;;
--disk_label_data=*|--DISK_LABEL_DATA=*)
disk_label_data=${1#*=}
shift
;;
--disk_label_machines=*|--DISK_LABEL_MACHINES=*)
disk_label_machines=${1#*=}
shift
;;
--disk_label_root=*|--DISK_LABEL_ROOT=*)
disk_label_root=${1#*=}
shift
;;
--disk_data=*|--DISK_DATA=*)
disk_dev_data=${1#*=}
shift
;;
--disk_machines=*|--DISK_MACHINES=*)
disk_dev_machines=${1#*=}
shift
;;
--disk_root=*|--DISK_ROOT=*)
disk_dev_root=${1#*=}
shift
;;
--label-prefix=*)
label_prefix=${1#*=}
shift
;;
--mountpoint=*)
mount_point=${1#*=}
shift
;;
--v=* | --verbose=*)
verbose=${1#*=}
shift
;;
--) # End of all options
shift
break
;;
-*)
printf "WARN: Unknown option \(ignored\): $1" >&2
die "Unknown option"
;;
*)
printf "Unknown option: %s\nRun '%s -h' for valid options.\n" $key $progname
die "Unknown option"
;;
esac
done
if [ "$color" ]; then
# ascii color
BLUE='\033[0;34m'
GREEN='\033[0;32m'
MAGENTA='\033[0;35m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
NO_COLOR='\033[0m'
fi
}
prepare_bootloader () {
# check for accessible uefi partition
if [ $dryrun -eq 1 ]; then
printf "${MAGENTA}Would update bootloader config'${NO_COLOR}\n"
else
ls /sys/firmware/efi/efivars > /dev/null
if [ $? -eq 0 ]; then
printf "UEFI Environmint is ok'\n"
mkdir -p /boot/loader/entries
printf '%s\n' \
"default arch" \
"timeout 3" \
"editor yes" \
"console-mode max" \
> /boot/loader/loader.conf
printf '%s\n' \
"title Arch Linux \(Mainline\)" \
"linux /vmlinuz-linux" \
"initrd /intel-ucode.img" \
"initrd /initramfs-linux.img" \
"options rootflags=subvol=root root=LABEL=${label_prefix}-${disk_label_root}" \
> /boot/loader/entries/arch.conf
bootctl status
fi
fi
}
prepare_locale () {
local LABEL=$1
hostnamectl set-hostname $LABEL-lin01
localectl set-keymap de-latin1-nodeadkeys
#systemd-firstboot --root-password-file= \
#systemd-firstboot --root-password=$root_password \
# --hostname=$LABEL-lin01 \
# --timezone="Europe/Berlin" \
# --locale="de_DE.UTF8" \
# --keymap=de-latin1
}
prepare_mount_units () {
printf '%s\n' \
"[Unit]" \
"Description=root-Snapshots \(btrfs\)" \
"Documentation=man:systemd.mount\(5\)" \
"DefaultDependencies=yes" \
"Before=local-fs.target" \
"" \
"[Mount]" \
"#What=LABLE=$disk_label_root" \
"What=UUID=$disk_uuid_root" \
"Where=/.snapshots" \
"Type=btrfs" \
"Options=defaults,discard,space_cache,autodefrag,compress=lzo,subvol=root_snapshots" \
"" \
"[Install]" \
"WantedBy=multi-user.target" \
> /etc/systemd/system/\x2esnapshots.mount
printf '%s\n' \
"[Unit]" \
"Description=Caching \(btrfs\)" \
"Documentation=man:systemd.mount\(5\)" \
"DefaultDependencies=yes" \
"Before=local-fs.target" \
"" \
"[Mount]" \
"#What=LABLE=$disk_label_root" \
"What=UUID=$disk_uuid_root" \
"Where=/var/cache" \
"Type=btrfs" \
"Options=defaults,discard,space_cache,autodefrag,compress=lzo,subvol=var/cache" \
"" \
"[Install]" \
"WantedBy=multi-user.target" \
> /etc/systemd/system/var-cache.mount
printf '%s\n' \
"[Unit]" \
"Description=data \(btrfs\)" \
"Documentation=man:systemd.mount\(5\)" \
"DefaultDependencies=yes" \
"Before=local-fs.target" \
"" \
"[Mount]" \
"#What=LABLE=$disk_label_data" \
"What=UUID=$disk_uuid_data" \
"Where=/data" \
"Type=btrfs" \
"Options=defaults,discard,space_cache,autodefrag,compress=lzo,subvol=data" \
"" \
"[Install]" \
"WantedBy=multi-user.target" \
> /etc/systemd/system/data.mount
printf '%s\n' \
"[Unit]" \
"Description=Home \(btrfs\)" \
"Documentation=man:systemd.mount\(5\)" \
"DefaultDependencies=yes" \
"Before=local-fs.target" \
"" \
"[Mount]" \
"#What=LABLE=$disk_label_home" \
"What=UUID=$disk_uuid_home" \
"Where=/home" \
"Type=btrfs" \
"Options=defaults,discard,space_cache,autodefrag,compress=lzo,subvol=home" \
"" \
"[Install]" \
"WantedBy=multi-user.target" \
> /etc/systemd/system/home.mount
printf '%s\n' \
"[Unit]" \
"Description=Logging \(btrfs\)" \
"Documentation=man:systemd.mount\(5\)" \
"DefaultDependencies=yes" \
"Before=local-fs.target" \
"" \
"[Mount]" \
"#What=LABLE=$disk_label_log" \
"What=UUID=$disk_uuid_log" \
"Where=/var/log" \
"Type=btrfs" \
"Options=defaults,discard,space_cache,autodefrag,compress=lzo,subvol=var/log" \
"" \
"[Install]" \
"WantedBy=multi-user.target" \
> /etc/systemd/system/var-log.mount
}
prepare_network_units () {
printf '%s\n' \
"[Match]" \
"Name=eth0" \
"" \
"[Network]" \
"Description=Slave Bridge-Interface" \
"Bridge=bridge-lan" \
> /etc/systemd/network/70-eth0-bridge-slave.netdev
printf '%s\n' \
"[Match]" \
"#Host=Lin01" \
"#Architecture=x86_64" \
"" \
"[NetDev]" \
"Description=Bridge for Containers" \
"Name=bridge-lan" \
"Kind=bridge" \
> /etc/systemd/network/80-bridge-lan.netdev
printf '%s\n' \
"[Match]" \
"Name=bridge-lan" \
"Driver=bridge" \
"" \
"[Network]" \
"Description=Bridge for Containers" \
"IPForward=yes" \
"IPMasquerqade=yes" \
"LinkLocalAddressisng=yes" \
"IPv6AcceptRA=yes" \
"IgnoreCarrierLoss=yes" \
> /etc/systemd/network/80-bridge-lan.network
}
prepare_target_disks () {
printf "${BLUE}prepare_target_disks${NO_COLOR}\n"
if [ ${#disk_dev_root} -gt 1 ]; then
create_disk ${disk_dev_root} $disk_label_root $label_prefix 2
fi
if [ ${#disk_dev_machines} -gt 1 ]; then
create_disk ${disk_dev_machines} $disk_label_machines $label_prefix
fi
if [ ${#disk_dev_data} -gt 1 ]; then
create_disk ${disk_dev_data} $disk_label_data $label_prefix
fi
}
prepare_target_filesystems () {
printf "${BLUE}prepare_target_filesystems${NO_COLOR}\n"
if [ ${#disk_dev_root} -gt 1 ]; then
create_fat $disk_label_uefi $label_prefix $mount_point
create_btrfs $disk_label_root $label_prefix $mount_point
fi
if [ ${#disk_dev_machines} -gt 1 ]; then
create_btrfs $disk_label_machines $label_prefix $mount_point
fi
if [ ${#disk_dev_data} -gt 1 ]; then
create_btrfs $disk_label_data $label_prefix $mount_point
fi
create_fs_structure $disk_label_root $label_prefix $mount_point /
if [ $? -gt 0 ]; then
die "Can't create filesystem structure on" "$mount_point"
fi
}
show_config () {
local options
if [ $verbose -ge 1 ]; then
printf "${BLUE}$progname (runtime arguments) ...${NO_COLOR}\n"
i=0
printf " Config File: '%s'\n" "$config_json"
printf " Config: '%s'\n" "$config_name"
printf " Mount Point: '%s'\n" "$mount_point"
printf " Label Prefix: '%s'\n" "$label_prefix"
printf " Target OS: '%s'\n" "$disk_dev_root"
printf " Label Root: '%s'\n" "$disk_label_root"
printf " Label UEFI: '%s'\n" "$disk_label_uefi"
if [ ${#disk_dev_machines} -gt 1 ]; then
printf " Target Machines: '%s'\n" "$disk_dev_machines"
printf " Label Machines: '%s'\n" "$disk_label_machines"
fi
if [ ${#disk_dev_data} -gt 1 ]; then
printf " Target Data: '%s'\n" "$disk_dev_data"
printf " Label DATA: '%s'\n" "$disk_label_data"
fi
options="verbose_level=$verbose"
if [ $dryrun -ge 1 ]; then options="${options} dryrun=true"; fi
if [ $color -ge 1 ]; then options="${options} color=true"; fi
printf "Options: '%s'\n\n" "${options}"
fi
}
usage () {
printf '%s\n' \
"$progname $version" \
"Usage: $progname [options]" \
"Options:" \
" -c, --config-file Use given given file" \
" --config use pre-defined values from given config name" \
" --color Enable colored output messages" \
" --dry-run perform a trial run (no changes are written)" \
" --get-config-names show pre-defined configs from config-file" \
" -i, --install prepare chroot environemnt and install on target" \
" --install-target install on target" \
" --mountpoint Specify MountPoint preparing targets OS-Disk" \
" --label-prefix <prefix> Specify the partion label prefix" \
" -t, --disk-dev-root <target> Specify the blockdevice for the target OS (e.g /dev/sda)" \
" --disk-dev-machines <target> Specify the blockdevice for containers/machines (e.g /dev/sdb)" \
" --disk-dev-data <target> Specify the blockdevice to store data (e.g /dev/sdc)" \
" -v, --verbose Be verbose on what's going on (min: --verbose=0, max: --verbose=3)" \
" --version show program version"
exit 0
}
###
# Main
###
# can't be ported to dash \(ERR is not supported\)
#trap 'traperror ${LINENO} $? "$BASH_COMMAND" $BASH_LINENO "${FUNCNAME[@]}"' ERR
trap trapkill TERM INT
check_prerequisites
# validate commandline options, set resonable defaults
parse_params $@
# commandline options handled with priority
parse_config_file
if [ ${config_name} != "NONE" ]; then
get_config_values $config_name
fi
case $cmd in
get-config-values)
#get_config_name 'HyperV-SingleDisk'
get_config_values $config_name
if test $? -gt 0; then
exit 1
#else
# if [ $quiet -eq 0 ]; then
# printf "${MAGENTA}Config Environment: ${GREEN}'%s'${NO_COLOR}\n" \
# "${config_name}"
# fi
fi
;;
get-config-names)
get_config_names
if [ $? -gt 0 ]; then
exit 1
else
if [ $quiet -eq 0 ]; then
printf "${MAGENTA}Config environment names: ${GREEN}'%s'${NO_COLOR}\n" \
"${config_names}"
fi
exit 0
fi
;;
install)
if [ $quiet -eq 0 ]; then
show_config
fi
prepare_target_disks
prepare_target_filesystems
if [ $? -gt 0 ]; then
exit 1
fi
mount_target_filesystems $disk_label_root $mount_point
exit 0
install_target_packages $mount_point
install_target $mount_point
if [ $? -eq 0 ]; then
printf "${MAGENTA}Installation succeeded for target mount-point: ${GREEN}'%s'${NO_COLOR}\n" \
"${TARGET}"
printf "You are save to reboot now.\n"
else
printf "${MAGENTA}Installation failed for target mount-point: ${GREEN}'%s'${NO_COLOR}\n" \
"${TARGET}"
exit 1
fi
;;
install-target)
install_target $mount_point
if [ $? -eq 0 ]; then
printf "${MAGENTA}Installation succeeded for target mount-point: ${GREEN}'%s'${NO_COLOR}\n" \
"${TARGET}"
printf "You are save to reboot now.\n"
else
printf "${MAGENTA}Installation failed for target mount-point: ${GREEN}'%s'${NO_COLOR}\n" \
"${TARGET}"
exit 1
fi
;;
*)
show_config
;;
esac