Created
May 22, 2019 16:50
-
-
Save Tschrock/5b9cd3f8af550b289205c49a33e51eff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Usage: | |
# sudo ./modify_rpi_disk_image.sh raspbian-stretch-lite.img | |
function exit_with_error { | |
echo "Error: $2" | |
exit $1 | |
} | |
function mount_raspbian_partitions { | |
device="$1" | |
boot_mount_location="$2" | |
root_mount_location="$3" | |
# Glob for any partitions | |
partitions=("$device"p*) | |
# Check if we got any partitions | |
if [[ "${partitions[0]}" == "$device"p"*" ]]; then | |
echo "Error: Could not find any partitions on device \"$device\"." | |
return 1 | |
fi | |
# Check that we have 2 partitions | |
partition_count="${#partitions[@]}" | |
if [[ "$partition_count" < "2" ]]; then | |
echo "Error: Expected 2 partitions on device \"$device\" (device has $partition_count)" | |
return 1 | |
fi | |
# Seperate the two partitions | |
img_loopback_boot="${partitions[0]}" | |
img_loopback_root="${partitions[1]}" | |
# Mount the boot partition | |
mount "$img_loopback_boot" "$boot_mount_location" | |
img_boot_mount_exit="$?" | |
# Check for success | |
[ "$img_boot_mount_exit" != "0" ] && return $img_boot_mount_exit | |
# Mount the boot partition | |
mount "$img_loopback_root" "$root_mount_location" | |
img_boot_mount_exit="$?" | |
# Check for success | |
if [[ "$img_boot_mount_exit" != "0" ]]; then | |
umount "$boot_mount_location" | |
return $img_boot_mount_exit | |
fi | |
return 0 | |
} | |
function mount_image_file { | |
img_file_location="$1" | |
boot_mount_location="$2" | |
root_mount_location="$3" | |
# Make sure it exists | |
[ ! -e "$img_file_location" ] && exit_with_error 1 "Image file \"$img_file_location\" does not exist." | |
# Set up the loop device | |
img_loopback_device="$(losetup --show -f -P "$img_file_location")" | |
img_losetup_exit="$?" | |
# Bail if it wasn't successful | |
if [[ "$img_losetup_exit" != "0" ]]; then | |
echo "$Error mounting img file \"$img_file_location\":" | |
echo "$img_loopback_device" | |
# Pass on the error code | |
return "$img_losetup_exit" | |
fi | |
# Mount the partitions | |
mount_raspbian_partitions "$img_loopback_device" "$boot_mount_location" "$root_mount_location" | |
img_mount_exit="$?" | |
# Bail if it wasn't successful | |
if [[ "$img_mount_exit" != "0" ]]; then | |
# Undo the loopback mount | |
losetup -d "$img_loopback_device" | |
# Pass on the error code | |
return "$img_mount_exit" | |
fi | |
echo "$img_loopback_device" | |
} | |
function convert_hex_to_background { | |
background_hex="$1" | |
dec_r=0 | |
dec_g=0 | |
dec_b=0 | |
if [[ ! -z "$background_hex" ]]; then | |
hex_chars=$(echo "${background_hex^^}" | sed 's/#//g') | |
hex_r=$(echo "$hex_chars" | cut -c-2) | |
hex_g=$(echo "$hex_chars" | cut -c3-4) | |
hex_b=$(echo "$hex_chars" | cut -c5-6) | |
dec_r=$(echo "ibase=16;scale=2; $hex_r/FF" | bc | sed -r 's/^(-?)\./\10./') | |
dec_g=$(echo "ibase=16;scale=2; $hex_g/FF" | bc | sed -r 's/^(-?)\./\10./') | |
dec_b=$(echo "ibase=16;scale=2; $hex_b/FF" | bc | sed -r 's/^(-?)\./\10./') | |
fi | |
echo "$dec_r, $dec_g, $dec_b" | |
} | |
function write_plymouth_theme_file { | |
themes_root="$1" | |
theme_name="$2" | |
theme_description="$3" | |
cat <<EOF > "$themes_root/$theme_name/$theme_name.plymouth" | |
[Plymouth Theme] | |
Name=$theme_name | |
Description=$theme_description | |
ModuleName=script | |
[script] | |
ImageDir=/usr/share/plymouth/themes/$theme_name | |
ScriptFile=/usr/share/plymouth/themes/$theme_name/$theme_name.script | |
EOF | |
} | |
function write_plymouth_script_file { | |
themes_root="$1" | |
theme_name="$2" | |
image_filename="$3" | |
background_hex="$4" | |
background="$(convert_hex_to_background "$background_hex")" | |
cat <<EOF > "$themes_root/$theme_name/$theme_name.script" | |
# ========== Background Color ========== # | |
Window.SetBackgroundTopColor ($background); | |
Window.SetBackgroundBottomColor ($background); | |
# ============ Centered Logo =========== # | |
logo = Image("$image_filename"); | |
logo_width = logo.GetWidth(); | |
logo_height = logo.GetHeight(); | |
screen_width = Window.GetWidth(); | |
screen_height = Window.GetHeight(); | |
scale = Math.Min( Math.Min( ( screen_width / 4 ) / logo_width, ( screen_height / 4 ) / logo_height ), 1); | |
logo_width = logo_width * scale; | |
logo_height = logo_height * scale; | |
logo = logo.Scale(logo_width, logo_height); | |
logo_sprite = Sprite( logo ); | |
logo_sprite.SetPosition( ( screen_width - logo_width ) / 2, (screen_height - logo_height) / 2, -100); | |
EOF | |
} | |
function create_splashscreen_theme { | |
filesystem_root="$1" | |
theme_name="$2" | |
theme_description="$3" | |
image_path="$4" | |
background_hex="$5" | |
image_name="$(basename "$image_path")" | |
themes_root="$filesystem_root/usr/share/plymouth/themes" | |
# Make sure the theme directory exists | |
mkdir -p "$themes_root/$theme_name" | |
# Write the theme file | |
write_plymouth_theme_file "$themes_root" "$theme_name" "$theme_description" | |
# Write the theme file | |
write_plymouth_script_file "$themes_root" "$theme_name" "$image_name" "$background_hex" | |
# Copy the image | |
cp "$image_path" "$themes_root/$theme_name/$image_name" | |
chmod 644 "$themes_root/$theme_name/$image_name" | |
} | |
function append_or_uncomment { | |
if grep -qG -- "^#$1$" "$2"; then | |
sed -i "s/^#$1$/$1/g" "$2"; | |
else | |
grep -qG -- "^$1$" "$2" || echo "$1" >> "$2" | |
fi | |
} | |
function disable_overscan { | |
filesystem_boot="$1" | |
append_or_uncomment "disable_overscan=1" "$filesystem_boot/config.txt" | |
} | |
function enable_splashscreen_theme { | |
filesystem_boot="$1" | |
filesystem_root="$2" | |
theme_name="$3" | |
# Modify the boot command line | |
sed -i "s/console=tty1/console=tty3 splash quiet plymouth.ignore-serial-consoles logo.nologo vt.global_cursor_default=0/g" "$filesystem_boot/cmdline.txt" | |
# Modify the boot config | |
append_or_uncomment "disable_splash=1" "$filesystem_boot/config.txt" | |
# Write the Plymouth theme config | |
cat <<EOF > "$filesystem_root/etc/plymouth/plymouthd.conf" | |
[Daemon] | |
Theme=$theme_name | |
EOF | |
} | |
function set_keyboard_language { | |
filesystem_root="$1" | |
language="$2" | |
sed -i "s/XKBLAYOUT=.*/XKBLAYOUT=\"$language\"/g" "$filesystem_root/etc/default/keyboard" | |
} | |
function add_wifi_network { | |
filesystem_root="$1" | |
wifi_ssid="$2" | |
wifi_password="$3" | |
wpa_passphrase "$wifi_ssid" "$wifi_password" | grep -vF "#psk=" >> "$filesystem_root/etc/wpa_supplicant/wpa_supplicant.conf" | |
} | |
function run_as_pi { | |
filesystem_root="$1" | |
command="$2" | |
proot -q qemu-arm -S "$filesystem_root" $command | |
} | |
function enable_auto_login { | |
filesystem_root="$1" | |
tty="$2" | |
username="$3" | |
run_as_pi "$filesystem_root" 'systemctl set-default multi-user.target' | |
run_as_pi "$filesystem_root" "ln -fs /lib/systemd/system/[email protected] /etc/systemd/system/getty.target.wants/getty@$tty.service" | |
cat > "$filesystem_root/etc/systemd/system/getty@$tty.service.d/autologin.conf" << EOF | |
[Service] | |
ExecStart= | |
ExecStart=-/sbin/agetty --skip-login --noclear --noissue --login-options "-f $username" %I \$TERM | |
EOF | |
} | |
img_file_location="$1" | |
# Make sure we were given an image file | |
[ -z "$img_file_location" ] && exit_with_error 1 "No image file specified." | |
# Make sure we have the right permissions | |
[ "$EUID" -ne "0" ] && exit_with_error 1 "This script must be run as root so it can mount the image and write in it's boot/root filesystems." | |
# Mount stuff | |
echo "Mounting image filesystems" | |
# Make some temp directories for our mounts | |
boot_mountpoint=$(mktemp -d) | |
root_mountpoint=$(mktemp -d) | |
# Mount the image file | |
img_loopback_device="$(mount_image_file "$img_file_location" "$boot_mountpoint" "$root_mountpoint")" | |
mount_image_file_exit="$?" | |
[ "$mount_image_file_exit" != "0" ] && exit_with_error "$mount_image_file_exit" "$img_loopback_device" | |
echo "Making modifications" | |
# Set the leyboard language | |
echo "Setting keyboard to \"us\"" | |
set_keyboard_language "$root_mountpoint" "us" | |
# Create a new splashscreen theme | |
create_splashscreen_theme "$root_mountpoint" "kiosk-blue" "Custom Theme" "./icon-white.png" "#6666ff" | |
# Enable the theme | |
enable_splashscreen_theme "$boot_mountpoint" "$root_mountpoint" "kiosk-blue" | |
# Disable overscan | |
disable_overscan "$boot_mountpoint" | |
# Add Wifi | |
add_wifi_network "$root_mountpoint" "Test Wifi" "123456789" | |
# Install Xorg + Chromium | |
run_as_pi "$filesystem_root" "apt-get install -y --no-install-recommends xserver-xorg xserver-xorg-video-fbdev xinit pciutils xinput xfonts-100dpi xfonts-75dpi xfonts-scalable chromium-browser" | |
# Set hostname | |
echo "rpi-kiosk" > "$filesystem_root/etc/hostname" | |
# Disable sudo without password | |
rm "$filesystem_root/etc/sudoers.d/010_pi-nopasswd" | |
# Rename default user account | |
kiosk_user="kiosk" | |
run_as_pi "$filesystem_root" "usermod -l $kiosk_user pi" | |
run_as_pi "$filesystem_root" "usermod -m -d /home/$kiosk_user $kiosk_user" | |
# Setup auto-login | |
enable_auto_login "$filesystem_root" "tty1" "$kiosk_user" | |
# Quiet login | |
touch "$filesystem_root/home/$kiosk_user/.hushlogin" | |
# Setup xstart | |
echo "[[ -z \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx &> /dev/null" >> "$filesystem_root/home/$kiosk_user/.profile" | |
# Setup chrome auto-start | |
cat > "$filesystem_root/home/$kiosk_user/.xinitrc" << EOF | |
xsetroot -solid "#6666ff" | |
while true; do | |
/usr/bin/chromium-browser --app=https://google.com --window-position=0,0 --window-size=1921,1081 | |
done | |
EOF | |
# Clean Up | |
echo "Cleaning up" | |
echo "Unmounting boot partition" | |
umount "$boot_mountpoint" | |
rm -r "$boot_mountpoint" | |
echo "Unmounting root partition" | |
umount "$root_mountpoint" | |
rm -r "$root_mountpoint" | |
echo "Removing loopback device" | |
losetup -d "$img_loopback_device" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment