screwed up merging cause im a moron, please be patient
This commit is contained in:
lurk 2024-09-20 23:33:20 -07:00
commit a294d17a83
15 changed files with 5086 additions and 28 deletions

View file

@ -1,28 +0,0 @@
# a horrible customized gentoo-install
i was too lazy to figure out how to fork it from github my apologies
[source/upstream/whatever it's called i forgot](https://gitlab.com/harha_/gentoo-installer)
[stole lots of flags and config stuff from here](https://www.reddit.com/r/Gentoo/comments/150r74m/guide_hyprland_nvidia_extremely_minimal_gentoo/)
## todo
- DO NOT USE!!!!! I'M NOT DONE (actually it would probably work cause i didnt change anything)
- figure out how shell works
- cut down on unneeded features
- automatically install programs i like
- not break things (hopefully)
## hopefully added features
- cut down on unneeded features
- fully automate certain portions (installing kernel/decent portage flags etc etc)
- default programs because i do not want to modify files just to get xorg working ever again
- actually working efibootmgr
## some notes for me (in the future)
1. no mbr
2. no full disk encryption
3. only uses efistub (no bootloader)
4. amd64 openrc only (could probably modify this in less than 10 seconds by changing stage3 download)
5. assumes intel cpu and nvidia gpu
gentoo is awful(ly good)

1652
configure vendored Executable file

File diff suppressed because it is too large Load diff

14
contrib/fstab Normal file
View file

@ -0,0 +1,14 @@
# /etc/fstab: static file system information.
#
# noatime turns off atimes for increased performance (atimes normally aren't
# needed); notail increases performance of ReiserFS (at the expense of storage
# efficiency). It's safe to drop the noatime options if you want and to
# switch between notail / tail freely.
#
# The root filesystem should have a pass number of either 0 or 1.
# All other filesystems should have a pass number of 0 or greater than 1.
#
# See the manpage fstab(5) for more information.
#
# <fs> <mountpoint> <type> <opts> <dump/pass>

489
contrib/i18n_supported Normal file
View file

@ -0,0 +1,489 @@
aa_DJ.UTF-8 UTF-8
aa_DJ ISO-8859-1
aa_ER UTF-8
aa_ER@saaho UTF-8
aa_ET UTF-8
af_ZA.UTF-8 UTF-8
af_ZA ISO-8859-1
agr_PE UTF-8
ak_GH UTF-8
am_ET UTF-8
an_ES.UTF-8 UTF-8
an_ES ISO-8859-15
anp_IN UTF-8
ar_AE.UTF-8 UTF-8
ar_AE ISO-8859-6
ar_BH.UTF-8 UTF-8
ar_BH ISO-8859-6
ar_DZ.UTF-8 UTF-8
ar_DZ ISO-8859-6
ar_EG.UTF-8 UTF-8
ar_EG ISO-8859-6
ar_IN UTF-8
ar_IQ.UTF-8 UTF-8
ar_IQ ISO-8859-6
ar_JO.UTF-8 UTF-8
ar_JO ISO-8859-6
ar_KW.UTF-8 UTF-8
ar_KW ISO-8859-6
ar_LB.UTF-8 UTF-8
ar_LB ISO-8859-6
ar_LY.UTF-8 UTF-8
ar_LY ISO-8859-6
ar_MA.UTF-8 UTF-8
ar_MA ISO-8859-6
ar_OM.UTF-8 UTF-8
ar_OM ISO-8859-6
ar_QA.UTF-8 UTF-8
ar_QA ISO-8859-6
ar_SA.UTF-8 UTF-8
ar_SA ISO-8859-6
ar_SD.UTF-8 UTF-8
ar_SD ISO-8859-6
ar_SS UTF-8
ar_SY.UTF-8 UTF-8
ar_SY ISO-8859-6
ar_TN.UTF-8 UTF-8
ar_TN ISO-8859-6
ar_YE.UTF-8 UTF-8
ar_YE ISO-8859-6
ayc_PE UTF-8
az_AZ UTF-8
az_IR UTF-8
as_IN UTF-8
ast_ES.UTF-8 UTF-8
ast_ES ISO-8859-15
be_BY.UTF-8 UTF-8
be_BY CP1251
be_BY@latin UTF-8
bem_ZM UTF-8
ber_DZ UTF-8
ber_MA UTF-8
bg_BG.UTF-8 UTF-8
bg_BG CP1251
bhb_IN.UTF-8 UTF-8
bho_IN UTF-8
bho_NP UTF-8
bi_VU UTF-8
bn_BD UTF-8
bn_IN UTF-8
bo_CN UTF-8
bo_IN UTF-8
br_FR.UTF-8 UTF-8
br_FR ISO-8859-1
br_FR@euro ISO-8859-15
brx_IN UTF-8
bs_BA.UTF-8 UTF-8
bs_BA ISO-8859-2
byn_ER UTF-8
C.UTF-8 UTF-8
ca_AD.UTF-8 UTF-8
ca_AD ISO-8859-15
ca_ES.UTF-8 UTF-8
ca_ES ISO-8859-1
ca_ES@euro ISO-8859-15
ca_ES@valencia UTF-8
ca_FR.UTF-8 UTF-8
ca_FR ISO-8859-15
ca_IT.UTF-8 UTF-8
ca_IT ISO-8859-15
ce_RU UTF-8
chr_US UTF-8
ckb_IQ UTF-8
cmn_TW UTF-8
crh_UA UTF-8
cs_CZ.UTF-8 UTF-8
cs_CZ ISO-8859-2
csb_PL UTF-8
cv_RU UTF-8
cy_GB.UTF-8 UTF-8
cy_GB ISO-8859-14
da_DK.UTF-8 UTF-8
da_DK ISO-8859-1
de_AT.UTF-8 UTF-8
de_AT ISO-8859-1
de_AT@euro ISO-8859-15
de_BE.UTF-8 UTF-8
de_BE ISO-8859-1
de_BE@euro ISO-8859-15
de_CH.UTF-8 UTF-8
de_CH ISO-8859-1
de_DE.UTF-8 UTF-8
de_DE ISO-8859-1
de_DE@euro ISO-8859-15
de_IT.UTF-8 UTF-8
de_IT ISO-8859-1
de_LI.UTF-8 UTF-8
de_LU.UTF-8 UTF-8
de_LU ISO-8859-1
de_LU@euro ISO-8859-15
doi_IN UTF-8
dsb_DE UTF-8
dv_MV UTF-8
dz_BT UTF-8
el_GR.UTF-8 UTF-8
el_GR ISO-8859-7
el_GR@euro ISO-8859-7
el_CY.UTF-8 UTF-8
el_CY ISO-8859-7
en_AG UTF-8
en_AU.UTF-8 UTF-8
en_AU ISO-8859-1
en_BW.UTF-8 UTF-8
en_BW ISO-8859-1
en_CA.UTF-8 UTF-8
en_CA ISO-8859-1
en_DK.UTF-8 UTF-8
en_DK ISO-8859-1
en_GB.UTF-8 UTF-8
en_GB ISO-8859-1
en_HK.UTF-8 UTF-8
en_HK ISO-8859-1
en_IE.UTF-8 UTF-8
en_IE ISO-8859-1
en_IE@euro ISO-8859-15
en_IL UTF-8
en_IN UTF-8
en_NG UTF-8
en_NZ.UTF-8 UTF-8
en_NZ ISO-8859-1
en_PH.UTF-8 UTF-8
en_PH ISO-8859-1
en_SC.UTF-8 UTF-8
en_SG.UTF-8 UTF-8
en_SG ISO-8859-1
en_US.UTF-8 UTF-8
en_US ISO-8859-1
en_ZA.UTF-8 UTF-8
en_ZA ISO-8859-1
en_ZM UTF-8
en_ZW.UTF-8 UTF-8
en_ZW ISO-8859-1
eo UTF-8
es_AR.UTF-8 UTF-8
es_AR ISO-8859-1
es_BO.UTF-8 UTF-8
es_BO ISO-8859-1
es_CL.UTF-8 UTF-8
es_CL ISO-8859-1
es_CO.UTF-8 UTF-8
es_CO ISO-8859-1
es_CR.UTF-8 UTF-8
es_CR ISO-8859-1
es_CU UTF-8
es_DO.UTF-8 UTF-8
es_DO ISO-8859-1
es_EC.UTF-8 UTF-8
es_EC ISO-8859-1
es_ES.UTF-8 UTF-8
es_ES ISO-8859-1
es_ES@euro ISO-8859-15
es_GT.UTF-8 UTF-8
es_GT ISO-8859-1
es_HN.UTF-8 UTF-8
es_HN ISO-8859-1
es_MX.UTF-8 UTF-8
es_MX ISO-8859-1
es_NI.UTF-8 UTF-8
es_NI ISO-8859-1
es_PA.UTF-8 UTF-8
es_PA ISO-8859-1
es_PE.UTF-8 UTF-8
es_PE ISO-8859-1
es_PR.UTF-8 UTF-8
es_PR ISO-8859-1
es_PY.UTF-8 UTF-8
es_PY ISO-8859-1
es_SV.UTF-8 UTF-8
es_SV ISO-8859-1
es_US.UTF-8 UTF-8
es_US ISO-8859-1
es_UY.UTF-8 UTF-8
es_UY ISO-8859-1
es_VE.UTF-8 UTF-8
es_VE ISO-8859-1
et_EE.UTF-8 UTF-8
et_EE ISO-8859-1
et_EE.ISO-8859-15 ISO-8859-15
eu_ES.UTF-8 UTF-8
eu_ES ISO-8859-1
eu_ES@euro ISO-8859-15
fa_IR UTF-8
ff_SN UTF-8
fi_FI.UTF-8 UTF-8
fi_FI ISO-8859-1
fi_FI@euro ISO-8859-15
fil_PH UTF-8
fo_FO.UTF-8 UTF-8
fo_FO ISO-8859-1
fr_BE.UTF-8 UTF-8
fr_BE ISO-8859-1
fr_BE@euro ISO-8859-15
fr_CA.UTF-8 UTF-8
fr_CA ISO-8859-1
fr_CH.UTF-8 UTF-8
fr_CH ISO-8859-1
fr_FR.UTF-8 UTF-8
fr_FR ISO-8859-1
fr_FR@euro ISO-8859-15
fr_LU.UTF-8 UTF-8
fr_LU ISO-8859-1
fr_LU@euro ISO-8859-15
fur_IT UTF-8
fy_NL UTF-8
fy_DE UTF-8
ga_IE.UTF-8 UTF-8
ga_IE ISO-8859-1
ga_IE@euro ISO-8859-15
gd_GB.UTF-8 UTF-8
gd_GB ISO-8859-15
gez_ER UTF-8
gez_ER@abegede UTF-8
gez_ET UTF-8
gez_ET@abegede UTF-8
gl_ES.UTF-8 UTF-8
gl_ES ISO-8859-1
gl_ES@euro ISO-8859-15
gu_IN UTF-8
gv_GB.UTF-8 UTF-8
gv_GB ISO-8859-1
ha_NG UTF-8
hak_TW UTF-8
he_IL.UTF-8 UTF-8
he_IL ISO-8859-8
hi_IN UTF-8
hif_FJ UTF-8
hne_IN UTF-8
hr_HR.UTF-8 UTF-8
hr_HR ISO-8859-2
hsb_DE ISO-8859-2
hsb_DE.UTF-8 UTF-8
ht_HT UTF-8
hu_HU.UTF-8 UTF-8
hu_HU ISO-8859-2
hy_AM UTF-8
hy_AM.ARMSCII-8 ARMSCII-8
ia_FR UTF-8
id_ID.UTF-8 UTF-8
id_ID ISO-8859-1
ig_NG UTF-8
ik_CA UTF-8
is_IS.UTF-8 UTF-8
is_IS ISO-8859-1
it_CH.UTF-8 UTF-8
it_CH ISO-8859-1
it_IT.UTF-8 UTF-8
it_IT ISO-8859-1
it_IT@euro ISO-8859-15
iu_CA UTF-8
ja_JP.EUC-JP EUC-JP
ja_JP.UTF-8 UTF-8
ka_GE.UTF-8 UTF-8
ka_GE GEORGIAN-PS
kab_DZ UTF-8
kk_KZ.UTF-8 UTF-8
kk_KZ PT154
kl_GL.UTF-8 UTF-8
kl_GL ISO-8859-1
km_KH UTF-8
kn_IN UTF-8
ko_KR.EUC-KR EUC-KR
ko_KR.UTF-8 UTF-8
kok_IN UTF-8
ks_IN UTF-8
ks_IN@devanagari UTF-8
ku_TR.UTF-8 UTF-8
ku_TR ISO-8859-9
kw_GB.UTF-8 UTF-8
kw_GB ISO-8859-1
ky_KG UTF-8
lb_LU UTF-8
lg_UG.UTF-8 UTF-8
lg_UG ISO-8859-10
li_BE UTF-8
li_NL UTF-8
lij_IT UTF-8
ln_CD UTF-8
lo_LA UTF-8
lt_LT.UTF-8 UTF-8
lt_LT ISO-8859-13
lv_LV.UTF-8 UTF-8
lv_LV ISO-8859-13
lzh_TW UTF-8
mag_IN UTF-8
mai_IN UTF-8
mai_NP UTF-8
mfe_MU UTF-8
mg_MG.UTF-8 UTF-8
mg_MG ISO-8859-15
mhr_RU UTF-8
mi_NZ.UTF-8 UTF-8
mi_NZ ISO-8859-13
miq_NI UTF-8
mjw_IN UTF-8
mk_MK.UTF-8 UTF-8
mk_MK ISO-8859-5
ml_IN UTF-8
mn_MN UTF-8
mni_IN UTF-8
mnw_MM UTF-8
mr_IN UTF-8
ms_MY.UTF-8 UTF-8
ms_MY ISO-8859-1
mt_MT.UTF-8 UTF-8
mt_MT ISO-8859-3
my_MM UTF-8
nan_TW UTF-8
nan_TW@latin UTF-8
nb_NO.UTF-8 UTF-8
nb_NO ISO-8859-1
nds_DE UTF-8
nds_NL UTF-8
ne_NP UTF-8
nhn_MX UTF-8
niu_NU UTF-8
niu_NZ UTF-8
nl_AW UTF-8
nl_BE.UTF-8 UTF-8
nl_BE ISO-8859-1
nl_BE@euro ISO-8859-15
nl_NL.UTF-8 UTF-8
nl_NL ISO-8859-1
nl_NL@euro ISO-8859-15
nn_NO.UTF-8 UTF-8
nn_NO ISO-8859-1
nr_ZA UTF-8
nso_ZA UTF-8
oc_FR.UTF-8 UTF-8
oc_FR ISO-8859-1
om_ET UTF-8
om_KE.UTF-8 UTF-8
om_KE ISO-8859-1
or_IN UTF-8
os_RU UTF-8
pa_IN UTF-8
pa_PK UTF-8
pap_AW UTF-8
pap_CW UTF-8
pl_PL.UTF-8 UTF-8
pl_PL ISO-8859-2
ps_AF UTF-8
pt_BR.UTF-8 UTF-8
pt_BR ISO-8859-1
pt_PT.UTF-8 UTF-8
pt_PT ISO-8859-1
pt_PT@euro ISO-8859-15
quz_PE UTF-8
raj_IN UTF-8
ro_RO.UTF-8 UTF-8
ro_RO ISO-8859-2
ru_RU.KOI8-R KOI8-R
ru_RU.UTF-8 UTF-8
ru_RU ISO-8859-5
ru_UA.UTF-8 UTF-8
ru_UA KOI8-U
rw_RW UTF-8
sa_IN UTF-8
sah_RU UTF-8
sat_IN UTF-8
sc_IT UTF-8
sd_IN UTF-8
sd_IN@devanagari UTF-8
se_NO UTF-8
sgs_LT UTF-8
shn_MM UTF-8
shs_CA UTF-8
si_LK UTF-8
sid_ET UTF-8
sk_SK.UTF-8 UTF-8
sk_SK ISO-8859-2
sl_SI.UTF-8 UTF-8
sl_SI ISO-8859-2
sm_WS UTF-8
so_DJ.UTF-8 UTF-8
so_DJ ISO-8859-1
so_ET UTF-8
so_KE.UTF-8 UTF-8
so_KE ISO-8859-1
so_SO.UTF-8 UTF-8
so_SO ISO-8859-1
sq_AL.UTF-8 UTF-8
sq_AL ISO-8859-1
sq_MK UTF-8
sr_ME UTF-8
sr_RS UTF-8
sr_RS@latin UTF-8
ss_ZA UTF-8
st_ZA.UTF-8 UTF-8
st_ZA ISO-8859-1
sv_FI.UTF-8 UTF-8
sv_FI ISO-8859-1
sv_FI@euro ISO-8859-15
sv_SE.UTF-8 UTF-8
sv_SE ISO-8859-1
sw_KE UTF-8
sw_TZ UTF-8
szl_PL UTF-8
ta_IN UTF-8
ta_LK UTF-8
tcy_IN.UTF-8 UTF-8
te_IN UTF-8
tg_TJ.UTF-8 UTF-8
tg_TJ KOI8-T
th_TH.UTF-8 UTF-8
th_TH TIS-620
the_NP UTF-8
ti_ER UTF-8
ti_ET UTF-8
tig_ER UTF-8
tk_TM UTF-8
tl_PH.UTF-8 UTF-8
tl_PH ISO-8859-1
tn_ZA UTF-8
to_TO UTF-8
tpi_PG UTF-8
tr_CY.UTF-8 UTF-8
tr_CY ISO-8859-9
tr_TR.UTF-8 UTF-8
tr_TR ISO-8859-9
ts_ZA UTF-8
tt_RU UTF-8
tt_RU@iqtelif UTF-8
ug_CN UTF-8
uk_UA.UTF-8 UTF-8
uk_UA KOI8-U
unm_US UTF-8
ur_IN UTF-8
ur_PK UTF-8
uz_UZ.UTF-8 UTF-8
uz_UZ ISO-8859-1
uz_UZ@cyrillic UTF-8
ve_ZA UTF-8
vi_VN UTF-8
wa_BE ISO-8859-1
wa_BE@euro ISO-8859-15
wa_BE.UTF-8 UTF-8
wae_CH UTF-8
wal_ET UTF-8
wo_SN UTF-8
xh_ZA.UTF-8 UTF-8
xh_ZA ISO-8859-1
yi_US.UTF-8 UTF-8
yi_US CP1255
yo_NG UTF-8
yue_HK UTF-8
yuw_PG UTF-8
zh_CN.GB18030 GB18030
zh_CN.GBK GBK
zh_CN.UTF-8 UTF-8
zh_CN GB2312
zh_HK.UTF-8 UTF-8
zh_HK BIG5-HKSCS
zh_SG.UTF-8 UTF-8
zh_SG.GBK GBK
zh_SG GB2312
zh_TW.EUC-TW EUC-TW
zh_TW.UTF-8 UTF-8
zh_TW BIG5
zu_ZA.UTF-8 UTF-8
zu_ZA ISO-8859-1

BIN
contrib/screenshot_configure.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

81
contrib/sshd_config Normal file
View file

@ -0,0 +1,81 @@
# /etc/ssh/sshd_config
#
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
Port 22
#AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
# Limit Host Key Algorithms
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519
# Limit Key Exchange Algorithms
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
# Logging
#SyslogFacility AUTH
LogLevel VERBOSE
#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
MaxAuthTries 3
MaxSessions 4
# Only allow root to login
AllowGroups root
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no
# Change to no to disable s/key passwords
ChallengeResponseAuthentication no
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication. Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes
AllowAgentForwarding no
AllowTcpForwarding yes
#GatewayPorts no
#X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
PrintLastLog yes
TCPKeepAlive no
#UseLogin no
#PermitUserEnvironment no
Compression delayed
ClientAliveInterval 300
ClientAliveCountMax 2
#UseDNS no
#PidFile /run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none
# override default of no subsystems
Subsystem sftp /usr/lib64/misc/sftp-server

136
install Executable file
View file

@ -0,0 +1,136 @@
#!/bin/bash
set -uo pipefail
################################################
# Initialize script environment
# Find the directory this script is stored in. (from: http://stackoverflow.com/questions/59895)
function get_source_dir() {
local source="${BASH_SOURCE[0]}"
while [[ -h $source ]]
do
local tmp
tmp="$(cd -P "$(dirname "${source}")" && pwd)"
source="$(readlink "${source}")"
[[ $source != /* ]] && source="${tmp}/${source}"
done
echo -n "$(realpath "$(dirname "${source}")")"
}
ACTUAL_WORKING_DIRECTORY="$(realpath "$(pwd)")" || exit 1
export ACTUAL_WORKING_DIRECTORY
GENTOO_INSTALL_REPO_DIR_ORIGINAL="$(get_source_dir)"
export GENTOO_INSTALL_REPO_DIR_ORIGINAL
export GENTOO_INSTALL_REPO_DIR="$GENTOO_INSTALL_REPO_DIR_ORIGINAL"
export GENTOO_INSTALL_REPO_SCRIPT_ACTIVE=true
export GENTOO_INSTALL_REPO_SCRIPT_PID=$$
umask 0077
# shellcheck source=./scripts/utils.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/utils.sh"
# shellcheck source=./scripts/config.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/config.sh"
# shellcheck source=./scripts/functions.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/functions.sh"
# shellcheck source=./scripts/main.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/main.sh"
################################################
# Main dispatch
# Instantly kill when pressing ctrl-c
trap 'kill "$GENTOO_INSTALL_REPO_SCRIPT_PID"' INT
ACTION=""
CONFIG="$GENTOO_INSTALL_REPO_DIR/gentoo.conf"
while [[ $# -gt 0 ]]; do
case "$1" in
""|"help"|"--help"|"-help"|"-h")
echo "Usage: $0 [opts]... <action>"
echo "Performs a minimal gentoo installation. See https://github.com/oddlama/gentoo-install"
echo "for more information. If the configuration file does not exist, the configurator will"
echo "be started instead."
echo ""
echo "Options:"
echo " -c, --config <CONFIG> Use the given configuration file instead of the default"
echo " location (gentoo.conf). Applies to installation as well"
echo " as initial configuration in case it doesn't exist."
echo ""
echo "Actions:"
echo " -i, --install Installs gentoo as configured. This is the default mode,"
echo " if the given configuration file exists."
echo " -R, --chroot <DIR> [CMD...] Chroot into an existing system. The root filesystem"
echo " must already be mounted under DIR. All required special"
echo " filesystems will be mounted inside, and unmounted when"
echo " the chroot exits."
exit 0
;;
"-c"|"--config")
[[ -f "$2" ]] \
|| die "Config file not found: '$2'"
CONFIG="$(cd "$ACTUAL_WORKING_DIRECTORY" && realpath --relative-to="$GENTOO_INSTALL_REPO_DIR" "$2" 2>/dev/null)" || die "Could not determine realpath to config"
shift
;;
"-R"|"--chroot")
[[ -z $ACTION ]] || die "Multiple actions given"
ACTION="chroot"
CHROOT_DIR="$2"
[[ -e "$CHROOT_DIR" ]] || die "Chroot directory not found: '$CHROOT_DIR'"
shift
;;
"-i"|"--install")
[[ -z $ACTION ]] || die "Multiple actions given"
ACTION="install"
;;
"__install_gentoo_in_chroot")
ACTION="__install_gentoo_in_chroot"
;;
*) die "Invalid option '$1'" ;;
esac
shift
done
# Check configuration location
[[ -z "${CONFIG%%"$GENTOO_INSTALL_REPO_DIR"*}" ]] \
|| die "Configuration file must be inside the installation directory. This is needed so it is accessible from within the chroot environment."
if [[ -z "$ACTION" ]]; then
if [[ -e "$CONFIG" ]]; then
# Default if configuration exists: Run installer
ACTION="install"
else
# Default if configuration does not exist: Run configurator, and exit afterwards.
echo "You have not created a gentoo.conf. Starting configurator instead of installing."
exec "$GENTOO_INSTALL_REPO_DIR/configure" "$CONFIG"
fi
fi
if [[ "$ACTION" != "chroot" ]]; then
# Load config if we aren't just chrooting
[[ -e "$CONFIG" ]] \
|| die "Configuration file '$CONFIG' does not exist. To run the configurator, omit '-i' flag or run ./configure"
# shellcheck disable=SC1090
source "$CONFIG" || die "Could not source config"
[[ $I_HAVE_READ_AND_EDITED_THE_CONFIG_PROPERLY == "true" ]] \
|| die "You have not properly read the config. Edit the config file and set I_HAVE_READ_AND_EDITED_THE_CONFIG_PROPERLY=true to continue."
preprocess_config
fi
[[ $EUID == 0 ]] \
|| die "Must be root"
mkdir_or_die 0755 "$TMP_DIR"
case "$ACTION" in
"chroot") main_chroot "$CHROOT_DIR" "$@" ;;
"install") main_install "$@" ;;
"__install_gentoo_in_chroot") main_install_gentoo_in_chroot "$@" ;;
*) die "Invalid action '$ACTION'" ;;
esac

639
scripts/config.sh Normal file
View file

@ -0,0 +1,639 @@
# shellcheck source=./scripts/protection.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1
################################################
# Script internal configuration
# The temporary directory for this script,
# must reside in /tmp to allow the chrooted system to access the files
TMP_DIR="/tmp/gentoo-install"
# Mountpoint for the new system
ROOT_MOUNTPOINT="$TMP_DIR/root"
# Mountpoint for the script files for access from chroot
GENTOO_INSTALL_REPO_BIND="$TMP_DIR/bind"
# Mountpoint for the script files for access from chroot
UUID_STORAGE_DIR="$TMP_DIR/uuids"
# Backup dir for luks headers
LUKS_HEADER_BACKUP_DIR="$TMP_DIR/luks-headers"
# Flag to track usage of raid (needed to check for mdadm existence)
USED_RAID=false
# Flag to track usage of luks (needed to check for cryptsetup existence)
USED_LUKS=false
# Flag to track usage of zfs
USED_ZFS=false
# Flag to track usage of btrfs
USED_BTRFS=false
# Flag to track usage of encryption
USED_ENCRYPTION=false
# Flag to track whether partitioning or formatting is forbidden
NO_PARTITIONING_OR_FORMATTING=false
# An array of disk related actions to perform
DISK_ACTIONS=()
# An array of dracut parameters needed to boot the selected configuration
DISK_DRACUT_CMDLINE=()
# An associative array from disk id to a resolvable string
declare -gA DISK_ID_TO_RESOLVABLE
# An associative array from disk id to parent gpt disk id (only for partitions)
declare -gA DISK_ID_PART_TO_GPT_ID
# An associative array to check for existing ids (maps to uuids)
declare -gA DISK_ID_TO_UUID
# An associative set to check for correct usage of size=remaining in gpt tables
declare -gA DISK_GPT_HAD_SIZE_REMAINING
function only_one_of() {
local previous=""
local a
for a in "$@"; do
if [[ -v arguments[$a] ]]; then
if [[ -z $previous ]]; then
previous="$a"
else
die_trace 2 "Only one of the arguments ($*) can be given"
fi
fi
done
}
function create_new_id() {
local id="${arguments[$1]}"
[[ $id == *';'* ]] \
&& die_trace 2 "Identifier contains invalid character ';'"
[[ ! -v DISK_ID_TO_UUID[$id] ]] \
|| die_trace 2 "Identifier '$id' already exists"
DISK_ID_TO_UUID[$id]="$(load_or_generate_uuid "$(base64 -w 0 <<< "$id")")"
}
function verify_existing_id() {
local id="${arguments[$1]}"
[[ -v DISK_ID_TO_UUID[$id] ]] \
|| die_trace 2 "Identifier $1='$id' not found"
}
function verify_existing_unique_ids() {
local arg="$1"
local ids="${arguments[$arg]}"
count_orig="$(tr ';' '\n' <<< "$ids" | grep -c '\S')"
count_uniq="$(tr ';' '\n' <<< "$ids" | grep '\S' | sort -u | wc -l)"
[[ $count_orig -gt 0 ]] \
|| die_trace 2 "$arg=... must contain at least one entry"
[[ $count_orig -eq $count_uniq ]] \
|| die_trace 2 "$arg=... contains duplicate identifiers"
local id
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
[[ -v DISK_ID_TO_UUID[$id] ]] \
|| die_trace 2 "$arg=... contains unknown identifier '$id'"
done
}
function verify_option() {
local opt="$1"
shift
local arg="${arguments[$opt]}"
local i
for i in "$@"; do
[[ $i == "$arg" ]] \
&& return 0
done
die_trace 2 "Invalid option $opt='$arg', must be one of ($*)"
}
# Named arguments:
# new_id: Id for the existing device
# device: The block device
function register_existing() {
local known_arguments=('+new_id' '+device')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
create_new_id new_id
local new_id="${arguments[new_id]}"
local device="${arguments[device]}"
create_resolve_entry_device "$new_id" "$device"
DISK_ACTIONS+=("action=existing" "$@" ";")
}
# Named arguments:
# new_id: Id for the new gpt table
# device|id: The operand block device or previously allocated id
function create_gpt() {
local known_arguments=('+new_id' '+device|id')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
only_one_of device id
create_new_id new_id
[[ -v arguments[id] ]] \
&& verify_existing_id id
local new_id="${arguments[new_id]}"
create_resolve_entry "$new_id" ptuuid "${DISK_ID_TO_UUID[$new_id]}"
DISK_ACTIONS+=("action=create_gpt" "$@" ";")
}
# Named arguments:
# new_id: Id for the new partition
# size: Size for the new partition, or 'remaining' to allocate the rest
# type: The parition type, either (bios, efi, swap, raid, luks, linux) (or a 4 digit hex-code for gdisk).
# id: The operand device id
function create_partition() {
local known_arguments=('+new_id' '+id' '+size' '+type')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
create_new_id new_id
verify_existing_id id
verify_option type bios efi swap raid luks linux
[[ -v "DISK_GPT_HAD_SIZE_REMAINING[${arguments[id]}]" ]] \
&& die_trace 1 "Cannot add another partition to table (${arguments[id]}) after size=remaining was used"
# shellcheck disable=SC2034
[[ ${arguments[size]} == "remaining" ]] \
&& DISK_GPT_HAD_SIZE_REMAINING[${arguments[id]}]=true
local new_id="${arguments[new_id]}"
DISK_ID_PART_TO_GPT_ID[$new_id]="${arguments[id]}"
create_resolve_entry "$new_id" partuuid "${DISK_ID_TO_UUID[$new_id]}"
DISK_ACTIONS+=("action=create_partition" "$@" ";")
}
# Named arguments:
# new_id: Id for the new raid
# level: Raid level
# name: Raid name (/dev/md/<name>)
# ids: Comma separated list of all member ids
function create_raid() {
USED_RAID=true
local known_arguments=('+new_id' '+level' '+name' '+ids')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
create_new_id new_id
verify_option level 0 1 5 6
verify_existing_unique_ids ids
local new_id="${arguments[new_id]}"
local uuid="${DISK_ID_TO_UUID[$new_id]}"
create_resolve_entry "$new_id" mdadm "$uuid"
DISK_DRACUT_CMDLINE+=("rd.md.uuid=$(uuid_to_mduuid "$uuid")")
DISK_ACTIONS+=("action=create_raid" "$@" ";")
}
# Named arguments:
# new_id: Id for the new luks
# id: The operand device id
function create_luks() {
USED_LUKS=true
USED_ENCRYPTION=true
local known_arguments=('+new_id' '+name' '+device|id')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
only_one_of device id
create_new_id new_id
[[ -v arguments[id] ]] \
&& verify_existing_id id
local new_id="${arguments[new_id]}"
local name="${arguments[name]}"
local uuid="${DISK_ID_TO_UUID[$new_id]}"
create_resolve_entry "$new_id" luks "$name"
DISK_DRACUT_CMDLINE+=("rd.luks.uuid=$uuid")
DISK_ACTIONS+=("action=create_luks" "$@" ";")
}
# Named arguments:
# new_id: Id for the new luks
# device: The device
function create_dummy() {
local known_arguments=('+new_id' '+device')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
create_new_id new_id
local new_id="${arguments[new_id]}"
local device="${arguments[device]}"
local uuid="${DISK_ID_TO_UUID[$new_id]}"
create_resolve_entry_device "$new_id" "$device"
DISK_ACTIONS+=("action=create_dummy" "$@" ";")
}
# Named arguments:
# id: Id of the device / partition created earlier
# type: One of (bios, efi, swap, ext4)
# label: The label for the formatted disk
function format() {
local known_arguments=('+id' '+type' '?label')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
verify_existing_id id
verify_option type bios efi swap ext4 btrfs
local type="${arguments[type]}"
if [[ "$type" == "btrfs" ]]; then
USED_BTRFS=true
fi
DISK_ACTIONS+=("action=format" "$@" ";")
}
# Named arguments:
# ids: List of ids for devices / partitions created earlier. Must contain at least 1 element.
# pool_type: The zfs pool type
# encrypt: Whether or not to encrypt the pool
function format_zfs() {
USED_ZFS=true
local known_arguments=('+ids' '?pool_type' '?encrypt' '?compress')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
verify_existing_unique_ids ids
USED_ENCRYPTION=${arguments[encrypt]:-false}
DISK_ACTIONS+=("action=format_zfs" "$@" ";")
}
# Named arguments:
# ids: List of ids for devices / partitions created earlier. Must contain at least 1 element.
# label: The label for the formatted disk
function format_btrfs() {
USED_BTRFS=true
local known_arguments=('+ids' '?raid_type' '?label')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
verify_existing_unique_ids ids
DISK_ACTIONS+=("action=format_btrfs" "$@" ";")
}
# Returns a comma separated list of all registered ids matching the given regex.
function expand_ids() {
local regex="$1"
for id in "${!DISK_ID_TO_UUID[@]}"; do
[[ $id =~ $regex ]] \
&& echo -n "$id;"
done
}
# Single disk, 3 partitions (efi, swap, root)
# Parameters:
# swap=<size> Create a swap partition with given size, or no swap at all if set to false.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
# luks=[true|false] Encrypt root partition. Defaults to false if not given.
# root_fs=[ext4|btrfs] Root filesystem. Defaults to ext4 if not given.
function create_classic_single_disk_layout() {
local known_arguments=('+swap' '?type' '?luks' '?root_fs')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -eq 1 ]] \
|| die_trace 1 "Expected exactly one positional argument (the device)"
local device="${extra_arguments[0]}"
local size_swap="${arguments[swap]}"
local type="${arguments[type]:-efi}"
local use_luks="${arguments[luks]:-false}"
local root_fs="${arguments[root_fs]:-ext4}"
create_gpt new_id=gpt device="$device"
create_partition new_id="part_$type" id=gpt size=1GiB type="$type"
[[ $size_swap != "false" ]] \
&& create_partition new_id=part_swap id=gpt size="$size_swap" type=swap
create_partition new_id=part_root id=gpt size=remaining type=linux
local root_id="part_root"
if [[ "$use_luks" == "true" ]]; then
create_luks new_id=part_luks_root name="root" id=part_root
root_id="part_luks_root"
fi
format id="part_$type" type="$type" label="$type"
[[ $size_swap != "false" ]] \
&& format id=part_swap type=swap label=swap
format id="$root_id" type="$root_fs" label=root
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_$type"
else
DISK_ID_BIOS="part_$type"
fi
[[ $size_swap != "false" ]] \
&& DISK_ID_SWAP=part_swap
DISK_ID_ROOT="$root_id"
if [[ $root_fs == "btrfs" ]]; then
DISK_ID_ROOT_TYPE="btrfs"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,compress-force=zstd,subvol=/root"
elif [[ $root_fs == "ext4" ]]; then
DISK_ID_ROOT_TYPE="ext4"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,errors=remount-ro,discard"
else
die "Unsupported root filesystem type"
fi
}
function create_single_disk_layout() {
die "'create_single_disk_layout' is deprecated, please use 'create_classic_single_disk_layout' instead. It is fully option-compatible to the old version."
}
# Skip partitioning, and use existing pre-formatted partitions. These must be trivially mountable.
# Parameters:
# swap=<device|false> Use the given device as swap, or no swap at all if set to false.
# boot=<device> Use the given device as the bios/efi partition.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
function create_existing_partitions_layout() {
NO_PARTITIONING_OR_FORMATTING=true
local known_arguments=('+swap' '+boot' '?type')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -eq 1 ]] \
|| die_trace 1 "Expected exactly one positional argument (the device)"
local device="${extra_arguments[0]}"
local swap_device="${arguments[swap]}"
local boot_device="${arguments[boot]}"
local type="${arguments[type]:-efi}"
register_existing new_id="part_$type" device="$boot_device"
[[ $swap_device != "false" ]] \
&& register_existing new_id="part_swap" device="$swap_device"
register_existing new_id="part_root" device="$device"
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_$type"
else
DISK_ID_BIOS="part_$type"
fi
[[ $swap_device != "false" ]] \
&& DISK_ID_SWAP=part_swap
DISK_ID_ROOT="part_root"
DISK_ID_ROOT_TYPE="" # unknown, could be anything. Left empty to skip generating an fstab entry.
}
# Multiple disks, up to 3 partitions on first disk (efi, optional swap, root with zfs).
# Additional devices will be added to the zfs pool.
# Parameters:
# swap=<size> Create a swap partition with given size, or no swap at all if set to false.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
# encrypt=[true|false] Encrypt zfs pool. Defaults to false if not given.
# pool_type=[stripe|mirror] Select raid type. Defaults to stripe.
function create_zfs_centric_layout() {
local known_arguments=('+swap' '?type' '?pool_type' '?encrypt' '?compress')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -gt 0 ]] \
|| die_trace 1 "Expected at least one positional argument (the devices)"
local device="${extra_arguments[0]}"
local size_swap="${arguments[swap]}"
local pool_type="${arguments[pool_type]:-stripe}"
local type="${arguments[type]:-efi}"
local encrypt="${arguments[encrypt]:-false}"
# Create layout on first disk
create_gpt new_id="gpt_dev0" device="${extra_arguments[0]}"
create_partition new_id="part_${type}_dev0" id="gpt_dev0" size=1GiB type="$type"
[[ $size_swap != "false" ]] \
&& create_partition new_id="part_swap_dev0" id="gpt_dev0" size="$size_swap" type=swap
create_partition new_id="part_root_dev0" id="gpt_dev0" size=remaining type=linux
local root_id="part_root_dev0"
local root_ids="part_root_dev0;"
local dev_id
for i in "${!extra_arguments[@]}"; do
[[ $i != 0 ]] || continue
dev_id="root_dev$i"
create_dummy new_id="$dev_id" device="${extra_arguments[$i]}"
root_ids="${root_ids}$dev_id;"
done
format id="part_${type}_dev0" type="$type" label="$type"
[[ $size_swap != "false" ]] \
&& format id="part_swap_dev0" type=swap label=swap
format_zfs ids="$root_ids" encrypt="$encrypt" pool_type="$pool_type"
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_${type}_dev0"
else
DISK_ID_BIOS="part_${type}_dev0"
fi
[[ $size_swap != "false" ]] \
&& DISK_ID_SWAP=part_swap_dev0
DISK_ID_ROOT="$root_id"
DISK_ID_ROOT_TYPE="zfs"
}
# Multiple disks, with raid 0 and luks
# - efi: partition on all disks, but only first disk used
# - swap: raid 0 → fs
# - root: raid 0 → luks → fs
# Parameters:
# swap=<size> Create a swap partition with given size for each disk, or no swap at all if set to false.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
# luks=[true|false] Encrypt root partition. Defaults to true if not given.
# root_fs=[ext4|btrfs] Root filesystem. Defaults to ext4 if not given.
function create_raid0_luks_layout() {
local known_arguments=('+swap' '?type' '?luks' '?root_fs')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -gt 0 ]] \
|| die_trace 1 "Expected at least one positional argument (the devices)"
local size_swap="${arguments[swap]}"
local type="${arguments[type]:-efi}"
local use_luks="${arguments[luks]:-true}"
local root_fs="${arguments[root_fs]:-ext4}"
for i in "${!extra_arguments[@]}"; do
create_gpt new_id="gpt_dev${i}" device="${extra_arguments[$i]}"
create_partition new_id="part_${type}_dev${i}" id="gpt_dev${i}" size=1GiB type="$type"
[[ $size_swap != "false" ]] \
&& create_partition new_id="part_swap_dev${i}" id="gpt_dev${i}" size="$size_swap" type=raid
create_partition new_id="part_root_dev${i}" id="gpt_dev${i}" size=remaining type=raid
done
[[ $size_swap != "false" ]] \
&& create_raid new_id=part_raid_swap name="swap" level=0 ids="$(expand_ids '^part_swap_dev[[:digit:]]$')"
create_raid new_id=part_raid_root name="root" level=0 ids="$(expand_ids '^part_root_dev[[:digit:]]$')"
local root_id="part_raid_root"
if [[ "$use_luks" == "true" ]]; then
create_luks new_id=part_luks_root name="root" id=part_raid_root
root_id="part_luks_root"
fi
format id="part_${type}_dev0" type="$type" label="$type"
[[ $size_swap != "false" ]] \
&& format id=part_raid_swap type=swap label=swap
format id="$root_id" type="$root_fs" label=root
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_${type}_dev0"
else
DISK_ID_BIOS="part_${type}_dev0"
fi
[[ $size_swap != "false" ]] \
&& DISK_ID_SWAP=part_raid_swap
DISK_ID_ROOT="$root_id"
if [[ $root_fs == "btrfs" ]]; then
DISK_ID_ROOT_TYPE="btrfs"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,compress=zstd,subvol=/root"
elif [[ $root_fs == "ext4" ]]; then
DISK_ID_ROOT_TYPE="ext4"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,errors=remount-ro,discard"
else
die "Unsupported root filesystem type"
fi
}
# Multiple disks, with raid 1 and luks
# - efi: partition on all disks, but only first disk used
# - swap: raid 1 → fs
# - root: raid 1 → luks → fs
# Parameters:
# swap=<size> Create a swap partition with given size for each disk, or no swap at all if set to false.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
# luks=[true|false] Encrypt root partition. Defaults to true if not given.
# root_fs=[ext4|btrfs] Root filesystem. Defaults to ext4 if not given.
function create_raid1_luks_layout() {
local known_arguments=('+swap' '?type' '?luks' '?root_fs')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -gt 0 ]] \
|| die_trace 1 "Expected at least one positional argument (the devices)"
local size_swap="${arguments[swap]}"
local type="${arguments[type]:-efi}"
local use_luks="${arguments[luks]:-true}"
local root_fs="${arguments[root_fs]:-ext4}"
for i in "${!extra_arguments[@]}"; do
create_gpt new_id="gpt_dev${i}" device="${extra_arguments[$i]}"
create_partition new_id="part_${type}_dev${i}" id="gpt_dev${i}" size=1GiB type="$type"
[[ $size_swap != "false" ]] \
&& create_partition new_id="part_swap_dev${i}" id="gpt_dev${i}" size="$size_swap" type=raid
create_partition new_id="part_root_dev${i}" id="gpt_dev${i}" size=remaining type=raid
done
[[ $size_swap != "false" ]] \
&& create_raid new_id=part_raid_swap name="swap" level=1 ids="$(expand_ids '^part_swap_dev[[:digit:]]$')"
create_raid new_id=part_raid_root name="root" level=1 ids="$(expand_ids '^part_root_dev[[:digit:]]$')"
local root_id="part_raid_root"
if [[ "$use_luks" == "true" ]]; then
create_luks new_id=part_luks_root name="root" id=part_raid_root
root_id="part_luks_root"
fi
format id="part_${type}_dev0" type="$type" label="$type"
[[ $size_swap != "false" ]] \
&& format id=part_raid_swap type=swap label=swap
format id="$root_id" type="$root_fs" label=root
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_${type}_dev0"
else
DISK_ID_BIOS="part_${type}_dev0"
fi
[[ $size_swap != "false" ]] \
&& DISK_ID_SWAP=part_raid_swap
DISK_ID_ROOT="$root_id"
if [[ $root_fs == "btrfs" ]]; then
DISK_ID_ROOT_TYPE="btrfs"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,compress=zstd,subvol=/root"
elif [[ $root_fs == "ext4" ]]; then
DISK_ID_ROOT_TYPE="ext4"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,errors=remount-ro,discard"
else
die "Unsupported root filesystem type"
fi
}
# Multiple disks, up to 3 partitions on first disk (efi, optional swap, root with btrfs).
# Additional devices will be first encrypted and then put directly into btrfs array.
# Parameters:
# swap=<size> Create a swap partition with given size, or no swap at all if set to false.
# type=[efi|bios] Selects the boot type. Defaults to efi if not given.
# luks=[true|false] Encrypt root partition and btrfs devices. Defaults to false if not given.
# raid_type=[raid0|raid1] Select raid type. Defaults to raid0.
function create_btrfs_centric_layout() {
local known_arguments=('+swap' '?type' '?raid_type' '?luks')
local extra_arguments=()
declare -A arguments; parse_arguments "$@"
[[ ${#extra_arguments[@]} -gt 0 ]] \
|| die_trace 1 "Expected at least one positional argument (the devices)"
local device="${extra_arguments[0]}"
local size_swap="${arguments[swap]}"
local raid_type="${arguments[raid_type]:-raid0}"
local type="${arguments[type]:-efi}"
local use_luks="${arguments[luks]:-false}"
# Create layout on first disk
create_gpt new_id="gpt_dev0" device="${extra_arguments[0]}"
create_partition new_id="part_${type}_dev0" id="gpt_dev0" size=1GiB type="$type"
[[ $size_swap != "false" ]] \
&& create_partition new_id="part_swap_dev0" id="gpt_dev0" size="$size_swap" type=swap
create_partition new_id="part_root_dev0" id="gpt_dev0" size=remaining type=linux
local root_id
local root_ids=""
if [[ "$use_luks" == "true" ]]; then
create_luks new_id=luks_dev0 name="luks_root_0" id=part_root_dev0
root_id="luks_dev0"
root_ids="${root_ids}luks_dev0;"
for i in "${!extra_arguments[@]}"; do
[[ $i != 0 ]] || continue
create_luks new_id="luks_dev$i" name="luks_root_$i" device="${extra_arguments[$i]}"
root_ids="${root_ids}luks_dev$i;"
done
else
local dev_id=""
root_id="part_root_dev0"
root_ids="${root_ids}part_root_dev0;"
for i in "${!extra_arguments[@]}"; do
[[ $i != 0 ]] || continue
dev_id="root_dev$i"
create_dummy new_id="$dev_id" device="${extra_arguments[$i]}"
root_ids="${root_ids}$dev_id;"
done
fi
format id="part_${type}_dev0" type="$type" label="$type"
[[ $size_swap != "false" ]] \
&& format id="part_swap_dev0" type=swap label=swap
format_btrfs ids="$root_ids" label=root raid_type="$raid_type"
if [[ $type == "efi" ]]; then
DISK_ID_EFI="part_${type}_dev0"
else
DISK_ID_BIOS="part_${type}_dev0"
fi
[[ $size_swap != "false" ]] \
&& DISK_ID_SWAP=part_swap_dev0
DISK_ID_ROOT="$root_id"
DISK_ID_ROOT_TYPE="btrfs"
DISK_ID_ROOT_MOUNT_OPTS="defaults,noatime,compress=zstd,subvol=/root"
}
function create_btrfs_raid_layout() {
die "'create_btrfs_raid_layout' is deprecated, please use 'create_btrfs_centric_layout' instead. It is fully option-compatible to the old version."
}

26
scripts/dispatch_chroot.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
set -uo pipefail
[[ $EXECUTED_IN_CHROOT != "true" ]] \
&& { echo "This script must not be executed directly!" >&2; exit 1; }
# Source the systems profile
source /etc/profile
# Set safe umask
umask 0077
# Export variables (used to determine processor count by some applications)
export NPROC="$(nproc || echo 2)"
export NPROC_ONE="$((NPROC + 1))"
# Set default makeflags and emerge flags for parallel emerges
export MAKEFLAGS="-j$NPROC"
export EMERGE_DEFAULT_OPTS="--jobs=$NPROC_ONE --load-average=$NPROC"
# Unset critical variables
unset key
# Execute the requested command
exec "$@"

999
scripts/functions.sh Normal file
View file

@ -0,0 +1,999 @@
# shellcheck source=./scripts/protection.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1
################################################
# Functions
function sync_time() {
einfo "Syncing time"
try ntpd -g -q
einfo "Current date: $(LANG=C date)"
einfo "Writing time to hardware clock"
hwclock --systohc --utc \
|| die "Could not save time to hardware clock"
}
function check_config() {
[[ $KEYMAP =~ ^[0-9A-Za-z-]*$ ]] \
|| die "KEYMAP contains invalid characters"
if [[ "$SYSTEMD" == "true" ]]; then
[[ "$STAGE3_BASENAME" == *systemd* ]] \
|| die "Using systemd requires a systemd stage3 archive!"
else
[[ "$STAGE3_BASENAME" != *systemd* ]] \
|| die "Using OpenRC requires a non-systemd stage3 archive!"
fi
# Check hostname per RFC1123
local hostname_regex='^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
[[ $HOSTNAME =~ $hostname_regex ]] \
|| die "'$HOSTNAME' is not a valid hostname"
[[ -v "DISK_ID_ROOT" && -n $DISK_ID_ROOT ]] \
|| die "You must assign DISK_ID_ROOT"
[[ -v "DISK_ID_EFI" && -n $DISK_ID_EFI ]] || [[ -v "DISK_ID_BIOS" && -n $DISK_ID_BIOS ]] \
|| die "You must assign DISK_ID_EFI or DISK_ID_BIOS"
[[ -v "DISK_ID_BIOS" ]] && [[ ! -v "DISK_ID_TO_UUID[$DISK_ID_BIOS]" ]] \
&& die "Missing uuid for DISK_ID_BIOS, have you made sure it is used?"
[[ -v "DISK_ID_EFI" ]] && [[ ! -v "DISK_ID_TO_UUID[$DISK_ID_EFI]" ]] \
&& die "Missing uuid for DISK_ID_EFI, have you made sure it is used?"
[[ -v "DISK_ID_SWAP" ]] && [[ ! -v "DISK_ID_TO_UUID[$DISK_ID_SWAP]" ]] \
&& die "Missing uuid for DISK_ID_SWAP, have you made sure it is used?"
[[ -v "DISK_ID_ROOT" ]] && [[ ! -v "DISK_ID_TO_UUID[$DISK_ID_ROOT]" ]] \
&& die "Missing uuid for DISK_ID_ROOT, have you made sure it is used?"
if [[ -v "DISK_ID_EFI" ]]; then
IS_EFI=true
else
IS_EFI=false
fi
}
function preprocess_config() {
disk_configuration
# Check encryption key if used
[[ $USED_ENCRYPTION == "true" ]] \
&& check_encryption_key
check_config
}
function prepare_installation_environment() {
maybe_exec 'before_prepare_environment'
einfo "Preparing installation environment"
local wanted_programs=(
gpg
hwclock
lsblk
ntpd
partprobe
python3
"?rhash"
sha512sum
sgdisk
uuidgen
wget
)
[[ $USED_BTRFS == "true" ]] \
&& wanted_programs+=(btrfs)
[[ $USED_ZFS == "true" ]] \
&& wanted_programs+=(zfs)
[[ $USED_RAID == "true" ]] \
&& wanted_programs+=(mdadm)
[[ $USED_LUKS == "true" ]] \
&& wanted_programs+=(cryptsetup)
# Check for existence of required programs
check_wanted_programs "${wanted_programs[@]}"
# Sync time now to prevent issues later
sync_time
maybe_exec 'after_prepare_environment'
}
function check_encryption_key() {
if [[ -z "${GENTOO_INSTALL_ENCRYPTION_KEY+set}" ]]; then
elog "You have enabled encryption, but haven't specified a key in the environment variable GENTOO_INSTALL_ENCRYPTION_KEY."
if ask "Do you want to enter an encryption key now?"; then
local encryption_key_1
local encryption_key_2
while true; do
flush_stdin
IFS="" read -s -r -p "Enter encryption key: " encryption_key_1 \
|| die "Error in read"
echo
[[ ${#encryption_key_1} -ge 8 ]] \
|| { ewarn "Your encryption key must be at least 8 characters long."; continue; }
flush_stdin
IFS="" read -s -r -p "Repeat encryption key: " encryption_key_2 \
|| die "Error in read"
echo
[[ "$encryption_key_1" == "$encryption_key_2" ]] \
|| { ewarn "Encryption keys mismatch."; continue; }
break
done
export GENTOO_INSTALL_ENCRYPTION_KEY="$encryption_key_1"
else
die "Please export GENTOO_INSTALL_ENCRYPTION_KEY with the desired key."
fi
fi
[[ ${#GENTOO_INSTALL_ENCRYPTION_KEY} -ge 8 ]] \
|| die "Your encryption key must be at least 8 characters long."
}
function add_summary_entry() {
local parent="$1"
local id="$2"
local name="$3"
local hint="$4"
local desc="$5"
local ptr
case "$id" in
"${DISK_ID_BIOS-__unused__}") ptr="← bios" ;;
"${DISK_ID_EFI-__unused__}") ptr="← efi" ;;
"${DISK_ID_SWAP-__unused__}") ptr="← swap" ;;
"${DISK_ID_ROOT-__unused__}") ptr="← root" ;;
# \x1f characters compensate for printf byte count and unicode character count mismatch due to '←'
*) ptr="$(echo -e "\x1f\x1f")" ;;
esac
summary_tree[$parent]+=";$id"
summary_name[$id]="$name"
summary_hint[$id]="$hint"
summary_ptr[$id]="$ptr"
summary_desc[$id]="$desc"
}
function summary_color_args() {
for arg in "$@"; do
if [[ -v "arguments[$arg]" ]]; then
printf '%-28s ' "$arg=${arguments[$arg]}"
fi
done
}
function disk_existing() {
local new_id="${arguments[new_id]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
add_summary_entry __root__ "$new_id" "${arguments[device]}" "(no-format, existing)" ""
fi
# no-op;
}
function disk_create_gpt() {
local new_id="${arguments[new_id]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
if [[ -v arguments[id] ]]; then
add_summary_entry "${arguments[id]}" "$new_id" "gpt" "" ""
else
add_summary_entry __root__ "$new_id" "${arguments[device]}" "(gpt)" ""
fi
return 0
fi
local device
local device_desc=""
if [[ -v arguments[id] ]]; then
device="$(resolve_device_by_id "${arguments[id]}")"
device_desc="$device ($id)"
else
device="${arguments[device]}"
device_desc="$device"
fi
local ptuuid="${DISK_ID_TO_UUID[$new_id]}"
einfo "Creating new gpt partition table ($new_id) on $device_desc"
wipefs --quiet --all --force "$device" \
|| die "Could not erase previous file system signatures from '$device'"
sgdisk -Z -U "$ptuuid" "$device" >/dev/null \
|| die "Could not create new gpt partition table ($new_id) on '$device'"
partprobe "$device"
}
function disk_create_partition() {
local new_id="${arguments[new_id]}"
local id="${arguments[id]}"
local size="${arguments[size]}"
local type="${arguments[type]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
add_summary_entry "$id" "$new_id" "part" "($type)" "$(summary_color_args size)"
return 0
fi
if [[ $size == "remaining" ]]; then
arg_size=0
else
arg_size="+$size"
fi
local device
device="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
local partuuid="${DISK_ID_TO_UUID[$new_id]}"
local extra_args=""
case "$type" in
'bios') type='ef02' extra_args='--attributes=0:set:2';;
'efi') type='ef00' ;;
'swap') type='8200' ;;
'raid') type='fd00' ;;
'luks') type='8309' ;;
'linux') type='8300' ;;
*) ;;
esac
einfo "Creating partition ($new_id) with type=$type, size=$size on $device"
# shellcheck disable=SC2086
sgdisk -n "0:0:$arg_size" -t "0:$type" -u "0:$partuuid" $extra_args "$device" >/dev/null \
|| die "Could not create new gpt partition ($new_id) on '$device' ($id)"
partprobe "$device"
# On some system, we need to wait a bit for the partition to show up.
local new_device
new_device="$(resolve_device_by_id "$new_id")" \
|| die "Could not resolve new device with id=$new_id"
for i in {1..10}; do
[[ -e "$new_device" ]] && break
[[ "$i" -eq 1 ]] && printf "Waiting for partition (%s) to appear..." "$new_device"
printf " %s" "$((10 - i + 1))"
sleep 1
[[ "$i" -eq 10 ]] && echo
done
}
function disk_create_raid() {
local new_id="${arguments[new_id]}"
local level="${arguments[level]}"
local name="${arguments[name]}"
local ids="${arguments[ids]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
local id
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
add_summary_entry "$id" "_$new_id" "raid$level" "" "$(summary_color_args name)"
done
add_summary_entry __root__ "$new_id" "raid$level" "" "$(summary_color_args name)"
return 0
fi
local devices_desc=""
local devices=()
local id
local dev
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
dev="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
devices+=("$dev")
devices_desc+="$dev ($id), "
done
devices_desc="${devices_desc:0:-2}"
local mddevice="/dev/md/$name"
local uuid="${DISK_ID_TO_UUID[$new_id]}"
extra_args=()
if [[ ${level} == 1 ]]; then
extra_args+=("--metadata=1.0")
else
extra_args+=("--metadata=1.2")
fi
einfo "Creating raid$level ($new_id) on $devices_desc"
mdadm \
--create "$mddevice" \
--verbose \
--homehost="$HOSTNAME" \
"${extra_args[@]}" \
--raid-devices="${#devices[@]}" \
--uuid="$uuid" \
--level="$level" \
"${devices[@]}" \
|| die "Could not create raid$level array '$mddevice' ($new_id) on $devices_desc"
}
function disk_create_luks() {
local new_id="${arguments[new_id]}"
local name="${arguments[name]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
if [[ -v arguments[id] ]]; then
add_summary_entry "${arguments[id]}" "$new_id" "luks" "" ""
else
add_summary_entry __root__ "$new_id" "${arguments[device]}" "(luks)" ""
fi
return 0
fi
local device
local device_desc=""
if [[ -v arguments[id] ]]; then
device="$(resolve_device_by_id "${arguments[id]}")"
device_desc="$device ($id)"
else
device="${arguments[device]}"
device_desc="$device"
fi
local uuid="${DISK_ID_TO_UUID[$new_id]}"
einfo "Creating luks ($new_id) on $device_desc"
cryptsetup luksFormat \
--type luks2 \
--uuid "$uuid" \
--key-file <(echo -n "$GENTOO_INSTALL_ENCRYPTION_KEY") \
--cipher aes-xts-plain64 \
--hash sha512 \
--pbkdf argon2id \
--iter-time 4000 \
--key-size 512 \
--batch-mode \
"$device" \
|| die "Could not create luks on $device_desc"
mkdir -p "$LUKS_HEADER_BACKUP_DIR" \
|| die "Could not create luks header backup dir '$LUKS_HEADER_BACKUP_DIR'"
local header_file="$LUKS_HEADER_BACKUP_DIR/luks-header-$id-${uuid,,}.img"
[[ ! -e $header_file ]] \
|| rm "$header_file" \
|| die "Could not remove old luks header backup file '$header_file'"
cryptsetup luksHeaderBackup "$device" \
--header-backup-file "$header_file" \
|| die "Could not backup luks header on $device_desc"
cryptsetup open --type luks2 \
--key-file <(echo -n "$GENTOO_INSTALL_ENCRYPTION_KEY") \
"$device" "$name" \
|| die "Could not open luks encrypted device $device_desc"
}
function disk_create_dummy() {
local new_id="${arguments[new_id]}"
local device="${arguments[device]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
add_summary_entry __root__ "$new_id" "$device" "" ""
return 0
fi
}
function init_btrfs() {
local device="$1"
local desc="$2"
mkdir -p /btrfs \
|| die "Could not create /btrfs directory"
mount "$device" /btrfs \
|| die "Could not mount $desc to /btrfs"
btrfs subvolume create /btrfs/root \
|| die "Could not create btrfs subvolume /root on $desc"
btrfs subvolume set-default /btrfs/root \
|| die "Could not set default btrfs subvolume to /root on $desc"
umount /btrfs \
|| die "Could not unmount btrfs on $desc"
}
function disk_format() {
local id="${arguments[id]}"
local type="${arguments[type]}"
local label="${arguments[label]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
add_summary_entry "${arguments[id]}" "__fs__${arguments[id]}" "${arguments[type]}" "(fs)" "$(summary_color_args label)"
return 0
fi
local device
device="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
einfo "Formatting $device ($id) with $type"
wipefs --quiet --all --force "$device" \
|| die "Could not erase previous file system signatures from '$device' ($id)"
case "$type" in
'bios'|'efi')
if [[ -v "arguments[label]" ]]; then
mkfs.fat -F 32 -n "$label" "$device" \
|| die "Could not format device '$device' ($id)"
else
mkfs.fat -F 32 "$device" \
|| die "Could not format device '$device' ($id)"
fi
;;
'swap')
if [[ -v "arguments[label]" ]]; then
mkswap -L "$label" "$device" \
|| die "Could not format device '$device' ($id)"
else
mkswap "$device" \
|| die "Could not format device '$device' ($id)"
fi
# Try to swapoff in case the system enabled swap automatically
swapoff "$device" &>/dev/null
;;
'ext4')
if [[ -v "arguments[label]" ]]; then
mkfs.ext4 -q -L "$label" "$device" \
|| die "Could not format device '$device' ($id)"
else
mkfs.ext4 -q "$device" \
|| die "Could not format device '$device' ($id)"
fi
;;
'btrfs')
if [[ -v "arguments[label]" ]]; then
mkfs.btrfs -q -L "$label" "$device" \
|| die "Could not format device '$device' ($id)"
else
mkfs.btrfs -q "$device" \
|| die "Could not format device '$device' ($id)"
fi
init_btrfs "$device" "'$device' ($id)"
;;
*) die "Unknown filesystem type" ;;
esac
}
# This function will be called when a custom zfs pool type has been chosen.
# $1: either 'true' or 'false' determining if the datasets should be encrypted
# $2: either 'false' or a value determining the dataset compression algorithm
# $3: a string describing all device paths (for error messages)
# $@: device paths
function format_zfs_standard() {
local encrypt="$1"
local compress="$2"
local device_desc="$3"
shift 3
local devices=("$@")
local extra_args=()
einfo "Creating zfs pool on $devices_desc"
local zfs_stdin=""
if [[ "$encrypt" == true ]]; then
extra_args+=(
"-O" "encryption=aes-256-gcm"
"-O" "keyformat=passphrase"
"-O" "keylocation=prompt"
)
zfs_stdin="$GENTOO_INSTALL_ENCRYPTION_KEY"
fi
# dnodesize=legacy might be needed for GRUB2, but auto is preferred for xattr=sa.
zpool create \
-R "$ROOT_MOUNTPOINT" \
-o ashift=12 \
-O acltype=posix \
-O atime=off \
-O xattr=sa \
-O dnodesize=auto \
-O mountpoint=none \
-O canmount=noauto \
-O devices=off \
"${extra_args[@]}" \
rpool \
"${devices[@]}" \
<<< "$zfs_stdin" \
|| die "Could not create zfs pool on $devices_desc"
if [[ "$compress" != false ]]; then
zfs set "compression=$compress" rpool \
|| die "Could enable compression on dataset 'rpool'"
fi
zfs create rpool/ROOT \
|| die "Could not create zfs dataset 'rpool/ROOT'"
zfs create -o mountpoint=/ rpool/ROOT/default \
|| die "Could not create zfs dataset 'rpool/ROOT/default'"
zpool set bootfs=rpool/ROOT/default rpool \
|| die "Could not set zfs property bootfs on rpool"
}
function disk_format_zfs() {
local ids="${arguments[ids]}"
local pool_type="${arguments[pool_type]}"
local encrypt="${arguments[encrypt]-false}"
local compress="${arguments[compress]-false}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
local id
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
add_summary_entry "$id" "__fs__$id" "zfs" "(fs)" "$(summary_color_args label)"
done
return 0
fi
local devices_desc=""
local devices=()
local id
local dev
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
dev="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
devices+=("$dev")
devices_desc+="$dev ($id), "
done
devices_desc="${devices_desc:0:-2}"
wipefs --quiet --all --force "${devices[@]}" \
|| die "Could not erase previous file system signatures from $devices_desc"
if [[ "$pool_type" == "custom" ]]; then
format_zfs_custom "$devices_desc" "${devices[@]}"
else
format_zfs_standard "$encrypt" "$compress" "$devices_desc" "${devices[@]}"
fi
}
function disk_format_btrfs() {
local ids="${arguments[ids]}"
local label="${arguments[label]}"
local raid_type="${arguments[raid_type]}"
if [[ ${disk_action_summarize_only-false} == "true" ]]; then
local id
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
add_summary_entry "$id" "__fs__$id" "btrfs" "(fs)" "$(summary_color_args label)"
done
return 0
fi
local devices_desc=""
local devices=()
local id
local dev
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${ids//';'/ }; do
dev="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
devices+=("$dev")
devices_desc+="$dev ($id), "
done
devices_desc="${devices_desc:0:-2}"
wipefs --quiet --all --force "${devices[@]}" \
|| die "Could not erase previous file system signatures from $devices_desc"
# Collect extra arguments
extra_args=()
if [[ "${#devices}" -gt 1 ]] && [[ -v "arguments[raid_type]" ]]; then
extra_args+=("-d" "$raid_type")
fi
if [[ -v "arguments[label]" ]]; then
extra_args+=("-L" "$label")
fi
einfo "Creating btrfs on $devices_desc"
mkfs.btrfs -q "${extra_args[@]}" "${devices[@]}" \
|| die "Could not create btrfs on $devices_desc"
init_btrfs "${devices[0]}" "btrfs array ($devices_desc)"
}
function apply_disk_action() {
unset known_arguments
unset arguments; declare -A arguments; parse_arguments "$@"
case "${arguments[action]}" in
'existing') disk_existing ;;
'create_gpt') disk_create_gpt ;;
'create_partition') disk_create_partition ;;
'create_raid') disk_create_raid ;;
'create_luks') disk_create_luks ;;
'create_dummy') disk_create_dummy ;;
'format') disk_format ;;
'format_zfs') disk_format_zfs ;;
'format_btrfs') disk_format_btrfs ;;
*) echo "Ignoring invalid action: ${arguments[action]}" ;;
esac
}
function print_summary_tree_entry() {
local indent_chars=""
local indent="0"
local d="1"
local maxd="$((depth - 1))"
while [[ $d -lt $maxd ]]; do
if [[ ${summary_depth_continues[$d]} == "true" ]]; then
indent_chars+='│ '
else
indent_chars+=' '
fi
indent=$((indent + 2))
d="$((d + 1))"
done
if [[ $maxd -gt 0 ]]; then
if [[ ${summary_depth_continues[$maxd]} == "true" ]]; then
indent_chars+='├─'
else
indent_chars+='└─'
fi
indent=$((indent + 2))
fi
local name="${summary_name[$root]}"
local hint="${summary_hint[$root]}"
local desc="${summary_desc[$root]}"
local ptr="${summary_ptr[$root]}"
local id_name=""
if [[ $root != __* ]]; then
if [[ $root == _* ]]; then
id_name="${root:1}"
else
id_name="${root}"
fi
fi
local align=0
if [[ $indent -lt 33 ]]; then
align="$((33 - indent))"
fi
elog "$indent_chars$(printf "%-${align}s %-47s %s" \
"$name $hint" \
"$id_name $ptr" \
"$desc")"
}
function print_summary_tree() {
local root="$1"
local depth="$((depth + 1))"
local has_children=false
if [[ -v "summary_tree[$root]" ]]; then
local children="${summary_tree[$root]}"
has_children=true
summary_depth_continues[$depth]=true
else
summary_depth_continues[$depth]=false
fi
if [[ $root != __root__ ]]; then
print_summary_tree_entry "$root"
fi
if [[ $has_children == "true" ]]; then
local count
count="$(tr ';' '\n' <<< "$children" | grep -c '\S')" \
|| count=0
local idx=0
# Splitting is intentional here
# shellcheck disable=SC2086
for id in ${children//';'/ }; do
idx="$((idx + 1))"
[[ $idx == "$count" ]] \
&& summary_depth_continues[$depth]=false
print_summary_tree "$id"
# separate blocks by newline
[[ ${summary_depth_continues[0]} == "true" ]] && [[ $depth == 1 ]] && [[ $idx == "$count" ]] \
&& elog
done
fi
}
function apply_disk_actions() {
local param
local current_params=()
for param in "${DISK_ACTIONS[@]}"; do
if [[ $param == ';' ]]; then
apply_disk_action "${current_params[@]}"
current_params=()
else
current_params+=("$param")
fi
done
}
function summarize_disk_actions() {
elog "Current lsblk output:"
for_line_in <(lsblk \
|| die "Error in lsblk") elog
local disk_action_summarize_only=true
declare -A summary_tree
declare -A summary_name
declare -A summary_hint
declare -A summary_ptr
declare -A summary_desc
declare -A summary_depth_continues
apply_disk_actions
local depth=-1
elog
elog "Configured disk layout:"
elog ────────────────────────────────────────────────────────────────────────────────
elog "$(printf '%-26s %-28s %s' NODE ID OPTIONS)"
elog ────────────────────────────────────────────────────────────────────────────────
print_summary_tree __root__
elog ────────────────────────────────────────────────────────────────────────────────
}
function apply_disk_configuration() {
summarize_disk_actions
if [[ $NO_PARTITIONING_OR_FORMATTING == true ]]; then
elog "You have chosen an existing disk configuration. No devices will"
elog "actually be re-partitioned or formatted. Please make sure that all"
elog "devices are already formatted."
else
ewarn "Please ensure that all selected devices are fully unmounted and are"
ewarn "not otherwise in use by the system. This includes stopping mdadm arrays"
ewarn "and closing opened luks volumes if applicable for all relevant devices."
ewarn "Otherwise, automatic partitioning may fail."
fi
ask "Do you really want to apply this disk configuration?" \
|| die "Aborted"
countdown "Applying in " 5
maybe_exec 'before_disk_configuration'
einfo "Applying disk configuration"
apply_disk_actions
einfo "Disk configuration was applied successfully"
elog "New lsblk output:"
for_line_in <(lsblk \
|| die "Error in lsblk") elog
maybe_exec 'after_disk_configuration'
}
function mount_efivars() {
# Skip if already mounted
mountpoint -q -- "/sys/firmware/efi/efivars" \
&& return
# Mount efivars
einfo "Mounting efivars"
mount -t efivarfs efivarfs "/sys/firmware/efi/efivars" \
|| die "Could not mount efivarfs"
}
function mount_by_id() {
local dev
local id="$1"
local mountpoint="$2"
# Skip if already mounted
mountpoint -q -- "$mountpoint" \
&& return
# Mount device
einfo "Mounting device with id=$id to '$mountpoint'"
mkdir -p "$mountpoint" \
|| die "Could not create mountpoint directory '$mountpoint'"
dev="$(resolve_device_by_id "$id")" \
|| die "Could not resolve device with id=$id"
mount "$dev" "$mountpoint" \
|| die "Could not mount device '$dev'"
}
function mount_root() {
if [[ $USED_ZFS == "true" ]] && ! mountpoint -q -- "$ROOT_MOUNTPOINT"; then
die "Error: Expected zfs to be mounted under '$ROOT_MOUNTPOINT', but it isn't."
else
mount_by_id "$DISK_ID_ROOT" "$ROOT_MOUNTPOINT"
fi
}
function bind_repo_dir() {
# Use new location by default
export GENTOO_INSTALL_REPO_DIR="$GENTOO_INSTALL_REPO_BIND"
# Bind the repo dir to a location in /tmp,
# so it can be accessed from within the chroot
mountpoint -q -- "$GENTOO_INSTALL_REPO_BIND" \
&& return
# Mount root device
einfo "Bind mounting repo directory"
mkdir -p "$GENTOO_INSTALL_REPO_BIND" \
|| die "Could not create mountpoint directory '$GENTOO_INSTALL_REPO_BIND'"
mount --bind "$GENTOO_INSTALL_REPO_DIR_ORIGINAL" "$GENTOO_INSTALL_REPO_BIND" \
|| die "Could not bind mount '$GENTOO_INSTALL_REPO_DIR_ORIGINAL' to '$GENTOO_INSTALL_REPO_BIND'"
}
function download_stage3() {
cd "$TMP_DIR" \
|| die "Could not cd into '$TMP_DIR'"
local STAGE3_RELEASES="$GENTOO_MIRROR/releases/$GENTOO_ARCH/autobuilds/current-$STAGE3_BASENAME/"
# Download upstream list of files
CURRENT_STAGE3="$(download_stdout "$STAGE3_RELEASES")" \
|| die "Could not retrieve list of tarballs"
# Decode urlencoded strings
CURRENT_STAGE3=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read()))' <<< "$CURRENT_STAGE3")
# Parse output for correct filename
CURRENT_STAGE3="$(grep -o "\"${STAGE3_BASENAME}-[0-9A-Z]*.tar.xz\"" <<< "$CURRENT_STAGE3" \
| sort -u | head -1)" \
|| die "Could not parse list of tarballs"
# Strip quotes
CURRENT_STAGE3="${CURRENT_STAGE3:1:-1}"
# File to indiciate successful verification
CURRENT_STAGE3_VERIFIED="${CURRENT_STAGE3}.verified"
maybe_exec 'before_download_stage3' "$STAGE3_BASENAME"
# Download file if not already downloaded
if [[ -e $CURRENT_STAGE3_VERIFIED ]]; then
einfo "$STAGE3_BASENAME tarball already downloaded and verified"
else
einfo "Downloading $STAGE3_BASENAME tarball"
download "$STAGE3_RELEASES/${CURRENT_STAGE3}" "${CURRENT_STAGE3}"
download "$STAGE3_RELEASES/${CURRENT_STAGE3}.DIGESTS" "${CURRENT_STAGE3}.DIGESTS"
# Import gentoo keys
einfo "Importing gentoo gpg key"
local GENTOO_GPG_KEY="$TMP_DIR/gentoo-keys.gpg"
download "https://gentoo.org/.well-known/openpgpkey/hu/wtktzo4gyuhzu8a4z5fdj3fgmr1u6tob?l=releng" "$GENTOO_GPG_KEY" \
|| die "Could not retrieve gentoo gpg key"
gpg --quiet --import < "$GENTOO_GPG_KEY" \
|| die "Could not import gentoo gpg key"
# Verify DIGESTS signature
einfo "Verifying tarball signature"
gpg --quiet --verify "${CURRENT_STAGE3}.DIGESTS" \
|| die "Signature of '${CURRENT_STAGE3}.DIGESTS' invalid!"
# Check hashes
einfo "Verifying tarball integrity"
# Replace any absolute paths in the digest file with just the stage3 basename, so it will be found by rhash
digest_line=$(grep 'tar.xz$' "${CURRENT_STAGE3}.DIGESTS" | sed -e 's/ .*stage3-/ stage3-/')
if type rhash &>/dev/null; then
rhash -P --check <(echo "# SHA512"; echo "$digest_line") \
|| die "Checksum mismatch!"
else
sha512sum --check <<< "$digest_line" \
|| die "Checksum mismatch!"
fi
# Create verification file in case the script is restarted
touch_or_die 0644 "$CURRENT_STAGE3_VERIFIED"
fi
maybe_exec 'after_download_stage3' "${CURRENT_STAGE3}"
}
function extract_stage3() {
mount_root
[[ -n $CURRENT_STAGE3 ]] \
|| die "CURRENT_STAGE3 is not set"
[[ -e "$TMP_DIR/$CURRENT_STAGE3" ]] \
|| die "stage3 file does not exist"
maybe_exec 'before_extract_stage3' "$TMP_DIR/$CURRENT_STAGE3" "$ROOT_MOUNTPOINT"
# Go to root directory
cd "$ROOT_MOUNTPOINT" \
|| die "Could not move to '$ROOT_MOUNTPOINT'"
# Ensure the directory is empty
find . -mindepth 1 -maxdepth 1 -not -name 'lost+found' \
| grep -q . \
&& die "root directory '$ROOT_MOUNTPOINT' is not empty"
# Extract tarball
einfo "Extracting stage3 tarball"
tar xpf "$TMP_DIR/$CURRENT_STAGE3" --xattrs --numeric-owner \
|| die "Error while extracting tarball"
cd "$TMP_DIR" \
|| die "Could not cd into '$TMP_DIR'"
maybe_exec 'after_extract_stage3' "$TMP_DIR/$CURRENT_STAGE3" "$ROOT_MOUNTPOINT"
}
function gentoo_umount() {
if mountpoint -q -- "$ROOT_MOUNTPOINT"; then
einfo "Unmounting root filesystem"
umount -R -l "$ROOT_MOUNTPOINT" \
|| die "Could not unmount filesystems"
fi
}
function init_bash() {
source /etc/profile
umask 0077
export PS1='(chroot) \[\]\u\[\]@\h \[\]\w \[\]\$ \[\]'
}; export -f init_bash
function env_update() {
env-update \
|| die "Error in env-update"
source /etc/profile \
|| die "Could not source /etc/profile"
umask 0077
}
function mkdir_or_die() {
# shellcheck disable=SC2174
mkdir -m "$1" -p "$2" \
|| die "Could not create directory '$2'"
}
function touch_or_die() {
touch "$2" \
|| die "Could not touch '$2'"
chmod "$1" "$2"
}
# $1: root directory
# $@: command...
function gentoo_chroot() {
if [[ $# -eq 1 ]]; then
einfo "To later unmount all virtual filesystems, simply use umount -l ${1@Q}"
gentoo_chroot "$1" /bin/bash --init-file <(echo 'init_bash')
fi
[[ ${EXECUTED_IN_CHROOT-false} == "false" ]] \
|| die "Already in chroot"
local chroot_dir="$1"
shift
# Bind repo directory to tmp
bind_repo_dir
# Copy resolv.conf
einfo "Preparing chroot environment"
install --mode=0644 /etc/resolv.conf "$chroot_dir/etc/resolv.conf" \
|| die "Could not copy resolv.conf"
# Mount virtual filesystems
einfo "Mounting virtual filesystems"
(
mountpoint -q -- "$chroot_dir/proc" || mount -t proc /proc "$chroot_dir/proc" || exit 1
mountpoint -q -- "$chroot_dir/run" || {
mount --rbind /run "$chroot_dir/run" &&
mount --make-rslave "$chroot_dir/run"; } || exit 1
mountpoint -q -- "$chroot_dir/tmp" || {
mount --rbind /tmp "$chroot_dir/tmp" &&
mount --make-rslave "$chroot_dir/tmp"; } || exit 1
mountpoint -q -- "$chroot_dir/sys" || {
mount --rbind /sys "$chroot_dir/sys" &&
mount --make-rslave "$chroot_dir/sys"; } || exit 1
mountpoint -q -- "$chroot_dir/dev" || {
mount --rbind /dev "$chroot_dir/dev" &&
mount --make-rslave "$chroot_dir/dev"; } || exit 1
) || die "Could not mount virtual filesystems"
# Cache lsblk output, because it doesn't work correctly in chroot (returns almost no info for devices, e.g. empty uuids)
cache_lsblk_output
# Execute command
einfo "Chrooting..."
EXECUTED_IN_CHROOT=true \
TMP_DIR="$TMP_DIR" \
CACHED_LSBLK_OUTPUT="$CACHED_LSBLK_OUTPUT" \
exec chroot -- "$chroot_dir" "$GENTOO_INSTALL_REPO_DIR/scripts/dispatch_chroot.sh" "$@" \
|| die "Failed to chroot into '$chroot_dir'."
}
function enable_service() {
if [[ $SYSTEMD == "true" ]]; then
try systemctl enable "$1"
else
try rc-update add "$1" default
fi
}

555
scripts/main.sh Normal file
View file

@ -0,0 +1,555 @@
# shellcheck source=./scripts/protection.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1
################################################
# Functions
function install_stage3() {
prepare_installation_environment
apply_disk_configuration
download_stage3
extract_stage3
}
function configure_base_system() {
if [[ $MUSL == "true" ]]; then
einfo "Installing musl-locales"
if [[ $USE_PORTAGE_TESTING == "false" ]]; then
echo "sys-apps/musl-locales" >> /etc/portage/package.accept_keywords/musl-locales
fi
try emerge --verbose sys-apps/musl-locales
else
einfo "Generating locales"
echo "$LOCALES" > /etc/locale.gen \
|| die "Could not write /etc/locale.gen"
locale-gen \
|| die "Could not generate locales"
fi
if [[ $SYSTEMD == "true" ]]; then
einfo "Setting machine-id"
systemd-machine-id-setup \
|| die "Could not setup systemd machine id"
# Set hostname
einfo "Selecting hostname"
echo "$HOSTNAME" > /etc/hostname \
|| die "Could not write /etc/hostname"
# Set keymap
einfo "Selecting keymap"
echo "KEYMAP=$KEYMAP" > /etc/vconsole.conf \
|| die "Could not write /etc/vconsole.conf"
# Set locale
einfo "Selecting locale"
echo "LANG=$LOCALE" > /etc/locale.conf \
|| die "Could not write /etc/locale.conf"
einfo "Selecting timezone"
ln -sfn "../usr/share/zoneinfo/$TIMEZONE" /etc/localtime \
|| die "Could not change /etc/localtime link"
else
# Set hostname
einfo "Selecting hostname"
sed -i "/hostname=/c\\hostname=\"$HOSTNAME\"" /etc/conf.d/hostname \
|| die "Could not sed replace in /etc/conf.d/hostname"
# Set timezone
if [[ $MUSL == "true" ]]; then
try emerge -v sys-libs/timezone-data
einfo "Selecting timezone"
echo -e "\nTZ=\"$TIMEZONE\"" >> /etc/env.d/00musl \
|| die "Could not write to /etc/env.d/00musl"
else
einfo "Selecting timezone"
echo "$TIMEZONE" > /etc/timezone \
|| die "Could not write /etc/timezone"
chmod 644 /etc/timezone \
|| die "Could not set correct permissions for /etc/timezone"
try emerge -v --config sys-libs/timezone-data
fi
# Set keymap
einfo "Selecting keymap"
sed -i "/keymap=/c\\keymap=\"$KEYMAP\"" /etc/conf.d/keymaps \
|| die "Could not sed replace in /etc/conf.d/keymaps"
# Set locale
einfo "Selecting locale"
try eselect locale set "$LOCALE"
fi
# Update environment
env_update
}
function configure_portage() {
# Prepare /etc/portage for autounmask
mkdir_or_die 0755 "/etc/portage/package.use"
touch_or_die 0644 "/etc/portage/package.use/zz-autounmask"
mkdir_or_die 0755 "/etc/portage/package.keywords"
touch_or_die 0644 "/etc/portage/package.keywords/zz-autounmask"
touch_or_die 0644 "/etc/portage/package.license"
if [[ $SELECT_MIRRORS == "true" ]]; then
einfo "Temporarily installing mirrorselect"
try emerge --verbose --oneshot app-portage/mirrorselect
einfo "Selecting fastest portage mirrors"
mirrorselect_params=("-s" "4" "-b" "10")
[[ $SELECT_MIRRORS_LARGE_FILE == "true" ]] \
&& mirrorselect_params+=("-D")
try mirrorselect "${mirrorselect_params[@]}"
fi
if [[ $ENABLE_BINPKG == "true" ]]; then
echo 'FEATURES="getbinpkg"' >> /etc/portage/make.conf
getuto
chmod 644 /etc/portage/gnupg/pubring.kbx
fi
chmod 644 /etc/portage/make.conf \
|| die "Could not chmod 644 /etc/portage/make.conf"
}
function enable_sshd() {
einfo "Installing and enabling sshd"
install -m0600 -o root -g root "$GENTOO_INSTALL_REPO_DIR/contrib/sshd_config" /etc/ssh/sshd_config \
|| die "Could not install /etc/ssh/sshd_config"
enable_service sshd
}
function install_authorized_keys() {
mkdir_or_die 0700 "/root/"
mkdir_or_die 0700 "/root/.ssh"
if [[ -n "$ROOT_SSH_AUTHORIZED_KEYS" ]]; then
einfo "Adding authorized keys for root"
touch_or_die 0600 "/root/.ssh/authorized_keys"
echo "$ROOT_SSH_AUTHORIZED_KEYS" > "/root/.ssh/authorized_keys" \
|| die "Could not add ssh key to /root/.ssh/authorized_keys"
fi
}
function generate_initramfs() {
local output="$1"
# Generate initramfs
einfo "Generating initramfs"
local modules=()
[[ $USED_RAID == "true" ]] \
&& modules+=("mdraid")
[[ $USED_LUKS == "true" ]] \
&& modules+=("crypt crypt-gpg")
[[ $USED_BTRFS == "true" ]] \
&& modules+=("btrfs")
[[ $USED_ZFS == "true" ]] \
&& modules+=("zfs")
local kver
kver="$(readlink /usr/src/linux)" \
|| die "Could not figure out kernel version from /usr/src/linux symlink."
kver="${kver#linux-}"
dracut_opts=()
if [[ $SYSTEMD == "true" && $SYSTEMD_INITRAMFS_SSHD == "true" ]]; then
cd /tmp || die "Could not change into /tmp"
try git clone https://github.com/gsauthof/dracut-sshd
try cp -r dracut-sshd/46sshd /usr/lib/dracut/modules.d
sed -e 's/^Type=notify/Type=simple/' \
-e 's@^\(ExecStart=/usr/sbin/sshd\) -D@\1 -e -D@' \
-i /usr/lib/dracut/modules.d/46sshd/sshd.service \
|| die "Could not replace sshd options in service file"
dracut_opts+=("--install" "/etc/systemd/network/20-wired.network")
modules+=("systemd-networkd")
fi
# Generate initramfs
# TODO --conf "/dev/null" \
# TODO --confdir "/dev/null" \
try dracut \
--kver "$kver" \
--zstd \
--no-hostonly \
--ro-mnt \
--add "bash ${modules[*]}" \
"${dracut_opts[@]}" \
--force \
"$output"
# Create script to repeat initramfs generation
cat > "$(dirname "$output")/generate_initramfs.sh" <<EOF
#!/bin/bash
kver="\$1"
output="\$2" # At setup time, this was "$output"
[[ -n "\$kver" ]] || { echo "usage \$0 <kernel_version> <output>" >&2; exit 1; }
dracut \\
--kver "\$kver" \\
--zstd \\
--no-hostonly \\
--ro-mnt \\
--add "bash ${modules[*]}" \\
${dracut_opts[@]@Q} \\
--force \\
"\$output"
EOF
}
function get_cmdline() {
local cmdline=("rd.vconsole.keymap=$KEYMAP_INITRAMFS")
cmdline+=("${DISK_DRACUT_CMDLINE[@]}")
if [[ $USED_ZFS != "true" ]]; then
cmdline+=("root=UUID=$(get_blkid_uuid_for_id "$DISK_ID_ROOT")")
fi
echo -n "${cmdline[*]}"
}
function install_kernel_efi() {
try emerge --verbose sys-boot/efibootmgr
# Copy kernel to EFI
local kernel_file
kernel_file="$(find "/boot" \( -name "vmlinuz-*" -or -name 'kernel-*' \) -printf '%f\n' | sort -V | tail -n 1)" \
|| die "Could not list newest kernel file"
try cp "/boot/$kernel_file" "/boot/efi/vmlinuz.efi"
# Generate initramfs
generate_initramfs "/boot/efi/initramfs.img"
# Create boot entry
einfo "Creating efi boot entry"
local efipartdev
efipartdev="$(resolve_device_by_id "$DISK_ID_EFI")" \
|| die "Could not resolve device with id=$DISK_ID_EFI"
efipartdev="$(realpath "$efipartdev")" \
|| die "Error in realpath '$efipartdev'"
local sys_efipart
sys_efipart="/sys/class/block/$(basename "$efipartdev")" \
|| die "Could not construct /sys path to efi partition"
local efipartnum
efipartnum="$(cat "$sys_efipart/partition")" \
|| die "Failed to find partition number for EFI partition $efipartdev"
local gptdev
gptdev="/dev/$(basename "$(readlink -f "$sys_efipart/..")")" \
|| die "Failed to find parent device for EFI partition $efipartdev"
if [[ ! -e "$gptdev" ]] || [[ -z "$gptdev" ]]; then
gptdev="$(resolve_device_by_id "${DISK_ID_PART_TO_GPT_ID[$DISK_ID_EFI]}")" \
|| die "Could not resolve device with id=${DISK_ID_PART_TO_GPT_ID[$DISK_ID_EFI]}"
fi
try efibootmgr --verbose --create --disk "$gptdev" --part "$efipartnum" --label "gentoo" --loader '\vmlinuz.efi' --unicode 'initrd=\initramfs.img'" $(get_cmdline)"
# Create script to repeat adding efibootmgr entry
cat > "/boot/efi/efibootmgr_add_entry.sh" <<EOF
#!/bin/bash
# This is the command that was used to create the efibootmgr entry when the
# system was installed using gentoo-install.
efibootmgr --verbose --create --disk "$gptdev" --part "$efipartnum" --label "gentoo" --loader '\\vmlinuz.efi' --unicode 'initrd=\\initramfs.img'" $(get_cmdline)"
EOF
}
function generate_syslinux_cfg() {
cat <<EOF
DEFAULT gentoo
PROMPT 0
TIMEOUT 0
LABEL gentoo
LINUX ../vmlinuz-current
APPEND initrd=../initramfs.img $(get_cmdline)
EOF
}
function install_kernel_bios() {
try emerge --verbose sys-boot/syslinux
# Link kernel to known name
local kernel_file
kernel_file="$(find "/boot" \( -name "vmlinuz-*" -or -name 'kernel-*' \) -printf '%f\n' | sort -V | tail -n 1)" \
|| die "Could not list newest kernel file"
try cp "/boot/$kernel_file" "/boot/bios/vmlinuz-current"
# Generate initramfs
generate_initramfs "/boot/bios/initramfs.img"
# Install syslinux
einfo "Installing syslinux"
local biosdev
biosdev="$(resolve_device_by_id "$DISK_ID_BIOS")" \
|| die "Could not resolve device with id=$DISK_ID_BIOS"
mkdir_or_die 0700 "/boot/bios/syslinux"
try syslinux --directory syslinux --install "$biosdev"
# Create syslinux.cfg
generate_syslinux_cfg > /boot/bios/syslinux/syslinux.cfg \
|| die "Could save generated syslinux.cfg"
# Install syslinux MBR record
einfo "Copying syslinux MBR record"
local gptdev
gptdev="$(resolve_device_by_id "${DISK_ID_PART_TO_GPT_ID[$DISK_ID_BIOS]}")" \
|| die "Could not resolve device with id=${DISK_ID_PART_TO_GPT_ID[$DISK_ID_BIOS]}"
try dd bs=440 conv=notrunc count=1 if=/usr/share/syslinux/gptmbr.bin of="$gptdev"
}
function install_kernel() {
# Install vanilla kernel
einfo "Installing vanilla kernel and related tools"
if [[ $IS_EFI == "true" ]]; then
install_kernel_efi
else
install_kernel_bios
fi
einfo "Installing linux-firmware"
echo "sys-kernel/linux-firmware linux-fw-redistributable no-source-code" >> /etc/portage/package.license \
|| die "Could not write to /etc/portage/package.license"
try emerge --verbose linux-firmware
}
function add_fstab_entry() {
printf '%-46s %-24s %-6s %-96s %s\n' "$1" "$2" "$3" "$4" "$5" >> /etc/fstab \
|| die "Could not append entry to fstab"
}
function generate_fstab() {
einfo "Generating fstab"
install -m0644 -o root -g root "$GENTOO_INSTALL_REPO_DIR/contrib/fstab" /etc/fstab \
|| die "Could not overwrite /etc/fstab"
if [[ $USED_ZFS != "true" && -n $DISK_ID_ROOT_TYPE ]]; then
add_fstab_entry "UUID=$(get_blkid_uuid_for_id "$DISK_ID_ROOT")" "/" "$DISK_ID_ROOT_TYPE" "$DISK_ID_ROOT_MOUNT_OPTS" "0 1"
fi
if [[ $IS_EFI == "true" ]]; then
add_fstab_entry "UUID=$(get_blkid_uuid_for_id "$DISK_ID_EFI")" "/boot/efi" "vfat" "defaults,noatime,fmask=0177,dmask=0077,noexec,nodev,nosuid,discard" "0 2"
else
add_fstab_entry "UUID=$(get_blkid_uuid_for_id "$DISK_ID_BIOS")" "/boot/bios" "vfat" "defaults,noatime,fmask=0177,dmask=0077,noexec,nodev,nosuid,discard" "0 2"
fi
if [[ -v "DISK_ID_SWAP" ]]; then
add_fstab_entry "UUID=$(get_blkid_uuid_for_id "$DISK_ID_SWAP")" "none" "swap" "defaults,discard" "0 0"
fi
}
function main_install_gentoo_in_chroot() {
[[ $# == 0 ]] || die "Too many arguments"
maybe_exec 'before_install'
# Remove the root password, making the account accessible for automated
# tasks during the period of installation.
einfo "Clearing root password"
passwd -d root \
|| die "Could not change root password"
if [[ $IS_EFI == "true" ]]; then
# Mount efi partition
mount_efivars
einfo "Mounting efi partition"
mount_by_id "$DISK_ID_EFI" "/boot/efi"
else
# Mount bios partition
einfo "Mounting bios partition"
mount_by_id "$DISK_ID_BIOS" "/boot/bios"
fi
# Sync portage
einfo "Syncing portage tree"
try emerge-webrsync
# Configure basic system things like timezone, locale, ...
maybe_exec 'before_configure_base_system'
configure_base_system
maybe_exec 'after_configure_base_system'
# Prepare portage environment
maybe_exec 'before_configure_portage'
configure_portage
# Install git (for git portage overlays)
einfo "Installing git"
try emerge --verbose dev-vcs/git
if [[ "$PORTAGE_SYNC_TYPE" == "git" ]]; then
mkdir_or_die 0755 "/etc/portage/repos.conf"
cat > /etc/portage/repos.conf/gentoo.conf <<EOF
[DEFAULT]
main-repo = gentoo
[gentoo]
location = /var/db/repos/gentoo
sync-type = git
sync-uri = $PORTAGE_GIT_MIRROR
auto-sync = yes
sync-depth = $([[ $PORTAGE_GIT_FULL_HISTORY == true ]] && echo -n 0 || echo -n 1)
sync-git-verify-commit-signature = yes
sync-openpgp-key-path = /usr/share/openpgp-keys/gentoo-release.asc
EOF
chmod 644 /etc/portage/repos.conf/gentoo.conf \
|| die "Could not change permissions of '/etc/portage/repos.conf/gentoo.conf'"
rm -rf /var/db/repos/gentoo \
|| die "Could not delete obsolete rsync gentoo repository"
try emerge --sync
fi
maybe_exec 'after_configure_portage'
einfo "Generating ssh host keys"
try ssh-keygen -A
# Install authorized_keys before dracut, which might need them for remote unlocking.
install_authorized_keys
einfo "Enabling dracut USE flag on sys-kernel/installkernel"
echo "sys-kernel/installkernel dracut" > /etc/portage/package.use/installkernel \
|| die "Could not write /etc/portage/package.use/installkernel"
# Install required programs and kernel now, in order to
# prevent emerging module before an imminent kernel upgrade
try emerge --verbose sys-kernel/dracut sys-kernel/gentoo-kernel-bin app-arch/zstd
# Install mdadm if we used raid (needed for uuid resolving)
if [[ $USED_RAID == "true" ]]; then
einfo "Installing mdadm"
try emerge --verbose sys-fs/mdadm
fi
# Install cryptsetup if we used luks
if [[ $USED_LUKS == "true" ]]; then
einfo "Installing cryptsetup"
try emerge --verbose sys-fs/cryptsetup
fi
if [[ $SYSTEMD == "true" && $USED_LUKS == "true" ]] ; then
einfo "Enabling cryptsetup USE flag on sys-apps/systemd"
echo "sys-apps/systemd cryptsetup" > /etc/portage/package.use/systemd \
|| die "Could not write /etc/portage/package.use/systemd"
einfo "Rebuilding systemd with changed USE flag"
try emerge --verbose --changed-use --oneshot sys-apps/systemd
fi
# Install btrfs-progs if we used btrfs
if [[ $USED_BTRFS == "true" ]]; then
einfo "Installing btrfs-progs"
try emerge --verbose sys-fs/btrfs-progs
fi
try emerge --verbose dev-vcs/git
# Install zfs kernel module and tools if we used zfs
if [[ $USED_ZFS == "true" ]]; then
einfo "Installing zfs"
try emerge --verbose sys-fs/zfs sys-fs/zfs-kmod
einfo "Enabling zfs services"
if [[ $SYSTEMD == "true" ]]; then
try systemctl enable zfs.target
try systemctl enable zfs-import-cache
try systemctl enable zfs-mount
try systemctl enable zfs-import.target
else
try rc-update add zfs-import boot
try rc-update add zfs-mount boot
fi
fi
# Install kernel and initramfs
maybe_exec 'before_install_kernel'
install_kernel
maybe_exec 'after_install_kernel'
# Generate a valid fstab file
generate_fstab
# Install gentoolkit
einfo "Installing gentoolkit"
try emerge --verbose app-portage/gentoolkit
if [[ $SYSTEMD == "true" ]]; then
if [[ $SYSTEMD_NETWORKD == "true" ]]; then
# Enable systemd networking and dhcp
enable_service systemd-networkd
enable_service systemd-resolved
if [[ $SYSTEMD_NETWORKD_DHCP == "true" ]]; then
echo -en "[Match]\nName=${SYSTEMD_NETWORKD_INTERFACE_NAME}\n\n[Network]\nDHCP=yes" > /etc/systemd/network/20-wired.network \
|| die "Could not write dhcp network config to '/etc/systemd/network/20-wired.network'"
else
addresses=""
for addr in "${SYSTEMD_NETWORKD_ADDRESSES[@]}"; do
addresses="${addresses}Address=$addr\n"
done
echo -en "[Match]\nName=${SYSTEMD_NETWORKD_INTERFACE_NAME}\n\n[Network]\n${addresses}Gateway=$SYSTEMD_NETWORKD_GATEWAY" > /etc/systemd/network/20-wired.network \
|| die "Could not write dhcp network config to '/etc/systemd/network/20-wired.network'"
fi
chown root:systemd-network /etc/systemd/network/20-wired.network \
|| die "Could not change owner of '/etc/systemd/network/20-wired.network'"
chmod 640 /etc/systemd/network/20-wired.network \
|| die "Could not change permissions of '/etc/systemd/network/20-wired.network'"
fi
else
# Install and enable dhcpcd
einfo "Installing dhcpcd"
try emerge --verbose net-misc/dhcpcd
enable_service dhcpcd
fi
if [[ $ENABLE_SSHD == "true" ]]; then
enable_sshd
fi
# Install additional packages, if any.
if [[ ${#ADDITIONAL_PACKAGES[@]} -gt 0 ]]; then
einfo "Installing additional packages"
# shellcheck disable=SC2086
try emerge --verbose --autounmask-continue=y -- "${ADDITIONAL_PACKAGES[@]}"
fi
if ask "Do you want to assign a root password now?"; then
try passwd root
einfo "Root password assigned"
else
try passwd -d root
ewarn "Root password cleared, set one as soon as possible!"
fi
# If configured, change to gentoo testing at the last moment.
# This is to ensure a smooth installation process. You can deal
# with the blockers after installation ;)
if [[ $USE_PORTAGE_TESTING == "true" ]]; then
einfo "Adding ~$GENTOO_ARCH to ACCEPT_KEYWORDS"
echo "ACCEPT_KEYWORDS=\"~$GENTOO_ARCH\"" >> /etc/portage/make.conf \
|| die "Could not modify /etc/portage/make.conf"
fi
maybe_exec 'after_install'
einfo "Gentoo installation complete."
[[ $USED_LUKS == "true" ]] \
&& einfo "A backup of your luks headers can be found at '$LUKS_HEADER_BACKUP_DIR', in case you want to have a backup."
einfo "You may now reboot your system or execute ./install --chroot $ROOT_MOUNTPOINT to enter your system in a chroot."
einfo "Chrooting in this way is always possible in case you need to fix something after rebooting."
}
function main_install() {
[[ $# == 0 ]] || die "Too many arguments"
gentoo_umount
install_stage3
[[ $IS_EFI == "true" ]] \
&& mount_efivars
gentoo_chroot "$ROOT_MOUNTPOINT" "$GENTOO_INSTALL_REPO_BIND/install" __install_gentoo_in_chroot
}
function main_chroot() {
# Skip if already mounted
mountpoint -q -- "$1" \
|| die "'$1' is not a mountpoint"
gentoo_chroot "$@"
}

4
scripts/protection.sh Normal file
View file

@ -0,0 +1,4 @@
if [[ "$GENTOO_INSTALL_REPO_SCRIPT_ACTIVE" != "true" ]]; then
echo " * ERROR: This script must not be executed directly!" >&2
exit 1
fi

453
scripts/utils.sh Normal file
View file

@ -0,0 +1,453 @@
# shellcheck source=./scripts/protection.sh
source "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1
function elog() {
echo "[+] $*"
}
function einfo() {
echo "[+] $*"
}
function ewarn() {
echo "[!] $*" >&2
}
function eerror() {
echo "error: $*" >&2
}
function die() {
eerror "$*"
[[ -v GENTOO_INSTALL_REPO_SCRIPT_PID && $$ -ne $GENTOO_INSTALL_REPO_SCRIPT_PID ]] \
&& kill "$GENTOO_INSTALL_REPO_SCRIPT_PID"
exit 1
}
# Prints an error with file:line info of the nth "stack frame".
# 0 is this function, 1 the calling function, 2 its parent, and so on.
function die_trace() {
local idx="${1:-0}"
shift
echo "${BASH_SOURCE[$((idx + 1))]}:${BASH_LINENO[$idx]}: error: ${FUNCNAME[$idx]}: $*" >&2
exit 1
}
function for_line_in() {
while IFS="" read -r line || [[ -n $line ]]; do
"$2" "$line"
done <"$1"
}
function flush_stdin() {
local empty_stdin
# Unused variable is intentional.
# shellcheck disable=SC2034
while read -r -t 0.01 empty_stdin; do true; done
}
function ask() {
local response
while true; do
flush_stdin
read -r -p "$* (Y/n) " response \
|| die "Error in read"
case "${response,,}" in
'') return 0 ;;
y|yes) return 0 ;;
n|no) return 1 ;;
*) continue ;;
esac
done
}
function try() {
local response
local cmd_status
local prompt_parens="(Shell/retry/abort/continue/print)"
# Outer loop, allows us to retry the command
while true; do
# Try command
"$@"
cmd_status="$?"
if [[ $cmd_status != 0 ]]; then
echo " * Command failed: \$ $*"
echo "Last command failed with exit code $cmd_status"
# Prompt until input is valid
while true; do
echo -n "Specify next action $prompt_parens "
flush_stdin
read -r response \
|| die "Error in read"
case "${response,,}" in
''|s|shell)
echo "You will be prompted for action again after exiting this shell."
/bin/bash --init-file <(echo "init_bash")
;;
r|retry) continue 2 ;;
a|abort) die "Installation aborted" ;;
c|continue) return 0 ;;
p|print) echo "\$ $*" ;;
*) ;;
esac
done
fi
return
done
}
function countdown() {
echo -n "$1" >&2
local i="$2"
while [[ $i -gt 0 ]]; do
echo -n "$i " >&2
i=$((i - 1))
sleep 1
done
echo >&2
}
function download_stdout() {
wget --quiet --https-only --secure-protocol=PFS -O - -- "$1"
}
function download() {
wget --quiet --https-only --secure-protocol=PFS --show-progress -O "$2" -- "$1"
}
function get_blkid_field_by_device() {
local blkid_field="$1"
local device="$2"
blkid -g -c /dev/null \
|| die "Error while executing blkid"
partprobe &>/dev/null
local val
val="$(blkid -c /dev/null -o export "$device")" \
|| die "Error while executing blkid '$device'"
val="$(grep -- "^$blkid_field=" <<< "$val")" \
|| die "Could not find $blkid_field=... in blkid output"
val="${val#"$blkid_field="}"
echo -n "$val"
}
function get_blkid_uuid_for_id() {
local dev
dev="$(resolve_device_by_id "$1")" \
|| die "Could not resolve device with id=$dev"
local uuid
uuid="$(get_blkid_field_by_device 'UUID' "$dev")" \
|| die "Could not get UUID from blkid for device=$dev"
echo -n "$uuid"
}
function get_device_by_blkid_field() {
local blkid_field="$1"
local field_value="$2"
blkid -g -c /dev/null \
|| die "Error while executing blkid"
type partprobe &>/dev/null && partprobe &>/dev/null
local dev
dev="$(blkid -c /dev/null -o export -t "$blkid_field=$field_value")" \
|| die "Error while executing blkid to find $blkid_field=$field_value"
dev="$(grep DEVNAME <<< "$dev")" \
|| die "Could not find DEVNAME=... in blkid output"
dev="${dev#"DEVNAME="}"
echo -n "$dev"
}
function get_device_by_partuuid() {
if [[ -e "/dev/disk/by-partuuid/$1" ]]; then
echo -n "/dev/disk/by-partuuid/$1"
else
get_device_by_blkid_field 'PARTUUID' "$1"
fi
}
function get_device_by_uuid() {
if [[ -e "/dev/disk/by-uuid/$1" ]]; then
echo -n "/dev/disk/by-uuid/$1"
else
get_device_by_blkid_field 'UUID' "$1"
fi
}
function cache_lsblk_output() {
CACHED_LSBLK_OUTPUT="$(lsblk --all --path --pairs --output NAME,PTUUID,PARTUUID)" \
|| die "Error while executing lsblk to cache output"
}
function get_device_by_ptuuid() {
local ptuuid="${1,,}"
local dev
if [[ -v CACHED_LSBLK_OUTPUT && -n "$CACHED_LSBLK_OUTPUT" ]]; then
dev="$CACHED_LSBLK_OUTPUT"
else
dev="$(lsblk --all --path --pairs --output NAME,PTUUID,PARTUUID)" \
|| die "Error while executing lsblk to find PTUUID=$ptuuid"
fi
dev="$(grep "ptuuid=\"$ptuuid\" partuuid=\"\"" <<< "${dev,,}")" \
|| die "Could not find PTUUID=... in lsblk output"
dev="${dev%'" ptuuid='*}"
dev="${dev#'name="'}"
echo -n "$dev"
}
function uuid_to_mduuid() {
local mduuid="${1,,}"
mduuid="${mduuid//-/}"
mduuid="${mduuid:0:8}:${mduuid:8:8}:${mduuid:16:8}:${mduuid:24:8}"
echo -n "$mduuid"
}
function get_device_by_mdadm_uuid() {
local mduuid
mduuid="$(uuid_to_mduuid "$1")" \
|| die "Could not resolve mduuid from uuid=$1"
local dev
dev="$(mdadm --examine --scan)" \
|| die "Error while executing mdadm to find array with UUID=$mduuid"
dev="$(grep "uuid=$mduuid" <<< "${dev,,}")" \
|| die "Could not find UUID=... in mdadm output"
dev="${dev%'metadata='*}"
dev="${dev#'array'}"
dev="${dev#"${dev%%[![:space:]]*}"}"
dev="${dev%"${dev##*[![:space:]]}"}"
echo -n "$dev"
}
function get_device_by_luks_name() {
echo -n "/dev/mapper/$1"
}
function create_resolve_entry() {
local id="$1"
local type="$2"
local arg="${3,,}"
DISK_ID_TO_RESOLVABLE[$id]="$type:$arg"
}
function create_resolve_entry_device() {
local id="$1"
local dev="$2"
DISK_ID_TO_RESOLVABLE[$id]="device:$dev"
}
# Returns the basename of the device, if its path starts with /dev/disk/by-id/
function shorten_device() {
echo -n "${1#/dev/disk/by-id/}"
}
# Return matching device from /dev/disk/by-id/ if possible,
# otherwise return the parameter unchanged.
function canonicalize_device() {
given_dev="$(realpath "$1")"
for dev in /dev/disk/by-id/*; do
if [[ "$(realpath "$dev")" == "$given_dev" ]]; then
echo -n "$dev"
return 0
fi
done
echo -n "$1"
}
function resolve_device_by_id() {
local id="$1"
[[ -v DISK_ID_TO_RESOLVABLE[$id] ]] \
|| die "Cannot resolve id='$id' to a block device (no table entry)"
local type="${DISK_ID_TO_RESOLVABLE[$id]%%:*}"
local arg="${DISK_ID_TO_RESOLVABLE[$id]#*:}"
local dev
case "$type" in
'partuuid') dev=$(get_device_by_partuuid "$arg") ;;
'ptuuid') dev=$(get_device_by_ptuuid "$arg") ;;
'uuid') dev=$(get_device_by_uuid "$arg") ;;
'mdadm') dev=$(get_device_by_mdadm_uuid "$arg") ;;
'luks') dev=$(get_device_by_luks_name "$arg") ;;
'device') dev="$arg" ;;
*) die "Cannot resolve '$type:$arg' to device (unknown type)"
esac
canonicalize_device "$dev"
}
function load_or_generate_uuid() {
local uuid
local uuid_file="$UUID_STORAGE_DIR/$1"
if [[ -e $uuid_file ]]; then
uuid="$(cat "$uuid_file")"
else
uuid="$(uuidgen -r)"
mkdir -p "$UUID_STORAGE_DIR"
echo -n "$uuid" > "$uuid_file"
fi
echo -n "$uuid"
}
# Parses named arguments and stores them in the associative array `arguments`.
# If given, the associative array `known_arguments` must contain a list of arguments
# prefixed with + (mandatory) or ? (optional). "at least one of" can be expressed by +a|b|c.
function parse_arguments() {
local key
local value
local a
for a in "$@"; do
key="${a%%=*}"
value="${a#*=}"
if [[ $key == "$a" ]]; then
extra_arguments+=("$a")
continue
fi
arguments[$key]="$value"
done
declare -A allowed_keys
if [[ -v known_arguments ]]; then
local m
for m in "${known_arguments[@]}"; do
case "${m:0:1}" in
'+')
m="${m:1}"
local has_opt=false
local m_opt
# Splitting is intentional here
# shellcheck disable=SC2086
for m_opt in ${m//|/ }; do
allowed_keys[$m_opt]=true
if [[ -v arguments[$m_opt] ]]; then
has_opt=true
fi
done
[[ $has_opt == "true" ]] \
|| die_trace 2 "Missing mandatory argument $m=..."
;;
'?')
allowed_keys[${m:1}]=true
;;
*) die_trace 2 "Invalid start character in known_arguments, in argument '$m'" ;;
esac
done
for a in "${!arguments[@]}"; do
[[ -v allowed_keys[$a] ]] \
|| die_trace 2 "Unknown argument '$a'"
done
fi
}
# $1: program
# $2: checkfile
function has_program() {
local program="$1"
local checkfile="$2"
if [[ -z "$checkfile" ]]; then
type "$program" &>/dev/null \
|| return 1
elif [[ "${checkfile:0:1}" == "/" ]]; then
[[ -e "$checkfile" ]] \
|| return 1
else
type "$checkfile" &>/dev/null \
|| return 1
fi
return 0
}
function check_wanted_programs() {
local missing_required=()
local missing_wanted=()
local tuple
local program
local checkfile
for tuple in "$@"; do
program="${tuple%%=*}"
checkfile=""
[[ "$tuple" == *=* ]] \
&& checkfile="${tuple##*=}"
if ! has_program "${program#"?"}" "$checkfile"; then
if [[ "$program" == "?"* ]]; then
missing_wanted+=("${program#"?"}")
else
missing_required+=("$program")
fi
fi
done
[[ "${#missing_required[@]}" -eq 0 && "${#missing_wanted[@]}" -eq 0 ]] \
&& return
if [[ "${#missing_required[@]}" -gt 0 ]]; then
elog "The following programs are required for the installer to work, but are currently missing on your system:" >&2
elog " ${missing_required[*]}" >&2
fi
if [[ "${#missing_wanted[@]}" -gt 0 ]]; then
elog "Missing optional programs:" >&2
elog " ${missing_wanted[*]}" >&2
fi
if type pacman &>/dev/null; then
declare -A pacman_packages
pacman_packages=(
[ntpd]=ntp
[zfs]=""
)
elog "Detected pacman package manager."
if ask "Do you want to install all missing programs automatically?"; then
local packages
local need_zfs=false
for program in "${missing_required[@]}" "${missing_wanted[@]}"; do
[[ "$program" == "zfs" ]] \
&& need_zfs=true
if [[ -v "pacman_packages[$program]" ]]; then
# Assignments to the empty string are explicitly ignored,
# as for example, zfs needs to be handled separately.
[[ -n "${pacman_packages[$program]}" ]] \
&& packages+=("${pacman_packages[$program]}")
else
packages+=("$program")
fi
done
pacman -Sy "${packages[@]}"
if [[ "$need_zfs" == true ]]; then
elog "On an Arch live-stick you need the archzfs repository and some tools and modifications to use zfs."
elog "There is an automated installer available at https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init."
if ask "Do you want to automatically download and execute this zfs installation script?"; then
curl -s "https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init" | bash
fi
fi
return
fi
fi
if [[ "${#missing_required[@]}" -gt 0 ]]; then
die "Aborted installer because of missing required programs."
else
ask "Continue without recommended programs?"
fi
}
# exec function if defined
# $@ function name and arguments
function maybe_exec() {
type "$1" &>/dev/null && "$@"
}

19
tests/create-vm-gentoo.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
virt-install \
--connect=qemu:///system \
--name=vm-gentoo \
--vcpus=2 \
--memory=2048 \
--cdrom=/vm/images/archlinux-2021.05.01-x86_64.iso \
--disk path=/vm/disks/disk-vm-gentoo.disk,size=25 \
--boot uefi \
--os-variant=gentoo \
--noautoconsole
# --transient \
# --graphics none \
# --console pty,target.type=virtio \
# --serial pty \
# --extra-args 'console=ttyS0,115200n8 --- console=ttyS0,115200n8' \
# virsh

19
tests/shellcheck.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/bash
# Find the directory this script is stored in. (from: http://stackoverflow.com/questions/59895)
function get_source_dir() {
local source="${BASH_SOURCE[0]}"
while [[ -h $source ]]
do
local tmp
tmp="$(cd -P "$(dirname "${source}")" && pwd)"
source="$(readlink "${source}")"
[[ $source != /* ]] && source="${tmp}/${source}"
done
echo -n "$(realpath "$(dirname "${source}")")"
}
cd "$(get_source_dir)/.."
shellcheck -s bash --check-sourced --external-sources ./install
shellcheck -s bash --check-sourced --external-sources ./configure