Advertisement
Guest User

ArchLinux USB install script

a guest
Apr 17th, 2019
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 21.32 KB | None | 0 0
  1. #!/bin/bash
  2. ##=============================  mk-usb  =============================##
  3. # mk-usb <DEVICE>
  4. # Automated Arch Linux USB install:
  5. #  * for use with 16GB USB stick
  6. #  * creates two bootable host OS partitions
  7. #  * OS partitions load completely to RAM by default with ramroot
  8. #  * enabled large page memory support for mining
  9. #  * tweaked journaling for running OS from a USB
  10. #  * reverted to traditional network device naming
  11. #  * disabled terminal bell
  12. #  * added custom repo to /etc/pacman.conf
  13. #  * set NOPASSWD for various user commands
  14. #  * various other tweaks
  15. # Partition scheme:
  16. #  * sdx1        10M
  17. #  * sdx2  vfat  500M
  18. #  * sdx3  ext4  $HOME_SIZE
  19. #  * sdx4  ext4  $HOST1_SIZE
  20. #  * sdx5  ext4  [remaining]
  21. # Mount scheme:
  22. #  * sdx4/sdx5       /
  23. #  * sdx3            /home
  24. #  * sdx2            /EFI
  25. #  * /EFI/$HOSTNAME  /boot
  26.  
  27. ##===========================  VARIABLES  ============================##
  28. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  29. # HOST/USER settings:
  30. HOME_SIZE='1G'
  31. HOST_KEYMAP='/usr/share/kbd/keymaps/i386/qwerty/us.map.gz'
  32. HOST_LANG='en_US.UTF-8'
  33. HOST_TIMEZONE='America/Chicago'
  34. ROOT_PASS='xxxxxxx'
  35. USER_COMMANDS_NOPASS=('ALL')
  36. USER_NAME='bikes-n-math'
  37. USER_PASS='xxxxxxx'
  38. HOST_PKGS_COMMON=(
  39.    base
  40.    base-devel
  41.    pacman-contrib
  42.    sudo
  43.    acpi
  44.    arch-install-scripts
  45.    arch-wiki-lite
  46.    bc
  47.    binwalk
  48.    cifs-utils
  49.    cmake
  50.    curl
  51.    dialog
  52.    dmidecode
  53.    dosfstools
  54.    efitools
  55.    efivar
  56.    elinks
  57.    exfat-utils
  58.    fbida
  59.    figlet
  60.    fuse2
  61.    fuse3
  62.    fuseiso
  63.    git
  64.    gnu-netcat
  65.    gnupg
  66.    gptfdisk
  67.    hddtemp
  68.    htop
  69.    ifplugd
  70.    iw
  71.    jq
  72.    libusbmuxd
  73.    lm_sensors
  74.    ltrace
  75.    macchanger
  76.    nano
  77.    nmap
  78.    ntfs-3g
  79.    openssh
  80.    openssl
  81.    p7zip
  82.    polkit
  83.    python
  84.    python2
  85.    ranger
  86.    rsync
  87.    sbsigntools
  88.    smbclient
  89.    sshfs
  90.    strace
  91.    sysstat
  92.    tmux
  93.    tor
  94.    torsocks
  95.    unrar
  96.    unzip
  97.    usbmuxd
  98.    vim
  99.    w3m
  100.    wget
  101.    wpa_supplicant
  102.    inxi
  103.    ramroot
  104. )
  105. # HOST1 settings:
  106. HOST1_NAME='arch-cpu'
  107. HOST1_SIZE='4G'
  108. HOST1_PART=4
  109. HOST1_PKGS=(
  110.    "${HOST_PKGS_COMMON[@]}"
  111. )
  112. # HOST2 settings:
  113. HOST2_NAME='arch-cpu-gpu'
  114. HOST2_PART=5
  115. HOST2_PKGS=(
  116.    "${HOST_PKGS_COMMON[@]}"
  117.    cuda
  118.    opencl-amd
  119. )
  120. # control:
  121. ANSWER=
  122. HOST_NAME=
  123. HOST_PART=
  124. HOST_PKGS=()
  125. # USB:
  126. USB_MOUNT='/mnt/usb'
  127. USB_DEVICE=
  128. USB_INFO=
  129. USB_SIZE_BYTES=
  130. USB_SIZE_HUMAN=
  131. USB_BLOCK_COUNT=
  132. USB_BLOCK_SIZE=
  133. # basic colors:
  134. C_BLUE_B=$'\e[1;38;5;27m'
  135. C_GREEN_B=$'\e[1;38;5;46m'
  136. C_RED_B=$'\e[1;38;5;196m'
  137. C_YELLOW_B=$'\e[1;33m'
  138. C_WHITE_B=$'\e[1;37m'
  139. C_GRAY=$'\e[0;37m'
  140.  
  141. ##===========================  FUNCTIONS  ============================##
  142. msg() {
  143. # print status message:
  144.    printf '%s==>%s %s%s\n' "$C_GREEN_B" "$C_WHITE_B" "$1" "$C_GRAY"
  145. }
  146.  
  147. msg_ask() {
  148. # print ask message:
  149.    printf '%s::>%s %s%s ' "$C_BLUE_B" "$C_WHITE_B" "$1" "$C_GRAY"
  150. }
  151.  
  152. msg_error() {
  153. # print error message:
  154.    printf '%s==> ERROR:%s %s%s\n' "$C_RED_B" "$C_WHITE_B" \
  155.        "$1" "$C_GRAY"
  156. }
  157.  
  158. msg_warn() {
  159. # print warning message:
  160.    printf '%s==> WARNING:%s %s%s\n' "$C_YELLOW_B" "$C_WHITE_B" \
  161.        "$1" "$C_GRAY"
  162. }
  163.  
  164. msg2() {
  165. # print status sub-message:
  166.    printf '%s ->%s %s%s\n' "$C_BLUE_B" "$C_WHITE_B" "$1" "$C_GRAY"
  167. }
  168.  
  169. internet_status() {
  170. # return internet connection status:
  171.    ping -q -c1 -W3 google.com &>/dev/null || \
  172.        ping -q -c1 -W5 archlinux.org &>/dev/null
  173. }
  174.  
  175. usb_pacstrap() {
  176. # pacstrap and configure basic system:
  177.    msg "Installing $HOST_NAME Arch Linux to $USB_DEVICE$HOST_PART..."
  178.  
  179.    # pacstrap all packages:
  180.    msg2 'pacstrap'
  181.    pacstrap -c "$USB_MOUNT" "${HOST_PKGS[@]}"
  182.    arch-chroot "$USB_MOUNT" paccache -rk0
  183.  
  184.    # create fstab:
  185.    genfstab -U "$USB_MOUNT" >> "$USB_MOUNT/etc/fstab"
  186.    sed -i "s~${USB_MOUNT}~~" "$USB_MOUNT/etc/fstab"
  187.    msg2 'fstab created'
  188.  
  189.    # set time zone
  190.    arch-chroot "$USB_MOUNT" ln \
  191.        -sf "/usr/share/zoneinfo/$HOST_TIMEZONE" /etc/localtime
  192.    msg2 "time zone set to $HOST_TIMEZONE"
  193.  
  194.    # gen /etc/adjtime:
  195.    arch-chroot "$USB_MOUNT" hwclock --systohc
  196.  
  197.    # set language:
  198.    sed -i "s/#${HOST_LANG}/${HOST_LANG}/" "$USB_MOUNT/etc/locale.gen"
  199.     arch-chroot "$USB_MOUNT" locale-gen
  200.     printf 'LANG=%s\n' "$HOST_LANG" > "$USB_MOUNT/etc/locale.conf"
  201.     msg2 "language/locale set to $HOST_LANG"
  202.  
  203.     # set hostname:
  204.     echo "$HOST_NAME" > "$USB_MOUNT/etc/hostname"
  205.     echo "127.0.1.1  $HOST_NAME.localdomain  $HOST_NAME" \
  206.         > "$USB_MOUNT/etc/hosts"
  207.     msg2 "hostname set to $HOST_NAME"
  208.  
  209.     # set root password:
  210.     msg2 'user root'
  211.     if [ -n "$ROOT_PASS" ]; then
  212.         # use ROOT_PASS as root passwd:
  213.         printf "printf '%s\\\n%s\\\n' | passwd\n" \
  214.             "$ROOT_PASS" "$ROOT_PASS" | arch-chroot "$USB_MOUNT"
  215.     else
  216.         # prompt for root passwd:
  217.         arch-chroot "$USB_MOUNT" passwd
  218.     fi
  219. }
  220.  
  221. usb_config() {
  222. # optimize system for a portable USB installation:
  223.     # set systemd journal to use RAM:
  224.     sed -i -e 's/#?Storage=.*/Storage=volatile/' \
  225.         -e 's/#*SystemMaxUse=.*/SystemMaxUse=16M/' \
  226.         "$USB_MOUNT/etc/systemd/journald.conf"
  227.     msg2 'journaling set to use RAM'
  228.  
  229.     # disable file access time record keeping:
  230.     sed -i 's/relatime/noatime/' "$USB_MOUNT/etc/fstab"
  231.     msg2 'file access time record keeping disabled'
  232.  
  233.     # disable autodetect kernel hook:
  234.     sed -i 's/^ *\(HOOKS=.*\)autodetect */\1/' \
  235.         "$USB_MOUNT/etc/mkinitcpio.conf"
  236.     msg2 'autodetect kernel build hook removed'
  237.  
  238.     # disable fallback initramfs image:
  239.     sed -i "s/ *PRESETS=.*/PRESETS=('default')/" \
  240.         "$USB_MOUNT"/etc/mkinitcpio.d/linux.preset
  241.     rm -rf "$USB_MOUNT/boot/"*fallback*
  242.     msg2 'fallback initrams image generation disabled'
  243.  
  244.     # enable traditional network interface naming:
  245.     arch-chroot "$USB_MOUNT" ln -sf /dev/null \
  246.         /etc/udev/rules.d/80-net-setup-link.rules
  247.     msg2 'traditional network interface naming enabled'
  248.  
  249.     # disable terminal bell:
  250.     mkdir -p "$USB_MOUNT/etc/modprobe.d"
  251.     printf 'blacklist pcspkr\n' \
  252.         > "$USB_MOUNT/etc/modprobe.d/nobeep.conf"
  253.     msg2 'terminal bell disabled'
  254.  
  255.     # enable pacman color:
  256.     sed -i 's/#*Color/Color/' "$USB_MOUNT/etc/pacman.conf"
  257.     msg2 'pacman color enabled'
  258. }
  259.  
  260. usb_config_grub() {
  261. # install and configure grub on USB mounted at USB_MOUNT:
  262.     local HOST1_MENU HOST1_UUID HOST2_MENU HOST2_UUID
  263.     msg 'Installing and configuring grub...'
  264.  
  265.     # install grub:
  266.     msg2 'pacstrap'
  267.     pacstrap -c "$USB_MOUNT" grub efibootmgr
  268.     arch-chroot "$USB_MOUNT" grub-install --target=i386-pc \
  269.         --boot-directory /boot "$USB_DEVICE"
  270.     arch-chroot "$USB_MOUNT" grub-install --target=x86_64-efi \
  271.         --efi-directory /ESP --boot-directory /boot --removable
  272.  
  273.     # set menu colors and timeout:
  274.     sed -i -e 's/#\(GRUB_COLOR_NORMAL\).*/\1="light-blue\/black"/' \
  275.         -e 's/#\(GRUB_COLOR_HIGHLIGHT\).*/\1="light-cyan\/black"/' \
  276.         -e 's/GRUB_TIMEOUT=.*/GRUB_TIMEOUT=2/' \
  277.         "$USB_MOUNT/etc/default/grub"
  278.     msg2 'grub menu colors set to light-cyan on light-blue'
  279.     msg2 'grub menu timeout set to 2 seconds'
  280.  
  281.     # disable submenus:
  282.     printf "\nGRUB_DISABLE_SUBMENU=y\n" >> "$USB_MOUNT/etc/default/grub"
  283.     msg2 'grub submenus disabled'
  284.  
  285.     # create custom grub menu entries in /etc/grub.d/40_custom:
  286.     arch-chroot "$USB_MOUNT" grub-mkconfig -o /boot/grub/grub.cfg
  287.     HOST1_UUID=$(lsblk -f | \
  288.         grep -Po "${USB_DEVICE##*/}${HOST1_PART}\s+ext4\s+\K[^\s]+")
  289.     HOST2_UUID=$(lsblk -f | \
  290.         grep -Po "${USB_DEVICE##*/}${HOST2_PART}\s+ext4\s+\K[^\s]+")
  291.     HOST1_MENU="$(sed -n "/^menuentry 'Arch/,/^}/p" \
  292.        "$USB_MOUNT/boot/grub/grub.cfg" | \
  293.        sed "s/, with Linux linux/ (${HOST1_NAME})/g")"
  294.    HOST2_MENU="$(sed -e "s/${HOST1_UUID}/${HOST2_UUID}/g" \
  295.        -e "s/${HOST1_NAME}/${HOST2_NAME}/g" <<<"$HOST1_MENU")"
  296.    { printf '%s\n\n%s\n\n' "$HOST1_MENU" "$HOST2_MENU"
  297.    printf 'if [ %s == "efi" ]; then\n' "\${grub_platform}"
  298.    printf '    menuentry "Firmware Setup" {\n'
  299.    printf '        fwsetup\n    }\nfi\n\n'
  300.    printf 'menuentry "Shutdown" {\n    halt\n}\n'
  301.        } >> "$USB_MOUNT/etc/grub.d/40_custom"
  302.    msg2 'custom grub menu entries created in /etc/grub.d/40_custom'
  303.  
  304.    # disable automatic grub menu entry generation for linux partitions:
  305.    arch-chroot "$USB_MOUNT" chmod -x /etc/grub.d/10_linux
  306.    msg2 'automatic menu entry generation for linux partitions disabled'
  307.  
  308.    # generate grub config:
  309.    arch-chroot "$USB_MOUNT" grub-mkconfig -o /boot/grub/grub.cfg
  310. }
  311.  
  312. usb_config_shim_signed() {
  313. # install and enable shim-signed boot loader:
  314.    msg 'Installing and enabling shim-signed...'
  315.    msg2 'pacstrap'
  316.    pacstrap -c "$USB_MOUNT" shim-signed
  317.    mv "$USB_MOUNT/ESP/EFI/BOOT/BOOTX64.efi" \
  318.        "$USB_MOUNT/ESP/EFI/BOOT/grubx64.efi"
  319.    cp "$USB_MOUNT/usr/share/shim-signed/shimx64.efi" \
  320.        "$USB_MOUNT/ESP/EFI/BOOT/BOOTX64.efi"
  321.    cp "$USB_MOUNT/usr/share/shim-signed/mmx64.efi" \
  322.        "$USB_MOUNT/ESP/EFI/BOOT/mmx64.efi"
  323. }
  324.  
  325. usb_config_large_memory_pages_enable() {
  326. # enable large memory pages:
  327.    sed -i 's/# End of file//' "$USB_MOUNT/etc/security/limits.conf"
  328.     { printf '*  soft  memlock  262144\n*  hard  memlock  262144\n'
  329.     printf '# End of file\n'
  330.         } >> "$USB_MOUNT/etc/security/limits.conf"
  331.     mkdir -p "$USB_MOUNT/etc/sysctl.d"
  332.     printf 'vm.nr_hugepages=128\n' \
  333.         > "$USB_MOUNT/etc/sysctl.d/99-sysctl.conf"
  334.     msg2 'large memory pages enabled'
  335. }
  336.  
  337. usb_config_sudo_lecture_disable() {
  338. # disable sudo lecture message:
  339.     printf 'Defaults lecture = never\n' \
  340.         > "$USB_MOUNT/etc/sudoers.d/01-neverlecture"
  341. }
  342.  
  343. usb_config_autologin_enable() {
  344. # enable autologin for USER_NAME on tty1:
  345.     local SYSTEM_DIR="$USB_MOUNT/etc/systemd/system"
  346.     mkdir -p "$SYSTEM_DIR/getty@tty1.service.d"
  347.     { printf '[Service]\nExecStart=\nExecStart=/usr/bin/agetty --auto'
  348.     printf 'login %s --noclear %s %s\n' "$USER_NAME" '%I' "\$TERM"
  349.         } >> "$SYSTEM_DIR/getty@tty1.service.d/override.conf"
  350. }
  351.  
  352. usb_config_capslock_to_escape() {
  353. # map Caps_Lock key to Escape key:
  354.     gunzip -c "$HOST_KEYMAP" | sed 's/Caps_Lock/Escape/' \
  355.         > "$USB_MOUNT/etc/vconsole.map"
  356.     printf 'KEYMAP=/etc/vconsole.map\n' > "$USB_MOUNT/etc/vconsole.conf"
  357. }
  358.  
  359. usb_issue_copy() {
  360. # copy custom /etc/issue:
  361.     if [ -f "$SCRIPT_DIR/.root/etc/issue_$HOST_NAME" ]; then
  362.         cp "$SCRIPT_DIR/.root/etc/issue_$HOST_NAME" \
  363.             "$USB_MOUNT/etc/issue"
  364.         msg2 'custom /etc/issue copied'
  365.     elif [ -f "$SCRIPT_DIR/.root/etc/issue" ]; then
  366.         cp "$SCRIPT_DIR/.root/etc/issue" "$USB_MOUNT/etc/issue"
  367.         msg2 'custom /etc/issue copied'
  368.     fi
  369. }
  370.  
  371. usb_root_copy() {
  372. # copy root home folder:
  373.     if [ -d "$SCRIPT_DIR/.root/root/" ]; then
  374.         rsync -a "$SCRIPT_DIR/.root/root/" "$USB_MOUNT/root/"
  375.         msg2 "root home folded copied from $SCRIPT_DIR/.root/root/"
  376.     fi
  377. }
  378.  
  379. usb_user_add() {
  380. # add USER_NAME and configure sudo NOPASSWD commands:
  381.     msg2 "useradd $USER_NAME"
  382.     arch-chroot "$USB_MOUNT" useradd -m "$USER_NAME" &>/dev/null
  383.     if [ -n "$USER_PASS" ]; then
  384.         printf "printf '%s\\\n%s\\\n' | passwd %s\n" \
  385.             "$USER_PASS" "$USER_PASS" "$USER_NAME" | \
  386.             arch-chroot "$USB_MOUNT"
  387.     else
  388.         arch-chroot "$USB_MOUNT" passwd "$USER_NAME"
  389.     fi
  390.  
  391.     # configure sudo user commands:
  392.     mkdir -p "$USB_MOUNT/etc/sudoers.d"
  393.     { printf '%s ALL=(ALL) ALL\n' "$USER_NAME"
  394.     if [ -n "${USER_COMMANDS_NOPASS[0]}" ]; then
  395.         printf '%s ALL=(ALL) NOPASSWD: ' "$USER_NAME"
  396.         printf '%s' "${USER_COMMANDS_NOPASS[0]}"
  397.         for CMD in "${USER_COMMANDS_NOPASS[@]:1}"; do
  398.             printf ', %s' "$CMD"
  399.         done
  400.         printf '\n'
  401.     fi ;} > "$USB_MOUNT/etc/sudoers.d/10-$USER_NAME"
  402.     msg2 "$USER_NAME sudo access configured"
  403. }
  404.  
  405. usb_user_copy() {
  406. # copy USER_NAME home folder:
  407.     if [ -d "$SCRIPT_DIR/$USER_NAME/" ]; then
  408.         rsync -a "$SCRIPT_DIR/$USER_NAME/" "$USB_MOUNT/home/$USER_NAME"
  409.         arch-chroot "$USB_MOUNT" chown -R \
  410.             "$USER_NAME:$USER_NAME" "/home/$USER_NAME"
  411.         msg2 "$USER_NAME files copied from $SCRIPT_DIR/$USER_NAME/"
  412.     elif [ -d "$SCRIPT_DIR/user/" ]; then
  413.         rsync -a "$SCRIPT_DIR/user/" "$USB_MOUNT/home/$USER_NAME"
  414.         arch-chroot "$USB_MOUNT" chown -R \
  415.             "$USER_NAME:$USER_NAME" "/home/$USER_NAME"
  416.         msg2 "$USER_NAME files copied from $SCRIPT_DIR/user/"
  417.     fi
  418. }
  419.  
  420. usb_user_repo_add() {
  421. # add local user repo to pacman.conf:
  422.     if [ -d "$USB_MOUNT/home/$USER_NAME/repo" ]; then
  423.         { printf '[%s-repo]\nSigLevel = Optional\n' "$USER_NAME"
  424.         printf 'Server = file:///home/%s/repo\n' "$USER_NAME"
  425.             } >> "$USB_MOUNT/etc/pacman.conf"
  426.         arch-chroot "$USB_MOUNT" pacman -Sy
  427.         msg2 "/home/$USER_NAME/repo added to /etc/pacman.conf"
  428.     fi
  429. }
  430.  
  431. usb_reboot_script_make() {
  432. # create post reboot script:
  433.     cat <<'REBOOT_SCRIPT' > "$USB_MOUNT/root/post-reboot.sh"
  434. #!/bin/bash
  435. C_BLUE_B=$'\e[1;38;5;27m'
  436. C_GREEN_B=$'\e[1;38;5;46m'
  437. C_RED_B=$'\e[1;38;5;196m'
  438. C_YELLOW_B=$'\e[1;33m'
  439. C_WHITE_B=$'\e[1;37m'
  440. C_GRAY=$'\e[0;37m'
  441. msg() {
  442.     printf '%s==>%s %s%s\n' "$C_GREEN_B" "$C_WHITE_B" "$1" "$C_GRAY"
  443. }
  444. msg_error() {
  445.     printf '%s==> ERROR:%s %s%s\n' "$C_RED_B" "$C_WHITE_B" \
  446.         "$1" "$C_GRAY"
  447. }
  448. msg_warn() {
  449.     printf '%s==> WARNING:%s %s%s\n' "$C_YELLOW_B" "$C_WHITE_B" \
  450.         "$1" "$C_GRAY"
  451. }
  452.  
  453. # setup ifplugd for automatic ethernet configuration:
  454. if (ip link | grep 'eth0:' &>/dev/null); then
  455.     cp /etc/netctl/examples/ethernet-dhcp "/etc/netctl/eth0-$HOSTNAME"
  456.     systemctl start netctl-ifplugd@eth0.service
  457.     systemctl enable netctl-ifplugd@eth0.service
  458.     msg 'automatic ethernet configuration enabled'
  459. else
  460.     msg_warn 'eth0 not found: automatic ethernet configuration disabled'
  461. fi
  462.  
  463. # start network time protocol:
  464. timedatectl set-ntp true
  465. msg 'network time sync enabled'
  466.  
  467. # configure sshd.socket service:
  468. mkdir -p /etc/systemd/system/sshd.socket.d/
  469. printf '[Socket]\nListenStream=\nListenStream=22322\n' \
  470.     > /etc/systemd/system/sshd.socket.d/override.conf
  471. printf 'Port 22322\nAllowUsers bot\nPermitRootLogin no\n' \
  472.     >> /etc/ssh/sshd_config
  473. systemctl start sshd.socket
  474. systemctl enable sshd.socket
  475. msg 'ssh server enabled on port 22322'
  476.  
  477. # enable tor:
  478. systemctl start tor.service
  479. systemctl enable tor.service
  480. msg 'tor service enabled'
  481. REBOOT_SCRIPT
  482.     chmod +x "$USB_MOUNT/root/post-reboot.sh"
  483.     msg_warn 'execute /root/post-reboot.sh after reboot'
  484. }
  485.  
  486. usb_mount() {
  487. # set $1=HOST_NAME; set HOST_PART, HOST_PKGS; mount HOST_NAME:
  488.     if [ "$1" = "$HOST1_NAME" ]; then
  489.         HOST_NAME="$HOST1_NAME"
  490.         HOST_PART="$HOST1_PART"
  491.         HOST_PKGS=("${HOST1_PKGS[@]}")
  492.     elif [ "$1" = "$HOST2_NAME" ]; then
  493.         HOST_NAME="$HOST2_NAME"
  494.         HOST_PART="$HOST2_PART"
  495.         HOST_PKGS=("${HOST2_PKGS[@]}")
  496.     elif [ -n "$1" ]; then
  497.         msg_error "hostname $1 in usb_mount() not found"
  498.         exit 3
  499.     else
  500.         msg_error 'no hostname supplied to usb_mount()'
  501.         exit 3
  502.     fi
  503.     # mount root:
  504.     mkdir -p "$USB_MOUNT"
  505.     mount "${USB_DEVICE}$HOST_PART" "$USB_MOUNT"
  506.     # mount ESP:
  507.     mkdir -p "$USB_MOUNT/ESP"
  508.     mount "${USB_DEVICE}2" "$USB_MOUNT/ESP"
  509.     # mount home:
  510.     mkdir -p "$USB_MOUNT/home"
  511.     mount "${USB_DEVICE}3" "$USB_MOUNT/home"
  512.     # bind-mount boot:
  513.     mkdir -p "$USB_MOUNT/boot"
  514.     mkdir -p "$USB_MOUNT/ESP/$HOST_NAME"
  515.     mount --bind "$USB_MOUNT/ESP/$HOST_NAME" "$USB_MOUNT/boot"
  516.     msg "${USB_DEVICE}$HOST_PART ${USB_DEVICE}2 ${USB_DEVICE}3 mounted"
  517. }
  518.  
  519. usb_umount() {
  520. # umount USB partitions:
  521.     local FLAG_UMOUNT='false'
  522.     if (mountpoint "$USB_MOUNT" &>/dev/null); then
  523.         printf '%s==>%s umount%s' "$C_GREEN_B" "$C_WHITE_B" "$C_GRAY"
  524.         FLAG_UMOUNT='true'
  525.     fi
  526.     # umount boot:
  527.     if (mountpoint "$USB_MOUNT/boot" &>/dev/null); then
  528.         umount "$USB_MOUNT/boot"
  529.         printf '%s %s/boot%s' "$C_WHITE_B" "$USB_MOUNT" "$C_GRAY"
  530.     fi
  531.     # umount ESP:
  532.     if (mountpoint "$USB_MOUNT/ESP" &>/dev/null); then
  533.         umount "$USB_MOUNT/ESP"
  534.         printf '%s %s/ESP%s' "$C_WHITE_B" "$USB_MOUNT" "$C_GRAY"
  535.     fi
  536.     # umount home:
  537.     if (mountpoint "$USB_MOUNT/home" &>/dev/null); then
  538.         umount "$USB_MOUNT/home"
  539.         printf '%s %s/home%s' "$C_WHITE_B" "$USB_MOUNT" "$C_GRAY"
  540.     fi
  541.     # umount root:
  542.     if (mountpoint "$USB_MOUNT" &>/dev/null); then
  543.         umount "$USB_MOUNT"
  544.         printf '%s %s%s' "$C_WHITE_B" "$USB_MOUNT" "$C_GRAY"
  545.     fi
  546.     if [ "$FLAG_UMOUNT" = 'true' ]; then
  547.         printf '\n'
  548.     fi
  549. }
  550.  
  551. usb_sync() {
  552. # sync USB memory:
  553.     msg "syncing $USB_DEVICE..."
  554.     sync
  555. }
  556.  
  557. mk-usb_kill() {
  558. # kill trap:
  559.     usb_umount
  560.     exit 3
  561. }
  562.  
  563. ##=============================  SCRIPT  =============================##
  564. # exit on error:
  565. set -e
  566.  
  567. # FAIL: must be run as root:
  568. if [ "$EUID" -ne 0 ]; then
  569.     msg_error 'must be run as root'
  570.     exit 2
  571. fi
  572.  
  573. # verify packages and settings:
  574. msg 'Verifying packages and settings...'
  575. USB_DEVICE="$1"
  576.  
  577. # FAIL: no argument given:
  578. if [ -z "$USB_DEVICE" ]; then
  579.     msg_error 'no argument given'
  580.     exit 1
  581. # FAIL: argument not a device:
  582. elif ! [[ "$USB_DEVICE" =~ ^/dev/sd[a-z]$ ]]; then
  583.     msg_error "not a device: $USB_DEVICE"
  584.     exit 1
  585. fi
  586.  
  587. # FAIL: invalid hostname HOST1_NAME:
  588. if ! [[ "$HOST1_NAME" =~ ^[a-z0-9\-]+$ ]]; then
  589.     msg_error "invalid hostname: $HOST1_NAME"
  590.     exit 1
  591. fi
  592.  
  593. # FAIL: invalid hostname HOST2_NAME:
  594. if ! [[ "$HOST2_NAME" =~ ^[a-z0-9\-]+$ ]]; then
  595.     msg_error "invalid hostname: $HOST2_NAME"
  596.     exit 1
  597. fi
  598.  
  599. # FAIL: hostnames not unique:
  600. if [ "$HOST1_NAME" = "$HOST2_NAME" ]; then
  601.     msg_error "non-unique hostnames"
  602.     exit 1
  603. fi
  604.  
  605. # FAIL: invalid language HOST_LANG:
  606. if ! (grep -P "^\s?#?\s?$HOST_LANG" /etc/locale.gen &>/dev/null); then
  607.     msg_error "invalid language: $HOST_LANG"
  608.     exit 1
  609. fi
  610.  
  611. # FAIL: invalid time zone HOST_TIMEZONE:
  612. if ! [ -f "/usr/share/zoneinfo/$HOST_TIMEZONE" ]; then
  613.     msg_error "invalid time zone: $HOST_TIMEZONE"
  614.     exit 1
  615. fi
  616.  
  617. # FAIL: invalid username USER_NAME:
  618. if ! [[ "$USER_NAME" =~ ^[A-Za-z0-9\-]+$ ]]; then
  619.     msg_error "invalid username: $USER_NAME"
  620.     exit 1
  621. fi
  622.  
  623. # FAIL: keymap not valid:
  624. if [ -n "$HOST_KEYMAP" ] && [ ! -f "$HOST_KEYMAP" ]; then
  625.     msg_error "invalid keymap: $HOST_KEYMAP"
  626. elif [ -z "$HOST_KEYMAP" ]; then
  627.     HOST_KEYMAP='/usr/share/kbd/keymaps/i386/qwerty/us.map.gz'
  628. fi
  629.  
  630. # FAIL: device mounted:
  631. if (grep "$USB_DEVICE" /proc/mounts &>/dev/null); then
  632.     msg_error "already mounted: $USB_DEVICE"
  633.     exit 1
  634. fi
  635.  
  636. # FAIL: no internet connection:
  637. if ! (internet_status); then
  638.     msg_error 'no internet connection'
  639.     exit 1
  640. fi
  641.  
  642. # FAIL: mount point not clear:
  643. if (mountpoint "$USB_MOUNT" &>/dev/null) || \
  644. { [ -d "$USB_MOUNT" ] && [ -n "$(ls -A "$USB_MOUNT")" ]; }; then
  645.     msg_error "$USB_MOUNT not clear"
  646.     exit 1
  647. fi
  648.  
  649. # get USB device info:
  650. USB_INFO="$(fdisk --color=never -l "$USB_DEVICE")"
  651. USB_SIZE_BYTES="$(grep -Po "$USB_DEVICE:.+ \K[0-9]+(?= bytes)" \
  652.     <<< "$USB_INFO")"
  653. USB_SIZE_HUMAN="$(grep -Po "$USB_DEVICE: *\K[^,]+" <<< "$USB_INFO")"
  654. USB_BLOCK_SIZE="$(grep -Po "Sector size.* \K[0-9]+" <<< "$USB_INFO")"
  655. USB_BLOCK_COUNT=$((USB_SIZE_BYTES / USB_BLOCK_SIZE))
  656.  
  657. # FAIL: USB not big enough:
  658. if [ "$USB_SIZE_BYTES" -lt 15560000000 ]; then
  659.    msg_error 'USB must be at least 14.5G'
  660.    exit 1
  661. fi
  662.  
  663. # FAIL: package not found:
  664. for PKG in $(printf '%s\n%s\n' "${HOST1_PKGS[@]}" "${HOST2_PKGS[@]}" | \
  665. sort -u); do if ! (pacman -Si "$PKG" &>/dev/null) && \
  666. ! (pacman -Sg "$PKG" &>/dev/null); then
  667.    msg_error "package not found: $PKG"
  668.    exit 1
  669. fi; done
  670.  
  671. # warn: all data will be lost:
  672. msg_warn "All data on $USB_DEVICE ($USB_SIZE_HUMAN) will be lost."
  673. msg_ask 'Proceed with installation? [Y/n]'
  674. read -r ANSWER
  675. if [[ "${ANSWER,,}" =~ ^(n|no)$ ]]; then
  676.    exit 0
  677. fi
  678.  
  679. # zero out USB:
  680. msg_ask 'Zero out USB? [Y/n]'
  681. read -r ANSWER
  682. if ! [[ "${ANSWER,,}" =~ ^(n|no)$ ]]; then
  683.    dd status=progress if=/dev/zero of="$USB_DEVICE" \
  684.        bs="$USB_BLOCK_SIZE" count="$USB_BLOCK_COUNT"
  685. fi
  686.  
  687. # partition USB:
  688. msg 'Partitioning USB...'
  689. sgdisk -o \
  690.    -n 1:0:+10M -t 1:EF02 \
  691.    -n 2:0:+500M -t 2:EF00 \
  692.    -n "3:0:+$HOME_SIZE" -t 3:8300 \
  693.    -n "${HOST1_PART}:0:+$HOST1_SIZE" -t 3:8300 \
  694.    -n "${HOST2_PART}:0:-5M" -t 4:8300 \
  695.    "$USB_DEVICE"
  696.  
  697. # format partitions:
  698. msg 'Formatting partitions...'
  699. mkfs.fat -F32 "${USB_DEVICE}2"
  700. mkfs.ext4 -F "${USB_DEVICE}3"
  701. mkfs.ext4 -F "${USB_DEVICE}4"
  702. mkfs.ext4 -F "${USB_DEVICE}5"
  703.  
  704. # set kill umount trap:
  705. trap mk-usb_kill ABRT EXIT HUP ILL INT QUIT SYS TERM
  706.  
  707. # HOST2:
  708. usb_mount "$HOST2_NAME"
  709. usb_pacstrap
  710. usb_config
  711. usb_config_capslock_to_escape
  712. usb_config_large_memory_pages_enable
  713. usb_config_autologin_enable
  714. usb_config_sudo_lecture_disable
  715. usb_issue_copy
  716. usb_root_copy
  717. usb_user_add
  718. usb_user_copy
  719. usb_user_repo_add
  720. usb_reboot_script_make
  721. usb_umount
  722. usb_sync
  723.  
  724. # HOST1: installed second to handle grub configuration:
  725. usb_mount "$HOST1_NAME"
  726. usb_pacstrap
  727. usb_config
  728. usb_config_grub
  729. usb_config_shim_signed
  730. usb_config_capslock_to_escape
  731. usb_config_large_memory_pages_enable
  732. usb_config_autologin_enable
  733. usb_config_sudo_lecture_disable
  734. usb_issue_copy
  735. usb_root_copy
  736. usb_user_add
  737. usb_user_repo_add
  738. usb_reboot_script_make
  739. usb_umount
  740. usb_sync
  741.  
  742. msg "$USB_DEVICE Arch Linux install complete"
  743. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement