You are not logged in.

#1 2024-11-21 15:33:40

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

[SOLVED] Looking for help with a shell script I made to install Arch

Hey Folks,

I decided to make my own shell script to semi-automate the installation of Arch. The script works as expected as long as the disk chosen not an nvme drive. Initially when I wrote this I did not account for the fact that nvme drives show up as "/dev/nvme0n1" or a variation of that depending where on the motherboad it is installed. Once you partition that drive your partitions would show up as "/dev/nvme0n1p1" and so on...

I tried to create some logic in the script to append a "p" at the end of the disk name, before the partition number. If the selected disk is nvme but the script fails after I set the EFI and Boot partition sizes just before formatting partitions and setting up LVM. The script seems to assume partition 3 is "/dev/sda3" even though the disk selected was "/dev/nvme0n1" and all printed output shows the base disk is "/dev/nvme0n1"

At this point I cannot tell if the "get_partitions" function is the issue or if I missed calling $PART3 somewhere. (likely tunnel vision on my part from staring at it for so long.) Script is available on github

Full disclosure, I have zero professional training or experience in writing scripts/code I know enough to be dangerous. I did this as a tool for my self and as a learning exorcise. Any insight is highly appreciated.

Link to script: https://github.com/live4thamuzik/archl4tm

*OP Editied to fix typos and add detail about the issue*

Last edited by live4thamuzik (2024-11-23 18:02:07)

Offline

#2 2024-11-22 04:00:30

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

had a thought... does it make more sense to just have a conditional logic for each time I need the script to add the "p" between ${disk} and the partition number versus relying on external variables?

## Global Functions ##

# --- Logging Functions ---
log_output() {
    local message="$1"
    echo "$(date +"%Y-%m-%d %H:%M:%S") - $message"
}

log_error() {
    local message="$1"
    local err_code="$2"
    echo "$(date +"%Y-%m-%d %H:%M:%S") - Error: $message (exit code: $err_code)" >&2
}

log_debug() {
    local message="$1"
    echo "$(date +"%Y-%m-%d %H:%M:%S") - Debug: $message" >&2
}

log_info() {
    local message="$1"
    echo "$(date +"%Y-%m-%d %H:%M:%S") - Info: $message"
}

log_warning() {
    local message="$1"
    echo "$(date +"%Y-%m-%d %H:%M:%S") - Warning: $message" >&2
}

# --- Input Validation Functions ---
validate_username() {
    local username="$1"
    if [[ "${username,,}" =~ ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$ ]]; then
        return 0  # True
    else
        log_error "Invalid username: $username" 1  # Log the error here
        return 1  # False
    fi
}

validate_hostname() {
    local hostname="$1"
    if [[ "${hostname,,}" =~ ^[a-z][a-z0-9_.-]{0,62}[a-z0-9]$ ]]; then
        return 0  # True
    else
        log_error "Invalid hostname: $hostname" 1  # Log the error here
        return 1  # False
    fi
}

validate_disk() {
    local disk="$1"
    if [ -b "$disk" ]; then 
        return 0  # True
    else
        log_error "Invalid disk path: $disk" 1  # Log the error here
        return 1  # False
    fi
}

# --- Confirmation Function ---
confirm_action() {
    local message="$1"
    read -r -p "$message (Y/n) " confirm
    confirm=${confirm,,}  # Convert to lowercase

    # Check if confirm is "y" or empty
    if [[ "$confirm" == "y" ]] || [[ -z "$confirm" ]]; then  
        return 0  # True
    else
        return 1  # False
    fi
}

get_disk() {
    # List Disks
    log_output "Available disks:"
    fdisk -l | grep "Disk /"  # Only list whole disks

    while true; do
        read -r -p "Enter the disk to use (e.g., /dev/sda): " disk

        if ! validate_disk "$disk"; then  # Use the validate_disk function from functions.sh
            continue
        fi

        # Confirm Disk Selection using confirm_action from functions.sh
        if confirm_action "You have selected $disk. Is this correct?"; then
            export DISK="$disk"
            log_output "Disk set to: $DISK"
            break
        fi
    done
}

get_partition_sizes() {
    while true; do
        read -r -p "Enter EFI partition size (e.g., 2G, 512M): " efi_size
        read -r -p "Enter boot partition size (e.g., 5G, 1G): " boot_size

        # Basic validation (you might want to add more robust checks)
        if [[ "$efi_size" =~ ^[0-9]+(G|M|K)$ ]] && \
           [[ "$boot_size" =~ ^[0-9]+(G|M|K)$ ]]; then
            export EFI_SIZE="$efi_size"
            export BOOT_SIZE="$boot_size"
            log_output "EFI partition size: $EFI_SIZE"
            log_output "Boot partition size: $BOOT_SIZE"
            break
        else
            log_error "Invalid partition size(s). Please use a format like 2G or 512M." 1
        fi
    done
}

get_encryption_password() {
    while true; do
        read -rs -p "Enter encryption password: " password
        echo
        read -rs -p "Confirm encryption password: " confirm_password
        echo

        if [[ "$password" != "$confirm_password" ]]; then
            log_error "Passwords do not match." 1
            continue
        fi

        export ENCRYPTION_PASSWORD="$password"
        log_output "Encryption password set."  # Avoid logging the password itself
        break
    done
}

partition_disk() {
    local disk="$1"
    local efi_size="$2"
    local boot_size="$3"

    log_output "Partitioning disk: $disk"

    # Create new GPT partition table
    if ! sgdisk --zap-all "$disk"; then
        log_error "Failed to clear disk" $?
        exit 1
    fi

    # Create EFI partition
    if ! sgdisk -n 1:0:+"$efi_size" -t 1:EF00 "$disk"; then
        log_error "Failed to create EFI partition" $?
        exit 1
    fi

    # Create boot partition
    if ! sgdisk -n 2:0:+"$boot_size" -t 2:8300 "$disk"; then
        log_error "Failed to create boot partition" $?
        exit 1
    fi

    # Create LVM partition (using remaining space)
    if ! sgdisk -n 3:0:0 -t 3:8E00 "$disk"; then
        log_error "Failed to create LVM partition" $?
        exit 1
    fi

    # Print partition table
    sgdisk -p "$disk"

    # Re-read partition table
    partprobe "$disk"
}

setup_lvm() {
    local disk="$1"
    local password="$2"  # Pass the encryption password as an argument

    log_output "Setting up LVM on disk: $disk"

    # Format EFI partition
      if [[ $disk =~ nvme ]]; then
          if ! mkfs.fat -F32 "${disk}p1"; then
          log_error "Failed to format EFI partition" $?
          exit 1
      fi
    else
      if ! mkfs.fat -F32 "${disk}1"; then
          log_error "Failed to format EFI partition" $?
          exit 1
      fi
    fi


    # Format boot partition
     if [[ $disk =~ nvme ]]; then 
      if ! mkfs.ext4 "${disk}p2"; then
          log_error "Failed to format boot partition" $?
          exit 1
      fi
    else
      if ! mkfs.ext4 "${disk}2"; then
          log_error "Failed to format boot partition" $?
          exit 1
      fi
    fi 

    # Setup encryption on partition 3 using LUKS
      if [[ $disk =~ nvme ]]; then
        if ! echo "$password" | cryptsetup luksFormat "${disk}p3"; then
          log_error "Failed to format LUKS partition" $?
          exit 1
        fi

    # Open LUKS partition
      if ! echo "$password" | cryptsetup open --type luks --batch-mode "${disk}p3" lvm; then
        log_error "Failed to open LUKS partition" $?
        exit 1
      fi
    else
      if ! echo "$password" | cryptsetup luksFormat "${disk}3"; then
        log_error "Failed to format LUKS partition" $?
        exit 1
      fi

    # Open LUKS partition
      if ! echo "$password" | cryptsetup open --type luks --batch-mode "${disk}3" lvm; then
        log_error "Failed to open LUKS partition" $?
        exit 1
      fi
    fi

    # Create physical volume for LVM on partition 3 with data alignment 1m
    if ! pvcreate /dev/mapper/lvm; then
        log_error "Failed to create physical volume" $?
        exit 1
    fi

    # Create volume group called volgroup0 on partition 3
    if ! vgcreate volgroup0 /dev/mapper/lvm; then
        log_error "Failed to create volume group" $?
        exit 1
    fi

    # Get logical volume sizes from the user
    get_lv_sizes() {
        read -r -p "Enter root logical volume size (e.g., 50G, 200G): " root_lv_size
        # You might want to add validation here for $root_lv_size
        export ROOT_LV_SIZE="$root_lv_size"
        log_output "Root logical volume size: $ROOT_LV_SIZE"
    }

    get_lv_sizes

    # Create logical volumes (lv_home will use remaining space)
    if ! lvcreate -L "$ROOT_LV_SIZE" volgroup0 -n lv_root || \
       ! lvcreate -l 100%FREE volgroup0 -n lv_home; then
        log_error "Failed to create logical volumes" $?
        exit 1
    fi

    # Load kernel module
    modprobe dm_mod

    # Scan system for volume groups
    vgscan

    # Activate volume group
    if ! vgchange -ay; then
        log_error "Failed to activate volume group" $?
        exit 1
    fi

    # Format and mount root volume
    if ! mkfs.ext4 /dev/volgroup0/lv_root; then
        log_error "Failed to format root volume" $?
        exit 1
    fi

    if ! mount /dev/volgroup0/lv_root /mnt; then
        log_error "Failed to mount root volume" $?
        exit 1
    fi

    # Create /boot directory and mount partition 2
    if ! mkdir -p /mnt/boot; then
      log_error "Failed to create /boot directory" $?
      exit 1
    fi

    if [[ $disk =~ nvme ]]; then
      if ! mount "${disk}p2" /mnt/boot; then
        log_error "Failed to mount /boot" $?
        exit 1
      fi
    else
      if ! mount "${disk}2" /mnt/boot; then
        log_error "Failed to mount /boot" $?
        exit 1
      fi
    fi

    # Create /boot/EFI directory and mount partition 1
    if ! mkdir -p /mnt/boot/EFI; then
      log_error "Failed to create /boot/EFI directory" $?
      exit 1
    fi

    if [[ $disk =~ nvme ]]; then
      if ! mount "${disk}p1" /mnt/boot/EFI; then
        log_error "Failed to mount /boot/EFI" $?
        exit 1
      fi
    else
      if ! mount "${disk}1" /mnt/boot/EFI; then
        log_error "Failed to mount /boot/EFI" $?
        exit 1
      fi
    fi

    # Format home volume
    if ! mkfs.ext4 /dev/volgroup0/lv_home; then
        log_error "Failed to format home volume" $?
        exit 1
    fi

    # Create /home directory
    if ! mkdir -p /mnt/home; then
        log_error "Failed to create /home directory" $?
        exit 1
    fi

    # Mount home volume
    if ! mount /dev/volgroup0/lv_home /mnt/home; then
        log_error "Failed to mount /home" $?
        exit 1
    fi

    # Ensure /mnt/etc exists
    if ! mkdir -p /mnt/etc; then
        log_error "Failed to create /mnt/etc directory" $?
        exit 1
    fi
}

# --- Timezone Selection ---
setup_timezone() {
    log_output "Setting up timezone..."

    # Function to get a list of timezones
    get_timezones() {
        local count=1
        find /usr/share/zoneinfo -type f | sed 's|/usr/share/zoneinfo/||' | awk -v cnt=$count '{print cnt". "$0; cnt++}'
    }

    # Collect timezones into an array
    mapfile -t timezones < <(get_timezones)

    # Check if timezones were collected
    if [ ${#timezones[@]} -eq 0 ]; then
        log_error "No timezones found. Please check the timezone directory and try again." 1
        exit 1
    fi

    # Constants
    PAGE_SIZE=40
    COLS=1  # Number of columns to display
    NUMBER_WIDTH=4  # Width for number and dot
    COLUMN_WIDTH=2  # Width of each column for timezones

    # Function to display a page of timezones in columns
    display_page() {
    local start=$1
    local end=$2  # Corrected this line
    local count=0

    echo "Timezones ($((start + 1)) to $end of ${#timezones[@]}):"

    for ((i=start; i<end; i++)); do
        # Print timezones in columns with minimized gap
        printf "%-${NUMBER_WIDTH}s%-${COLUMN_WIDTH}s" "${timezones[$i]}" ""
        count=$((count + 1))

        if ((count % COLS == 0)); then
            echo
        fi
    done
    
}

 # Display pages of timezones
    total_timezones=${#timezones[@]}
    current_page=0

    while true; do
        start=$((current_page * PAGE_SIZE))
        end=$((start + PAGE_SIZE))
        if ((end > total_timezones)); then
            end=$total_timezones
        fi

        display_page "$start" "$end"

        # Prompt user for selection or continue
        read -r -p "Enter the number of your timezone choice from this page, or press Enter to see more timezones: " choice

        # Check if user made a choice
        if [[ "$choice" =~ ^[0-9]+$ ]]; then 
            if ((choice >= 1 && choice <= total_timezones)); then 
                # Extract the selected timezone
                selected_timezone=$(echo "${timezones[$((choice-1))]}" | awk '{print $2}')

                # Set timezone
                ln -sf /usr/share/zoneinfo/"$selected_timezone" /etc/localtime

                # Verify timezone setting
                log_output "Timezone has been set to $(readlink -f /etc/localtime)"
                break  # Exit the loop if a valid selection is made
            else
                echo "Invalid selection. Please enter a valid number from the displayed list."
            fi
        elif [[ -z "$choice" ]]; then  # User pressed Enter, go to the next page
            if ((end == total_timezones)); then  # If this is the last page, loop back to the beginning
                current_page=0
            else
                current_page=$((current_page + 1))
            fi
        else
            echo "Invalid input. Please enter a number or press Enter to continue."
        fi
    done
}

configure_pacman() {
    log_output "Configuring pacman..."

    if ! sed -i "/^#Color/c\Color\nILoveCandy" /etc/pacman.conf || \
       ! sed -i "/^#VerbosePkgLists/c\VerbosePkgLists" /etc/pacman.conf || \
       ! sed -i "/^#ParallelDownloads/c\ParallelDownloads = 5" /etc/pacman.conf || \
       ! sed -i '/^#\[multilib\]/,+1 s/^#//' /etc/pacman.conf; then
        log_error "Failed to configure pacman" $?
        exit 1
    fi
}

install_microcode() {
    log_output "Installing microcode..."
    proc_type=$(lscpu | grep -oP '^Vendor ID:\s+\K\w+')
    if [ "$proc_type" = "GenuineIntel" ]; then
        log_output "Installing Intel microcode"
        if ! pacman -Sy --noconfirm --needed intel-ucode; then
            log_error "Failed to install Intel microcode" $?
            exit 1
        fi
    elif [ "$proc_type" = "AuthenticAMD" ]; then
        log_output "Installing AMD microcode"
        if ! pacman -Sy --noconfirm --needed amd-ucode; then
            log_error "Failed to install AMD microcode" $?
            exit 1
        fi
    fi
}

enable_services() {
    log_output "Enabling services..."
    if ! systemctl enable NetworkManager.service || \
       ! systemctl enable fstrim.timer; then
        log_error "Failed to enable services" $?
        exit 1
    fi
}

set_locale() {
    log_output "Setting locale..."
    if ! sed -i 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen || \
       ! locale-gen || \
       ! echo 'LANG=en_US.UTF-8' > /etc/locale.conf; then 
        log_error "Failed to set locale" $?
        exit 1
    fi
}

update_initramfs() {
    log_output "Updating initramfs..."
    if ! sed -i 's/^HOOKS\s*=\s*(.*)/HOOKS=(base udev autodetect modconf block encrypt lvm2 filesystems keyboard fsck)/' /etc/mkinitcpio.conf || \
       ! mkinitcpio -p linux; then
        log_error "Failed to update initramfs" $?
        exit 1
    fi
}

# --- User Input Functions ---
get_username() {
    while true; do
        read -r -p "Enter a username: " username

        if ! validate_username "$username"; then
            continue  # No need to log here, validate_username already logs
        fi

        export USERNAME="$username"
        log_output "Username set to: $USERNAME"
        break
    done
}

get_user_password() { # Renamed to avoid conflict with the existing get_password function
    while true; do
        read -rs -p "Set a password for $USERNAME: " USER_PASSWORD1
        echo
        read -rs -p "Confirm password: " USER_PASSWORD2
        echo

        if [[ "$USER_PASSWORD1" != "$USER_PASSWORD2" ]]; then
            log_error "Passwords do not match." 1
            continue
        fi

        export USER_PASSWORD="$USER_PASSWORD1"
        log_output "Password set for $USERNAME successfully."
        break
    done
}

get_root_password() {
    while true; do
        read -rs -p "Set root password: " ROOT_PASSWORD1
        echo
        read -rs -p "Confirm root password: " ROOT_PASSWORD2
        echo

        if [[ "$ROOT_PASSWORD1" != "$ROOT_PASSWORD2" ]]; then
            log_error "Passwords do not match." 1
            continue
        fi

        export ROOT_PASSWORD="$ROOT_PASSWORD1"
        log_output "Root password set successfully."
        break
    done
}

get_hostname() {
    while true; do
        read -r -p "Enter a hostname: " hostname

        if ! validate_hostname "$hostname"; then
            continue
        fi

        export HOSTNAME="$hostname"
        log_output "Hostname set to: $HOSTNAME"
        break
    done
}

create_user() {
    local username="$1"
    log_output "Creating user: $username"
    if ! useradd -m -G wheel,power,storage,uucp,network -s /bin/bash "$username"; then
        log_error "Failed to create user" $?
        exit 1
    fi
}

set_passwords() {
    log_output "Setting passwords..."
    # Make sure USERNAME and PASSWORD are exported before calling this function
    if ! echo "$USERNAME:$USER_PASSWORD" | chpasswd || \
       ! echo "root:$ROOT_PASSWORD" | chpasswd; then  # Assuming ROOT_PASSWORD is exported
        log_error "Failed to set passwords" $?
        exit 1
    fi
}

set_hostname() {
    local hostname="$1"
    log_output "Setting hostname: $hostname"
    if ! echo "$hostname" > /etc/hostname; then 
        log_error "Failed to set hostname" $?
        exit 1
    fi
}

update_sudoers() {
    log_output "Updating sudoers..."
    if ! cp /etc/sudoers /etc/sudoers.backup || \
       ! sed -i 's/^# *%wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers || \
       ! echo 'Defaults targetpw' >> /etc/sudoers || \
       ! visudo -c; then
        log_error "Failed to update sudoers" $?
        # Restore backup if visudo fails
        cp /etc/sudoers.backup /etc/sudoers
        exit 1
    fi
}

install_grub() {
    log_output "Installing GRUB..."
    if ! grub-install --target=x86_64-efi --bootloader-id=grub_uefi --recheck; then
        log_error "Failed to install GRUB" $?
        exit 1
    fi
}

configure_grub() {
    log_output "Configuring GRUB..."

    # Make sure DISK is exported and available in the environment
    if [[ $disk =~ nvme ]]; then
      if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
         ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"'p3:volgroup0 loglevel=3"' /etc/default/grub || \
         ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
         ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
         ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
         ! grub-mkconfig -o /boot/grub/grub.cfg; then
          log_error "Failed to configure GRUB" $?
          exit 1
      fi
    else
      if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
         ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"'3:volgroup0 loglevel=3"' /etc/default/grub || \
         ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
         ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
         ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
         ! grub-mkconfig -o /boot/grub/grub.cfg; then
          log_error "Failed to configure GRUB" $?
          exit 1
      fi
}


install_nvidia_drivers() {
    log_output "Detecting NVIDIA GPUs..."

    # Detect NVIDIA GPUs
    readarray -t dGPU < <(lspci -k | grep -E "(VGA|3D)" | grep -i nvidia)

    # Check if any NVIDIA GPUs were found
    if [ ${#dGPU[@]} -gt 0 ]; then
        log_output "NVIDIA GPU(s) detected:"
        for gpu in "${dGPU[@]}"; do
            log_output "  $gpu"
        done

        log_output "Installing NVIDIA drivers..."

        # Install NVIDIA drivers and related packages
        if ! pacman -Sy --noconfirm --needed nvidia libglvnd nvidia-utils opencl-nvidia lib32-libglvnd lib32-nvidia-utils lib32-opencl-nvidia nvidia-settings; then 
            log_error "Failed to install NVIDIA packages" $?
            exit 1
        fi

        # Add NVIDIA modules to initramfs
        if ! sed -i '/^MODULES=()/c\MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' /etc/mkinitcpio.conf || \
           ! mkinitcpio -p linux; then
            log_error "Failed to update initramfs with NVIDIA modules" $?
            exit 1
        fi

        # Update GRUB configuration with NVIDIA settings
        # Make sure DISK is exported and available in the environment
        if [[ $disk =~ nvme ]]; then
          if ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'p3:volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'p3:volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
             ! grub-mkconfig -o /boot/grub/grub.cfg; then
              log_error "Failed to update GRUB configuration with NVIDIA settings" $?
              exit 1
          fi
        else
          fi ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
             ! grub-mkconfig -o /boot/grub/grub.cfg; then
              log_error "Failed to update GRUB configuration with NVIDIA settings" $?
              exit 1
            fi
          fi
      else
          log_output "No NVIDIA GPUs detected. Skipping NVIDIA driver installation."
      fi
}

install_prerequisites() {
    log_output "Installing prerequisite packages..."
    if ! pacman -Sy --noconfirm --needed pacman-contrib reflector rsync; then
        log_error "Failed to install prerequisite packages" $?
        exit 1
    fi
}

configure_mirrors() {
    log_output "Configuring pacman mirrors for faster downloads..."
    if ! cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.backup || \
       ! reflector -a 48 -f 5 -l 20 --sort rate --save /etc/pacman.d/mirrorlist; then
        log_error "Failed to configure pacman mirrors" $?
        exit 1
    fi
}

install_base_packages() {
    log_output "Installing base packages using pacstrap..."
    if ! pacstrap -K /mnt base linux linux-firmware linux-headers --noconfirm --needed; then
        log_error "Failed to install base packages" $?
        exit 1
    fi
}

install_additional_packages() {
    log_output "Installing additional packages..."
    if ! pacman -Sy --noconfirm --needed - < ./pkgs.lst; then
        log_error "Failed to install additional packages" $?
        exit 1
    fi
}

# --- Desktop Environment ---
select_gui() {
    log_output "Selecting GUI..."

    options=("Server (No GUI)" "GNOME" "KDE Plasma")
    select gui_choice in "${options[@]}"; do
        case "$gui_choice" in
            "GNOME")
                export GUI_CHOICE="gnome"
                log_output "GNOME selected."
                ;;
            "KDE Plasma")
                export GUI_CHOICE="kde"
                log_output "KDE Plasma selected."
                ;;
            *)
                export GUI_CHOICE="none"
                log_output "No GUI selected."
                ;;
        esac
        break
    done
}

install_gui() {
    if [[ "$GUI_CHOICE" == "gnome" ]]; then
        log_output "Installing GNOME desktop environment..."
        if ! pacman -Sy --noconfirm --needed gnome gnome-extra gnome-tweaks gnome-shell-extensions gnome-browser-connector firefox; then
            log_error "Failed to install GNOME packages" $?
            exit 1
        fi
        if ! systemctl enable gdm.service; then
            log_error "Failed to enable gdm service" $?
            exit 1
        fi
        log_output "GNOME installed and gdm enabled."
    elif [[ "$GUI_CHOICE" == "kde" ]]; then
        log_output "Installing KDE Plasma desktop environment..."
        if ! pacman -Sy --noconfirm --needed xorg plasma-desktop sddm kde-applications dolphin firefox lxappearance; then
            log_error "Failed to install KDE Plasma packages" $?
            exit 1
        fi
        if ! systemctl enable sddm.service; then
            log_error "Failed to enable sddm service" $?
            exit 1
        fi
        log_output "KDE Plasma installed and sddm enabled."
    else
        log_output "No GUI selected. Skipping GUI installation."
    fi
}

# --- AUR Helper ---
get_aur_helper() {
    log_output "Selecting AUR helper..."

    # Ask the user if they want to install an AUR helper
    if ! confirm_action "Do you want to install an AUR helper?"; then
        log_output "Skipping AUR helper installation."
        export AUR_HELPER="none"
        return 0
    fi

    # Ask the user which AUR helper they want
    options=("paru" "yay" "none")
    select aur_helper in "${options[@]}"; do
        case "$aur_helper" in
            paru)
                export AUR_HELPER="paru"
                log_output "paru selected."
                ;;
            yay)
                export AUR_HELPER="yay"
                log_output "yay selected."
                ;;
            none)
                export AUR_HELPER="none"
                log_output "No AUR helper selected."
                ;;
            *)
            log_output "Invalid option. Skipping AUR helper installation."
            export AUR_HELPER="none"
            exit 1  # Exit the script if the option is invalid
            ;;
    esac
    break
