- server/ profile and build-iso.sh [desktop|server]; make iso-server, make qemu-server, run-qemu.sh --serial; login on tty and ttyS0 - das-server: SSH, chrony, NetworkManager/dhcpcd, Docker with Compose, nftables (closed except SSH, own table), fail2ban reading socklog, sshd without root passwords, Docker log rotation, tools - das-server-install: dialog installer in Ukrainian and English, or unattended with --config; GPT + Btrfs subvolumes, GRUB for UEFI and BIOS, serial console, SSH key with optional keys-only logins - das-install-common: mount-source and postinstall shared with Calamares - dsv: runit service manager (list, enable, disable, start, stop, restart, status, logs) with bash completion, also on the desktop - das-update split into a subpackage without GTK; the window and tray icon move to /usr/libexec/das/das-update-gui - das-snapper-setup enables dbus, which snapper needs - TODO: ISO size review with measured numbers and options Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
583 lines
20 KiB
Bash
Executable File
583 lines
20 KiB
Bash
Executable File
#!/bin/bash
|
|
# das-server-install - console installer for Das OS Server.
|
|
#
|
|
# Usage: das-server-install [--config FILE]
|
|
#
|
|
# Asks its questions with dialog and installs the live image on a disk:
|
|
# GPT with an EFI or BIOS boot partition and Btrfs with the same subvolumes
|
|
# as the desktop installer, so snapshots and rollback work the same way.
|
|
# With --config it asks nothing and takes the answers from FILE (see
|
|
# /usr/share/examples/das-server-installer/install.conf).
|
|
set -Eeuo pipefail
|
|
|
|
log=/tmp/das-server-install.log
|
|
target=/mnt/das-target
|
|
source_root=/run/das-installer/root
|
|
libexec=/usr/libexec/das/installer
|
|
|
|
# Answers; a config file sets the same variables
|
|
DISK=""
|
|
HOST_NAME=dasos-server
|
|
USER_NAME=""
|
|
USER_PASSWORD=""
|
|
SSH_KEY=""
|
|
SSH_PASSWORDS=yes
|
|
TIMEZONE=Europe/Kyiv
|
|
KEYMAP=us
|
|
LOCALE=en_US.UTF-8
|
|
NETWORK=dhcpcd
|
|
ERASE_DISK=no
|
|
|
|
lang=en
|
|
[[ ${LANG:-} == uk* ]] && lang=uk
|
|
|
|
# t KEY: the message in the installer's language
|
|
t() {
|
|
local key=$1
|
|
if [[ $lang == uk ]]; then
|
|
case $key in
|
|
title) echo "Встановлення Das OS Server" ;;
|
|
lang) echo "Мова встановлення:" ;;
|
|
disk) echo "Диск для встановлення. Усі дані на ньому буде знищено." ;;
|
|
nodisk) echo "Не знайдено дисків для встановлення." ;;
|
|
host) echo "Ім'я сервера (латинські літери, цифри, дефіс):" ;;
|
|
badhost) echo "Неправильне ім'я сервера." ;;
|
|
user) echo "Ім'я користувача-адміністратора (вхід через sudo, root вимкнено):" ;;
|
|
baduser) echo "Неправильне ім'я: малі латинські літери, цифри, _ та -, не root." ;;
|
|
pass) echo "Пароль користувача:" ;;
|
|
pass2) echo "Ще раз той самий пароль:" ;;
|
|
badpass) echo "Паролі порожні або не збігаються." ;;
|
|
key) echo "Публічний ключ SSH для входу (вставте рядок ssh-ed25519 ... або залиште порожнім):" ;;
|
|
badkey) echo "Це не схоже на публічний ключ SSH." ;;
|
|
nopass) echo "Заборонити вхід по SSH за паролем (лише за ключем)?" ;;
|
|
tz) echo "Часовий пояс (наприклад, Europe/Kyiv, UTC):" ;;
|
|
badtz) echo "Немає такого часового поясу." ;;
|
|
keymap) echo "Розкладка клавіатури в консолі:" ;;
|
|
locale) echo "Мова системи:" ;;
|
|
net) echo "Мережа:" ;;
|
|
net_dhcpcd) echo "dhcpcd: DHCP на всіх інтерфейсах (просто)" ;;
|
|
net_nm) echo "NetworkManager: налаштування через nmtui (Wi-Fi, статичні адреси)" ;;
|
|
summary) echo "Перевірте налаштування:" ;;
|
|
erase) echo "УСІ ДАНІ НА ДИСКУ %s БУДЕ ЗНИЩЕНО. Продовжити?" ;;
|
|
cancel) echo "Встановлення скасовано." ;;
|
|
s_source) echo "Підготовка образу системи..." ;;
|
|
s_disk) echo "Розмітка диска %s..." ;;
|
|
s_copy) echo "Копіювання системи (кілька хвилин)..." ;;
|
|
s_config) echo "Налаштування системи..." ;;
|
|
s_post) echo "Локалі, знімки, initramfs (кілька хвилин)..." ;;
|
|
s_boot) echo "Встановлення завантажувача..." ;;
|
|
finished) echo "Das OS Server встановлено. Вийміть інсталяційний носій і перезавантажте сервер.\n\nВхід: %s, далі sudo. Служби: dsv list. Оновлення: das-update." ;;
|
|
reboot) echo "Перезавантажити зараз?" ;;
|
|
failed) echo "Помилка встановлення. Журнал: %s" ;;
|
|
root) echo "Інсталятор треба запускати від root (sudo das-server-install)." ;;
|
|
yes) echo "так" ;;
|
|
no) echo "ні" ;;
|
|
esac
|
|
else
|
|
case $key in
|
|
title) echo "Das OS Server installation" ;;
|
|
lang) echo "Installation language:" ;;
|
|
disk) echo "Disk to install to. All data on it will be erased." ;;
|
|
nodisk) echo "No disks to install to were found." ;;
|
|
host) echo "Server name (Latin letters, digits, hyphen):" ;;
|
|
badhost) echo "Invalid server name." ;;
|
|
user) echo "Administrator user name (uses sudo, root login is disabled):" ;;
|
|
baduser) echo "Invalid name: lowercase Latin letters, digits, _ and -, not root." ;;
|
|
pass) echo "User password:" ;;
|
|
pass2) echo "The same password again:" ;;
|
|
badpass) echo "The passwords are empty or do not match." ;;
|
|
key) echo "SSH public key to log in with (paste the ssh-ed25519 ... line, or leave empty):" ;;
|
|
badkey) echo "This does not look like an SSH public key." ;;
|
|
nopass) echo "Disable SSH password login (keys only)?" ;;
|
|
tz) echo "Time zone (for example Europe/Kyiv, UTC):" ;;
|
|
badtz) echo "No such time zone." ;;
|
|
keymap) echo "Console keyboard layout:" ;;
|
|
locale) echo "System language:" ;;
|
|
net) echo "Network:" ;;
|
|
net_dhcpcd) echo "dhcpcd: DHCP on all interfaces (simple)" ;;
|
|
net_nm) echo "NetworkManager: set up with nmtui (Wi-Fi, static addresses)" ;;
|
|
summary) echo "Check the settings:" ;;
|
|
erase) echo "ALL DATA ON %s WILL BE ERASED. Continue?" ;;
|
|
cancel) echo "Installation cancelled." ;;
|
|
s_source) echo "Preparing the system image..." ;;
|
|
s_disk) echo "Partitioning %s..." ;;
|
|
s_copy) echo "Copying the system (a few minutes)..." ;;
|
|
s_config) echo "Configuring the system..." ;;
|
|
s_post) echo "Locales, snapshots, initramfs (a few minutes)..." ;;
|
|
s_boot) echo "Installing the boot loader..." ;;
|
|
finished) echo "Das OS Server is installed. Remove the installation medium and reboot.\n\nLog in as %s, then use sudo. Services: dsv list. Updates: das-update." ;;
|
|
reboot) echo "Reboot now?" ;;
|
|
failed) echo "Installation failed. Log: %s" ;;
|
|
root) echo "The installer must run as root (sudo das-server-install)." ;;
|
|
yes) echo "yes" ;;
|
|
no) echo "no" ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
interactive=1
|
|
dialog_ui() {
|
|
local labels=()
|
|
if [[ $lang == uk ]]; then
|
|
labels=(--ok-label "Гаразд" --cancel-label "Скасувати" --yes-label "Так" --no-label "Ні")
|
|
fi
|
|
dialog --backtitle "Das OS Server" --title "$(t title)" "${labels[@]}" "$@" 3>&1 1>&2 2>&3
|
|
}
|
|
|
|
# Shows a step in the dialog, or prints it when installing from a config file
|
|
step() {
|
|
if [[ -n $interactive ]]; then
|
|
dialog --backtitle "Das OS Server" --title "$(t title)" --infobox "$1" 5 70
|
|
echo "==> $1" >>"$log"
|
|
else
|
|
echo "==> $1" | tee -a "$log"
|
|
fi
|
|
}
|
|
|
|
fail() {
|
|
trap - ERR
|
|
local msg
|
|
# shellcheck disable=SC2059
|
|
msg=$(printf "$(t failed)" "$log")
|
|
if [[ -n $interactive ]]; then
|
|
dialog --backtitle "Das OS Server" --title "$(t title)" --msgbox "$msg\n\n$(tail -n 12 "$log" | sed 's/\\/\\\\/g')" 22 76 || true
|
|
clear
|
|
fi
|
|
echo "das-server-install: $msg" >&2
|
|
exit 1
|
|
}
|
|
|
|
cancelled() {
|
|
[[ -n $interactive ]] && clear
|
|
t cancel
|
|
exit 1
|
|
}
|
|
|
|
# --- Questions -------------------------------------------------------------
|
|
|
|
# Disks that can be installed to: not read-only, not the live medium
|
|
list_disks() {
|
|
local live="" dev
|
|
if dev=$(blkid -L VOID_LIVE 2>/dev/null); then
|
|
live=$(lsblk -no PKNAME "$dev" 2>/dev/null | head -n 1)
|
|
[[ -n $live ]] && live=/dev/$live || live=$dev
|
|
fi
|
|
lsblk -dpno NAME,TYPE,RO,SIZE,MODEL | while read -r name type ro size model; do
|
|
[[ $type == disk && $ro == 0 && $name != "$live" ]] || continue
|
|
[[ $name == /dev/zram* || $name == /dev/loop* ]] && continue
|
|
printf '%s\t%s %s\n' "$name" "$size" "${model:-}"
|
|
done
|
|
}
|
|
|
|
ask_language() {
|
|
local choice
|
|
choice=$(dialog --backtitle "Das OS Server" --title "Das OS Server" \
|
|
--default-item "$lang" --menu "Language / Мова:" 10 50 2 \
|
|
en "English" uk "Українська" 3>&1 1>&2 2>&3) || cancelled
|
|
lang=$choice
|
|
if [[ $lang == uk ]]; then
|
|
LOCALE=uk_UA.UTF-8
|
|
# The default console font has no Cyrillic (a font needs a Unicode table,
|
|
# .psfu); there is no font to set on serial or ssh
|
|
case $(tty) in /dev/tty[0-9]*) setfont cyr-sun16 2>/dev/null || true ;; esac
|
|
fi
|
|
}
|
|
|
|
ask_disk() {
|
|
local items=() name desc
|
|
while IFS=$'\t' read -r name desc; do
|
|
items+=("$name" "$desc")
|
|
done < <(list_disks)
|
|
if [[ ${#items[@]} -eq 0 ]]; then
|
|
dialog_ui --msgbox "$(t nodisk)" 7 60 || true
|
|
cancelled
|
|
fi
|
|
DISK=$(dialog_ui --menu "$(t disk)" 15 70 6 "${items[@]}") || cancelled
|
|
}
|
|
|
|
valid_host() {
|
|
[[ $1 =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$ ]]
|
|
}
|
|
|
|
valid_user() {
|
|
[[ $1 =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || return 1
|
|
case $1 in root | live | nobody | daemon | bin | sys) return 1 ;; esac
|
|
}
|
|
|
|
valid_key() {
|
|
[[ -z $1 || $1 =~ ^(ssh-(ed25519|rsa|dss)|ecdsa-sha2-[a-z0-9]+|sk-[a-z0-9@.-]+)\ [A-Za-z0-9+/=]+ ]]
|
|
}
|
|
|
|
ask_host() {
|
|
while :; do
|
|
HOST_NAME=$(dialog_ui --inputbox "$(t host)" 8 60 "$HOST_NAME") || cancelled
|
|
valid_host "$HOST_NAME" && return
|
|
dialog_ui --msgbox "$(t badhost)" 6 50 || true
|
|
done
|
|
}
|
|
|
|
ask_user() {
|
|
local p1 p2
|
|
while :; do
|
|
USER_NAME=$(dialog_ui --inputbox "$(t user)" 9 60 "$USER_NAME") || cancelled
|
|
valid_user "$USER_NAME" && break
|
|
dialog_ui --msgbox "$(t baduser)" 7 60 || true
|
|
done
|
|
while :; do
|
|
p1=$(dialog_ui --insecure --passwordbox "$(t pass)" 8 60) || cancelled
|
|
p2=$(dialog_ui --insecure --passwordbox "$(t pass2)" 8 60) || cancelled
|
|
if [[ -n $p1 && $p1 == "$p2" ]]; then
|
|
USER_PASSWORD=$p1
|
|
return
|
|
fi
|
|
dialog_ui --msgbox "$(t badpass)" 6 50 || true
|
|
done
|
|
}
|
|
|
|
ask_ssh() {
|
|
while :; do
|
|
SSH_KEY=$(dialog_ui --inputbox "$(t key)" 10 76 "$SSH_KEY") || cancelled
|
|
valid_key "$SSH_KEY" && break
|
|
dialog_ui --msgbox "$(t badkey)" 6 50 || true
|
|
done
|
|
SSH_PASSWORDS=yes
|
|
if [[ -n $SSH_KEY ]] && dialog_ui --yesno "$(t nopass)" 7 60; then
|
|
SSH_PASSWORDS=no
|
|
fi
|
|
}
|
|
|
|
ask_system() {
|
|
while :; do
|
|
TIMEZONE=$(dialog_ui --inputbox "$(t tz)" 8 60 "$TIMEZONE") || cancelled
|
|
[[ -f /usr/share/zoneinfo/$TIMEZONE ]] && break
|
|
dialog_ui --msgbox "$(t badtz)" 6 50 || true
|
|
done
|
|
KEYMAP=$(dialog_ui --default-item "$KEYMAP" --menu "$(t keymap)" 15 50 7 \
|
|
us "English (US)" ua-utf "Українська" uk "English (UK)" de-latin1 "Deutsch" \
|
|
fr-latin1 "Français" pl2 "Polski") || cancelled
|
|
LOCALE=$(dialog_ui --default-item "$LOCALE" --menu "$(t locale)" 10 50 2 \
|
|
en_US.UTF-8 "English" uk_UA.UTF-8 "Українська") || cancelled
|
|
NETWORK=$(dialog_ui --default-item "$NETWORK" --menu "$(t net)" 10 76 2 \
|
|
dhcpcd "$(t net_dhcpcd)" NetworkManager "$(t net_nm)") || cancelled
|
|
}
|
|
|
|
confirm() {
|
|
local key pw msg
|
|
key=$(t no)
|
|
pw=$(t yes)
|
|
[[ -n $SSH_KEY ]] && key=$(t yes)
|
|
[[ $SSH_PASSWORDS == no ]] && pw=$(t no)
|
|
dialog_ui --no-collapse --msgbox "$(t summary)
|
|
|
|
Disk: $DISK
|
|
Host: $HOST_NAME
|
|
User: $USER_NAME
|
|
SSH key: $key
|
|
SSH passwords: $pw
|
|
Time zone: $TIMEZONE
|
|
Keyboard: $KEYMAP
|
|
Language: $LOCALE
|
|
Network: $NETWORK" 18 60 || cancelled
|
|
# shellcheck disable=SC2059
|
|
msg=$(printf "$(t erase)" "$DISK")
|
|
dialog_ui --defaultno --yesno "$msg" 7 60 || cancelled
|
|
ERASE_DISK=yes
|
|
}
|
|
|
|
# --- Installation ----------------------------------------------------------
|
|
|
|
# part N: the device of partition N on $DISK
|
|
part() {
|
|
lsblk -lnpo NAME,TYPE "$DISK" | awk '$2 == "part" { print $1 }' | sed -n "${1}p"
|
|
}
|
|
|
|
in_target() {
|
|
chroot "$target" "$@"
|
|
}
|
|
|
|
# Undo mounts from an earlier attempt or a failure
|
|
unmount_target() {
|
|
if mountpoint -q "$target"; then
|
|
umount -R "$target" || umount -Rl "$target"
|
|
fi
|
|
}
|
|
|
|
uefi() {
|
|
[[ -d /sys/firmware/efi ]]
|
|
}
|
|
|
|
serial_console() {
|
|
grep -q 'console=ttyS0' /proc/cmdline
|
|
}
|
|
|
|
partition() {
|
|
local efi root
|
|
unmount_target
|
|
wipefs -a "$DISK"
|
|
sgdisk --zap-all "$DISK"
|
|
if uefi; then
|
|
sgdisk -n 1:0:+512M -t 1:ef00 -c 1:EFI -n 2:0:0 -t 2:8300 -c 2:dasos "$DISK"
|
|
else
|
|
# GRUB on BIOS keeps its core image in a small partition on GPT
|
|
sgdisk -n 1:0:+1M -t 1:ef02 -c 1:bios -n 2:0:0 -t 2:8300 -c 2:dasos "$DISK"
|
|
fi
|
|
partprobe "$DISK" 2>/dev/null || true
|
|
udevadm settle
|
|
efi=$(part 1)
|
|
root=$(part 2)
|
|
[[ -b $root ]] || return 1
|
|
|
|
uefi && mkfs.vfat -F 32 -n EFI "$efi"
|
|
mkfs.btrfs -f -L dasos "$root"
|
|
|
|
# The same subvolumes as the Calamares configuration (mount.conf)
|
|
mkdir -p "$target"
|
|
mount "$root" "$target"
|
|
for sv in @ @home @snapshots @var_log @var_cache_xbps; do
|
|
btrfs subvolume create "$target/$sv"
|
|
done
|
|
umount "$target"
|
|
|
|
local opts=noatime,compress=zstd:1
|
|
mount -o "$opts,subvol=@" "$root" "$target"
|
|
mkdir -p "$target"/{home,.snapshots,var/log,var/cache/xbps}
|
|
mount -o "$opts,subvol=@home" "$root" "$target/home"
|
|
mount -o "$opts,subvol=@snapshots" "$root" "$target/.snapshots"
|
|
mount -o "$opts,subvol=@var_log" "$root" "$target/var/log"
|
|
mount -o "$opts,subvol=@var_cache_xbps" "$root" "$target/var/cache/xbps"
|
|
if uefi; then
|
|
mkdir -p "$target/boot/efi"
|
|
mount -o umask=0077 "$efi" "$target/boot/efi"
|
|
fi
|
|
}
|
|
|
|
copy_system() {
|
|
# No --sparse: GRUB can't read kernels with Btrfs holes (see postinstall)
|
|
rsync -aHAX --numeric-ids "$source_root/" "$target/"
|
|
}
|
|
|
|
write_fstab() {
|
|
local root_uuid efi_uuid opts=noatime,compress=zstd:1
|
|
root_uuid=$(blkid -s UUID -o value "$(part 2)")
|
|
{
|
|
echo "# <file system> <dir> <type> <options> <dump> <pass>"
|
|
echo "UUID=$root_uuid / btrfs $opts,subvol=@ 0 0"
|
|
echo "UUID=$root_uuid /home btrfs $opts,subvol=@home 0 0"
|
|
echo "UUID=$root_uuid /.snapshots btrfs $opts,subvol=@snapshots 0 0"
|
|
echo "UUID=$root_uuid /var/log btrfs $opts,subvol=@var_log 0 0"
|
|
echo "UUID=$root_uuid /var/cache/xbps btrfs $opts,subvol=@var_cache_xbps 0 0"
|
|
if uefi; then
|
|
efi_uuid=$(blkid -s UUID -o value "$(part 1)")
|
|
echo "UUID=$efi_uuid /boot/efi vfat defaults,umask=0077 0 2"
|
|
fi
|
|
} >"$target/etc/fstab"
|
|
}
|
|
|
|
mount_chroot() {
|
|
mount -t proc proc "$target/proc"
|
|
mount -t sysfs sys "$target/sys"
|
|
mount --rbind /dev "$target/dev"
|
|
mount -t tmpfs tmpfs "$target/run"
|
|
if uefi; then
|
|
mount -t efivarfs efivarfs "$target/sys/firmware/efi/efivars" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
# set_rc VAR VALUE: sets VAR in /etc/rc.conf, uncommenting Void's example line
|
|
set_rc() {
|
|
local rc=$target/etc/rc.conf
|
|
if grep -q "^#\?$1=" "$rc"; then
|
|
sed -i "s|^#\?$1=.*|$1=\"$2\"|" "$rc"
|
|
else
|
|
echo "$1=\"$2\"" >>"$rc"
|
|
fi
|
|
}
|
|
|
|
enable_service() {
|
|
ln -sfn "/etc/sv/$1" "$target/etc/runit/runsvdir/default/$1"
|
|
}
|
|
|
|
disable_service() {
|
|
rm -f "$target/etc/runit/runsvdir/default/$1"
|
|
}
|
|
|
|
configure() {
|
|
local home
|
|
|
|
# The live image's /etc/issue tells how to log in to the live system
|
|
printf 'Das OS Server \\r (\\l)\n\n' >"$target/etc/issue"
|
|
echo "$HOST_NAME" >"$target/etc/hostname"
|
|
if ! grep -q "[[:space:]]$HOST_NAME\$" "$target/etc/hosts"; then
|
|
printf '127.0.1.1\t%s\n' "$HOST_NAME" >>"$target/etc/hosts"
|
|
fi
|
|
echo "LANG=$LOCALE" >"$target/etc/locale.conf"
|
|
ln -sfn "/usr/share/zoneinfo/$TIMEZONE" "$target/etc/localtime"
|
|
set_rc KEYMAP "$KEYMAP"
|
|
set_rc HARDWARECLOCK UTC
|
|
[[ $LOCALE == uk* || $KEYMAP == ua* ]] && set_rc FONT cyr-sun16
|
|
|
|
# The administrator: sudo through wheel, root has no password
|
|
in_target useradd -m -s /bin/bash -G wheel,docker,socklog "$USER_NAME"
|
|
# -c: Void's PAM stack for chpasswd only has pam_permit for passwords, so
|
|
# without it nothing is written (Void's own installer does the same)
|
|
printf '%s:%s\n' "$USER_NAME" "$USER_PASSWORD" | in_target chpasswd -c SHA512
|
|
in_target passwd -l root >/dev/null
|
|
echo "%wheel ALL=(ALL:ALL) ALL" >"$target/etc/sudoers.d/10-wheel"
|
|
chmod 440 "$target/etc/sudoers.d/10-wheel"
|
|
|
|
if [[ -n $SSH_KEY ]]; then
|
|
home=$target/home/$USER_NAME
|
|
install -d -m 700 "$home/.ssh"
|
|
echo "$SSH_KEY" >"$home/.ssh/authorized_keys"
|
|
chmod 600 "$home/.ssh/authorized_keys"
|
|
in_target chown -R "$USER_NAME:$USER_NAME" "/home/$USER_NAME/.ssh"
|
|
fi
|
|
if [[ $SSH_PASSWORDS == no ]]; then
|
|
printf '%s\n' "# Written by das-server-install: SSH keys only" \
|
|
"PasswordAuthentication no" "KbdInteractiveAuthentication no" \
|
|
>"$target/etc/ssh/sshd_config.d/40-das-keys-only.conf"
|
|
fi
|
|
|
|
for sv in sshd chronyd nftables fail2ban docker socklog-unix nanoklogd; do
|
|
enable_service "$sv"
|
|
done
|
|
if [[ $NETWORK == NetworkManager ]]; then
|
|
disable_service dhcpcd
|
|
enable_service dbus
|
|
enable_service NetworkManager
|
|
else
|
|
enable_service dhcpcd
|
|
fi
|
|
serial_console && enable_service agetty-ttyS0
|
|
return 0
|
|
}
|
|
|
|
install_bootloader() {
|
|
local grub=$target/etc/default/grub cmdline="loglevel=4"
|
|
serial_console && cmdline+=" console=tty0 console=ttyS0,115200"
|
|
sed -i -e '/^GRUB_DISTRIBUTOR=/d' -e '/^GRUB_CMDLINE_LINUX_DEFAULT=/d' -e '/^GRUB_TIMEOUT=/d' \
|
|
-e '/^GRUB_TERMINAL/d' -e '/^GRUB_SERIAL_COMMAND=/d' -e '/^GRUB_DISABLE_OS_PROBER=/d' "$grub"
|
|
{
|
|
echo 'GRUB_DISTRIBUTOR="Das OS"'
|
|
echo "GRUB_CMDLINE_LINUX_DEFAULT=\"$cmdline\""
|
|
echo 'GRUB_TIMEOUT=5'
|
|
echo 'GRUB_DISABLE_OS_PROBER=true'
|
|
if serial_console; then
|
|
echo 'GRUB_TERMINAL="console serial"'
|
|
echo 'GRUB_SERIAL_COMMAND="serial --unit=0 --speed=115200"'
|
|
fi
|
|
} >>"$grub"
|
|
|
|
if uefi; then
|
|
in_target grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=dasos
|
|
# Also at the fallback path, for firmware that forgets boot entries
|
|
in_target grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --no-nvram
|
|
else
|
|
in_target grub-install --target=i386-pc "$DISK"
|
|
fi
|
|
}
|
|
|
|
finish() {
|
|
# The first snapshot, also shown in the boot menu (like Calamares' shellprocess@snapshot)
|
|
in_target snapper --no-dbus -c root create -d "Fresh install" -c number --userdata important=yes || true
|
|
in_target grub-mkconfig -o /boot/grub/grub.cfg
|
|
# The installer is not needed on the installed system
|
|
in_target xbps-remove -y das-server-installer das-install-common || true
|
|
sync
|
|
}
|
|
|
|
# Any failing command stops the installation with the log (set -E: also
|
|
# inside functions; "f || fail" would switch set -e off within f).
|
|
install_system() {
|
|
: >"$log"
|
|
trap fail ERR
|
|
step "$(t s_source)"
|
|
"$libexec/mount-source" >>"$log" 2>&1
|
|
# shellcheck disable=SC2059
|
|
step "$(printf "$(t s_disk)" "$DISK")"
|
|
partition >>"$log" 2>&1
|
|
step "$(t s_copy)"
|
|
copy_system >>"$log" 2>&1
|
|
step "$(t s_config)"
|
|
write_fstab >>"$log" 2>&1
|
|
mount_chroot >>"$log" 2>&1
|
|
configure >>"$log" 2>&1
|
|
step "$(t s_post)"
|
|
in_target "$libexec/postinstall" >>"$log" 2>&1
|
|
step "$(t s_boot)"
|
|
install_bootloader >>"$log" 2>&1
|
|
finish >>"$log" 2>&1
|
|
unmount_target >>"$log" 2>&1
|
|
trap - ERR
|
|
}
|
|
|
|
load_config() {
|
|
local file=$1
|
|
[[ -r $file ]] || {
|
|
echo "das-server-install: cannot read $file" >&2
|
|
exit 1
|
|
}
|
|
# shellcheck source=/dev/null
|
|
. "$file"
|
|
interactive=""
|
|
local bad=""
|
|
[[ -b $DISK ]] || bad+=" DISK"
|
|
valid_host "$HOST_NAME" || bad+=" HOST_NAME"
|
|
valid_user "$USER_NAME" || bad+=" USER_NAME"
|
|
[[ -n $USER_PASSWORD ]] || bad+=" USER_PASSWORD"
|
|
valid_key "$SSH_KEY" || bad+=" SSH_KEY"
|
|
[[ $SSH_PASSWORDS == yes || ( $SSH_PASSWORDS == no && -n $SSH_KEY ) ]] || bad+=" SSH_PASSWORDS"
|
|
[[ -f /usr/share/zoneinfo/$TIMEZONE ]] || bad+=" TIMEZONE"
|
|
[[ $NETWORK == dhcpcd || $NETWORK == NetworkManager ]] || bad+=" NETWORK"
|
|
[[ $ERASE_DISK == yes ]] || bad+=" ERASE_DISK"
|
|
if [[ -n $bad ]]; then
|
|
echo "das-server-install: invalid or missing in $file:$bad" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
exec sudo --preserve-env=LANG "$0" "$@"
|
|
fi
|
|
case ${1:-} in
|
|
--config)
|
|
load_config "${2:?--config needs a file}"
|
|
;;
|
|
-h | --help)
|
|
sed -n '2,11s/^# \{0,1\}//p' "$0"
|
|
exit 0
|
|
;;
|
|
"") ;;
|
|
*)
|
|
echo "das-server-install: unknown option $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [[ -n $interactive ]]; then
|
|
ask_language
|
|
ask_disk
|
|
ask_host
|
|
ask_user
|
|
ask_ssh
|
|
ask_system
|
|
confirm
|
|
fi
|
|
install_system
|
|
|
|
if [[ -n $interactive ]]; then
|
|
local msg
|
|
# shellcheck disable=SC2059
|
|
msg=$(printf "$(t finished)" "$USER_NAME")
|
|
if dialog_ui --yesno "$msg\n\n$(t reboot)" 12 70; then
|
|
reboot
|
|
fi
|
|
clear
|
|
else
|
|
echo "==> done, log: $log"
|
|
fi
|
|
}
|
|
|
|
main "$@"
|