#!/bin/bash # CREDIT TO THESE TUTORIALS: # petr.io/en/blog/2015/11/09/read-only-raspberry-pi-with-jessie # hallard.me/raspberry-pi-read-only # k3a.me/how-to-make-raspberrypi-truly-read-only-reliable-and-trouble-free if [ $(id -u) -ne 0 ]; then echo "Installer must be run as root." echo "Try 'sudo bash $0'" exit 1 fi clear echo "This script configures a Raspberry Pi" echo "SD card to boot into read-only mode," echo "obviating need for clean shutdown." echo "NO FILES ON THE CARD CAN BE CHANGED" echo "WHEN PI IS BOOTED IN THIS STATE. Either" echo "the filesystems must be remounted in" echo "read/write mode, card must be mounted" echo "R/W on another system, or an optional" echo "jumper can be used to enable read/write" echo "on boot." echo echo "Links to original tutorials are in" echo "script source. THIS IS A ONE-WAY" echo "OPERATION. THERE IS NO SCRIPT TO" echo "REVERSE THIS SETUP! ALL other system" echo "config should be complete before using" echo "this script. MAKE A BACKUP FIRST." echo echo "Run time ~5 minutes. Reboot required." echo echo -n "CONTINUE? [y/N] " read if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then echo "Canceled." exit 0 fi # FEATURE PROMPTS ---------------------------------------------------------- # Installation doesn't begin until after all user input is taken. INSTALL_RW_JUMPER=0 INSTALL_HALT=0 INSTALL_WATCHDOG=0 # Given a list of strings representing options, display each option # preceded by a number (1 to N), display a prompt, check input until # a valid number within the selection range is entered. selectN() { for ((i=1; i<=$#; i++)); do echo $i. ${!i} done echo REPLY="" while : do echo -n "SELECT 1-$#: " read if [[ $REPLY -ge 1 ]] && [[ $REPLY -le $# ]]; then return $REPLY fi done } SYS_TYPES=(Pi\ 3\ /\ Pi\ Zero\ W All\ other\ models) WATCHDOG_MODULES=(bcm2835_wdog bcm2708_wdog) OPTION_NAMES=(NO YES) # echo -n "Enable boot-time read/write jumper? [y/N] " # read # if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then # INSTALL_RW_JUMPER=1 # echo -n "GPIO pin for R/W jumper: " # read # RW_PIN=$REPLY # fi # # echo -n "Install GPIO-halt utility? [y/N] " # read # if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then # INSTALL_HALT=1 # echo -n "GPIO pin for halt button: " # read # HALT_PIN=$REPLY # fi # # echo -n "Enable kernel panic watchdog? [y/N] " # read # if [[ "$REPLY" =~ (yes|y|Y)$ ]]; then # INSTALL_WATCHDOG=1 # echo "Target system type:" # selectN "${SYS_TYPES[0]}" \ # "${SYS_TYPES[1]}" # WD_TARGET=$? # fi # VERIFY SELECTIONS BEFORE CONTINUING -------------------------------------- # echo # if [ $INSTALL_RW_JUMPER -eq 1 ]; then # echo "Boot-time R/W jumper: YES (GPIO$RW_PIN)" # else # echo "Boot-time R/W jumper: NO" # fi # if [ $INSTALL_HALT -eq 1 ]; then # echo "Install GPIO-halt: YES (GPIO$HALT_PIN)" # else # echo "Install GPIO-halt: NO" # fi # if [ $INSTALL_WATCHDOG -eq 1 ]; then # echo "Enable watchdog: YES (${SYS_TYPES[WD_TARGET-1]})" # else # echo "Enable watchdog: NO" # fi # echo # echo -n "CONTINUE? [y/N] " # read # if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then # echo "Canceled." # exit 0 # fi # START INSTALL ------------------------------------------------------------ # All selections have been validated at this point... # Given a filename, a regex pattern to match and a replacement string: # Replace string if found, else no change. # (# $1 = filename, $2 = pattern to match, $3 = replacement) replace() { grep $2 $1 >/dev/null if [ $? -eq 0 ]; then # Pattern found; replace in file sed -i "s/$2/$3/g" $1 >/dev/null fi } # Given a filename, a regex pattern to match and a replacement string: # If found, perform replacement, else append file w/replacement on new line. replaceAppend() { grep $2 $1 >/dev/null if [ $? -eq 0 ]; then # Pattern found; replace in file sed -i "s/$2/$3/g" $1 >/dev/null else # Not found; append on new line (silently) echo $3 | sudo tee -a $1 >/dev/null fi } # Given a filename, a regex pattern to match and a string: # If found, no change, else append file with string on new line. append1() { grep $2 $1 >/dev/null if [ $? -ne 0 ]; then # Not found; append on new line (silently) echo $3 | sudo tee -a $1 >/dev/null fi } # Given a filename, a regex pattern to match and a string: # If found, no change, else append space + string to last line -- # this is used for the single-line /boot/cmdline.txt file. append2() { grep $2 $1 >/dev/null if [ $? -ne 0 ]; then # Not found; insert in file before EOF sed -i "s/\'/ $3/g" $1 >/dev/null fi } echo echo "Starting installation..." echo "Removing unwanted packages..." #apt-get remove -y --force-yes --purge triggerhappy logrotate dbus \ # dphys-swapfile xserver-common lightdm fake-hwclock # Let's keep dbus...that includes avahi-daemon, a la 'raspberrypi.local', # also keeping xserver & lightdm for GUI login (WIP, not working yet) apt-get remove -y --force-yes --purge triggerhappy logrotate \ dphys-swapfile fake-hwclock apt-get -y --force-yes autoremove --purge # Replace log management with busybox (use logread if needed) echo "Installing ntp and busybox-syslogd..." apt-get -y --force-yes install ntp busybox-syslogd; dpkg --purge rsyslog echo "Configuring system..." # Add fastboot, noswap and/or ro to end of /boot/cmdline.txt append2 /boot/cmdline.txt fastboot fastboot append2 /boot/cmdline.txt noswap noswap append2 /boot/cmdline.txt ro^o^t ro # Move /var/spool to /tmp rm -rf /var/spool ln -s /tmp /var/spool # Change spool permissions in var.conf (rondie/Margaret fix) replace /usr/lib/tmpfiles.d/var.conf "spool\s*0755" "spool 1777" # Move dhcpd.resolv.conf to tmpfs touch /tmp/dhcpcd.resolv.conf rm /etc/resolv.conf ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf # Make edits to fstab # make / ro # tmpfs /var/log tmpfs nodev,nosuid 0 0 # tmpfs /var/tmp tmpfs nodev,nosuid 0 0 # tmpfs /tmp tmpfs nodev,nosuid 0 0 replace /etc/fstab "vfat\s*defaults\s" "vfat defaults,ro " replace /etc/fstab "ext4\s*defaults,noatime\s" "ext4 defaults,noatime,ro " cat << EOF >> /etc/fstab tmpfs /var/log tmpfs nodev,nosuid 0 0 tmpfs /var/tmp tmpfs nodev,nosuid 0 0 tmpfs /var/lib/ntp tmpfs nodev,nosuid 0 0 tmpfs /var/lib/pyheatpump tmpfs nodev,nosuid 0 0 tmpfs /var/run tmpfs nodev,nosuid 0 0 tmpfs /tmp tmpfs nodev,nosuid 0 0 EOF # PROMPT FOR REBOOT -------------------------------------------------------- echo "Done." echo echo "Settings take effect on next boot." echo echo -n "REBOOT NOW? [y/N] " read if [[ ! "$REPLY" =~ ^(yes|y|Y)$ ]]; then echo "Exiting without reboot." exit 0 fi echo "Reboot started..." reboot exit 0