SPS · log · files · refs · tags · releases · people · languages · GitHub · file
bin/setup
69775 bytes at 2909919 · GitHub
#!/bin/sh
# SPS Linux installer (informally Splux). Interactive screens use dialog(1);
# every choice is also available as a flag for scripts and tests.
# This is the distro installer, not a generic LFS helper. SPS the package
# system still works on other trees without running setup.
sps_prog=setup
sps_bin_dir=$(CDPATH= cd -P "$(dirname "$0")" 2>/dev/null && pwd) || exit 1
if [ -z "${SPS_LIBDIR+x}" ]; then
if [ -r "$sps_bin_dir/../lib/common.sh" ]; then
SPS_LIBDIR=$sps_bin_dir/../lib
else
SPS_LIBDIR=$sps_bin_dir/../lib/sps
fi
fi
if ! . "$SPS_LIBDIR/common.sh"; then
printf '%s\n' "setup: cannot load $SPS_LIBDIR/common.sh" >&2
exit 1
fi
# Remember caller-supplied paths. sps_load_config exports defaults for the
# running system; after --target those must not leak into src/sget.
[ "${SPS_DB+x}" = x ] && setup_keep_db=1 || setup_keep_db=0
[ "${SPS_CACHE+x}" = x ] && setup_keep_cache=1 || setup_keep_cache=0
[ "${SPS_BUILD+x}" = x ] && setup_keep_build=1 || setup_keep_build=0
[ "${SPS_REPO_ROOT+x}" = x ] && setup_keep_repo_root=1 || setup_keep_repo_root=0
[ "${SPS_CONFIG+x}" = x ] && setup_keep_config=1 || setup_keep_config=0
[ "${SPS_REPOS_CONFIG+x}" = x ] && setup_keep_repos=1 || setup_keep_repos=0
sps_load_config || exit $?
setup_usage()
{
cat <<'EOF'
usage: setup [options]
Install SPS Linux (Splux) into a target root using dialog(1) screens, or
the same choices from the command line. This is the distro installer.
SPS the package system can be used without it (for example on LFS).
Disks: guided layouts erase the chosen disk after two confirmations.
--plan and --non-interactive never touch a disk unless you pass --disk
and --format yes together. grub-install still needs --install-bootloader
and --disk; setup will not guess a target.
options:
--target DIR installation root (SPS_ROOT)
--profile NAME minimal, server, plasma-desktop, or plasma-full
--enable SET[,SET...] extra sets (repeatable)
--disable SET[,SET...] extra sets to leave off (repeatable)
--hostname NAME
--timezone ZONE e.g. UTC or America/Los_Angeles
--keymap MAP e.g. us, uk, de, dvorak
--locale NAME e.g. C.UTF-8 or en_US.UTF-8
--shell PATH login shell for the first user
--user NAME first unprivileged user (uid 1000)
--user-gecos TEXT
--user-password-file FILE hash the first line as the user password
--root-password-file FILE hash the first line as the root password
--user-password-hash HASH already-hashed user shadow field
--root-password-hash HASH already-hashed root shadow field
--extra PKG[,PKG...] individual packages in addition to sets
--ssh yes|no include the ssh set (OpenSSH)
--printing yes|no include the printing set (CUPS)
--flatpak yes|no add Flathub after install when flatpak is present
--nvidia yes|no proprietary NVIDIA set (off by default; live uses nouveau)
--partition LAYOUT none, existing, efi-root, efi-swap-root,
efi-swap-root-home, bios-root, manage
--disk DEV whole disk for guided layouts (e.g. /dev/sda)
--swap-size SIZE sfdisk size for swap (default 8GiB)
--root-part DEV root partition for --partition manage
--efi-part DEV EFI partition for --partition manage
--swap-part DEV swap partition for --partition manage
--home-part DEV /home partition for --partition manage
--format yes|no required yes for non-interactive guided layouts
--services yes|no enable known services for the chosen init (default yes)
--kernel NAME none, linux, linux-desktop, or custom
--kernel-config FILE .config for --kernel custom
--drivers auto|none map lspci/cpu to packages (microcode, nouveau, iwd)
--init systemd|openrc|s6|runit|dinit|shepherd|sysvinit|none
PID 1 on the installed system (default: none in
scripts; live /etc/sps/init or systemd on the
interactive screen)
--bootloader none|grub write bootloader notes; never implied
--install-bootloader run grub-install on --disk (explicit, dangerous)
--update|--no-update run or skip src update before installing
--non-interactive do not start dialog; all choices from options
--plan print the package list and exit
--write-answers FILE write the resolved answers
--read-answers FILE load answers (options override file values)
--list-profiles
--list-sets
--list-hardware print detected devices and suggested packages
-h, --help
EOF
}
setup_dir=${SPS_SETUP_DIR:-$SPS_LIBDIR/setup}
if [ -f "$SPS_LIBDIR/setup/partition.sh" ]; then
# shellcheck disable=SC1091
. "$SPS_LIBDIR/setup/partition.sh"
fi
if [ -f "$SPS_LIBDIR/setup/hwdetect.sh" ]; then
# shellcheck disable=SC1091
. "$SPS_LIBDIR/setup/hwdetect.sh"
fi
if [ -f "$SPS_LIBDIR/setup/service.sh" ]; then
# shellcheck disable=SC1091
. "$SPS_LIBDIR/setup/service.sh"
fi
# True when package $2 cannot be installed with chosen init $1.
setup_pkg_conflicts_init()
{
setup_ci=$1
setup_cp=$2
case $setup_ci in
systemd)
case $setup_cp in
elogind|eudev|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd|sysvinit|init-sysvinit)
return 0
;;
esac
;;
openrc)
case $setup_cp in
systemd|init-systemd|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd|sysvinit|init-sysvinit)
return 0
;;
esac
;;
s6)
case $setup_cp in
systemd|init-systemd|openrc|init-openrc|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd|sysvinit|init-sysvinit)
return 0
;;
esac
;;
runit)
case $setup_cp in
systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|dinit|init-dinit|shepherd|init-shepherd|sysvinit|init-sysvinit)
return 0
;;
esac
;;
dinit)
case $setup_cp in
systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|shepherd|init-shepherd|sysvinit|init-sysvinit)
return 0
;;
esac
;;
shepherd)
case $setup_cp in
systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|sysvinit|init-sysvinit)
return 0
;;
esac
;;
sysvinit)
case $setup_cp in
systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd)
return 0
;;
esac
;;
esac
return 1
}
setup_collect_recipe_names()
{
setup_names_out=$1
: >"$setup_names_out"
for setup_rtree in \
/usr/src/sps/core \
/usr/src/sps/extra \
"${SPS_LIVE_ROOT:-/}/usr/src/sps/core" \
"${SPS_LIVE_ROOT:-/}/usr/src/sps/extra"
do
[ -d "$setup_rtree" ] || continue
find "$setup_rtree" \( -type d \( -name .git -o -name files -o \
-name patches -o -name hooks \) -prune \) -o \
-type f -name recipe -print
done | while IFS= read -r setup_rfile || [ -n "$setup_rfile" ]
do
[ -n "$setup_rfile" ] || continue
awk '/^name[[:space:]]/ { print $2; exit }' "$setup_rfile"
done | LC_ALL=C sort -u >"$setup_names_out"
}
setup_reject_unknown_packages()
{
setup_index=${SPS_CACHE:-$setup_root/var/cache/sps}/indexes/packages.index
[ -f "$setup_index" ] || return 0
[ -s "$setup_pkg_file" ] || return 0
setup_unknown=$setup_work/unknown-packages
awk -F '\t' '
FNR == NR {
gsub(/\r/, "")
if ($1 != "")
want[$1] = 1
next
}
{ have[$1] = 1 }
END {
for (n in want)
if (!(n in have))
print n
}
' "$setup_pkg_file" "$setup_index" | LC_ALL=C sort >"$setup_unknown"
[ -s "$setup_unknown" ] || return 0
printf '%s: packages not in the recipe index:\n' "$sps_prog" >&2
sed 's/^/ /' "$setup_unknown" >&2
setup_fail "$SPS_EX_NOTFOUND" \
"requested packages are not packaged (see the list above)"
}
setup_work=
setup_interactive=1
setup_plan=0
setup_list_profiles=0
setup_list_sets=0
setup_do_update=1
setup_install_bootloader=0
setup_target=
setup_profile=
setup_hostname=
setup_timezone=
setup_keymap=
setup_locale=
setup_shell=
setup_user=
setup_user_gecos=
setup_user_password_hash=
setup_root_password_hash=
setup_extra=
setup_ssh=
setup_printing=
setup_flatpak=
setup_nvidia=
setup_bootloader=none
setup_answers_in=
setup_answers_out=
setup_enable=
setup_disable=
setup_partition=
setup_disk=
setup_swap_size=8GiB
setup_format=
setup_kernel=
setup_kernel_config=
setup_drivers=
setup_init=
setup_services=
setup_part_efi=
setup_part_root=
setup_part_swap=
setup_part_home=
setup_part_bios=
setup_list_hardware=0
setup_cleanup()
{
if [ -n "$setup_work" ] && [ -d "$setup_work" ]; then
rm -rf "$setup_work"
fi
}
setup_fail()
{
setup_code=$1
shift
printf '%s: %s\n' "$sps_prog" "$*" >&2
exit "$setup_code"
}
setup_need_value()
{
[ "$#" -ge 2 ] && [ -n "${2-}" ] ||
setup_fail "$SPS_EX_USAGE" "option '$1' requires an argument"
}
setup_append_csv()
{
setup_dest=$1
setup_csv=$2
setup_old=$3
setup_item=
setup_rest=$setup_csv,
while [ -n "$setup_rest" ]; do
setup_item=${setup_rest%%,*}
setup_rest=${setup_rest#*,}
[ -n "$setup_item" ] || continue
case " $setup_old " in
*" $setup_item "*) ;;
*) setup_old="$setup_old $setup_item" ;;
esac
done
eval "$setup_dest=\"\$(printf '%s' \"\$setup_old\" | sed 's/^ //')\""
}
setup_hash_password_stdin()
{
if command -v openssl >/dev/null 2>&1; then
openssl passwd -6 -stdin
return $?
fi
if command -v python3 >/dev/null 2>&1; then
python3 -c 'import crypt,sys
p=sys.stdin.read().rstrip("\n")
print(crypt.crypt(p, crypt.mksalt(crypt.METHOD_SHA512)))'
return $?
fi
if command -v busybox >/dev/null 2>&1; then
busybox cryptpw -m sha512 "$(cat)"
return $?
fi
return 1
}
setup_hash_password_file()
{
setup_pwf=$1
[ -f "$setup_pwf" ] && [ -r "$setup_pwf" ] ||
setup_fail "$SPS_EX_NOTFOUND" "cannot read password file: $setup_pwf"
setup_pw=$(sed -n '1p' "$setup_pwf")
[ -n "$setup_pw" ] ||
setup_fail "$SPS_EX_USAGE" "password file is empty: $setup_pwf"
printf '%s\n' "$setup_pw" | setup_hash_password_stdin
}
setup_shadow_set_hash()
{
setup_sh_file=$1
setup_sh_user=$2
setup_sh_hash=$3
[ -f "$setup_sh_file" ] || return 0
[ -n "$setup_sh_hash" ] || return 0
setup_sh_tmp=$setup_sh_file.tmp
setup_u=$setup_sh_user setup_h=$setup_sh_hash awk -F: '
BEGIN { OFS = ":" }
$1 == ENVIRON["setup_u"] { $2 = ENVIRON["setup_h"] }
{ print }
' "$setup_sh_file" >"$setup_sh_tmp" || return 1
mv "$setup_sh_tmp" "$setup_sh_file"
chmod 0640 "$setup_sh_file" 2>/dev/null || chmod 0600 "$setup_sh_file" || :
}
setup_group_ensure()
{
setup_gf=$1
setup_gn=$2
setup_gid=$3
[ -f "$setup_gf" ] || return 0
grep -q "^$setup_gn:" "$setup_gf" && return 0
printf '%s:x:%s:\n' "$setup_gn" "$setup_gid" >>"$setup_gf"
}
setup_group_add_user()
{
setup_gf=$1
setup_gn=$2
setup_gu=$3
[ -f "$setup_gf" ] || return 0
grep -q "^$setup_gn:" "$setup_gf" || return 0
setup_g_tmp=$setup_gf.tmp
setup_g=$setup_gn setup_u=$setup_gu awk -F: '
BEGIN { OFS = ":" }
$1 == ENVIRON["setup_g"] {
if ($4 == "") $4 = ENVIRON["setup_u"]
else {
n = split($4, a, ",")
found = 0
for (i = 1; i <= n; i++) if (a[i] == ENVIRON["setup_u"]) found = 1
if (!found) $4 = $4 "," ENVIRON["setup_u"]
}
}
{ print }
' "$setup_gf" >"$setup_g_tmp" || return 1
mv "$setup_g_tmp" "$setup_gf"
}
setup_ask_password_pair()
{
setup_pw_title=$1
setup_pw_prompt=$2
setup_p1=$(setup_dialog --title "$setup_pw_title" \
--passwordbox "$setup_pw_prompt" 9 50) ||
setup_fail "$SPS_EX_USAGE" "cancelled"
setup_p2=$(setup_dialog --title "$setup_pw_title" \
--passwordbox "Repeat the password." 9 50) ||
setup_fail "$SPS_EX_USAGE" "cancelled"
if [ "$setup_p1" != "$setup_p2" ]; then
setup_fail "$SPS_EX_USAGE" "passwords do not match"
fi
if [ -z "$setup_p1" ]; then
printf '%s\n' ''
return 0
fi
printf '%s\n' "$setup_p1" | setup_hash_password_stdin ||
setup_fail "$SPS_EX_PACKAGE" \
"cannot hash passwords (need openssl passwd, python3, or busybox cryptpw)"
}
while [ "$#" -gt 0 ]; do
case $1 in
-h|--help)
setup_usage
exit 0
;;
--target)
setup_need_value "$@"
setup_target=$2
shift 2
;;
--profile)
setup_need_value "$@"
setup_profile=$2
shift 2
;;
--enable)
setup_need_value "$@"
setup_append_csv setup_enable "$2" "$setup_enable"
shift 2
;;
--disable)
setup_need_value "$@"
setup_append_csv setup_disable "$2" "$setup_disable"
shift 2
;;
--hostname)
setup_need_value "$@"
setup_hostname=$2
shift 2
;;
--timezone)
setup_need_value "$@"
setup_timezone=$2
shift 2
;;
--keymap)
setup_need_value "$@"
setup_keymap=$2
shift 2
;;
--locale)
setup_need_value "$@"
setup_locale=$2
shift 2
;;
--shell)
setup_need_value "$@"
setup_shell=$2
shift 2
;;
--extra)
setup_need_value "$@"
setup_append_csv setup_extra "$2" "$setup_extra"
shift 2
;;
--ssh)
setup_need_value "$@"
setup_ssh=$2
shift 2
;;
--printing)
setup_need_value "$@"
setup_printing=$2
shift 2
;;
--user)
setup_need_value "$@"
setup_user=$2
shift 2
;;
--user-gecos)
setup_need_value "$@"
setup_user_gecos=$2
shift 2
;;
--user-password-file)
setup_need_value "$@"
setup_user_password_hash=$(setup_hash_password_file "$2") ||
setup_fail "$SPS_EX_PACKAGE" "cannot hash $2"
[ -n "$setup_user_password_hash" ] ||
setup_fail "$SPS_EX_USAGE" "user password hash was empty"
shift 2
;;
--root-password-file)
setup_need_value "$@"
setup_root_password_hash=$(setup_hash_password_file "$2") ||
setup_fail "$SPS_EX_PACKAGE" "cannot hash $2"
[ -n "$setup_root_password_hash" ] ||
setup_fail "$SPS_EX_USAGE" "root password hash was empty"
shift 2
;;
--user-password-hash)
setup_need_value "$@"
setup_user_password_hash=$2
shift 2
;;
--root-password-hash)
setup_need_value "$@"
setup_root_password_hash=$2
shift 2
;;
--flatpak)
setup_need_value "$@"
setup_flatpak=$2
shift 2
;;
--nvidia)
setup_need_value "$@"
setup_nvidia=$2
shift 2
;;
--partition)
setup_need_value "$@"
setup_partition=$2
shift 2
;;
--disk)
setup_need_value "$@"
setup_disk=$2
shift 2
;;
--swap-size)
setup_need_value "$@"
setup_swap_size=$2
shift 2
;;
--root-part)
setup_need_value "$@"
setup_part_root=$2
shift 2
;;
--efi-part)
setup_need_value "$@"
setup_part_efi=$2
shift 2
;;
--swap-part)
setup_need_value "$@"
setup_part_swap=$2
shift 2
;;
--home-part)
setup_need_value "$@"
setup_part_home=$2
shift 2
;;
--format)
setup_need_value "$@"
setup_format=$2
shift 2
;;
--kernel)
setup_need_value "$@"
setup_kernel=$2
shift 2
;;
--kernel-config)
setup_need_value "$@"
setup_kernel_config=$2
shift 2
;;
--drivers)
setup_need_value "$@"
setup_drivers=$2
shift 2
;;
--init)
setup_need_value "$@"
setup_init=$2
shift 2
;;
--services)
setup_need_value "$@"
setup_services=$2
shift 2
;;
--list-hardware)
setup_list_hardware=1
shift
;;
--bootloader)
setup_need_value "$@"
setup_bootloader=$2
shift 2
;;
--install-bootloader)
setup_install_bootloader=1
shift
;;
--update)
setup_do_update=1
shift
;;
--no-update)
setup_do_update=0
shift
;;
--non-interactive)
setup_interactive=0
shift
;;
--plan)
setup_plan=1
setup_interactive=0
shift
;;
--write-answers)
setup_need_value "$@"
setup_answers_out=$2
shift 2
;;
--read-answers)
setup_need_value "$@"
setup_answers_in=$2
shift 2
;;
--list-profiles)
setup_list_profiles=1
shift
;;
--list-sets)
setup_list_sets=1
shift
;;
--)
shift
break
;;
-*)
setup_fail "$SPS_EX_USAGE" "unknown option: $1"
;;
*)
setup_fail "$SPS_EX_USAGE" "unexpected argument: $1"
;;
esac
done
[ "$#" -eq 0 ] || setup_fail "$SPS_EX_USAGE" "unexpected argument: $1"
setup_field()
{
setup_file=$1
setup_key=$2
awk -v k="$setup_key" '
$1 == k {
$1 = ""
sub(/^[ \t]+/, "")
print
exit
}
' "$setup_file"
}
setup_packages_from()
{
awk '
$1 == "package" {
$1 = ""
sub(/^[ \t]+/, "")
if ($0 != "") print
}
' "$1"
}
setup_profile_file()
{
printf '%s/profiles/%s.profile' "$setup_dir" "$1"
}
setup_set_file()
{
printf '%s/sets/%s.set' "$setup_dir" "$1"
}
setup_each_profile()
{
setup_p=
for setup_p in "$setup_dir"/profiles/*.profile; do
[ -f "$setup_p" ] || continue
setup_field "$setup_p" name
done
}
setup_each_set()
{
setup_s=
for setup_s in "$setup_dir"/sets/*.set; do
[ -f "$setup_s" ] || continue
setup_field "$setup_s" name
done
}
setup_valid_profile()
{
[ -f "$(setup_profile_file "$1")" ]
}
setup_valid_set()
{
[ -f "$(setup_set_file "$1")" ]
}
setup_default_sets_for_profile()
{
setup_ds=
setup_sf=
for setup_sf in "$setup_dir"/sets/*.set; do
[ -f "$setup_sf" ] || continue
setup_sn=$(setup_field "$setup_sf" name)
setup_def=$(setup_field "$setup_sf" default)
[ "$setup_def" = 1 ] || continue
case $setup_profile in
plasma-desktop|plasma-full)
setup_ds="$setup_ds $setup_sn"
;;
*)
# Power and firmware default on for desktops only.
[ "$setup_sn" = power ] && continue
[ "$setup_sn" = firmware ] && continue
[ "$setup_sn" = qol-cli ] &&
setup_ds="$setup_ds $setup_sn"
;;
esac
done
printf '%s' "$setup_ds" | sed 's/^ //'
}
if [ "$setup_list_profiles" -eq 1 ]; then
for setup_pf in "$setup_dir"/profiles/*.profile; do
[ -f "$setup_pf" ] || continue
printf '%s\t%s\t%s\n' \
"$(setup_field "$setup_pf" name)" \
"$(setup_field "$setup_pf" title)" \
"$(setup_field "$setup_pf" description)"
done
exit 0
fi
if [ "$setup_list_sets" -eq 1 ]; then
for setup_sf in "$setup_dir"/sets/*.set; do
[ -f "$setup_sf" ] || continue
printf '%s\t%s\t%s\n' \
"$(setup_field "$setup_sf" name)" \
"$(setup_field "$setup_sf" title)" \
"$(setup_field "$setup_sf" description)"
done
exit 0
fi
if [ "$setup_list_hardware" -eq 1 ]; then
if command -v setup_hw_summary >/dev/null 2>&1; then
setup_hw_summary
printf '\nSuggested packages:\n'
setup_hw_packages | sort -u
else
setup_fail "$SPS_EX_NOTFOUND" "hardware detection is not available"
fi
exit 0
fi
setup_load_answers()
{
setup_af=$1
[ -f "$setup_af" ] && [ -r "$setup_af" ] ||
setup_fail "$SPS_EX_NOTFOUND" "cannot read answers file: $setup_af"
while IFS= read -r setup_line || [ -n "$setup_line" ]; do
case $setup_line in
''|\#*) continue ;;
esac
setup_ak=${setup_line%% *}
setup_av=${setup_line#* }
[ "$setup_ak" = "$setup_line" ] && setup_av=
case $setup_ak in
target) [ -n "$setup_target" ] || setup_target=$setup_av ;;
profile) [ -n "$setup_profile" ] || setup_profile=$setup_av ;;
hostname) [ -n "$setup_hostname" ] || setup_hostname=$setup_av ;;
timezone) [ -n "$setup_timezone" ] || setup_timezone=$setup_av ;;
keymap) [ -n "$setup_keymap" ] || setup_keymap=$setup_av ;;
locale) [ -n "$setup_locale" ] || setup_locale=$setup_av ;;
shell) [ -n "$setup_shell" ] || setup_shell=$setup_av ;;
user) [ -n "$setup_user" ] || setup_user=$setup_av ;;
user-gecos) [ -n "$setup_user_gecos" ] || setup_user_gecos=$setup_av ;;
user-password-hash)
[ -n "$setup_user_password_hash" ] ||
setup_user_password_hash=$setup_av
;;
root-password-hash)
[ -n "$setup_root_password_hash" ] ||
setup_root_password_hash=$setup_av
;;
extra) setup_append_csv setup_extra "$setup_av" "$setup_extra" ;;
ssh) [ -n "$setup_ssh" ] || setup_ssh=$setup_av ;;
printing) [ -n "$setup_printing" ] || setup_printing=$setup_av ;;
flatpak) [ -n "$setup_flatpak" ] || setup_flatpak=$setup_av ;;
nvidia) [ -n "$setup_nvidia" ] || setup_nvidia=$setup_av ;;
partition) [ -n "$setup_partition" ] || setup_partition=$setup_av ;;
disk) [ -n "$setup_disk" ] || setup_disk=$setup_av ;;
swap-size) [ "$setup_swap_size" = 8GiB ] && [ -n "$setup_av" ] && setup_swap_size=$setup_av ;;
root-part) [ -n "$setup_part_root" ] || setup_part_root=$setup_av ;;
efi-part) [ -n "$setup_part_efi" ] || setup_part_efi=$setup_av ;;
swap-part) [ -n "$setup_part_swap" ] || setup_part_swap=$setup_av ;;
home-part) [ -n "$setup_part_home" ] || setup_part_home=$setup_av ;;
format) [ -n "$setup_format" ] || setup_format=$setup_av ;;
kernel) [ -n "$setup_kernel" ] || setup_kernel=$setup_av ;;
kernel-config) [ -n "$setup_kernel_config" ] || setup_kernel_config=$setup_av ;;
drivers) [ -n "$setup_drivers" ] || setup_drivers=$setup_av ;;
init) [ -n "$setup_init" ] || setup_init=$setup_av ;;
services) [ -n "$setup_services" ] || setup_services=$setup_av ;;
bootloader) [ "$setup_bootloader" = none ] && [ -n "$setup_av" ] && setup_bootloader=$setup_av ;;
install-bootloader)
[ "$setup_av" = yes ] && setup_install_bootloader=1
[ "$setup_av" = no ] && setup_install_bootloader=0
;;
enable) setup_append_csv setup_enable "$setup_av" "$setup_enable" ;;
disable) setup_append_csv setup_disable "$setup_av" "$setup_disable" ;;
update)
[ "$setup_av" = no ] && setup_do_update=0
[ "$setup_av" = yes ] && setup_do_update=1
;;
esac
done <"$setup_af"
}
[ -z "$setup_answers_in" ] || setup_load_answers "$setup_answers_in"
setup_yesno_norm()
{
case $1 in
yes|y|true|1) printf 'yes\n' ;;
no|n|false|0|'') printf 'no\n' ;;
*) setup_fail "$SPS_EX_USAGE" "expected yes or no, got '$1'" ;;
esac
}
setup_dialog()
{
command -v dialog >/dev/null 2>&1 ||
setup_fail "$SPS_EX_PACKAGE" \
"dialog is not on PATH; install it or use --non-interactive"
dialog --backtitle "SPS Linux installer (Splux)" --output-fd 1 "$@"
}
setup_msg()
{
if [ "$setup_interactive" -eq 1 ]; then
dialog --backtitle "SPS Linux installer (Splux)" --msgbox "$1" 12 64 || :
else
printf '%s\n' "$1"
fi
}
setup_confirm()
{
if [ "$setup_interactive" -eq 1 ]; then
dialog --backtitle "SPS Linux installer (Splux)" --yesno "$1" 14 64
else
return 0
fi
}
setup_pick_disk()
{
setup_dargs=
setup_dline=
setup_dname=
setup_drest=
if ! command -v setup_disk_menu_lines >/dev/null 2>&1; then
setup_fail "$SPS_EX_PACKAGE" "partition helpers are missing"
fi
while IFS= read -r setup_dline || [ -n "$setup_dline" ]; do
[ -n "$setup_dline" ] || continue
setup_dname=${setup_dline%% *}
setup_drest=$setup_dline
# dialog tags cannot contain spaces; NAME is the tag.
setup_dargs="$setup_dargs $setup_dname '$setup_drest'"
done <<EOF
$(setup_disk_menu_lines)
EOF
[ -n "$setup_dargs" ] ||
setup_fail "$SPS_EX_NOTFOUND" "no disks found (is lsblk on PATH?)"
eval "setup_dialog --title 'Select disk' \
--menu 'Guided layouts erase this whole disk. Manage existing opens cfdisk first.' \
18 72 10 $setup_dargs" ||
setup_fail "$SPS_EX_USAGE" "cancelled"
}
setup_pick_partition()
{
# $1 title, $2 allow none
setup_title=$1
setup_allow_none=$2
setup_pargs=
[ "$setup_allow_none" = 1 ] && setup_pargs="none 'No partition'"
setup_pline=
setup_pname=
while IFS= read -r setup_pline || [ -n "$setup_pline" ]; do
[ -n "$setup_pline" ] || continue
setup_pname=${setup_pline%% *}
setup_pargs="$setup_pargs $setup_pname '$setup_pline'"
done <<EOF
$(setup_lsblk -n -p -o NAME,SIZE,TYPE,FSTYPE "$setup_disk" 2>/dev/null | awk '$3 == "part" { print }')
EOF
eval "setup_dialog --title '$setup_title' \
--menu 'Partition on $setup_disk' 18 72 10 $setup_pargs" ||
setup_fail "$SPS_EX_USAGE" "cancelled"
}
setup_ask_partition()
{
if [ -z "$setup_partition" ]; then
setup_partition=$(setup_dialog --title "Disks" \
--menu "Partitioning. Guided layouts wait until the final install confirmation before they write anything. Existing mounts are left alone." \
18 72 8 \
existing "Use mounts already on the target path" \
manage "cfdisk, then pick root / EFI / swap / home" \
efi-root "Guided: GPT, EFI + root" \
efi-swap-root "Guided: GPT, EFI + swap + root" \
efi-swap-root-home "Guided: GPT, EFI + swap + root + home" \
bios-root "Guided: GPT, BIOS boot + root" \
none "Do not touch disks") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
case $setup_partition in
none|existing)
return 0
;;
manage|efi-root|efi-swap-root|efi-swap-root-home|bios-root)
if [ -z "$setup_disk" ]; then
setup_disk=$(setup_pick_disk) ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if command -v setup_partition_tool_ok >/dev/null 2>&1 &&
! setup_partition_tool_ok
then
setup_fail "$SPS_EX_PACKAGE" \
"guided layouts need sfdisk, mkfs.ext4, mkfs.vfat, and mkswap on PATH"
fi
;;
esac
case $setup_partition in
efi-root|efi-swap-root|efi-swap-root-home|bios-root)
if [ -z "$setup_format" ]; then
if setup_dialog --title "Format disk" --yesno \
"Erase ALL data on $setup_disk and write a new $setup_partition partition table?
This is the format confirmation. Non-interactive installs pass --format yes. setup still waits until the last screen before it writes the disk." 14 64
then
setup_format=yes
else
setup_fail "$SPS_EX_USAGE" \
"cancelled (guided layouts need a format confirmation)"
fi
fi
;;
esac
if [ "$setup_partition" = manage ]; then
if command -v cfdisk >/dev/null 2>&1 ||
[ -x /sbin/cfdisk ]; then
setup_cfdisk=$(command -v cfdisk 2>/dev/null || printf /sbin/cfdisk)
setup_msg "cfdisk will open on $setup_disk. Write the table, then quit. setup will ask which partitions to use."
"$setup_cfdisk" "$setup_disk" || :
else
setup_msg "cfdisk is not on PATH. Using the partition table that is already on $setup_disk."
fi
setup_part_root=$(setup_pick_partition "Root partition" 0)
[ "$setup_part_root" != none ] ||
setup_fail "$SPS_EX_USAGE" "root partition is required"
setup_part_efi=$(setup_pick_partition "EFI partition" 1)
[ "$setup_part_efi" != none ] || setup_part_efi=
setup_part_swap=$(setup_pick_partition "Swap partition" 1)
[ "$setup_part_swap" != none ] || setup_part_swap=
setup_part_home=$(setup_pick_partition "Home partition" 1)
[ "$setup_part_home" != none ] || setup_part_home=
if setup_dialog --title "Format" --yesno \
"Format the selected partitions (ext4 / vfat / swap) before mounting them on the target?
This erases data on those partitions only, not necessarily the whole disk." 12 64
then
setup_format=yes
else
setup_format=no
fi
fi
}
setup_ask_kernel()
{
if [ -n "$setup_kernel" ]; then
return 0
fi
setup_kdef=none
case $setup_profile in
plasma-desktop|plasma-full) setup_kdef=linux-desktop ;;
esac
setup_kernel=$(setup_dialog --title "SPS Linux kernel" --default-item "$setup_kdef" \
--menu "none leaves the host kernel story alone (current live ISO). linux is upstream defconfig. linux-desktop is the SPS Linux (Splux) daily-driver config: DRM (nouveau/i915/amdgpu), NVMe, audio, extra filesystems. custom copies a .config you already have." \
18 72 6 \
none "Do not build a kernel package" \
linux-desktop "SPS Linux desktop config (Splux; Plasma default)" \
linux "Upstream defconfig" \
custom "Use a .config from this machine") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
if [ "$setup_kernel" = custom ] && [ -z "$setup_kernel_config" ]; then
setup_kernel_config=$(setup_dialog --title "Kernel config" \
--inputbox "Absolute path to a Linux .config. It is copied to the target as /etc/sps/kernel.config before the kernel package builds." \
10 72 "${setup_kernel_config:-/etc/sps/kernel.config}") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
# If the user just hit enter on the default-looking first item, keep the
# profile default when they picked none on a Plasma install? No: none is
# an explicit choice. Profile default only applies when the flag is omitted
# in non-interactive mode.
:
}
setup_ask_init()
{
if [ -n "$setup_init" ]; then
return 0
fi
setup_init_default=systemd
if [ -f /etc/sps/init ]; then
setup_live_init=$(sed -n '1p' /etc/sps/init)
case $setup_live_init in
systemd|openrc|s6|runit|dinit|shepherd|sysvinit)
setup_init_default=$setup_live_init
;;
esac
fi
setup_init=$(setup_dialog --title "Init system" --default-item "$setup_init_default" \
--menu "PID 1 on the installed system. These packages conflict; sget will refuse more than one. Non-systemd inits pull eudev and elogind. Daemon packages ship scripts for every init into the right directories; setup enables the detected init." \
22 76 9 \
systemd "systemd (udev, logind, the default)" \
openrc "OpenRC plus eudev and elogind" \
s6 "s6 plus eudev and elogind" \
runit "runit plus eudev and elogind" \
dinit "dinit plus eudev and elogind" \
shepherd "GNU Shepherd plus eudev, elogind, and Guile" \
sysvinit "SysV init plus eudev and elogind") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
}
setup_ask_drivers()
{
if [ -n "$setup_drivers" ]; then
return 0
fi
setup_hwtxt="Could not probe PCI."
if command -v setup_hw_summary >/dev/null 2>&1; then
setup_hwtxt=$(setup_hw_summary)
fi
if setup_dialog --title "Drivers and microcode" --yesno \
"Detect hardware and install matching packages from the repos?
$setup_hwtxt
This pulls CPU microcode, Mesa/nouveau, firmware, and wireless helpers. Proprietary NVIDIA modules stay off unless you enable them on the next screen." 18 72
then
setup_drivers=auto
else
setup_drivers=none
fi
}
# Interactive defaults when flags were omitted.
if [ "$setup_interactive" -eq 1 ] && [ ! -t 0 ]; then
setup_fail "$SPS_EX_USAGE" \
"stdin is not a terminal; use --non-interactive"
fi
if [ "$setup_interactive" -eq 1 ]; then
command -v dialog >/dev/null 2>&1 ||
setup_fail "$SPS_EX_PACKAGE" \
"dialog is required for interactive setup"
fi
setup_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" setup) || exit $?
trap setup_cleanup 0 HUP INT TERM
setup_collect_recipe_names "$setup_work/recipe-names"
if [ "$setup_interactive" -eq 1 ]; then
if [ -f /etc/sps/live ]; then
setup_msg \
"This is the SPS Linux live image (Splux). You can partition a disk here, or mount something on /mnt first and choose Use existing mounts.
Guided layouts wait until the last confirmation before they erase a disk. Bootloader install is a separate, explicit step (needs --install-bootloader and --disk)."
else
setup_msg \
"This installer configures SPS Linux (Splux) the way Slackware and FreeBSD setup do: disks, profile, kernel, drivers, then a visible package install.
Guided partition layouts erase the chosen disk only after you confirm at the end. grub-install is never implied. An LFS tree using SPS does not have to go through this installer."
fi
setup_ask_partition
if [ -z "$setup_target" ]; then
setup_target=$(setup_dialog --title "Installation target" \
--inputbox "Absolute path of the target root.
Use /mnt after mounting the destination filesystem, or after a guided layout (setup mounts it for you at the end). / is the live system." \
10 64 "${setup_target:-/mnt}") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -z "$setup_profile" ]; then
setup_menu_args=
for setup_pf in "$setup_dir"/profiles/*.profile; do
[ -f "$setup_pf" ] || continue
setup_pn=$(setup_field "$setup_pf" name)
setup_pt=$(setup_field "$setup_pf" title)
setup_menu_args="$setup_menu_args $setup_pn '$setup_pt'"
done
setup_profile=$(eval "setup_dialog --title 'Installation profile' \
--menu 'Choose a base system.' 16 64 8 $setup_menu_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
setup_ask_kernel
setup_ask_init
if [ -z "$setup_hostname" ]; then
setup_hostname=$(setup_dialog --title "Hostname" \
--inputbox "System hostname." 8 50 "${setup_hostname:-splux}") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -z "$setup_timezone" ]; then
setup_tz_args=
while IFS= read -r setup_tz || [ -n "$setup_tz" ]; do
[ -n "$setup_tz" ] || continue
setup_tz_args="$setup_tz_args $setup_tz '$setup_tz'"
done <"$setup_dir/timezones"
setup_timezone=$(eval "setup_dialog --title 'Timezone' \
--menu 'Select a timezone.' 20 60 12 $setup_tz_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -z "$setup_locale" ]; then
setup_loc_args=
while IFS= read -r setup_loc || [ -n "$setup_loc" ]; do
[ -n "$setup_loc" ] || continue
setup_loc_args="$setup_loc_args $setup_loc '$setup_loc'"
done <"$setup_dir/locales"
setup_locale=$(eval "setup_dialog --title 'Locale' \
--menu 'LANG for /etc/locale.conf. C.UTF-8 needs no generated locales.' 20 60 12 $setup_loc_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -z "$setup_keymap" ]; then
setup_km_args=
while IFS= read -r setup_km || [ -n "$setup_km" ]; do
[ -n "$setup_km" ] || continue
setup_km_args="$setup_km_args $setup_km '$setup_km'"
done <"$setup_dir/keymaps"
setup_keymap=$(eval "setup_dialog --title 'Keyboard' \
--menu 'Keyboard map for the console.' 18 50 12 $setup_km_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
setup_chk_args=
for setup_sf in "$setup_dir"/sets/*.set; do
[ -f "$setup_sf" ] || continue
setup_sn=$(setup_field "$setup_sf" name)
setup_st=$(setup_field "$setup_sf" title)
setup_sd=$(setup_field "$setup_sf" description)
setup_on=off
case " $setup_enable " in *" $setup_sn "*) setup_on=on ;; esac
if [ -z "$setup_enable" ]; then
setup_def=$(setup_field "$setup_sf" default)
if [ "$setup_def" = 1 ]; then
case $setup_profile in
plasma-desktop|plasma-full) setup_on=on ;;
*)
[ "$setup_sn" = qol-cli ] && setup_on=on
[ "$setup_sn" = power ] && setup_on=off
[ "$setup_sn" = firmware ] && setup_on=off
;;
esac
fi
fi
case " $setup_disable " in *" $setup_sn "*) setup_on=off ;; esac
setup_chk_args="$setup_chk_args $setup_sn '$setup_st' $setup_on"
done
setup_sel=$(eval "setup_dialog --title 'Optional sets' \
--checklist 'Space toggles a set. These are installed in addition to the profile.' \
20 72 12 $setup_chk_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
setup_enable=
# dialog may quote tags
set -- $setup_sel
for setup_tok; do
setup_tok=${setup_tok#\"}
setup_tok=${setup_tok%\"}
[ -n "$setup_tok" ] || continue
setup_append_csv setup_enable "$setup_tok" "$setup_enable"
done
if [ -f "$setup_dir/extras" ]; then
setup_ex_args=
while IFS= read -r setup_exline || [ -n "$setup_exline" ]; do
[ -n "$setup_exline" ] || continue
case $setup_exline in \#*) continue ;; esac
setup_en=${setup_exline%% *}
setup_et=${setup_exline#* }
[ -n "$setup_en" ] || continue
[ "$setup_et" != "$setup_exline" ] || setup_et=$setup_en
# Live recipe trees are the source of truth. Unpackaged
# names (mpv, LibreOffice, ...) must not reach sget.
if [ -s "$setup_work/recipe-names" ] &&
! grep -qx "$setup_en" "$setup_work/recipe-names"
then
printf '%s: skipping unpackaged extra %s\n' \
"$sps_prog" "$setup_en" >&2
continue
fi
setup_on=off
case " $setup_extra " in *" $setup_en "*) setup_on=on ;; esac
setup_ex_args="$setup_ex_args $setup_en '$setup_et' $setup_on"
done <"$setup_dir/extras"
if [ -n "$setup_ex_args" ]; then
setup_sel=$(eval "setup_dialog --title 'Extra packages' \
--checklist 'Optional packages on top of the profile and sets. Space toggles an item.' \
22 72 14 $setup_ex_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
set -- $setup_sel
for setup_tok; do
setup_tok=${setup_tok#\"}
setup_tok=${setup_tok%\"}
[ -n "$setup_tok" ] || continue
setup_append_csv setup_extra "$setup_tok" "$setup_extra"
done
fi
fi
setup_ask_drivers
if [ -z "$setup_nvidia" ]; then
if setup_dialog --title "NVIDIA proprietary" --yesno \
"Include NVIDIA open kernel modules and user-space utilities?
The live image and auto drivers use nouveau. This extra set is the proprietary/open-rm stack and needs a matching kernel on the target. Off by default." 12 64
then
setup_nvidia=yes
else
setup_nvidia=no
fi
fi
if [ -z "$setup_flatpak" ]; then
if setup_dialog --title "Flatpak" --yesno \
"Install Flatpak and add the Flathub remote after package installation?" 9 64
then
setup_flatpak=yes
else
setup_flatpak=no
fi
fi
if [ -z "$setup_user" ]; then
setup_user=$(setup_dialog --title "First user" \
--inputbox "Unprivileged account (uid 1000). Leave empty to skip." \
9 50 "${setup_user:-}") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -z "$setup_user_gecos" ] && [ -n "$setup_user" ]; then
setup_user_gecos=$(setup_dialog --title "User full name" \
--inputbox "GECOS / display name for $setup_user." \
8 50 "$setup_user") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
if [ -n "$setup_user" ] && [ -z "$setup_user_password_hash" ]; then
setup_user_password_hash=$(setup_ask_password_pair "User password" \
"Password for $setup_user. Leave empty to lock the account until you run passwd after boot.")
fi
if [ -z "$setup_root_password_hash" ]; then
setup_root_password_hash=$(setup_ask_password_pair "Root password" \
"Password for root. Leave empty to keep root locked.")
fi
if [ -z "$setup_shell" ]; then
setup_sh_args=
if [ -f "$setup_dir/shells" ]; then
while IFS= read -r setup_sh || [ -n "$setup_sh" ]; do
[ -n "$setup_sh" ] || continue
setup_sh_args="$setup_sh_args $setup_sh '$setup_sh'"
done <"$setup_dir/shells"
fi
[ -n "$setup_sh_args" ] || setup_sh_args="/bin/sh '/bin/sh' /bin/bash '/bin/bash'"
setup_shell=$(eval "setup_dialog --title 'Login shell' \
--menu 'Login shell for the first user, or the default account later. zsh and fish need the shells-extra set.' 16 64 6 $setup_sh_args") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
setup_dialog --title "Network" --msgbox \
"This installer does not configure DHCP, static addresses, or wireless.
After the first boot, put an interface file in /etc/network or use the NetworkManager applet on Plasma. Notes are written to /root/sps-network.txt." 12 64 || :
setup_bl=$(setup_dialog --title "Bootloader" \
--menu "SPS never guesses a grub-install target. Notes are always written to /root/sps-bootloader.txt. Install GRUB only if you pick the last item and confirm the disk." \
16 72 4 \
none "Write /root/sps-bootloader.txt only" \
grub "Write GRUB notes (still does not install)" \
install "Install GRUB to the chosen disk (asks again)") ||
setup_fail "$SPS_EX_USAGE" "cancelled"
case $setup_bl in
install)
setup_bootloader=grub
if [ -z "$setup_disk" ]; then
setup_msg "No disk was selected. Writing GRUB notes only. Pass a disk on the partition screen, or run grub-install yourself later."
elif setup_dialog --title "Install GRUB" --yesno \
"Run grub-install on $setup_disk now?
This is the only interactive path that installs a bootloader. Scripts still need --install-bootloader and --disk together." 12 64
then
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH
export PATH
if command -v grub-install >/dev/null 2>&1; then
setup_install_bootloader=1
else
setup_msg "grub-install is not on PATH. Notes will be written; install GRUB after the packages land or from another system."
fi
fi
;;
*)
setup_bootloader=$setup_bl
;;
esac
fi
# Non-interactive defaults
[ -n "$setup_target" ] || setup_target=/mnt
[ -n "$setup_profile" ] || setup_profile=minimal
[ -n "$setup_hostname" ] || setup_hostname=splux
[ -n "$setup_timezone" ] || setup_timezone=UTC
[ -n "$setup_keymap" ] || setup_keymap=us
[ -n "$setup_locale" ] || setup_locale=C.UTF-8
[ -n "$setup_shell" ] || setup_shell=/bin/sh
setup_flatpak=$(setup_yesno_norm "${setup_flatpak:-no}")
setup_nvidia=$(setup_yesno_norm "${setup_nvidia:-no}")
setup_ssh=$(setup_yesno_norm "${setup_ssh:-no}")
setup_printing=$(setup_yesno_norm "${setup_printing:-no}")
[ -n "$setup_partition" ] || setup_partition=none
[ -n "$setup_drivers" ] || setup_drivers=none
[ -n "$setup_init" ] || setup_init=none
[ -n "$setup_format" ] || setup_format=no
setup_format=$(setup_yesno_norm "$setup_format")
setup_services=$(setup_yesno_norm "${setup_services:-yes}")
if [ -z "$setup_kernel" ]; then
setup_kernel=none
fi
case $setup_kernel in
none|linux|linux-desktop|custom) ;;
*) setup_fail "$SPS_EX_USAGE" "kernel must be none, linux, linux-desktop, or custom" ;;
esac
case $setup_drivers in
auto|none) ;;
*) setup_fail "$SPS_EX_USAGE" "drivers must be auto or none" ;;
esac
case $setup_init in
none|systemd|openrc|s6|runit|dinit|shepherd|sysvinit) ;;
*) setup_fail "$SPS_EX_USAGE" "init must be systemd, openrc, s6, runit, dinit, shepherd, sysvinit, or none" ;;
esac
case $setup_services in
yes|no) ;;
*) setup_fail "$SPS_EX_USAGE" "services must be yes or no" ;;
esac
case $setup_partition in
none|existing|efi-root|efi-swap-root|efi-swap-root-home|bios-root|manage) ;;
*) setup_fail "$SPS_EX_USAGE" "unknown partition layout: $setup_partition" ;;
esac
case $setup_partition in
efi-root|efi-swap-root|efi-swap-root-home|bios-root)
[ -n "$setup_disk" ] ||
setup_fail "$SPS_EX_USAGE" "guided --partition needs --disk"
case $setup_disk in
/dev/*) ;;
*) setup_fail "$SPS_EX_USAGE" "disk must be a /dev path" ;;
esac
if [ "$setup_plan" -eq 0 ] && [ "$setup_format" != yes ]; then
setup_fail "$SPS_EX_USAGE" \
"guided partitioning requires --format yes (refusing to erase $setup_disk)"
fi
if [ "$setup_plan" -eq 0 ]; then
if command -v setup_partition_tool_ok >/dev/null 2>&1 &&
! setup_partition_tool_ok
then
setup_fail "$SPS_EX_PACKAGE" \
"guided layouts need sfdisk, mkfs.ext4, mkfs.vfat, and mkswap on PATH"
fi
fi
;;
manage)
[ -n "$setup_part_root" ] ||
setup_fail "$SPS_EX_USAGE" "--partition manage needs --root-part"
;;
esac
if [ "$setup_kernel" = custom ]; then
[ -n "$setup_kernel_config" ] ||
setup_fail "$SPS_EX_USAGE" "--kernel custom requires --kernel-config FILE"
case $setup_kernel_config in
/*) ;;
*) setup_fail "$SPS_EX_USAGE" "kernel config must be an absolute path" ;;
esac
[ -f "$setup_kernel_config" ] ||
setup_fail "$SPS_EX_NOTFOUND" "kernel config not found: $setup_kernel_config"
fi
if [ "$setup_install_bootloader" -eq 1 ]; then
[ -n "$setup_disk" ] ||
setup_fail "$SPS_EX_USAGE" \
"--install-bootloader requires --disk; refusing to guess"
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH
export PATH
if [ "$setup_plan" -eq 0 ] && ! command -v grub-install >/dev/null 2>&1; then
setup_fail "$SPS_EX_PACKAGE" "grub-install is not on PATH"
fi
fi
case $setup_bootloader in
none|grub) ;;
*) setup_fail "$SPS_EX_USAGE" "bootloader must be none or grub" ;;
esac
setup_valid_profile "$setup_profile" ||
setup_fail "$SPS_EX_NOTFOUND" "unknown profile: $setup_profile"
case $setup_target in
/*) ;;
*) setup_fail "$SPS_EX_USAGE" "target must be an absolute path: $setup_target" ;;
esac
case $setup_hostname in
*[!A-Za-z0-9.-]*|'') setup_fail "$SPS_EX_USAGE" "invalid hostname: $setup_hostname" ;;
esac
case $setup_locale in
*[!A-Za-z0-9._+-]*|'') setup_fail "$SPS_EX_USAGE" "invalid locale: $setup_locale" ;;
esac
case $setup_shell in
/*) ;;
*) setup_fail "$SPS_EX_USAGE" "shell must be an absolute path: $setup_shell" ;;
esac
if [ -n "$setup_user" ]; then
case $setup_user in
*[!a-z0-9_-]*|''|root|nobody)
setup_fail "$SPS_EX_USAGE" "invalid user name: $setup_user"
;;
esac
fi
# Non-interactive installs start from profile defaults, then --enable.
if [ "$setup_interactive" -eq 0 ]; then
setup_defs=$(setup_default_sets_for_profile)
for setup_def in $setup_defs; do
setup_append_csv setup_enable "$setup_def" "$setup_enable"
done
fi
# Apply nvidia/flatpak convenience flags to sets.
if [ "$setup_nvidia" = yes ]; then
setup_append_csv setup_enable nvidia "$setup_enable"
fi
if [ "$setup_flatpak" = yes ]; then
setup_append_csv setup_enable flatpak "$setup_enable"
fi
if [ "$setup_ssh" = yes ]; then
setup_append_csv setup_enable ssh "$setup_enable"
fi
if [ "$setup_printing" = yes ]; then
setup_append_csv setup_enable printing "$setup_enable"
fi
case $setup_shell in
*/zsh|*/fish)
setup_append_csv setup_enable shells-extra "$setup_enable"
;;
esac
# --disable wins
setup_en_new=
for setup_item in $setup_enable; do
setup_skip=0
for setup_d in $setup_disable; do
[ "$setup_item" = "$setup_d" ] && setup_skip=1
done
[ "$setup_skip" -eq 0 ] || continue
setup_valid_set "$setup_item" ||
setup_fail "$SPS_EX_NOTFOUND" "unknown set: $setup_item"
setup_en_new="$setup_en_new $setup_item"
done
setup_enable=$(printf '%s' "$setup_en_new" | sed 's/^ //')
setup_pkg_file=$setup_work/packages
: >"$setup_pkg_file"
setup_add_pkg()
{
setup_pkg=$1
[ -n "$setup_pkg" ] || return 0
grep -qx "$setup_pkg" "$setup_pkg_file" 2>/dev/null && return 0
printf '%s\n' "$setup_pkg" >>"$setup_pkg_file"
}
setup_packages_from "$(setup_profile_file "$setup_profile")" |
while IFS= read -r setup_pkg; do
setup_add_pkg "$setup_pkg"
done
# The pipeline subshell cannot update the file... actually it can, the file is
# in the filesystem. setup_add_pkg appends. Good.
for setup_item in $setup_enable; do
setup_packages_from "$(setup_set_file "$setup_item")" |
while IFS= read -r setup_pkg; do
setup_add_pkg "$setup_pkg"
done
done
for setup_item in $setup_extra; do
setup_add_pkg "$setup_item"
done
# Chosen init wins over set/package combinations that sget would refuse.
for setup_item in $setup_extra $setup_enable; do
[ -n "$setup_item" ] || continue
if setup_pkg_conflicts_init "$setup_init" "$setup_item"; then
setup_fail "$SPS_EX_CONFLICT" \
"cannot combine --init $setup_init with $setup_item"
fi
done
setup_pkg_filtered=$setup_work/packages.filtered
: >"$setup_pkg_filtered"
while IFS= read -r setup_pkg || [ -n "$setup_pkg" ]; do
[ -n "$setup_pkg" ] || continue
if setup_pkg_conflicts_init "$setup_init" "$setup_pkg"; then
printf '%s: dropping %s (conflicts with init %s)\n' \
"$sps_prog" "$setup_pkg" "$setup_init" >&2
continue
fi
printf '%s\n' "$setup_pkg" >>"$setup_pkg_filtered"
done <"$setup_pkg_file"
mv "$setup_pkg_filtered" "$setup_pkg_file"
case $setup_kernel in
linux) setup_add_pkg linux ;;
linux-desktop) setup_add_pkg linux-desktop ;;
custom) setup_add_pkg linux-custom ;;
esac
case $setup_init in
systemd) setup_add_pkg init-systemd ;;
openrc) setup_add_pkg init-openrc ;;
s6) setup_add_pkg init-s6 ;;
runit) setup_add_pkg init-runit ;;
dinit) setup_add_pkg init-dinit ;;
shepherd) setup_add_pkg init-shepherd ;;
sysvinit) setup_add_pkg init-sysvinit ;;
esac
if [ "$setup_drivers" = auto ]; then
if command -v setup_hw_packages >/dev/null 2>&1; then
setup_hw_packages | sort -u |
while IFS= read -r setup_pkg; do
[ -n "$setup_pkg" ] || continue
setup_add_pkg "$setup_pkg"
done
fi
fi
setup_write_answers_file()
{
setup_out=$1
{
printf 'target %s\n' "$setup_target"
printf 'profile %s\n' "$setup_profile"
printf 'hostname %s\n' "$setup_hostname"
printf 'timezone %s\n' "$setup_timezone"
printf 'keymap %s\n' "$setup_keymap"
printf 'locale %s\n' "$setup_locale"
printf 'shell %s\n' "$setup_shell"
[ -n "$setup_user" ] && printf 'user %s\n' "$setup_user"
[ -n "$setup_user_gecos" ] && printf 'user-gecos %s\n' "$setup_user_gecos"
printf 'flatpak %s\n' "$setup_flatpak"
printf 'nvidia %s\n' "$setup_nvidia"
printf 'ssh %s\n' "$setup_ssh"
printf 'printing %s\n' "$setup_printing"
printf 'bootloader %s\n' "$setup_bootloader"
printf 'partition %s\n' "$setup_partition"
[ -n "$setup_disk" ] && printf 'disk %s\n' "$setup_disk"
printf 'swap-size %s\n' "$setup_swap_size"
[ -n "$setup_part_root" ] && printf 'root-part %s\n' "$setup_part_root"
[ -n "$setup_part_efi" ] && printf 'efi-part %s\n' "$setup_part_efi"
[ -n "$setup_part_swap" ] && printf 'swap-part %s\n' "$setup_part_swap"
[ -n "$setup_part_home" ] && printf 'home-part %s\n' "$setup_part_home"
printf 'format %s\n' "$setup_format"
printf 'kernel %s\n' "$setup_kernel"
[ -n "$setup_kernel_config" ] && printf 'kernel-config %s\n' "$setup_kernel_config"
printf 'drivers %s\n' "$setup_drivers"
printf 'init %s\n' "$setup_init"
printf 'services %s\n' "$setup_services"
printf 'install-bootloader %s\n' \
"$([ "$setup_install_bootloader" -eq 1 ] && printf yes || printf no)"
printf 'update %s\n' "$([ "$setup_do_update" -eq 1 ] && printf yes || printf no)"
for setup_item in $setup_enable; do
printf 'enable %s\n' "$setup_item"
done
for setup_item in $setup_extra; do
printf 'extra %s\n' "$setup_item"
done
} >"$setup_out"
}
setup_summary()
{
printf 'Target: %s\n' "$setup_target"
printf 'Profile: %s\n' "$setup_profile"
printf 'Hostname: %s\n' "$setup_hostname"
printf 'Timezone: %s\n' "$setup_timezone"
printf 'Keymap: %s\n' "$setup_keymap"
printf 'Locale: %s\n' "$setup_locale"
printf 'Shell: %s\n' "$setup_shell"
printf 'User: %s\n' "${setup_user:-<none>}"
if [ -n "$setup_user_password_hash" ]; then
printf 'User pw: set\n'
elif [ -n "$setup_user" ]; then
printf 'User pw: locked\n'
fi
if [ -n "$setup_root_password_hash" ]; then
printf 'Root pw: set\n'
else
printf 'Root pw: locked\n'
fi
printf 'Sets: %s\n' "${setup_enable:-<none>}"
printf 'Extra: %s\n' "${setup_extra:-<none>}"
printf 'Flatpak: %s\n' "$setup_flatpak"
printf 'NVIDIA: %s\n' "$setup_nvidia"
printf 'SSH: %s\n' "$setup_ssh"
printf 'Printing: %s\n' "$setup_printing"
printf 'Bootloader: %s\n' "$setup_bootloader"
printf 'Partition: %s\n' "$setup_partition"
printf 'Disk: %s\n' "${setup_disk:-<none>}"
printf 'Kernel: %s\n' "$setup_kernel"
printf 'Init: %s\n' "$setup_init"
printf 'Drivers: %s\n' "$setup_drivers"
printf 'Services: %s\n' "$setup_services"
printf 'Format: %s\n' "$setup_format"
if [ "$setup_install_bootloader" -eq 1 ]; then
printf 'GRUB: install on %s\n' "$setup_disk"
fi
if [ "$setup_init" = none ] &&
grep -Eq '^(sddm|plasma-desktop|plasma-full)$' "$setup_pkg_file"
then
printf '\nNote: init is none; SDDM/Plasma will not start at boot. Choose systemd, OpenRC, s6, runit, dinit, Shepherd, or SysV.\n'
fi
if [ "$setup_kernel" = none ]; then
case $setup_partition in
efi-root|efi-swap-root|efi-swap-root-home|bios-root)
printf '\nNote: no kernel package was selected; the installed disk will not boot until you add one.\n'
;;
esac
fi
printf '\nPackages:\n'
sort -u "$setup_pkg_file"
}
if [ -n "$setup_answers_out" ]; then
setup_write_answers_file "$setup_answers_out"
fi
if [ "$setup_plan" -eq 1 ]; then
setup_summary
exit 0
fi
if [ "$setup_interactive" -eq 1 ]; then
setup_warn=
case $setup_partition in
efi-root|efi-swap-root|efi-swap-root-home|bios-root)
setup_warn="
THIS ERASES $setup_disk. Partition table, filesystems, all of it."
;;
manage)
if [ "$setup_format" = yes ]; then
setup_warn="
Selected partitions will be formatted: $setup_part_root $setup_part_efi $setup_part_swap $setup_part_home"
fi
;;
esac
setup_confirm "Install with this configuration?
$(setup_summary)
$setup_warn
Package builds run on this terminal. Source packages may take a long time." ||
setup_fail "$SPS_EX_USAGE" "cancelled"
fi
# Guided layouts run here, after the last chance to cancel.
case $setup_partition in
none|existing) ;;
*)
if [ "$setup_plan" -eq 0 ]; then
printf '%s: applying partition layout %s on %s\n' \
"$sps_prog" "$setup_partition" "${setup_disk:-partitions}" >&2
PATH=/sbin:/usr/sbin:$PATH
export PATH
setup_apply_partition "$setup_target" ||
setup_fail "$SPS_EX_PERMISSION" "partitioning failed"
fi
;;
esac
setup_apply_config()
{
setup_etc=$setup_root/etc
mkdir -p "$setup_etc/sps" "$setup_root/root" \
"$setup_etc" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create target directories"
printf '%s\n' "$setup_hostname" >"$setup_etc/hostname"
printf '%s\n' "$setup_timezone" >"$setup_etc/timezone"
printf 'KEYMAP=%s\n' "$setup_keymap" >"$setup_etc/vconsole.conf"
printf 'keymap %s\n' "$setup_keymap" >"$setup_etc/sps/keymap"
printf 'LANG=%s\n' "$setup_locale" >"$setup_etc/locale.conf"
printf '%s\n' "${setup_init:-none}" >"$setup_etc/sps/init"
if [ -f /usr/share/zoneinfo/"$setup_timezone" ]; then
cp /usr/share/zoneinfo/"$setup_timezone" "$setup_etc/localtime" 2>/dev/null || :
fi
setup_write_answers_file "$setup_etc/sps/setup-answers"
# Installed-system paths. src update during setup used the live
# system's bundled trees; the installed machine tracks the official
# Git remotes so it keeps receiving recipe updates.
setup_write_repos_conf ""
if [ ! -f "$setup_etc/passwd" ]; then
printf '%s\n' \
'root:x:0:0:root:/root:/bin/sh' \
'nobody:x:65534:65534:Unprivileged User:/:/usr/bin/nologin' \
>"$setup_etc/passwd"
printf '%s\n' 'root:x:0:' 'wheel:x:10:' 'audio:x:17:' \
'video:x:18:' 'nogroup:x:65534:' \
>"$setup_etc/group"
printf '%s\n' \
'root:!:0:0:99999:7:::' \
'nobody:!:0:0:99999:7:::' \
>"$setup_etc/shadow"
fi
setup_group_ensure "$setup_etc/group" wheel 10
setup_group_ensure "$setup_etc/group" audio 17
setup_group_ensure "$setup_etc/group" video 18
setup_group_ensure "$setup_etc/group" input 97
setup_group_ensure "$setup_etc/group" kvm 36
setup_group_ensure "$setup_etc/group" users 100
if [ -f "$setup_etc/shadow" ] && ! grep -q '^root:' "$setup_etc/shadow"
then
printf '%s\n' 'root:!:0:0:99999:7:::' >>"$setup_etc/shadow"
fi
if [ -n "$setup_user" ]; then
setup_home=/home/$setup_user
mkdir -p "$setup_root$setup_home"
if ! grep -q "^$setup_user:" "$setup_etc/passwd"; then
printf '%s:x:1000:1000:%s:%s:%s\n' \
"$setup_user" \
"${setup_user_gecos:-$setup_user}" \
"$setup_home" \
"$setup_shell" >>"$setup_etc/passwd"
fi
if [ -f "$setup_etc/group" ] &&
! grep -q "^$setup_user:" "$setup_etc/group"; then
printf '%s:x:1000:\n' "$setup_user" >>"$setup_etc/group"
fi
if [ -f "$setup_etc/shadow" ] &&
! grep -q "^$setup_user:" "$setup_etc/shadow"; then
printf '%s:!:0:0:99999:7:::\n' "$setup_user" >>"$setup_etc/shadow"
fi
for setup_g in wheel audio video input kvm users
do
setup_group_add_user "$setup_etc/group" "$setup_g" "$setup_user"
done
chown -R 1000:1000 "$setup_root$setup_home" 2>/dev/null || :
chmod 0750 "$setup_root$setup_home" 2>/dev/null || :
setup_shadow_set_hash "$setup_etc/shadow" "$setup_user" \
"$setup_user_password_hash"
if [ -n "$setup_user_password_hash" ]; then
cat >"$setup_root/root/sps-user.txt" <<EOF
The account '$setup_user' (uid 1000) was recorded in /etc/passwd.
The password was set during setup. The account is in wheel, audio, video,
input, kvm, and users when those groups exist.
EOF
else
cat >"$setup_root/root/sps-user.txt" <<EOF
The account '$setup_user' (uid 1000) was recorded in /etc/passwd.
The password is locked. From a live root run:
passwd $setup_user
EOF
fi
fi
setup_shadow_set_hash "$setup_etc/shadow" root "$setup_root_password_hash"
if [ -n "$setup_root_password_hash" ]; then
mkdir -p "$setup_root/root"
if [ -f "$setup_root/root/sps-user.txt" ]; then
printf '%s\n' 'The root password was set during setup.' \
>>"$setup_root/root/sps-user.txt"
else
printf '%s\n' 'The root password was set during setup.' \
>"$setup_root/root/sps-user.txt"
fi
elif [ ! -f "$setup_root/root/sps-user.txt" ]; then
mkdir -p "$setup_root/root"
printf '%s\n' 'The root password is locked. Run passwd after boot.' \
>"$setup_root/root/sps-user.txt"
fi
cat >"$setup_root/root/sps-bootloader.txt" <<EOF
SPS does not run grub-install or change EFI variables during setup.
To install GRUB yourself, after chrooting into $setup_root:
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=SPS
grub-mkconfig -o /boot/grub/grub.cfg
Or for BIOS:
grub-install /dev/DISK
grub-mkconfig -o /boot/grub/grub.cfg
Replace DISK with the actual disk. This is an administrator action.
EOF
cat >"$setup_root/root/sps-init.txt" <<EOF
Init choice: ${setup_init:-none}
These PID 1 packages conflict; sget will not install more than one.
After the packages land:
systemd PID 1 is /usr/lib/systemd/systemd. Enable getty@tty1
and, for Plasma, a display manager. /etc/machine-id is
created on first boot if missing.
openrc PID 1 is OpenRC. Add services with rc-update. eudev and
elogind are in the init-openrc set.
s6 PID 1 execs s6-svscan on /run/service. Enabled longruns
live in /etc/s6/enabled. eudev and elogind come with
init-s6. s6-linux-init-maker is installed for admins
who want the official stage tree.
runit PID 1 is runit-init. Stages are /etc/runit/1, 2, 3.
Enable with a symlink in /etc/runit/runsvdir/default.
dinit PID 1 is dinit. Enable by linking into /etc/dinit.d/boot.d.
Halt/reboot wrappers are named dinit-halt and friends so
they do not collide with busybox.
shepherd PID 1 is GNU Shepherd. Services are Scheme files in
/etc/shepherd.d. This pulls Guile and Fibers.
sysvinit PID 1 is /sbin/init. Boot scripts live in /etc/rc.d.
Enable with a symlink in /etc/rc.d/rc3.d (and friends).
eudev and elogind come with init-sysvinit.
none no init package was requested. Busybox can still provide
an init applet if you copy it to /sbin/init yourself.
setup enables known services for the init recorded in /etc/sps/init
(and the landed init-* package). Daemon packages ship scripts for
every init into the directories that init expects. Pass --services no
to skip enablement. Later sget install of a mapped package enables
it too.
EOF
cat >"$setup_root/root/sps-network.txt" <<EOF
SPS setup does not write DHCP or static network configuration.
On a Plasma system, use plasma-nm after the first graphical login.
On a console system, configure the interface by hand, for example with
ip(8) and a simple /etc/network script of your own.
OpenSSH is installed when the server profile or the ssh set is selected.
EOF
cat >"$setup_root/root/sps-fstab.txt" <<EOF
If setup partitioned the disk, /etc/fstab was written from blkid UUIDs.
Read it before the first boot. Add extra mounts yourself.
Partition layout: $setup_partition
Disk: ${setup_disk:-<none>}
Root: ${setup_part_root:-<none>}
EFI: ${setup_part_efi:-<none>}
Swap: ${setup_part_swap:-<none>}
Home: ${setup_part_home:-<none>}
EOF
if command -v setup_write_fstab >/dev/null 2>&1 &&
[ -n "$setup_part_root" ]; then
setup_write_fstab "$setup_root" || :
fi
}
# Index the live system's bundled recipe trees in place (read-only) and
# write repos.conf so src update during setup works offline. Live ISOs
# already have those trees at /usr/src/sps/{core,extra}; cloning GitHub
# during setup is what made "updating source repositories" hang or fail
# offline. The installed machine re-points to the official Git remotes so
# it keeps receiving recipe updates after install.
setup_tree_has_recipe()
{
setup_probe=$1
[ -d "$setup_probe" ] || return 1
find "$setup_probe" \( -type d \( -name .git -o -name files -o \
-name patches -o -name hooks \) -prune \) -o \
-type f -name recipe -print | {
IFS= read -r setup_probe_recipe || true
[ -n "$setup_probe_recipe" ]
}
}
setup_seed_one_tree()
{
setup_seed_name=$1
setup_seed_src=${setup_live_root%/}/usr/src/sps/$setup_seed_name
setup_tree_has_recipe "$setup_seed_src" || return 1
return 0
}
setup_write_repos_conf()
{
setup_repos_prefix=$1
setup_repos_mode=${2:-installed}
setup_repos_core=/usr/src/sps/core
setup_repos_extra=/usr/src/sps/extra
if [ -n "$setup_repos_prefix" ] && [ "$setup_repos_prefix" != / ]; then
setup_repos_core=$setup_repos_prefix$setup_repos_core
setup_repos_extra=$setup_repos_prefix$setup_repos_extra
fi
{
if [ "$setup_repos_mode" = seed ] &&
[ "${setup_have_core:-0}" -eq 1 ]; then
printf 'dir core %s 100\n' "$setup_repos_core"
else
printf 'git core https://github.com/RobertFlexx/sps-core.git 100\n'
fi
if [ "$setup_repos_mode" = seed ] &&
[ "${setup_have_extra:-0}" -eq 1 ]; then
printf 'dir extra %s 80\n' "$setup_repos_extra"
else
printf 'git extra https://github.com/RobertFlexx/sps-extra.git 80\n'
fi
sps_print_community_repos_hint
} >"$setup_root/etc/sps/repos.conf" ||
setup_fail "$SPS_EX_PERMISSION" "cannot write target repos.conf"
}
setup_seed_recipe_trees()
{
setup_live_root=${SPS_LIVE_ROOT:-/}
case $setup_live_root in
/*) ;;
*)
setup_fail "$SPS_EX_USAGE" \
"SPS_LIVE_ROOT must be an absolute path: $setup_live_root"
;;
esac
[ "$setup_live_root" = / ] || setup_live_root=${setup_live_root%/}
mkdir -p "$setup_root/etc/sps" "$setup_root/usr/src/sps" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create target repository directories"
setup_have_core=0
setup_have_extra=0
if setup_seed_one_tree core; then
setup_have_core=1
fi
if setup_seed_one_tree extra; then
setup_have_extra=1
fi
# During setup, the live system's bundled trees must exist for src
# update to index them in place. apply_config later re-points the
# installed machine at the official Git remotes.
setup_write_repos_conf "${setup_live_root%/}" seed
if [ "$setup_have_core" -eq 1 ] && [ "$setup_have_extra" -eq 1 ]; then
printf '%s: indexing live bundled recipes; the installed system tracks the official Git repositories\n' \
"$sps_prog" >&2
else
printf '%s: missing bundled recipes; src update will clone from the network\n' \
"$sps_prog" >&2
fi
}
# Packages first, then hostname/user so filesystem's passwd is already present.
setup_root=${setup_target%/}
mkdir -p "$setup_root" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create target: $setup_root"
export SPS_ROOT=$setup_root
[ "$setup_keep_db" -eq 1 ] || unset SPS_DB
[ "$setup_keep_cache" -eq 1 ] || unset SPS_CACHE
[ "$setup_keep_build" -eq 1 ] || unset SPS_BUILD
[ "$setup_keep_repo_root" -eq 1 ] || unset SPS_REPO_ROOT
[ "$setup_keep_config" -eq 1 ] || unset SPS_CONFIG
[ "$setup_keep_repos" -eq 1 ] || unset SPS_REPOS_CONFIG
if [ "$setup_kernel" = custom ] && [ -n "$setup_kernel_config" ]; then
mkdir -p "$setup_root/etc/sps"
cp "$setup_kernel_config" "$setup_root/etc/sps/kernel.config" ||
setup_fail "$SPS_EX_PERMISSION" "cannot copy kernel config"
fi
setup_seed_recipe_trees
# Hooks run during sget, before apply_config. Record the init choice so
# post-install can enable SDDM and friends for the chosen PID 1.
mkdir -p "$setup_root/etc/sps" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create $setup_root/etc/sps"
printf '%s\n' "${setup_init:-none}" >"$setup_root/etc/sps/init"
# sget/mkpkg read $SPS_ROOT/etc/sps/sps.conf once SPS_CONFIG is unset.
# Write it before the first package so makejobs/cflags apply to the
# source install, not only after apply_config.
if [ ! -f "$setup_root/etc/sps/sps.conf" ]; then
if [ -f /etc/sps/live ] && [ -f /etc/sps/sps.conf ]; then
cp /etc/sps/sps.conf "$setup_root/etc/sps/sps.conf" ||
setup_fail "$SPS_EX_PERMISSION" "cannot copy live sps.conf"
else
sps_print_default_conf >"$setup_root/etc/sps/sps.conf" ||
setup_fail "$SPS_EX_PERMISSION" "cannot write sps.conf"
fi
fi
if [ "$setup_do_update" -eq 1 ]; then
printf '%s: updating source repositories\n' "$sps_prog" >&2
src update || setup_fail "$SPS_EX_NETWORK" "src update failed"
fi
setup_reject_unknown_packages
setup_pkgs=
while IFS= read -r setup_pkg; do
[ -n "$setup_pkg" ] || continue
setup_pkgs="$setup_pkgs $setup_pkg"
done <<EOF
$(sort -u "$setup_pkg_file")
EOF
printf '%s: installing packages:%s\n' "$sps_prog" "$setup_pkgs" >&2
# Fail before the first autoconf package if the live gcc cannot link.
# Missing libmpc (cc1) or crt1.o shows up as "C compiler cannot create
# executables" two screens later otherwise.
if command -v gcc >/dev/null 2>&1; then
setup_cc_dir=${SPS_BUILD:-$setup_root/var/tmp/sps}
mkdir -p "$setup_cc_dir" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create $setup_cc_dir"
setup_cc_log=$setup_cc_dir/sps-cc-probe.log
setup_cc_out=$setup_cc_dir/sps-cc-probe
if ! printf '%s\n' 'int main(void){return 0;}' |
gcc -x c - -o "$setup_cc_out" >"$setup_cc_log" 2>&1
then
printf '%s: live gcc cannot create executables\n' "$sps_prog" >&2
cat "$setup_cc_log" >&2
setup_fail "$SPS_EX_BUILD" "live gcc cannot create executables"
fi
rm -f "$setup_cc_out"
fi
# Meson recipes (libxml2 is the first for many desktop graphs) fail with
# a Python traceback when mesonbuild is missing, or with a meson error
# about option "python" when the recipe passes -Dpython=false.
if command -v meson >/dev/null 2>&1; then
setup_meson_dir=${SPS_BUILD:-$setup_root/var/tmp/sps}
mkdir -p "$setup_meson_dir" ||
setup_fail "$SPS_EX_PERMISSION" "cannot create $setup_meson_dir"
setup_meson_log=$setup_meson_dir/sps-meson-probe.log
if ! meson --version >"$setup_meson_log" 2>&1; then
printf '%s: live meson cannot run\n' "$sps_prog" >&2
cat "$setup_meson_log" >&2
setup_fail "$SPS_EX_BUILD" "live meson cannot run"
fi
fi
# sget treats requested names as explicit world roots.
# shellcheck disable=SC2086
sget install $setup_pkgs ||
setup_fail "$SPS_EX_BUILD" "package installation failed"
# Hostname, user, and notes land after packages so filesystem's passwd wins.
setup_apply_config
if [ "$setup_services" = yes ] &&
command -v setup_service_enable_present >/dev/null 2>&1
then
case $setup_init in
systemd|openrc|s6|runit|dinit|shepherd|sysvinit)
printf '%s: enabling services after user configuration, init %s\n' \
"$sps_prog" "$setup_init" >&2
setup_svc_list=$(setup_service_enable_present \
"$setup_root" "$setup_init") || setup_svc_list=
{
printf '%s\n' \
"Init: $setup_init" \
"setup enables known services that were installed." \
"Daemon packages already contain scripts for this init." \
"Missing files are filled from stock copies" \
"in $SPS_LIBDIR/setup/sv/$setup_init." \
"It does not start daemons inside a target chroot." \
""
if [ -n "$setup_svc_list" ]; then
printf '%s\n' 'Enabled:'
printf '%s\n' "$setup_svc_list" | sort -u
else
printf '%s\n' \
'No mapped service could be enabled in the target.' \
'Install the package and rerun, or enable it yourself after first boot.'
fi
} >"$setup_root/root/sps-services.txt"
;;
*)
cat >"$setup_root/root/sps-services.txt" <<EOF
Init is ${setup_init:-none}. setup did not enable services.
To have SDDM, sshd, NetworkManager, and similar start at boot, rerun
setup with --init systemd, openrc, s6, runit, dinit, shepherd, or sysvinit, or
enable them yourself.
EOF
;;
esac
fi
# On a live ISO, rewrite of repos.conf pointed at /usr/src/sps. Rebuild
# the index so the installed system does not keep /mnt-prefixed recipe paths.
if [ "$setup_do_update" -eq 1 ] && [ -f /etc/sps/live ]; then
printf '%s: reindexing recipes for the installed system\n' "$sps_prog" >&2
src update || printf '%s: installed-system reindex failed; run src update after boot\n' \
"$sps_prog" >&2
fi
if [ "$setup_flatpak" = yes ]; then
setup_fp=$setup_root/usr/bin/flatpak
if [ -x "$setup_fp" ]; then
printf '%s: adding Flathub remote\n' "$sps_prog" >&2
"$setup_fp" --installation=system remote-add --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo \
--user=false 2>/dev/null ||
SPS_ROOT=$setup_root "$setup_fp" remote-add --if-not-exists \
flathub https://dl.flathub.org/repo/flathub.flatpakrepo ||
printf '%s: could not add Flathub; add it after first boot\n' \
"$sps_prog" >&2
else
printf '%s: flatpak is not in the target yet; Flathub skipped\n' \
"$sps_prog" >&2
fi
fi
if [ "$setup_install_bootloader" -eq 1 ]; then
command -v grub-install >/dev/null 2>&1 ||
setup_fail "$SPS_EX_PACKAGE" "grub-install is not available"
[ -n "$setup_disk" ] ||
setup_fail "$SPS_EX_USAGE" \
"--install-bootloader requires --disk; refusing to guess. See /root/sps-bootloader.txt"
printf '%s: grub-install %s (explicit)\n' "$sps_prog" "$setup_disk" >&2
if [ -d "$setup_root/boot/efi" ]; then
# Firmware fallback path: \EFI\BOOT\BOOTX64.EFI. This does not
# need efibootmgr or working NVRAM, which QEMU often lacks.
printf '%s: grub-install EFI removable (EFI/BOOT/BOOTX64.EFI)\n' \
"$sps_prog" >&2
grub-install --target=x86_64-efi \
--efi-directory="$setup_root/boot/efi" \
--boot-directory="$setup_root/boot" \
--removable --no-nvram \
"$setup_disk" ||
setup_fail "$SPS_EX_BUILD" "grub-install (EFI removable) failed"
if command -v efibootmgr >/dev/null 2>&1; then
printf '%s: grub-install EFI Boot#### via efibootmgr\n' \
"$sps_prog" >&2
grub-install --target=x86_64-efi \
--efi-directory="$setup_root/boot/efi" \
--bootloader-id=SPS \
--boot-directory="$setup_root/boot" \
"$setup_disk" ||
printf '%s: efibootmgr NVRAM update failed; EFI/BOOT/BOOTX64.EFI is in place\n' \
"$sps_prog" >&2
fi
else
grub-install --target=i386-pc --boot-directory="$setup_root/boot" \
"$setup_disk" ||
setup_fail "$SPS_EX_BUILD" "grub-install (BIOS) failed"
fi
fi
setup_msg "Installation finished.
Target: $setup_root
Answers: $setup_root/etc/sps/setup-answers
Bootloader notes: $setup_root/root/sps-bootloader.txt
Set passwords before the first login. If you selected NVIDIA, rebuild the
kernel modules against the installed kernel."
exit 0