Skip to content

Instantly share code, notes, and snippets.

@Tschrock
Created May 22, 2019 16:50
Show Gist options
  • Save Tschrock/5b9cd3f8af550b289205c49a33e51eff to your computer and use it in GitHub Desktop.
Save Tschrock/5b9cd3f8af550b289205c49a33e51eff to your computer and use it in GitHub Desktop.
#!/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/getty@.service /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