You've already forked al-installer
Fix a number of issues and move global declarations
This commit is contained in:
@ -8,23 +8,15 @@
|
||||
# Some ideas and code has been taken from other installers
|
||||
# AIF, Cnichi, Calamares, The Arch Wiki.. Credit where credit is due
|
||||
|
||||
VER="1.8.28" # version
|
||||
VER="1.8.33" # version
|
||||
DIST="ArchLabs" # distributor
|
||||
MNT="/mnt" # mountpoint
|
||||
MNT="/mnt" # install mountpoint
|
||||
|
||||
# Bulk defaults
|
||||
# {
|
||||
|
||||
ERR="/tmp/errlog" # error log used internally
|
||||
DBG="/tmp/debuglog" # debug log when passed -d
|
||||
RUN="/run/archiso/bootmnt/arch/boot" # path for live /boot
|
||||
BT="$DIST Installer - v$VER" # backtitle used for dialogs
|
||||
VM="$(dmesg | grep -i "hypervisor")" # is the system a vm
|
||||
# bulk default values {
|
||||
|
||||
ROOT_PART="" # root partition
|
||||
BOOT_PART="" # boot partition
|
||||
BOOT_DEVICE="" # device used for BIOS grub install
|
||||
AUTO_BOOT_PART="" # filled with the boot partition from auto_partiton()
|
||||
BOOTLDR="" # bootloader selected
|
||||
EXTRA_MNT="" # holder for additional partitions while mounting
|
||||
EXTRA_MNTS="" # when an extra partition is mounted append it's info
|
||||
@ -44,15 +36,6 @@ UCODE="" # cpu manufacturer microcode filename (if any)
|
||||
HOOKS="shutdown" # list of additional HOOKS to add in /etc/mkinitcpio.conf
|
||||
FONT="ter-i16n" # font used in the linux console
|
||||
|
||||
# sane baseline
|
||||
BASE_PKGS="archlabs-scripts archlabs-skel-base archlabs-themes archlabs-dARK archlabs-icons archlabs-wallpapers "
|
||||
BASE_PKGS+="base-devel xorg xorg-drivers sudo git gtk3 gtk-engines gtk-engine-murrine pavucontrol tumbler "
|
||||
BASE_PKGS+="playerctl ffmpeg gstreamer libmad libmatroska gst-libav gst-plugins-base gst-plugins-good"
|
||||
|
||||
# sane extras for window managers
|
||||
WM_BASE_PKGS="arandr archlabs-networkmanager-dmenu xdg-user-dirs nitrogen polkit-gnome volumeicon xclip "
|
||||
WM_BASE_PKGS+="xdotool compton gnome-keyring dunst feh gsimplecal xfce4-power-manager xfce4-settings laptop-detect"
|
||||
|
||||
LUKS="" # empty when not using luks encryption
|
||||
LUKS_DEV="" # boot parameter string for LUKS
|
||||
LUKS_PART="" # partition used for encryption
|
||||
@ -70,6 +53,159 @@ AUTOLOGIN=false # enable autologin for xinit
|
||||
CONFIG_DONE=false # basic configuration is finished
|
||||
BROADCOM_WL=false # fixes for broadcom cards eg. BCM4352
|
||||
|
||||
# sane baseline
|
||||
BASE_PKGS="archlabs-scripts archlabs-skel-base archlabs-themes archlabs-dARK archlabs-icons archlabs-wallpapers "
|
||||
BASE_PKGS+="base-devel xorg xorg-drivers sudo git gtk3 gtk-engines gtk-engine-murrine pavucontrol tumbler "
|
||||
BASE_PKGS+="playerctl ffmpeg gstreamer libmad libmatroska gst-libav gst-plugins-base gst-plugins-good"
|
||||
|
||||
# sane extras for window managers
|
||||
WM_BASE_PKGS="arandr archlabs-networkmanager-dmenu xdg-user-dirs nitrogen polkit-gnome volumeicon xclip exo "
|
||||
WM_BASE_PKGS+="xdotool compton gnome-keyring dunst feh gsimplecal xfce4-power-manager xfce4-settings laptop-detect"
|
||||
|
||||
ERR="/tmp/errlog" # error log used internally
|
||||
DBG="/tmp/debuglog" # debug log when passed -d
|
||||
RUN="/run/archiso/bootmnt/arch/boot" # path for live /boot
|
||||
BT="$DIST Installer - v$VER" # backtitle used for dialogs
|
||||
VM="$(dmesg | grep -i "hypervisor")" # is the system a vm
|
||||
|
||||
AUTO_ROOT_PART="" # values from auto partition
|
||||
AUTO_BOOT_PART=""
|
||||
|
||||
# }
|
||||
|
||||
# giant ugly container :P {
|
||||
|
||||
# amount of RAM in the system in Mb
|
||||
SYS_MEM="$(awk '/MemTotal/ {
|
||||
print int($2 / 1024)"M"
|
||||
}' /proc/meminfo)"
|
||||
|
||||
# parsed string of locales from /etc/locale.gen
|
||||
LOCALES="$(awk '/\.UTF-8/ {
|
||||
gsub(/# .*|#/, "")
|
||||
if ($1) {
|
||||
print $1 " -"
|
||||
}
|
||||
}' /etc/locale.gen)"
|
||||
|
||||
# parsed string of linux console keyboard mappings
|
||||
CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{
|
||||
gsub(/\.map\.gz|.*\//, "")
|
||||
print $1 " -"
|
||||
}' | sort)"
|
||||
|
||||
# make sure these are defined for some dialog size calculation
|
||||
[[ $LINES ]] || LINES=$(tput lines)
|
||||
[[ $COLUMNS ]] || COLUMNS=$(tput cols)
|
||||
|
||||
# various associative arrays
|
||||
# {
|
||||
|
||||
# command used to install each bootloader
|
||||
declare -A BCMDS=(
|
||||
[grub]="grub-install --recheck --force"
|
||||
[syslinux]="syslinux-install_update -iam"
|
||||
[systemd-boot]="bootctl --path=/boot install"
|
||||
)
|
||||
|
||||
# boot partition mount points for each bootloader
|
||||
declare -A BMNTS=(
|
||||
[BIOS-grub]="/boot"
|
||||
[UEFI-grub]="/boot/efi"
|
||||
[BIOS-syslinux]="/boot"
|
||||
[UEFI-systemd-boot]="/boot"
|
||||
)
|
||||
|
||||
# bootloader options with respective boot partition mountpoint
|
||||
declare -A BOOTLDRS=(
|
||||
[BIOS]="grub ${BMNTS[BIOS-grub]} syslinux ${BMNTS[BIOS-syslinux]}"
|
||||
[UEFI]="systemd-boot ${BMNTS[UEFI-systemd-boot]} grub ${BMNTS[UEFI-grub]}"
|
||||
)
|
||||
|
||||
# match the wm name with the actual session name used for xinit
|
||||
declare -A WM_SESSIONS=(
|
||||
[dwm]='dwm'
|
||||
[i3-gaps]='i3'
|
||||
[bspwm]='bspwm'
|
||||
[xfce4]='startxfce4'
|
||||
[plasma]='startkde'
|
||||
[gnome]='gnome-session'
|
||||
[openbox]='openbox-session'
|
||||
[cinnamon]='cinnamon-session'
|
||||
)
|
||||
|
||||
# additional packages installed for each wm/de
|
||||
declare -A WM_EXT=(
|
||||
[gnome]=""
|
||||
[plasma]="kdebase-meta"
|
||||
[bspwm]="sxhkd archlabs-skel-bspwm rofi archlabs-polybar"
|
||||
[i3-gaps]="i3status perl-anyevent-i3 archlabs-skel-i3-gaps rofi archlabs-polybar"
|
||||
[openbox]="obconf archlabs-skel-openbox jgmenu archlabs-polybar tint2 conky rofi lxmenu-data"
|
||||
[xfce4]="xfce4-goodies xfce4-pulseaudio-plugin network-manager-applet volumeicon rofi archlabs-skel-xfce4"
|
||||
)
|
||||
|
||||
# files the user can edit during the final stage of install
|
||||
declare -A EDIT_FILES=(
|
||||
[login]=""
|
||||
[fstab]="/etc/fstab"
|
||||
[sudoers]="/etc/sudoers"
|
||||
[crypttab]="/etc/crypttab"
|
||||
[pacman]="/etc/pacman.conf"
|
||||
[console]="/etc/vconsole.conf"
|
||||
[mkinitcpio]="/etc/mkinitcpio.conf"
|
||||
[hostname]="/etc/hostname /etc/hosts"
|
||||
[bootloader]="/boot/loader/entries/$DIST.conf"
|
||||
[locale]="/etc/locale.conf /etc/default/locale"
|
||||
[keyboard]="/etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard"
|
||||
)
|
||||
|
||||
# PKG_EXT: if you add a package to $PACKAGES in any dialog
|
||||
# and it uses/requires some additional packages,
|
||||
# you can add them here to keep it simple: [package]="extra"
|
||||
# duplicates are removed with `uniq` before install
|
||||
declare -A PKG_EXT=(
|
||||
[vlc]="qt4"
|
||||
[mpd]="mpc"
|
||||
[mupdf]="mupdf-tools"
|
||||
[qutebrowser]="qt5ct qt5-styleplugins"
|
||||
[qt5ct]="qt5-styleplugins"
|
||||
[vlc]="qt5ct qt5-styleplugins"
|
||||
[zathura]="zathura-pdf-poppler"
|
||||
[noto-fonts]="noto-fonts-emoji"
|
||||
[cairo-dock]="cairo-dock-plug-ins"
|
||||
[qbittorrent]="qt5ct qt5-styleplugins"
|
||||
[kdenlive]="kdebase-meta dvdauthor frei0r-plugins breeze breeze-gtk qt5ct qt5-styleplugins"
|
||||
)
|
||||
|
||||
# mkfs command to format a partition as a given file system
|
||||
declare -A FS_CMDS=(
|
||||
[ext2]="mkfs.ext2 -q"
|
||||
[ext3]="mkfs.ext3 -q"
|
||||
[ext4]="mkfs.ext4 -q"
|
||||
[f2fs]="mkfs.f2fs"
|
||||
[jfs]="mkfs.jfs -q"
|
||||
[xfs]="mkfs.xfs -f"
|
||||
[vfat]="mkfs.vfat -F32"
|
||||
[nilfs2]="mkfs.nilfs2 -q"
|
||||
[ntfs]="mkfs.ntfs -q"
|
||||
[reiserfs]="mkfs.reiserfs -q"
|
||||
)
|
||||
|
||||
# mount options for a given file system
|
||||
declare -A FS_OPTS=(
|
||||
[vfat]=""
|
||||
[ntfs]=""
|
||||
[ext2]=""
|
||||
[ext3]=""
|
||||
[jfs]="discard - off errors=continue - off errors=panic - off nointegrity - off"
|
||||
[reiserfs]="acl - off nolog - off notail - off replayonly - off user_xattr - off"
|
||||
[ext4]="discard - off dealloc - off nofail - off noacl - off relatime - off noatime - off nobarrier - off nodelalloc - off"
|
||||
[xfs]="discard - off filestreams - off ikeep - off largeio - off noalign - off nobarrier - off norecovery - off noquota - off wsync - off"
|
||||
[nilfs2]="discard - off nobarrier - off errors=continue - off errors=panic - off order=relaxed - off order=strict - off norecovery - off"
|
||||
[f2fs]="data_flush - off disable_roll_forward - off disable_ext_identify - off discard - off fastboot - off flush_merge - off inline_xattr - off inline_data - off inline_dentry - off no_heap - off noacl - off nobarrier - off noextent_cache - off noinline_data - off norecovery - off"
|
||||
)
|
||||
# }
|
||||
|
||||
# }
|
||||
|
||||
main()
|
||||
@ -466,7 +602,8 @@ select_mirrorcmd()
|
||||
'country': Server location;
|
||||
'score': MirrorStatus score;
|
||||
'delay': MirrorStatus delay.\n" 0 0 "$MIRROR_CMD")"
|
||||
else
|
||||
elif hash rankmirrors >/dev/null 2>&1; then
|
||||
infobox "$_MirrorTitle" "\nQuerying mirrors near your location\n"
|
||||
c="$(json 'country_code' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_code")"
|
||||
local w="https://www.archlinux.org/mirrorlist"
|
||||
if [[ $c ]]; then
|
||||
@ -479,6 +616,7 @@ select_mirrorcmd()
|
||||
MIRROR_CMD="curl -s '$w/?country=US&country=CA&country=NZ&country=GB&country=AU&use_mirror_status=on'"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -663,7 +801,7 @@ select_extra()
|
||||
format()
|
||||
{
|
||||
infobox "$_FSTitle" "\nRunning: ${FS_CMDS[$2]} $1\n" 0
|
||||
${FS_CMDS[$2]} $1 >/dev/null 2>$ERR
|
||||
${FS_CMDS[$2]} "$1" >/dev/null 2>$ERR
|
||||
errshow "${FS_CMDS[$2]} $1"
|
||||
}
|
||||
|
||||
@ -706,17 +844,37 @@ partition()
|
||||
if [[ $choice == "$_Done" || $choice == "" ]]; then
|
||||
return 0
|
||||
elif [[ $choice != "$_PartWipe" && $choice != "$_PartAuto" && $choice != "$_PartShowTree" ]]; then
|
||||
clear; tput cnorm; $choice $device
|
||||
clear; tput cnorm; $choice "$device"; partition "$device"
|
||||
elif [[ $choice == "$_PartShowTree" ]]; then
|
||||
msgbox "$_PrepShowDev" "\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT "$device")\n"
|
||||
partition $device
|
||||
partition "$device"
|
||||
elif [[ $choice == "$_PartWipe" ]]; then
|
||||
yesno "$_PartWipe" "$_PartBody1 $device $_PartWipeBody2" && wipe -Ifrev $device
|
||||
partition $device
|
||||
partition "$device"
|
||||
else
|
||||
# if auto_partition fails we need to empty the partition variables
|
||||
auto_partition $device || return 1
|
||||
local root_size msg ret table boot_fs
|
||||
root_size=$(lsblk -lno SIZE "$device" | awk 'NR == 1 {
|
||||
if ($1 ~ "G") {
|
||||
sub(/G/, ""); print ($1 * 1000 - 512) / 1000"G"
|
||||
} else {
|
||||
sub(/M/, ""); print ($1 - 512)"M"
|
||||
}
|
||||
}')
|
||||
|
||||
if [[ $SYS == 'BIOS' ]]; then
|
||||
msg="$(sed 's|vfat/fat32|ext4|' <<< "$_PartBody2")"; table="msdos"; boot_fs="ext4"
|
||||
else
|
||||
msg="$_PartBody2"; table="gpt"; boot_fs="fat32"
|
||||
fi
|
||||
|
||||
if yesno "$_PrepParts" "$_PartBody1 $device $msg ($size)$_PartBody3"; then
|
||||
auto_partition "$device" "$table" "$boot_fs" "$root_size" || return 1
|
||||
else
|
||||
partition "$device"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
decr_count()
|
||||
@ -852,38 +1010,17 @@ check_cryptlvm()
|
||||
|
||||
auto_partition()
|
||||
{
|
||||
local device="$1"
|
||||
local size
|
||||
size=$(lsblk -lno SIZE $device | awk 'NR == 1 {
|
||||
if ($1 ~ "G") {
|
||||
sub(/G/, ""); print ($1 * 1000 - 512) / 1000"G"
|
||||
} else {
|
||||
sub(/M/, ""); print ($1 - 512)"M"
|
||||
}
|
||||
}')
|
||||
local device="$1" table="$2" boot_fs="$3" size="$4"
|
||||
local dev_info="$(parted -s $device print)"
|
||||
|
||||
if [[ $SYS == 'BIOS' ]]; then
|
||||
local msg
|
||||
msg="$(sed 's|vfat/fat32|ext4|' <<< "$_PartBody2")"
|
||||
local table="msdos"
|
||||
local fs="ext4"
|
||||
else
|
||||
local msg="$_PartBody2"
|
||||
local table="gpt"
|
||||
local fs="fat32";
|
||||
fi
|
||||
|
||||
# confirm or bail
|
||||
yesno "$_PrepParts" "$_PartBody1 $device $msg ($size)$_PartBody3" || return 0
|
||||
infobox "$_PrepParts" "\nRemoving partitions on $device and setting table to $table\n" 2
|
||||
swapoff -a # in case the device was previously used for swap
|
||||
|
||||
local dev_info
|
||||
dev_info="$(parted -s $device print)"
|
||||
# in case the device was previously used for swap
|
||||
swapoff -a
|
||||
|
||||
# walk the partitions on the device in reverse order and delete them
|
||||
while read -r i; do
|
||||
parted -s $device rm $i >/dev/null 2>&1
|
||||
while read -r PART; do
|
||||
parted -s $device rm $PART >/dev/null 2>&1
|
||||
done <<< "$(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r)"
|
||||
|
||||
if [[ $(awk '/Table:/ {print $3}' <<< "$dev_info") != "$table" ]]; then
|
||||
@ -897,7 +1034,7 @@ auto_partition()
|
||||
parted -s $device mkpart ESP $fs 1MiB 513MiB >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
sleep 0.1
|
||||
sleep 0.5
|
||||
BOOT_DEVICE="$device"
|
||||
AUTO_BOOT_PART=$(lsblk -lno NAME,TYPE $device | awk 'NR == 2 {print "/dev/"$1}')
|
||||
|
||||
@ -909,10 +1046,10 @@ auto_partition()
|
||||
|
||||
infobox "$_PrepParts" "\nCreating a $size ext4 root partition.\n" 0
|
||||
parted -s $device mkpart primary ext4 513MiB 100% >/dev/null 2>&1
|
||||
sleep 0.1
|
||||
local rp
|
||||
rp="$(lsblk -lno NAME,TYPE $device | awk 'NR == 3 {print "/dev/"$1}')"
|
||||
mkfs.ext4 -q $rp >/dev/null 2>&1
|
||||
|
||||
sleep 0.5
|
||||
AUTO_ROOT_PART="$(lsblk -lno NAME,TYPE $device | awk 'NR == 3 {print "/dev/"$1}')"
|
||||
mkfs.ext4 -q $AUTO_ROOT_PART >/dev/null 2>&1
|
||||
|
||||
tput civis
|
||||
sleep 0.5
|
||||
@ -929,10 +1066,8 @@ mount_partition()
|
||||
|
||||
if [[ $fs && ${FS_OPTS[$fs]} && $part != "$BOOT_PART" ]] && select_mount_opts "$part" "$fs"; then
|
||||
mount -o $MNT_OPTS "$part" "$mountp" 2>$ERR
|
||||
errshow "mount -o $MNT_OPTS $part $mountp"
|
||||
else
|
||||
mount "$part" "$mountp" 2>$ERR
|
||||
errshow "mount $part $mountp"
|
||||
fi
|
||||
|
||||
confirm_mount $part "$mountp" || return 1
|
||||
@ -964,20 +1099,21 @@ find_partitions()
|
||||
fi
|
||||
|
||||
# number of partitions total
|
||||
COUNT=$(wc -l <<< "$PARTS")
|
||||
if [[ $PARTS ]]; then
|
||||
COUNT=$(wc -l <<< "$PARTS")
|
||||
else
|
||||
COUNT=0
|
||||
fi
|
||||
|
||||
# ensure we have enough partitions for the system and action type
|
||||
case $str in
|
||||
'part|lvm|crypt') [[ $COUNT -eq 0 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_PartErrBody" ;;
|
||||
'part|crypt') (( COUNT == 0 )) && err="$_LvmPartErrBody" ;;
|
||||
case "$str" in
|
||||
'part|lvm|crypt') [[ $COUNT -lt 1 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_PartErrBody" ;;
|
||||
'part|crypt') (( COUNT < 1 )) && err="$_LvmPartErrBody" ;;
|
||||
'part|lvm') (( COUNT < 2 )) && err="$_LuksPartErrBody" ;;
|
||||
esac
|
||||
|
||||
# if there aren't enough partitions show the error message
|
||||
if [[ $err ]]; then
|
||||
msgbox "$_ErrTitle" "$err"
|
||||
return 1
|
||||
fi
|
||||
# if there aren't enough partitions show the relevant error message
|
||||
[[ $err ]] && { msgbox "$_ErrTitle" "$err"; return 1; }
|
||||
|
||||
return 0
|
||||
}
|
||||
@ -1011,7 +1147,7 @@ mnt_menu()
|
||||
# prepare partition list PARTS for dialog
|
||||
lvm_detect
|
||||
umount_dir $MNT
|
||||
find_partitions 'part|lvm|crypt' || return 1
|
||||
find_partitions 'part|lvm|crypt' || { SELECTED=2; return 1; }
|
||||
select_root_partition || return 1
|
||||
|
||||
if [[ $SYS == "UEFI" ]]; then
|
||||
@ -1100,7 +1236,7 @@ select_filesystem()
|
||||
{
|
||||
local part="$1"
|
||||
local fs cur_fs
|
||||
cur_fs="$(lsblk -lno FSTYPE $part 2>/dev/null)"
|
||||
cur_fs="$(lsblk -lno FSTYPE "$part" 2>/dev/null)"
|
||||
local msg="\nSelect which filesystem you want to use for $part\n\nPartition Name: "
|
||||
|
||||
tput civis
|
||||
@ -1135,9 +1271,9 @@ select_filesystem()
|
||||
[[ $fs ]] || return 1
|
||||
|
||||
if yesno "$_FSTitle" "\nFormat $part as $fs?\n" "Format" "Go Back"; then
|
||||
format $part $fs
|
||||
format "$part" "$fs"
|
||||
else
|
||||
select_filesystem $part || return 1
|
||||
select_filesystem "$part" || return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
@ -1151,11 +1287,13 @@ select_efi_partition()
|
||||
infobox "$_PrepMount" "$_OnlyOne for EFI: $BOOT_PART\n" 1
|
||||
elif ! BOOT_PART="$(menubox "$_PrepMount" "$_SelUefiBody" $PARTS)"; then
|
||||
return 1
|
||||
elif [[ $BOOT_PART == "$AUTO_BOOT_PART" ]]; then
|
||||
return 0 # were done here
|
||||
fi
|
||||
|
||||
if grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"; then
|
||||
local msg="$_FormUefiBody $BOOT_PART $_FormUefiBody2"
|
||||
if [[ $AUTO_BOOT_PART != "$BOOT_PART" ]] && yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Do Not Format" "no"; then
|
||||
if yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Do Not Format" "no"; then
|
||||
format "$BOOT_PART" "vfat"
|
||||
sleep 1
|
||||
fi
|
||||
@ -1163,6 +1301,7 @@ select_efi_partition()
|
||||
format "$BOOT_PART" "vfat"
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -1171,17 +1310,19 @@ select_boot_partition()
|
||||
tput civis
|
||||
if ! BOOT_PART="$(menubox "$_PrepMount" "$_SelBiosBody" "$_Skip" "-" $PARTS)" || [[ $BOOT_PART == "$_Skip" ]]; then
|
||||
BOOT_PART=""
|
||||
else
|
||||
if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then
|
||||
local msg="$_FormBiosBody $BOOT_PART $_FormBiosBody2"
|
||||
if [[ $AUTO_BOOT_PART != "$BOOT_PART" ]] && yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Skip Formatting" "no"; then
|
||||
format "$BOOT_PART" "ext4"
|
||||
sleep 1
|
||||
fi
|
||||
else
|
||||
elif [[ $AUTO_BOOT_PART == "$BOOT_PART" ]]; then
|
||||
return 0 # were done here
|
||||
fi
|
||||
|
||||
if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then
|
||||
local msg="$_FormBiosBody $BOOT_PART $_FormBiosBody2"
|
||||
if yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Skip Formatting" "no"; then
|
||||
format "$BOOT_PART" "ext4"
|
||||
sleep 1
|
||||
fi
|
||||
else
|
||||
format "$BOOT_PART" "ext4"
|
||||
sleep 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@ -1189,7 +1330,7 @@ select_boot_partition()
|
||||
select_root_partition()
|
||||
{
|
||||
tput civis
|
||||
if [[ $COUNT -eq 1 ]]; then
|
||||
if (( COUNT == 1 )); then
|
||||
ROOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
|
||||
infobox "$_PrepMount" "$_OnlyOne for root (/): $ROOT_PART\n" 1
|
||||
elif ! ROOT_PART="$(menubox "$_PrepMount" "$_SelRootBody" $PARTS)"; then
|
||||
@ -1349,9 +1490,11 @@ create_user()
|
||||
{
|
||||
printf "Creating user $NEWUSER, setting passwords, and setting shell\n"
|
||||
|
||||
chrun "chpasswd <<< 'root:$ROOT_PASS'"
|
||||
chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR
|
||||
errshow 1 "Setting root password"
|
||||
if [[ $MYSHELL != *zsh ]]; then
|
||||
chrun "usermod -s $MYSHELL root"
|
||||
chrun "usermod -s $MYSHELL root" 2>$ERR
|
||||
errshow 1 "Setting root shell"
|
||||
if [[ $MYSHELL == "/usr/bin/mksh" ]]; then
|
||||
cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc
|
||||
fi
|
||||
@ -1514,21 +1657,19 @@ mirrorlist_sort()
|
||||
|
||||
package_operations()
|
||||
{
|
||||
local rmpkg="archlabs-installer"
|
||||
|
||||
if [[ $inpkg =~ (openbox|bspwm|i3) || $INSTALL_WMS == *dwm* ]]; then
|
||||
if [[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|dwm) ]]; then
|
||||
local inpkg="$PACKAGES $BASE_PKGS $WM_BASE_PKGS"
|
||||
else
|
||||
local inpkg="$PACKAGES $BASE_PKGS"
|
||||
fi
|
||||
|
||||
if ! [[ $INSTALL_WMS == 'plasma' || $INSTALL_WMS == 'gnome' || $INSTALL_WMS == 'cinnamon' ]]; then
|
||||
inpkg+=" archlabs-ksuperkey"
|
||||
fi
|
||||
[[ $INSTALL_WMS =~ ^(plasma|gnome|cinnamon)$ ]] || inpkg+=" archlabs-ksuperkey"
|
||||
|
||||
if [[ $KERNEL == 'linux-lts' ]]; then
|
||||
inpkg+=" linux-lts"
|
||||
rmpkg+=" linux"
|
||||
local rmpkg="archlabs-installer linux"
|
||||
else
|
||||
local rmpkg="archlabs-installer"
|
||||
fi
|
||||
|
||||
[[ $BOOTLDR == 'grub' ]] && inpkg+=" grub"
|
||||
@ -1549,15 +1690,13 @@ suckless_install()
|
||||
mkdir -pv $MNT/home/$NEWUSER/suckless
|
||||
|
||||
for i in dwm dmenu st; do
|
||||
p="/home/$NEWUSER/suckless/$i"
|
||||
chrun "git clone https://bitbucket.org/natemaia/$i $p"
|
||||
e=$?
|
||||
if (( e == 0 )); then
|
||||
chrun "cd $p; rm -f config.h; make clean install; make clean"
|
||||
if chrun "git clone https://bitbucket.org/natemaia/$i /home/$NEWUSER/suckless/$i"; then
|
||||
chrun "cd /home/$NEWUSER/suckless/$i; rm -f config.h; make clean install; make clean"
|
||||
else
|
||||
printf "Failed to clone $i repo\n"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -d /home/$NEWUSER/suckless/dwm ]]; then
|
||||
printf "To configure dwm edit /home/$NEWUSER/suckless/dwm/config.h\n"
|
||||
printf "You can then recompile it with 'sudo make clean install'\n"
|
||||
@ -1585,6 +1724,8 @@ setup_boot()
|
||||
|
||||
setup_grub()
|
||||
{
|
||||
local efidir="/sys/firmware/efi"
|
||||
|
||||
# grub has by far the worst setup of the three however
|
||||
# the configuration is shorter due to grub-mkconfig
|
||||
EDIT_FILES[bootloader]="/etc/default/grub"
|
||||
@ -1595,9 +1736,13 @@ setup_grub()
|
||||
if [[ $ROOT_PART == */dev/mapper/* && ! $LVM && ! $LUKS_PASS ]]; then
|
||||
luks_pass "$_LuksOpen" "" || return 1
|
||||
fi
|
||||
BCMDS[grub]="${BCMDS[grub]} --bootloader-id=$DIST"
|
||||
BCMDS[grub]="mount -t efivarfs efivarfs $efidir/efivars; \
|
||||
${BCMDS[grub]} --bootloader-id=$DIST"
|
||||
fi
|
||||
|
||||
grep -q $efidir/efivars <<< "$(mount)" ||
|
||||
mount -t efivarfs efivarfs $efidir/efivars
|
||||
|
||||
BCMDS[grub]="mkdir -p /run/udev && mkdir -p /run/lvm &&
|
||||
mount --bind /hostrun/udev /run/udev &&
|
||||
mount --bind /hostrun/lvm /run/lvm &&
|
||||
@ -2047,7 +2192,7 @@ luks_menu()
|
||||
tput civis
|
||||
local choice
|
||||
choice="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_PrepLUKS " \
|
||||
--menu "${_LuksMenuBody}${_LuksMenuBody2}${_LuksMenuBody3}" 0 0 6 \
|
||||
--menu "${_LuksMenuBody}${_LuksMenuBody2}${_LuksMenuBody3}" 0 0 0 \
|
||||
"$_LuksEncrypt" "cryptsetup -q luksFormat" \
|
||||
"$_LuksOpen" "cryptsetup open --type luks" \
|
||||
"$_LuksEncryptAdv" "cryptsetup -q -s -c luksFormat" \
|
||||
@ -2217,14 +2362,12 @@ chrun()
|
||||
}
|
||||
|
||||
json()
|
||||
{
|
||||
# extract a value from http://api.ipstack.com json output
|
||||
{ # extract a value from http://api.ipstack.com json output
|
||||
curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])"
|
||||
}
|
||||
|
||||
src()
|
||||
{
|
||||
# source file ($1), if it fails we die with an error message
|
||||
{ # source file ($1), if it fails we die with an error message
|
||||
if ! . "$1" 2>/dev/null; then
|
||||
printf "Failed to source file %s\n" "$1"
|
||||
die 1
|
||||
@ -2233,8 +2376,7 @@ src()
|
||||
}
|
||||
|
||||
ssd()
|
||||
{
|
||||
# returns 0 (true) when the device passed ($1) is NOT a rotational device
|
||||
{ # returns 0 (true) when the device passed ($1) is NOT a rotational device
|
||||
local i dev=$1
|
||||
|
||||
# check for LVM and or LUKS for their origin devices
|
||||
@ -2252,47 +2394,51 @@ ssd()
|
||||
|
||||
die()
|
||||
{
|
||||
if (( $# >= 1 )); then
|
||||
local exitcode=$1
|
||||
else
|
||||
local exitcode=0
|
||||
fi
|
||||
(( $# >= 1 )) && local exitcode=$1 || local exitcode=0
|
||||
|
||||
# reset SIGINT
|
||||
# reset SIGINT and terminal
|
||||
trap - INT
|
||||
|
||||
tput cnorm
|
||||
|
||||
if [[ -d $MNT ]] && command cd /; then
|
||||
umount_dir $MNT
|
||||
if (( exitcode == 127 )); then
|
||||
umount -l /run/archiso/bootmnt
|
||||
sleep 0.5
|
||||
systemctl -i reboot
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $TERM == 'linux' ]]; then
|
||||
# restore custom linux console 0-15 colors when not rebooting
|
||||
colors=("\e]P0191919" "\e]P1D15355" "\e]P2609960" "\e]P3FFCC66"
|
||||
"\e]P4255A9B" "\e]P5AF86C8" "\e]P62EC8D3" "\e]P7949494" "\e]P8191919" "\e]P9D15355"
|
||||
"\e]PA609960" "\e]PBFF9157" "\e]PC4E88CF" "\e]PDAF86C8" "\e]PE2ec8d3" "\e]PFE1E1E1"
|
||||
)
|
||||
for col in "${colors[@]}"; do
|
||||
printf "$col"
|
||||
done
|
||||
fi
|
||||
|
||||
exit $exitcode
|
||||
}
|
||||
|
||||
sigint()
|
||||
{
|
||||
# used to trap SIGINT and cleanly exit the program
|
||||
{ # used to trap SIGINT (^C) and cleanly exit
|
||||
printf "CTRL-C caught\nCleaning up...\n"
|
||||
die 1
|
||||
}
|
||||
|
||||
print4()
|
||||
{
|
||||
# takes an arbitrary number of input fields and prints them out in fourths on separate lines
|
||||
{ # takes an arbitrary number of input fields and prints them out in fourths on separate lines
|
||||
local str="$*"
|
||||
if [[ ${#str} -gt $((COLUMNS - 10)) ]]; then
|
||||
if [[ $COLUMNS -gt 110 && ${#str} -gt $((COLUMNS - 10)) ]]; then
|
||||
str="$(awk '{
|
||||
p1=p2=p3=p4=""
|
||||
p1=$1
|
||||
q=int(NF / 4)
|
||||
for (i=2; i<=q; i++) { p1=p1" "$i }
|
||||
for (i=q; i<=q*2; i++) { p2=p2" "$i }
|
||||
for (i=q*2; i<=q*3; i++) { p3=p3" "$i }
|
||||
for (i=q*3; i<=NF; i++) { p4=p4" "$i }
|
||||
i=2; p1=p2=p3=p4=""; p1=$1; q=int(NF / 4)
|
||||
for (;i<=q; i++) { p1=p1" "$i }
|
||||
for (;i<=q*2; i++) { p2=p2" "$i }
|
||||
for (;i<=q*3; i++) { p3=p3" "$i }
|
||||
for (;i<=NF; i++) { p4=p4" "$i }
|
||||
printf "%s\n %s\n %s\n %s", p1, p2, p3, p4
|
||||
}' <<< "$str")"
|
||||
printf "%s\n" "$str"
|
||||
@ -2314,6 +2460,7 @@ system_devices()
|
||||
awk '/disk/ {print "/dev/" $1 " " $2}')"
|
||||
fi
|
||||
|
||||
[[ $SYS_DEVS ]] || { infobox "$_ErrTitle" "\nNo available devices...$_Exit\n"; die 1; }
|
||||
DEV_COUNT="$(wc -l <<< "$SYS_DEVS")"
|
||||
}
|
||||
|
||||
@ -2338,8 +2485,7 @@ system_identify()
|
||||
|
||||
if [[ -d $efidir ]]; then
|
||||
SYS="UEFI"
|
||||
grep -q $efidir/efivars <<< "$(mount)" ||
|
||||
mount -t efivarfs efivarfs $efidir/efivars
|
||||
grep -q $efidir/efivars <<< "$(mount)" || mount -t efivarfs efivarfs $efidir/efivars
|
||||
else
|
||||
SYS="BIOS"
|
||||
fi
|
||||
@ -2349,7 +2495,7 @@ system_identify()
|
||||
|
||||
load_bcm()
|
||||
{
|
||||
infobox "Broadcom Wireless Setup" "\nLoading wifi kernel modules please wait...\n" 1
|
||||
infobox "Broadcom Wireless Setup" "\nLoading broadcom wifi kernel modules please wait...\n" 0
|
||||
rmmod wl >/dev/null 2>&1
|
||||
rmmod bcma >/dev/null 2>&1
|
||||
rmmod b43 >/dev/null 2>&1
|
||||
@ -2359,9 +2505,14 @@ load_bcm()
|
||||
BROADCOM_WL=true
|
||||
}
|
||||
|
||||
chk_connect()
|
||||
{
|
||||
infobox "Network Connect" "\nVerifying connection to https://www.archlinux.org\n"
|
||||
curl -sI --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
|
||||
}
|
||||
|
||||
net_connect()
|
||||
{
|
||||
chk_connect() { curl -s --head 'https://www.archlinux.org/mirrorlist/all/' | sed '1q' | grep -qw '200'; }
|
||||
if ! chk_connect; then
|
||||
if [[ $(systemctl is-active NetworkManager) == "active" ]] && hash nmtui >/dev/null 2>&1; then
|
||||
tput civis
|
||||
@ -2400,7 +2551,7 @@ prechecks()
|
||||
{
|
||||
if [[ $1 -ge 0 ]] && ! [[ $(lsblk -lno MOUNTPOINT) =~ $MNT ]]; then
|
||||
msgbox "$_ErrTitle" "$_ErrNoMount"; SELECTED=4; return 1
|
||||
elif [[ $1 -ge 1 && ! $NEWUSER ]]; then
|
||||
elif [[ $1 -ge 1 && ($NEWUSER == "" || $USER_PASS == "") ]]; then
|
||||
msgbox "$_ErrTitle" "\nYou need to create a user first.\n"; SELECTED=5; return 1
|
||||
elif [[ $1 -ge 2 && $CONFIG_DONE != true ]]; then
|
||||
msgbox "$_ErrTitle" "$_ErrNoConfig"; SELECTED=6; return 1
|
||||
@ -2508,189 +2659,9 @@ yesno()
|
||||
fi
|
||||
}
|
||||
|
||||
select_language()
|
||||
{
|
||||
LNG="/usr/share/archlabs/installer/lang" # translation file path
|
||||
|
||||
tput civis
|
||||
local lang
|
||||
lang=$(menubox "Select Language" \
|
||||
"\nLanguage - sprache - taal - språk - lingua - idioma - nyelv - língua\n" \
|
||||
"1" "English (en_**)" \
|
||||
"2" "Español (es_ES)" \
|
||||
"3" "Português [Brasil] (pt_BR)" \
|
||||
"4" "Português (pt_PT)" \
|
||||
"5" "Français (fr_FR)" \
|
||||
"6" "Russkiy (ru_RU)" \
|
||||
"7" "Italiano (it_IT)" \
|
||||
"8" "Nederlands (nl_NL)" \
|
||||
"9" "Magyar (hu_HU)" \
|
||||
"10" "Chinese (zh_CN)")
|
||||
|
||||
src $LNG/english.trans
|
||||
FONT="ter-i16n"
|
||||
case $lang in
|
||||
1) LOC="en_US.UTF-8" ;;
|
||||
2) src $LNG/spanish.trans && LOC="es_ES.UTF-8" ;;
|
||||
3) src $LNG/p_brasil.trans && LOC="pt_BR.UTF-8" ;;
|
||||
4) src $LNG/portuguese.trans && LOC="pt_PT.UTF-8" ;;
|
||||
5) src $LNG/french.trans && LOC="fr_FR.UTF-8" ;;
|
||||
6) src $LNG/russian.trans && LOC="ru_RU.UTF-8" FONT="LatKaCyrHeb-16" ;;
|
||||
7) src $LNG/italian.trans && LOC="it_IT.UTF-8" ;;
|
||||
8) src $LNG/dutch.trans && LOC="nl_NL.UTF-8" ;;
|
||||
9) src $LNG/hungarian.trans && LOC="hu_HU.UTF-8" FONT="lat2-16" ;;
|
||||
10) src $LNG/chinese.trans && LOC="zh_CN.UTF-8" ;;
|
||||
*) die
|
||||
esac
|
||||
|
||||
sed -i "s/#en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen
|
||||
if [[ $LOC != "en_US.UTF-8" ]]; then
|
||||
sed -i "s/#${LOC}/${LOC}/" /etc/locale.gen
|
||||
locale-gen >/dev/null 2>&1
|
||||
fi
|
||||
[[ $TERM == 'linux' ]] && setfont $FONT >/dev/null 2>&1
|
||||
export LANG="$LOC"
|
||||
return 0
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# entry point
|
||||
|
||||
# giant ugly variable container :P {
|
||||
|
||||
# amount of RAM in the system in Mb
|
||||
SYS_MEM="$(awk '/MemTotal/ {
|
||||
print int($2 / 1024)"M"
|
||||
}' /proc/meminfo)"
|
||||
|
||||
# parsed string of locales from /etc/locale.gen
|
||||
LOCALES="$(awk '/\.UTF-8/ {
|
||||
gsub(/# .*|#/, "")
|
||||
if ($1) {
|
||||
print $1 " -"
|
||||
}
|
||||
}' /etc/locale.gen)"
|
||||
|
||||
# parsed string of linux console keyboard mappings
|
||||
CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{
|
||||
gsub(/\.map\.gz|.*\//, "")
|
||||
print $1 " -"
|
||||
}' | sort)"
|
||||
|
||||
# make sure these are defined for some dialog size calculation
|
||||
[[ $LINES ]] || LINES=$(tput lines)
|
||||
[[ $COLUMNS ]] || COLUMNS=$(tput cols)
|
||||
|
||||
# various associative arrays
|
||||
# {
|
||||
|
||||
# command used to install each bootloader
|
||||
declare -A BCMDS=(
|
||||
[grub]="grub-install --recheck --force"
|
||||
[syslinux]="syslinux-install_update -iam"
|
||||
[systemd-boot]="bootctl --path=/boot install"
|
||||
)
|
||||
|
||||
# boot partition mount points for each bootloader
|
||||
declare -A BMNTS=(
|
||||
[BIOS-grub]="/boot"
|
||||
[UEFI-grub]="/boot/efi"
|
||||
[BIOS-syslinux]="/boot"
|
||||
[UEFI-systemd-boot]="/boot"
|
||||
)
|
||||
|
||||
# bootloader options with respective boot partition mountpoint
|
||||
declare -A BOOTLDRS=(
|
||||
[BIOS]="grub ${BMNTS[BIOS-grub]} syslinux ${BMNTS[BIOS-syslinux]}"
|
||||
[UEFI]="systemd-boot ${BMNTS[UEFI-systemd-boot]} grub ${BMNTS[UEFI-grub]}"
|
||||
)
|
||||
|
||||
# match the wm name with the actual session name used for xinit
|
||||
declare -A WM_SESSIONS=(
|
||||
[dwm]='dwm'
|
||||
[i3-gaps]='i3'
|
||||
[bspwm]='bspwm'
|
||||
[xfce4]='startxfce4'
|
||||
[plasma]='startkde'
|
||||
[gnome]='gnome-session'
|
||||
[openbox]='openbox-session'
|
||||
[cinnamon]='cinnamon-session'
|
||||
)
|
||||
|
||||
# additional packages installed for each wm/de
|
||||
declare -A WM_EXT=(
|
||||
[gnome]=""
|
||||
[plasma]="kdebase-meta"
|
||||
[bspwm]="sxhkd archlabs-skel-bspwm rofi archlabs-polybar"
|
||||
[i3-gaps]="i3status perl-anyevent-i3 archlabs-skel-i3-gaps rofi archlabs-polybar"
|
||||
[openbox]="obconf archlabs-skel-openbox jgmenu archlabs-polybar tint2 conky rofi"
|
||||
[xfce4]="xfce4-goodies xfce4-pulseaudio-plugin network-manager-applet volumeicon rofi archlabs-skel-xfce4 xdg-user-dirs"
|
||||
)
|
||||
|
||||
# files the user can edit during the final stage of install
|
||||
declare -A EDIT_FILES=(
|
||||
[login]="" # login files.. Populated later once login method is chosen
|
||||
[fstab]="/etc/fstab"
|
||||
[sudoers]="/etc/sudoers"
|
||||
[crypttab]="/etc/crypttab"
|
||||
[pacman]="/etc/pacman.conf"
|
||||
[console]="/etc/vconsole.conf"
|
||||
[mkinitcpio]="/etc/mkinitcpio.conf"
|
||||
[hostname]="/etc/hostname /etc/hosts"
|
||||
[bootloader]="/boot/loader/entries/$DIST.conf"
|
||||
[locale]="/etc/locale.conf /etc/default/locale"
|
||||
[keyboard]="/etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard"
|
||||
)
|
||||
|
||||
# PKG_EXT: if you add a package to $PACKAGES in any dialog
|
||||
# and it uses/requires some additional packages,
|
||||
# you can add them here to keep it simple: [package]="extra"
|
||||
# duplicates are removed with `uniq` before install
|
||||
declare -A PKG_EXT=(
|
||||
[vlc]="qt4"
|
||||
[mpd]="mpc"
|
||||
[mupdf]="mupdf-tools"
|
||||
[qutebrowser]="qt5ct qt5-styleplugins"
|
||||
[qt5ct]="qt5-styleplugins"
|
||||
[vlc]="qt5ct qt5-styleplugins"
|
||||
[zathura]="zathura-pdf-poppler"
|
||||
[noto-fonts]="noto-fonts-emoji"
|
||||
[cairo-dock]="cairo-dock-plug-ins"
|
||||
[qbittorrent]="qt5ct qt5-styleplugins"
|
||||
[kdenlive]="kdebase-meta dvdauthor frei0r-plugins breeze breeze-gtk qt5ct qt5-styleplugins"
|
||||
)
|
||||
|
||||
# mkfs command to format a partition as a given file system
|
||||
declare -A FS_CMDS=(
|
||||
[ext2]="mkfs.ext2 -q"
|
||||
[ext3]="mkfs.ext3 -q"
|
||||
[ext4]="mkfs.ext4 -q"
|
||||
[f2fs]="mkfs.f2fs"
|
||||
[jfs]="mkfs.jfs -q"
|
||||
[xfs]="mkfs.xfs -f"
|
||||
[vfat]="mkfs.vfat -F32"
|
||||
[nilfs2]="mkfs.nilfs2 -q"
|
||||
[ntfs]="mkfs.ntfs -q"
|
||||
[reiserfs]="mkfs.reiserfs -q"
|
||||
)
|
||||
|
||||
# mount options for a given file system
|
||||
declare -A FS_OPTS=(
|
||||
[vfat]=""
|
||||
[ntfs]=""
|
||||
[ext2]=""
|
||||
[ext3]=""
|
||||
[jfs]="discard - off errors=continue - off errors=panic - off nointegrity - off"
|
||||
[reiserfs]="acl - off nolog - off notail - off replayonly - off user_xattr - off"
|
||||
[ext4]="discard - off dealloc - off nofail - off noacl - off relatime - off noatime - off nobarrier - off nodelalloc - off"
|
||||
[xfs]="discard - off filestreams - off ikeep - off largeio - off noalign - off nobarrier - off norecovery - off noquota - off wsync - off"
|
||||
[nilfs2]="discard - off nobarrier - off errors=continue - off errors=panic - off order=relaxed - off order=strict - off norecovery - off"
|
||||
[f2fs]="data_flush - off disable_roll_forward - off disable_ext_identify - off discard - off fastboot - off flush_merge - off inline_xattr - off inline_data - off inline_dentry - off no_heap - off noacl - off nobarrier - off noextent_cache - off noinline_data - off norecovery - off"
|
||||
)
|
||||
# }
|
||||
|
||||
# }
|
||||
|
||||
# trap Ctrl-C to properly exit
|
||||
trap sigint INT
|
||||
|
||||
@ -2699,10 +2670,7 @@ for arg in "$@"; do
|
||||
done
|
||||
|
||||
# initial prep
|
||||
|
||||
#select_language
|
||||
src /usr/share/archlabs/installer/lang/english.trans
|
||||
|
||||
select_keymap
|
||||
system_checks
|
||||
system_identify
|
||||
|
Reference in New Issue
Block a user