done
}

install_aur_helper() {
    log_output "Installing AUR helper..."

     # Temporarily allow the user to run sudo without a password
     echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

    # Switch to the created user
    if ! runuser -u "$USERNAME" -- /bin/bash -c "
        # Install git if not already installed
        if ! pacman -Qi git &> /dev/null; then
            if ! sudo pacman -S --noconfirm git; then
                log_error \"Failed to install git\" 3
                exit 3
            fi
        fi

        # Install the chosen AUR helper
        case \"$AUR_HELPER\" in
            yay)
                mkdir -p tmp
                cd tmp && git clone https://aur.archlinux.org/yay.git || { log_error \"Failed to clone yay repository\" 4; exit 4; }
                cd yay && makepkg -si --noconfirm -C yay || { log_error \"Failed to build and install yay\" 5; exit 5; }
                ;;
            paru)
                mkdir -p tmp
                cd tmp && git clone https://aur.archlinux.org/paru.git || { log_error \"Failed to clone paru repository\" 6; exit 6; }
                cd paru && makepkg -si --noconfirm -C paru || { log_error \"Failed to build and install paru\" 7; exit 7; }
                ;;
            *)
                log_error \"Invalid AUR helper specified\" 8
                exit 8
                ;;
        esac
    "; then
        log_error "Failed to switch to user" 9
        return 9
    fi

    # Remove the temporary sudoers entry
    sed -i "/$USERNAME ALL=(ALL) NOPASSWD: ALL/d" /etc/sudoers

    log_output "AUR helper installed."
}

