diff --git a/installers/minwrite.sh b/installers/minwrite.sh new file mode 100755 index 00000000..b4b2df21 --- /dev/null +++ b/installers/minwrite.sh @@ -0,0 +1,179 @@ +#!/bin/bash +# +# RaspAP minimal microSD card write operation +# Original author: @zbchristian +# Original source URI: https://github.com/RaspAP/raspap-tools +# Modified by: @billz +# License: GNU General Public License v3.0 +# License URI: https://github.com/raspap/raspap-webgui/blob/master/LICENSE +# +# Limits microSD card write operation to a minimum by moving temporary and log files to RAM. +# Several packages are removed and the default logging service is replaced. +# The file system is still in read/write mode, so RaspAP settings can be saved. +# Write access can be checked with "iotop -aoP". +# Remaining access originates mainly from the ext4 journal update (process jbd2). + +# Exit on error +set -o errexit +# Exit on error inside functions +set -o errtrace + +# Set defaults +readonly bootcmd="/boot/cmdline.txt" + +function _install_minwrite() { + _display_welcome + _begin_install + _remove_packages + _disable_services + _install_logger + _disable_swap + _move_directories + _install_complete +} + +function _dirs2tmpfs() { + for dir in "${dirs[@]}"; do + echo "Moving $dir to RAM" + if ! grep -q " $dir " /etc/fstab; then + echo "tmpfs $dir tmpfs nosuid,nodev 0 0" | sudo tee -a /etc/fstab + fi + done +} + +# Determines host Linux distribution details +function _get_linux_distro() { + if type lsb_release >/dev/null 2>&1; then # linuxbase.org + OS=$(lsb_release -si) + RELEASE=$(lsb_release -sr) + CODENAME=$(lsb_release -sc) + DESC=$(lsb_release -sd) + elif [ -f /etc/os-release ]; then # freedesktop.org + . /etc/os-release + OS=$ID + RELEASE=$VERSION_ID + CODENAME=$VERSION_CODENAME + DESC=$PRETTY_NAME + else + _install_status 1 "Unsupported Linux distribution" + fi +} + +function _begin_install() { + _install_log "Modify the OS to minimize microSD card write operation" + _get_linux_distro + echo "Detected OS: ${DESC}" +} + +function _remove_packages() { + _install_log "Removing packages" + echo -e "The following packages will be removed: ${ANSI_YELLOW}dphys-swapfile logrotate${ANSI_RESET}" + echo -n "Proceed? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + _install_status 0 "(Skipped)" + else + sudo apt-get -y remove --purge dphys-swapfile logrotate || _install_status 1 "Unable to remove packages" + sudo apt-get -y autoremove --purge || _install_status 1 "Unable to autoremove packages" + _install_status 0 + fi + else + echo "(Skipped)" + fi +} + +function _disable_services() { + _install_log "Disabling services" + echo -e "The following services will be disabled: ${ANSI_YELLOW}bootlogd.service bootlogs console-setup apt-daily${ANSI_RESET}" + echo -n "Proceed? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + _install_status 0 "(Skipped)" + else + sudo systemctl unmask bootlogd.service || _install_status 2 "Service bootlogd.service does not exist" + sudo systemctl disable bootlogs || _install_status 2 "Service bootlogs does not exist" + sudo systemctl disable apt-daily.service apt-daily.timer apt-daily-upgrade.timer apt-daily-upgrade.service || _install_status 2 "Service apt-daily does not exist" + _install_status 0 + fi + else + echo "(Skipped)" + fi +} + +function _install_logger() { + _install_log "Installing new logger" + echo -e "The following new logger will be installed: ${ANSI_YELLOW}busybox-syslogd${ANSI_RESET}" + echo -n "Proceed? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + _install_status 0 "(Skipped)" + else + sudo apt-get -y install busybox-syslogd || _install_status 1 "Unable to install busybox-syslogd" + sudo dpkg --purge rsyslog || _install_status 1 "Unable to purge rsyslog" + _install_status 0 + fi + else + echo "(Skipped)" + fi +} + +function _disable_swap() { + _install_log "Modifying boot options to disable swap and filesystem check" + echo "The noswap option will be written to ${bootcmd}" + echo -n "Proceed? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + _install_status 0 "(Skipped)" + else + if ! grep -q "noswap" $bootcmd; then + sudo sed -i '1 s/$/ fsck.mode=skip noswap/' $bootcmd || _install_status 1 "Unable to write to ${bootcmd}" + echo "Modified ${bootcmd} with noswap option" + _install_status 0 + fi + fi + else + echo "(Skipped)" + fi +} + +function _move_directories() { + _install_log "Add tmpfs entries to /etc/fstab" + # move directories to RAM + dirs=( "/tmp" "/var/log" "/var/tmp" "/var/lib/misc" "/var/cache") + # special dirs used by vnstat and php + dirs+=( "/var/lib/vnstat" "/var/php/sessions" ) + echo "The following directories will be moved to RAM:" + echo -e "${ANSI_YELLOW}${dirs[*]}${ANSI_RESET}" + echo -n "Proceed? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + _install_status 0 "(Skipped)" + else + _dirs2tmpfs || _install_status 1 "Unable to call dirs2tmpfs" + _install_status 0 + fi + else + echo "(Skipped)" + fi +} + +function _install_complete() { + _install_log "Installation completed" + echo -e "${ANSI_RED}The system needs to be rebooted as a final step.${ANSI_RESET}" + echo -n "Reboot now? [Y/n]: " + if [ "$assume_yes" == 0 ]; then + read answer < /dev/tty + if [ "$answer" != "${answer#[Nn]}" ]; then + echo "Installation reboot aborted." + exit 0 + fi + echo "Rebooting..." + sudo reboot || _install_status 1 "Unable to execute reboot" + fi +} + diff --git a/installers/raspbian.sh b/installers/raspbian.sh index 7df63832..d5efe387 100755 --- a/installers/raspbian.sh +++ b/installers/raspbian.sh @@ -20,10 +20,12 @@ # -t, --token Specify a GitHub token to access a private repository # -u, --upgrade Upgrades an existing installation to the latest release version # -i, --insiders Installs from the Insiders Edition (RaspAP/raspap-insiders) +# -m, --minwrite Configures a microSD card for minimum write operation # -v, --version Outputs release info and exits -# -n, --uninstall Loads and executes the uninstaller +# -n, --uninstall Loads and executes the uninstaller # -h, --help Outputs usage notes and exits # +# NOTE # Depending on options passed to the installer, ONE of the following # additional shell scripts will be downloaded and sourced: # @@ -31,6 +33,8 @@ # - or - # https://raw.githubusercontent.com/raspap/raspap-webgui/master/installers/mkcert.sh # - or - +# https://raw.githubusercontent.com/raspap/raspap-webgui/master/installers/minwrite.sh +# - or - # https://raw.githubusercontent.com/raspap/raspap-webgui/master/installers/uninstall.sh # # You are not obligated to bundle the LICENSE file with your RaspAP projects as long @@ -55,6 +59,7 @@ function _parse_params() { ovpn_option=1 adblock_option=1 insiders=0 + minwrite=0 acctoken="" while :; do @@ -92,6 +97,9 @@ function _parse_params() { -i|--insiders) insiders=1 ;; + -m|--minwrite) + minwrite=1 + ;; -t|--token) acctoken="$2" shift @@ -146,6 +154,7 @@ OPTIONS: -t, --token Specify a GitHub token to access a private repository -u, --upgrade Upgrades an existing installation to the latest release version -i, --insiders Installs from the Insiders Edition (RaspAP/raspap-insiders) +-m, --minwrite Configures a microSD card for minimum write operation -v, --version Outputs release info and exits -n, --uninstall Loads and executes the uninstaller -h, --help Outputs usage notes and exits @@ -203,7 +212,7 @@ function _get_release() { # Outputs a RaspAP Install log line function _install_log() { - echo -e "${ANSI_GREEN}RaspAP Install: $1${ANSI_RESET}" + echo -e "${ANSI_GREEN}RaspAP ${component}: $1${ANSI_RESET}" } # Outputs a RaspAP divider @@ -255,16 +264,25 @@ function _load_installer() { if [ "${install_cert:-}" = 1 ]; then source="mkcert" + component="mkcert" wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh source /tmp/raspap_${source}.sh && rm -f /tmp/raspap_${source}.sh _install_certificate || _install_status 1 "Unable to install certificate" + elif [ "${minwrite}" = 1 ]; then + source="minwrite" + component="Minwrite" + wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh + source /tmp/raspap_${source}.sh && rm -f /tmp/raspap_${source}.sh + _install_minwrite || _install_status 1 "Unable to execute minimal write install" elif [ "${uninstall}" = 1 ]; then source="uninstall" + component"Uninstall" wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh source /tmp/raspap_${source}.sh && rm -f /tmp/raspap_${source}.sh _remove_raspap || _install_status 1 "Unable to uninstall RaspAP" else source="common" + component="Install" wget "${header[@]}" -q ${UPDATE_URL}installers/${source}.sh -O /tmp/raspap_${source}.sh source /tmp/raspap_${source}.sh && rm -f /tmp/raspap_${source}.sh _install_raspap || _install_status 1 "Unable to install RaspAP"