319 lines
13 KiB
Bash
Executable File
319 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# exit on error; treat unset variables as errors; exit on errors in piped commands
|
|
set -euo pipefail
|
|
|
|
# Ensure we operate from consistent pwd for the rest of the script
|
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # Figure out the ABSOLUTE PATH of this script without relying on the realpath command, which may not always be available
|
|
cd "$DIR"
|
|
|
|
if [ "$OSTYPE" == "linux-gnu" ]; then
|
|
MOUNTED_BOOT_VOLUME="/media/$(whoami)/boot" # i.e. under which name is the SD card mounted under /media in Linux (Ubuntu)
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
MOUNTED_BOOT_VOLUME="/Volumes/boot" # i.e. under which name is the SD card mounted under /Volumes on macOS
|
|
else
|
|
echo "Error: Unsupported platform $OSTYPE, sorry" && exit 1
|
|
fi
|
|
|
|
BOOT_CMDLINE_TXT="$MOUNTED_BOOT_VOLUME/cmdline.txt"
|
|
BOOT_CONFIG_TXT="$MOUNTED_BOOT_VOLUME/config.txt"
|
|
SD_SIZE_REAL=2500 # this is in MB
|
|
SD_SIZE_SAFE=2800 # this is in MB
|
|
SD_SIZE_ZERO=3200 # this is in MB
|
|
SSH_PUBKEY="$(cat ~/.ssh/id_rsa.pub)"
|
|
SSH_CONNECT_TIMEOUT=30
|
|
LOCALE="en_US.UTF-8 UTF-8" # or e.g. "fi_FI.UTF-8 UTF-8" for Finland
|
|
LANGUAGE="en_US.UTF-8" # should match above
|
|
KEYBOARD="us" # or e.g. "fi" for Finnish
|
|
TIMEZONE="Etc/UTC" # or e.g. "Europe/Helsinki"; see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
|
|
|
function echo-bold {
|
|
echo -e "$(tput -Txterm-256color bold)$1$(tput -Txterm-256color sgr 0)" # https://unix.stackexchange.com/a/269085; the -T arg accounts for $ENV not being set
|
|
}
|
|
function working {
|
|
echo-bold "\n[WORKING] $1"
|
|
}
|
|
function question {
|
|
echo-bold "\n[QUESTION] $1"
|
|
}
|
|
function ssh {
|
|
/usr/bin/ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout="$SSH_CONNECT_TIMEOUT" "pi@$IP" "$1"
|
|
}
|
|
function scp {
|
|
/usr/bin/scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$@" "pi@$IP:/home/pi"
|
|
}
|
|
function figureOutSdCard {
|
|
if [ "$OSTYPE" == "linux-gnu" ]; then
|
|
lsblk --fs
|
|
DISK="/dev/$(lsblk -l | grep "$MOUNTED_BOOT_VOLUME" | sed 's/[0-9].*//')"
|
|
DISK_SAMPLE="/dev/sda"
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
diskutil list
|
|
DISK="$(diskutil list | grep /dev/ | grep external | grep physical | cut -d ' ' -f 1 | head -n 1)"
|
|
DISK_SAMPLE="/dev/disk2"
|
|
else
|
|
echo "Error: Unsupported platform $OSTYPE, sorry" && exit 1
|
|
fi
|
|
}
|
|
function unmountSdCard {
|
|
if [ "$OSTYPE" == "linux-gnu" ]; then
|
|
for part in $(lsblk --list "$DISK" | grep part | sed 's/ .*//'); do
|
|
udisksctl unmount -b "/dev/$part"
|
|
done
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
diskutil unmountDisk "$DISK"
|
|
else
|
|
echo "Error: Unsupported platform $OSTYPE, sorry" && exit 1
|
|
fi
|
|
}
|
|
|
|
question "Enter version (e.g. \"1.2.3\") being built"
|
|
echo "Omit the \"v\" prefix, it'll be added where needed"
|
|
echo "For alpha/beta builds, use a \"-betaN\" suffic"
|
|
echo "For RC builds, DO NOT use any suffix, as then the same image can't be promoted to stable without rebuilding"
|
|
echo "Enter version:"
|
|
read TAG
|
|
|
|
working "Updating version file"
|
|
echo -e "$TAG\n\nhttps://github.com/futurice/chilipie-kiosk" > ../home/.chilipie-kiosk-version
|
|
|
|
working "Generating first-boot.html"
|
|
if [ ! -d "node_modules" ]; then
|
|
npm install markdown-styles@3.1.10 html-inline@1.2.0
|
|
fi
|
|
rm -rf md-input md-output
|
|
mkdir md-input md-output
|
|
cp ../docs/first-boot.md md-input
|
|
./node_modules/.bin/generate-md --layout github --input md-input/ --output md-output/
|
|
./node_modules/.bin/html-inline -i md-output/first-boot.html > ../home/first-boot.html
|
|
rm -rf md-input md-output
|
|
|
|
question "Physically mount the SD card to this machine "
|
|
echo "(press enter when ready)"
|
|
read
|
|
|
|
working "Figuring out SD card device"
|
|
figureOutSdCard
|
|
|
|
question "Based on the above, SD card determined to be \"$DISK\""
|
|
echo "Should be e.g. \"$DISK_SAMPLE\""
|
|
echo "(press enter to confirm)"
|
|
read
|
|
|
|
working "Safely unmounting the card"
|
|
unmountSdCard
|
|
|
|
working "Writing the card full of zeros"
|
|
# ...for security and compressibility reasons
|
|
echo "This may take a long time"
|
|
echo "You may be prompted for your password by sudo"
|
|
if [ "$OSTYPE" == "linux-gnu" ]; then
|
|
sudo dd bs=1M count="$SD_SIZE_ZERO" if=/dev/zero of="$DISK" status=progress
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
sudo dd bs=1m count="$SD_SIZE_ZERO" if=/dev/zero of="$DISK"
|
|
else
|
|
echo "Error: Unsupported platform $OSTYPE, sorry" && exit 1
|
|
fi
|
|
|
|
question "Prepare baseline Raspbian:"
|
|
echo "* Flash Raspbian Lite with Etcher"
|
|
echo "* Eject the SD card"
|
|
echo "* Mount the card back"
|
|
echo "* Wait for your OS to mount it"
|
|
echo "(press enter when ready)"
|
|
read
|
|
|
|
working "Backing up original boot files"
|
|
cp -v "$BOOT_CMDLINE_TXT" "$BOOT_CMDLINE_TXT.backup"
|
|
cp -v "$BOOT_CONFIG_TXT" "$BOOT_CONFIG_TXT.backup"
|
|
|
|
working "Disabling automatic root filesystem expansion"
|
|
echo "Updating: $BOOT_CMDLINE_TXT"
|
|
cat "$BOOT_CMDLINE_TXT" | sed "s#init=/usr/lib/raspi-config/init_resize.sh##" > temp
|
|
mv temp "$BOOT_CMDLINE_TXT"
|
|
|
|
working "Enabling SSH for first boot"
|
|
# https://www.raspberrypi.org/documentation/remote-access/ssh/
|
|
touch "$MOUNTED_BOOT_VOLUME/ssh"
|
|
|
|
working "Safely unmounting the card"
|
|
unmountSdCard
|
|
|
|
question "Do initial Pi setup:"
|
|
echo "* Eject the card"
|
|
echo "* Connect your Pi to Ethernet"
|
|
echo "* Boot the Pi from your card"
|
|
echo "* Make note of the \"My IP address is\" message at the end of boot"
|
|
echo "Enter the IP address:"
|
|
read IP
|
|
|
|
working "Installing temporary SSH pubkey"
|
|
echo -e "Password hint: \"raspberry\""
|
|
ssh "mkdir .ssh && echo '$SSH_PUBKEY' > .ssh/authorized_keys"
|
|
|
|
working "Figuring out partition start"
|
|
ssh "echo -e 'p\nq\n' | sudo fdisk /dev/mmcblk0 | grep /dev/mmcblk0p2 | tr -s ' ' | cut -d ' ' -f 2" > temp
|
|
START="$(cat temp)"
|
|
rm temp
|
|
|
|
question "Partition start determined to be \"$START\""
|
|
echo "Should be e.g. \"98304\""
|
|
echo "(press enter to confirm)"
|
|
read
|
|
|
|
working "Resizing the root partition"
|
|
ssh "echo -e 'd\n2\nn\np\n2\n$START\n+${SD_SIZE_REAL}M\ny\nw\n' | sudo fdisk /dev/mmcblk0"
|
|
|
|
working "Setting locale"
|
|
# We want to do this as early as possible, so perl et al won't complain about misconfigured locales for the rest of the image prep
|
|
ssh "echo $LOCALE | sudo tee /etc/locale.gen"
|
|
ssh "sudo locale-gen"
|
|
ssh "echo -e \"LANGUAGE=$LANGUAGE\nLC_ALL=$LANGUAGE\" | sudo tee /etc/environment"
|
|
|
|
working "Setting hostname"
|
|
# We want to do this right before reboot, so we don't get a lot of unnecessary complaints about "sudo: unable to resolve host chilipie-kiosk" (https://askubuntu.com/a/59517)
|
|
ssh "sudo hostnamectl set-hostname chilipie-kiosk"
|
|
ssh "sudo perl -i -p0e 's/raspberrypi/chilipie-kiosk/g' /etc/hosts" # "perl" is more cross-platform than "sed -i"
|
|
|
|
# From now on, some ssh commands will exit non-0, which should be fine
|
|
set +e
|
|
|
|
working "Rebooting the Pi"
|
|
ssh "sudo reboot"
|
|
|
|
echo "Waiting for host to come back up..."
|
|
until SSH_CONNECT_TIMEOUT=5 ssh "echo OK"
|
|
do
|
|
sleep 1
|
|
done
|
|
|
|
working "Finishing the root partition resize"
|
|
ssh "df -h . && sudo resize2fs /dev/mmcblk0p2 && df -h ."
|
|
|
|
# From raspi-config: https://github.com/RPi-Distro/raspi-config/blob/d98686647ced7c0c0490dc123432834735d1c13d/raspi-config#L1313-L1321
|
|
# See also: https://github.com/futurice/chilipie-kiosk/issues/61#issuecomment-524622522
|
|
working "Enabling auto-login to CLI"
|
|
ssh "sudo systemctl set-default multi-user.target"
|
|
ssh "sudo ln -fs /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty1.service"
|
|
ssh "sudo ln -fs /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty2.service"
|
|
ssh "sudo ln -fs /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty3.service"
|
|
ssh "sudo mkdir -p /etc/systemd/system/getty@tty1.service.d"
|
|
ssh "sudo mkdir -p /etc/systemd/system/getty@tty2.service.d"
|
|
ssh "sudo mkdir -p /etc/systemd/system/getty@tty3.service.d"
|
|
ssh "echo -e '[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin pi --noclear %I \$TERM\n' | sudo tee /etc/systemd/system/getty@tty1.service.d/autologin.conf"
|
|
ssh "echo -e '[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin pi --noclear %I \$TERM\n' | sudo tee /etc/systemd/system/getty@tty2.service.d/autologin.conf"
|
|
ssh "echo -e '[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin pi --noclear %I \$TERM\n' | sudo tee /etc/systemd/system/getty@tty3.service.d/autologin.conf"
|
|
|
|
working "Setting timezone"
|
|
ssh "(echo '$TIMEZONE' | sudo tee /etc/timezone) && sudo dpkg-reconfigure --frontend noninteractive tzdata"
|
|
|
|
working "Setting keyboard layout"
|
|
ssh "(echo -e 'XKBMODEL="pc105"\nXKBLAYOUT="$KEYBOARD"\nXKBVARIANT=""\nXKBOPTIONS=""\nBACKSPACE="guess"\n' | sudo tee /etc/default/keyboard) && sudo dpkg-reconfigure --frontend noninteractive keyboard-configuration"
|
|
|
|
working "Silencing console logins" # this is to avoid a brief flash of the console login before X comes up
|
|
ssh "sudo rm /etc/profile.d/sshpwd.sh /etc/profile.d/wifi-check.sh" # remove warnings about default password and WiFi country (https://raspberrypi.stackexchange.com/a/105234)
|
|
ssh "touch .hushlogin" # https://scribles.net/silent-boot-on-raspbian-stretch-in-console-mode/
|
|
ssh "sudo perl -i -p0e 's#--autologin pi#--skip-login --noissue --login-options \"-f pi\"#g' /etc/systemd/system/getty@tty1.service.d/autologin.conf" # "perl" is more cross-platform than "sed -i"
|
|
|
|
working "Installing packages"
|
|
ssh "sudo apt-get update && DEBIAN_FRONTEND=noninteractive sudo apt-get install -y vim matchbox-window-manager unclutter mailutils nitrogen jq chromium-browser xserver-xorg xinit rpd-plym-splash xdotool rng-tools xinput-calibrator cec-utils"
|
|
# We install mailutils just so that you can check "mail" for cronjob output
|
|
|
|
working "Setting home directory default content"
|
|
ssh "rm -rfv /home/pi/*"
|
|
scp $(find ../home -type f)
|
|
|
|
working "Setting splash screen background"
|
|
ssh "sudo rm /usr/share/plymouth/themes/pix/splash.png && sudo ln -s /home/pi/background.png /usr/share/plymouth/themes/pix/splash.png"
|
|
|
|
working "Installing default crontab"
|
|
ssh "crontab /home/pi/crontab.example"
|
|
|
|
working "Rebooting the Pi"
|
|
ssh "sudo reboot"
|
|
|
|
question "Once the Pi has rebooted into Chromium:"
|
|
echo "* Tell Chromium we don't want to sign in"
|
|
echo "* Configure Chromium to start \"where you left off\""
|
|
echo " * F11 to exit full screen"
|
|
echo " * Alt + F, then S to go to Settings"
|
|
echo " * Type \"continue\" to filter the options"
|
|
echo " * Tab to select \"Continue where you left off\""
|
|
echo "* Navigate to \"file:///home/pi/first-boot.html\""
|
|
echo "(press enter when ready)"
|
|
read
|
|
|
|
working "Figuring out software versions"
|
|
ssh "hostnamectl | grep 'Operating System:' | tr -s ' ' | cut -d ' ' -f 4-" > temp
|
|
VERSION_LINUX="$(cat temp)"
|
|
ssh "hostnamectl | grep 'Kernel:' | tr -s ' ' | cut -d ' ' -f 3-4" > temp
|
|
VERSION_KERNEL="$(cat temp)"
|
|
ssh "chromium-browser --version | cut -d ' ' -f 1-2" > temp
|
|
VERSION_CHROMIUM="$(cat temp)"
|
|
rm temp
|
|
|
|
working "Removing SSH host keys & enabling regeneration"
|
|
ssh "sudo rm -f -v /etc/ssh/ssh_host_*_key* && sudo systemctl enable regenerate_ssh_host_keys"
|
|
|
|
working "Removing temporary SSH pubkey, disabling SSH & shutting down"
|
|
tempFile="$(ssh mktemp)"
|
|
ssh "chmod a+x $tempFile"
|
|
ssh "echo 'rm .ssh/authorized_keys && systemctl disable ssh && poweroff' > $tempFile"
|
|
ssh "sudo nohup $tempFile"
|
|
|
|
question "Eject the SD card from the Pi, and mount it back to this computer"
|
|
echo "(press enter when ready)"
|
|
read
|
|
|
|
# We do this again now just to be safe
|
|
working "Figuring out SD card device"
|
|
figureOutSdCard
|
|
|
|
question "Based on the above, SD card determined to be \"$DISK\""
|
|
echo "Should be e.g. \"$DISK_SAMPLE\""
|
|
echo "(press enter to confirm)"
|
|
read
|
|
|
|
working "Making boot quieter (part 1)" # https://scribles.net/customizing-boot-up-screen-on-raspberry-pi/
|
|
echo "Updating: $BOOT_CONFIG_TXT"
|
|
perl -i -p0e "s/#disable_overscan=1/disable_overscan=1/g" "$BOOT_CONFIG_TXT" # "perl" is more cross-platform than "sed -i"
|
|
echo -e "\ndisable_splash=1" >> "$BOOT_CONFIG_TXT"
|
|
|
|
working "Making boot quieter (part 2)" # https://scribles.net/customizing-boot-up-screen-on-raspberry-pi/
|
|
echo "You may want to revert these changes if you ever need to debug the startup process"
|
|
echo "Updating: $BOOT_CMDLINE_TXT"
|
|
cat "$BOOT_CMDLINE_TXT" \
|
|
| sed 's/console=tty1/console=tty3/' \
|
|
| sed 's/$/ splash plymouth.ignore-serial-consoles logo.nologo vt.global_cursor_default=0/' \
|
|
> temp
|
|
mv temp "$BOOT_CMDLINE_TXT"
|
|
|
|
working "Safely unmounting the card"
|
|
unmountSdCard
|
|
|
|
working "Dumping the image from the card"
|
|
cd ..
|
|
echo "This may take a long time"
|
|
echo "You may be prompted for your password by sudo"
|
|
if [ "$OSTYPE" == "linux-gnu" ]; then
|
|
sudo dd bs=1M count="$SD_SIZE_ZERO" if="$DISK" of="chilipie-kiosk-$TAG.img" status=progress
|
|
elif [[ "$OSTYPE" == darwin* ]]; then
|
|
sudo dd bs=1m count="$SD_SIZE_ZERO" if="$DISK" of="chilipie-kiosk-$TAG.img"
|
|
else
|
|
echo "Error: Unsupported platform $OSTYPE, sorry" && exit 1
|
|
fi
|
|
|
|
working "Compressing image"
|
|
COPYFILE_DISABLE=1 tar -zcvf chilipie-kiosk-$TAG.img.tar.gz chilipie-kiosk-$TAG.img
|
|
|
|
working "Listing image sizes"
|
|
du -hs chilipie-kiosk-$TAG.img*
|
|
|
|
working "Calculating image hashes"
|
|
openssl sha1 chilipie-kiosk-$TAG.img*
|
|
|
|
working "Software versions are:"
|
|
echo "* Linux: \`$VERSION_LINUX\`"
|
|
echo "* Kernel: \`$VERSION_KERNEL\`"
|
|
echo "* Chromium: \`$VERSION_CHROMIUM\`"
|