# --- Cleanup Function ---
cleanup() {
    log_output "Cleaning up..."

    # Remove temporary files and directories created during the installation
    rm -rf /mnt/global_functions.sh
    rm -rf /mnt/chroot.sh
    rm -rf /mnt/tmp

    # Copy log files to the installed system
    cp /var/log/arch_install.log /mnt/var/log/arch_install.log
    cp /var/log/arch_install_error.log /mnt/var/log/arch_install_error.log
}

Offline

#3 2024-11-22 05:42:29

Head_on_a_Stick
Member
From: The Wirral
Registered: 2014-02-20
Posts: 8,284
Website

Re: [SOLVED] Looking for help with a shell script I made to install Arch

I only checked the script very quickly but you need to read this:

https://wiki.archlinux.org/title/System … nsupported


Para todos todo, para nosotros nada

Offline

#4 2024-11-22 15:13:34

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

Head_on_a_Stick wrote:

I only checked the script very quickly but you need to read this:

https://wiki.archlinux.org/title/System … nsupported


Thank you for your response and taking a look for me. I apologize in advance if my question is dumb or elementary to those who are more experienced in Arch than myself... but I don't seem to understand the connection between my use of pacman and my issue. I only ask because your link directly opens to section 3.3 of that article so I assume that something caught your eye regarding my use of pacman. Can you please give me some context? I did read the entire page but don't want to change things blindly without understanding what you're hinting at.


I was able to get Arch installed on baremetal with my script when selecting a NVMe drive using my thought from last night but now I just need to make sure /etc/default/grub is being updated correctly as grub was unable to find /dev/mapper/volgroup0-lv_root. I suspect the conditional logic didn't work right with "sed -i" or I made a typo somewhere.

Offline

#5 2024-11-22 18:43:22

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

Update. After some testing and commenting out the "cleanup" function, and "umount -R /mnt" I found that the cryptdevice was set to "/dev/nvme0n13:volgroup0” rather than "/dev/nvme0n1p3:volgroup0"

I suppose this could be easily avoided by using UUID over absolute path.

Offline

#6 2024-11-22 19:20:16

Head_on_a_Stick
Member
From: The Wirral
Registered: 2014-02-20
Posts: 8,284
Website

Re: [SOLVED] Looking for help with a shell script I made to install Arch

live4thamuzik wrote:

I don't seem to understand the connection between my use of pacman and my issue

I just noticed the script made use of `pacman -Sy` several times, which is thoroughly bad practice. I have no comment about your issue because I posted just before leaving for $DAY_JOB and I didn't have time to read everything (sorry).


Para todos todo, para nosotros nada

Offline

#7 2024-11-22 19:22:35

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

Head_on_a_Stick wrote:
live4thamuzik wrote:

I don't seem to understand the connection between my use of pacman and my issue

I just noticed the script made use of `pacman -Sy` several times, which is thoroughly bad practice. I have no comment about your issue because I posted just before leaving for $DAY_JOB and I didn't have time to read everything (sorry).


That makes sense. I will correct the excessive use of pacman -Sy. Thanks again!


UPDATE:
I changed all subordinate pacman -Sy commands to pacman -S. At this time only "install_prerequisites" uses the -Sy flag as it is the very first time pacman is used in the script.

As far as the issue in OP - I looks like my use of the sed -i command to update /etc/default/grub was the reason for the "p" not being added. I adjusted that code from

old

configure_grub() {
  log_output "Configuring GRUB..."

  # Make sure DISK is exported and available in the environment
  if [[ $disk =~ nvme ]]; then
    if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
       ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"'p3:volgroup0 loglevel=3"' /etc/default/grub || \
       ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
       ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
       ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
       ! grub-mkconfig -o /boot/grub/grub.cfg; then
      log_error "Failed to configure GRUB" $?
      exit 1
    fi
  else
    if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
       ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"'3:volgroup0 loglevel=3"' /etc/default/grub || \
       ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
       ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
       ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
       ! grub-mkconfig -o /boot/grub/grub.cfg; then
      log_error "Failed to configure GRUB" $?
      exit 1
    fi
  fi
}


install_nvidia_drivers() {
    log_output "Detecting NVIDIA GPUs..."

    # Detect NVIDIA GPUs
    readarray -t dGPU < <(lspci -k | grep -E "(VGA|3D)" | grep -i nvidia)

    # Check if any NVIDIA GPUs were found
    if [ ${#dGPU[@]} -gt 0 ]; then
        log_output "NVIDIA GPU(s) detected:"
        for gpu in "${dGPU[@]}"; do
            log_output "  $gpu"
        done

        log_output "Installing NVIDIA drivers..."

        # Install NVIDIA drivers and related packages
        if ! pacman -S --noconfirm --needed nvidia libglvnd nvidia-utils opencl-nvidia lib32-libglvnd lib32-nvidia-utils lib32-opencl-nvidia nvidia-settings; then 
            log_error "Failed to install NVIDIA packages" $?
            exit 1
        fi

        # Add NVIDIA modules to initramfs
        if ! sed -i '/^MODULES=()/c\MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' /etc/mkinitcpio.conf || \
           ! mkinitcpio -p linux; then
            log_error "Failed to update initramfs with NVIDIA modules" $?
            exit 1
        fi

        # Update GRUB configuration with NVIDIA settings
        if [[ $DISK =~ nvme ]]; then
            if ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'p3:volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'p3:volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
               ! grub-mkconfig -o /boot/grub/grub.cfg; then
                log_error "Failed to update GRUB configuration with NVIDIA settings" $?
            fi
        else
            if ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
               ! grub-mkconfig -o /boot/grub/grub.cfg; then
                log_error "Failed to update GRUB configuration with NVIDIA settings" $?
            fi
        fi

        else
            log_output "No NVIDIA GPUs detected. Skipping NVIDIA driver installation."
        fi
    }

new  # The change is subtle but I moved "p3" inside single quotes

configure_grub() {
  log_output "Configuring GRUB..."

  # Make sure DISK is exported and available in the environment
  if [[ $disk =~ nvme ]]; then
    if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
       ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"p3':volgroup0 loglevel=3"' /etc/default/grub || \
       ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
       ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
       ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
       ! grub-mkconfig -o /boot/grub/grub.cfg; then
      log_error "Failed to configure GRUB" $?
      exit 1
    fi
  else
    if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
       ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='"$DISK"'3:volgroup0 loglevel=3"' /etc/default/grub || \
       ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
       ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
       ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
       ! grub-mkconfig -o /boot/grub/grub.cfg; then
      log_error "Failed to configure GRUB" $?
      exit 1
    fi
  fi
}


install_nvidia_drivers() {
    log_output "Detecting NVIDIA GPUs..."

    # Detect NVIDIA GPUs
    readarray -t dGPU < <(lspci -k | grep -E "(VGA|3D)" | grep -i nvidia)

    # Check if any NVIDIA GPUs were found
    if [ ${#dGPU[@]} -gt 0 ]; then
        log_output "NVIDIA GPU(s) detected:"
        for gpu in "${dGPU[@]}"; do
            log_output "  $gpu"
        done

        log_output "Installing NVIDIA drivers..."

        # Install NVIDIA drivers and related packages
        if ! pacman -S --noconfirm --needed nvidia libglvnd nvidia-utils opencl-nvidia lib32-libglvnd lib32-nvidia-utils lib32-opencl-nvidia nvidia-settings; then 
            log_error "Failed to install NVIDIA packages" $?
            exit 1
        fi

        # Add NVIDIA modules to initramfs
        if ! sed -i '/^MODULES=()/c\MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)' /etc/mkinitcpio.conf || \
           ! mkinitcpio -p linux; then
            log_error "Failed to update initramfs with NVIDIA modules" $?
            exit 1
        fi

        # Update GRUB configuration with NVIDIA settings
        if [[ $DISK =~ nvme ]]; then
            if ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"p3':volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"p3':volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
               ! grub-mkconfig -o /boot/grub/grub.cfg; then
                log_error "Failed to update GRUB configuration with NVIDIA settings" $?
            fi
        else
            if ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 loglevel=3"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice=\/dev\/'"$DISK"'3:volgroup0 nvidia_drm_modeset=1 loglevel=3"' /etc/default/grub || \
               ! grub-mkconfig -o /boot/grub/grub.cfg; then
                log_error "Failed to update GRUB configuration with NVIDIA settings" $?
            fi
        fi

        else
            log_output "No NVIDIA GPUs detected. Skipping NVIDIA driver installation."
        fi
    }

I intend to move to using UUID still but it looks like I cannot avoid the use of conditional logic. So i figured it made more sense to resolve the existing issue rather than trying the same logic with blkid to pull the UUID from ${DISK}3 or ${DISK}p3

Last edited by live4thamuzik (2024-11-22 21:23:55)

Offline

#8 2024-11-22 22:36:10

MAYBL8
Member
Registered: 2022-01-14
Posts: 257

Re: [SOLVED] Looking for help with a shell script I made to install Arch

Tried to run your latest script . Just downloaded it.
I tried to install on a usb flash drive. /dev/sda
I have no experience with luks. Hope I answered the questions correctly.
I got this error:

warning: rsync-3.3.0-2 is up to date -- skipping
2024-11-22 17:17:16 - Error: Failed to install prerequisite packages (exit code: 0)

I might not be the one to test this.

Online

#9 2024-11-22 22:41:57

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

MAYBL8 wrote:

Tried to run your latest script . Just downloaded it.
I tried to install on a usb flash drive. /dev/sda
I have no experience with luks. Hope I answered the questions correctly.
I got this error:

warning: rsync-3.3.0-2 is up to date -- skipping
2024-11-22 17:17:16 - Error: Failed to install prerequisite packages (exit code: 0)

I might not be the one to test this.

Oh, that's interesting! I've never tried to install on a usb stick. I will dig into that when I finish testing the NVMe support. Thank you!

Offline

#10 Yesterday 03:34:55

live4thamuzik
Member
Registered: 2024-02-27
Posts: 43

Re: [SOLVED] Looking for help with a shell script I made to install Arch

Sorry for not posting the solution earlier but I solved the problem with GRUB finding the LUKS partition on NVMe drives by reworking the logic a little bit and correcting my sed command.

configure_grub() {
  log_output "Configuring GRUB..."

  # Make sure DISK is exported and available in the environment
  if [[ $DISK == "/dev/nvme"* ]]; then
    PART_PREFIX="p"
  else
    PART_PREFIX=""
    fi
    
    if ! sed -i '/^GRUB_DEFAULT=/c\GRUB_DEFAULT=saved' /etc/default/grub || \
       ! sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/c\GRUB_CMDLINE_LINUX_DEFAULT="quiet cryptdevice='${DISK}${PART_PREFIX}'3:volgroup0 loglevel=3"' /etc/default/grub || \
       ! sed -i '/^#GRUB_ENABLE_CRYPTODISK=y/c\GRUB_ENABLE_CRYPTODISK=y' /etc/default/grub || \
       ! sed -i '/^#GRUB_SAVEDEFAULT=true/c\GRUB_SAVEDEFAULT=true' /etc/default/grub || \
       ! cp /usr/share/locale/en\@quot/LC_MESSAGES/grub.mo /boot/grub/locale.en.mo || \
       ! grub-mkconfig -o /boot/grub/grub.cfg; then
      log_error "Failed to configure GRUB" $?
      exit 1
    fi
}

I have one more small issue with the script not updating /etc/default/grub after installing nvidia drivers but I'll start a new thread for that if I can't sort it out on my own.

Offline

Board footer

Powered by FluxBB